fix box dims for odd font sizes

This commit is contained in:
jacob 2025-12-30 06:51:37 -06:00
parent b3012c37bd
commit 78d0a8b832
4 changed files with 131 additions and 126 deletions

View File

@ -179,16 +179,16 @@ GC_Run GC_RunFromString(Arena *arena, String str, GC_FontKey font, f32 font_size
GC_Glyph *glyph = ready_glyphs[glyph_idx]; GC_Glyph *glyph = ready_glyphs[glyph_idx];
GC_RunRect *rect = &result.rects[glyph_idx]; GC_RunRect *rect = &result.rects[glyph_idx];
f32 advance = advance = glyph->advance; f32 advance = glyph->advance;
if (TweakB32("Ceil glyph advances", 0)) if (TweakB32("Ceil glyph advances", 0))
{ {
advance = CeilF32(advance); advance = CeilF32(advance);
} }
if (TweakB32("Floor glyph advances", 1)) if (TweakB32("Floor glyph advances", 0))
{ {
advance = FloorF32(advance); advance = FloorF32(advance);
} }
if (TweakB32("Round glyph advances", 0)) if (TweakB32("Round glyph advances", 1))
{ {
advance = RoundF32(advance); advance = RoundF32(advance);
} }

View File

@ -82,13 +82,15 @@ V_WidgetTheme V_GetWidgetTheme(void)
// theme.font = GC_FontKeyFromResource(ResourceKeyFromStore(&V_Resources, Lit("font/fixedsys.ttf"))); // theme.font = GC_FontKeyFromResource(ResourceKeyFromStore(&V_Resources, Lit("font/fixedsys.ttf")));
// theme.font_size = 16; // theme.font_size = 16;
// theme.font = GC_FontKeyFromResource(ResourceKeyFromStore(&V_Resources, Lit("font/seguisb.ttf")));
// theme.font_size = 16;
theme.font = GC_FontKeyFromResource(ResourceKeyFromStore(&V_Resources, Lit("font/seguisb.ttf"))); theme.font = GC_FontKeyFromResource(ResourceKeyFromStore(&V_Resources, Lit("font/seguisb.ttf")));
theme.font_size = 16; theme.font_size = 14;
// theme.font = GC_FontKeyFromResource(ResourceKeyFromStore(&V_Resources, Lit("font/seguisb.ttf"))); // theme.font = GC_FontKeyFromResource(ResourceKeyFromStore(&V_Resources, Lit("font/seguisb.ttf")));
// theme.font_size = 12; // theme.font_size = 12;
// theme.font = GC_FontKeyFromResource(ResourceKeyFromStore(&V_Resources, Lit("font/seguisb.ttf"))); // theme.font = GC_FontKeyFromResource(ResourceKeyFromStore(&V_Resources, Lit("font/seguisb.ttf")));
// theme.font_size = 30; // theme.font_size = 30;
@ -253,7 +255,7 @@ void V_TickForever(WaveLaneCtx *lane)
frame->tick = V.current_frame_tick; frame->tick = V.current_frame_tick;
frame->time_ns = TimeNs(); frame->time_ns = TimeNs();
frame->dt_ns = frame->time_ns - last_frame->time_ns; frame->dt_ns = ClampI64(frame->time_ns - last_frame->time_ns, 1, NsFromSeconds(1.0 / 50));
frame->dt = SecondsFromNs(frame->dt_ns); frame->dt = SecondsFromNs(frame->dt_ns);
if (S_IsKeyNil(V.player_key)) if (S_IsKeyNil(V.player_key))
@ -341,18 +343,18 @@ void V_TickForever(WaveLaneCtx *lane)
V_WidgetTheme theme = V_GetWidgetTheme(); V_WidgetTheme theme = V_GetWidgetTheme();
V_PushWidgetThemeStyles(theme); V_PushWidgetThemeStyles(theme);
UI_Push(ChildLayoutAxis, Axis_Y);
UI_Push(Width, UI_GROW(1, 0));
UI_Push(Height, UI_GROW(1, 0));
UI_SetNext(Flags, UI_BoxFlag_Interactable);
UI_Key vis_box = UI_KeyF("vis box"); UI_Key vis_box = UI_KeyF("vis box");
UI_Push(Parent, UI_BuildColumnEx(vis_box)); UI_Report vis_box_rep = UI_ReportFromKey(vis_box);
{ {
// TODO: Don't rely on ui report for draw size since it introduces one frame of delay when resizing UI_Push(ChildLayoutAxis, Axis_Y);
UI_Report vis_rep = UI_ReportFromKey(vis_box); UI_Push(Width, UI_GROW(1, 0));
frame->ui_dims = RoundVec2ToI32(DimsFromRng2(vis_rep.screen_rect)); UI_Push(Height, UI_GROW(1, 0));
UI_SetNext(Flags, UI_BoxFlag_Interactable);
UI_Push(Parent, UI_BuildColumnEx(vis_box));
} }
// TODO: Don't rely on ui report for draw size since it introduces one frame of delay when resizing
frame->ui_dims = RoundVec2ToI32(DimsFromRng2(vis_box_rep.screen_rect));
frame->ui_dims.x = MaxI32(frame->ui_dims.x, 64); frame->ui_dims.x = MaxI32(frame->ui_dims.x, 64);
frame->ui_dims.y = MaxI32(frame->ui_dims.y, 64); frame->ui_dims.y = MaxI32(frame->ui_dims.y, 64);
frame->draw_dims = frame->ui_dims; frame->draw_dims = frame->ui_dims;
@ -1284,7 +1286,7 @@ void V_TickForever(WaveLaneCtx *lane)
//- Build command palette //- Build command palette
V_Palette *palette = &frame->palette; V_Palette *palette = &frame->palette;
palette->show = LerpF32(palette->show, palette->pref_show, 50.0 * frame->dt); palette->show = LerpF32(palette->show, palette->pref_show, 30.0 * frame->dt);
if (palette->show > 0.001) if (palette->show > 0.001)
{ {
palette->key = UI_KeyF("command palette"); palette->key = UI_KeyF("command palette");
@ -1307,7 +1309,7 @@ void V_TickForever(WaveLaneCtx *lane)
window_border_color = LerpSrgb(window_border_color, Rgb32(0x0078a6), titlebar_rep.hot); window_border_color = LerpSrgb(window_border_color, Rgb32(0x0078a6), titlebar_rep.hot);
UI_Push(Tint, VEC4(1, 1, 1, palette->show)); UI_Push(Tint, VEC4(1, 1, 1, palette->show));
UI_SetNext(Scale, LerpF32(0.75, 1, palette->show)); UI_SetNext(Scale, LerpF32(0.85, 1, palette->show));
UI_Push(BackgroundColor, window_background_color); UI_Push(BackgroundColor, window_background_color);
UI_Push(BorderColor, window_border_color); UI_Push(BorderColor, window_border_color);
@ -1318,7 +1320,7 @@ void V_TickForever(WaveLaneCtx *lane)
UI_Push(ChildLayoutAxis, Axis_Y); UI_Push(ChildLayoutAxis, Axis_Y);
UI_Push(FloatingPos, palette->pos); UI_Push(FloatingPos, palette->pos);
UI_SetNext(FloatingPosAnchor, UI_Region_Center); UI_SetNext(FloatingPosAnchor, UI_Region_Center);
UI_SetNext(Flags, UI_BoxFlag_Floating); UI_SetNext(Flags, UI_BoxFlag_Floating | UI_BoxFlag_Interactable);
UI_PushCP(UI_BuildBoxEx(palette->key)); UI_PushCP(UI_BuildBoxEx(palette->key));
{ {
// Title bar // Title bar
@ -1442,7 +1444,8 @@ void V_TickForever(WaveLaneCtx *lane)
for (PaletteItem *item = first_item; item; item = item->next) for (PaletteItem *item = first_item; item; item = item->next)
{ {
UI_BuildDivider(UI_PIX(1, 1), theme.divider_color, Axis_Y); // Divider
UI_BuildDivider(UI_PIX(1, 1), divider_color, Axis_Y);
UI_Report item_rep = UI_ReportFromKey(item->key); UI_Report item_rep = UI_ReportFromKey(item->key);
if (item_rep.m1.presses) if (item_rep.m1.presses)

View File

@ -1112,6 +1112,18 @@ void UI_EndFrame(UI_Frame *frame)
box->cursor = 0; box->cursor = 0;
ZeroStructs(box->final_children_size_accum, countof(box->final_children_size_accum)); ZeroStructs(box->final_children_size_accum, countof(box->final_children_size_accum));
box->solved_dims = VEC2(0, 0); box->solved_dims = VEC2(0, 0);
// Compute scale
UI_Box *parent = box->parent;
box->solved_scale = box->desc.scale;
if (parent)
{
box->solved_scale *= parent->solved_scale;
}
if (AbsF32(1.0 - box->solved_scale) < 0.003)
{
box->solved_scale = 1;
}
} }
else else
{ {
@ -1124,7 +1136,7 @@ void UI_EndFrame(UI_Frame *frame)
Assert(post_index == boxes_count); Assert(post_index == boxes_count);
} }
// Compute independent sizes // Solve independent sizes
for (u64 pre_index = 0; pre_index < boxes_count; ++pre_index) for (u64 pre_index = 0; pre_index < boxes_count; ++pre_index)
{ {
UI_Box *box = boxes_pre[pre_index]; UI_Box *box = boxes_pre[pre_index];
@ -1141,7 +1153,7 @@ void UI_EndFrame(UI_Frame *frame)
f32 text_size = 0; f32 text_size = 0;
if (axis == Axis_X) if (axis == Axis_X)
{ {
text_size = CeilF32(box->glyph_run.baseline_length); text_size = box->glyph_run.baseline_length;
} }
else else
{ {
@ -1152,7 +1164,7 @@ void UI_EndFrame(UI_Frame *frame)
} }
} }
// Compute upwards-dependent sizes along layout axis // Solve upwards-dependent sizes along layout axis
for (u64 pre_index = 0; pre_index < boxes_count; ++pre_index) for (u64 pre_index = 0; pre_index < boxes_count; ++pre_index)
{ {
UI_Box *box = boxes_pre[pre_index]; UI_Box *box = boxes_pre[pre_index];
@ -1179,7 +1191,7 @@ void UI_EndFrame(UI_Frame *frame)
} }
} }
// Compute downwards-dependent sizes // Solve downwards-dependent sizes
for (u64 post_index = 0; post_index < boxes_count; ++post_index) for (u64 post_index = 0; post_index < boxes_count; ++post_index)
{ {
UI_Box *box = boxes_post[post_index]; UI_Box *box = boxes_post[post_index];
@ -1193,22 +1205,23 @@ void UI_EndFrame(UI_Frame *frame)
{ {
if (!AnyBit(child->desc.flags, UI_BoxFlag_Floating)) if (!AnyBit(child->desc.flags, UI_BoxFlag_Floating))
{ {
f32 child_size = child->solved_dims.v[axis];
if (axis == box->desc.child_layout_axis) if (axis == box->desc.child_layout_axis)
{ {
accum += child->solved_dims.v[axis]; accum += child_size;
} }
else else
{ {
accum = MaxF32(child->solved_dims.v[axis], accum); accum = MaxF32(child_size, accum);
} }
} }
} }
box->solved_dims.v[axis] = accum + (sem_dims.v * 2); box->solved_dims.v[axis] = CeilF32(accum + (sem_dims.v * 2));
} }
} }
} }
// Compute upwards-dependent sizes along non-layout axis // Solve upwards-dependent sizes along non-layout axis
for (u64 pre_index = 0; pre_index < boxes_count; ++pre_index) for (u64 pre_index = 0; pre_index < boxes_count; ++pre_index)
{ {
UI_Box *box = boxes_pre[pre_index]; UI_Box *box = boxes_pre[pre_index];
@ -1230,108 +1243,76 @@ void UI_EndFrame(UI_Frame *frame)
for (Axis axis = 0; axis < Axis_COUNTXY; ++axis) for (Axis axis = 0; axis < Axis_COUNTXY; ++axis)
{ {
f32 box_size = box->solved_dims.v[axis]; f32 box_size = box->solved_dims.v[axis];
// Solve non-floating violations // Accumulate non-floating sizes
{ {
f32 size_accum = 0; f32 unconstrained_size_accum = 0;
f32 flex_accum = 0; f32 flex_accum = 0;
for (UI_Box *child = box->first; child; child = child->next) f32 violation = 0;
{ {
if (!AnyBit(child->desc.flags, UI_BoxFlag_Floating))
{
f32 size = child->solved_dims.v[axis];
f32 strictness = child->desc.pref_semantic_dims[axis].strictness;
f32 flex = size * (1.0 - strictness);
if (axis == box->desc.child_layout_axis)
{
size_accum += size;
flex_accum += flex;
}
else
{
size_accum = MaxF32(size_accum, size);
flex_accum = MaxF32(flex_accum, flex);
}
}
}
f32 violation = size_accum - box_size;
if (violation > 0 && flex_accum > 0)
{
f32 adjusted_size_accum = 0;
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)) b32 is_floating = AnyBit(child->desc.flags, UI_BoxFlag_Floating);
if (!is_floating)
{ {
child->solved_dims.v[axis] = RoundF32(child->solved_dims.v[axis]);
f32 size = child->solved_dims.v[axis]; f32 size = child->solved_dims.v[axis];
f32 strictness = child->desc.pref_semantic_dims[axis].strictness; f32 strictness = child->desc.pref_semantic_dims[axis].strictness;
f32 flex = size * (1.0 - strictness); f32 flex = size * (1.0 - strictness);
f32 new_size = size;
if (axis == box->desc.child_layout_axis) if (axis == box->desc.child_layout_axis)
{ {
f32 chopoff = MinF32(flex, violation * (flex / flex_accum)); unconstrained_size_accum += size;
new_size = size - chopoff; flex_accum += flex;
} }
else else
{ {
if (size > box_size) unconstrained_size_accum = MaxF32(unconstrained_size_accum, size);
{ flex_accum = MaxF32(flex_accum, flex);
new_size = MaxF32(size - flex, box_size);
}
} }
adjusted_size_accum += new_size;
child->solved_dims.v[axis] = new_size;
} }
} }
size_accum = adjusted_size_accum; violation = unconstrained_size_accum - box_size;
} }
box->final_children_size_accum[axis] = size_accum;
}
// Solve floating violations
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))
{ {
f32 size = child->solved_dims.v[axis]; f32 size_accum = 0;
if (size > box_size) for (UI_Box *child = box->first; child; child = child->next)
{ {
b32 is_floating = AnyBit(child->desc.flags, UI_BoxFlag_Floating);
f32 unconstrained_size = child->solved_dims.v[axis];
f32 strictness = child->desc.pref_semantic_dims[axis].strictness; f32 strictness = child->desc.pref_semantic_dims[axis].strictness;
f32 flex = size * (1.0 - strictness); f32 flex = unconstrained_size * (1.0 - strictness);
child->solved_dims.v[axis] = MaxF32(size - flex, box_size); f32 new_size = unconstrained_size;
// Solve non-floating violation
if (!is_floating && violation > 0 && flex_accum > 0)
{
if (axis == box->desc.child_layout_axis)
{
f32 chopoff = MinF32(flex, violation * (flex / flex_accum));
new_size = new_size - chopoff;
}
else if (new_size > box_size)
{
new_size = MaxF32(new_size - flex, box_size);
}
}
// Solve floating violation
if (is_floating && new_size > box_size && !AnyBit(child->desc.flags, UI_BoxFlag_NoFloatingClamp))
{
new_size = MaxF32(new_size - flex, box_size);
}
if (!is_floating)
{
size_accum += new_size;
}
child->solved_dims.v[axis] = new_size;
} }
box->final_children_size_accum[axis] = size_accum;
} }
} }
} }
} }
// Apply scale // Solve final positions
for (u64 pre_index = 0; pre_index < boxes_count; ++pre_index)
{
UI_Box *box = boxes_pre[pre_index];
UI_Box *parent = box->parent;
box->solved_scale = box->desc.scale;
if (parent)
{
box->solved_scale *= parent->solved_scale;
}
for (Axis axis = 0; axis < Axis_COUNTXY; ++axis)
{
UI_Size sem_dims = box->desc.pref_semantic_dims[axis];
f32 unscaled_size = box->solved_dims.v[axis];
f32 scaled_size = unscaled_size * box->solved_scale;
// if (AbsF32(1.0 - box->solved_scale) < 0.001)
// {
// scaled_size = unscaled_size;
// }
if (unscaled_size >= 1)
{
scaled_size = MaxF32(scaled_size, 1);
}
box->solved_dims.v[axis] = scaled_size;
}
}
// Compute final positions
for (u64 pre_index = 0; pre_index < boxes_count; ++pre_index) for (u64 pre_index = 0; pre_index < boxes_count; ++pre_index)
{ {
UI_Box *box = boxes_pre[pre_index]; UI_Box *box = boxes_pre[pre_index];
@ -1348,6 +1329,22 @@ void UI_EndFrame(UI_Frame *frame)
alignment_in_parent = UI_PairFromRegion(UI_Region_TopLeft); alignment_in_parent = UI_PairFromRegion(UI_Region_TopLeft);
} }
// Apply scale
for (Axis axis = 0; axis < Axis_COUNTXY; ++axis)
{
f32 unscaled_size = box->solved_dims.v[axis];
f32 scaled_size = 0;
if (box->solved_scale == 1)
{
scaled_size = CeilF32(unscaled_size);
}
else
{
scaled_size = unscaled_size * box->solved_scale;
}
box->solved_dims.v[axis] = scaled_size;
}
// Initialize layout cursor based on alignment // Initialize layout cursor based on alignment
{ {
Axis axis = box->desc.child_layout_axis; Axis axis = box->desc.child_layout_axis;
@ -1366,13 +1363,16 @@ void UI_EndFrame(UI_Frame *frame)
box->cursor = box_size - size_accum; box->cursor = box_size - size_accum;
} break; } break;
} }
// box->cursor = FloorF32(box->cursor);
} }
// Position // Position
{ {
Vec2 final_pos = Zi; Vec2 screen_pos = Zi;
Vec2 offset = Zi; Vec2 offset = Zi;
Vec2 anchor_offset = Zi; Vec2 anchor_offset = Zi;
// Vec2 screen_dims = CeilVec2(box->solved_dims);
Vec2 screen_dims = box->solved_dims;
// Floating box position // Floating box position
if (AnyBit(box->desc.flags, UI_BoxFlag_Floating)) if (AnyBit(box->desc.flags, UI_BoxFlag_Floating))
@ -1387,28 +1387,31 @@ void UI_EndFrame(UI_Frame *frame)
default: break; default: break;
case UI_AxisRegion_Center: case UI_AxisRegion_Center:
{ {
anchor_offset.v[axis] = box->solved_dims.v[axis] * 0.5; anchor_offset.v[axis] = screen_dims.v[axis] * 0.5;
} break; } break;
case UI_AxisRegion_End: case UI_AxisRegion_End:
{ {
anchor_offset.v[axis] = box->solved_dims.v[axis]; anchor_offset.v[axis] = screen_dims.v[axis];
} break; } break;
} }
} }
offset = SubVec2(box->desc.floating_pos, anchor_offset); offset = SubVec2(box->desc.floating_pos, anchor_offset);
offset = FloorVec2(offset); if (box->solved_scale == 1)
{
offset = RoundVec2(offset);
}
final_pos = AddVec2(parent->screen_rect.p0, offset); screen_pos = AddVec2(parent->screen_rect.p0, offset);
if (!AnyBit(box->desc.flags, UI_BoxFlag_NoFloatingClamp)) if (!AnyBit(box->desc.flags, UI_BoxFlag_NoFloatingClamp))
{ {
{ {
f32 overshoot = MaxF32(0, (final_pos.x + box->solved_dims.x) - parent->screen_rect.p1.x); f32 overshoot = MaxF32(0, (screen_pos.x + screen_dims.x) - parent->screen_rect.p1.x);
final_pos.x = MaxF32(parent->screen_rect.p0.x, final_pos.x - overshoot); screen_pos.x = MaxF32(parent->screen_rect.p0.x, screen_pos.x - overshoot);
} }
{ {
f32 overshoot = MaxF32((final_pos.y + box->solved_dims.y) - parent->screen_rect.p1.y, 0); f32 overshoot = MaxF32((screen_pos.y + screen_dims.y) - parent->screen_rect.p1.y, 0);
final_pos.y = MaxF32(parent->screen_rect.p0.y, final_pos.y - overshoot); screen_pos.y = MaxF32(parent->screen_rect.p0.y, screen_pos.y - overshoot);
} }
} }
} }
@ -1431,28 +1434,27 @@ void UI_EndFrame(UI_Frame *frame)
case UI_AxisRegion_Center: case UI_AxisRegion_Center:
{ {
f32 parent_size = parent->solved_dims.v[axis]; f32 parent_size = parent->solved_dims.v[axis];
f32 box_size = box->solved_dims.v[axis]; f32 box_size = screen_dims.v[axis];
offset.v[axis] = parent_size / 2 - box_size / 2; offset.v[axis] = parent_size / 2 - box_size / 2;
} break; } break;
case UI_AxisRegion_End: case UI_AxisRegion_End:
{ {
f32 parent_size = parent->solved_dims.v[axis]; f32 parent_size = parent->solved_dims.v[axis];
f32 box_size = box->solved_dims.v[axis]; f32 box_size = screen_dims.v[axis];
offset.v[axis] = parent_size - box_size; offset.v[axis] = parent_size - box_size;
} break; } break;
} }
} }
offset = RoundVec2(offset); // offset = RoundVec2(offset);
final_pos.x = parent->screen_rect.p0.x + offset.x; // offset = FloorVec2(offset);
final_pos.y = parent->screen_rect.p0.y + offset.y; screen_pos.x = parent->screen_rect.p0.x + offset.x;
parent->cursor += box->solved_dims.v[parent->desc.child_layout_axis]; screen_pos.y = parent->screen_rect.p0.y + offset.y;
parent->cursor += screen_dims.v[parent->desc.child_layout_axis];
} }
// Submit position // Submit position
Vec2 rounded_final_pos = final_pos; box->screen_rect.p0 = screen_pos;
box->screen_rect.p0 = rounded_final_pos; box->screen_rect.p1 = AddVec2(box->screen_rect.p0, screen_dims);
box->screen_rect.p1 = AddVec2(rounded_final_pos, box->solved_dims);
box->screen_anchor = AddVec2(box->screen_rect.p0, anchor_offset); box->screen_anchor = AddVec2(box->screen_rect.p0, anchor_offset);
} }
@ -1522,9 +1524,9 @@ void UI_EndFrame(UI_Frame *frame)
UI_RegionPair child_alignment = UI_PairFromRegion(box->desc.child_alignment); UI_RegionPair child_alignment = UI_PairFromRegion(box->desc.child_alignment);
b32 is_visible = 1; b32 is_visible = 1;
is_visible = is_visible && (box->desc.tint.w >= 0.001); is_visible = is_visible && (box->desc.tint.w >= 0.005);
is_visible = is_visible && (box->screen_rect.p1.x - box->screen_rect.p0.x >= 1); is_visible = is_visible && (box->screen_rect.p1.x - box->screen_rect.p0.x > 0.001);
is_visible = is_visible && (box->screen_rect.p1.y - box->screen_rect.p0.y >= 1); is_visible = is_visible && (box->screen_rect.p1.y - box->screen_rect.p0.y > 0.001);
if (is_visible || AnyBit(frame->frame_flags, UI_FrameFlag_Debug)) if (is_visible || AnyBit(frame->frame_flags, UI_FrameFlag_Debug))
{ {
Vec4 debug_lin = is_visible ? LinearFromSrgb(box->desc.debug_color) : LinearFromSrgb(box->desc.invisible_debug_color); Vec4 debug_lin = is_visible ? LinearFromSrgb(box->desc.debug_color) : LinearFromSrgb(box->desc.invisible_debug_color);

View File

@ -307,10 +307,10 @@ Struct(UI_Box)
u64 post_index; u64 post_index;
//- Layout data //- Layout data
f32 cursor;
f32 final_children_size_accum[Axis_COUNTXY];
f32 solved_scale; f32 solved_scale;
f32 final_children_size_accum[Axis_COUNTXY];
Vec2 solved_dims; Vec2 solved_dims;
f32 cursor;
//- Layout results //- Layout results
Rng2 screen_rect; Rng2 screen_rect;