diff --git a/src/pp/pp.c b/src/pp/pp.c index e9cb1621..f9811608 100644 --- a/src/pp/pp.c +++ b/src/pp/pp.c @@ -452,7 +452,7 @@ void UpdateUser(P_Window *window) //- Begin UI UI_BeginBuild(); UI_Box *pp_root_box = UI_BuildBox(UI_Flag_DrawImage, UI_NilKey); - UI_PushParent(pp_root_box); + UI_Push(Parent, pp_root_box); //- Init render data buffers if (!g->material_instances_arena) @@ -2006,10 +2006,12 @@ void UpdateUser(P_Window *window) __profn("Draw debug info"); UI_Box *dbg_box = UI_BuildBox(0, UI_NilKey); - UI_PushParent(dbg_box); - UI_PushSize(Axis_X, UI_SizeKind_Pixel, 5, 0); - UI_PushSize(Axis_Y, UI_SizeKind_Pixel, 5, 0); + UI_PushCheckpoint(); { + UI_Push(Parent, dbg_box); + UI_Push(Width, UI_PixelSize(5, 0)); + UI_Push(Height, UI_PixelSize(5, 0)); + UI_BuildLabelF("blended world entities: %F/%F", FmtUint(g->ss_blended->num_ents_allocated), FmtUint(g->ss_blended->num_ents_reserved)); UI_BuildSeparator(); @@ -2105,9 +2107,7 @@ void UpdateUser(P_Window *window) draw_text(g->render_sig, D_TEXTPARAMS(.font = font, .pos = pos, .str = text, .offset_y = offset_y, .color = ColorWhite)); #endif } - UI_PopSize(Axis_X); - UI_PopSize(Axis_Y); - UI_PopParent(); + UI_PopCheckpoint(); } #else if (g->debug_draw) @@ -2545,7 +2545,6 @@ void UpdateUser(P_Window *window) } /* Render UI */ - UI_PopParent(); UI_SetDisplayImage(pp_root_box, g->ui_target); GPU_Resource *ui_render = UI_EndBuild(ui_viewport); diff --git a/src/ui/ui.c b/src/ui/ui.c index 6679bf63..08f336f8 100644 --- a/src/ui/ui.c +++ b/src/ui/ui.c @@ -3,118 +3,128 @@ UI_SharedState UI_shared_state = ZI; //////////////////////////////// //~ Key helpers -UI_Key UI_KeyFromString(u64 seed, String str) +u64 UI_HashFromTop(void) +{ + UI_Box *parent_box = UI_PeekTop(Parent); + u64 tag_hash = UI_PeekTop(Tag); + return RandU64FromSeeds(parent_box->key.hash, tag_hash); +} + +u64 UI_HashFromString(u64 seed, String str) { - /* TOIMPL */ if (seed == 0) { - UI_SharedState *g = &UI_shared_state; - seed = RandU64FromSeeds(g->top_parent->box->key.hash, g->top_tag->hash); + seed = UI_HashFromTop(); } + return HashFnv64(seed, str); +} + +UI_Key UI_KeyFromString(u64 seed, String str) +{ UI_Key result = ZI; - result.hash = HashFnv64(seed, str); + result.hash = UI_HashFromString(seed, str); return result; } //////////////////////////////// -//~ Stack helpers +//~ Checkpoint helpers -//- Tag - -void UI_PushTagFromHash(u64 hash) +void UI_PushCheckpoint(void) { UI_SharedState *g = &UI_shared_state; - if (g->top_tag) + UI_Checkpoint *cp = g->first_free_checkpoint; + if (cp) { - hash = RandU64FromSeeds(hash, g->top_tag->hash); + g->first_free_checkpoint = cp->next; + ZeroStruct(cp); } - UI_TagNode *n = PushStruct(g->build_arena, UI_TagNode); - StackPush(g->top_tag, n); - n->hash = hash; -} - -void UI_PushTagFromString(String str) -{ - UI_SharedState *g = &UI_shared_state; - u64 hash = Fnv64Basis; - if (g->top_tag) + else { - hash = g->top_tag->hash; + cp = PushStruct(g->build_arena, UI_Checkpoint); } - hash = HashFnv64(hash, str); - UI_TagNode *n = PushStruct(g->build_arena, UI_TagNode); - n->hash = hash; - StackPush(g->top_tag, n); + cp->v = g->top_checkpoint->v + 1; + g->top_checkpoint = cp; } -void UI_PushTagF_(char *fmt_cstr, ...) +void UI_PopCheckpoint(void) { - TempArena scratch = BeginScratchNoConflict(); - String str = ZI; + UI_SharedState *g = &UI_shared_state; + UI_Checkpoint *cp = g->top_checkpoint; + u64 v = cp->v; + for (UI_StyleKind kind = UI_StyleKind_None; kind < UI_StyleKind_Count; ++kind) { - va_list va; - va_start(va, fmt_cstr); - str = FormatStringV(scratch.arena, StringFromCstrNoLimit(fmt_cstr), va); - va_end(va); + UI_StyleNode *n = g->style_tops[kind]; + while (n && n->checkpoint >= v) + { + UI_StyleNode *next = n->next; + g->style_tops[kind] = next; + n->next = g->first_free_style_node; + g->first_free_style_node = n; + n = next; + } } - UI_PushTagFromString(str); - EndScratch(scratch); -} - -void UI_PopTag(void) -{ - StackPop(UI_shared_state.top_tag); -} - -//- Parent - -void UI_PushParent(UI_Box *box) -{ - UI_SharedState *g = &UI_shared_state; - UI_ParentNode *n = PushStruct(g->build_arena, UI_ParentNode); - n->box = box; - StackPush(g->top_parent, n); -} - -void UI_PopParent(void) -{ - StackPop(UI_shared_state.top_parent); -} - -//- Size - -void UI_PushSize(Axis axis, UI_SizeKind kind, f32 v, f32 strictness) -{ - UI_SharedState *g = &UI_shared_state; - UI_SizeNode *n = PushStruct(g->build_arena, UI_SizeNode); - n->size.kind = kind; - n->size.v = v; - n->size.strictness = strictness; - StackPush(g->top_size[axis], n); -} - -void UI_PopSize(Axis axis) -{ - StackPop(UI_shared_state.top_size[axis]); -} - -//- Layout dir - -void UI_PushLayoutDir(Axis dir) -{ - UI_SharedState *g = &UI_shared_state; - UI_AxisNode *n = PushStruct(g->build_arena, UI_AxisNode); - n->axis = dir; - StackPush(g->top_layout_dir, n); -} - -void UI_PopLayoutDir(void) -{ - StackPop(UI_shared_state.top_layout_dir); + cp->next = g->first_free_checkpoint; + g->first_free_checkpoint = cp; } //////////////////////////////// -//~ Box helpers +//~ Style stack helpers + +UI_StyleNode *UI_PushStyleNode(UI_StyleKind kind, b32 pop_when_touched, UI_Style desc) +{ + UI_SharedState *g = &UI_shared_state; + 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->pop_when_touched = pop_when_touched; + n->style = desc; + n->next = g->style_tops[kind]; + n->checkpoint = g->top_checkpoint->v; + g->style_tops[kind] = n; + return n; +} + +UI_Style UI_PopStyleNode(UI_StyleKind kind) +{ + UI_SharedState *g = &UI_shared_state; + UI_StyleNode *n = g->style_tops[kind]; + UI_Style style = n->style; + g->style_tops[kind] = n->next; + n->next = g->first_free_style_node; + g->first_free_style_node = n; + return style; +} + +UI_StyleNode *UI_PeekStyleNode(UI_StyleKind kind) +{ + UI_SharedState *g = &UI_shared_state; + UI_StyleNode *n = g->style_tops[kind]; + return n; +} + +UI_Style UI_StyleFromTopNode(UI_StyleKind kind, b32 touch) +{ + UI_SharedState *g = &UI_shared_state; + UI_StyleNode *n = g->style_tops[kind]; + UI_Style style = n->style; + if (touch && n->pop_when_touched) + { + g->style_tops[kind] = n->next; + n->next = g->first_free_style_node; + g->first_free_style_node = n; + } + return style; +} + +//////////////////////////////// +//~ Box helper s UI_Box *UI_BuildBox(UI_Flag flags, UI_Key key) { @@ -164,13 +174,13 @@ UI_Box *UI_BuildBox(UI_Flag flags, UI_Key key) box->tint = 0xFFFFFFFF; - /* Pull from stack */ - box->parent = g->top_parent->box; - box->pref_size[Axis_X] = g->top_size[Axis_X]->size; - box->pref_size[Axis_Y] = g->top_size[Axis_Y]->size; - box->layout_dir = g->top_layout_dir->axis; + /* Pull from style stack */ + box->parent = UI_TouchTop(Parent); + box->pref_size[Axis_X] = UI_TouchTop(Width); + box->pref_size[Axis_Y] = UI_TouchTop(Height); + box->layout_axis = UI_TouchTop(LayoutAxis); - DllPushBack(g->top_parent->box->first, g->top_parent->box->last, box); + DllPushBack(box->parent->first, box->parent->last, box); ++box->parent->count; return box; @@ -212,6 +222,7 @@ void UI_BeginBuild(void) } ResetArena(g->build_arena); g->boxes_count = 0; + g->first_free_style_node = 0; /* Init bins */ g->box_bins = PushStructs(g->build_arena, UI_BoxBin, UI_NumBoxLookupBins); @@ -223,12 +234,28 @@ void UI_BeginBuild(void) DllPushBackNP(bin->first, bin->last, g->root_box, next_in_bin, prev_in_bin); ++g->boxes_count; - /* Init stacks */ - UI_PushTagF("root"); - UI_PushParent(g->root_box); - UI_PushSize(Axis_X, UI_SizeKind_Percent, 1, 0); - UI_PushSize(Axis_Y, UI_SizeKind_Percent, 1, 0); - UI_PushLayoutDir(Axis_X); + /* 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; + switch(kind) + { + default: break; + case UI_StyleKind_Tag: { style->Tag = HashFnv64(Fnv64Basis, Lit("root")); } break; + case UI_StyleKind_Parent: { style->Parent = g->root_box; } break; + case UI_StyleKind_Width: { style->Width = UI_RatioSize(1, 0); } break; + case UI_StyleKind_Height: { style->Height = UI_RatioSize(1, 0); } break; + } + } + } if (!g->back_build_arena) { @@ -323,7 +350,7 @@ GPU_Resource *UI_EndBuild(Rect render_viewport) for (Axis axis = 0; axis < Axis_CountXY; ++axis) { UI_Size pref_size = box->pref_size[axis]; - if (pref_size.kind == UI_SizeKind_Percent) + if (pref_size.kind == UI_SizeKind_Ratio) { UI_Box *ancestor = box->parent; f32 ancestor_size = 0; @@ -349,7 +376,7 @@ GPU_Resource *UI_EndBuild(Rect render_viewport) Vec2 final_size = VEC2(box->solved_dims[0], box->solved_dims[1]); if (parent) { - b32 is_layout_x = parent->layout_dir == Axis_X; + b32 is_layout_x = parent->layout_axis == Axis_X; f32 layout_cursor = parent->layout_cursor; Vec2 offset = VEC2(layout_cursor * is_layout_x, layout_cursor * !is_layout_x); parent->layout_cursor += final_size.x * is_layout_x + final_size.y * is_layout_x; diff --git a/src/ui/ui.h b/src/ui/ui.h index c14db5ef..076a8427 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -15,7 +15,7 @@ Struct(UI_Key) Enum(UI_SizeKind) { UI_SizeKind_Children, - UI_SizeKind_Percent, + UI_SizeKind_Ratio, UI_SizeKind_Pixel, UI_SizeKind_Text, }; @@ -52,7 +52,7 @@ Struct(UI_Box) String display_text; GPU_Resource *display_image; UI_Size pref_size[Axis_CountXY]; - Axis layout_dir; + Axis layout_axis; u32 tint; //- Layout data @@ -71,12 +71,56 @@ Struct(UI_BoxBin) }; //////////////////////////////// -//~ Stack types +//~ Style types -Struct(UI_TagNode) { UI_TagNode *next; u64 hash; }; -Struct(UI_ParentNode) { UI_ParentNode *next; UI_Box *box; }; -Struct(UI_SizeNode) { UI_SizeNode *next; UI_Size size; }; -Struct(UI_AxisNode) { UI_AxisNode *next; Axis axis; }; +//- Style data +#define UI_StyleKindsXMacro(x) \ + x(Tag, u64) \ + x(Parent, UI_Box *) \ + x(LayoutAxis, Axis) \ + x(Width, UI_Size) \ + x(Height, UI_Size) \ + x(BackgroundColor, u32) \ + x(Opacity, f32) \ + +//- Style node +Enum(UI_StyleKind) +{ + #define X(name, type) UI_StyleKind_##name, + UI_StyleKind_None, + UI_StyleKindsXMacro(X) + UI_StyleKind_Count, + #undef X +}; + +Struct(UI_Style) +{ + UI_StyleKind kind; + /* Union of all style data types */ + union + { + #define X(name, type) type name; + UI_StyleKindsXMacro(X) + #undef X + }; +}; + +Struct(UI_StyleNode) +{ + UI_StyleNode *next; + b32 pop_when_touched; + u64 checkpoint; + UI_Style style; +}; + +//////////////////////////////// +//~ Checkpoint types + +Struct(UI_Checkpoint) +{ + UI_Checkpoint *next; + u64 v; +}; //////////////////////////////// //~ State types @@ -98,17 +142,16 @@ Struct(UI_SharedState) u64 boxes_count; u64 back_boxes_count; - //- Stack state - UI_TagNode *top_tag; - UI_ParentNode *top_parent; - UI_SizeNode *top_size[Axis_CountXY]; - UI_AxisNode *top_layout_dir; + UI_Checkpoint *top_checkpoint; + UI_Checkpoint *first_free_checkpoint; + + UI_StyleNode *style_tops[UI_StyleKind_Count]; + UI_StyleNode *first_free_style_node; //- Render state GPU_Resource *render_target; i64 render_fence_target; - GPU_TransientBuffer draw_rects_tbuff; Arena *draw_rects_arena; @@ -117,29 +160,37 @@ Struct(UI_SharedState) //////////////////////////////// //~ Key helpers +u64 UI_HashFromTop(void); +u64 UI_HashFromString(u64 seed, String str); UI_Key UI_KeyFromString(u64 seed, String str); //////////////////////////////// -//~ Stack helpers +//~ Checkpoint helpers -//- Tag -void UI_PushTagFromHash(u64 hash); -void UI_PushTagFromString(String str); -#define UI_PushTagF(fmt_cstr, ...) UI_PushTagF_(fmt_cstr, __VA_ARGS__, FmtEnd); -void UI_PushTagF_(char *fmt_cstr, ...); -void UI_PopTag(void); +void UI_PushCheckpoint(void); +void UI_PopCheckpoint(void); -//- Parent -void UI_PushParent(UI_Box *box); -void UI_PopParent(void); +//////////////////////////////// +//~ Style stack helpers -//- Size -void UI_PushSize(Axis axis, UI_SizeKind kind, f32 v, f32 strictness); -void UI_PopSize(Axis axis); +UI_StyleNode *UI_PushStyleNode(UI_StyleKind kind, b32 pop_when_touched, UI_Style desc); +UI_Style UI_PopStyleNode(UI_StyleKind kind); +UI_StyleNode *UI_PeekTopStyleNode(UI_StyleKind kind); +UI_Style UI_StyleFromTopNode(UI_StyleKind kind, b32 touch); -//- Layout dir -void UI_PushLayoutDir(Axis dir); -void UI_PopLayoutDir(void); +#define UI_SetNext(name, ...) UI_PushStyleNode(UI_StyleKind_##name, 1, (UI_Style) { .name = __VA_ARGS__ }) +#define UI_Push(name, ...) UI_PushStyleNode(UI_StyleKind_##name, 0, (UI_Style) { .name = __VA_ARGS__ }) +#define UI_Pop(name, ...) UI_PopStyleNode(UI_StyleKind_##name).name +#define UI_TouchTop(name) UI_StyleFromTopNode(UI_StyleKind_##name, 1).name +#define UI_PeekTop(name) UI_StyleFromTopNode(UI_StyleKind_##name, 0).name + +//////////////////////////////// +//~ Size helpers + +#define UI_ChildrenSize(_strictness) ((UI_Size) { .kind = UI_SizeKind_Children, .strictness = strictness_ }) +#define UI_RatioSize(_ratio, _strictness) ((UI_Size) { .kind = UI_SizeKind_Ratio, .v = _ratio, .strictness = _strictness }) +#define UI_PixelSize(_pixels, _strictness) ((UI_Size) { .kind = UI_SizeKind_Pixel, .v = _pixels, .strictness = _strictness }) +#define UI_TextSize(_strictness) ((UI_Size) { .kind = UI_SizeKind_Text, .strictness = _strictness }) //////////////////////////////// //~ Box