eliminate recursive version of final ui layout position pass

This commit is contained in:
Ryan Fleury
2025-04-25 08:29:44 -07:00
parent fe46e61acb
commit e0ba1155e8
2 changed files with 137 additions and 145 deletions
+132 -140
View File
@@ -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<<axis)))
F32 allowed_size = box->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<<axis)))
{
child->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<<axis)))
{
total_size += child->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<<axis)))
{
if(!(child->flags & (UI_BoxFlag_FloatingX<<axis)))
{
F32 fixup_size_this_child = child->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<<axis)))
U64 child_idx = 0;
for(UI_Box *child = box->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<<axis)))
{
F32 fixup_size_this_child = child->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<<axis)))
{
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;
}
}
}
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<<axis)))
//- rjf: lay out children
F32 bounds = 0;
for(UI_Box *child = box->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<<axis)))
{
layout_position += child->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<<axis))
{
if(child->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<<axis))*floor_f32(box->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<<axis))*floor_f32(box->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<<axis))
//- rjf: store view bounds
{
if(child->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<<axis))*floor_f32(root->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<<axis))*floor_f32(root->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();
}
+5 -5
View File
@@ -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);
////////////////////////////////