retain box state cache within ui core

This commit is contained in:
jacob 2025-12-19 15:07:04 -06:00
parent ad56fafeff
commit fbf7882db2
7 changed files with 615 additions and 424 deletions

View File

@ -277,7 +277,7 @@ Inline void RemoveDictEntry(Dict *dict, DictEntry *entry)
dict->last = prev; dict->last = prev;
} }
} }
/* Insert into free list */ /* Add to free list */
{ {
entry->next = dict->first_free; entry->next = dict->first_free;
dict->first_free = entry; dict->first_free = entry;

View File

@ -31,6 +31,8 @@ u64 GC_HashFromGlyphDesc(GC_GlyphDesc desc)
GC_Run GC_RunFromString(Arena *arena, String str, GC_FontKey font, f32 font_size) GC_Run GC_RunFromString(Arena *arena, String str, GC_FontKey font, f32 font_size)
{ {
GC_Run result = Zi; GC_Run result = Zi;
if (str.len > 0)
{
TempArena scratch = BeginScratch(arena); TempArena scratch = BeginScratch(arena);
Arena *perm = PermArena(); Arena *perm = PermArena();
@ -208,10 +210,12 @@ GC_Run GC_RunFromString(Arena *arena, String str, GC_FontKey font, f32 font_size
result.font_cap = glyph->font_cap; result.font_cap = glyph->font_cap;
} }
EndScratch(scratch);
}
// result.ready = uncached_codepoints_count == 0 && pending_glyphs_count == 0; // result.ready = uncached_codepoints_count == 0 && pending_glyphs_count == 0;
result.ready = 1; result.ready = 1;
EndScratch(scratch);
return result; return result;
} }

View File

@ -286,7 +286,7 @@ N_MsgAssembler *N_AcquireMsgAssembler(N_Channel *channel, u64 msg_id, u64 chunk_
/* FIXME: Ensure chunk_count > 0 */ /* FIXME: Ensure chunk_count > 0 */
ma->is_reliable = is_reliable; ma->is_reliable = is_reliable;
/* Insert into channel list */ /* Add to channel list */
ma->touched_ns = now_ns; ma->touched_ns = now_ns;
if (channel->most_recent_msg_assembler) if (channel->most_recent_msg_assembler)
{ {
@ -299,7 +299,7 @@ N_MsgAssembler *N_AcquireMsgAssembler(N_Channel *channel, u64 msg_id, u64 chunk_
} }
channel->most_recent_msg_assembler = ma; channel->most_recent_msg_assembler = ma;
/* Insert into lookup table */ /* Add to lookup table */
u64 hash = N_HashFromMsg(channel->id, msg_id); u64 hash = N_HashFromMsg(channel->id, msg_id);
ma->hash = hash; ma->hash = hash;
N_MsgAssemblerLookupBin *bin = &host->msg_assembler_lookup_bins[hash % host->num_msg_assembler_lookup_bins]; N_MsgAssemblerLookupBin *bin = &host->msg_assembler_lookup_bins[hash % host->num_msg_assembler_lookup_bins];

View File

@ -19,7 +19,12 @@ void V_Shutdown(void)
V_Frame *V_CurrentFrame(void) V_Frame *V_CurrentFrame(void)
{ {
return &V.frames[V.current_frame_idx]; return &V.frames[V.current_frame_tick % countof(V.frames)];
}
V_Frame *V_LastFrame(void)
{
return &V.frames[(V.current_frame_tick - 1) % countof(V.frames)];
} }
S_Cmd *V_PushSimCmd(S_CmdKind kind) S_Cmd *V_PushSimCmd(S_CmdKind kind)
@ -421,19 +426,13 @@ void V_TickForever(WaveLaneCtx *lane)
b32 shutdown = 0; b32 shutdown = 0;
while (!shutdown) while (!shutdown)
{ {
u64 last_frame_idx = V.current_frame_idx;
u64 frame_idx = last_frame_idx + 1;
if (frame_idx >= countof(V.frames))
{
frame_idx = 0;
}
V.current_frame_idx = frame_idx;
V_Frame *last_frame = &V.frames[last_frame_idx];
V_Frame *frame = &V.frames[frame_idx];
////////////////////////////// //////////////////////////////
//- Begin frame //- Begin frame
V.current_frame_tick += 1;
V_Frame *last_frame = V_LastFrame();
V_Frame *frame = V_CurrentFrame();
{ {
Arena *old_arena = frame->arena; Arena *old_arena = frame->arena;
Arena *old_dverts_arena = frame->dverts_arena; Arena *old_dverts_arena = frame->dverts_arena;
@ -462,8 +461,8 @@ void V_TickForever(WaveLaneCtx *lane)
frame->edit_camera_zoom = last_frame->edit_camera_zoom; frame->edit_camera_zoom = last_frame->edit_camera_zoom;
frame->look = last_frame->look; frame->look = last_frame->look;
frame->tick = V.current_frame_tick;
frame->time_ns = TimeNs(); frame->time_ns = TimeNs();
frame->tick = last_frame->tick + 1;
frame->dt_ns = frame->time_ns - last_frame->time_ns; frame->dt_ns = frame->time_ns - last_frame->time_ns;
frame->dt = SecondsFromNs(frame->dt_ns); frame->dt = SecondsFromNs(frame->dt_ns);
@ -790,20 +789,29 @@ void V_TickForever(WaveLaneCtx *lane)
/* TODO: Remove this (testing) */ /* TODO: Remove this (testing) */
//- Init test layout //- Init test layout
if (!V.root_space) if (!V.root_panel)
{ {
V_Space *space = PushStruct(perm, V_Space); {
space->axis = Axis_X; V_Panel *panel = PushStruct(perm, V_Panel);
V.root_space = space; panel->axis = Axis_X;
// panel->pref_size[Axis_X] = UI_PIX(frame->ui_dims.x, 0);
space->pref_size[Axis_X] = UI_GROW(1, 0); // panel->pref_size[Axis_Y] = UI_PIX(frame->ui_dims.y, 0);
space->pref_size[Axis_Y] = UI_GROW(1, 0); panel->key = UI_TransKey(); /* TODO: Don't use transient keys for panels */
panel->pref_size[Axis_X] = UI_GROW(1, 0);
panel->pref_size[Axis_Y] = UI_GROW(1, 0);
panel->is_organizing_panel = 1;
V.root_panel = panel;
}
{ {
V_Panel *panel = PushStruct(perm, V_Panel); V_Panel *panel = PushStruct(perm, V_Panel);
panel->space = space; panel->parent = V.root_panel;
DllQueuePushNP(space->first_panel, space->last_panel, panel, next_in_space, prev_in_space); panel->axis = !panel->parent->axis;
++space->panels_count; panel->key = UI_TransKey(); /* TODO: Don't use transient keys for panels */
panel->pref_size[Axis_X] = UI_GROW(1, 0);
panel->pref_size[Axis_Y] = UI_GROW(1, 0);
DllQueuePush(panel->parent->first, panel->parent->last, panel);
++panel->parent->count;
{ {
V_Window *window = PushStruct(perm, V_Window); V_Window *window = PushStruct(perm, V_Window);
@ -824,9 +832,13 @@ void V_TickForever(WaveLaneCtx *lane)
{ {
V_Panel *panel = PushStruct(perm, V_Panel); V_Panel *panel = PushStruct(perm, V_Panel);
panel->space = space; panel->parent = V.root_panel;
DllQueuePushNP(space->first_panel, space->last_panel, panel, next_in_space, prev_in_space); panel->axis = !panel->parent->axis;
++space->panels_count; panel->key = UI_TransKey(); /* TODO: Don't use transient keys for panels */
panel->pref_size[Axis_X] = UI_GROW(1, 0);
panel->pref_size[Axis_Y] = UI_GROW(1, 0);
DllQueuePush(panel->parent->first, panel->parent->last, panel);
++panel->parent->count;
{ {
V_Window *window = PushStruct(perm, V_Window); V_Window *window = PushStruct(perm, V_Window);
@ -837,6 +849,29 @@ void V_TickForever(WaveLaneCtx *lane)
} }
} }
// {
// V_Space *space = PushStruct(perm, V_Space);
// space->parent = V.root_space;
// space->axis = !space->parent->axis;
// space->pref_size[Axis_X] = UI_GROW(1, 0);
// space->pref_size[Axis_Y] = UI_GROW(1, 0);
// DllQueuePush(space->parent->first, space->parent->last, space);
// ++space->parent->count;
// V_Panel *panel = PushStruct(perm, V_Panel);
// panel->space = space;
// DllQueuePushNP(space->first_panel, space->last_panel, panel, next_in_space, prev_in_space);
// ++space->panels_count;
// {
// V_Window *window = PushStruct(perm, V_Window);
// window->panel = panel;
// window->is_viewport_window = 1;
// DllQueuePushNP(panel->first_window, panel->last_window, window, next_in_panel, prev_in_panel);
// ++panel->windows_count;
// }
// }
// { // {
// V_Panel *panel = PushStruct(perm, V_Panel); // V_Panel *panel = PushStruct(perm, V_Panel);
// panel->space = space; // panel->space = space;
@ -855,55 +890,53 @@ void V_TickForever(WaveLaneCtx *lane)
if (frame->is_editing) if (frame->is_editing)
{ {
Struct(SpaceDfsNode) { SpaceDfsNode *next; b32 visited; V_Space *space; UI_Checkpoint cp; }; Struct(PanelDfsNode) { PanelDfsNode *next; b32 visited; V_Panel *panel; UI_Checkpoint cp; };
SpaceDfsNode *first_space_dfs = PushStruct(frame->arena, SpaceDfsNode); PanelDfsNode *first_panel_dfs = PushStruct(frame->arena, PanelDfsNode);
first_space_dfs->space = V.root_space; first_panel_dfs->panel = V.root_panel;
//- Iterate spaces //- Iterate panels
while (first_space_dfs) while (first_panel_dfs)
{ {
SpaceDfsNode *space_dfs = first_space_dfs; PanelDfsNode *panel_dfs = first_panel_dfs;
V_Space *space = space_dfs->space; V_Panel *panel = panel_dfs->panel;
if (!space_dfs->visited) if (!panel_dfs->visited)
{ {
space_dfs->visited = 1; panel_dfs->visited = 1;
for (V_Space *child = space->last; child; child = child->prev) for (V_Panel *child = panel->last; child; child = child->prev)
{ {
SpaceDfsNode *n = PushStruct(frame->arena, SpaceDfsNode); PanelDfsNode *n = PushStruct(frame->arena, PanelDfsNode);
n->space = child; n->panel = child;
SllStackPush(first_space_dfs, n); SllStackPush(first_panel_dfs, n);
} }
UI_Key space_key = UI_TransKey(); UI_SetNext(Width, panel->pref_size[Axis_X]);
UI_SetNext(Width, space->pref_size[Axis_X]); UI_SetNext(Height, panel->pref_size[Axis_Y]);
UI_SetNext(Width, space->pref_size[Axis_Y]); if (panel->axis == Axis_X)
if (space->axis == Axis_X)
{ {
UI_BuildRowEx(space_key); UI_BuildRowEx(panel->key);
} }
else else
{ {
UI_BuildColumnEx(space_key); UI_BuildColumnEx(panel->key);
} }
space_dfs->cp = UI_PushCP(space_key); panel_dfs->cp = UI_PushCP(panel->key);
//- Build space panels if (!panel->is_organizing_panel)
for (V_Panel *panel = space->first_panel; panel; panel = panel->next_in_space)
{ {
// UI_SetNext(Width, UI_SHRINK(0, 1)); // UI_SetNext(Width, UI_SHRINK(0, 1));
UI_SetNext(Width, UI_GROW(1, 0)); // UI_SetNext(Width, UI_GROW(1, 0));
UI_SetNext(Height, UI_GROW(1, 0)); // UI_SetNext(Height, UI_GROW(1, 0));
UI_PushCP(UI_BuildColumn()); UI_PushCP(UI_BuildColumn());
{ {
i64 active_window_idx = ClampI64(panel->active_window_idx, 0, MaxI64(panel->windows_count - 1, 0)); i64 active_window_idx = ClampI64(panel->active_window_idx, 0, MaxI64(panel->windows_count - 1, 0));
//- Build panel window tabs //- Build window tabs
f32 tab_spacing = 10; f32 tab_spacing = 10;
V_Window *active_window = 0; V_Window *active_window = 0;
{ {
UI_SetNext(Width, UI_SHRINK(0, 0));
UI_SetNext(Height, UI_SHRINK(0, 0));
UI_PushCP(UI_BuildRow());
i64 window_idx = 0; i64 window_idx = 0;
UI_SetNext(Width, UI_SHRINK(0, 1));
UI_SetNext(Height, UI_SHRINK(0, 1));
UI_PushCP(UI_BuildRow());
for (V_Window *window = panel->first_window; window; window = window->next_in_panel) for (V_Window *window = panel->first_window; window; window = window->next_in_panel)
{ {
if (!window->is_viewport_window) if (!window->is_viewport_window)
@ -920,8 +953,8 @@ void V_TickForever(WaveLaneCtx *lane)
} }
UI_SetNext(BackgroundColor, VEC4(0.2, 0.2, 0.2, 1)); UI_SetNext(BackgroundColor, VEC4(0.2, 0.2, 0.2, 1));
UI_SetNext(Border, 1); UI_SetNext(Border, 1);
UI_SetNext(Width, UI_SHRINK(theme.text_padding_x, 0)); UI_SetNext(Width, UI_SHRINK(theme.text_padding_x, 1));
UI_SetNext(Height, UI_SHRINK(theme.text_padding_y, 0)); UI_SetNext(Height, UI_SHRINK(theme.text_padding_y, 1));
UI_SetNext(ChildAlignment, UI_Alignment_Center); UI_SetNext(ChildAlignment, UI_Alignment_Center);
UI_PushCP(UI_BuildRow()); UI_PushCP(UI_BuildRow());
{ {
@ -940,7 +973,7 @@ void V_TickForever(WaveLaneCtx *lane)
} }
UI_PopCP(UI_TopCP()); UI_PopCP(UI_TopCP());
} }
//- Build active panel active_window //- Build active window
if (active_window && !active_window->is_viewport_window) if (active_window && !active_window->is_viewport_window)
{ {
V_Window *window = active_window; V_Window *window = active_window;
@ -968,13 +1001,24 @@ void V_TickForever(WaveLaneCtx *lane)
} }
} }
UI_PopCP(UI_TopCP()); UI_PopCP(UI_TopCP());
} }
} }
else else
{ {
UI_PopCP(space_dfs->cp); UI_PopCP(panel_dfs->cp);
SllStackPop(first_space_dfs); SllStackPop(first_panel_dfs);
//- Build divider
if (panel->next != 0)
{
UI_Key divider_key = UI_TransKey();
UI_SetNext(BackgroundColor, Color_Cyan);
// UI_SetNext(Border, 2);
UI_SetNext(AxisSize, UI_GROW(1, 1), .axis = panel->axis);
UI_SetNext(AxisSize, UI_PIX(5, 1), .axis = !panel->axis);
/* FIXME: Non-transient key */
UI_BuildBoxEx(divider_key);
}
} }
} }
} }

View File

@ -149,28 +149,21 @@ Struct(V_CommandsWidget)
/* TODO: Move boolean fields into bitwise property flags */ /* TODO: Move boolean fields into bitwise property flags */
Struct(V_Space)
{
V_Space *parent;
V_Space *next;
V_Space *prev;
V_Space *first;
V_Space *last;
i64 panels_count;
struct V_Panel *first_panel;
struct V_Panel *last_panel;
UI_Size pref_size[Axis_CountXY];
Axis axis;
};
Struct(V_Panel) Struct(V_Panel)
{ {
V_Space *space; V_Panel *parent;
V_Panel *next_in_space; V_Panel *next;
V_Panel *prev_in_space; V_Panel *prev;
V_Panel *first;
V_Panel *last;
i64 count;
UI_Key key;
UI_Size pref_size[Axis_CountXY];
Axis axis;
b32 is_organizing_panel;
i64 active_window_idx; i64 active_window_idx;
i64 windows_count; i64 windows_count;
@ -277,12 +270,12 @@ Struct(V_Ctx)
S_Lookup lookup; S_Lookup lookup;
S_Key player_key; S_Key player_key;
V_Space *root_space; V_Panel *root_panel;
Atomic32 shutdown; Atomic32 shutdown;
Fence shutdown_complete; Fence shutdown_complete;
u64 current_frame_idx; i64 current_frame_tick;
V_Frame frames[2]; V_Frame frames[2];
}; };
@ -298,6 +291,7 @@ void V_Shutdown(void);
//~ Helpers //~ Helpers
V_Frame *V_CurrentFrame(void); V_Frame *V_CurrentFrame(void);
V_Frame *V_LastFrame(void);
S_Cmd *V_PushSimCmd(S_CmdKind kind); S_Cmd *V_PushSimCmd(S_CmdKind kind);
String V_StringFromHotkey(Arena *arena, V_Hotkey hotkey); String V_StringFromHotkey(Arena *arena, V_Hotkey hotkey);

View File

@ -1,4 +1,4 @@
UI_State UI_state = Zi; UI_Ctx UI = Zi;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Bootstrap //~ Bootstrap
@ -18,6 +18,11 @@ GC_FontKey UI_GetDefaultFont(void)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Key helpers //~ Key helpers
b32 UI_MatchKey(UI_Key a, UI_Key b)
{
return a.hash == b.hash;
}
UI_Key UI_KeyFromString(String str) UI_Key UI_KeyFromString(String str)
{ {
u64 top_tag = UI_UseTop(Tag); u64 top_tag = UI_UseTop(Tag);
@ -43,8 +48,7 @@ UI_Key UI_KeyF_(String fmt, ...)
UI_Key UI_TransKey(void) UI_Key UI_TransKey(void)
{ {
UI_Frame *frame = UI_CurrentFrame(); u64 seed = ++UI.transient_key_seed;
u64 seed = ++frame->transient_key_seed;
UI_Key key = Zi; UI_Key key = Zi;
key.hash = RandU64FromSeed(seed); key.hash = RandU64FromSeed(seed);
return key; return key;
@ -52,16 +56,14 @@ UI_Key UI_TransKey(void)
UI_Box *UI_BoxFromKey(UI_Key key) UI_Box *UI_BoxFromKey(UI_Key key)
{ {
UI_State *g = &UI_state;
UI_Box *box = 0; UI_Box *box = 0;
if (key.hash != 0) if (key.hash != 0)
{ {
UI_BoxBin *bin = &g->box_bins[key.hash % UI_NumBoxLookupBins]; UI_BoxBin *bin = &UI.box_bins[key.hash % countof(UI.box_bins)];
for (UI_Box *tmp = bin->first; tmp; tmp = tmp->next_in_bin) for (box = bin->first; box; box = box->next_in_bin)
{ {
if (tmp->key.hash == key.hash) if (UI_MatchKey(box->key, key))
{ {
box = tmp;
break; break;
} }
} }
@ -69,6 +71,105 @@ UI_Box *UI_BoxFromKey(UI_Key key)
return box; return box;
} }
////////////////////////////////////////////////////////////
//~ Iteration helpers
UI_BoxIterResult UI_FirstBox(Arena *arena, UI_BoxIter *iter, UI_Key start_key)
{
/* Free old stack */
if (iter->first)
{
if (iter->last_free)
{
iter->last_free->next = iter->first;
}
else
{
iter->first_free = iter->first;
}
iter->last_free = iter->first;
iter->first = 0;
}
/* Create root dfs node */
UI_BoxIterDfsNode *dfs = iter->first_free;
if (dfs)
{
SllQueuePop(iter->first_free, iter->last_free);
ZeroStruct(dfs);
}
else
{
dfs = PushStruct(arena, UI_BoxIterDfsNode);
}
dfs->box = UI_BoxFromKey(start_key);
iter->first = dfs;
return UI_NextBox(arena, iter);
}
UI_BoxIterResult UI_NextBox(Arena *arena, UI_BoxIter *iter)
{
UI_BoxIterResult result = Zi;
if (iter->first)
{
UI_BoxIterDfsNode *dfs = iter->first;
UI_Box *box = dfs->box;
if (!dfs->visited)
{
/* Pre order */
dfs->visited = 1;
result.box = box;
result.pre = 1;
/* Push floating children to dfs stack */
for (UI_Box *child = box->last; child; child = child->prev)
{
if (AnyBit(child->desc.flags, UI_BoxFlag_Floating))
{
UI_BoxIterDfsNode *child_dfs = iter->first_free;
if (child_dfs)
{
SllQueuePop(iter->first_free, iter->last_free);
ZeroStruct(child_dfs);
}
else
{
child_dfs = PushStruct(arena, UI_BoxIterDfsNode);
}
child_dfs->box = child;
SllStackPush(iter->first, child_dfs);
}
}
/* Push non-floating children to dfs stack */
for (UI_Box *child = box->last; child; child = child->prev)
{
if (!AnyBit(child->desc.flags, UI_BoxFlag_Floating))
{
UI_BoxIterDfsNode *child_dfs = iter->first_free;
if (child_dfs)
{
SllQueuePop(iter->first_free, iter->last_free);
ZeroStruct(child_dfs);
}
else
{
child_dfs = PushStruct(arena, UI_BoxIterDfsNode);
}
child_dfs->box = child;
SllStackPush(iter->first, child_dfs);
}
}
}
else
{
/* Post order */
result.box = box;
result.pre = 0;
SllStackPop(iter->first);
SllQueuePush(iter->first_free, iter->last_free, dfs);
}
}
return result;
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ String helpers //~ String helpers
@ -410,7 +511,6 @@ UI_Key UI_BuildBoxEx(UI_Key key)
return key; return key;
} }
void UI_SetRawTexture(UI_Key key, G_Texture2DRef tex, Rng2 uv) void UI_SetRawTexture(UI_Key key, G_Texture2DRef tex, Rng2 uv)
{ {
UI_Frame *frame = UI_CurrentFrame(); UI_Frame *frame = UI_CurrentFrame();
@ -430,7 +530,6 @@ void UI_SetRawTexture(UI_Key key, G_Texture2DRef tex, Rng2 uv)
UI_Report UI_ReportFromKey(UI_Key key) UI_Report UI_ReportFromKey(UI_Key key)
{ {
UI_State *g = &UI_state;
UI_Report result = Zi; UI_Report result = Zi;
UI_Box *box = UI_BoxFromKey(key); UI_Box *box = UI_BoxFromKey(key);
@ -447,38 +546,39 @@ UI_Report UI_ReportFromKey(UI_Key key)
UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color) UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color)
{ {
UI_State *g = &UI_state;
////////////////////////////// //////////////////////////////
//- Init persistent state //- Init persistent state
if (!g->box_arena) if (!UI.box_arena)
{ {
g->box_arena = AcquireArena(Gibi(64)); UI.box_arena = AcquireArena(Gibi(64));
g->box_bins = PushStructs(g->box_arena, UI_BoxBin, UI_NumBoxLookupBins); /* Init frames */
for (u64 i = 0; i < countof(UI.frames); ++i)
for (u64 i = 0; i < countof(g->frames); ++i)
{ {
UI_Frame *frame = &g->frames[i]; UI_Frame *frame = &UI.frames[i];
frame->arena = AcquireArena(Gibi(64)); frame->arena = AcquireArena(Gibi(64));
frame->rects_arena = AcquireArena(Gibi(64)); frame->rects_arena = AcquireArena(Gibi(64));
frame->gpu_arena = G_AcquireArena(); frame->gpu_arena = G_AcquireArena();
} }
/* Init root box */
{
UI_Box *box = PushStruct(UI.box_arena, UI_Box);
box->key = UI_RootKey;
UI.boxes_count += 1;
UI_BoxBin *bin = &UI.box_bins[box->key.hash % countof(UI.box_bins)];
bin->first = box;
bin->last = box;
UI.root_box = box;
}
} }
////////////////////////////// //////////////////////////////
//- Begin frame //- Begin frame
u64 last_frame_idx = g->current_frame_idx; UI.current_frame_tick += 1;
u64 frame_idx = last_frame_idx + 1; UI_Frame *last_frame = UI_LastFrame();
if (frame_idx >= countof(g->frames)) UI_Frame *frame = UI_CurrentFrame();
{
frame_idx = 0;
}
g->current_frame_idx = frame_idx;
UI_Frame *last_frame = &g->frames[last_frame_idx];
UI_Frame *frame = &g->frames[frame_idx];
{ {
Arena *old_arena = frame->arena; Arena *old_arena = frame->arena;
@ -500,7 +600,7 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color)
i64 dt_ns = now_ns - last_frame->time_ns; i64 dt_ns = now_ns - last_frame->time_ns;
frame->time_ns = now_ns; frame->time_ns = now_ns;
frame->dt_ns = dt_ns; frame->dt_ns = dt_ns;
frame->tick = last_frame->tick + 1; frame->tick = UI.current_frame_tick;
} }
/* Init style stack */ /* Init style stack */
@ -538,7 +638,7 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color)
} }
/* Init box reports */ /* Init box reports */
for (u64 pre_index = g->boxes_count; pre_index-- > 0;) for (u64 pre_index = UI.boxes_count; pre_index-- > 0;)
{ {
UI_Box *box = last_frame->boxes_pre[pre_index]; UI_Box *box = last_frame->boxes_pre[pre_index];
if (hovered_box == 0 && box->desc.flags & UI_BoxFlag_Interactable) if (hovered_box == 0 && box->desc.flags & UI_BoxFlag_Interactable)
@ -627,7 +727,7 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color)
} }
/* Update box hot & active states */ /* Update box hot & active states */
for (u64 pre_index = 0; pre_index < g->boxes_count; ++pre_index) for (u64 pre_index = 0; pre_index < UI.boxes_count; ++pre_index)
{ {
UI_Box *box = last_frame->boxes_pre[pre_index]; UI_Box *box = last_frame->boxes_pre[pre_index];
UI_Report *report = &box->report; UI_Report *report = &box->report;
@ -666,8 +766,12 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color)
UI_Frame *UI_CurrentFrame(void) UI_Frame *UI_CurrentFrame(void)
{ {
UI_State *g = &UI_state; return &UI.frames[UI.current_frame_tick % countof(UI.frames)];
return &g->frames[g->current_frame_idx]; };
UI_Frame *UI_LastFrame(void)
{
return &UI.frames[(UI.current_frame_tick - 1) % countof(UI.frames)];
}; };
Arena *UI_FrameArena(void) Arena *UI_FrameArena(void)
@ -686,7 +790,7 @@ Vec2 UI_CursorPos(void)
void UI_EndFrame(UI_Frame *frame) void UI_EndFrame(UI_Frame *frame)
{ {
TempArena scratch = BeginScratchNoConflict(); TempArena scratch = BeginScratchNoConflict();
UI_State *g = &UI_state; UI_BoxIter box_iter = Zi;
Vec2I32 monitor_size = frame->window_frame.monitor_size; Vec2I32 monitor_size = frame->window_frame.monitor_size;
Rng3 monitor_viewport = RNG3(VEC3(0, 0, 0), VEC3(monitor_size.x, monitor_size.y, 1)); Rng3 monitor_viewport = RNG3(VEC3(0, 0, 0), VEC3(monitor_size.x, monitor_size.y, 1));
@ -699,7 +803,51 @@ void UI_EndFrame(UI_Frame *frame)
////////////////////////////// //////////////////////////////
//- Process commands //- Process commands
g->boxes_count = 0; {
//////////////////////////////
//- Create boxes from build cmds
for (UI_CmdNode *cmd_node = frame->first_cmd_node; cmd_node; cmd_node = cmd_node->next)
{
UI_Cmd cmd = cmd_node->cmd;
if (cmd.kind == UI_CmdKind_BuildBox)
{
UI_Key key = cmd.box.key;
UI_Box *box = 0;
{
UI_BoxBin *bin = &UI.box_bins[key.hash % countof(UI.box_bins)];
for (box = bin->first; box; box = box->next_in_bin)
{
if (UI_MatchKey(box->key, key))
{
break;
}
}
/* Allocate new box */
if (box == 0)
{
/* Allocate new box */
box = UI.first_free_box;
if (box)
{
SllStackPop(UI.first_free_box);
ZeroStruct(box);
}
else
{
box = PushStruct(UI.box_arena, UI_Box);
}
box->key = key;
DllQueuePushNP(bin->first, bin->last, box, next_in_bin, prev_in_bin);
++UI.boxes_count;
}
}
box->last_build_tick = frame->tick;
}
}
//////////////////////////////
//- Update boxes from cmds
for (UI_CmdNode *cmd_node = frame->first_cmd_node; cmd_node; cmd_node = cmd_node->next) for (UI_CmdNode *cmd_node = frame->first_cmd_node; cmd_node; cmd_node = cmd_node->next)
{ {
@ -710,93 +858,45 @@ void UI_EndFrame(UI_Frame *frame)
case UI_CmdKind_BuildBox: case UI_CmdKind_BuildBox:
{ {
UI_Key key = cmd.box.key; UI_Key key = cmd.box.key;
if (key.hash == 0) if (UI_MatchKey(key, UI_NilKey))
{ {
key = UI_TransKey(); key = UI_TransKey();
} }
UI_Box *box = UI_BoxFromKey(key);
b32 is_root = frame->root_box == 0;
UI_Box *parent = 0; UI_Box *parent = 0;
if (!is_root) if (box != UI.root_box)
{ {
parent = UI_BoxFromKey(cmd.box.parent); parent = UI_BoxFromKey(cmd.box.parent);
if (!parent) if (!parent)
{ {
parent = frame->root_box; parent = UI.root_box;
} }
} }
/* Allocate box */ /* Update parent */
UI_Box *box = 0; if (box->parent != parent)
{ {
UI_BoxBin *bin = &g->box_bins[key.hash % UI_NumBoxLookupBins];
for (UI_Box *tmp = bin->first; tmp && !box; tmp = tmp->next_in_bin)
{
if (tmp->key.hash == key.hash)
{
box = tmp;
}
}
if (box)
{
/* Remove box from old parent */
if (box->parent) if (box->parent)
{ {
/* Remove from old parent */
DllQueueRemove(box->parent->first, box->parent->last, box); DllQueueRemove(box->parent->first, box->parent->last, box);
--box->parent->count; --box->parent->count;
} }
} if (parent)
else
{ {
box = g->first_free_box; /* Add to new parent */
if (box) DllQueuePush(parent->first, parent->last, box);
{ ++parent->count;
SllStackPop(g->first_free_box);
ZeroStruct(box);
} }
else box->parent = parent;
{
box = PushStruct(g->box_arena, UI_Box);
} }
DllQueuePushNP(bin->first, bin->last, box, next_in_bin, prev_in_bin);
}
}
++g->boxes_count;
/* Reset box */
UI_Box old_box = *box;
{
ZeroStruct(box);
box->next_in_bin = old_box.next_in_bin;
box->prev_in_bin = old_box.prev_in_bin;
box->report = old_box.report;
}
box->key = key;
box->last_updated_tick = frame->tick;
/* Update box */ /* Update box */
{ {
box->desc = cmd.box; box->desc = cmd.box;
/* Insert box into parent */
if (parent)
{
DllQueuePush(parent->first, parent->last, box);
box->parent = parent;
++parent->count;
}
/* Fetch run */
if (box->desc.text.len > 0)
{
box->glyph_run = GC_RunFromString(frame->arena, box->desc.text, box->desc.font, box->desc.font_size); box->glyph_run = GC_RunFromString(frame->arena, box->desc.text, box->desc.font, box->desc.font_size);
} }
}
if (is_root)
{
frame->root_box = box;
}
} break; } break;
case UI_CmdKind_SetRawTexture: case UI_CmdKind_SetRawTexture:
@ -810,73 +910,90 @@ void UI_EndFrame(UI_Frame *frame)
} }
} break; } break;
} }
}
} }
////////////////////////////// //////////////////////////////
//- Prune boxes //- Prune cached boxes
// { {
// u64 cur_tick = frame->tick; u64 prunes_count = 0;
// UI_BoxIter it = UI_ITER(g->root_box); UI_Box **prunes = PushStructsNoZero(scratch.arena, UI_Box *, UI.boxes_count);
// UI_Box *box = UI_NextBox(&it); for (UI_BoxIterResult ir = UI_FirstBox(scratch.arena, &box_iter, UI_RootKey); ir.box; ir = UI_NextBox(scratch.arena, &box_iter))
// while (box != 0) {
// { if (ir.pre)
// UI_Box *next = UI_NextBox(&it); {
// box = next; UI_Box *box = ir.box;
// } if (box->last_build_tick < frame->tick)
// } {
prunes[prunes_count++] = box;
}
}
}
for (u64 prune_idx = 0; prune_idx < prunes_count; ++prune_idx)
{
UI_Box *box = prunes[prune_idx];
UI_Box *parent = box->parent;
UI_BoxBin *bin = &UI.box_bins[box->key.hash % countof(UI.box_bins)];
/* Re-parent children */
if (box->first)
{
for (UI_Box *child = box->first; child; child = child->next)
{
child->parent = parent;
}
if (parent->last)
{
parent->last->next = box->first;
box->first->prev = parent->last;
}
else
{
parent->first = box->first;
}
parent->last = box->last;
parent->count += box->count;
}
/* Remove from parent */
DllQueueRemove(parent->first, parent->last, box);
--parent->count;
/* Remove from lookup table */
DllQueueRemoveNP(bin->first, bin->last, box, next_in_bin, prev_in_bin);
/* Add to free list */
SllStackPush(UI.first_free_box, box);
--UI.boxes_count;
}
}
////////////////////////////// //////////////////////////////
//- Layout //- Layout
/* Build pre-order & post-order box arrays */ /* Prepare layout data */
u64 boxes_count = g->boxes_count; u64 boxes_count = UI.boxes_count;
UI_Box **boxes_pre = PushStructsNoZero(frame->arena, UI_Box *, boxes_count); UI_Box **boxes_pre = PushStructsNoZero(frame->arena, UI_Box *, boxes_count);
UI_Box **boxes_post = PushStructsNoZero(frame->arena, UI_Box *, boxes_count); UI_Box **boxes_post = PushStructsNoZero(frame->arena, UI_Box *, boxes_count);
frame->boxes_pre = boxes_pre; frame->boxes_pre = boxes_pre;
frame->boxes_post = boxes_post; frame->boxes_post = boxes_post;
{ {
Struct(BoxNode) { BoxNode *next; b32 visited; UI_Box *box; };
BoxNode *first_dfs = PushStruct(scratch.arena, BoxNode);
u64 pre_index = 0; u64 pre_index = 0;
u64 post_index = 0; u64 post_index = 0;
first_dfs->box = frame->root_box; for (UI_BoxIterResult ir = UI_FirstBox(scratch.arena, &box_iter, UI_RootKey); ir.box; ir = UI_NextBox(scratch.arena, &box_iter))
while (first_dfs)
{ {
BoxNode *n = first_dfs; UI_Box *box = ir.box;
UI_Box *box = n->box; if (ir.pre)
if (!n->visited)
{ {
/* Push floating children to dfs stack */ box->pre_index = pre_index;
for (UI_Box *child = box->last; child; child = child->prev) boxes_pre[pre_index] = box;
{ pre_index += 1;
if (AnyBit(child->desc.flags, UI_BoxFlag_Floating))
{ /* Reset layout data */
BoxNode *child_n = PushStruct(scratch.arena, BoxNode); ZeroStruct(&box->layout);
child_n->box = child;
SllStackPush(first_dfs, child_n);
}
}
/* Push non-floating children to dfs stack */
for (UI_Box *child = box->last; child; child = child->prev)
{
if (!AnyBit(child->desc.flags, UI_BoxFlag_Floating))
{
BoxNode *child_n = PushStruct(scratch.arena, BoxNode);
child_n->box = child;
SllStackPush(first_dfs, child_n);
}
}
box->pre_index = pre_index++;
boxes_pre[box->pre_index] = box;
n->visited = 1;
} }
else else
{ {
SllStackPop(first_dfs); box->post_index = post_index;
box->post_index = post_index++; boxes_post[post_index] = box;
boxes_post[box->post_index] = box; post_index += 1;
} }
} }
Assert(pre_index == boxes_count); Assert(pre_index == boxes_count);
@ -892,7 +1009,7 @@ void UI_EndFrame(UI_Frame *frame)
UI_Size pref_size = box->desc.pref_size[axis]; UI_Size pref_size = box->desc.pref_size[axis];
if (pref_size.kind == UI_SizeKind_Pixel) if (pref_size.kind == UI_SizeKind_Pixel)
{ {
box->solved_dims[axis] = pref_size.v; box->layout.solved_dims[axis] = pref_size.v;
} }
else if (pref_size.kind == UI_SizeKind_Shrink && AnyBit(box->desc.flags, UI_BoxFlag_DrawText)) else if (pref_size.kind == UI_SizeKind_Shrink && AnyBit(box->desc.flags, UI_BoxFlag_DrawText))
{ {
@ -906,7 +1023,7 @@ void UI_EndFrame(UI_Frame *frame)
{ {
text_size = box->glyph_run.font_ascent + box->glyph_run.font_descent; text_size = box->glyph_run.font_ascent + box->glyph_run.font_descent;
} }
box->solved_dims[axis] = text_size + (pref_size.v * 2); box->layout.solved_dims[axis] = text_size + (pref_size.v * 2);
} }
} }
} }
@ -929,11 +1046,11 @@ void UI_EndFrame(UI_Frame *frame)
if (ancestor_size.kind == UI_SizeKind_Pixel || (ancestor_size.kind == UI_SizeKind_Shrink && AnyBit(box->desc.flags, UI_BoxFlag_DrawText))) if (ancestor_size.kind == UI_SizeKind_Pixel || (ancestor_size.kind == UI_SizeKind_Shrink && AnyBit(box->desc.flags, UI_BoxFlag_DrawText)))
{ {
/* Match independent ancestor */ /* Match independent ancestor */
match_size = ancestor->solved_dims[axis]; match_size = ancestor->layout.solved_dims[axis];
found_match = 1; found_match = 1;
} }
} }
box->solved_dims[axis] = match_size * pref_size.v; box->layout.solved_dims[axis] = match_size * pref_size.v;
} }
} }
} }
@ -954,15 +1071,15 @@ void UI_EndFrame(UI_Frame *frame)
{ {
if (axis == box->desc.child_layout_axis) if (axis == box->desc.child_layout_axis)
{ {
accum += child->solved_dims[axis]; accum += child->layout.solved_dims[axis];
} }
else else
{ {
accum = MaxF32(child->solved_dims[axis], accum); accum = MaxF32(child->layout.solved_dims[axis], accum);
} }
} }
} }
box->solved_dims[axis] = accum + (pref_size.v * 2); box->layout.solved_dims[axis] = accum + (pref_size.v * 2);
} }
} }
} }
@ -977,7 +1094,7 @@ void UI_EndFrame(UI_Frame *frame)
UI_Size pref_size = box->desc.pref_size[axis]; UI_Size pref_size = box->desc.pref_size[axis];
if (pref_size.kind == UI_SizeKind_Grow) if (pref_size.kind == UI_SizeKind_Grow)
{ {
box->solved_dims[axis] = box->parent->solved_dims[axis] * pref_size.v; box->layout.solved_dims[axis] = box->parent->layout.solved_dims[axis] * pref_size.v;
} }
} }
} }
@ -988,7 +1105,7 @@ void UI_EndFrame(UI_Frame *frame)
UI_Box *box = boxes_pre[pre_index]; UI_Box *box = boxes_pre[pre_index];
for (Axis axis = 0; axis < Axis_CountXY; ++axis) for (Axis axis = 0; axis < Axis_CountXY; ++axis)
{ {
f32 box_size = box->solved_dims[axis]; f32 box_size = box->layout.solved_dims[axis];
/* Solve non-floating violations */ /* Solve non-floating violations */
{ {
f32 size_accum = 0; f32 size_accum = 0;
@ -997,7 +1114,7 @@ void UI_EndFrame(UI_Frame *frame)
{ {
if (!AnyBit(child->desc.flags, UI_BoxFlag_Floating)) if (!AnyBit(child->desc.flags, UI_BoxFlag_Floating))
{ {
f32 size = child->solved_dims[axis]; f32 size = child->layout.solved_dims[axis];
f32 strictness = child->desc.pref_size[axis].strictness; f32 strictness = child->desc.pref_size[axis].strictness;
f32 flex = size * (1.0 - strictness); f32 flex = size * (1.0 - strictness);
if (axis == box->desc.child_layout_axis) if (axis == box->desc.child_layout_axis)
@ -1020,7 +1137,7 @@ void UI_EndFrame(UI_Frame *frame)
{ {
if (!AnyBit(child->desc.flags, UI_BoxFlag_Floating)) if (!AnyBit(child->desc.flags, UI_BoxFlag_Floating))
{ {
f32 size = child->solved_dims[axis]; f32 size = child->layout.solved_dims[axis];
f32 strictness = child->desc.pref_size[axis].strictness; f32 strictness = child->desc.pref_size[axis].strictness;
f32 flex = size * (1.0 - strictness); f32 flex = size * (1.0 - strictness);
f32 new_size = size; f32 new_size = size;
@ -1037,24 +1154,24 @@ void UI_EndFrame(UI_Frame *frame)
} }
} }
adjusted_size_accum += new_size; adjusted_size_accum += new_size;
child->solved_dims[axis] = new_size; child->layout.solved_dims[axis] = new_size;
} }
} }
size_accum = adjusted_size_accum; size_accum = adjusted_size_accum;
} }
box->final_children_size_accum[axis] = size_accum; box->layout.final_children_size_accum[axis] = size_accum;
} }
/* Solve floating violations */ /* Solve floating violations */
for (UI_Box *child = box->first; child; child = child->next) for (UI_Box *child = box->first; child; child = child->next)
{ {
if (AnyBit(child->desc.flags, UI_BoxFlag_Floating) && !AnyBit(child->desc.flags, UI_BoxFlag_NoFloatingClamp)) if (AnyBit(child->desc.flags, UI_BoxFlag_Floating) && !AnyBit(child->desc.flags, UI_BoxFlag_NoFloatingClamp))
{ {
f32 size = child->solved_dims[axis]; f32 size = child->layout.solved_dims[axis];
if (size > box_size) if (size > box_size)
{ {
f32 strictness = child->desc.pref_size[axis].strictness; f32 strictness = child->desc.pref_size[axis].strictness;
f32 flex = size * (1.0 - strictness); f32 flex = size * (1.0 - strictness);
child->solved_dims[axis] = MaxF32(size - flex, box_size); child->layout.solved_dims[axis] = MaxF32(size - flex, box_size);
} }
} }
} }
@ -1071,25 +1188,25 @@ void UI_EndFrame(UI_Frame *frame)
{ {
Axis axis = box->desc.child_layout_axis; Axis axis = box->desc.child_layout_axis;
UI_AxisAlignment alignment = box->desc.child_alignment[axis]; UI_AxisAlignment alignment = box->desc.child_alignment[axis];
f32 box_size = box->solved_dims[axis]; f32 box_size = box->layout.solved_dims[axis];
f32 size_accum = box->final_children_size_accum[axis]; f32 size_accum = box->layout.final_children_size_accum[axis];
switch(alignment) switch(alignment)
{ {
default: break; default: break;
case UI_AxisAlignment_Center: case UI_AxisAlignment_Center:
{ {
box->layout_cursor = box_size / 2 - size_accum / 2; box->layout.cursor = box_size / 2 - size_accum / 2;
} break; } break;
case UI_AxisAlignment_End: case UI_AxisAlignment_End:
{ {
box->layout_cursor = box_size - size_accum; box->layout.cursor = box_size - size_accum;
} break; } break;
} }
} }
/* Position */ /* Position */
{ {
f32 *dims_arr = box->solved_dims; f32 *dims_arr = box->layout.solved_dims;
Vec2 dims_vec = VEC2(dims_arr[0], dims_arr[1]); Vec2 dims_vec = VEC2(dims_arr[0], dims_arr[1]);
Vec2 final_pos = Zi; Vec2 final_pos = Zi;
@ -1113,7 +1230,7 @@ void UI_EndFrame(UI_Frame *frame)
/* Non-floating box position */ /* Non-floating box position */
else if (parent) else if (parent)
{ {
f32 layout_cursor = parent->layout_cursor; f32 layout_cursor = parent->layout.cursor;
f32 offset[2] = Zi; f32 offset[2] = Zi;
/* Compute offset in layout direction */ /* Compute offset in layout direction */
{ {
@ -1129,13 +1246,13 @@ void UI_EndFrame(UI_Frame *frame)
default: break; default: break;
case UI_AxisAlignment_Center: case UI_AxisAlignment_Center:
{ {
f32 parent_size = parent->solved_dims[axis]; f32 parent_size = parent->layout.solved_dims[axis];
f32 box_size = dims_arr[axis]; f32 box_size = dims_arr[axis];
offset[axis] = parent_size / 2 - box_size / 2; offset[axis] = parent_size / 2 - box_size / 2;
} break; } break;
case UI_AxisAlignment_End: case UI_AxisAlignment_End:
{ {
f32 parent_size = parent->solved_dims[axis]; f32 parent_size = parent->layout.solved_dims[axis];
f32 box_size = dims_arr[axis]; f32 box_size = dims_arr[axis];
offset[axis] = parent_size - box_size; offset[axis] = parent_size - box_size;
} break; } break;
@ -1143,7 +1260,7 @@ void UI_EndFrame(UI_Frame *frame)
} }
final_pos.x = parent->rect.p0.x + offset[0]; final_pos.x = parent->rect.p0.x + offset[0];
final_pos.y = parent->rect.p0.y + offset[1]; final_pos.y = parent->rect.p0.y + offset[1];
parent->layout_cursor += dims_arr[parent->desc.child_layout_axis]; parent->layout.cursor += dims_arr[parent->desc.child_layout_axis];
} }
/* Submit position */ /* Submit position */

View File

@ -2,7 +2,7 @@
//~ Key types //~ Key types
#define UI_NilKey ((UI_Key) { 0 }) #define UI_NilKey ((UI_Key) { 0 })
#define UI_RootKey ((UI_Key) { 0xa3deb3749ef35a7aUll }) #define UI_RootKey ((UI_Key) { 0xaaaaaaaaaaaaaaaaUll })
Struct(UI_Key) Struct(UI_Key)
{ {
@ -253,7 +253,7 @@ Struct(UI_Box)
UI_Box *next_in_bin; UI_Box *next_in_bin;
UI_Box *prev_in_bin; UI_Box *prev_in_bin;
UI_Report report; UI_Report report;
u64 last_updated_tick; i64 last_build_tick;
//- Tree links //- Tree links
UI_Box *parent; UI_Box *parent;
@ -267,16 +267,19 @@ Struct(UI_Box)
UI_BoxDesc desc; UI_BoxDesc desc;
G_Texture2DRef raw_texture; G_Texture2DRef raw_texture;
Rng2 raw_texture_slice_uv; Rng2 raw_texture_slice_uv;
GC_Run glyph_run;
//- Pre-layout data //- Pre-layout data
u64 pre_index; u64 pre_index;
u64 post_index; u64 post_index;
//- Layout data //- Layout data
GC_Run glyph_run; struct
f32 layout_cursor; {
f32 cursor;
f32 solved_dims[Axis_CountXY]; f32 solved_dims[Axis_CountXY];
f32 final_children_size_accum[Axis_CountXY]; f32 final_children_size_accum[Axis_CountXY];
} layout;
//- Layout results //- Layout results
Rng2 rect; Rng2 rect;
@ -293,9 +296,30 @@ Struct(UI_BoxBin)
}; };
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ State types //~ Iterator types
#define UI_NumBoxLookupBins 16384 Struct(UI_BoxIterDfsNode)
{
UI_BoxIterDfsNode *next;
UI_Box *box;
b32 visited;
};
Struct(UI_BoxIter)
{
UI_BoxIterDfsNode *first;
UI_BoxIterDfsNode *first_free;
UI_BoxIterDfsNode *last_free;
};
Struct(UI_BoxIterResult)
{
UI_Box *box;
b32 pre;
};
////////////////////////////////////////////////////////////
//~ Context types
Enum(UI_FrameFlag) Enum(UI_FrameFlag)
{ {
@ -314,10 +338,8 @@ Struct(UI_Frame)
G_ResourceHandle backbuffer; G_ResourceHandle backbuffer;
G_CommandListHandle cl; G_CommandListHandle cl;
u64 transient_key_seed;
/* Time */ /* Time */
u64 tick; i64 tick;
i64 time_ns; i64 time_ns;
i64 dt_ns; i64 dt_ns;
@ -338,21 +360,25 @@ Struct(UI_Frame)
UI_StyleNode *first_free_style_node; UI_StyleNode *first_free_style_node;
/* Layout */ /* Layout */
UI_Box *root_box;
UI_Box **boxes_pre; UI_Box **boxes_pre;
UI_Box **boxes_post; UI_Box **boxes_post;
}; };
Struct(UI_State) Struct(UI_Ctx)
{ {
Arena *box_arena;
UI_BoxBin *box_bins;
u64 boxes_count; u64 boxes_count;
Arena *box_arena;
UI_Box *root_box;
UI_BoxBin box_bins[Kibi(256)];
UI_Box *first_free_box; UI_Box *first_free_box;
u64 current_frame_idx; u64 transient_key_seed;
i64 current_frame_tick;
UI_Frame frames[2]; UI_Frame frames[2];
} extern UI_state; };
extern UI_Ctx UI;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Bootstrap //~ Bootstrap
@ -367,14 +393,19 @@ GC_FontKey UI_GetDefaultFont(void);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Key helpers //~ Key helpers
b32 UI_MatchKey(UI_Key a, UI_Key b);
UI_Key UI_KeyFromString(String str); UI_Key UI_KeyFromString(String str);
UI_Key UI_KeyF_(String fmt, ...); UI_Key UI_KeyF_(String fmt, ...);
#define UI_KeyF(fmt_cstr, ...) UI_KeyF_(StringFromCstrNoLimit(fmt_cstr), __VA_ARGS__, FmtEnd) #define UI_KeyF(fmt_cstr, ...) UI_KeyF_(StringFromCstrNoLimit(fmt_cstr), __VA_ARGS__, FmtEnd)
UI_Key UI_TransKey(void); UI_Key UI_TransKey(void);
UI_Box *UI_BoxFromKey(UI_Key key); UI_Box *UI_BoxFromKey(UI_Key key);
////////////////////////////////////////////////////////////
//~ Iteration helpers
UI_BoxIterResult UI_FirstBox(Arena *arena, UI_BoxIter *iter, UI_Key start_key);
UI_BoxIterResult UI_NextBox(Arena *arena, UI_BoxIter *iter);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ String helpers //~ String helpers
@ -454,6 +485,7 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color);
//~ Frame helpers //~ Frame helpers
UI_Frame *UI_CurrentFrame(void); UI_Frame *UI_CurrentFrame(void);
UI_Frame *UI_LastFrame(void);
Arena *UI_FrameArena(void); Arena *UI_FrameArena(void);
Vec2 UI_CursorPos(void); Vec2 UI_CursorPos(void);