unified ui padding

This commit is contained in:
jacob 2025-11-04 00:19:46 -06:00
parent a3f6191247
commit b1f285f3f1
9 changed files with 457 additions and 160 deletions

View File

@ -378,6 +378,7 @@ void __asan_unpoison_memory_region(void const volatile *add, size_t);
#define Color_Yellow Rgb32(0xFF, 0xFF, 0x00) #define Color_Yellow Rgb32(0xFF, 0xFF, 0x00)
#define Color_Orange Rgb32(0xFF, 0xA5, 0x00) #define Color_Orange Rgb32(0xFF, 0xA5, 0x00)
#define Color_Purple Rgb32(0xFF, 0x00, 0XFF) #define Color_Purple Rgb32(0xFF, 0x00, 0XFF)
#define Color_Cyan Rgb32(0x00, 0xFF, 0XFF)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Intrinsic headers //~ Intrinsic headers

View File

@ -72,8 +72,6 @@
#define GPU_DEBUG 1 #define GPU_DEBUG 1
#define GPU_DEBUG_VALIDATION 0 #define GPU_DEBUG_VALIDATION 0
#define UI_DEBUG 0
/* If virtual fibers are enabled, each fiber will get its own OS thread, /* If virtual fibers are enabled, each fiber will get its own OS thread,
* and fiber suspend/resume will be emulated using OS thread primitives. * and fiber suspend/resume will be emulated using OS thread primitives.
* This is slow but allows for easier debugging in tricky cases * This is slow but allows for easier debugging in tricky cases

View File

@ -394,7 +394,13 @@ void PP_UpdateUser(void)
g->screen_size.y = MaxI32(g->screen_size.y, 1); g->screen_size.y = MaxI32(g->screen_size.y, 1);
//- Begin UI //- Begin UI
UI_BeginBuild(controller_events); UI_BuildFlag ui_build_flags = 0;
if (g->ui_debug)
{
ui_build_flags |= UI_BuildFlag_Debug;
}
UI_BeginBuild(controller_events, ui_build_flags);
UI_Push(LayoutAxis, Axis_Y); UI_Push(LayoutAxis, Axis_Y);
if (window_frame.forced_top) if (window_frame.forced_top)
{ {
@ -676,6 +682,11 @@ void PP_UpdateUser(void)
g->debug_camera = !g->debug_camera; g->debug_camera = !g->debug_camera;
} }
if (g->bind_states[PP_BindKind_DebugUi].num_presses > 0)
{
g->ui_debug = !g->ui_debug;
}
if (g->bind_states[PP_BindKind_DebugLister].num_presses > 0) if (g->bind_states[PP_BindKind_DebugLister].num_presses > 0)
{ {
g->lister_active = !g->lister_active; g->lister_active = !g->lister_active;
@ -1953,22 +1964,28 @@ void PP_UpdateUser(void)
UI_Push(BorderColor, Rgba32F(0.2, 0.2, 0.2, 1)); UI_Push(BorderColor, Rgba32F(0.2, 0.2, 0.2, 1));
UI_Push(Border, 2); UI_Push(Border, 2);
UI_SetNext(FloatingPos, g->lister_pos); // UI_Size padding = UI_FILL(1, 0);
UI_SetNext(LayoutAxis, Axis_Y); // UI_Size padding = UI_PIX(10, 0);
UI_SetNext(Rounding, UI_RPIX(10)); UI_Push(Width, UI_PIX(size.x, 0));
UI_Push(Height, UI_PIX(size.y, 0));
UI_Push(LayoutAxis, Axis_Y);
UI_Push(FloatingPos, g->lister_pos);
UI_SetNext(Flags, UI_BoxFlag_Floating); UI_SetNext(Flags, UI_BoxFlag_Floating);
UI_SetNext(Width, UI_PIX(size.x, 0)); UI_SetNext(Rounding, UI_RPIX(50));
UI_SetNext(Height, UI_PIX(size.y, 0));
UI_Box *lister_box = UI_BuildBox(Lit("lister")); UI_Box *lister_box = UI_BuildBox(Lit("lister"));
UI_PushCP(lister_box); UI_Box *lister_pad = lister_box;
UI_PushCP(lister_pad);
{ {
UI_Push(BackgroundColor, 0);
// UI_BuildSpacer(UI_PIX(10, 0)); // UI_BuildSpacer(UI_PIX(10, 0));
UI_Push(BackgroundColor, 0);
UI_Push(LayoutAxis, Axis_X);
UI_Push(Width, UI_FILL(1, 0));
UI_Push(Height, UI_FNT(5, 0));
{ {
UI_SetNext(LayoutAxis, Axis_X); UI_Push(BackgroundColor, Color_Orange);
UI_SetNext(Width, UI_FILL(1, 0)); UI_Push(Border, 2);
UI_SetNext(Height, UI_FNT(1, 0));
// UI_SetNext(Border, 0);
UI_Box *title_bar = UI_BuildBox(Zstr); UI_Box *title_bar = UI_BuildBox(Zstr);
UI_PushCP(title_bar); UI_PushCP(title_bar);
{ {
@ -1976,19 +1993,27 @@ void PP_UpdateUser(void)
UI_Push(Padding, 0); UI_Push(Padding, 0);
UI_Push(BackgroundColor, 0); UI_Push(BackgroundColor, 0);
UI_Push(Border, 0); UI_Push(Border, 0);
{
UI_Box *left_box = UI_BuildBox(Zstr); UI_Box *left_box = UI_BuildBox(Zstr);
} // UI_SetNext(LeftPadding, UI_PIX(10, 1));
// UI_SetNext(Padding, UI_PAD(UI_FILL(1, 0)));
UI_Box *center_box = UI_BuildBox(Zstr);
UI_Box *right_box = UI_BuildBox(Zstr);
// UI_Pad title_padding = UI_PADEX(.left = UI_PIX(50, 0), .right = UI_PIX(50, 0), .bottom = UI_PIX(10, 0));
// UI_Pad title_padding = UI_PAD(UI_FILL(1, 0));
// UI_Box *center_pad = UI_BuildPad(center_box, title_padding);
// UI_Box *center_pad = center_box;
UI_PushCP(center_box);
{ {
UI_SetNext(Text, Lit("Title")); UI_SetNext(Text, Lit("Title"));
UI_SetNext(Flags, UI_BoxFlag_DrawText); UI_SetNext(Flags, UI_BoxFlag_DrawText);
UI_SetNext(Width, UI_FIT(1)); // UI_SetNext(Padding, UI_PADEX(.left = UI_PIX(100, 0), .right = UI_PIX(50, 0), .bottom = UI_PIX(10, 0)));
// UI_SetNext(Padding, UI_PADEX(.left = UI_PIX(1, 0)));
UI_SetNext(Padding, UI_PADALL(UI_FILL(1, 0)));
UI_Box *title_box = UI_BuildBox(Zstr); UI_Box *title_box = UI_BuildBox(Zstr);
} }
{ UI_PopCP();
UI_Box *right_box = UI_BuildBox(Zstr);
}
} }
UI_PopCP(); UI_PopCP();
} }
@ -2017,7 +2042,7 @@ void PP_UpdateUser(void)
{ {
UI_Push(BackgroundColor, 0); UI_Push(BackgroundColor, 0);
UI_Push(BorderColor, 0); UI_Push(BorderColor, 0);
UI_Push(Padding, 2); // UI_Push(Padding, UI_PIX(5, 0));
UI_BuildLabelF("Blended world entities: %F/%F", FmtUint(g->ss_blended->num_ents_allocated), FmtUint(g->ss_blended->num_ents_reserved)); UI_BuildLabelF("Blended world entities: %F/%F", FmtUint(g->ss_blended->num_ents_allocated), FmtUint(g->ss_blended->num_ents_reserved));
UI_BuildLabelF("Blended world tick: %F", FmtUint(g->ss_blended->tick)); UI_BuildLabelF("Blended world tick: %F", FmtUint(g->ss_blended->tick));

View File

@ -33,6 +33,7 @@ Enum(PP_BindKind)
PP_BindKind_DebugTeleport, PP_BindKind_DebugTeleport,
PP_BindKind_DebugExplode, PP_BindKind_DebugExplode,
PP_BindKind_DebugToggleTopmost, PP_BindKind_DebugToggleTopmost,
PP_BindKind_DebugUi,
PP_BindKind_FullscreenMod, PP_BindKind_FullscreenMod,
PP_BindKind_Fullscreen, PP_BindKind_Fullscreen,
PP_BindKind_ZoomIn, PP_BindKind_ZoomIn,
@ -83,6 +84,7 @@ Global Readonly PP_BindKind g_binds[Btn_Count] = {
[Btn_F3] = PP_BindKind_DebugDraw, [Btn_F3] = PP_BindKind_DebugDraw,
[Btn_Tab] = PP_BindKind_DebugLister, [Btn_Tab] = PP_BindKind_DebugLister,
[Btn_F4] = PP_BindKind_DebugToggleTopmost, [Btn_F4] = PP_BindKind_DebugToggleTopmost,
[Btn_F5] = PP_BindKind_DebugUi,
[Btn_GraveAccent] = PP_BindKind_DebugConsole, [Btn_GraveAccent] = PP_BindKind_DebugConsole,
[Btn_Alt] = PP_BindKind_FullscreenMod, [Btn_Alt] = PP_BindKind_FullscreenMod,
[Btn_Enter] = PP_BindKind_Fullscreen, [Btn_Enter] = PP_BindKind_Fullscreen,
@ -245,6 +247,8 @@ Struct(PP_SharedUserState)
//- Debug ui //- Debug ui
b32 ui_debug;
PP_EntKey debug_following; PP_EntKey debug_following;
Vec2 debug_camera_pan_start; Vec2 debug_camera_pan_start;
b32 debug_camera_panning; b32 debug_camera_panning;

View File

@ -124,9 +124,9 @@ UI_Box *PP_BuildDebugConsole(b32 minimized)
UI_Box *log_box = UI_BuildBox(Zstr); UI_Box *log_box = UI_BuildBox(Zstr);
UI_PushCP(log_box); UI_PushCP(log_box);
{ {
UI_SetNext(Padding, UI_PADALL(UI_FNT(0.25, 0)));
UI_SetNext(BackgroundColor, 0); UI_SetNext(BackgroundColor, 0);
UI_SetNext(Border, 0); UI_SetNext(Border, 0);
UI_SetNext(Padding, 6);
UI_SetNext(Text, text); UI_SetNext(Text, text);
UI_SetNext(Width, UI_FILL(1, 0)); UI_SetNext(Width, UI_FILL(1, 0));
UI_SetNext(Height, UI_FIT(1)); UI_SetNext(Height, UI_FIT(1));

View File

@ -1,6 +1,5 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Label widget //~ Label helpers
UI_Box *UI_BuildLabel(String text) UI_Box *UI_BuildLabel(String text)
{ {
UI_SetNext(Width, UI_FIT(0)); UI_SetNext(Width, UI_FIT(0));
@ -27,19 +26,32 @@ UI_Box *UI_BuildLabelF_(char *fmt_cstr, ...)
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Spacing widgets //~ Spacing helpers
UI_Box *UI_BuildSpacer(UI_Size size) UI_Box *UI_BuildSpacer(UI_Size size)
{
UI_Box *box = 0;
UI_Size old_width = UI_PeekTop(Width);
UI_Size old_height = UI_PeekTop(Height);
UI_Box *old_parent = UI_PeekTop(Parent);
UI_PushStack();
{ {
UI_SetNext(Tint, 0); UI_SetNext(Tint, 0);
if (UI_PeekTop(Parent)->layout_axis == Axis_X) UI_SetNext(Parent, old_parent);
if (old_parent->layout_axis == Axis_X)
{ {
UI_SetNext(Width, size); UI_SetNext(Width, size);
UI_SetNext(Height, old_height);
} }
else else
{ {
UI_SetNext(Width, old_width);
UI_SetNext(Height, size); UI_SetNext(Height, size);
} }
UI_Box *box = UI_BuildBox(Zstr); box = UI_BuildBox(Zstr);
}
UI_PopStack();
return box; return box;
} }

View File

@ -1,11 +1,11 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Label widget //~ Label helpers
UI_Box *UI_BuildLabel(String text); UI_Box *UI_BuildLabel(String text);
#define UI_BuildLabelF(fmt_cstr, ...) UI_BuildLabelF_(fmt_cstr, __VA_ARGS__, FmtEnd) #define UI_BuildLabelF(fmt_cstr, ...) UI_BuildLabelF_(fmt_cstr, __VA_ARGS__, FmtEnd)
UI_Box *UI_BuildLabelF_(char *fmt_cstr, ...); UI_Box *UI_BuildLabelF_(char *fmt_cstr, ...);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Spacing widgets //~ Spacing helpers
UI_Box *UI_BuildSpacer(UI_Size size); UI_Box *UI_BuildSpacer(UI_Size size);

View File

@ -65,6 +65,7 @@ UI_Box *UI_FrontBoxFromKey(UI_Key key)
void UI_PushCP(UI_Box *parent) void UI_PushCP(UI_Box *parent)
{ {
UI_SharedState *g = &UI_shared_state; UI_SharedState *g = &UI_shared_state;
UI_Stack *stack = g->top_stack;
UI_Checkpoint *cp = g->first_free_checkpoint; UI_Checkpoint *cp = g->first_free_checkpoint;
if (cp) if (cp)
{ {
@ -75,9 +76,9 @@ void UI_PushCP(UI_Box *parent)
{ {
cp = PushStruct(g->build_arena, UI_Checkpoint); cp = PushStruct(g->build_arena, UI_Checkpoint);
} }
cp->next = g->top_checkpoint; cp->next = stack->top_checkpoint;
cp->v = g->top_checkpoint->v + 1; cp->v = stack->top_checkpoint->v + 1;
g->top_checkpoint = cp; stack->top_checkpoint = cp;
if (parent != 0) if (parent != 0)
{ {
UI_Push(Parent, parent); UI_Push(Parent, parent);
@ -87,22 +88,23 @@ void UI_PushCP(UI_Box *parent)
void UI_PopCP(void) void UI_PopCP(void)
{ {
UI_SharedState *g = &UI_shared_state; UI_SharedState *g = &UI_shared_state;
UI_Checkpoint *cp = g->top_checkpoint; UI_Stack *stack = g->top_stack;
UI_Checkpoint *cp = stack->top_checkpoint;
u64 v = cp->v; u64 v = cp->v;
/* Pop styles */ /* Pop styles */
for (UI_StyleKind kind = UI_StyleKind_None; kind < UI_StyleKind_Count; ++kind) for (UI_StyleKind kind = UI_StyleKind_None; kind < UI_StyleKind_Count; ++kind)
{ {
UI_StyleNode *n = g->style_tops[kind]; UI_StyleNode *n = stack->style_tops[kind];
while (n && n->checkpoint >= v) while (n && n->checkpoint >= v)
{ {
UI_StyleNode *next = n->next; UI_StyleNode *next = n->next;
n->next = g->first_free_style_node; n->next = g->first_free_style_node;
g->first_free_style_node = n; g->first_free_style_node = n;
g->style_tops[kind] = next; stack->style_tops[kind] = next;
n = next; n = next;
} }
} }
g->top_checkpoint = cp->next; stack->top_checkpoint = cp->next;
cp->next = g->first_free_checkpoint; cp->next = g->first_free_checkpoint;
g->first_free_checkpoint = cp; g->first_free_checkpoint = cp;
} }
@ -110,17 +112,124 @@ void UI_PopCP(void)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Style stack helpers //~ Style stack helpers
UI_StyleNode *UI_PushStyleNode(UI_Style desc) void UI_PushStack(void)
{ {
UI_SharedState *g = &UI_shared_state; UI_SharedState *g = &UI_shared_state;
UI_StyleNode *n = g->style_tops[desc.kind]; UI_Stack *stack = PushStruct(g->build_arena, UI_Stack);
b32 cancel = 0;
/* Init checkpoint */
{
UI_Checkpoint *cp = g->first_free_checkpoint;
if (cp)
{
g->first_free_checkpoint = cp->next;
ZeroStruct(cp);
}
else
{
cp = PushStruct(g->build_arena, UI_Checkpoint);
}
stack->top_checkpoint = cp;
}
/* Init styles */
{
for (UI_StyleKind kind = UI_StyleKind_None; kind < UI_StyleKind_Count; ++kind)
{
UI_StyleNode *n = g->first_free_style_node;
if (n)
{
g->first_free_style_node = n->next;
ZeroStruct(n);
}
else
{
n = PushStruct(g->build_arena, UI_StyleNode);
}
n->style.kind = kind;
stack->style_tops[kind] = n;
}
stack->style_tops[UI_StyleKind_Parent]->style.Parent = g->root_box;
stack->style_tops[UI_StyleKind_Width]->style.Width = UI_FILL(1, 0);
stack->style_tops[UI_StyleKind_Height]->style.Height = UI_FILL(1, 0);
stack->style_tops[UI_StyleKind_Font]->style.Font = UI_GetDefaultFontResource();
stack->style_tops[UI_StyleKind_FontSize]->style.FontSize = 16.0f;
stack->style_tops[UI_StyleKind_Tint]->style.Tint = 0xFFFFFFFF;
stack->style_tops[UI_StyleKind_Tag]->style.Tag.name = Lit("root");
stack->style_tops[UI_StyleKind_Tag]->style.Tag.hash = HashFnv64(Fnv64Basis, stack->style_tops[UI_StyleKind_Tag]->style.Tag.name);
}
stack->next = g->top_stack;
g->top_stack = stack;
}
void UI_PopStack(void)
{
UI_SharedState *g = &UI_shared_state;
UI_Stack *stack = g->top_stack;
/* Free checkpoints */
{
UI_Checkpoint *cp = stack->top_checkpoint;
while (cp)
{
UI_Checkpoint *next = cp->next;
cp->next = g->first_free_checkpoint;
g->first_free_checkpoint = cp;
cp = next;
}
}
/* Free style nodes */
for (UI_StyleKind kind = UI_StyleKind_None; kind < UI_StyleKind_Count; ++kind)
{
UI_StyleNode *n = stack->style_tops[kind];
while (n)
{
UI_StyleNode *next = n->next;
n->next = g->first_free_style_node;
g->first_free_style_node = n;
n = next;
}
}
g->top_stack = stack->next;
stack->next = g->first_free_stack;
g->first_free_stack = stack;
}
void UI_PushStyle(UI_Style desc)
{
UI_SharedState *g = &UI_shared_state;
UI_Stack *stack = g->top_stack;
if (desc.kind >= UI_StyleKind_BeginVirtualStyles_)
{
switch(desc.kind)
{
default: break;
case UI_StyleKind_Padding:
{
UI_Pad pad = desc.Padding;
UI_PushCopy(TopPadding, desc, pad.top);
UI_PushCopy(BottomPadding, desc, pad.bottom);
UI_PushCopy(LeftPadding, desc, pad.left);
UI_PushCopy(RightPadding, desc, pad.right);
} break;
}
}
else
{
UI_StyleNode *n = stack->style_tops[desc.kind];
if (!n->style.forced) if (!n->style.forced)
{ {
{ {
if (n->style.pop_when_used) if (n->style.pop_when_used)
{ {
UI_StyleNode *next = n->next;
ZeroStruct(n); ZeroStruct(n);
n->next = next;
} }
else else
{ {
@ -134,10 +243,11 @@ UI_StyleNode *UI_PushStyleNode(UI_Style desc)
{ {
n = PushStruct(g->build_arena, UI_StyleNode); n = PushStruct(g->build_arena, UI_StyleNode);
} }
n->next = g->style_tops[desc.kind]; n->next = stack->style_tops[desc.kind];
stack->style_tops[desc.kind] = n;
} }
n->style = desc; n->style = desc;
n->checkpoint = g->top_checkpoint->v; n->checkpoint = stack->top_checkpoint->v;
} }
/* Initialize style data from desc */ /* Initialize style data from desc */
@ -155,56 +265,168 @@ UI_StyleNode *UI_PushStyleNode(UI_Style desc)
n->style.Tag.name = PushString(g->build_arena, desc.Tag.name); n->style.Tag.name = PushString(g->build_arena, desc.Tag.name);
if (n->style.Tag.hash == 0) if (n->style.Tag.hash == 0)
{ {
n->style.Tag.hash = HashFnv64(g->style_tops[desc.kind]->style.Tag.hash, n->style.Tag.name); n->style.Tag.hash = HashFnv64(stack->style_tops[desc.kind]->style.Tag.hash, n->style.Tag.name);
} }
} break; } break;
} }
} }
g->style_tops[desc.kind] = n; }
return n;
} }
UI_Style UI_PopStyleNode(UI_StyleKind kind) UI_Style UI_PopStyle(UI_StyleKind kind)
{ {
UI_SharedState *g = &UI_shared_state; UI_SharedState *g = &UI_shared_state;
UI_StyleNode *n = g->style_tops[kind]; UI_Stack *stack = g->top_stack;
UI_StyleNode *n = stack->style_tops[kind];
UI_Style style = n->style; UI_Style style = n->style;
g->style_tops[kind] = n->next; stack->style_tops[kind] = n->next;
n->next = g->first_free_style_node; n->next = g->first_free_style_node;
g->first_free_style_node = n; g->first_free_style_node = n;
return style; return style;
} }
UI_StyleNode *UI_PeekStyleNode(UI_StyleKind kind) UI_Style UI_FetchStyle(UI_StyleKind kind, b32 use)
{ {
UI_SharedState *g = &UI_shared_state; UI_SharedState *g = &UI_shared_state;
UI_StyleNode *n = g->style_tops[kind]; UI_Stack *stack = g->top_stack;
return n; UI_Style result = ZI;
} result.kind = kind;
if (kind >= UI_StyleKind_BeginVirtualStyles_)
{
switch(kind)
{
default: break;
UI_Style UI_StyleFromTopNode(UI_StyleKind kind, b32 use) case UI_StyleKind_Padding:
{ {
UI_SharedState *g = &UI_shared_state; result.Padding.top = UI_FetchTop(TopPadding, use);
UI_StyleNode *n = g->style_tops[kind]; result.Padding.bottom = UI_FetchTop(BottomPadding, use);
UI_Style style = n->style; result.Padding.left = UI_FetchTop(LeftPadding, use);
result.Padding.right = UI_FetchTop(RightPadding, use);
} break;
}
}
else
{
UI_StyleNode *n = stack->style_tops[kind];
result = n->style;
if (use && n->style.pop_when_used) if (use && n->style.pop_when_used)
{ {
g->style_tops[kind] = n->next; stack->style_tops[kind] = n->next;
n->next = g->first_free_style_node; n->next = g->first_free_style_node;
g->first_free_style_node = n; g->first_free_style_node = n;
} }
return style;
}
return result;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Box helpers //~ Padding helpers
UI_Box *UI_BuildPadAlongAxis(UI_Box *parent, UI_Size start, UI_Size end, Axis final_layout_axis)
{
UI_Box *pad = parent;
b32 has_start_padding = !(start.kind == UI_SizeKind_Pixel && start.v == 0);
b32 has_end_padding = !(end.kind == UI_SizeKind_Pixel && end.v == 0);
UI_Size old_width = UI_PeekTop(Width);
UI_Size old_height = UI_PeekTop(Height);
{
UI_PushStack();
{
UI_PushCP(parent);
{
UI_ForcePush(Padding, UI_PIX(0, 0));
UI_Push(Tint, 0);
/* Start */
if (has_start_padding)
{
if (parent->layout_axis == Axis_X)
{
UI_SetNext(Width, start);
UI_SetNext(Height, old_height);
}
else
{
UI_SetNext(Width, old_width);
UI_SetNext(Height, start);
}
UI_BuildBox(Zstr);
}
/* Pad */
{
UI_SetNext(Width, old_width);
UI_SetNext(Height, old_height);
UI_SetNext(LayoutAxis, final_layout_axis);
pad = UI_BuildBox(Zstr);
}
/* End */
if (has_end_padding)
{
if (parent->layout_axis == Axis_X)
{
UI_SetNext(Width, end);
UI_SetNext(Height, old_height);
}
else
{
UI_SetNext(Width, old_width);
UI_SetNext(Height, end);
}
UI_BuildBox(Zstr);
}
}
UI_PopCP();
}
UI_PopStack();
}
return pad;
}
UI_Box *UI_BuildPad(UI_Box *parent, UI_Pad padding)
{
UI_Box *pad = parent;
b32 has_top_padding = !(padding.top.kind == UI_SizeKind_Pixel && padding.top.v == 0);
b32 has_bottom_padding = !(padding.bottom.kind == UI_SizeKind_Pixel && padding.bottom.v == 0);
b32 has_left_padding = !(padding.left.kind == UI_SizeKind_Pixel && padding.left.v == 0);
b32 has_right_padding = !(padding.right.kind == UI_SizeKind_Pixel && padding.right.v == 0);
if (has_top_padding || has_bottom_padding || has_left_padding || has_right_padding)
{
if (parent->layout_axis == Axis_X)
{
b32 needs_second_pass = has_top_padding || has_bottom_padding;
pad = UI_BuildPadAlongAxis(pad, padding.left, padding.right, needs_second_pass ? Axis_Y : Axis_X);
if (needs_second_pass)
{
pad = UI_BuildPadAlongAxis(pad, padding.top, padding.bottom, Axis_X);
}
}
else
{
b32 needs_second_pass = has_left_padding || has_right_padding;
pad = UI_BuildPadAlongAxis(pad, padding.top, padding.bottom, needs_second_pass ? Axis_X : Axis_Y);
if (needs_second_pass)
{
pad = UI_BuildPadAlongAxis(pad, padding.left, padding.right, Axis_Y);
}
}
}
return pad;
}
////////////////////////////////////////////////////////////
//~ Box
UI_Box *UI_BuildBox(String seed) UI_Box *UI_BuildBox(String seed)
{ {
UI_SharedState *g = &UI_shared_state; UI_SharedState *g = &UI_shared_state;
UI_Box *parent = UI_UseTop(Parent);
/* Create padding */
UI_Pad padding = UI_UseTop(Padding);
parent = UI_BuildPad(parent, padding);
/* Calculate key */ /* Calculate key */
UI_Box *parent = UI_UseTop(Parent);
UI_Tag tag = UI_UseTop(Tag); UI_Tag tag = UI_UseTop(Tag);
UI_Key key = ZI; UI_Key key = ZI;
if (seed.len > 0) if (seed.len > 0)
@ -259,7 +481,6 @@ UI_Box *UI_BuildBox(String seed)
box->border = UI_UseTop(Border); box->border = UI_UseTop(Border);
box->font_resource = UI_UseTop(Font); box->font_resource = UI_UseTop(Font);
box->font_size = UI_UseTop(FontSize); box->font_size = UI_UseTop(FontSize);
box->padding = UI_UseTop(Padding);
box->rounding = UI_UseTop(Rounding); box->rounding = UI_UseTop(Rounding);
box->text = UI_UseTop(Text); box->text = UI_UseTop(Text);
box->floating_pos = UI_UseTop(FloatingPos); box->floating_pos = UI_UseTop(FloatingPos);
@ -304,7 +525,7 @@ UI_Event UI_EventFromKey(UI_Key key)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Begin build //~ Begin build
void UI_BeginBuild(ControllerEventsArray controller_events) void UI_BeginBuild(ControllerEventsArray controller_events, UI_BuildFlag build_flags)
{ {
UI_SharedState *g = &UI_shared_state; UI_SharedState *g = &UI_shared_state;
@ -409,6 +630,7 @@ void UI_BeginBuild(ControllerEventsArray controller_events)
g->back_boxes_count = g->boxes_count; g->back_boxes_count = g->boxes_count;
g->back_boxes_pre = g->boxes_pre; g->back_boxes_pre = g->boxes_pre;
g->back_boxes_post = g->boxes_post; g->back_boxes_post = g->boxes_post;
g->back_build_flags = g->build_flags;
} }
////////////////////////////// //////////////////////////////
@ -423,6 +645,9 @@ void UI_BeginBuild(ControllerEventsArray controller_events)
g->boxes_count = 0; g->boxes_count = 0;
g->first_free_style_node = 0; g->first_free_style_node = 0;
g->first_free_checkpoint = 0; g->first_free_checkpoint = 0;
g->first_free_stack = 0;
g->build_flags = build_flags;
/* Init bins */ /* Init bins */
g->box_bins = PushStructs(g->build_arena, UI_BoxBin, UI_NumBoxLookupBins); g->box_bins = PushStructs(g->build_arena, UI_BoxBin, UI_NumBoxLookupBins);
@ -437,38 +662,14 @@ void UI_BeginBuild(ControllerEventsArray controller_events)
if (!g->back_build_arena) if (!g->back_build_arena)
{ {
/* Back buffer not initialized, swap again */ /* Back buffer not initialized, swap again */
UI_BeginBuild(controller_events); UI_BeginBuild(controller_events, build_flags);
} }
} }
////////////////////////////// //////////////////////////////
//- Init style stack //- Init style stack
{ UI_PushStack();
/* Init checkpoints */
g->top_checkpoint = PushStruct(g->build_arena, UI_Checkpoint);
/* Init styles */
{
UI_StyleNode *nodes = PushStructs(g->build_arena, UI_StyleNode, UI_StyleKind_Count);
for (UI_StyleKind kind = UI_StyleKind_None; kind < UI_StyleKind_Count; ++kind)
{
UI_StyleNode *n = &nodes[kind];
UI_Style *style = &n->style;
style->kind = kind;
g->style_tops[kind] = n;
}
g->style_tops[UI_StyleKind_Parent]->style.Parent = g->root_box;
g->style_tops[UI_StyleKind_Width]->style.Width = UI_FILL(1, 0);
g->style_tops[UI_StyleKind_Height]->style.Height = UI_FILL(1, 0);
g->style_tops[UI_StyleKind_Font]->style.Font = UI_GetDefaultFontResource();
g->style_tops[UI_StyleKind_FontSize]->style.FontSize = 16.0f;
g->style_tops[UI_StyleKind_Tint]->style.Tint = 0xFFFFFFFF;
g->style_tops[UI_StyleKind_Tag]->style.Tag.name = Lit("root");
g->style_tops[UI_StyleKind_Tag]->style.Tag.hash = HashFnv64(Fnv64Basis, g->style_tops[UI_StyleKind_Tag]->style.Tag.name);
}
}
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@ -594,7 +795,7 @@ i64 UI_EndBuild(GPU_Resource *render_target, Xform ui_to_screen_xf)
text_size = box->font->ascent + box->font->descent; text_size = box->font->ascent + box->font->descent;
} }
} }
box->solved_dims[axis] = text_size + box->padding * 2; box->solved_dims[axis] = text_size;
} }
} }
} }
@ -840,7 +1041,7 @@ i64 UI_EndBuild(GPU_Resource *render_target, Xform ui_to_screen_xf)
is_visible = is_visible && ((box->tint & 0xFF000000) != 0); is_visible = is_visible && ((box->tint & 0xFF000000) != 0);
is_visible = is_visible && (box->p1.x > box->p0.x); is_visible = is_visible && (box->p1.x > box->p0.x);
is_visible = is_visible && (box->p1.y > box->p0.y); is_visible = is_visible && (box->p1.y > box->p0.y);
if (is_visible || UI_DEBUG) if (is_visible || AnyBit(g->build_flags, UI_BuildFlag_Debug))
{ {
/* Box rect */ /* Box rect */
{ {
@ -905,13 +1106,12 @@ i64 UI_EndBuild(GPU_Resource *render_target, Xform ui_to_screen_xf)
f32 ascent = box->font->ascent; f32 ascent = box->font->ascent;
f32 descent = box->font->descent; f32 descent = box->font->descent;
f32 padding = box->padding;
Vec2 baseline = box->p0; Vec2 baseline = box->p0;
baseline = AddVec2(baseline, VEC2(padding, padding + ascent)); baseline.y += ascent;
F_Run run = box->glyph_run; F_Run run = box->glyph_run;
f32 max_baseline = box->p1.x - (padding * 2); f32 max_baseline = box->p1.x;
b32 should_truncate = run.count > 0 && (run.rects[run.count - 1].pos + run.rects[run.count - 1].advance) > max_baseline; b32 should_truncate = run.count > 0 && (run.rects[run.count - 1].pos + run.rects[run.count - 1].advance) > max_baseline;
/* Truncate run */ /* Truncate run */
@ -1021,7 +1221,7 @@ i64 UI_EndBuild(GPU_Resource *render_target, Xform ui_to_screen_xf)
} }
/* Render rect wireframes */ /* Render rect wireframes */
if (UI_DEBUG) if (AnyBit(g->build_flags, UI_BuildFlag_Debug))
{ {
UI_RectSig sig = ZI; UI_RectSig sig = ZI;
sig.viewport_size = RoundVec2ToVec2I32(render_viewport.size); sig.viewport_size = RoundVec2ToVec2I32(render_viewport.size);

View File

@ -9,14 +9,23 @@ Struct(UI_Key)
u64 hash; u64 hash;
}; };
////////////////////////////////////////////////////////////
//~ Build flags
Enum(UI_BuildFlag)
{
UI_BuildFlag_None = 0,
UI_BuildFlag_Debug = (1 << 0),
};
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Size types //~ Size types
Enum(UI_SizeKind) Enum(UI_SizeKind)
{ {
UI_SizeKind_Fit, /* Size to contents */
UI_SizeKind_Fill, /* Size as percent of parent size */
UI_SizeKind_Pixel, /* Exact size */ UI_SizeKind_Pixel, /* Exact size */
UI_SizeKind_Fill, /* Size as percent of parent size */
UI_SizeKind_Fit, /* Size to contents */
}; };
Struct(UI_Size) Struct(UI_Size)
@ -42,7 +51,27 @@ Struct(UI_Round)
}; };
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Box flag types //~ Padding types
Struct(UI_Pad)
{
UI_Size top;
UI_Size bottom;
UI_Size left;
UI_Size right;
};
////////////////////////////////////////////////////////////
//~ Checkpoint types
Struct(UI_Checkpoint)
{
UI_Checkpoint *next;
u64 v;
};
////////////////////////////////////////////////////////////
//~ Box flags
Enum(UI_BoxFlag) Enum(UI_BoxFlag)
{ {
@ -68,12 +97,20 @@ Enum(UI_BoxFlag)
x(BorderColor, u32) \ x(BorderColor, u32) \
x(Tint, u32) \ x(Tint, u32) \
x(Border, f32) \ x(Border, f32) \
x(FloatingPos, Vec2) \
x(Rounding, UI_Round) \
x(Font, ResourceKey) \ x(Font, ResourceKey) \
x(FontSize, u32) \ x(FontSize, u32) \
x(Text, String) \ x(Text, String) \
x(Padding, f32) \ x(LeftPadding, UI_Size) \
x(Rounding, UI_Round) \ x(RightPadding, UI_Size) \
x(FloatingPos, Vec2) \ x(TopPadding, UI_Size) \
x(BottomPadding, UI_Size) \
/* ----------------------------------- */ \
/* --------- Virtual styles --------- */ \
/* ----------------------------------- */ \
x(BeginVirtualStyles_, i8) \
x(Padding, UI_Pad) \
/* ------------------------------------------- */ /* ------------------------------------------- */
Struct(UI_Tag) Struct(UI_Tag)
@ -112,13 +149,12 @@ Struct(UI_StyleNode)
UI_Style style; UI_Style style;
}; };
//////////////////////////////////////////////////////////// Struct(UI_Stack)
//~ Checkpoint types
Struct(UI_Checkpoint)
{ {
UI_Checkpoint *next; UI_Stack *next;
u64 v;
UI_Checkpoint *top_checkpoint;
UI_StyleNode *style_tops[UI_StyleKind_Count];
}; };
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@ -174,10 +210,9 @@ Struct(UI_Box)
f32 border; f32 border;
Vec2 floating_pos; Vec2 floating_pos;
String text; String text;
Axis layout_axis;
ResourceKey font_resource; ResourceKey font_resource;
f32 font_size; f32 font_size;
f32 padding; Axis layout_axis;
//- Pre-layout data //- Pre-layout data
u64 pre_index; u64 pre_index;
@ -219,6 +254,9 @@ Struct(UI_SharedState)
Arena *build_arena; Arena *build_arena;
Arena *back_build_arena; Arena *back_build_arena;
UI_BuildFlag build_flags;
UI_BuildFlag back_build_flags;
UI_BoxBin *box_bins; UI_BoxBin *box_bins;
UI_BoxBin *back_box_bins; UI_BoxBin *back_box_bins;
@ -228,8 +266,8 @@ Struct(UI_SharedState)
u64 boxes_count; u64 boxes_count;
u64 back_boxes_count; u64 back_boxes_count;
UI_Checkpoint *top_checkpoint; UI_Stack *top_stack;
UI_StyleNode *style_tops[UI_StyleKind_Count]; UI_Stack *first_free_stack;
UI_Checkpoint *first_free_checkpoint; UI_Checkpoint *first_free_checkpoint;
UI_StyleNode *first_free_style_node; UI_StyleNode *first_free_style_node;
@ -269,20 +307,30 @@ void UI_PushCP(UI_Box *parent);
void UI_PopCP(void); void UI_PopCP(void);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Style helpers //~ Style stack helpers
UI_StyleNode *UI_PushStyleNode(UI_Style desc); void UI_PushStack(void);
UI_Style UI_PopStyleNode(UI_StyleKind kind); void UI_PopStack(void);
UI_StyleNode *UI_PeekTopStyleNode(UI_StyleKind kind);
UI_Style UI_StyleFromTopNode(UI_StyleKind kind, b32 use);
#define UI_SetNext(name, ...) UI_PushStyleNode((UI_Style) { .kind = UI_StyleKind_##name, .name = __VA_ARGS__, .pop_when_used = 1 }) void UI_PushStyle(UI_Style desc);
#define UI_Push(name, ...) UI_PushStyleNode((UI_Style) { .kind = UI_StyleKind_##name, .name = __VA_ARGS__ }) UI_Style UI_PopStyle(UI_StyleKind kind);
#define UI_ForceNext(name, ...) UI_PushStyleNode((UI_Style) { .kind = UI_StyleKind_##name, .name = __VA_ARGS__, .pop_when_used = 1, .forced = 1 }) UI_Style UI_FetchStyle(UI_StyleKind kind, b32 use);
#define UI_ForcePush(name, ...) UI_PushStyleNode((UI_Style) { .kind = UI_StyleKind_##name, .name = __VA_ARGS__, .forced = 1 })
#define UI_Pop(name) UI_PopStyleNode(UI_StyleKind_##name).name #define UI_SetNext(name, ...) UI_PushStyle((UI_Style) { .kind = UI_StyleKind_##name, .name = __VA_ARGS__, .pop_when_used = 1 })
#define UI_UseTop(name) UI_StyleFromTopNode(UI_StyleKind_##name, 1).name #define UI_Push(name, ...) UI_PushStyle((UI_Style) { .kind = UI_StyleKind_##name, .name = __VA_ARGS__ })
#define UI_PeekTop(name) UI_StyleFromTopNode(UI_StyleKind_##name, 0).name #define UI_ForceNext(name, ...) UI_PushStyle((UI_Style) { .kind = UI_StyleKind_##name, .name = __VA_ARGS__, .pop_when_used = 1, .forced = 1 })
#define UI_ForcePush(name, ...) UI_PushStyle((UI_Style) { .kind = UI_StyleKind_##name, .name = __VA_ARGS__, .forced = 1 })
#define UI_Pop(name) UI_PopStyle(UI_StyleKind_##name).name
#define UI_FetchTop(name, use) UI_FetchStyle(UI_StyleKind_##name, use).name
#define UI_PeekTop(name) UI_FetchTop(name, 0)
#define UI_UseTop(name) UI_FetchTop(name, 1)
#define UI_PushCopy(name, src, ...) do { \
UI_Style _new = src; \
_new.kind = UI_StyleKind_##name; \
_new.name = __VA_ARGS__; \
UI_PushStyle(_new); \
} while (0)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Size helpers //~ Size helpers
@ -302,6 +350,15 @@ UI_Style UI_StyleFromTopNode(UI_StyleKind kind, b32 use);
#define UI_RPIX(_v) UI_ROUND(UI_RoundKind_Pixel, (_v)) #define UI_RPIX(_v) UI_ROUND(UI_RoundKind_Pixel, (_v))
#define UI_RFILL(_v) UI_ROUND(UI_RoundKind_Fill, (_v)) #define UI_RFILL(_v) UI_ROUND(UI_RoundKind_Fill, (_v))
////////////////////////////////////////////////////////////
//~ Padding helpers
#define UI_PAD(...) ((UI_Pad) { __VA_ARGS__ })
#define UI_PADALL(size) ((UI_Pad) { .top = size, .bottom = size, .left = size, .right = size })
UI_Box *UI_BuildPadAlongAxis(UI_Box *parent, UI_Size start, UI_Size end, Axis final_layout_axis);
UI_Box *UI_BuildPad(UI_Box *parent, UI_Pad padding);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Box //~ Box
@ -317,7 +374,7 @@ UI_Event UI_EventFromKey(UI_Key key);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Begin build //~ Begin build
void UI_BeginBuild(ControllerEventsArray controller_events); void UI_BeginBuild(ControllerEventsArray controller_events, UI_BuildFlag flags);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ End build //~ End build