ui lerp progress

This commit is contained in:
jacob 2025-12-29 00:38:08 -06:00
parent 3522e47c0a
commit 54025f670f
5 changed files with 217 additions and 116 deletions

View File

@ -747,7 +747,7 @@ void V_TickForever(WaveLaneCtx *lane)
{ {
if (last_frame->tick == 0) if (last_frame->tick == 0)
{ {
frame->camera_lerp_ratio = 1; frame->camera_lerp_rate = 1;
} }
else if (frame->is_editing) else if (frame->is_editing)
{ {
@ -757,21 +757,20 @@ void V_TickForever(WaveLaneCtx *lane)
// When panning & not zooming, we want camera interpolation to be // When panning & not zooming, we want camera interpolation to be
// instant (no smoothing). However, the transition to non // instant (no smoothing). However, the transition to non
// interpolation must be smooth. // interpolation must be smooth.
frame->camera_lerp_ratio += last_frame->camera_lerp_ratio + (5 * frame->dt); frame->camera_lerp_rate += last_frame->camera_lerp_rate + (5 * frame->dt);
frame->camera_lerp_ratio = MinF32(frame->camera_lerp_ratio, 1);
} }
else else
{ {
frame->camera_lerp_ratio = 40.0 * frame->dt; frame->camera_lerp_rate = 40.0 * frame->dt;
} }
} }
else else
{ {
frame->camera_lerp_ratio = 15.0 * frame->dt; frame->camera_lerp_rate = 15.0 * frame->dt;
} }
frame->camera_lerp_rate = ClampF32(frame->camera_lerp_rate, 0, 1);
frame->camera_pos = LerpVec2(last_frame->camera_pos, target_camera_pos, frame->camera_lerp_ratio); frame->camera_pos = LerpVec2(last_frame->camera_pos, target_camera_pos, frame->camera_lerp_rate);
frame->camera_zoom = LerpF32(last_frame->camera_zoom, target_camera_zoom, frame->camera_lerp_ratio); frame->camera_zoom = LerpF32(last_frame->camera_zoom, target_camera_zoom, frame->camera_lerp_rate);
{ {
f32 camera_scale = (f32)frame->draw_dims.x / (meters_per_draw_width * frame->camera_zoom); f32 camera_scale = (f32)frame->draw_dims.x / (meters_per_draw_width * frame->camera_zoom);
frame->world_to_ui_xf = XformFromScale(VEC2(camera_scale, camera_scale)); frame->world_to_ui_xf = XformFromScale(VEC2(camera_scale, camera_scale));
@ -870,7 +869,7 @@ void V_TickForever(WaveLaneCtx *lane)
{ {
{ {
V_Panel *panel = PushStruct(perm, V_Panel); V_Panel *panel = PushStruct(perm, V_Panel);
panel->key = UI_TransKey(); // TODO: Don't use transient keys for panels panel->key = UI_RandKey(); // TODO: Don't use random keys for panels
panel->axis = Axis_X; panel->axis = Axis_X;
panel->pref_ratio = 1; panel->pref_ratio = 1;
panel->is_organizing_panel = 1; panel->is_organizing_panel = 1;
@ -880,7 +879,7 @@ void V_TickForever(WaveLaneCtx *lane)
{ {
V_Panel *panel = PushStruct(perm, V_Panel); V_Panel *panel = PushStruct(perm, V_Panel);
panel->parent = V.root_panel; panel->parent = V.root_panel;
panel->key = UI_TransKey(); // TODO: Don't use transient keys panel->key = UI_RandKey(); // TODO: Don't use random keys
panel->axis = !panel->parent->axis; panel->axis = !panel->parent->axis;
panel->pref_ratio = 0.25; panel->pref_ratio = 0.25;
DllQueuePush(panel->parent->first, panel->parent->last, panel); DllQueuePush(panel->parent->first, panel->parent->last, panel);
@ -889,7 +888,7 @@ void V_TickForever(WaveLaneCtx *lane)
{ {
V_Window *window = PushStruct(perm, V_Window); V_Window *window = PushStruct(perm, V_Window);
window->panel = panel; window->panel = panel;
window->key = UI_TransKey(); // TODO: Don't use transient keys window->key = UI_RandKey(); // TODO: Don't use random keys
// window->is_tile_window = 1; // window->is_tile_window = 1;
DllQueuePushNP(panel->first_window, panel->last_window, window, next_in_panel, prev_in_panel); DllQueuePushNP(panel->first_window, panel->last_window, window, next_in_panel, prev_in_panel);
++panel->windows_count; ++panel->windows_count;
@ -897,7 +896,7 @@ void V_TickForever(WaveLaneCtx *lane)
{ {
V_Window *window = PushStruct(perm, V_Window); V_Window *window = PushStruct(perm, V_Window);
window->panel = panel; window->panel = panel;
window->key = UI_TransKey(); // TODO: Don't use transient keys window->key = UI_RandKey(); // TODO: Don't use random keys
window->is_tile_window = 1; window->is_tile_window = 1;
DllQueuePushNP(panel->first_window, panel->last_window, window, next_in_panel, prev_in_panel); DllQueuePushNP(panel->first_window, panel->last_window, window, next_in_panel, prev_in_panel);
++panel->windows_count; ++panel->windows_count;
@ -908,7 +907,7 @@ void V_TickForever(WaveLaneCtx *lane)
{ {
V_Panel *panel = PushStruct(perm, V_Panel); V_Panel *panel = PushStruct(perm, V_Panel);
panel->parent = V.root_panel; panel->parent = V.root_panel;
panel->key = UI_TransKey(); // TODO: Don't use transient keys panel->key = UI_RandKey(); // TODO: Don't use random keys
panel->axis = !panel->parent->axis; panel->axis = !panel->parent->axis;
DllQueuePush(panel->parent->first, panel->parent->last, panel); DllQueuePush(panel->parent->first, panel->parent->last, panel);
++panel->parent->count; ++panel->parent->count;
@ -919,7 +918,7 @@ void V_TickForever(WaveLaneCtx *lane)
// { // {
// V_Window *window = PushStruct(perm, V_Window); // V_Window *window = PushStruct(perm, V_Window);
// window->panel = panel; // window->panel = panel;
// window->key = UI_TransKey(); // TODO: Don't use transient keys // window->key = UI_RandKey(); // TODO: Don't use random keys
// window->is_viewport_window = 1; // window->is_viewport_window = 1;
// DllQueuePushNP(panel->first_window, panel->last_window, window, next_in_panel, prev_in_panel); // DllQueuePushNP(panel->first_window, panel->last_window, window, next_in_panel, prev_in_panel);
// ++panel->windows_count; // ++panel->windows_count;
@ -929,7 +928,7 @@ void V_TickForever(WaveLaneCtx *lane)
// { // {
// V_Panel *panel = PushStruct(perm, V_Panel); // V_Panel *panel = PushStruct(perm, V_Panel);
// panel->parent = V.root_panel; // panel->parent = V.root_panel;
// panel->key = UI_TransKey(); // TODO: Don't use transient keys // panel->key = UI_RandKey(); // TODO: Don't use random keys
// panel->axis = !panel->parent->axis; // panel->axis = !panel->parent->axis;
// DllQueuePush(panel->parent->first, panel->parent->last, panel); // DllQueuePush(panel->parent->first, panel->parent->last, panel);
// ++panel->parent->count; // ++panel->parent->count;
@ -937,7 +936,7 @@ void V_TickForever(WaveLaneCtx *lane)
// { // {
// V_Window *window = PushStruct(perm, V_Window); // V_Window *window = PushStruct(perm, V_Window);
// window->panel = panel; // window->panel = panel;
// window->key = UI_TransKey(); // TODO: Don't use transient keys // window->key = UI_RandKey(); // TODO: Don't use random keys
// // window->is_tile_window = 1; // // window->is_tile_window = 1;
// DllQueuePushNP(panel->first_window, panel->last_window, window, next_in_panel, prev_in_panel); // DllQueuePushNP(panel->first_window, panel->last_window, window, next_in_panel, prev_in_panel);
// ++panel->windows_count; // ++panel->windows_count;
@ -945,7 +944,7 @@ void V_TickForever(WaveLaneCtx *lane)
// { // {
// V_Window *window = PushStruct(perm, V_Window); // V_Window *window = PushStruct(perm, V_Window);
// window->panel = panel; // window->panel = panel;
// window->key = UI_TransKey(); // TODO: Don't use transient keys // window->key = UI_RandKey(); // TODO: Don't use random keys
// window->is_tile_window = 1; // window->is_tile_window = 1;
// DllQueuePushNP(panel->first_window, panel->last_window, window, next_in_panel, prev_in_panel); // DllQueuePushNP(panel->first_window, panel->last_window, window, next_in_panel, prev_in_panel);
// ++panel->windows_count; // ++panel->windows_count;
@ -1062,6 +1061,7 @@ void V_TickForever(WaveLaneCtx *lane)
//- Build tab row //- Build tab row
f32 tab_spacing = 10; f32 tab_spacing = 10;
V_Window *active_window = 0; V_Window *active_window = 0;
V_Window *close_window = 0;
{ {
i64 window_idx = 0; i64 window_idx = 0;
UI_SetNext(Width, UI_GROW(1, 0)); UI_SetNext(Width, UI_GROW(1, 0));
@ -1145,6 +1145,11 @@ void V_TickForever(WaveLaneCtx *lane)
UI_SetNext(Width, UI_SHRINK(6, 0)); UI_SetNext(Width, UI_SHRINK(6, 0));
UI_SetNext(Height, UI_SHRINK(2, 0)); UI_SetNext(Height, UI_SHRINK(2, 0));
UI_BuildRowEx(close_key); UI_BuildRowEx(close_key);
if (close_rep.m1.presses)
{
close_window = window;
}
} }
} }
UI_PopCP(UI_TopCP()); UI_PopCP(UI_TopCP());
@ -1241,6 +1246,15 @@ void V_TickForever(WaveLaneCtx *lane)
} }
UI_PopCP(UI_TopCP()); UI_PopCP(UI_TopCP());
} }
//- Close window
if (close_window != 0)
{
V_Window *window = close_window;
// TODO: Add window to free list
// TODO: Remove panel if windowless
DllQueueRemoveNP(panel->first_window, panel->last_window, window, next_in_panel, prev_in_panel);
panel->windows_count -= 1;
}
} }
UI_PopCP(UI_TopCP()); UI_PopCP(UI_TopCP());
panel->active_window_idx = new_active_window_idx; panel->active_window_idx = new_active_window_idx;

View File

@ -240,7 +240,7 @@ Struct(V_Frame)
f32 edit_camera_zoom; f32 edit_camera_zoom;
// Camera // Camera
f32 camera_lerp_ratio; f32 camera_lerp_rate;
Vec2 camera_pos; Vec2 camera_pos;
f32 camera_zoom; f32 camera_zoom;

View File

@ -51,9 +51,9 @@ UI_Key UI_KeyF_(String fmt, ...)
return key; return key;
} }
UI_Key UI_TransKey(void) UI_Key UI_RandKey(void)
{ {
u64 seed = ++UI.transient_key_seed; u64 seed = ++UI.rand_key_seed;
UI_Key key = Zi; UI_Key key = Zi;
key.hash = RandU64FromSeed(seed); key.hash = RandU64FromSeed(seed);
return key; return key;
@ -486,13 +486,22 @@ UI_Style UI_PopStyle(UI_StyleDesc desc)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Command helpers //~ Command helpers
UI_Key UI_BuildBoxEx(UI_Key key) UI_Key UI_BuildBoxEx(UI_Key semantic_key)
{ {
UI_Key key = semantic_key;
b32 is_transient = 0;
if (UI_IsKeyNil(key))
{
key = UI_RandKey();
is_transient = 1;
}
UI_Frame *frame = UI_CurrentFrame(); UI_Frame *frame = UI_CurrentFrame();
UI_CmdNode *n = PushStruct(frame->arena, UI_CmdNode); UI_CmdNode *n = PushStruct(frame->arena, UI_CmdNode);
n->cmd.kind = UI_CmdKind_BuildBox; n->cmd.kind = UI_CmdKind_BuildBox;
{ {
n->cmd.box.key = key; n->cmd.box.key = key;
n->cmd.box.is_transient = is_transient;
n->cmd.box.parent = UI_UseTop(Parent); n->cmd.box.parent = UI_UseTop(Parent);
n->cmd.box.flags = UI_UseTop(Flags); n->cmd.box.flags = UI_UseTop(Flags);
n->cmd.box.pref_size[Axis_X] = UI_UseTop(Width); n->cmd.box.pref_size[Axis_X] = UI_UseTop(Width);
@ -570,6 +579,7 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color)
{ {
UI_Box *box = PushStruct(UI.box_arena, UI_Box); UI_Box *box = PushStruct(UI.box_arena, UI_Box);
box->key = UI_RootKey; box->key = UI_RootKey;
box->gen = 1;
UI.boxes_count += 1; UI.boxes_count += 1;
UI_BoxBin *bin = &UI.box_bins[box->key.hash % countof(UI.box_bins)]; UI_BoxBin *bin = &UI.box_bins[box->key.hash % countof(UI.box_bins)];
bin->first = box; bin->first = box;
@ -605,6 +615,7 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color)
i64 dt_ns = now_ns - last_frame->time_ns; i64 dt_ns = now_ns - last_frame->time_ns;
frame->time_ns = now_ns; frame->time_ns = now_ns;
frame->dt_ns = dt_ns; frame->dt_ns = dt_ns;
frame->dt = SecondsFromNs(frame->dt_ns);
frame->tick = UI.current_frame_tick; frame->tick = UI.current_frame_tick;
} }
@ -625,8 +636,6 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color)
ControllerEventsArray controller_events = frame->window_frame.controller_events; ControllerEventsArray controller_events = frame->window_frame.controller_events;
frame->cursor_pos = last_frame->cursor_pos; frame->cursor_pos = last_frame->cursor_pos;
f64 dt = SecondsFromNs(frame->dt_ns);
f64 inv_dt = 1.0 / dt;
// Locate boxes // Locate boxes
UI_Box *hovered_box = 0; UI_Box *hovered_box = 0;
@ -651,18 +660,18 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color)
b32 is_cursor_in_box = 0; b32 is_cursor_in_box = 0;
{ {
// TODO: More efficient test. This logic is just copied from the renderer's SDF function for now. // TODO: More efficient test. This logic is just copied from the renderer's SDF function for now.
Vec2 p0 = box->rect.p0; Vec2 p0 = box->draw_rect.p0;
Vec2 p1 = box->rect.p1; Vec2 p1 = box->draw_rect.p1;
Vec2 point = frame->cursor_pos; Vec2 point = frame->cursor_pos;
b32 is_corner = 0; b32 is_corner = 0;
f32 non_corner_edge_dist = MinF32(MinF32(point.x - p0.x, p1.x - point.x), MinF32(point.y - p0.y, p1.y - point.y)); f32 non_corner_edge_dist = MinF32(MinF32(point.x - p0.x, p1.x - point.x), MinF32(point.y - p0.y, p1.y - point.y));
f32 corner_edge_dist = non_corner_edge_dist; f32 corner_edge_dist = non_corner_edge_dist;
if (non_corner_edge_dist >= 0) if (non_corner_edge_dist >= 0)
{ {
f32 tl_radius = box->rounding_tl; f32 tl_radius = box->draw_rounding_tl;
f32 tr_radius = box->rounding_tr; f32 tr_radius = box->draw_rounding_tr;
f32 br_radius = box->rounding_br; f32 br_radius = box->draw_rounding_br;
f32 bl_radius = box->rounding_bl; f32 bl_radius = box->draw_rounding_bl;
Vec2 tl = VEC2(p0.x + tl_radius, p0.y + tl_radius); Vec2 tl = VEC2(p0.x + tl_radius, p0.y + tl_radius);
Vec2 tr = VEC2(p1.x - tr_radius, p0.y + tr_radius); Vec2 tr = VEC2(p1.x - tr_radius, p0.y + tr_radius);
Vec2 br = VEC2(p1.x - br_radius, p1.y - br_radius); Vec2 br = VEC2(p1.x - br_radius, p1.y - br_radius);
@ -705,7 +714,7 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color)
{ {
if (hovered_box) if (hovered_box)
{ {
hovered_box->report.last_down_mouse_offset = SubVec2(frame->cursor_pos, hovered_box->rect.p0); hovered_box->report.last_down_mouse_offset = SubVec2(frame->cursor_pos, hovered_box->draw_rect.p0);
if (cev.button == Button_M1) if (cev.button == Button_M1)
{ {
++hovered_box->report.m1.downs; ++hovered_box->report.m1.downs;
@ -786,15 +795,15 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color)
f32 target_hot = report->is_hot; f32 target_hot = report->is_hot;
f32 target_active = box == active_box; f32 target_active = box == active_box;
f32 target_selected = !!(box->desc.flags & UI_BoxFlag_Selected); f32 target_selected = !!(box->desc.flags & UI_BoxFlag_Selected);
f32 hot_blend_rate = target_hot == 1 ? 1 : (15 * dt); f32 hot_blend_rate = target_hot == 1 ? 1 : (15 * frame->dt);
f32 active_blend_rate = target_active == 1 ? 1 : (15 * dt); f32 active_blend_rate = target_active == 1 ? 1 : (15 * frame->dt);
f32 hovered_blend_rate = target_hovered == 1 ? 1 : (15 * dt); f32 hovered_blend_rate = target_hovered == 1 ? 1 : (15 * frame->dt);
f32 selected_blend_rate = target_selected == 1 ? 1 : (15 * dt); f32 selected_blend_rate = target_selected == 1 ? 1 : (15 * frame->dt);
report->hot = LerpF32(report->hot, target_hot, hot_blend_rate); report->hot = LerpF32(report->hot, target_hot, hot_blend_rate);
report->active = LerpF32(report->active, target_active, active_blend_rate); report->active = LerpF32(report->active, target_active, active_blend_rate);
report->hovered = LerpF32(report->hovered, target_hovered, hovered_blend_rate); report->hovered = LerpF32(report->hovered, target_hovered, hovered_blend_rate);
report->selected = LerpF32(report->selected, target_selected, selected_blend_rate); report->selected = LerpF32(report->selected, target_selected, selected_blend_rate);
report->screen_rect = box->rect; report->screen_rect = box->draw_rect;
} }
frame->hovered_box = hovered_box ? hovered_box->key : UI_NilKey; frame->hovered_box = hovered_box ? hovered_box->key : UI_NilKey;
@ -882,8 +891,10 @@ void UI_EndFrame(UI_Frame *frame)
{ {
// Allocate new box // Allocate new box
box = UI.first_free_box; box = UI.first_free_box;
i64 old_gen = 0;
if (box) if (box)
{ {
old_gen = box->gen;
SllStackPop(UI.first_free_box); SllStackPop(UI.first_free_box);
ZeroStruct(box); ZeroStruct(box);
} }
@ -892,6 +903,8 @@ void UI_EndFrame(UI_Frame *frame)
box = PushStruct(UI.box_arena, UI_Box); box = PushStruct(UI.box_arena, UI_Box);
} }
box->key = key; box->key = key;
box->old_gen = old_gen;
box->gen = old_gen + 1;
DllQueuePushNP(bin->first, bin->last, box, next_in_bin, prev_in_bin); DllQueuePushNP(bin->first, bin->last, box, next_in_bin, prev_in_bin);
++UI.boxes_count; ++UI.boxes_count;
} }
@ -914,7 +927,7 @@ void UI_EndFrame(UI_Frame *frame)
UI_Key key = cmd.box.key; UI_Key key = cmd.box.key;
if (UI_MatchKey(key, UI_NilKey)) if (UI_MatchKey(key, UI_NilKey))
{ {
key = UI_TransKey(); key = UI_RandKey();
} }
UI_Box *box = UI_BoxFromKey(key); UI_Box *box = UI_BoxFromKey(key);
@ -967,6 +980,8 @@ void UI_EndFrame(UI_Frame *frame)
////////////////////////////// //////////////////////////////
//- Prune cached boxes //- Prune cached boxes
i64 box_grace_frames = 10;
{ {
u64 prunes_count = 0; u64 prunes_count = 0;
UI_Box **prunes = PushStructsNoZero(scratch.arena, UI_Box *, UI.boxes_count); UI_Box **prunes = PushStructsNoZero(scratch.arena, UI_Box *, UI.boxes_count);
@ -977,7 +992,12 @@ void UI_EndFrame(UI_Frame *frame)
UI_Box *box = ir.box; UI_Box *box = ir.box;
if (box->last_build_tick < frame->tick) if (box->last_build_tick < frame->tick)
{ {
prunes[prunes_count++] = box; box->desc.pref_size[Axis_X] = UI_PIX(0, 0);
// box->desc.pref_size[Axis_Y] = UI_PIX(0, 0);
if (box->desc.is_transient || (frame->tick - box->last_build_tick > box_grace_frames))
{
prunes[prunes_count++] = box;
}
} }
} }
} }
@ -1038,7 +1058,9 @@ void UI_EndFrame(UI_Frame *frame)
pre_index += 1; pre_index += 1;
// Reset layout data // Reset layout data
ZeroStruct(&box->layout); box->cursor = 0;
ZeroStructs(box->final_children_size_accum, countof(box->final_children_size_accum));
ZeroStructs(box->target_dims, countof(box->target_dims));
} }
else else
{ {
@ -1060,7 +1082,7 @@ void UI_EndFrame(UI_Frame *frame)
UI_Size pref_size = box->desc.pref_size[axis]; UI_Size pref_size = box->desc.pref_size[axis];
if (pref_size.kind == UI_SizeKind_Pixel) if (pref_size.kind == UI_SizeKind_Pixel)
{ {
box->layout.solved_dims[axis] = pref_size.v; box->target_dims[axis] = pref_size.v;
} }
else if (pref_size.kind == UI_SizeKind_Shrink && AnyBit(box->desc.flags, UI_BoxFlag_DrawText)) else if (pref_size.kind == UI_SizeKind_Shrink && AnyBit(box->desc.flags, UI_BoxFlag_DrawText))
{ {
@ -1074,7 +1096,7 @@ void UI_EndFrame(UI_Frame *frame)
{ {
text_size = box->glyph_run.font_ascent + box->glyph_run.font_descent; text_size = box->glyph_run.font_ascent + box->glyph_run.font_descent;
} }
box->layout.solved_dims[axis] = text_size + (pref_size.v * 2); box->target_dims[axis] = text_size + (pref_size.v * 2);
} }
} }
} }
@ -1097,11 +1119,11 @@ void UI_EndFrame(UI_Frame *frame)
if (ancestor_size.kind == UI_SizeKind_Pixel || (ancestor_size.kind == UI_SizeKind_Shrink && AnyBit(box->desc.flags, UI_BoxFlag_DrawText))) if (ancestor_size.kind == UI_SizeKind_Pixel || (ancestor_size.kind == UI_SizeKind_Shrink && AnyBit(box->desc.flags, UI_BoxFlag_DrawText)))
{ {
// Match independent ancestor // Match independent ancestor
match_size = ancestor->layout.solved_dims[axis]; match_size = ancestor->target_dims[axis];
found_match = 1; found_match = 1;
} }
} }
box->layout.solved_dims[axis] = match_size * pref_size.v; box->target_dims[axis] = match_size * pref_size.v;
} }
} }
} }
@ -1122,15 +1144,15 @@ void UI_EndFrame(UI_Frame *frame)
{ {
if (axis == box->desc.child_layout_axis) if (axis == box->desc.child_layout_axis)
{ {
accum += child->layout.solved_dims[axis]; accum += child->target_dims[axis];
} }
else else
{ {
accum = MaxF32(child->layout.solved_dims[axis], accum); accum = MaxF32(child->target_dims[axis], accum);
} }
} }
} }
box->layout.solved_dims[axis] = accum + (pref_size.v * 2); box->target_dims[axis] = accum + (pref_size.v * 2);
} }
} }
} }
@ -1145,7 +1167,7 @@ void UI_EndFrame(UI_Frame *frame)
UI_Size pref_size = box->desc.pref_size[axis]; UI_Size pref_size = box->desc.pref_size[axis];
if (pref_size.kind == UI_SizeKind_Grow) if (pref_size.kind == UI_SizeKind_Grow)
{ {
box->layout.solved_dims[axis] = box->parent->layout.solved_dims[axis] * pref_size.v; box->target_dims[axis] = box->parent->target_dims[axis] * pref_size.v;
} }
} }
} }
@ -1156,7 +1178,7 @@ void UI_EndFrame(UI_Frame *frame)
UI_Box *box = boxes_pre[pre_index]; UI_Box *box = boxes_pre[pre_index];
for (Axis axis = 0; axis < Axis_COUNTXY; ++axis) for (Axis axis = 0; axis < Axis_COUNTXY; ++axis)
{ {
f32 box_size = box->layout.solved_dims[axis]; f32 box_size = box->target_dims[axis];
// Solve non-floating violations // Solve non-floating violations
{ {
f32 size_accum = 0; f32 size_accum = 0;
@ -1165,7 +1187,7 @@ void UI_EndFrame(UI_Frame *frame)
{ {
if (!AnyBit(child->desc.flags, UI_BoxFlag_Floating)) if (!AnyBit(child->desc.flags, UI_BoxFlag_Floating))
{ {
f32 size = child->layout.solved_dims[axis]; f32 size = child->target_dims[axis];
f32 strictness = child->desc.pref_size[axis].strictness; f32 strictness = child->desc.pref_size[axis].strictness;
f32 flex = size * (1.0 - strictness); f32 flex = size * (1.0 - strictness);
if (axis == box->desc.child_layout_axis) if (axis == box->desc.child_layout_axis)
@ -1188,7 +1210,7 @@ void UI_EndFrame(UI_Frame *frame)
{ {
if (!AnyBit(child->desc.flags, UI_BoxFlag_Floating)) if (!AnyBit(child->desc.flags, UI_BoxFlag_Floating))
{ {
f32 size = child->layout.solved_dims[axis]; f32 size = child->target_dims[axis];
f32 strictness = child->desc.pref_size[axis].strictness; f32 strictness = child->desc.pref_size[axis].strictness;
f32 flex = size * (1.0 - strictness); f32 flex = size * (1.0 - strictness);
f32 new_size = size; f32 new_size = size;
@ -1205,24 +1227,24 @@ void UI_EndFrame(UI_Frame *frame)
} }
} }
adjusted_size_accum += new_size; adjusted_size_accum += new_size;
child->layout.solved_dims[axis] = new_size; child->target_dims[axis] = new_size;
} }
} }
size_accum = adjusted_size_accum; size_accum = adjusted_size_accum;
} }
box->layout.final_children_size_accum[axis] = size_accum; box->final_children_size_accum[axis] = size_accum;
} }
// Solve floating violations // Solve floating violations
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) && !AnyBit(child->desc.flags, UI_BoxFlag_NoFloatingClamp)) if (AnyBit(child->desc.flags, UI_BoxFlag_Floating) && !AnyBit(child->desc.flags, UI_BoxFlag_NoFloatingClamp))
{ {
f32 size = child->layout.solved_dims[axis]; f32 size = child->target_dims[axis];
if (size > box_size) if (size > box_size)
{ {
f32 strictness = child->desc.pref_size[axis].strictness; f32 strictness = child->desc.pref_size[axis].strictness;
f32 flex = size * (1.0 - strictness); f32 flex = size * (1.0 - strictness);
child->layout.solved_dims[axis] = MaxF32(size - flex, box_size); child->target_dims[axis] = MaxF32(size - flex, box_size);
} }
} }
} }
@ -1235,29 +1257,51 @@ void UI_EndFrame(UI_Frame *frame)
UI_Box *box = boxes_pre[pre_index]; UI_Box *box = boxes_pre[pre_index];
UI_Box *parent = box->parent; UI_Box *parent = box->parent;
f32 lerp_rate = 20.0 * frame->dt;
if (box->gen != box->old_gen)
{
box->draw_dims[Axis_X] = 0;
box->draw_dims[Axis_Y] = 0;
}
if (box->desc.is_transient)
{
lerp_rate = 1;
}
lerp_rate = ClampF32(lerp_rate, 0, 1);
box->draw_dims[Axis_X] = LerpF32(box->draw_dims[Axis_X], box->target_dims[Axis_X], lerp_rate);
box->draw_dims[Axis_Y] = LerpF32(box->draw_dims[Axis_Y], box->target_dims[Axis_Y], lerp_rate);
Rng2 target_rect = Zi;
f32 target_rounding_tl = 0;
f32 target_rounding_tr = 0;
f32 target_rounding_br = 0;
f32 target_rounding_bl = 0;
// 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;
UI_AxisAlignment alignment = box->desc.child_alignment[axis]; UI_AxisAlignment alignment = box->desc.child_alignment[axis];
f32 box_size = box->layout.solved_dims[axis]; f32 box_size = box->draw_dims[axis];
f32 size_accum = box->layout.final_children_size_accum[axis]; f32 size_accum = box->final_children_size_accum[axis];
switch(alignment) switch(alignment)
{ {
default: break; default: break;
case UI_AxisAlignment_Center: case UI_AxisAlignment_Center:
{ {
box->layout.cursor = box_size / 2 - size_accum / 2; box->cursor = box_size / 2 - size_accum / 2;
} break; } break;
case UI_AxisAlignment_End: case UI_AxisAlignment_End:
{ {
box->layout.cursor = box_size - size_accum; box->cursor = box_size - size_accum;
} break; } break;
} }
} }
// Position // Position
{ {
f32 *dims_arr = box->layout.solved_dims; f32 *dims_arr = box->draw_dims;
Vec2 dims_vec = VEC2(dims_arr[0], dims_arr[1]); Vec2 dims_vec = VEC2(dims_arr[0], dims_arr[1]);
Vec2 final_pos = Zi; Vec2 final_pos = Zi;
@ -1265,23 +1309,23 @@ void UI_EndFrame(UI_Frame *frame)
if (AnyBit(box->desc.flags, UI_BoxFlag_Floating)) if (AnyBit(box->desc.flags, UI_BoxFlag_Floating))
{ {
Vec2 offset = box->desc.floating_pos; Vec2 offset = box->desc.floating_pos;
final_pos = AddVec2(parent->rect.p0, offset); final_pos = AddVec2(parent->draw_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 + dims_vec.x) - parent->rect.p1.x); f32 overshoot = MaxF32(0, (final_pos.x + dims_vec.x) - parent->draw_rect.p1.x);
final_pos.x = MaxF32(parent->rect.p0.x, final_pos.x - overshoot); final_pos.x = MaxF32(parent->draw_rect.p0.x, final_pos.x - overshoot);
} }
{ {
f32 overshoot = MaxF32((final_pos.y + dims_vec.y) - parent->rect.p1.y, 0); f32 overshoot = MaxF32((final_pos.y + dims_vec.y) - parent->draw_rect.p1.y, 0);
final_pos.y = MaxF32(parent->rect.p0.y, final_pos.y - overshoot); final_pos.y = MaxF32(parent->draw_rect.p0.y, final_pos.y - overshoot);
} }
} }
} }
// Non-floating box position // Non-floating box position
else if (parent) else if (parent)
{ {
f32 layout_cursor = parent->layout.cursor; f32 layout_cursor = parent->cursor;
f32 offset[2] = Zi; f32 offset[2] = Zi;
// Compute offset in layout direction // Compute offset in layout direction
{ {
@ -1297,34 +1341,34 @@ void UI_EndFrame(UI_Frame *frame)
default: break; default: break;
case UI_AxisAlignment_Center: case UI_AxisAlignment_Center:
{ {
f32 parent_size = parent->layout.solved_dims[axis]; f32 parent_size = parent->draw_dims[axis];
f32 box_size = dims_arr[axis]; f32 box_size = dims_arr[axis];
offset[axis] = parent_size / 2 - box_size / 2; offset[axis] = parent_size / 2 - box_size / 2;
} break; } break;
case UI_AxisAlignment_End: case UI_AxisAlignment_End:
{ {
f32 parent_size = parent->layout.solved_dims[axis]; f32 parent_size = parent->draw_dims[axis];
f32 box_size = dims_arr[axis]; f32 box_size = dims_arr[axis];
offset[axis] = parent_size - box_size; offset[axis] = parent_size - box_size;
} break; } break;
} }
} }
final_pos.x = parent->rect.p0.x + offset[0]; final_pos.x = parent->draw_rect.p0.x + offset[0];
final_pos.y = parent->rect.p0.y + offset[1]; final_pos.y = parent->draw_rect.p0.y + offset[1];
parent->layout.cursor += dims_arr[parent->desc.child_layout_axis]; parent->cursor += dims_arr[parent->desc.child_layout_axis];
} }
// Submit position // Submit position
Vec2 rounded_final_pos = RoundVec2(final_pos); Vec2 rounded_final_pos = RoundVec2(final_pos);
Vec2 rounded_dims = RoundVec2(dims_vec); Vec2 rounded_dims = RoundVec2(dims_vec);
box->rect.p0 = FloorVec2(rounded_final_pos); target_rect.p0 = FloorVec2(rounded_final_pos);
box->rect.p1 = AddVec2(rounded_final_pos, rounded_dims); target_rect.p1 = AddVec2(rounded_final_pos, rounded_dims);
} }
// Rounding // Rounding
{ {
UI_Round rounding = box->desc.rounding; UI_Round rounding = box->desc.rounding;
Vec2 half_dims = MulVec2(SubVec2(box->rect.p1, box->rect.p0), 0.5); Vec2 half_dims = MulVec2(SubVec2(target_rect.p1, target_rect.p0), 0.5);
f32 min_half_dims = MinF32(half_dims.x, half_dims.y); f32 min_half_dims = MinF32(half_dims.x, half_dims.y);
f32 final_rounding_tl = 0; f32 final_rounding_tl = 0;
f32 final_rounding_tr = 0; f32 final_rounding_tr = 0;
@ -1352,22 +1396,61 @@ void UI_EndFrame(UI_Frame *frame)
if (parent && !AllBits(box->desc.flags, UI_BoxFlag_Floating | UI_BoxFlag_NoFloatingClamp)) if (parent && !AllBits(box->desc.flags, UI_BoxFlag_Floating | UI_BoxFlag_NoFloatingClamp))
{ {
Vec2 vtl = SubVec2(VEC2(parent->rect.p0.x, parent->rect.p0.y), VEC2(box->rect.p0.x, box->rect.p0.y)); Vec2 vtl = SubVec2(VEC2(parent->draw_rect.p0.x, parent->draw_rect.p0.y), VEC2(target_rect.p0.x, target_rect.p0.y));
Vec2 vtr = SubVec2(VEC2(parent->rect.p1.x, parent->rect.p0.y), VEC2(box->rect.p1.x, box->rect.p0.y)); Vec2 vtr = SubVec2(VEC2(parent->draw_rect.p1.x, parent->draw_rect.p0.y), VEC2(target_rect.p1.x, target_rect.p0.y));
Vec2 vbr = SubVec2(VEC2(parent->rect.p1.x, parent->rect.p1.y), VEC2(box->rect.p1.x, box->rect.p1.y)); Vec2 vbr = SubVec2(VEC2(parent->draw_rect.p1.x, parent->draw_rect.p1.y), VEC2(target_rect.p1.x, target_rect.p1.y));
Vec2 vbl = SubVec2(VEC2(parent->rect.p0.x, parent->rect.p1.y), VEC2(box->rect.p0.x, box->rect.p1.y)); Vec2 vbl = SubVec2(VEC2(parent->draw_rect.p0.x, parent->draw_rect.p1.y), VEC2(target_rect.p0.x, target_rect.p1.y));
final_rounding_tl = MaxF32(final_rounding_tl, parent->rounding_tl - Vec2Len(vtl)); final_rounding_tl = MaxF32(final_rounding_tl, parent->draw_rounding_tl - Vec2Len(vtl));
final_rounding_tr = MaxF32(final_rounding_tr, parent->rounding_tr - Vec2Len(vtr)); final_rounding_tr = MaxF32(final_rounding_tr, parent->draw_rounding_tr - Vec2Len(vtr));
final_rounding_br = MaxF32(final_rounding_br, parent->rounding_br - Vec2Len(vbr)); final_rounding_br = MaxF32(final_rounding_br, parent->draw_rounding_br - Vec2Len(vbr));
final_rounding_bl = MaxF32(final_rounding_bl, parent->rounding_bl - Vec2Len(vbl)); final_rounding_bl = MaxF32(final_rounding_bl, parent->draw_rounding_bl - Vec2Len(vbl));
} }
// Submit rounding // Submit rounding
box->rounding_tl = final_rounding_tl; target_rounding_tl = final_rounding_tl;
box->rounding_tr = final_rounding_tr; target_rounding_tr = final_rounding_tr;
box->rounding_br = final_rounding_br; target_rounding_br = final_rounding_br;
box->rounding_bl = final_rounding_bl; target_rounding_bl = final_rounding_bl;
} }
// Interpolate target into final rect
// {
// f32 lerp_rate = 30.0 * frame->dt;
// if (box->gen != box->old_gen)
// {
// lerp_rate = 1;
// }
// box->draw_rect.p0 = LerpVec2(box->draw_rect.p0, box->target_rect.p0, lerp_rate);
// box->draw_rect.p1 = LerpVec2(box->draw_rect.p1, box->target_rect.p1, lerp_rate);
// box->draw_rounding_tl = LerpF32(box->draw_rounding_tl, box->target_rounding_tl, lerp_rate);
// box->draw_rounding_tr = LerpF32(box->draw_rounding_tr, box->target_rounding_tr, lerp_rate);
// box->draw_rounding_br = LerpF32(box->draw_rounding_br, box->target_rounding_br, lerp_rate);
// box->draw_rounding_bl = LerpF32(box->draw_rounding_bl, box->target_rounding_bl, lerp_rate);
// }
// {
// box->draw_rect.p0 = LerpVec2(box->draw_rect.p0, target_rect.p0, lerp_rate);
// box->draw_rect.p1 = LerpVec2(box->draw_rect.p1, target_rect.p1, lerp_rate);
// box->draw_rounding_tl = LerpF32(box->draw_rounding_tl, target_rounding_tl, lerp_rate);
// box->draw_rounding_tr = LerpF32(box->draw_rounding_tr, target_rounding_tr, lerp_rate);
// box->draw_rounding_br = LerpF32(box->draw_rounding_br, target_rounding_br, lerp_rate);
// box->draw_rounding_bl = LerpF32(box->draw_rounding_bl, target_rounding_bl, lerp_rate);
// }
{
box->draw_rect.p0 = target_rect.p0;
box->draw_rect.p1 = target_rect.p1;
box->draw_rounding_tl = target_rounding_tl;
box->draw_rounding_tr = target_rounding_tr;
box->draw_rounding_br = target_rounding_br;
box->draw_rounding_bl = target_rounding_bl;
}
box->gen = box->old_gen;
} }
////////////////////////////// //////////////////////////////
@ -1384,24 +1467,24 @@ void UI_EndFrame(UI_Frame *frame)
UI_Box *box = boxes_pre[pre_index]; UI_Box *box = boxes_pre[pre_index];
b32 is_visible = 1; b32 is_visible = 1;
is_visible = is_visible && (box->desc.tint.w != 0); is_visible = is_visible && (box->desc.tint.w != 0);
is_visible = is_visible && (box->rect.p1.x > box->rect.p0.x); is_visible = is_visible && (box->draw_rect.p1.x > box->draw_rect.p0.x);
is_visible = is_visible && (box->rect.p1.y > box->rect.p0.y); is_visible = is_visible && (box->draw_rect.p1.y > box->draw_rect.p0.y);
if (is_visible || AnyBit(frame->frame_flags, UI_FrameFlag_Debug)) if (is_visible || AnyBit(frame->frame_flags, UI_FrameFlag_Debug))
{ {
// Box rect // Box rect
{ {
UI_DRect *rect = PushStruct(frame->rects_arena, UI_DRect); UI_DRect *rect = PushStruct(frame->rects_arena, UI_DRect);
rect->bounds = box->rect; rect->bounds = box->draw_rect;
rect->background_lin = LinearFromSrgb(box->desc.background_color); rect->background_lin = LinearFromSrgb(box->desc.background_color);
rect->border_lin = LinearFromSrgb(box->desc.border_color); rect->border_lin = LinearFromSrgb(box->desc.border_color);
rect->debug_lin = LinearFromSrgb(box->desc.debug_color); rect->debug_lin = LinearFromSrgb(box->desc.debug_color);
rect->tint_lin = LinearFromSrgb(box->desc.tint); rect->tint_lin = LinearFromSrgb(box->desc.tint);
rect->border = box->desc.border; rect->border = box->desc.border;
rect->tl_rounding = box->rounding_tl; rect->tl_rounding = box->draw_rounding_tl;
rect->tr_rounding = box->rounding_tr; rect->tr_rounding = box->draw_rounding_tr;
rect->br_rounding = box->rounding_br; rect->br_rounding = box->draw_rounding_br;
rect->bl_rounding = box->rounding_bl; rect->bl_rounding = box->draw_rounding_bl;
rect->tex = box->raw_texture; rect->tex = box->raw_texture;
rect->tex_slice_uv = box->raw_texture_slice_uv; rect->tex_slice_uv = box->raw_texture_slice_uv;
} }
@ -1410,7 +1493,7 @@ void UI_EndFrame(UI_Frame *frame)
GC_Run raw_run = box->glyph_run; GC_Run raw_run = box->glyph_run;
if (AnyBit(box->desc.flags, UI_BoxFlag_DrawText) && raw_run.ready) if (AnyBit(box->desc.flags, UI_BoxFlag_DrawText) && raw_run.ready)
{ {
f32 max_baseline = DimsFromRng2(box->rect).x; f32 max_baseline = DimsFromRng2(box->draw_rect).x;
b32 should_truncate = raw_run.baseline_length > max_baseline && !AnyBit(box->desc.flags, UI_BoxFlag_NoTextTruncation); b32 should_truncate = raw_run.baseline_length > max_baseline && !AnyBit(box->desc.flags, UI_BoxFlag_NoTextTruncation);
// Truncate run // Truncate run
@ -1466,22 +1549,22 @@ void UI_EndFrame(UI_Frame *frame)
f32 font_descent = raw_run.font_descent; f32 font_descent = raw_run.font_descent;
f32 cap = raw_run.font_cap; f32 cap = raw_run.font_cap;
f32 baseline_height = ascent + font_descent; f32 baseline_height = ascent + font_descent;
Vec2 box_dims = DimsFromRng2(box->rect); Vec2 box_dims = DimsFromRng2(box->draw_rect);
Vec2 baseline = Zi; Vec2 baseline = Zi;
switch (x_alignment) switch (x_alignment)
{ {
case UI_AxisAlignment_Start: case UI_AxisAlignment_Start:
{ {
baseline.x = box->rect.p0.x; baseline.x = box->draw_rect.p0.x;
} break; } break;
case UI_AxisAlignment_End: case UI_AxisAlignment_End:
{ {
baseline.x = box->rect.p1.x; baseline.x = box->draw_rect.p1.x;
baseline.x -= final_baseline_length; baseline.x -= final_baseline_length;
} break; } break;
case UI_AxisAlignment_Center: case UI_AxisAlignment_Center:
{ {
baseline.x = box->rect.p0.x; baseline.x = box->draw_rect.p0.x;
baseline.x += (box_dims.x - final_baseline_length) / 2; baseline.x += (box_dims.x - final_baseline_length) / 2;
} break; } break;
} }
@ -1489,17 +1572,17 @@ void UI_EndFrame(UI_Frame *frame)
{ {
case UI_AxisAlignment_Start: case UI_AxisAlignment_Start:
{ {
baseline.y = box->rect.p0.y; baseline.y = box->draw_rect.p0.y;
baseline.y += ascent; baseline.y += ascent;
} break; } break;
case UI_AxisAlignment_End: case UI_AxisAlignment_End:
{ {
baseline.y = box->rect.p1.y; baseline.y = box->draw_rect.p1.y;
baseline.y -= font_descent; baseline.y -= font_descent;
} break; } break;
case UI_AxisAlignment_Center: case UI_AxisAlignment_Center:
{ {
baseline.y = box->rect.p0.y; baseline.y = box->draw_rect.p0.y;
baseline.y += box_dims.y / 2; baseline.y += box_dims.y / 2;
baseline.y += cap / 2; baseline.y += cap / 2;
} break; } break;

View File

@ -218,6 +218,8 @@ Struct(UI_BoxDesc)
UI_Key key; UI_Key key;
UI_Key parent; UI_Key parent;
b32 is_transient;
UI_Size pref_size[Axis_COUNTXY]; UI_Size pref_size[Axis_COUNTXY];
UI_Round rounding; UI_Round rounding;
Vec4 background_color; Vec4 background_color;
@ -267,6 +269,9 @@ Struct(UI_Box)
UI_Report report; UI_Report report;
i64 last_build_tick; i64 last_build_tick;
i64 old_gen;
i64 gen;
//- Tree links //- Tree links
UI_Box *parent; UI_Box *parent;
UI_Box *first; UI_Box *first;
@ -286,19 +291,17 @@ Struct(UI_Box)
u64 post_index; u64 post_index;
//- Layout data //- Layout data
struct f32 cursor;
{ f32 final_children_size_accum[Axis_COUNTXY];
f32 cursor; f32 target_dims[Axis_COUNTXY];
f32 solved_dims[Axis_COUNTXY]; f32 draw_dims[Axis_COUNTXY];
f32 final_children_size_accum[Axis_COUNTXY];
} layout;
//- Layout results //- Layout results
Rng2 rect; Rng2 draw_rect;
f32 rounding_tl; f32 draw_rounding_tl;
f32 rounding_tr; f32 draw_rounding_tr;
f32 rounding_br; f32 draw_rounding_br;
f32 rounding_bl; f32 draw_rounding_bl;
}; };
Struct(UI_BoxBin) Struct(UI_BoxBin)
@ -354,6 +357,7 @@ Struct(UI_Frame)
i64 tick; i64 tick;
i64 time_ns; i64 time_ns;
i64 dt_ns; i64 dt_ns;
f64 dt;
// Control // Control
Vec2 cursor_pos; Vec2 cursor_pos;
@ -385,7 +389,7 @@ Struct(UI_Ctx)
UI_BoxBin box_bins[Kibi(256)]; UI_BoxBin box_bins[Kibi(256)];
UI_Box *first_free_box; UI_Box *first_free_box;
u64 transient_key_seed; u64 rand_key_seed;
i64 current_frame_tick; i64 current_frame_tick;
UI_Frame frames[2]; UI_Frame frames[2];
@ -411,7 +415,7 @@ b32 UI_IsKeyNil(UI_Key key);
UI_Key UI_KeyFromString(String str); UI_Key UI_KeyFromString(String str);
UI_Key UI_KeyF_(String fmt, ...); UI_Key UI_KeyF_(String fmt, ...);
#define UI_KeyF(fmt_cstr, ...) UI_KeyF_(StringFromCstrNoLimit(fmt_cstr), __VA_ARGS__, FmtEnd) #define UI_KeyF(fmt_cstr, ...) UI_KeyF_(StringFromCstrNoLimit(fmt_cstr), __VA_ARGS__, FmtEnd)
UI_Key UI_TransKey(void); UI_Key UI_RandKey(void);
UI_Box *UI_BoxFromKey(UI_Key key); UI_Box *UI_BoxFromKey(UI_Key key);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@ -480,8 +484,8 @@ UI_Style UI_PopStyle(UI_StyleDesc desc);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Command helpers //~ Command helpers
UI_Key UI_BuildBoxEx(UI_Key key); UI_Key UI_BuildBoxEx(UI_Key semantic_key);
#define UI_BuildBox() UI_BuildBoxEx(UI_TransKey()) #define UI_BuildBox() UI_BuildBoxEx(UI_NilKey)
void UI_SetRawTexture(UI_Key key, G_Texture2DRef tex, Rng2 uv); void UI_SetRawTexture(UI_Key key, G_Texture2DRef tex, Rng2 uv);

View File

@ -17,5 +17,5 @@ UI_Key UI_BuildDivider(UI_Size size, Vec4 color, Axis axis);
UI_Key UI_BuildRowEx(UI_Key key); UI_Key UI_BuildRowEx(UI_Key key);
UI_Key UI_BuildColumnEx(UI_Key key); UI_Key UI_BuildColumnEx(UI_Key key);
#define UI_BuildRow() UI_BuildRowEx(UI_TransKey()) #define UI_BuildRow() UI_BuildRowEx(UI_NilKey)
#define UI_BuildColumn() UI_BuildColumnEx(UI_TransKey()) #define UI_BuildColumn() UI_BuildColumnEx(UI_NilKey)