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_RunRect *rect = &result.rects[glyph_idx];
f32 advance = advance = glyph->advance;
f32 advance = glyph->advance;
if (TweakB32("Ceil glyph advances", 0))
{
advance = CeilF32(advance);
}
if (TweakB32("Floor glyph advances", 1))
if (TweakB32("Floor glyph advances", 0))
{
advance = FloorF32(advance);
}
if (TweakB32("Round glyph advances", 0))
if (TweakB32("Round glyph advances", 1))
{
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_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_size = 16;
theme.font_size = 14;
// theme.font = GC_FontKeyFromResource(ResourceKeyFromStore(&V_Resources, Lit("font/seguisb.ttf")));
// theme.font_size = 12;
// theme.font = GC_FontKeyFromResource(ResourceKeyFromStore(&V_Resources, Lit("font/seguisb.ttf")));
// theme.font_size = 30;
@ -253,7 +255,7 @@ void V_TickForever(WaveLaneCtx *lane)
frame->tick = V.current_frame_tick;
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);
if (S_IsKeyNil(V.player_key))
@ -341,18 +343,18 @@ void V_TickForever(WaveLaneCtx *lane)
V_WidgetTheme theme = V_GetWidgetTheme();
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_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_Report vis_rep = UI_ReportFromKey(vis_box);
frame->ui_dims = RoundVec2ToI32(DimsFromRng2(vis_rep.screen_rect));
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_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.y = MaxI32(frame->ui_dims.y, 64);
frame->draw_dims = frame->ui_dims;
@ -1284,7 +1286,7 @@ void V_TickForever(WaveLaneCtx *lane)
//- Build command 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)
{
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);
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(BorderColor, window_border_color);
@ -1318,7 +1320,7 @@ void V_TickForever(WaveLaneCtx *lane)
UI_Push(ChildLayoutAxis, Axis_Y);
UI_Push(FloatingPos, palette->pos);
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));
{
// Title bar
@ -1442,7 +1444,8 @@ void V_TickForever(WaveLaneCtx *lane)
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);
if (item_rep.m1.presses)

View File

@ -1112,6 +1112,18 @@ void UI_EndFrame(UI_Frame *frame)
box->cursor = 0;
ZeroStructs(box->final_children_size_accum, countof(box->final_children_size_accum));
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
{
@ -1124,7 +1136,7 @@ void UI_EndFrame(UI_Frame *frame)
Assert(post_index == boxes_count);
}
// Compute independent sizes
// Solve independent sizes
for (u64 pre_index = 0; pre_index < boxes_count; ++pre_index)
{
UI_Box *box = boxes_pre[pre_index];
@ -1141,7 +1153,7 @@ void UI_EndFrame(UI_Frame *frame)
f32 text_size = 0;
if (axis == Axis_X)
{
text_size = CeilF32(box->glyph_run.baseline_length);
text_size = box->glyph_run.baseline_length;
}
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)
{
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)
{
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))
{
f32 child_size = child->solved_dims.v[axis];
if (axis == box->desc.child_layout_axis)
{
accum += child->solved_dims.v[axis];
accum += child_size;
}
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)
{
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)
{
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;
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)
{
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 strictness = child->desc.pref_semantic_dims[axis].strictness;
f32 flex = size * (1.0 - strictness);
f32 new_size = size;
if (axis == box->desc.child_layout_axis)
{
f32 chopoff = MinF32(flex, violation * (flex / flex_accum));
new_size = size - chopoff;
unconstrained_size_accum += size;
flex_accum += flex;
}
else
{
if (size > box_size)
{
new_size = MaxF32(size - flex, box_size);
}
unconstrained_size_accum = MaxF32(unconstrained_size_accum, size);
flex_accum = MaxF32(flex_accum, flex);
}
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];
if (size > box_size)
f32 size_accum = 0;
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 flex = size * (1.0 - strictness);
child->solved_dims.v[axis] = MaxF32(size - flex, box_size);
f32 flex = unconstrained_size * (1.0 - strictness);
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
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
// Solve final positions
for (u64 pre_index = 0; pre_index < boxes_count; ++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);
}
// 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
{
Axis axis = box->desc.child_layout_axis;
@ -1366,13 +1363,16 @@ void UI_EndFrame(UI_Frame *frame)
box->cursor = box_size - size_accum;
} break;
}
// box->cursor = FloorF32(box->cursor);
}
// Position
{
Vec2 final_pos = Zi;
Vec2 screen_pos = Zi;
Vec2 offset = Zi;
Vec2 anchor_offset = Zi;
// Vec2 screen_dims = CeilVec2(box->solved_dims);
Vec2 screen_dims = box->solved_dims;
// Floating box position
if (AnyBit(box->desc.flags, UI_BoxFlag_Floating))
@ -1387,28 +1387,31 @@ void UI_EndFrame(UI_Frame *frame)
default: break;
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;
case UI_AxisRegion_End:
{
anchor_offset.v[axis] = box->solved_dims.v[axis];
anchor_offset.v[axis] = screen_dims.v[axis];
} break;
}
}
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))
{
{
f32 overshoot = MaxF32(0, (final_pos.x + box->solved_dims.x) - parent->screen_rect.p1.x);
final_pos.x = MaxF32(parent->screen_rect.p0.x, final_pos.x - overshoot);
f32 overshoot = MaxF32(0, (screen_pos.x + screen_dims.x) - parent->screen_rect.p1.x);
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);
final_pos.y = MaxF32(parent->screen_rect.p0.y, final_pos.y - overshoot);
f32 overshoot = MaxF32((screen_pos.y + screen_dims.y) - parent->screen_rect.p1.y, 0);
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:
{
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;
} break;
case UI_AxisRegion_End:
{
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;
} break;
}
}
offset = RoundVec2(offset);
final_pos.x = parent->screen_rect.p0.x + offset.x;
final_pos.y = parent->screen_rect.p0.y + offset.y;
parent->cursor += box->solved_dims.v[parent->desc.child_layout_axis];
// offset = RoundVec2(offset);
// offset = FloorVec2(offset);
screen_pos.x = parent->screen_rect.p0.x + offset.x;
screen_pos.y = parent->screen_rect.p0.y + offset.y;
parent->cursor += screen_dims.v[parent->desc.child_layout_axis];
}
// Submit position
Vec2 rounded_final_pos = final_pos;
box->screen_rect.p0 = rounded_final_pos;
box->screen_rect.p1 = AddVec2(rounded_final_pos, box->solved_dims);
box->screen_rect.p0 = screen_pos;
box->screen_rect.p1 = AddVec2(box->screen_rect.p0, screen_dims);
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);
b32 is_visible = 1;
is_visible = is_visible && (box->desc.tint.w >= 0.001);
is_visible = is_visible && (box->screen_rect.p1.x - box->screen_rect.p0.x >= 1);
is_visible = is_visible && (box->screen_rect.p1.y - box->screen_rect.p0.y >= 1);
is_visible = is_visible && (box->desc.tint.w >= 0.005);
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 > 0.001);
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);

View File

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