2 dimensional ui animation scale

This commit is contained in:
jacob 2025-12-31 14:01:10 -06:00
parent 13fa38ad68
commit 4689be8dc7
4 changed files with 148 additions and 135 deletions

View File

@ -94,6 +94,8 @@ V_WidgetTheme V_GetWidgetTheme(void)
// theme.font = GC_FontKeyFromResource(ResourceKeyFromStore(&V_Resources, Lit("font/seguisb.ttf")));
// theme.font_size = 30;
// theme.font_size = TweakI32("Font size", 14, RNGI32(5, 100));
theme.window_background_color = Rgb32(0xff1a1d1e);
theme.window_border_color = Rgb32(0xff343a3b);
theme.window_border = 1;
@ -1093,11 +1095,15 @@ void V_TickForever(WaveLaneCtx *lane)
bg_color.w *= 0.5;
Vec4 border_color = LerpSrgb(VEC4(0, 0, 0, 0), theme.button_active_color, rep.exists);
UI_SetNext(Scale, rep.exists);
// UI_SetNext(Anchor, UI_Region_Center);
UI_SetNext(Anchor, UI_Region_TopRight);
UI_SetNext(Scale, VEC2(rep.exists, 1));
UI_SetNext(BackgroundColor, bg_color);
UI_SetNext(BorderColor, border_color);
UI_SetNext(Border, 2);
UI_SetNext(Width, UI_PIX(30, 0));
// UI_SetNext(Width, UI_PIX(30, 0));
UI_SetNext(Width, UI_PIX(100, 0));
UI_SetNext(Height, UI_GROW(1, 0));
UI_SetNext(ChildAlignment, UI_Region_Center);
// UI_SetNext(FontSize, theme.font_size * 1.5);
@ -1314,8 +1320,9 @@ void V_TickForever(WaveLaneCtx *lane)
}
window_border_color = LerpSrgb(window_border_color, Rgb32(0x0078a6), titlebar_rep.hot);
f32 scale = LerpF32(0.85, 1, palette->show);
UI_Push(Tint, VEC4(1, 1, 1, palette->show));
UI_SetNext(Scale, LerpF32(0.85, 1, palette->show));
UI_SetNext(Scale, VEC2(scale, scale));
UI_Push(BackgroundColor, window_background_color);
UI_Push(BorderColor, window_border_color);
@ -1325,7 +1332,7 @@ void V_TickForever(WaveLaneCtx *lane)
UI_Push(Height, UI_SHRINK(0, 0));
UI_Push(ChildLayoutAxis, Axis_Y);
UI_Push(FloatingPos, palette->pos);
UI_SetNext(FloatingPosAnchor, UI_Region_Center);
UI_SetNext(Anchor, UI_Region_Center);
UI_SetNext(Flags, UI_BoxFlag_Floating | UI_BoxFlag_Interactable);
UI_PushCP(UI_BuildBoxEx(palette->key));
{
@ -1510,10 +1517,14 @@ void V_TickForever(WaveLaneCtx *lane)
// Tweak checkbox
case TweakKind_Bool:
{
Vec4 tweak_bg_color = LerpSrgb(theme.color_negative, theme.color_positive, new_tweak_value.x);
Vec4 tweak_border_color = Zi;
tweak_border_color = LerpSrgb(tweak_border_color, theme.button_hot_color, tweak_rep.hot);
tweak_border_color = LerpSrgb(tweak_border_color, theme.button_active_color, tweak_rep.active);
// Vec4 tweak_bg_color = LerpSrgb(theme.color_negative, theme.color_positive, new_tweak_value.x);
Vec4 tweak_bg_color = Zi;
tweak_bg_color = LerpSrgb(tweak_bg_color, theme.color_positive, new_tweak_value.x);
Vec4 tweak_border_color = theme.window_border_color;
// tweak_border_color = LerpSrgb(tweak_border_color, theme.button_hot_color, tweak_rep.hot);
// tweak_border_color = LerpSrgb(tweak_border_color, theme.button_active_color, tweak_rep.active);
tweak_border_color = LerpSrgb(tweak_border_color, theme.button_active_color, tweak_rep.hot);
if (tweak_rep.m1.downs)
{
@ -1522,8 +1533,8 @@ void V_TickForever(WaveLaneCtx *lane)
UI_SetNext(BackgroundColor, tweak_bg_color);
UI_SetNext(BorderColor, tweak_border_color);
UI_SetNext(Rounding, UI_RGROW(0.5));
UI_SetNext(Border, 2);
UI_SetNext(Rounding, UI_RGROW(0.8));
UI_SetNext(Border, 1);
UI_SetNext(Width, UI_FNT(1.25, 1));
UI_SetNext(Height, UI_FNT(1.25, 1));
UI_SetNext(Flags, UI_BoxFlag_Interactable);

View File

@ -344,7 +344,7 @@ void UI_PushDefaults(void)
case UI_StyleKind_Parent: { desc.style.Parent = UI_RootKey; } break;
case UI_StyleKind_Width: { desc.style.Width = UI_GROW(1, 0); } break;
case UI_StyleKind_Height: { desc.style.Height = UI_GROW(1, 0); }
case UI_StyleKind_Scale: { desc.style.Scale = 1; } break;
case UI_StyleKind_Scale: { desc.style.Scale = VEC2(1, 1); } break;
case UI_StyleKind_Font: { desc.style.Font = UI_GetDefaultFont(); } break;
u8 prefetch[127] = Zi;
@ -520,7 +520,7 @@ UI_Key UI_BuildBoxEx(UI_Key semantic_key)
n->cmd.box.font_size = UI_UseTop(FontSize);
n->cmd.box.rounding = UI_UseTop(Rounding);
n->cmd.box.text = UI_UseTop(Text);
n->cmd.box.floating_pos_anchor = UI_UseTop(FloatingPosAnchor);
n->cmd.box.anchor = UI_UseTop(Anchor);
n->cmd.box.floating_pos = UI_UseTop(FloatingPos);
}
++frame->cmds_count;
@ -812,11 +812,11 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color)
f32 target_active = box == active_box;
f32 target_selected = report->is_selected;
f32 exists_blend_rate = (40 * frame->dt);
f32 exists_blend_rate = (30 * frame->dt);
f32 hot_blend_rate = target_hot == 1 ? 1 : (15 * frame->dt);
f32 active_blend_rate = target_active == 1 ? 1 : (15 * frame->dt);
f32 hovered_blend_rate = target_hovered == 1 ? 1 : (15 * frame->dt);
f32 selected_blend_rate = (40 * frame->dt);
f32 selected_blend_rate = (30 * frame->dt);
report->exists = LerpF32(report->exists, target_exists, exists_blend_rate);
report->hot = LerpF32(report->hot, target_hot, hot_blend_rate);
@ -876,16 +876,16 @@ Vec2 UI_CursorPos(void)
////////////////////////////////////////////////////////////
//~ Text layout helpers
GC_Run UI_ScaleRun(Arena *arena, GC_Run unscaled_run, f32 scale)
GC_Run UI_ScaleRun(Arena *arena, GC_Run unscaled_run, Vec2 scale)
{
GC_Run result = Zi;
result = unscaled_run;
result.font_size *= scale;
result.font_ascent *= scale;
result.font_descent *= scale;
result.font_cap *= scale;
result.baseline_length *= scale;
result.font_size *= scale.y;
result.font_ascent *= scale.y;
result.font_descent *= scale.y;
result.font_cap *= scale.y;
result.baseline_length *= scale.x;
result.rects_count = unscaled_run.rects_count;
result.rects = PushStructsNoZero(arena, GC_RunRect, result.rects_count);
@ -895,9 +895,9 @@ GC_Run UI_ScaleRun(Arena *arena, GC_Run unscaled_run, f32 scale)
GC_RunRect *dst = &result.rects[rect_idx];
*dst = *src;
dst->bounds = MulRng2Vec2(dst->bounds, VEC2(scale, scale));
dst->advance *= scale;
dst->baseline_pos *= scale;
dst->bounds = MulRng2Vec2(dst->bounds, scale);
dst->advance *= scale.x;
dst->baseline_pos *= scale.x;
}
return result;
@ -1110,19 +1110,25 @@ void UI_EndFrame(UI_Frame *frame)
// Reset layout data
box->cursor = 0;
ZeroStructs(box->final_children_size_accum, countof(box->final_children_size_accum));
box->final_children_size_accum = VEC2(0, 0);
box->solved_dims = VEC2(0, 0);
// Compute scale
UI_Box *parent = box->parent;
box->solved_scale = box->desc.scale;
if (parent)
// Solve scale
{
box->solved_scale *= parent->solved_scale;
}
if (AbsF32(1.0 - box->solved_scale) < 0.001)
{
box->solved_scale = 1;
UI_Box *parent = box->parent;
box->solved_scale = box->desc.scale;
if (parent)
{
box->solved_scale = MulVec2Vec2(parent->solved_scale, box->solved_scale);
}
if (AbsF32(1.0 - box->solved_scale.x) < 0.001)
{
box->solved_scale.x = 1;
}
if (AbsF32(1.0 - box->solved_scale.y) < 0.001)
{
box->solved_scale.y = 1;
}
}
}
else
@ -1306,7 +1312,7 @@ void UI_EndFrame(UI_Frame *frame)
}
child->solved_dims.v[axis] = new_size;
}
box->final_children_size_accum[axis] = size_accum;
box->final_children_size_accum.v[axis] = size_accum;
}
}
}
@ -1319,38 +1325,58 @@ void UI_EndFrame(UI_Frame *frame)
UI_Box *parent = box->parent;
UI_RegionPair child_alignment = UI_PairFromRegion(box->desc.child_alignment);
UI_RegionPair alignment_in_parent = Zi;
if (parent)
{
alignment_in_parent = UI_PairFromRegion(parent->desc.child_alignment);
}
else
{
alignment_in_parent = UI_PairFromRegion(UI_Region_TopLeft);
}
UI_RegionPair alignment_in_parent = UI_PairFromRegion(parent ? parent->desc.child_alignment : UI_Region_TopLeft);
Axis child_layout_axis = box->desc.child_layout_axis;
Axis layout_axis_in_parent = parent ? parent->desc.child_layout_axis : Axis_X;
b32 is_floating = AnyBit(box->desc.flags, UI_BoxFlag_Floating);
// 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)
if (box->solved_scale.v[axis] == 1)
{
scaled_size = CeilF32(unscaled_size);
scaled_size = RoundF32(unscaled_size);
}
else
{
scaled_size = unscaled_size * box->solved_scale;
scaled_size = unscaled_size * box->solved_scale.v[axis];
}
box->solved_dims.v[axis] = scaled_size;
}
// Compute anchor offset
Vec2 anchor_offset = Zi;
{
UI_RegionPair anchor_region = UI_PairFromRegion(box->desc.anchor);
for (Axis axis = 0; axis < Axis_COUNTXY; ++axis)
{
UI_AxisRegion anchor = anchor_region.v[axis];
switch (anchor)
{
default: break;
case UI_AxisRegion_Center:
{
anchor_offset.v[axis] = box->solved_dims.v[axis] * 0.5;
} break;
case UI_AxisRegion_End:
{
anchor_offset.v[axis] = box->solved_dims.v[axis];
} break;
}
}
}
// Initialize layout cursor based on alignment
{
Axis axis = box->desc.child_layout_axis;
Axis axis = child_layout_axis;
UI_AxisRegion alignment = child_alignment.v[axis];
f32 box_size = box->solved_dims.v[axis];
f32 size_accum = box->final_children_size_accum[axis];
f32 size_accum = box->final_children_size_accum.v[axis];
switch(alignment)
{
default: break;
@ -1366,99 +1392,76 @@ void UI_EndFrame(UI_Frame *frame)
// box->cursor = FloorF32(box->cursor);
}
// Position
// Solve screen rect
{
Vec2 screen_pos = Zi;
// Compute offset
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))
{
UI_RegionPair floating_anchor = UI_PairFromRegion(box->desc.floating_pos_anchor);
for (Axis axis = 0; axis < Axis_COUNTXY; ++axis)
// Floating box offset
if (is_floating)
{
UI_AxisRegion anchor = floating_anchor.v[axis];
switch (anchor)
offset = box->desc.floating_pos;
offset = SubVec2(offset, anchor_offset);
}
// Non-floating box offset
else if (parent)
{
// Compute offset in layout direction (based on parent cursor)
offset.v[layout_axis_in_parent] = parent->cursor;
// Compute offset in non-layout direction (based on alignment)
{
default: break;
case UI_AxisRegion_Center:
Axis axis = !layout_axis_in_parent;
UI_AxisRegion alignment = alignment_in_parent.v[axis];
switch(alignment)
{
anchor_offset.v[axis] = screen_dims.v[axis] * 0.5;
} break;
case UI_AxisRegion_End:
{
anchor_offset.v[axis] = screen_dims.v[axis];
} break;
default: break;
case UI_AxisRegion_Center:
{
f32 parent_size = parent->solved_dims.v[axis];
f32 box_size = box->solved_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];
offset.v[axis] = parent_size - box_size;
} break;
}
}
}
offset = SubVec2(box->desc.floating_pos, anchor_offset);
if (box->solved_scale == 1)
if (box->solved_scale.x == 1)
{
offset = RoundVec2(offset);
offset.x = RoundF32(offset.x);
}
screen_pos = AddVec2(parent->screen_rect.p0, offset);
if (!AnyBit(box->desc.flags, UI_BoxFlag_NoFloatingClamp))
if (box->solved_scale.y == 1)
{
{
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((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);
}
offset.y = RoundF32(offset.y);
}
}
// Non-floating box position
else if (parent)
{
f32 layout_cursor = parent->cursor;
// Compute offset in layout direction
{
Axis axis = parent->desc.child_layout_axis;
offset.v[axis] = layout_cursor;
}
// Compute offset in non-layout direction (based on alignment)
{
Axis axis = !parent->desc.child_layout_axis;
UI_AxisRegion alignment = alignment_in_parent.v[axis];
switch(alignment)
{
default: break;
case UI_AxisRegion_Center:
{
f32 parent_size = parent->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 = screen_dims.v[axis];
offset.v[axis] = parent_size - box_size;
} break;
}
}
// 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
// Compute rect
Vec2 screen_pos = parent ? AddVec2(parent->screen_rect.p0, offset) : VEC2(0, 0);
if (is_floating && !AnyBit(box->desc.flags, UI_BoxFlag_NoFloatingClamp))
{
Vec2 overshoot = Zi;
overshoot.x = MaxF32(0, (screen_pos.x + box->solved_dims.x) - parent->screen_rect.p1.x);
overshoot.y = MaxF32((screen_pos.y + box->solved_dims.y) - parent->screen_rect.p1.y, 0);
screen_pos.x = MaxF32(parent->screen_rect.p0.x, screen_pos.x - overshoot.x);
screen_pos.y = MaxF32(parent->screen_rect.p0.y, screen_pos.y - overshoot.y);
}
box->screen_rect.p0 = screen_pos;
box->screen_rect.p1 = AddVec2(box->screen_rect.p0, screen_dims);
box->screen_rect.p1 = AddVec2(box->screen_rect.p0, box->solved_dims);
box->screen_anchor = AddVec2(box->screen_rect.p0, anchor_offset);
// Update parent cursor
if (parent && !is_floating)
{
parent->cursor += box->solved_dims.v[layout_axis_in_parent];
}
}
// Rounding
// Solve screen rounding
{
UI_Round rounding = box->desc.rounding;
Vec2 half_dims = MulVec2(SubVec2(box->screen_rect.p1, box->screen_rect.p0), 0.5);
@ -1499,7 +1502,6 @@ void UI_EndFrame(UI_Frame *frame)
final_rounding_bl = MaxF32(final_rounding_bl, parent->rounding_bl - Vec2Len(vbl));
}
// Submit rounding
box->rounding_tl = final_rounding_tl;
box->rounding_tr = final_rounding_tr;
box->rounding_br = final_rounding_br;

View File

@ -107,14 +107,14 @@ Enum(UI_BoxFlag)
X(ChildAlignment, UI_Region) \
X(Width, UI_Size) \
X(Height, UI_Size) \
X(Scale, f32) \
X(Scale, Vec2) \
X(BackgroundColor, Vec4) \
X(BorderColor, Vec4) \
X(DebugColor, Vec4) \
X(InvisibleDebugColor, Vec4) \
X(Tint, Vec4) \
X(Border, f32) \
X(FloatingPosAnchor, UI_Region) \
X(Anchor, UI_Region) \
X(FloatingPos, Vec2) \
X(Rounding, UI_Round) \
X(Font, GC_FontKey) \
@ -241,14 +241,14 @@ Struct(UI_BoxDesc)
Vec4 invisible_debug_color;
Vec4 tint;
f32 border;
f32 scale;
Vec2 floating_pos;
Vec2 scale;
String text;
GC_FontKey font;
f32 font_size;
Axis child_layout_axis;
UI_Region child_alignment;
UI_Region floating_pos_anchor;
UI_Region anchor;
Vec2 floating_pos;
};
Struct(UI_Cmd)
@ -307,8 +307,8 @@ Struct(UI_Box)
u64 post_index;
//- Layout data
f32 solved_scale;
f32 final_children_size_accum[Axis_COUNTXY];
Vec2 solved_scale;
Vec2 final_children_size_accum;
Vec2 solved_dims;
f32 cursor;
@ -533,7 +533,7 @@ Vec2 UI_CursorPos(void);
////////////////////////////////////////////////////////////
//~ Text layout helpers
GC_Run UI_ScaleRun(Arena *arena, GC_Run unscaled_run, f32 scale);
GC_Run UI_ScaleRun(Arena *arena, GC_Run unscaled_run, Vec2 scale);
////////////////////////////////////////////////////////////
//~ End frame

View File

@ -6,7 +6,7 @@ UI_Key UI_BuildLabel(String text)
UI_Key parent = UI_UseTop(Parent);
GC_FontKey font = UI_UseTop(Font);
f32 font_size = UI_UseTop(FontSize);
f32 scale = UI_UseTop(Scale);
Vec2 scale = UI_UseTop(Scale);
Vec4 tint = UI_UseTop(Tint);
UI_Region alignment = UI_UseTop(ChildAlignment);
@ -52,7 +52,7 @@ UI_Key UI_BuildSpacer(UI_Size size, Axis axis)
{
UI_Key parent = UI_UseTop(Parent);
UI_Key key = Zi;
f32 scale = UI_UseTop(Scale);
Vec2 scale = UI_UseTop(Scale);
UI_PushCP(UI_NilKey);
{
UI_PushDefaults();
@ -71,7 +71,7 @@ UI_Key UI_BuildDivider(UI_Size size, Vec4 color, Axis axis)
{
UI_Key key = Zi;
UI_Key parent = UI_UseTop(Parent);
f32 scale = UI_UseTop(Scale);
Vec2 scale = UI_UseTop(Scale);
Vec4 tint = UI_UseTop(Tint);
UI_PushCP(UI_NilKey);
{