From e0ba1155e843d7066f28da94519c636f5e9c8b28 Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Fri, 25 Apr 2025 08:29:44 -0700 Subject: [PATCH] eliminate recursive version of final ui layout position pass --- src/ui/ui_core.c | 272 +++++++++++++++++++++++------------------------ src/ui/ui_core.h | 10 +- 2 files changed, 137 insertions(+), 145 deletions(-) diff --git a/src/ui/ui_core.c b/src/ui/ui_core.c index 7a853e81..70abe70e 100644 --- a/src/ui/ui_core.c +++ b/src/ui/ui_core.c @@ -1272,11 +1272,11 @@ ui_end_build(void) root->rect = new_root_rect; for(Axis2 axis = (Axis2)0; axis < Axis2_COUNT; axis = (Axis2)(axis + 1)) { - ui_calc_sizes_standalone__in_place_rec(root, axis); - ui_calc_sizes_upwards_dependent__in_place_rec(root, axis); - ui_calc_sizes_downwards_dependent__in_place_rec(root, axis); - ui_layout_enforce_constraints__in_place_rec(root, axis); - ui_layout_position__in_place_rec(root, axis); + ui_calc_sizes_standalone__in_place(root, axis); + ui_calc_sizes_upwards_dependent__in_place(root, axis); + ui_calc_sizes_downwards_dependent__in_place(root, axis); + ui_layout_enforce_constraints__in_place(root, axis); + ui_layout_position__in_place(root, axis); } } } @@ -1613,7 +1613,7 @@ ui_end_build(void) } internal void -ui_calc_sizes_standalone__in_place_rec(UI_Box *root, Axis2 axis) +ui_calc_sizes_standalone__in_place(UI_Box *root, Axis2 axis) { ProfBeginFunction(); for(UI_Box *b = root; !ui_box_is_nil(b); b = ui_box_rec_df_pre(b, root).next) @@ -1637,7 +1637,7 @@ ui_calc_sizes_standalone__in_place_rec(UI_Box *root, Axis2 axis) } internal void -ui_calc_sizes_upwards_dependent__in_place_rec(UI_Box *root, Axis2 axis) +ui_calc_sizes_upwards_dependent__in_place(UI_Box *root, Axis2 axis) { ProfBeginFunction(); for(UI_Box *b = root; !ui_box_is_nil(b); b = ui_box_rec_df_pre(b, root).next) @@ -1673,7 +1673,7 @@ ui_calc_sizes_upwards_dependent__in_place_rec(UI_Box *root, Axis2 axis) } internal void -ui_calc_sizes_downwards_dependent__in_place_rec(UI_Box *root, Axis2 axis) +ui_calc_sizes_downwards_dependent__in_place(UI_Box *root, Axis2 axis) { ProfBeginFunction(); UI_BoxRec rec = {0}; @@ -1710,180 +1710,172 @@ ui_calc_sizes_downwards_dependent__in_place_rec(UI_Box *root, Axis2 axis) } internal void -ui_layout_enforce_constraints__in_place_rec(UI_Box *root, Axis2 axis) +ui_layout_enforce_constraints__in_place(UI_Box *root, Axis2 axis) { ProfBeginFunction(); Temp scratch = scratch_begin(0, 0); - - // NOTE(rjf): The "layout axis" is the direction in which children - // of some node are intended to be laid out. - - //- rjf: fixup children sizes (if we're solving along the *non-layout* axis) - if(axis != root->child_layout_axis && !(root->flags & (UI_BoxFlag_AllowOverflowX << axis))) + for(UI_Box *box = root; !ui_box_is_nil(box); box = ui_box_rec_df_pre(box, root).next) { - F32 allowed_size = root->fixed_size.v[axis]; - for(UI_Box *child = root->first; !ui_box_is_nil(child); child = child->next) + //- rjf: fixup children sizes (if we're solving along the *non-layout* axis) + if(axis != box->child_layout_axis && !(box->flags & (UI_BoxFlag_AllowOverflowX << axis))) { - if(!(child->flags & (UI_BoxFlag_FloatingX<fixed_size.v[axis]; + for(UI_Box *child = box->first; !ui_box_is_nil(child); child = child->next) { - F32 child_size = child->fixed_size.v[axis]; - F32 violation = child_size - allowed_size; - F32 max_fixup = child_size; - F32 fixup = Clamp(0, violation, max_fixup); - if(fixup > 0) + if(!(child->flags & (UI_BoxFlag_FloatingX<fixed_size.v[axis] -= fixup; + F32 child_size = child->fixed_size.v[axis]; + F32 violation = child_size - allowed_size; + F32 max_fixup = child_size; + F32 fixup = Clamp(0, violation, max_fixup); + if(fixup > 0) + { + child->fixed_size.v[axis] -= fixup; + } } } } - } - - //- rjf: fixup children sizes (in the direction of the layout axis) - if(axis == root->child_layout_axis && !(root->flags & (UI_BoxFlag_AllowOverflowX << axis))) - { - // rjf: figure out total allowed size & total size - F32 total_allowed_size = root->fixed_size.v[axis]; - F32 total_size = 0; - F32 total_weighted_size = 0; - for(UI_Box *child = root->first; !ui_box_is_nil(child); child = child->next) - { - if(!(child->flags & (UI_BoxFlag_FloatingX<fixed_size.v[axis]; - total_weighted_size += child->fixed_size.v[axis] * (1-child->pref_size[axis].strictness); - } - } - // rjf: if we have a violation, we need to subtract some amount from all children - F32 violation = total_size - total_allowed_size; - if(violation > 0 && total_weighted_size > 0) + //- rjf: fixup children sizes (in the direction of the layout axis) + if(axis == box->child_layout_axis && !(box->flags & (UI_BoxFlag_AllowOverflowX << axis))) { - // rjf: figure out how much we can take in totality - F32 child_fixup_sum = 0; - F32 *child_fixups = push_array(scratch.arena, F32, root->child_count); + // rjf: figure out total allowed size & total size + F32 total_allowed_size = box->fixed_size.v[axis]; + F32 total_size = 0; + F32 total_weighted_size = 0; + for(UI_Box *child = box->first; !ui_box_is_nil(child); child = child->next) { - U64 child_idx = 0; - for(UI_Box *child = root->first; !ui_box_is_nil(child); child = child->next, child_idx += 1) + if(!(child->flags & (UI_BoxFlag_FloatingX<flags & (UI_BoxFlag_FloatingX<fixed_size.v[axis] * (1-child->pref_size[axis].strictness); - fixup_size_this_child = ClampBot(0, fixup_size_this_child); - child_fixups[child_idx] = fixup_size_this_child; - child_fixup_sum += fixup_size_this_child; - } + total_size += child->fixed_size.v[axis]; + total_weighted_size += child->fixed_size.v[axis] * (1-child->pref_size[axis].strictness); } } - // rjf: fixup child sizes + // rjf: if we have a violation, we need to subtract some amount from all children + F32 violation = total_size - total_allowed_size; + if(violation > 0 && total_weighted_size > 0) { - U64 child_idx = 0; - for(UI_Box *child = root->first; !ui_box_is_nil(child); child = child->next, child_idx += 1) + Temp temp = temp_begin(scratch.arena); + + // rjf: figure out how much we can take in totality + F32 child_fixup_sum = 0; + F32 *child_fixups = push_array(temp.arena, F32, box->child_count); { - if(!(child->flags & (UI_BoxFlag_FloatingX<first; !ui_box_is_nil(child); child = child->next, child_idx += 1) { - F32 fixup_pct = (violation / total_weighted_size); - fixup_pct = Clamp(0, fixup_pct, 1); - child->fixed_size.v[axis] -= child_fixups[child_idx] * fixup_pct; + if(!(child->flags & (UI_BoxFlag_FloatingX<fixed_size.v[axis] * (1-child->pref_size[axis].strictness); + fixup_size_this_child = ClampBot(0, fixup_size_this_child); + child_fixups[child_idx] = fixup_size_this_child; + child_fixup_sum += fixup_size_this_child; + } } } + + // rjf: fixup child sizes + { + U64 child_idx = 0; + for(UI_Box *child = box->first; !ui_box_is_nil(child); child = child->next, child_idx += 1) + { + if(!(child->flags & (UI_BoxFlag_FloatingX<fixed_size.v[axis] -= child_fixups[child_idx] * fixup_pct; + } + } + } + + temp_end(temp); + } + } + + //- rjf: fixup upwards-relative sizes + if(box->flags & (UI_BoxFlag_AllowOverflowX << axis)) + { + for(UI_Box *child = box->first; !ui_box_is_nil(child); child = child->next) + { + if(child->pref_size[axis].kind == UI_SizeKind_ParentPct) + { + child->fixed_size.v[axis] = box->fixed_size.v[axis] * child->pref_size[axis].value; + } } } - } - - //- rjf: fixup upwards-relative sizes - if(root->flags & (UI_BoxFlag_AllowOverflowX << axis)) - { - for(UI_Box *child = root->first; !ui_box_is_nil(child); child = child->next) + + //- rjf: enforce clamps + for(UI_Box *child = box->first; !ui_box_is_nil(child); child = child->next) { - if(child->pref_size[axis].kind == UI_SizeKind_ParentPct) - { - child->fixed_size.v[axis] = root->fixed_size.v[axis] * child->pref_size[axis].value; - } + child->fixed_size.v[axis] = Max(child->fixed_size.v[axis], child->min_size.v[axis]); } } - - //- rjf: enforce clamps - for(UI_Box *child = root->first; !ui_box_is_nil(child); child = child->next) - { - child->fixed_size.v[axis] = Max(child->fixed_size.v[axis], child->min_size.v[axis]); - } - - //- rjf: recurse - for(UI_Box *child = root->first; !ui_box_is_nil(child); child = child->next) - { - ui_layout_enforce_constraints__in_place_rec(child, axis); - } - scratch_end(scratch); ProfEnd(); } internal void -ui_layout_position__in_place_rec(UI_Box *root, Axis2 axis) +ui_layout_position__in_place(UI_Box *root, Axis2 axis) { ProfBeginFunction(); - F32 layout_position = 0; - - //- rjf: lay out children - F32 bounds = 0; - for(UI_Box *child = root->first; !ui_box_is_nil(child); child = child->next) + for(UI_Box *box = root; !ui_box_is_nil(box); box = ui_box_rec_df_pre(box, root).next) { - // rjf: grab original position - F32 original_position = Min(child->rect.p0.v[axis], child->rect.p1.v[axis]); + F32 layout_position = 0; - // rjf: calculate fixed position & size - if(!(child->flags & (UI_BoxFlag_FloatingX<first; !ui_box_is_nil(child); child = child->next) { - child->fixed_position.v[axis] = layout_position; - if(root->child_layout_axis == axis) + // rjf: grab original position + F32 original_position = Min(child->rect.p0.v[axis], child->rect.p1.v[axis]); + + // rjf: calculate fixed position & size + if(!(child->flags & (UI_BoxFlag_FloatingX<fixed_size.v[axis]; - bounds += child->fixed_size.v[axis]; + child->fixed_position.v[axis] = layout_position; + if(box->child_layout_axis == axis) + { + layout_position += child->fixed_size.v[axis]; + bounds += child->fixed_size.v[axis]; + } + else + { + bounds = Max(bounds, child->fixed_size.v[axis]); + } + } + + // rjf: determine final rect for child, given fixed_position & size + if(child->flags & (UI_BoxFlag_AnimatePosX<first_touched_build_index == child->last_touched_build_index) + { + child->fixed_position_animated = child->fixed_position; + } + child->rect.p0.v[axis] = box->rect.p0.v[axis] + child->fixed_position_animated.v[axis] - !(child->flags&(UI_BoxFlag_SkipViewOffX<view_off.v[axis]); } else { - bounds = Max(bounds, child->fixed_size.v[axis]); + child->rect.p0.v[axis] = box->rect.p0.v[axis] + child->fixed_position.v[axis] - !(child->flags&(UI_BoxFlag_SkipViewOffX<view_off.v[axis]); } + child->rect.p1.v[axis] = child->rect.p0.v[axis] + child->fixed_size.v[axis]; + child->rect.p0.x = floor_f32(child->rect.p0.x); + child->rect.p0.y = floor_f32(child->rect.p0.y); + child->rect.p1.x = floor_f32(child->rect.p1.x); + child->rect.p1.y = floor_f32(child->rect.p1.y); + + // rjf: grab new position + F32 new_position = Min(child->rect.p0.v[axis], child->rect.p1.v[axis]); + + // rjf: store position delta + child->position_delta.v[axis] = new_position - original_position; } - // rjf: determine final rect for child, given fixed_position & size - if(child->flags & (UI_BoxFlag_AnimatePosX<first_touched_build_index == child->last_touched_build_index) - { - child->fixed_position_animated = child->fixed_position; - } - child->rect.p0.v[axis] = root->rect.p0.v[axis] + child->fixed_position_animated.v[axis] - !(child->flags&(UI_BoxFlag_SkipViewOffX<view_off.v[axis]); + box->view_bounds.v[axis] = bounds; } - else - { - child->rect.p0.v[axis] = root->rect.p0.v[axis] + child->fixed_position.v[axis] - !(child->flags&(UI_BoxFlag_SkipViewOffX<view_off.v[axis]); - } - child->rect.p1.v[axis] = child->rect.p0.v[axis] + child->fixed_size.v[axis]; - child->rect.p0.x = floor_f32(child->rect.p0.x); - child->rect.p0.y = floor_f32(child->rect.p0.y); - child->rect.p1.x = floor_f32(child->rect.p1.x); - child->rect.p1.y = floor_f32(child->rect.p1.y); - - // rjf: grab new position - F32 new_position = Min(child->rect.p0.v[axis], child->rect.p1.v[axis]); - - // rjf: store position delta - child->position_delta.v[axis] = new_position - original_position; } - - //- rjf: store view bounds - { - root->view_bounds.v[axis] = bounds; - } - - //- rjf: recurse - for(UI_Box *child = root->first; !ui_box_is_nil(child); child = child->next) - { - ui_layout_position__in_place_rec(child, axis); - } - ProfEnd(); } @@ -1891,11 +1883,11 @@ internal void ui_layout_root(UI_Box *root, Axis2 axis) { ProfBegin("ui layout pass (%s)", axis == Axis2_X ? "x" : "y"); - ui_calc_sizes_standalone__in_place_rec(root, axis); - ui_calc_sizes_upwards_dependent__in_place_rec(root, axis); - ui_calc_sizes_downwards_dependent__in_place_rec(root, axis); - ui_layout_enforce_constraints__in_place_rec(root, axis); - ui_layout_position__in_place_rec(root, axis); + ui_calc_sizes_standalone__in_place(root, axis); + ui_calc_sizes_upwards_dependent__in_place(root, axis); + ui_calc_sizes_downwards_dependent__in_place(root, axis); + ui_layout_enforce_constraints__in_place(root, axis); + ui_layout_position__in_place(root, axis); ProfEnd(); } diff --git a/src/ui/ui_core.h b/src/ui/ui_core.h index f2546163..98c0349c 100644 --- a/src/ui/ui_core.h +++ b/src/ui/ui_core.h @@ -862,11 +862,11 @@ internal UI_Box * ui_box_from_key(UI_Key key); internal void ui_begin_build(OS_Handle window, UI_EventList *events, UI_IconInfo *icon_info, UI_Theme *theme, UI_AnimationInfo *animation_info, F32 real_dt, F32 animation_dt); internal void ui_end_build(void); -internal void ui_calc_sizes_standalone__in_place_rec(UI_Box *root, Axis2 axis); -internal void ui_calc_sizes_upwards_dependent__in_place_rec(UI_Box *root, Axis2 axis); -internal void ui_calc_sizes_downwards_dependent__in_place_rec(UI_Box *root, Axis2 axis); -internal void ui_layout_enforce_constraints__in_place_rec(UI_Box *root, Axis2 axis); -internal void ui_layout_position__in_place_rec(UI_Box *root, Axis2 axis); +internal void ui_calc_sizes_standalone__in_place(UI_Box *root, Axis2 axis); +internal void ui_calc_sizes_upwards_dependent__in_place(UI_Box *root, Axis2 axis); +internal void ui_calc_sizes_downwards_dependent__in_place(UI_Box *root, Axis2 axis); +internal void ui_layout_enforce_constraints__in_place(UI_Box *root, Axis2 axis); +internal void ui_layout_position__in_place(UI_Box *root, Axis2 axis); internal void ui_layout_root(UI_Box *root, Axis2 axis); ////////////////////////////////