diff --git a/src/main.cpp b/src/main.cpp index 0b21b13..cbed27e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,7 +10,6 @@ #include "midi/midi.h" #include "audio/audio.h" #include "ui/ui_core.h" -#include "ui/ui_theme.h" #include "ui/ui_widgets.h" // [cpp] @@ -94,9 +93,14 @@ struct AppState { B32 show_about_window; B32 show_confirm_dialog; S32 settings_theme_sel; + S32 settings_theme_prev; B32 settings_vsync; B32 settings_autosave; + // Accent color selection + S32 accent_sel; + S32 accent_prev; + // Audio device selection S32 audio_device_sel; // dropdown index: 0 = None, 1+ = device S32 audio_device_prev; // previous selection for change detection @@ -507,11 +511,19 @@ static void settings_window_content(void *user_data) { AppState *app = (AppState *)user_data; ui_label("SettingsLblTheme", "Theme"); - static const char *theme_options[] = { "Dark", "Light", "Solarized" }; + static const char *theme_options[] = { "Dark", "Light" }; CLAY(CLAY_ID("SettingsThemeWrap"), .layout = { .sizing = { .width = CLAY_SIZING_FIXED(uis(180)), .height = CLAY_SIZING_FIT() } } ) { - ui_dropdown("SettingsTheme", theme_options, 3, &app->settings_theme_sel); + ui_dropdown("SettingsTheme", theme_options, 2, &app->settings_theme_sel); + } + + ui_label("SettingsLblAccent", "Accent Color"); + static const char *accent_options[] = { "Blue", "Turquoise", "Orange", "Purple", "Pink", "Red", "Green" }; + CLAY(CLAY_ID("SettingsAccentWrap"), + .layout = { .sizing = { .width = CLAY_SIZING_FIXED(uis(180)), .height = CLAY_SIZING_FIT() } } + ) { + ui_dropdown("SettingsAccent", accent_options, 7, &app->accent_sel); } ui_checkbox("SettingsVsync", "V-Sync", &app->settings_vsync); @@ -575,11 +587,11 @@ static void settings_window_content(void *user_data) { .padding = { uip(12), uip(12), 0, 0 }, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER }, }, - .backgroundColor = Clay_Color{50, 50, 50, 255}, + .backgroundColor = g_theme.disabled_bg, .cornerRadius = CLAY_CORNER_RADIUS(uis(3)) ) { static Clay_TextElementConfig disabled_text = {}; - disabled_text.textColor = Clay_Color{100, 100, 100, 255}; + disabled_text.textColor = g_theme.disabled_text; disabled_text.fontSize = FONT_SIZE_NORMAL; disabled_text.wrapMode = CLAY_TEXT_WRAP_NONE; CLAY_TEXT(CLAY_STRING("Play Test Tone"), &disabled_text); @@ -705,6 +717,34 @@ static void do_frame(AppState *app) { g_ui_scale = app->ui_scale; renderer_set_font_scale(app->renderer, app->ui_scale); + // Handle theme change + if (app->settings_theme_sel != app->settings_theme_prev) { + ui_set_theme(app->settings_theme_sel); + ui_set_accent(app->accent_sel); // reapply accent on new base theme + app->settings_theme_prev = app->settings_theme_sel; + + // Refresh all text configs with new theme colors + g_text_config_normal.textColor = g_theme.text; + g_text_config_title.textColor = g_theme.text; + g_text_config_dim.textColor = g_theme.text_dim; + ui_widgets_theme_changed(); + + // Update renderer clear color to match theme + renderer_set_clear_color(app->renderer, + g_theme.bg_dark.r / 255.f, g_theme.bg_dark.g / 255.f, g_theme.bg_dark.b / 255.f); + } + + // Handle accent change + if (app->accent_sel != app->accent_prev) { + ui_set_accent(app->accent_sel); + app->accent_prev = app->accent_sel; + + g_text_config_normal.textColor = g_theme.text; + g_text_config_title.textColor = g_theme.text; + g_text_config_dim.textColor = g_theme.text_dim; + ui_widgets_theme_changed(); + } + // Update text configs when scale changes if (g_text_config_normal.fontSize != FONT_SIZE_NORMAL) { g_text_config_normal.fontSize = FONT_SIZE_NORMAL; @@ -760,7 +800,7 @@ int main(int argc, char **argv) { AudioEngine *audio = audio_create(platform_get_native_handle(window)); // Initialize UI (Clay) - ui_init_theme(); + ui_set_theme(0); UI_Context *ui = ui_create((F32)w, (F32)h); ui_set_measure_text_fn(ui, renderer_measure_text, renderer); init_text_configs(); diff --git a/src/renderer/renderer.h b/src/renderer/renderer.h index 2acfdc2..06b1974 100644 --- a/src/renderer/renderer.h +++ b/src/renderer/renderer.h @@ -19,6 +19,7 @@ bool renderer_begin_frame(Renderer *renderer); void renderer_end_frame(Renderer *renderer, Clay_RenderCommandArray render_commands); void renderer_resize(Renderer *renderer, int32_t width, int32_t height); void renderer_set_font_scale(Renderer *renderer, float scale); +void renderer_set_clear_color(Renderer *renderer, float r, float g, float b); // Text measurement callback compatible with UI_MeasureTextFn // Measures text of given length (NOT necessarily null-terminated) at font_size pixels. diff --git a/src/renderer/renderer_dx12.cpp b/src/renderer/renderer_dx12.cpp index 5c032b0..1ecf2e8 100644 --- a/src/renderer/renderer_dx12.cpp +++ b/src/renderer/renderer_dx12.cpp @@ -1,6 +1,5 @@ #include "renderer/renderer.h" #include "ui/ui_core.h" -#include "ui/ui_theme.h" #include #include diff --git a/src/renderer/renderer_metal.mm b/src/renderer/renderer_metal.mm index 49b077a..b8259dd 100644 --- a/src/renderer/renderer_metal.mm +++ b/src/renderer/renderer_metal.mm @@ -1,6 +1,5 @@ #include "renderer/renderer.h" #include "ui/ui_core.h" -#include "ui/ui_theme.h" #import #import @@ -166,6 +165,9 @@ struct Renderer { // Text measurement (Core Text) CTFontRef measure_font; F32 measure_font_size; + + // Clear color + F32 clear_r, clear_g, clear_b; }; //////////////////////////////// @@ -617,6 +619,11 @@ Renderer *renderer_create(RendererDesc *desc) { r->measure_font = nullptr; r->measure_font_size = 0; + // Default clear color (dark theme bg_dark) + r->clear_r = 0.12f; + r->clear_g = 0.12f; + r->clear_b = 0.13f; + return r; } @@ -647,7 +654,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { pass.colorAttachments[0].texture = drawable.texture; pass.colorAttachments[0].loadAction = MTLLoadActionClear; pass.colorAttachments[0].storeAction = MTLStoreActionStore; - pass.colorAttachments[0].clearColor = MTLClearColorMake(0.12, 0.12, 0.13, 1.0); + pass.colorAttachments[0].clearColor = MTLClearColorMake(r->clear_r, r->clear_g, r->clear_b, 1.0); id cmd_buf = [r->command_queue commandBuffer]; id encoder = [cmd_buf renderCommandEncoderWithDescriptor:pass]; @@ -801,6 +808,12 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { r->frame_index++; } +void renderer_set_clear_color(Renderer *r, float cr, float cg, float cb) { + r->clear_r = cr; + r->clear_g = cg; + r->clear_b = cb; +} + void renderer_set_font_scale(Renderer *r, float scale) { float target_size = 15.0f * scale; if (fabsf(target_size - r->font_atlas_size) < 0.1f) return; diff --git a/src/ui/ui_core.cpp b/src/ui/ui_core.cpp index 9ede171..a648f2f 100644 --- a/src/ui/ui_core.cpp +++ b/src/ui/ui_core.cpp @@ -19,20 +19,122 @@ F32 g_ui_scale = 1.0f; // Theme global UI_Theme g_theme = {}; +S32 g_theme_id = 0; -void ui_init_theme() { - g_theme.bg_dark = Clay_Color{ 30, 30, 33, 255}; - g_theme.bg_medium = Clay_Color{ 41, 41, 43, 255}; - g_theme.bg_light = Clay_Color{ 51, 51, 56, 255}; - g_theme.bg_lighter = Clay_Color{ 56, 56, 61, 255}; - g_theme.border = Clay_Color{ 56, 56, 61, 255}; - g_theme.text = Clay_Color{224, 224, 224, 255}; - g_theme.text_dim = Clay_Color{112, 112, 112, 255}; - g_theme.accent = Clay_Color{ 87, 138, 176, 255}; - g_theme.accent_hover = Clay_Color{102, 153, 191, 255}; - g_theme.title_bar = Clay_Color{ 26, 26, 28, 255}; - g_theme.scrollbar_bg = Clay_Color{ 26, 26, 28, 255}; - g_theme.scrollbar_grab= Clay_Color{ 61, 61, 66, 255}; +void ui_set_theme(S32 theme_id) { + g_theme_id = theme_id; + + switch (theme_id) { + default: + case 0: // Dark + g_theme.bg_dark = Clay_Color{ 30, 30, 33, 255}; + g_theme.bg_medium = Clay_Color{ 41, 41, 43, 255}; + g_theme.bg_light = Clay_Color{ 51, 51, 56, 255}; + g_theme.bg_lighter = Clay_Color{ 56, 56, 61, 255}; + g_theme.border = Clay_Color{ 56, 56, 61, 255}; + g_theme.text = Clay_Color{224, 224, 224, 255}; + g_theme.text_dim = Clay_Color{112, 112, 112, 255}; + g_theme.accent = Clay_Color{ 87, 138, 176, 255}; + g_theme.accent_hover = Clay_Color{102, 153, 191, 255}; + g_theme.button_text = Clay_Color{224, 224, 224, 255}; + g_theme.disabled_bg = Clay_Color{ 50, 50, 50, 255}; + g_theme.disabled_text = Clay_Color{100, 100, 100, 255}; + g_theme.title_bar = Clay_Color{ 26, 26, 28, 255}; + g_theme.scrollbar_bg = Clay_Color{ 26, 26, 28, 255}; + g_theme.scrollbar_grab = Clay_Color{ 61, 61, 66, 255}; + g_theme.tab_active_top = Clay_Color{ 70, 120, 160, 255}; + g_theme.tab_active_bottom= Clay_Color{ 30, 55, 80, 255}; + g_theme.tab_inactive = Clay_Color{ 45, 45, 48, 255}; + g_theme.tab_inactive_hover= Clay_Color{ 55, 55, 60, 255}; + g_theme.tab_text = Clay_Color{240, 240, 240, 255}; + break; + + case 1: // Light + g_theme.bg_dark = Clay_Color{210, 210, 215, 255}; + g_theme.bg_medium = Clay_Color{230, 230, 233, 255}; + g_theme.bg_light = Clay_Color{242, 242, 245, 255}; + g_theme.bg_lighter = Clay_Color{248, 248, 250, 255}; + g_theme.border = Clay_Color{190, 190, 195, 255}; + g_theme.text = Clay_Color{ 30, 30, 35, 255}; + g_theme.text_dim = Clay_Color{120, 120, 130, 255}; + g_theme.accent = Clay_Color{ 50, 110, 170, 255}; + g_theme.accent_hover = Clay_Color{ 65, 125, 185, 255}; + g_theme.button_text = Clay_Color{255, 255, 255, 255}; + g_theme.disabled_bg = Clay_Color{195, 195, 200, 255}; + g_theme.disabled_text = Clay_Color{150, 150, 155, 255}; + g_theme.title_bar = Clay_Color{220, 220, 225, 255}; + g_theme.scrollbar_bg = Clay_Color{220, 220, 225, 255}; + g_theme.scrollbar_grab = Clay_Color{180, 180, 185, 255}; + g_theme.tab_active_top = Clay_Color{ 70, 130, 180, 255}; + g_theme.tab_active_bottom= Clay_Color{ 50, 100, 150, 255}; + g_theme.tab_inactive = Clay_Color{218, 218, 222, 255}; + g_theme.tab_inactive_hover= Clay_Color{205, 205, 210, 255}; + g_theme.tab_text = Clay_Color{255, 255, 255, 255}; + break; + } +} + +//////////////////////////////// +// Accent palette + +S32 g_accent_id = 0; + +void ui_set_accent(S32 accent_id) { + g_accent_id = accent_id; + B32 dark = (g_theme_id == 0); + + // Each palette: accent, accent_hover, tab_top, tab_bottom, button_text, tab_text + struct AccentColors { + Clay_Color accent, accent_hover, tab_top, tab_bottom, button_text, tab_text; + }; + + // Dark-mode palettes + static const AccentColors dark_palettes[] = { + // 0: Blue + { {87,138,176,255}, {102,153,191,255}, {70,120,160,255}, {30,55,80,255}, {224,224,224,255}, {240,240,240,255} }, + // 1: Turquoise + { {60,160,155,255}, {75,175,170,255}, {50,140,135,255}, {25,70,68,255}, {224,224,224,255}, {240,240,240,255} }, + // 2: Orange + { {200,130,50,255}, {215,145,65,255}, {190,120,40,255}, {100,60,20,255}, {240,240,240,255}, {240,240,240,255} }, + // 3: Purple + { {130,100,180,255}, {145,115,195,255}, {120,90,170,255}, {60,45,85,255}, {224,224,224,255}, {240,240,240,255} }, + // 4: Pink + { {185,95,140,255}, {200,110,155,255}, {175,85,130,255}, {88,42,65,255}, {240,240,240,255}, {240,240,240,255} }, + // 5: Red + { {190,75,75,255}, {205,90,90,255}, {180,65,65,255}, {90,32,32,255}, {240,240,240,255}, {240,240,240,255} }, + // 6: Green + { {80,155,80,255}, {95,170,95,255}, {70,140,70,255}, {35,70,35,255}, {240,240,240,255}, {240,240,240,255} }, + }; + + // Light-mode palettes (slightly more saturated for contrast on light bg) + static const AccentColors light_palettes[] = { + // 0: Blue + { {50,110,170,255}, {65,125,185,255}, {70,130,180,255}, {50,100,150,255}, {255,255,255,255}, {255,255,255,255} }, + // 1: Turquoise + { {30,140,135,255}, {45,155,150,255}, {40,150,145,255}, {25,115,110,255}, {255,255,255,255}, {255,255,255,255} }, + // 2: Orange + { {190,115,25,255}, {205,130,40,255}, {195,120,30,255}, {155,90,15,255}, {255,255,255,255}, {255,255,255,255} }, + // 3: Purple + { {110,75,170,255}, {125,90,185,255}, {115,80,175,255}, {85,55,140,255}, {255,255,255,255}, {255,255,255,255} }, + // 4: Pink + { {175,70,125,255}, {190,85,140,255}, {180,75,130,255}, {140,50,100,255}, {255,255,255,255}, {255,255,255,255} }, + // 5: Red + { {185,55,55,255}, {200,70,70,255}, {190,60,60,255}, {150,40,40,255}, {255,255,255,255}, {255,255,255,255} }, + // 6: Green + { {55,140,55,255}, {70,155,70,255}, {60,145,60,255}, {40,110,40,255}, {255,255,255,255}, {255,255,255,255} }, + }; + + S32 idx = accent_id; + if (idx < 0) idx = 0; + if (idx > 6) idx = 6; + + const AccentColors &c = dark ? dark_palettes[idx] : light_palettes[idx]; + g_theme.accent = c.accent; + g_theme.accent_hover = c.accent_hover; + g_theme.tab_active_top = c.tab_top; + g_theme.tab_active_bottom = c.tab_bottom; + g_theme.button_text = c.button_text; + g_theme.tab_text = c.tab_text; } //////////////////////////////// diff --git a/src/ui/ui_core.h b/src/ui/ui_core.h index b9dda2d..77cba87 100644 --- a/src/ui/ui_core.h +++ b/src/ui/ui_core.h @@ -58,11 +58,107 @@ struct UI_Theme { Clay_Color text_dim; Clay_Color accent; Clay_Color accent_hover; + Clay_Color button_text; + Clay_Color disabled_bg; + Clay_Color disabled_text; Clay_Color title_bar; Clay_Color scrollbar_bg; Clay_Color scrollbar_grab; + + // Tab bar colors + Clay_Color tab_active_top; + Clay_Color tab_active_bottom; + Clay_Color tab_inactive; + Clay_Color tab_inactive_hover; + Clay_Color tab_text; // Always light — readable on colored tab gradient }; extern UI_Theme g_theme; +extern S32 g_theme_id; -void ui_init_theme(); +// Set theme by index: 0 = Dark, 1 = Light +void ui_set_theme(S32 theme_id); + +// Accent color palette: 0=Blue, 1=Turquoise, 2=Orange, 3=Purple, 4=Pink, 5=Red, 6=Green +extern S32 g_accent_id; +void ui_set_accent(S32 accent_id); + +//////////////////////////////// +// UI scale (Cmd+/Cmd- zoom) + +extern F32 g_ui_scale; + +// Scale a float value (for CLAY_SIZING_FIXED, corner radii, etc.) +static inline float uis(float x) { return x * g_ui_scale; } + +// Scale to uint16_t (for Clay_Padding, childGap, etc.) +static inline uint16_t uip(float x) { return (uint16_t)(x * g_ui_scale + 0.5f); } + +// Scale to uint16_t font size +static inline uint16_t uifs(float x) { return (uint16_t)(x * g_ui_scale + 0.5f); } + +//////////////////////////////// +// Tab styling + +#define TAB_ACTIVE_TOP g_theme.tab_active_top +#define TAB_ACTIVE_BOTTOM g_theme.tab_active_bottom +#define TAB_INACTIVE_BG g_theme.tab_inactive +#define TAB_INACTIVE_HOVER g_theme.tab_inactive_hover +#define TAB_HEIGHT uis(26) +#define TAB_CORNER_RADIUS uis(5) +#define TAB_PADDING_H uip(10) + +//////////////////////////////// +// Custom render types (for gradient rects via CLAY_RENDER_COMMAND_TYPE_CUSTOM) + +enum CustomRenderType { + CUSTOM_RENDER_VGRADIENT = 1, +}; + +struct CustomGradientData { + CustomRenderType type; + Clay_Color top_color; + Clay_Color bottom_color; +}; + +//////////////////////////////// +// Font sizes + +#define FONT_SIZE_NORMAL uifs(15) +#define FONT_SIZE_SMALL uifs(12) + +//////////////////////////////// +// Widget sizing + +#define WIDGET_BUTTON_HEIGHT uis(30) +#define WIDGET_CHECKBOX_HEIGHT uis(28) +#define WIDGET_CHECKBOX_SIZE uis(18) +#define WIDGET_RADIO_OUTER uis(16) +#define WIDGET_RADIO_INNER uis(8) +#define WIDGET_INPUT_HEIGHT uis(30) +#define WIDGET_DROPDOWN_HEIGHT uis(30) +#define WIDGET_DROPDOWN_ITEM_H uis(28) + +//////////////////////////////// +// Corner radii + +#define CORNER_RADIUS_SM uis(3) +#define CORNER_RADIUS_MD uis(6) +#define CORNER_RADIUS_ROUND uis(8) + +//////////////////////////////// +// Modal / window styling + +#define MODAL_OVERLAY_COLOR Clay_Color{ 0, 0, 0, 120} +#define MODAL_WIDTH uis(400) +#define MODAL_CORNER_RADIUS CORNER_RADIUS_MD + +#define WINDOW_CORNER_RADIUS CORNER_RADIUS_MD +#define WINDOW_TITLE_HEIGHT uis(32) + +//////////////////////////////// +// Panel sizing + +#define PANEL_BROWSER_WIDTH uis(200) +#define PANEL_RIGHT_COL_WIDTH uis(250) +#define PANEL_LOG_HEIGHT uis(180) diff --git a/src/ui/ui_theme.h b/src/ui/ui_theme.h deleted file mode 100644 index 184a3de..0000000 --- a/src/ui/ui_theme.h +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once -// ui_theme.h - Theme constants and defines for the UI layer. -// Centralizes colors, sizing, padding, and radius values used across the app. - -#include "clay.h" - -//////////////////////////////// -// UI scale (Cmd+/Cmd- zoom) - -extern F32 g_ui_scale; - -// Scale a float value (for CLAY_SIZING_FIXED, corner radii, etc.) -static inline float uis(float x) { return x * g_ui_scale; } - -// Scale to uint16_t (for Clay_Padding, childGap, etc.) -static inline uint16_t uip(float x) { return (uint16_t)(x * g_ui_scale + 0.5f); } - -// Scale to uint16_t font size -static inline uint16_t uifs(float x) { return (uint16_t)(x * g_ui_scale + 0.5f); } - -//////////////////////////////// -// Tab styling - -#define TAB_ACTIVE_TOP Clay_Color{ 70, 120, 160, 255} -#define TAB_ACTIVE_BOTTOM Clay_Color{ 30, 55, 80, 255} -#define TAB_INACTIVE_BG Clay_Color{ 45, 45, 48, 255} -#define TAB_INACTIVE_HOVER Clay_Color{ 55, 55, 60, 255} -#define TAB_HEIGHT uis(26) -#define TAB_CORNER_RADIUS uis(5) -#define TAB_PADDING_H uip(10) - -//////////////////////////////// -// Custom render types (for gradient rects via CLAY_RENDER_COMMAND_TYPE_CUSTOM) - -enum CustomRenderType { - CUSTOM_RENDER_VGRADIENT = 1, -}; - -struct CustomGradientData { - CustomRenderType type; - Clay_Color top_color; - Clay_Color bottom_color; -}; - -//////////////////////////////// -// Font sizes - -#define FONT_SIZE_NORMAL uifs(15) -#define FONT_SIZE_SMALL uifs(12) - -//////////////////////////////// -// Widget sizing - -#define WIDGET_BUTTON_HEIGHT uis(30) -#define WIDGET_CHECKBOX_HEIGHT uis(28) -#define WIDGET_CHECKBOX_SIZE uis(18) -#define WIDGET_RADIO_OUTER uis(16) -#define WIDGET_RADIO_INNER uis(8) -#define WIDGET_INPUT_HEIGHT uis(30) -#define WIDGET_DROPDOWN_HEIGHT uis(30) -#define WIDGET_DROPDOWN_ITEM_H uis(28) - -//////////////////////////////// -// Corner radii - -#define CORNER_RADIUS_SM uis(3) -#define CORNER_RADIUS_MD uis(6) -#define CORNER_RADIUS_ROUND uis(8) - -//////////////////////////////// -// Modal / window styling - -#define MODAL_OVERLAY_COLOR Clay_Color{ 0, 0, 0, 120} -#define MODAL_WIDTH uis(400) -#define MODAL_CORNER_RADIUS CORNER_RADIUS_MD - -#define WINDOW_CORNER_RADIUS CORNER_RADIUS_MD -#define WINDOW_TITLE_HEIGHT uis(32) - -//////////////////////////////// -// Panel sizing - -#define PANEL_BROWSER_WIDTH uis(200) -#define PANEL_RIGHT_COL_WIDTH uis(250) -#define PANEL_LOG_HEIGHT uis(180) diff --git a/src/ui/ui_widgets.cpp b/src/ui/ui_widgets.cpp index 408d7dd..e460aa7 100644 --- a/src/ui/ui_widgets.cpp +++ b/src/ui/ui_widgets.cpp @@ -43,8 +43,14 @@ void ui_widgets_begin_frame(PlatformInput input) { static Clay_TextElementConfig g_widget_text_config; static Clay_TextElementConfig g_widget_text_config_dim; static Clay_TextElementConfig g_widget_text_config_sel; +static Clay_TextElementConfig g_widget_text_config_btn; +static Clay_TextElementConfig g_widget_text_config_tab; static F32 g_widget_text_configs_scale = 0; +void ui_widgets_theme_changed() { + g_widget_text_configs_scale = 0; +} + static void ensure_widget_text_configs() { if (g_widget_text_configs_scale == g_ui_scale) return; g_widget_text_configs_scale = g_ui_scale; @@ -64,6 +70,18 @@ static void ensure_widget_text_configs() { g_widget_text_config_sel.textColor = Clay_Color{255, 255, 255, 255}; g_widget_text_config_sel.fontSize = FONT_SIZE_NORMAL; g_widget_text_config_sel.wrapMode = CLAY_TEXT_WRAP_NONE; + + // Button text (always readable on accent background) + g_widget_text_config_btn = {}; + g_widget_text_config_btn.textColor = g_theme.button_text; + g_widget_text_config_btn.fontSize = FONT_SIZE_NORMAL; + g_widget_text_config_btn.wrapMode = CLAY_TEXT_WRAP_NONE; + + // Tab text (always light — readable on colored tab gradient) + g_widget_text_config_tab = {}; + g_widget_text_config_tab.textColor = g_theme.tab_text; + g_widget_text_config_tab.fontSize = FONT_SIZE_NORMAL; + g_widget_text_config_tab.wrapMode = CLAY_TEXT_WRAP_NONE; } static Clay_String clay_str(const char *s) { @@ -111,7 +129,7 @@ B32 ui_button(const char *id, const char *text) { .backgroundColor = hovered ? g_theme.accent_hover : g_theme.accent, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS_SM) ) { - CLAY_TEXT(clay_str(text), &g_widget_text_config); + CLAY_TEXT(clay_str(text), &g_widget_text_config_btn); } return (hovered && g_wstate.mouse_clicked) ? 1 : 0; @@ -737,6 +755,9 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected) Clay_Color item_bg = is_item_selected ? g_theme.accent : g_theme.bg_dark; if (item_hovered) item_bg = g_theme.bg_lighter; + Clay_TextElementConfig *item_text = (is_item_selected && !item_hovered) + ? &g_widget_text_config_btn : &g_widget_text_config; + CLAY(item_id, .layout = { .sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(WIDGET_DROPDOWN_ITEM_H) }, @@ -745,7 +766,7 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected) }, .backgroundColor = item_bg ) { - CLAY_TEXT(clay_str(options[i]), &g_widget_text_config); + CLAY_TEXT(clay_str(options[i]), item_text); } if (item_hovered && g_wstate.mouse_clicked) { @@ -892,7 +913,7 @@ S32 ui_modal(const char *id, const char *title, const char *message, .backgroundColor = btn_hovered ? g_theme.accent_hover : g_theme.accent, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS_SM) ) { - CLAY_TEXT(clay_str(buttons[i]), &g_widget_text_config); + CLAY_TEXT(clay_str(buttons[i]), &g_widget_text_config_btn); } if (btn_hovered && g_wstate.mouse_clicked) { @@ -1057,7 +1078,7 @@ B32 ui_window(const char *id, const char *title, B32 *open, .backgroundColor = close_bg, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS_SM) ) { - CLAY_TEXT(CLAY_STRING("x"), &g_widget_text_config); + CLAY_TEXT(CLAY_STRING("x"), &g_widget_text_config_btn); } } @@ -1088,11 +1109,13 @@ B32 ui_window(const char *id, const char *title, B32 *open, //////////////////////////////// // Tab bar -static CustomGradientData g_tab_gradient = { - CUSTOM_RENDER_VGRADIENT, TAB_ACTIVE_TOP, TAB_ACTIVE_BOTTOM -}; +static CustomGradientData g_tab_gradient; S32 ui_tab_bar(const char *id, const char **labels, S32 count, S32 *selected) { + g_tab_gradient.type = CUSTOM_RENDER_VGRADIENT; + g_tab_gradient.top_color = TAB_ACTIVE_TOP; + g_tab_gradient.bottom_color = TAB_ACTIVE_BOTTOM; + Clay_String id_str = clay_str(id); Clay_ElementId row_eid = Clay__HashString(id_str, 0); @@ -1122,7 +1145,7 @@ S32 ui_tab_bar(const char *id, const char **labels, S32 count, S32 *selected) { .cornerRadius = { .topLeft = TAB_CORNER_RADIUS, .topRight = TAB_CORNER_RADIUS, .bottomLeft = 0, .bottomRight = 0 }, .custom = { .customData = &g_tab_gradient }, ) { - CLAY_TEXT(lbl_str, &g_widget_text_config); + CLAY_TEXT(lbl_str, &g_widget_text_config_tab); } } else { Clay_Color bg = hovered ? (Clay_Color)TAB_INACTIVE_HOVER : (Clay_Color)TAB_INACTIVE_BG; diff --git a/src/ui/ui_widgets.h b/src/ui/ui_widgets.h index c2c4164..e67cdb1 100644 --- a/src/ui/ui_widgets.h +++ b/src/ui/ui_widgets.h @@ -78,6 +78,9 @@ void ui_widgets_init(); // Call each frame before building widgets. Pass in the frame's input. void ui_widgets_begin_frame(PlatformInput input); +// Call after changing theme to force text config refresh +void ui_widgets_theme_changed(); + // Reset per-frame text input display buffer allocator (called by begin_frame, but // can also be called manually if needed) void ui_text_input_reset_display_bufs();