Mercurial
view third_party/raylib/custom.c @ 279:b3b547563ec7
Add Google connector service and agent wiki
Implement the C/Seobeo Google Drive and Gmail connector with encrypted OAuth storage, Zenbu authentication, browser testing, AI tool discovery, chunked HTTP decoding, and Bazel coverage. Consolidate repository guidance into progressive wiki documentation and enforce arena-first allocation for new first-party C code.
Co-authored-by: Copilot <[email protected]>
Copilot-Session: 84c338fd-0939-4bb3-b7f3-1062eb213e5d
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Mon, 17 Aug 2026 22:22:36 -0700 |
| parents | 058de208e640 |
| children |
line wrap: on
line source
#include "third_party/raylib/include/raylib.h" #define RAYGUI_IMPLEMENTATION #include "third_party/raylib/include/raygui.h" #include "third_party/raylib/custom.h" #include <string.h> // ============================================================================ // COLOR SCHEME IMPLEMENTATION // ============================================================================ // Global color scheme instance ColorScheme g_colors = {0}; ColorScheme PostDog_DefaultColorScheme(void) { return (ColorScheme){ .primary = (Color){255, 140, 0, 255}, // Orange .secondary = (Color){255, 248, 231, 255}, // Beige/Egg white .background = (Color){255, 255, 255, 255}, // White .text = (Color){30, 30, 30, 255}, // Near black .text_light = (Color){255, 255, 255, 255}, // White .border = (Color){200, 200, 200, 255}, // Light gray .highlight = (Color){255, 180, 100, 255}, // Light orange .success = (Color){76, 175, 80, 255}, // Green .error = (Color){244, 67, 54, 255}, // Red .muted = (Color){158, 158, 158, 255}, // Gray }; } ColorScheme PostDog_DarkColorScheme(void) { return (ColorScheme){ .primary = (Color){255, 140, 0, 255}, // Orange .secondary = (Color){45, 45, 48, 255}, // Dark gray .background = (Color){30, 30, 30, 255}, // Near black .text = (Color){240, 240, 240, 255}, // Near white .text_light = (Color){255, 255, 255, 255}, // White .border = (Color){70, 70, 70, 255}, // Dark gray .highlight = (Color){255, 180, 100, 255}, // Light orange .success = (Color){76, 175, 80, 255}, // Green .error = (Color){244, 67, 54, 255}, // Red .muted = (Color){100, 100, 100, 255}, // Gray }; } void PostDog_InitColorScheme(void) { g_colors = PostDog_DefaultColorScheme(); } void PostDog_SetColorScheme(ColorScheme scheme) { g_colors = scheme; } // ============================================================================ // FUNCTIONALITY HELPERS // ============================================================================ void DefaultBehaviours(void) { IncreaseFontSize(); DecreaseFontSize(); } void IncreaseFontSize(void) { if ((IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyDown(KEY_EQUAL)) GuiSetStyle(DEFAULT, TEXT_SIZE, GuiGetStyle(DEFAULT, TEXT_SIZE) + 1); } void DecreaseFontSize(void) { if ((IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyDown(KEY_MINUS)) GuiSetStyle(DEFAULT, TEXT_SIZE, GuiGetStyle(DEFAULT, TEXT_SIZE) - 1); } // --- Layout helper --- // Rectangle RightOf(Rectangle ref, float padding) { return (Rectangle){ .x = ref.x + ref.width + padding, .y = ref.y, .width = 0, .height = ref.height }; } Rectangle Below(Rectangle ref, float padding) { return (Rectangle){ .x = ref.x, .y = ref.y + ref.height + padding, .width = ref.width, .height = 0 }; } Rectangle LeftColumn(Rectangle container, float ratio, float padding) { return (Rectangle){ .x = container.x + padding, .y = container.y + padding, .width = (container.width * ratio) - padding, .height = container.height - (2 * padding) }; } Rectangle RightColumn(Rectangle container, Rectangle leftCol, float padding) { return (Rectangle){ .x = leftCol.x + leftCol.width + padding, .y = container.y + padding, .width = container.width - leftCol.width - (3 * padding), .height = container.height - (2 * padding) }; } Rectangle HorizontalSplit(Rectangle container, float ratio) { return (Rectangle){ .x = container.x, .y = container.y, .width = container.width * ratio, .height = container.height }; } void DrawRectangleSelectiveRounded(Rectangle rec, float radius, int segments, Color color, boolean roundTL, boolean roundTR, boolean roundBR, boolean roundBL) { // Clamp radius to valid range float maxR = fminf(rec.width, rec.height) * 0.5f; radius = fminf(fmaxf(radius, 0.0f), maxR); // Fill center DrawRectangleRec((Rectangle){ rec.x + radius, rec.y + radius, rec.width - 2.0f * radius, rec.height - 2.0f * radius }, color); // // |--------| // | | // | | // |--------| // Top edge float topStart = rec.x + (roundTL ? radius : 0.0f); float topWidth = rec.width - (roundTL ? radius : 0.0f) - (roundTR ? radius : 0.0f); if (topWidth > 0.0f) DrawRectangleRec((Rectangle){ topStart, rec.y, topWidth, radius }, color); // Bottom edge float botStart = rec.x + (roundBL ? radius : 0.0f); float botWidth = rec.width - (roundBL ? radius : 0.0f) - (roundBR ? radius : 0.0f); if (botWidth > 0.0f) DrawRectangleRec((Rectangle){ botStart, rec.y + rec.height - radius, botWidth, radius }, color); // Left edge float leftStart = rec.y + (roundTL ? radius : 0.0f); float leftHeight = rec.height - (roundTL ? radius : 0.0f) - (roundBL ? radius : 0.0f); if (leftHeight > 0.0f) DrawRectangleRec((Rectangle){ rec.x, leftStart, radius, leftHeight - radius}, color); // Right edge float rightStart = rec.y + (roundTR ? radius : 0.0f); float rightHeight = rec.height - (roundTR ? radius : 0.0f) - (roundBR ? radius : 0.0f); if (rightHeight > 0.0f) DrawRectangleRec((Rectangle){ rec.x + rec.width - radius, rightStart, radius, rightHeight - radius }, color); // Corners (arcs) if (roundTL) { DrawCircleSector((Vector2){ rec.x + radius, rec.y + radius }, radius, 180.0f, 270.0f, segments, color); } if (roundTR) { DrawCircleSector((Vector2){ rec.x + rec.width - radius, rec.y + radius }, radius, 270.0f, 360.0f, segments, color); } if (roundBR) { DrawCircleSector((Vector2){ rec.x + rec.width - radius, rec.y + rec.height - radius }, radius, 0.0f, 90.0f, segments, color); } if (roundBL) { DrawCircleSector((Vector2){ rec.x + radius, rec.y + rec.height - radius }, radius, 90.0f, 180.0f, segments, color); } } Rectangle AddPadding(Rectangle rect, float padding) { return (Rectangle){ rect.x + padding, rect.y + padding, rect.width - (2 * padding), rect.height - (2 * padding) }; } Rectangle AddPaddingAll(Rectangle rect, float top, float right,float down, float left) { return (Rectangle){ rect.x + left, rect.y + top, rect.width - (right + left), rect.height - (top + down), }; } Rectangle AddPaddingHorizontal(Rectangle rect, float padding) { return (Rectangle){ rect.x + padding, rect.y, rect.width - (2 * padding), rect.height }; } Rectangle AddPaddingVertical(Rectangle rect, float padding) { return (Rectangle){ rect.x, rect.y + padding, rect.width, rect.height - (2 * padding) }; } Rectangle VerticalSplit(Rectangle container, float ratio) { return (Rectangle){ .x = container.x, .y = container.y, .width = container.width, .height = container.height * ratio }; } // ============================================================================ // DRAWING HELPERS - Additional // ============================================================================ void DrawRectangleSelectiveRoundedLines(Rectangle rec, float radius, int segments, Color color, boolean roundTL, boolean roundTR, boolean roundBR, boolean roundBL) { // Top edge DrawLine(rec.x + (roundTL ? radius : 0), rec.y, rec.x + rec.width - (roundTR ? radius : 0), rec.y, color); // Bottom edge DrawLine(rec.x + (roundBL ? radius : 0), rec.y + rec.height, rec.x + rec.width - (roundBR ? radius : 0), rec.y + rec.height, color); // Left edge DrawLine(rec.x, rec.y + (roundTL ? radius : 0), rec.x, rec.y + rec.height - (roundBL ? radius : 0), color); // Right edge DrawLine(rec.x + rec.width, rec.y + (roundTR ? radius : 0), rec.x + rec.width, rec.y + rec.height - (roundBR ? radius : 0), color); // Corner arcs if (roundTL) DrawCircleSectorLines((Vector2){rec.x + radius, rec.y + radius}, radius, 180, 270, segments, color); if (roundTR) DrawCircleSectorLines((Vector2){rec.x + rec.width - radius, rec.y + radius}, radius, 270, 360, segments, color); if (roundBR) DrawCircleSectorLines((Vector2){rec.x + rec.width - radius, rec.y + rec.height - radius}, radius, 0, 90, segments, color); if (roundBL) DrawCircleSectorLines((Vector2){rec.x + radius, rec.y + rec.height - radius}, radius, 90, 180, segments, color); } // ============================================================================ // CUSTOM TAB COMPONENT IMPLEMENTATION // ============================================================================ boolean PostDog_TabBar(Rectangle bounds, TabConfig config) { if (config.count <= 0 || config.labels == NULL || config.active_tab == NULL) return FALSE; boolean changed = FALSE; float tab_width = (bounds.width - config.padding * (config.count - 1)) / config.count; Vector2 mouse = GetMousePosition(); for (int i = 0; i < config.count; i++) { Rectangle tab_rect = { .x = bounds.x + i * (tab_width + config.padding), .y = bounds.y, .width = tab_width, .height = bounds.height }; boolean is_active = (i == *config.active_tab); boolean is_hovered = CheckCollisionPointRec(mouse, tab_rect); Color bg_color = is_active ? config.active_color : config.inactive_color; Color txt_color = is_active ? config.active_text_color : config.text_color; // Hover effect if (is_hovered && !is_active) bg_color = Fade(config.active_color, 0.5f); // Draw tab background with rounded top corners DrawRectangleSelectiveRounded(tab_rect, config.corner_radius, 8, bg_color, TRUE, TRUE, FALSE, FALSE); // Draw tab label int text_width = MeasureText(config.labels[i], GuiGetStyle(DEFAULT, TEXT_SIZE)); Vector2 text_pos = { tab_rect.x + (tab_rect.width - text_width) / 2, tab_rect.y + (tab_rect.height - GuiGetStyle(DEFAULT, TEXT_SIZE)) / 2 }; DrawText(config.labels[i], text_pos.x, text_pos.y, GuiGetStyle(DEFAULT, TEXT_SIZE), txt_color); // Handle click if (is_hovered && IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && !is_active) { *config.active_tab = i; changed = TRUE; } } return changed; } boolean PostDog_TabBarSimple(Rectangle bounds, const char **labels, int count, int *active_tab) { TabConfig config = { .labels = labels, .count = count, .active_tab = active_tab, .padding = 2, .corner_radius = 8, .active_color = g_colors.primary, .inactive_color = g_colors.secondary, .text_color = g_colors.text, .active_text_color = g_colors.text_light, }; return PostDog_TabBar(bounds, config); } // ============================================================================ // SPLIT PANEL IMPLEMENTATION // ============================================================================ void PostDog_SplitPanel(Rectangle bounds, SplitPanelConfig config, Rectangle *out_left, Rectangle *out_right) { float left_width = bounds.width * config.split_ratio - config.gap / 2; float right_width = bounds.width * (1.0f - config.split_ratio) - config.gap / 2; if (out_left) { *out_left = (Rectangle){ .x = bounds.x, .y = bounds.y, .width = left_width, .height = bounds.height }; } if (out_right) { *out_right = (Rectangle){ .x = bounds.x + left_width + config.gap, .y = bounds.y, .width = right_width, .height = bounds.height }; } } void PostDog_SplitPanelDraw(Rectangle bounds, SplitPanelConfig config) { Rectangle left_rect, right_rect; PostDog_SplitPanel(bounds, config, &left_rect, &right_rect); // Draw left panel with rounded left corners DrawRectangleSelectiveRounded(left_rect, config.corner_radius, 8, config.left_color, TRUE, FALSE, FALSE, TRUE); // Draw right panel with rounded right corners DrawRectangleSelectiveRounded(right_rect, config.corner_radius, 8, config.right_color, FALSE, TRUE, TRUE, FALSE); } // ============================================================================ // COMBINED INPUT COMPONENTS IMPLEMENTATION // ============================================================================ // Helper: Parse semicolon-separated items and return item at index static const char* GetDropdownItem(const char *items, int index, char *buffer, int buffer_size) { int current_index = 0; int start = 0; int len = strlen(items); for (int i = 0; i <= len; i++) { if (items[i] == ';' || items[i] == '\0') { if (current_index == index) { int item_len = i - start; if (item_len >= buffer_size) item_len = buffer_size - 1; strncpy(buffer, items + start, item_len); buffer[item_len] = '\0'; return buffer; } current_index++; start = i + 1; } } buffer[0] = '\0'; return buffer; } // Helper: Count items in semicolon-separated string static int CountDropdownItems(const char *items) { if (!items || items[0] == '\0') return 0; int count = 1; for (int i = 0; items[i]; i++) if (items[i] == ';') count++; return count; } boolean PostDog_DropdownTextBox(Rectangle bounds, DropdownTextBoxConfig config) { boolean result = FALSE; // Calculate dropdown width float dropdown_w = config.dropdown_width; if (dropdown_w <= 1.0f) dropdown_w = bounds.width * dropdown_w; // Calculate rectangles Rectangle dropdown_rect = { .x = bounds.x, .y = bounds.y, .width = dropdown_w, .height = bounds.height }; Rectangle textbox_rect = { .x = bounds.x + dropdown_w, .y = bounds.y, .width = bounds.width - dropdown_w, .height = bounds.height }; // Draw combined background DrawRectangleSelectiveRounded(dropdown_rect, config.corner_radius, 8, config.background_color, TRUE, FALSE, FALSE, TRUE); DrawRectangleSelectiveRounded(textbox_rect, config.corner_radius, 8, config.background_color, FALSE, TRUE, TRUE, FALSE); // Draw separator line DrawLine(dropdown_rect.x + dropdown_rect.width, dropdown_rect.y + 4, dropdown_rect.x + dropdown_rect.width, dropdown_rect.y + dropdown_rect.height - 4, config.border_color); // Draw border DrawRectangleSelectiveRoundedLines(bounds, config.corner_radius, 8, config.border_color, TRUE, TRUE, TRUE, TRUE); // Custom dropdown with per-item colors Vector2 mouse_pos = GetMousePosition(); int item_count = config.item_count > 0 ? config.item_count : CountDropdownItems(config.dropdown_items); int font_size = GuiGetStyle(DEFAULT, TEXT_SIZE); char item_buffer[64]; // Get current selected item GetDropdownItem(config.dropdown_items, *config.dropdown_active, item_buffer, sizeof(item_buffer)); Color current_color = (config.item_colors && *config.dropdown_active < item_count) ? config.item_colors[*config.dropdown_active] : config.text_color; // Draw selected item in dropdown area Rectangle dropdown_inner = AddPadding(dropdown_rect, 6); boolean dropdown_hovered = CheckCollisionPointRec(mouse_pos, dropdown_rect); // Highlight on hover if (dropdown_hovered && !(*config.dropdown_edit_mode)) { DrawRectangleSelectiveRounded(dropdown_rect, config.corner_radius, 8, Fade(current_color, 0.5f), TRUE, FALSE, FALSE, TRUE); } // Draw selected text with color int text_width = MeasureText(item_buffer, font_size); DrawText(item_buffer, dropdown_inner.x + (dropdown_inner.width - text_width) / 2, dropdown_inner.y + (dropdown_inner.height - font_size) / 2, font_size, current_color); DrawRectangleSelectiveRounded(dropdown_rect, config.corner_radius, 8, Fade(current_color, 0.3f), TRUE, FALSE, FALSE, TRUE); // Draw dropdown arrow float arrow_size = 6; float arrow_x = dropdown_rect.x + dropdown_rect.width - 14; float arrow_y = dropdown_rect.y + dropdown_rect.height / 2; DrawTriangle( (Vector2){arrow_x, arrow_y - arrow_size/2}, (Vector2){arrow_x + arrow_size, arrow_y - arrow_size/2}, (Vector2){arrow_x + arrow_size/2, arrow_y + arrow_size/2}, config.text_color); // Handle dropdown click if (dropdown_hovered && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { *config.dropdown_edit_mode = !(*config.dropdown_edit_mode); } // Draw dropdown list when open if (*config.dropdown_edit_mode) { float item_height = bounds.height; Rectangle list_rect = { .x = dropdown_rect.x, .y = dropdown_rect.y + dropdown_rect.height + 2, .width = dropdown_rect.width, .height = item_height * item_count }; // Background DrawRectangleRounded(list_rect, 0.1f, 8, config.background_color); DrawRectangleRoundedLines(list_rect, 0.1f, 8, config.border_color); // Draw each item for (int i = 0; i < item_count; i++) { Rectangle item_rect = { .x = list_rect.x, .y = list_rect.y + i * item_height, .width = list_rect.width, .height = item_height }; GetDropdownItem(config.dropdown_items, i, item_buffer, sizeof(item_buffer)); Color item_color = (config.item_colors && i < item_count) ? config.item_colors[i] : config.text_color; boolean item_hovered = CheckCollisionPointRec(mouse_pos, item_rect); // Highlight hovered item if (item_hovered) DrawRectangleRec(item_rect, Fade(item_color, 0.15f)); // Highlight selected item DrawRectangleRec(item_rect, Fade(item_color, 0.2f)); // Draw item text text_width = MeasureText(item_buffer, font_size); DrawText(item_buffer, item_rect.x + (item_rect.width - text_width) / 2, item_rect.y + (item_rect.height - font_size) / 2, font_size, item_color); // Handle item click if (item_hovered && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { *config.dropdown_active = i; *config.dropdown_edit_mode = FALSE; } } // Close dropdown if clicked outside if (!CheckCollisionPointRec(mouse_pos, list_rect) && !CheckCollisionPointRec(mouse_pos, dropdown_rect) && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { *config.dropdown_edit_mode = FALSE; } } // Draw text box (using raygui) Rectangle textbox_inner = AddPadding(textbox_rect, 4); boolean ctrl_pressed = IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL) || IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_RIGHT_SUPER); if (*config.text_edit_mode && ctrl_pressed && IsKeyPressed(KEY_V)) { const char *clipboard = GetClipboardText(); snprintf(config.text_buffer, config.text_buffer_size, "%s", clipboard); } if (GuiTextBox(textbox_inner, config.text_buffer, config.text_buffer_size, *config.text_edit_mode)) { *config.text_edit_mode = !(*config.text_edit_mode); result = TRUE; } return result; }