Mercurial
view infinite_canvas/theme.c @ 278:8d560f50ed4c
Improve infinite canvas interactions and browser chrome
Render Lucide icons directly with Raylib, add searchable icon browsing, robust text editing, entity lifecycle animations, z-order-safe input, semantic themes, and animated editable browser controls. Document rendering, pinning, context, and component extension for future agents.
Co-authored-by: Copilot <[email protected]>
Copilot-Session: f68442b1-fa8f-46a0-9689-81710613bbd4
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Mon, 17 Aug 2026 22:16:14 -0700 |
| parents | b55c22cff335 |
| children |
line wrap: on
line source
#include "infinite_canvas/theme.h" #include <stdlib.h> #include <string.h> #if defined(PLATFORM_WEB) || defined(__EMSCRIPTEN__) #include <emscripten/emscripten.h> EM_JS(int, Canvas_Web_Prefers_Dark, (), { return window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches ? 1 : 0; }); #elif defined(_WIN32) #include <windows.h> #else #include <stdio.h> #endif Canvas_Theme_Mode Canvas_Theme_Mode_From_String(const char *p_value) { if (!p_value || strcmp(p_value, "auto") == 0) return CANVAS_THEME_AUTO; if (strcmp(p_value, "light") == 0) return CANVAS_THEME_LIGHT; if (strcmp(p_value, "dark") == 0) return CANVAS_THEME_DARK; return CANVAS_THEME_AUTO; } boolean Canvas_System_Prefers_Dark(void) { #if defined(PLATFORM_WEB) || defined(__EMSCRIPTEN__) return Canvas_Web_Prefers_Dark() ? TRUE : FALSE; #elif defined(_WIN32) DWORD light_theme = 1; DWORD size = sizeof(light_theme); LSTATUS status = RegGetValueA( HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", "AppsUseLightTheme", RRF_RT_REG_DWORD, NULL, &light_theme, &size); return status == ERROR_SUCCESS && light_theme == 0 ? TRUE : FALSE; #elif defined(__APPLE__) FILE *p_pipe = popen("defaults read -g AppleInterfaceStyle 2>/dev/null", "r"); if (!p_pipe) return FALSE; char value[32] = {0}; boolean dark = fgets(value, sizeof(value), p_pipe) && strstr(value, "Dark") ? TRUE : FALSE; pclose(p_pipe); return dark; #else const char *p_gtk_theme = getenv("GTK_THEME"); if (p_gtk_theme && (strstr(p_gtk_theme, "dark") || strstr(p_gtk_theme, "Dark"))) { return TRUE; } FILE *p_pipe = popen( "gsettings get org.gnome.desktop.interface color-scheme 2>/dev/null", "r"); if (!p_pipe) return FALSE; char value[64] = {0}; boolean dark = fgets(value, sizeof(value), p_pipe) && strstr(value, "prefer-dark") ? TRUE : FALSE; pclose(p_pipe); return dark; #endif } void Canvas_Theme_Resolve(Canvas_Theme *p_theme, Canvas_Theme_Mode mode) { Canvas_Theme_Mode resolved = mode; if (resolved == CANVAS_THEME_AUTO) { resolved = Canvas_System_Prefers_Dark() ? CANVAS_THEME_DARK : CANVAS_THEME_LIGHT; } *p_theme = (Canvas_Theme){ .requested_mode = mode, .resolved_mode = resolved, }; if (resolved == CANVAS_THEME_DARK) { p_theme->background = (Color){15, 15, 17, 255}; p_theme->surface = (Color){24, 24, 27, 255}; p_theme->surface_muted = (Color){39, 39, 42, 255}; p_theme->border = (Color){63, 63, 70, 255}; p_theme->text = (Color){244, 244, 245, 255}; p_theme->text_muted = (Color){161, 161, 170, 255}; p_theme->accent = (Color){167, 139, 250, 255}; p_theme->accent_soft = (Color){46, 35, 72, 255}; p_theme->on_accent = (Color){255, 255, 255, 255}; p_theme->selection = (Color){196, 181, 253, 230}; p_theme->entity_outline = (Color){255, 255, 255, 26}; p_theme->button = (Color){63, 63, 70, 255}; p_theme->button_hover = (Color){82, 82, 91, 255}; p_theme->button_active = (Color){124, 58, 237, 255}; p_theme->button_active_hover = (Color){139, 92, 246, 255}; p_theme->switch_track_off = (Color){63, 63, 70, 255}; p_theme->switch_track_off_hover = (Color){82, 82, 91, 255}; p_theme->switch_track_on = (Color){52, 211, 153, 255}; p_theme->switch_track_on_hover = (Color){110, 231, 183, 255}; p_theme->switch_thumb = (Color){250, 250, 250, 255}; p_theme->pin = (Color){251, 191, 36, 255}; p_theme->danger = (Color){248, 113, 113, 255}; p_theme->shadow = (Color){0, 0, 0, 110}; p_theme->toast_surface = (Color){24, 24, 27, 255}; p_theme->toast_border = (Color){63, 63, 70, 255}; } else { p_theme->background = (Color){255, 255, 255, 255}; p_theme->surface = (Color){255, 255, 255, 252}; p_theme->surface_muted = (Color){244, 247, 251, 255}; p_theme->border = (Color){205, 211, 221, 255}; p_theme->text = (Color){24, 24, 27, 255}; p_theme->text_muted = (Color){113, 113, 122, 255}; p_theme->accent = (Color){37, 99, 235, 255}; p_theme->accent_soft = (Color){239, 246, 255, 255}; p_theme->on_accent = (Color){255, 255, 255, 255}; p_theme->selection = (Color){0, 122, 255, 230}; p_theme->entity_outline = (Color){0, 0, 0, 20}; p_theme->button = (Color){24, 24, 27, 255}; p_theme->button_hover = (Color){39, 39, 42, 255}; p_theme->button_active = (Color){37, 99, 235, 255}; p_theme->button_active_hover = (Color){59, 130, 246, 255}; p_theme->switch_track_off = (Color){205, 211, 221, 255}; p_theme->switch_track_off_hover = (Color){161, 161, 170, 255}; p_theme->switch_track_on = (Color){22, 163, 74, 255}; p_theme->switch_track_on_hover = (Color){34, 197, 94, 255}; p_theme->switch_thumb = (Color){255, 255, 255, 255}; p_theme->pin = (Color){217, 119, 6, 255}; p_theme->danger = (Color){220, 38, 38, 255}; p_theme->shadow = (Color){15, 23, 42, 36}; p_theme->toast_surface = (Color){255, 255, 255, 255}; p_theme->toast_border = (Color){212, 216, 223, 255}; } } void Canvas_Theme_Toggle(Canvas_Theme *p_theme) { Canvas_Theme_Resolve( p_theme, p_theme->resolved_mode == CANVAS_THEME_DARK ? CANVAS_THEME_LIGHT : CANVAS_THEME_DARK); } void Canvas_Theme_Draw_Shadow( const Canvas_Theme *p_theme, Rectangle bounds, float roundness, int32 segments, float zoom, float opacity) { float offset = 6.0f / zoom; float shadow_alpha = ((float)p_theme->shadow.a / 255.0f) * opacity; DrawRectangleRounded( (Rectangle){ bounds.x + offset, bounds.y + offset, bounds.width, bounds.height, }, roundness, segments, Fade(p_theme->shadow, shadow_alpha)); }