add proper tab implementation
This commit is contained in:
8
.claude/settings.local.json
Normal file
8
.claude/settings.local.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(cd /c/Users/mta/projects/autosample && ./nob.exe debug 2>&1)",
|
||||||
|
"Bash(cd /c/Users/mta/projects/autosample && rm -f build/*.pdb build/*.obj build/*.ilk && ./nob.exe debug 2>&1)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
118
src/main.cpp
118
src/main.cpp
@@ -6,6 +6,7 @@
|
|||||||
#include "renderer/renderer.h"
|
#include "renderer/renderer.h"
|
||||||
#include "midi/midi.h"
|
#include "midi/midi.h"
|
||||||
#include "ui/ui_core.h"
|
#include "ui/ui_core.h"
|
||||||
|
#include "ui/ui_theme.h"
|
||||||
#include "ui/ui_widgets.h"
|
#include "ui/ui_widgets.h"
|
||||||
|
|
||||||
// [cpp]
|
// [cpp]
|
||||||
@@ -57,6 +58,9 @@ struct AppState {
|
|||||||
LARGE_INTEGER freq;
|
LARGE_INTEGER freq;
|
||||||
LARGE_INTEGER last_time;
|
LARGE_INTEGER last_time;
|
||||||
|
|
||||||
|
// Tab state
|
||||||
|
S32 right_panel_tab; // 0 = Properties, 1 = MIDI Devices
|
||||||
|
|
||||||
// Demo widget state
|
// Demo widget state
|
||||||
B32 demo_checkbox_a;
|
B32 demo_checkbox_a;
|
||||||
B32 demo_checkbox_b;
|
B32 demo_checkbox_b;
|
||||||
@@ -78,18 +82,65 @@ struct AppState {
|
|||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
// Panel builders
|
// Panel builders
|
||||||
|
|
||||||
static void build_panel_title_bar(Clay_ElementId id, Clay_String title) {
|
static CustomGradientData g_tab_gradient = {
|
||||||
CLAY(id,
|
CUSTOM_RENDER_VGRADIENT, TAB_ACTIVE_TOP, TAB_ACTIVE_BOTTOM
|
||||||
|
};
|
||||||
|
|
||||||
|
static S32 build_tab_bar(const char *id, const char **labels, S32 count, S32 *selected) {
|
||||||
|
S32 id_len = (S32)strlen(id);
|
||||||
|
Clay_String id_str = { .isStaticallyAllocated = false, .length = id_len, .chars = id };
|
||||||
|
Clay_ElementId row_eid = Clay__HashString(id_str, 0);
|
||||||
|
|
||||||
|
CLAY(row_eid,
|
||||||
.layout = {
|
.layout = {
|
||||||
.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(24) },
|
.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIT() },
|
||||||
.padding = { 8, 8, 0, 0 },
|
.padding = { 0, 0, 4, 0 },
|
||||||
|
.layoutDirection = CLAY_LEFT_TO_RIGHT,
|
||||||
|
},
|
||||||
|
.backgroundColor = g_theme.bg_medium,
|
||||||
|
.border = { .color = g_theme.border, .width = { .bottom = 1 } },
|
||||||
|
) {
|
||||||
|
for (S32 i = 0; i < count; i++) {
|
||||||
|
Clay_ElementId tab_eid = Clay__HashStringWithOffset(id_str, (uint32_t)i, 0);
|
||||||
|
B32 is_active = (i == *selected);
|
||||||
|
B32 hovered = Clay_PointerOver(tab_eid);
|
||||||
|
|
||||||
|
S32 lbl_len = (S32)strlen(labels[i]);
|
||||||
|
Clay_String lbl_str = { .isStaticallyAllocated = false, .length = lbl_len, .chars = labels[i] };
|
||||||
|
|
||||||
|
if (is_active) {
|
||||||
|
CLAY(tab_eid,
|
||||||
|
.layout = {
|
||||||
|
.sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(TAB_HEIGHT) },
|
||||||
|
.padding = { TAB_PADDING_H, TAB_PADDING_H, 0, 6 },
|
||||||
.childAlignment = { .y = CLAY_ALIGN_Y_CENTER },
|
.childAlignment = { .y = CLAY_ALIGN_Y_CENTER },
|
||||||
},
|
},
|
||||||
.backgroundColor = g_theme.title_bar,
|
.cornerRadius = { .topLeft = TAB_CORNER_RADIUS, .topRight = TAB_CORNER_RADIUS, .bottomLeft = 0, .bottomRight = 0 },
|
||||||
.border = { .color = g_theme.border, .width = { .bottom = 1 } }
|
.custom = { .customData = &g_tab_gradient },
|
||||||
) {
|
) {
|
||||||
CLAY_TEXT(title, &g_text_config_title);
|
CLAY_TEXT(lbl_str, &g_text_config_title);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Clay_Color bg = hovered ? (Clay_Color)TAB_INACTIVE_HOVER : (Clay_Color)TAB_INACTIVE_BG;
|
||||||
|
CLAY(tab_eid,
|
||||||
|
.layout = {
|
||||||
|
.sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(TAB_HEIGHT) },
|
||||||
|
.padding = { TAB_PADDING_H, TAB_PADDING_H, 0, 6 },
|
||||||
|
.childAlignment = { .y = CLAY_ALIGN_Y_CENTER },
|
||||||
|
},
|
||||||
|
.backgroundColor = bg,
|
||||||
|
.cornerRadius = { .topLeft = TAB_CORNER_RADIUS, .topRight = TAB_CORNER_RADIUS, .bottomLeft = 0, .bottomRight = 0 },
|
||||||
|
) {
|
||||||
|
CLAY_TEXT(lbl_str, &g_text_config_title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hovered && g_wstate.mouse_clicked) {
|
||||||
|
*selected = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void build_browser_panel(B32 show) {
|
static void build_browser_panel(B32 show) {
|
||||||
@@ -103,7 +154,11 @@ static void build_browser_panel(B32 show) {
|
|||||||
.backgroundColor = g_theme.bg_medium,
|
.backgroundColor = g_theme.bg_medium,
|
||||||
.border = { .color = g_theme.border, .width = { .right = 1 } }
|
.border = { .color = g_theme.border, .width = { .right = 1 } }
|
||||||
) {
|
) {
|
||||||
build_panel_title_bar(CLAY_ID("BrowserTitleBar"), CLAY_STRING("Browser"));
|
{
|
||||||
|
S32 sel = 0;
|
||||||
|
static const char *browser_tabs[] = { "Browser" };
|
||||||
|
build_tab_bar("BrowserTabRow", browser_tabs, 1, &sel);
|
||||||
|
}
|
||||||
|
|
||||||
CLAY(CLAY_ID("BrowserContent"),
|
CLAY(CLAY_ID("BrowserContent"),
|
||||||
.layout = {
|
.layout = {
|
||||||
@@ -126,7 +181,11 @@ static void build_main_panel(AppState *app) {
|
|||||||
},
|
},
|
||||||
.backgroundColor = g_theme.bg_light
|
.backgroundColor = g_theme.bg_light
|
||||||
) {
|
) {
|
||||||
build_panel_title_bar(CLAY_ID("MainTitleBar"), CLAY_STRING("Main"));
|
{
|
||||||
|
S32 sel = 0;
|
||||||
|
static const char *main_tabs[] = { "Main" };
|
||||||
|
build_tab_bar("MainTabRow", main_tabs, 1, &sel);
|
||||||
|
}
|
||||||
|
|
||||||
CLAY(CLAY_ID("MainContent"),
|
CLAY(CLAY_ID("MainContent"),
|
||||||
.layout = {
|
.layout = {
|
||||||
@@ -258,19 +317,20 @@ static void build_main_panel(AppState *app) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void build_properties_panel(B32 show) {
|
static void build_right_panel(AppState *app) {
|
||||||
if (!show) return;
|
static const char *right_tabs[] = { "Properties", "MIDI Devices" };
|
||||||
|
|
||||||
CLAY(CLAY_ID("PropertiesPanel"),
|
CLAY(CLAY_ID("RightPanel"),
|
||||||
.layout = {
|
.layout = {
|
||||||
.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() },
|
.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() },
|
||||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||||
},
|
},
|
||||||
.backgroundColor = g_theme.bg_medium,
|
.backgroundColor = g_theme.bg_medium
|
||||||
.border = { .color = g_theme.border, .width = { .bottom = 1 } }
|
|
||||||
) {
|
) {
|
||||||
build_panel_title_bar(CLAY_ID("PropertiesTitleBar"), CLAY_STRING("Properties"));
|
build_tab_bar("RightTabs", right_tabs, 2, &app->right_panel_tab);
|
||||||
|
|
||||||
|
if (app->right_panel_tab == 0) {
|
||||||
|
// Properties content
|
||||||
CLAY(CLAY_ID("PropertiesContent"),
|
CLAY(CLAY_ID("PropertiesContent"),
|
||||||
.layout = {
|
.layout = {
|
||||||
.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() },
|
.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() },
|
||||||
@@ -281,21 +341,9 @@ static void build_properties_panel(B32 show) {
|
|||||||
) {
|
) {
|
||||||
CLAY_TEXT(CLAY_STRING("Details"), &g_text_config_normal);
|
CLAY_TEXT(CLAY_STRING("Details"), &g_text_config_normal);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
}
|
// MIDI Devices content
|
||||||
|
MidiEngine *midi = app->midi;
|
||||||
static void build_midi_panel(B32 show, MidiEngine *midi) {
|
|
||||||
if (!show) return;
|
|
||||||
|
|
||||||
CLAY(CLAY_ID("MidiPanel"),
|
|
||||||
.layout = {
|
|
||||||
.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() },
|
|
||||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
|
||||||
},
|
|
||||||
.backgroundColor = g_theme.bg_medium
|
|
||||||
) {
|
|
||||||
build_panel_title_bar(CLAY_ID("MidiTitleBar"), CLAY_STRING("MIDI Devices"));
|
|
||||||
|
|
||||||
CLAY(CLAY_ID("MidiContent"),
|
CLAY(CLAY_ID("MidiContent"),
|
||||||
.layout = {
|
.layout = {
|
||||||
.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() },
|
.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() },
|
||||||
@@ -455,6 +503,7 @@ static void build_midi_panel(B32 show, MidiEngine *midi) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void build_log_panel(B32 show) {
|
static void build_log_panel(B32 show) {
|
||||||
if (!show) return;
|
if (!show) return;
|
||||||
@@ -467,7 +516,11 @@ static void build_log_panel(B32 show) {
|
|||||||
.backgroundColor = g_theme.bg_medium,
|
.backgroundColor = g_theme.bg_medium,
|
||||||
.border = { .color = g_theme.border, .width = { .top = 1 } }
|
.border = { .color = g_theme.border, .width = { .top = 1 } }
|
||||||
) {
|
) {
|
||||||
build_panel_title_bar(CLAY_ID("LogTitleBar"), CLAY_STRING("Log"));
|
{
|
||||||
|
S32 sel = 0;
|
||||||
|
static const char *log_tabs[] = { "Log" };
|
||||||
|
build_tab_bar("LogTabRow", log_tabs, 1, &sel);
|
||||||
|
}
|
||||||
|
|
||||||
CLAY(CLAY_ID("LogContent"),
|
CLAY(CLAY_ID("LogContent"),
|
||||||
.layout = {
|
.layout = {
|
||||||
@@ -535,8 +588,7 @@ static void build_ui(AppState *app) {
|
|||||||
},
|
},
|
||||||
.border = { .color = g_theme.border, .width = { .left = 1 } }
|
.border = { .color = g_theme.border, .width = { .left = 1 } }
|
||||||
) {
|
) {
|
||||||
build_properties_panel(app->show_props);
|
build_right_panel(app);
|
||||||
build_midi_panel(app->show_midi_devices, app->midi);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "renderer/renderer.h"
|
#include "renderer/renderer.h"
|
||||||
#include "ui/ui_core.h"
|
#include "ui/ui_core.h"
|
||||||
|
#include "ui/ui_theme.h"
|
||||||
|
|
||||||
#include <d3d12.h>
|
#include <d3d12.h>
|
||||||
#include <dxgi1_5.h>
|
#include <dxgi1_5.h>
|
||||||
@@ -41,7 +42,7 @@ struct UIVertex {
|
|||||||
float col[4];
|
float col[4];
|
||||||
float rect_min[2];
|
float rect_min[2];
|
||||||
float rect_max[2];
|
float rect_max[2];
|
||||||
float corner_radius;
|
float corner_radii[4]; // TL, TR, BR, BL
|
||||||
float border_thickness;
|
float border_thickness;
|
||||||
float softness;
|
float softness;
|
||||||
float mode; // 0 = rect SDF, 1 = textured
|
float mode; // 0 = rect SDF, 1 = textured
|
||||||
@@ -66,7 +67,7 @@ struct VSInput {
|
|||||||
float4 col : COLOR0;
|
float4 col : COLOR0;
|
||||||
float2 rect_min : TEXCOORD1;
|
float2 rect_min : TEXCOORD1;
|
||||||
float2 rect_max : TEXCOORD2;
|
float2 rect_max : TEXCOORD2;
|
||||||
float corner_radius : TEXCOORD3;
|
float4 corner_radii : TEXCOORD3;
|
||||||
float border_thickness : TEXCOORD4;
|
float border_thickness : TEXCOORD4;
|
||||||
float softness : TEXCOORD5;
|
float softness : TEXCOORD5;
|
||||||
float mode : TEXCOORD6;
|
float mode : TEXCOORD6;
|
||||||
@@ -78,7 +79,7 @@ struct PSInput {
|
|||||||
float4 col : COLOR0;
|
float4 col : COLOR0;
|
||||||
float2 rect_min : TEXCOORD1;
|
float2 rect_min : TEXCOORD1;
|
||||||
float2 rect_max : TEXCOORD2;
|
float2 rect_max : TEXCOORD2;
|
||||||
float corner_radius : TEXCOORD3;
|
float4 corner_radii : TEXCOORD3;
|
||||||
float border_thickness : TEXCOORD4;
|
float border_thickness : TEXCOORD4;
|
||||||
float softness : TEXCOORD5;
|
float softness : TEXCOORD5;
|
||||||
float mode : TEXCOORD6;
|
float mode : TEXCOORD6;
|
||||||
@@ -102,7 +103,7 @@ PSInput VSMain(VSInput input) {
|
|||||||
output.col = input.col;
|
output.col = input.col;
|
||||||
output.rect_min = input.rect_min;
|
output.rect_min = input.rect_min;
|
||||||
output.rect_max = input.rect_max;
|
output.rect_max = input.rect_max;
|
||||||
output.corner_radius = input.corner_radius;
|
output.corner_radii = input.corner_radii;
|
||||||
output.border_thickness = input.border_thickness;
|
output.border_thickness = input.border_thickness;
|
||||||
output.softness = input.softness;
|
output.softness = input.softness;
|
||||||
output.mode = input.mode;
|
output.mode = input.mode;
|
||||||
@@ -126,7 +127,10 @@ float4 PSMain(PSInput input) : SV_TARGET {
|
|||||||
float2 pixel_pos = input.pos.xy;
|
float2 pixel_pos = input.pos.xy;
|
||||||
float2 rect_center = (input.rect_min + input.rect_max) * 0.5;
|
float2 rect_center = (input.rect_min + input.rect_max) * 0.5;
|
||||||
float2 rect_half_size = (input.rect_max - input.rect_min) * 0.5;
|
float2 rect_half_size = (input.rect_max - input.rect_min) * 0.5;
|
||||||
float radius = input.corner_radius;
|
// corner_radii = (TL, TR, BR, BL) — select radius by quadrant
|
||||||
|
float radius = (pixel_pos.x < rect_center.x)
|
||||||
|
? ((pixel_pos.y < rect_center.y) ? input.corner_radii.x : input.corner_radii.w)
|
||||||
|
: ((pixel_pos.y < rect_center.y) ? input.corner_radii.y : input.corner_radii.z);
|
||||||
float softness = max(input.softness, 0.5);
|
float softness = max(input.softness, 0.5);
|
||||||
float dist = rounded_rect_sdf(pixel_pos, rect_center, rect_half_size, radius);
|
float dist = rounded_rect_sdf(pixel_pos, rect_center, rect_half_size, radius);
|
||||||
|
|
||||||
@@ -692,7 +696,7 @@ static bool create_ui_pipeline(Renderer *r) {
|
|||||||
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, offsetof(UIVertex, col), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, offsetof(UIVertex, col), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
||||||
{ "TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(UIVertex, rect_min), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
{ "TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(UIVertex, rect_min), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
||||||
{ "TEXCOORD", 2, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(UIVertex, rect_max), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
{ "TEXCOORD", 2, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(UIVertex, rect_max), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
||||||
{ "TEXCOORD", 3, DXGI_FORMAT_R32_FLOAT, 0, offsetof(UIVertex, corner_radius), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
{ "TEXCOORD", 3, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, offsetof(UIVertex, corner_radii), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
||||||
{ "TEXCOORD", 4, DXGI_FORMAT_R32_FLOAT, 0, offsetof(UIVertex, border_thickness), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
{ "TEXCOORD", 4, DXGI_FORMAT_R32_FLOAT, 0, offsetof(UIVertex, border_thickness), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
||||||
{ "TEXCOORD", 5, DXGI_FORMAT_R32_FLOAT, 0, offsetof(UIVertex, softness), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
{ "TEXCOORD", 5, DXGI_FORMAT_R32_FLOAT, 0, offsetof(UIVertex, softness), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
||||||
{ "TEXCOORD", 6, DXGI_FORMAT_R32_FLOAT, 0, offsetof(UIVertex, mode), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
{ "TEXCOORD", 6, DXGI_FORMAT_R32_FLOAT, 0, offsetof(UIVertex, mode), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
||||||
@@ -809,7 +813,8 @@ static void emit_quad(DrawBatch *batch,
|
|||||||
float u0, float v0, float u1, float v1,
|
float u0, float v0, float u1, float v1,
|
||||||
float cr, float cg, float cb, float ca,
|
float cr, float cg, float cb, float ca,
|
||||||
float rmin_x, float rmin_y, float rmax_x, float rmax_y,
|
float rmin_x, float rmin_y, float rmax_x, float rmax_y,
|
||||||
float corner_radius, float border_thickness, float softness, float mode)
|
float cr_tl, float cr_tr, float cr_br, float cr_bl,
|
||||||
|
float border_thickness, float softness, float mode)
|
||||||
{
|
{
|
||||||
if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES)
|
if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES)
|
||||||
return;
|
return;
|
||||||
@@ -833,7 +838,8 @@ static void emit_quad(DrawBatch *batch,
|
|||||||
v[i].col[0] = cr; v[i].col[1] = cg; v[i].col[2] = cb; v[i].col[3] = ca;
|
v[i].col[0] = cr; v[i].col[1] = cg; v[i].col[2] = cb; v[i].col[3] = ca;
|
||||||
v[i].rect_min[0] = rmin_x; v[i].rect_min[1] = rmin_y;
|
v[i].rect_min[0] = rmin_x; v[i].rect_min[1] = rmin_y;
|
||||||
v[i].rect_max[0] = rmax_x; v[i].rect_max[1] = rmax_y;
|
v[i].rect_max[0] = rmax_x; v[i].rect_max[1] = rmax_y;
|
||||||
v[i].corner_radius = corner_radius;
|
v[i].corner_radii[0] = cr_tl; v[i].corner_radii[1] = cr_tr;
|
||||||
|
v[i].corner_radii[2] = cr_br; v[i].corner_radii[3] = cr_bl;
|
||||||
v[i].border_thickness = border_thickness;
|
v[i].border_thickness = border_thickness;
|
||||||
v[i].softness = softness;
|
v[i].softness = softness;
|
||||||
v[i].mode = mode;
|
v[i].mode = mode;
|
||||||
@@ -850,13 +856,60 @@ static void emit_quad(DrawBatch *batch,
|
|||||||
static void emit_rect(DrawBatch *batch,
|
static void emit_rect(DrawBatch *batch,
|
||||||
float x0, float y0, float x1, float y1,
|
float x0, float y0, float x1, float y1,
|
||||||
float cr, float cg, float cb, float ca,
|
float cr, float cg, float cb, float ca,
|
||||||
float corner_radius, float border_thickness, float softness)
|
float cr_tl, float cr_tr, float cr_br, float cr_bl,
|
||||||
|
float border_thickness, float softness)
|
||||||
{
|
{
|
||||||
emit_quad(batch, x0, y0, x1, y1,
|
emit_quad(batch, x0, y0, x1, y1,
|
||||||
0, 0, 0, 0,
|
0, 0, 0, 0,
|
||||||
cr, cg, cb, ca,
|
cr, cg, cb, ca,
|
||||||
x0, y0, x1, y1,
|
x0, y0, x1, y1,
|
||||||
corner_radius, border_thickness, softness, 0.0f);
|
cr_tl, cr_tr, cr_br, cr_bl,
|
||||||
|
border_thickness, softness, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void emit_rect_vgradient(DrawBatch *batch,
|
||||||
|
float x0, float y0, float x1, float y1,
|
||||||
|
float tr, float tg, float tb, float ta,
|
||||||
|
float br, float bg, float bb_, float ba,
|
||||||
|
float cr_tl, float cr_tr, float cr_br, float cr_bl,
|
||||||
|
float softness)
|
||||||
|
{
|
||||||
|
if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES)
|
||||||
|
return;
|
||||||
|
|
||||||
|
U32 base = batch->vertex_count;
|
||||||
|
UIVertex *v = &batch->vertices[base];
|
||||||
|
|
||||||
|
float pad = softness + 1.0f;
|
||||||
|
float px0 = x0 - pad, py0 = y0 - pad, px1 = x1 + pad, py1 = y1 + pad;
|
||||||
|
|
||||||
|
v[0].pos[0] = px0; v[0].pos[1] = py0; v[0].uv[0] = 0; v[0].uv[1] = 0;
|
||||||
|
v[1].pos[0] = px1; v[1].pos[1] = py0; v[1].uv[0] = 0; v[1].uv[1] = 0;
|
||||||
|
v[2].pos[0] = px1; v[2].pos[1] = py1; v[2].uv[0] = 0; v[2].uv[1] = 0;
|
||||||
|
v[3].pos[0] = px0; v[3].pos[1] = py1; v[3].uv[0] = 0; v[3].uv[1] = 0;
|
||||||
|
|
||||||
|
// Top vertices get top color, bottom vertices get bottom color
|
||||||
|
v[0].col[0] = tr; v[0].col[1] = tg; v[0].col[2] = tb; v[0].col[3] = ta;
|
||||||
|
v[1].col[0] = tr; v[1].col[1] = tg; v[1].col[2] = tb; v[1].col[3] = ta;
|
||||||
|
v[2].col[0] = br; v[2].col[1] = bg; v[2].col[2] = bb_; v[2].col[3] = ba;
|
||||||
|
v[3].col[0] = br; v[3].col[1] = bg; v[3].col[2] = bb_; v[3].col[3] = ba;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
v[i].rect_min[0] = x0; v[i].rect_min[1] = y0;
|
||||||
|
v[i].rect_max[0] = x1; v[i].rect_max[1] = y1;
|
||||||
|
v[i].corner_radii[0] = cr_tl; v[i].corner_radii[1] = cr_tr;
|
||||||
|
v[i].corner_radii[2] = cr_br; v[i].corner_radii[3] = cr_bl;
|
||||||
|
v[i].border_thickness = 0;
|
||||||
|
v[i].softness = softness;
|
||||||
|
v[i].mode = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 *idx = &batch->indices[batch->index_count];
|
||||||
|
idx[0] = base; idx[1] = base + 1; idx[2] = base + 2;
|
||||||
|
idx[3] = base; idx[4] = base + 2; idx[5] = base + 3;
|
||||||
|
|
||||||
|
batch->vertex_count += 4;
|
||||||
|
batch->index_count += 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void emit_text_glyphs(DrawBatch *batch, Renderer *r,
|
static void emit_text_glyphs(DrawBatch *batch, Renderer *r,
|
||||||
@@ -901,7 +954,8 @@ static void emit_text_glyphs(DrawBatch *batch, Renderer *r,
|
|||||||
g->u0, g->v0, g->u1, g->v1,
|
g->u0, g->v0, g->u1, g->v1,
|
||||||
cr, cg, cb, ca,
|
cr, cg, cb, ca,
|
||||||
0, 0, 0, 0,
|
0, 0, 0, 0,
|
||||||
0, 0, 0, 1.0f);
|
0, 0, 0, 0,
|
||||||
|
0, 0, 1.0f);
|
||||||
|
|
||||||
x += g->x_advance * scale;
|
x += g->x_advance * scale;
|
||||||
}
|
}
|
||||||
@@ -1075,13 +1129,12 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
|||||||
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
|
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
|
||||||
Clay_RectangleRenderData *rect = &cmd->renderData.rectangle;
|
Clay_RectangleRenderData *rect = &cmd->renderData.rectangle;
|
||||||
Clay_Color c = rect->backgroundColor;
|
Clay_Color c = rect->backgroundColor;
|
||||||
// Use average corner radius for SDF
|
|
||||||
float cr = (rect->cornerRadius.topLeft + rect->cornerRadius.topRight +
|
|
||||||
rect->cornerRadius.bottomLeft + rect->cornerRadius.bottomRight) * 0.25f;
|
|
||||||
emit_rect(&batch,
|
emit_rect(&batch,
|
||||||
bb.x, bb.y, bb.x + bb.width, bb.y + bb.height,
|
bb.x, bb.y, bb.x + bb.width, bb.y + bb.height,
|
||||||
c.r / 255.f, c.g / 255.f, c.b / 255.f, c.a / 255.f,
|
c.r / 255.f, c.g / 255.f, c.b / 255.f, c.a / 255.f,
|
||||||
cr, 0, 1.0f);
|
rect->cornerRadius.topLeft, rect->cornerRadius.topRight,
|
||||||
|
rect->cornerRadius.bottomRight, rect->cornerRadius.bottomLeft,
|
||||||
|
0, 1.0f);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case CLAY_RENDER_COMMAND_TYPE_BORDER: {
|
case CLAY_RENDER_COMMAND_TYPE_BORDER: {
|
||||||
@@ -1095,19 +1148,19 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
|||||||
// Draw individual border sides as thin rects
|
// Draw individual border sides as thin rects
|
||||||
if (border->width.top > 0) {
|
if (border->width.top > 0) {
|
||||||
emit_rect(&batch, bb.x, bb.y, bb.x + bb.width, bb.y + border->width.top,
|
emit_rect(&batch, bb.x, bb.y, bb.x + bb.width, bb.y + border->width.top,
|
||||||
cr_norm, cg_norm, cb_norm, ca_norm, 0, 0, 1.0f);
|
cr_norm, cg_norm, cb_norm, ca_norm, 0, 0, 0, 0, 0, 1.0f);
|
||||||
}
|
}
|
||||||
if (border->width.bottom > 0) {
|
if (border->width.bottom > 0) {
|
||||||
emit_rect(&batch, bb.x, bb.y + bb.height - border->width.bottom, bb.x + bb.width, bb.y + bb.height,
|
emit_rect(&batch, bb.x, bb.y + bb.height - border->width.bottom, bb.x + bb.width, bb.y + bb.height,
|
||||||
cr_norm, cg_norm, cb_norm, ca_norm, 0, 0, 1.0f);
|
cr_norm, cg_norm, cb_norm, ca_norm, 0, 0, 0, 0, 0, 1.0f);
|
||||||
}
|
}
|
||||||
if (border->width.left > 0) {
|
if (border->width.left > 0) {
|
||||||
emit_rect(&batch, bb.x, bb.y, bb.x + border->width.left, bb.y + bb.height,
|
emit_rect(&batch, bb.x, bb.y, bb.x + border->width.left, bb.y + bb.height,
|
||||||
cr_norm, cg_norm, cb_norm, ca_norm, 0, 0, 1.0f);
|
cr_norm, cg_norm, cb_norm, ca_norm, 0, 0, 0, 0, 0, 1.0f);
|
||||||
}
|
}
|
||||||
if (border->width.right > 0) {
|
if (border->width.right > 0) {
|
||||||
emit_rect(&batch, bb.x + bb.width - border->width.right, bb.y, bb.x + bb.width, bb.y + bb.height,
|
emit_rect(&batch, bb.x + bb.width - border->width.right, bb.y, bb.x + bb.width, bb.y + bb.height,
|
||||||
cr_norm, cg_norm, cb_norm, ca_norm, 0, 0, 1.0f);
|
cr_norm, cg_norm, cb_norm, ca_norm, 0, 0, 0, 0, 0, 1.0f);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
@@ -1137,8 +1190,26 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
|||||||
r->command_list->RSSetScissorRects(1, &full_scissor);
|
r->command_list->RSSetScissorRects(1, &full_scissor);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case CLAY_RENDER_COMMAND_TYPE_CUSTOM: {
|
||||||
|
Clay_CustomRenderData *custom = &cmd->renderData.custom;
|
||||||
|
if (custom->customData) {
|
||||||
|
CustomRenderType type = *(CustomRenderType *)custom->customData;
|
||||||
|
if (type == CUSTOM_RENDER_VGRADIENT) {
|
||||||
|
CustomGradientData *grad = (CustomGradientData *)custom->customData;
|
||||||
|
Clay_Color tc = grad->top_color;
|
||||||
|
Clay_Color bc = grad->bottom_color;
|
||||||
|
emit_rect_vgradient(&batch,
|
||||||
|
bb.x, bb.y, bb.x + bb.width, bb.y + bb.height,
|
||||||
|
tc.r / 255.f, tc.g / 255.f, tc.b / 255.f, tc.a / 255.f,
|
||||||
|
bc.r / 255.f, bc.g / 255.f, bc.b / 255.f, bc.a / 255.f,
|
||||||
|
custom->cornerRadius.topLeft, custom->cornerRadius.topRight,
|
||||||
|
custom->cornerRadius.bottomRight, custom->cornerRadius.bottomLeft,
|
||||||
|
1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
case CLAY_RENDER_COMMAND_TYPE_IMAGE:
|
case CLAY_RENDER_COMMAND_TYPE_IMAGE:
|
||||||
case CLAY_RENDER_COMMAND_TYPE_CUSTOM:
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
71
src/ui/ui_theme.h
Normal file
71
src/ui/ui_theme.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#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"
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// 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 26
|
||||||
|
#define TAB_CORNER_RADIUS 5
|
||||||
|
#define TAB_PADDING_H 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 15
|
||||||
|
#define FONT_SIZE_SMALL 12
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// Widget sizing
|
||||||
|
|
||||||
|
#define WIDGET_BUTTON_HEIGHT 30
|
||||||
|
#define WIDGET_CHECKBOX_HEIGHT 28
|
||||||
|
#define WIDGET_CHECKBOX_SIZE 18
|
||||||
|
#define WIDGET_RADIO_OUTER 16
|
||||||
|
#define WIDGET_RADIO_INNER 8
|
||||||
|
#define WIDGET_INPUT_HEIGHT 30
|
||||||
|
#define WIDGET_DROPDOWN_HEIGHT 30
|
||||||
|
#define WIDGET_DROPDOWN_ITEM_H 28
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// Corner radii
|
||||||
|
|
||||||
|
#define CORNER_RADIUS_SM 3
|
||||||
|
#define CORNER_RADIUS_MD 6
|
||||||
|
#define CORNER_RADIUS_ROUND 8
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// Modal / window styling
|
||||||
|
|
||||||
|
#define MODAL_OVERLAY_COLOR Clay_Color{ 0, 0, 0, 120}
|
||||||
|
#define MODAL_WIDTH 400
|
||||||
|
#define MODAL_CORNER_RADIUS CORNER_RADIUS_MD
|
||||||
|
|
||||||
|
#define WINDOW_CORNER_RADIUS CORNER_RADIUS_MD
|
||||||
|
#define WINDOW_TITLE_HEIGHT 32
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// Panel sizing
|
||||||
|
|
||||||
|
#define PANEL_BROWSER_WIDTH 200
|
||||||
|
#define PANEL_RIGHT_COL_WIDTH 250
|
||||||
|
#define PANEL_LOG_HEIGHT 180
|
||||||
Reference in New Issue
Block a user