// ui_core.cpp - Clay wrapper implementation // CLAY_IMPLEMENTATION must be defined exactly once before including clay.h. // In the unity build, ui_core.h (which includes clay.h) has #pragma once, // so we include clay.h directly here first to get the implementation compiled. #include #define CLAY_IMPLEMENTATION #include "clay.h" #include "ui/ui_core.h" //////////////////////////////// // Theme global UI_Theme g_theme = {}; 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}; } //////////////////////////////// // Clay error handler static void clay_error_handler(Clay_ErrorData error) { char buf[512]; int len = error.errorText.length < 511 ? error.errorText.length : 511; memcpy(buf, error.errorText.chars, len); buf[len] = '\0'; fprintf(stderr, "[Clay Error] %s\n", buf); } //////////////////////////////// // Clay text measurement bridge // Clay calls this; we forward to the app's UI_MeasureTextFn static UI_Context *g_measure_ctx = nullptr; static Clay_Dimensions clay_measure_text(Clay_StringSlice text, Clay_TextElementConfig *config, void *user_data) { UI_Context *ctx = (UI_Context *)user_data; if (!ctx || !ctx->measure_text_fn || text.length == 0) { return Clay_Dimensions{0, (float)config->fontSize}; } Vec2F32 result = ctx->measure_text_fn(text.chars, text.length, (F32)config->fontSize, ctx->measure_text_user_data); return Clay_Dimensions{result.x, result.y}; } //////////////////////////////// // Lifecycle UI_Context *ui_create(F32 viewport_w, F32 viewport_h) { UI_Context *ctx = (UI_Context *)calloc(1, sizeof(UI_Context)); uint32_t min_memory = Clay_MinMemorySize(); ctx->clay_memory = malloc(min_memory); Clay_Arena clay_arena = Clay_CreateArenaWithCapacityAndMemory(min_memory, ctx->clay_memory); Clay_ErrorHandler err_handler = {}; err_handler.errorHandlerFunction = clay_error_handler; err_handler.userData = ctx; ctx->clay_ctx = Clay_Initialize(clay_arena, Clay_Dimensions{viewport_w, viewport_h}, err_handler); Clay_SetMeasureTextFunction(clay_measure_text, ctx); return ctx; } void ui_destroy(UI_Context *ctx) { if (!ctx) return; free(ctx->clay_memory); free(ctx); } void ui_begin_frame(UI_Context *ctx, F32 viewport_w, F32 viewport_h, Vec2F32 mouse_pos, B32 mouse_down, Vec2F32 scroll_delta, F32 dt) { Clay_SetCurrentContext(ctx->clay_ctx); Clay_SetLayoutDimensions(Clay_Dimensions{viewport_w, viewport_h}); Clay_SetPointerState(Clay_Vector2{mouse_pos.x, mouse_pos.y}, mouse_down != 0); Clay_UpdateScrollContainers(true, Clay_Vector2{scroll_delta.x, scroll_delta.y}, dt); Clay_BeginLayout(); } Clay_RenderCommandArray ui_end_frame(UI_Context *ctx) { (void)ctx; return Clay_EndLayout(); } void ui_set_measure_text_fn(UI_Context *ctx, UI_MeasureTextFn fn, void *user_data) { ctx->measure_text_fn = fn; ctx->measure_text_user_data = user_data; }