ui signals wip

This commit is contained in:
jacob 2026-03-30 21:19:09 -05:00
parent 246f63a61f
commit d4bfdfcd4c
2 changed files with 518 additions and 68 deletions

View File

@ -528,6 +528,20 @@ void UI_SetRawTexture(UI_Key key, G_TextureRef tex, Rng2 uv)
SllQueuePush(frame->first_cmd_node, frame->last_cmd_node, n); SllQueuePush(frame->first_cmd_node, frame->last_cmd_node, n);
} }
UI_ButtonState *UI_SignalButton(UI_Key key, Button button)
{
UI_Frame *frame = UI_CurrentFrame();
UI_CmdNode *n = PushStruct(frame->arena, UI_CmdNode);
n->cmd.kind = UI_CmdKind_Signal;
{
n->cmd.signal.key = key;
n->cmd.signal.button = button;
}
++frame->cmds_count;
SllQueuePush(frame->first_cmd_node, frame->last_cmd_node, n);
return &n->cmd.signal.button_state;
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Report //~ Report
@ -545,9 +559,16 @@ UI_BoxReports UI_ReportsFromKey(UI_Key key)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Begin frame //~ Begin frame
UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags) UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags)
{ {
////////////////////////////// //////////////////////////////
//- Init persistent state //- Init persistent state
@ -613,7 +634,7 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags)
frame->frame_flags = frame_flags; frame->frame_flags = frame_flags;
////////////////////////////// //////////////////////////////
//- Process controller events //- Process controller events into signals
frame->cursor_pos = prev_frame->cursor_pos; frame->cursor_pos = prev_frame->cursor_pos;
frame->drag_cursor_pos = prev_frame->drag_cursor_pos; frame->drag_cursor_pos = prev_frame->drag_cursor_pos;
@ -676,15 +697,13 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags)
} }
for (u64 button_idx = 0; button_idx < countof(report->buttons); ++button_idx) for (u64 button_idx = 0; button_idx < countof(report->buttons); ++button_idx)
{ {
UI_ButtonReport *br = &report->buttons[button_idx]; UI_ButtonState *br = &report->buttons[button_idx];
b32 old_held = br->held; b32 old_held = br->held;
{ {
ZeroStruct(br); ZeroStruct(br);
} }
br->held = old_held; br->held = old_held;
} }
box->reports.old_tree_capture = box->reports.tree_capture;
box->reports.tree_capture = UI_NilKey;
report->is_hot = 0; report->is_hot = 0;
} }
@ -810,74 +829,110 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags)
// hot_box->reports.tree_capture = hot_box->key; // hot_box->reports.tree_capture = hot_box->key;
} }
//- Update box reports
{
f32 lower_target = TweakFloat("UI lower blend target", -0.05, -1, 0);
f32 upper_target = TweakFloat("UI upper blend target", 1.05, 1, 10);
for (u64 pre_index = UI.boxes_count; pre_index-- > 0;)
{
UI_Box *box = prev_frame->boxes_pre[pre_index];
UI_BoxReport *report = &box->reports.draw;
UI_DebugBreak(box, UI_DebugBreakFlag_BuildReport);
if (box == hot_box)
//- Update reports from signals
for (UI_CmdNode *cmd_node = frame->first_cmd_node; cmd_node; cmd_node = cmd_node->next)
{ {
report->is_hot = 1; UI_Cmd cmd = cmd_node->cmd;
box->reports.tree_capture = box->key; if (cmd.kind == UI_CmdKind_Signal)
{
UI_Box *box = UI_BoxFromKey(cmd.signal.key);
if (box)
{
Button button = cmd.signal.button;
UI_ButtonState state = cmd.signal.button_state;
box->reports.draw.buttons[button] = state;
// Propagate button state upwards
for (UI_Box *parent = box->parent; parent; parent = parent->parent)
{
if (parent->desc.flags & UI_BoxFlag_CaptureThroughChildren)
{
parent->reports.draw.buttons[button] = state;
}
} }
if (!UI_MatchKey(box->reports.tree_capture, box->reports.old_tree_capture))
{
for (u64 button_idx = 0; button_idx < countof(report->buttons); ++button_idx)
{
UI_ButtonReport *button = &report->buttons[button_idx];
if (button->held)
{
button->held = 0;
button->ups += 1;
} }
} }
} }
f32 target_exists = (box->last_build_tick >= (frame->tick - 1)) ? upper_target : lower_target;
f32 target_hovered = report->is_hovered ? Inf : lower_target;
f32 target_hot = report->is_hot ? Inf : lower_target;
f32 target_active = box == active_box ? Inf : lower_target;
f64 target_misc = box->desc.misc;
// TODO: Configurable per-box blend rates
f32 exists_blend_rate = (30 * frame->dt);
f32 hot_blend_rate = (15 * frame->dt);
f32 active_blend_rate = (15 * frame->dt);
f32 hovered_blend_rate = (15 * frame->dt);
// f64 misc_blend_rate = (30 * frame->dt);
f64 misc_blend_rate = 1;
report->exists = SaturateF32(LerpF32(report->exists, target_exists, exists_blend_rate));
report->hot = SaturateF32(LerpF32(report->hot, target_hot, hot_blend_rate));
report->active = SaturateF32(LerpF32(report->active, target_active, active_blend_rate));
report->hovered = SaturateF32(LerpF32(report->hovered, target_hovered, hovered_blend_rate));
report->misc = SaturateF32(LerpF32(report->misc, target_misc, misc_blend_rate));
report->screen_rect = box->screen_rect;
report->screen_anchor = box->screen_anchor;
if (mouse_downs > 0)
{
box->reports.drag = *report;
}
// Propagate upwards captures to tree
if (box->parent && !UI_IsKeyNil(box->reports.tree_capture))
{
box->parent->reports.tree_capture = box->reports.tree_capture; //- Update box reports from inputs
UI_Box *captured_child = UI_BoxFromKey(box->reports.tree_capture); // {
if (box->parent->desc.flags & UI_BoxFlag_CaptureThroughChildren) // f32 lower_target = TweakFloat("UI lower blend target", -0.05, -1, 0);
{ // f32 upper_target = TweakFloat("UI upper blend target", 1.05, 1, 10);
box->parent->reports.draw = captured_child->reports.draw; // for (u64 pre_index = UI.boxes_count; pre_index-- > 0;)
} // {
} // UI_Box *box = prev_frame->boxes_pre[pre_index];
} // UI_BoxReport *report = &box->reports.draw;
} // UI_DebugBreak(box, UI_DebugBreakFlag_BuildReport);
// if (box == hot_box)
// {
// report->is_hot = 1;
// box->reports.tree_capture = box->key;
// }
// // if (!UI_MatchKey(box->reports.tree_capture, box->reports.old_tree_capture))
// // {
// // for (u64 button_idx = 0; button_idx < countof(report->buttons); ++button_idx)
// // {
// // UI_ButtonState *button = &report->buttons[button_idx];
// // if (button->held)
// // {
// // button->held = 0;
// // button->ups += 1;
// // }
// // }
// // }
// f32 target_exists = (box->last_build_tick >= (frame->tick - 1)) ? upper_target : lower_target;
// f32 target_hovered = report->is_hovered ? Inf : lower_target;
// f32 target_hot = report->is_hot ? Inf : lower_target;
// f32 target_active = box == active_box ? Inf : lower_target;
// f64 target_misc = box->desc.misc;
// // TODO: Configurable per-box blend rates
// f32 exists_blend_rate = (30 * frame->dt);
// f32 hot_blend_rate = (15 * frame->dt);
// f32 active_blend_rate = (15 * frame->dt);
// f32 hovered_blend_rate = (15 * frame->dt);
// // f64 misc_blend_rate = (30 * frame->dt);
// f64 misc_blend_rate = 1;
// report->exists = SaturateF32(LerpF32(report->exists, target_exists, exists_blend_rate));
// report->hot = SaturateF32(LerpF32(report->hot, target_hot, hot_blend_rate));
// report->active = SaturateF32(LerpF32(report->active, target_active, active_blend_rate));
// report->hovered = SaturateF32(LerpF32(report->hovered, target_hovered, hovered_blend_rate));
// report->misc = SaturateF32(LerpF32(report->misc, target_misc, misc_blend_rate));
// report->screen_rect = box->screen_rect;
// report->screen_anchor = box->screen_anchor;
// if (mouse_downs > 0)
// {
// box->reports.drag = *report;
// }
// // Propagate upwards captures to tree
// if (box->parent && !UI_IsKeyNil(box->reports.tree_capture))
// {
// box->parent->reports.tree_capture = box->reports.tree_capture;
// UI_Box *captured_child = UI_BoxFromKey(box->reports.tree_capture);
// if (box->parent->desc.flags & UI_BoxFlag_CaptureThroughChildren)
// {
// box->parent->reports.draw.captures = captured_child->reports.draw.captures;
// }
// }
// }
// }
if (mouse_downs > 0) if (mouse_downs > 0)
{ {
@ -902,6 +957,392 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags)
return frame; return frame;
} }
// UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags)
// {
// //////////////////////////////
// //- Init persistent state
// if (!UI.box_arena)
// {
// UI.box_arena = AcquireArena(Gibi(64));
// UI.gpu_frame_arena = G_AcquireArena();
// // Init frames
// for (u64 i = 0; i < countof(UI.frames); ++i)
// {
// UI_Frame *frame = &UI.frames[i];
// frame->arena = AcquireArena(Gibi(64));
// frame->rects_arena = AcquireArena(Gibi(64));
// }
// // Init root box
// {
// UI_Box *box = PushStruct(UI.box_arena, UI_Box);
// box->key = UI_RootKey;
// box->gen = 1;
// UI.boxes_count += 1;
// UI_BoxBin *bin = &UI.box_bins[box->key.v % countof(UI.box_bins)];
// bin->first = box;
// bin->last = box;
// UI.root_box = box;
// }
// }
// //////////////////////////////
// //- Begin frame
// UI.cur_frame_tick += 1;
// UI_Frame *prev_frame = UI_PrevFrame();
// UI_Frame *frame = UI_CurrentFrame();
// {
// Arena *old_arena = frame->arena;
// Arena *old_rects_arena = frame->rects_arena;
// ZeroStruct(frame);
// frame->arena = old_arena;
// frame->rects_arena = old_rects_arena;
// }
// frame->window_frame = WND_BeginFrame(G_Format_R16G16B16A16_Float, WND_BackbufferSizeMode_MatchMonitor);
// UI.cl = G_PrepareCommandList(G_QueueKind_Direct);
// ResetArena(frame->arena);
// ResetArena(frame->rects_arena);
// G_ResetArena(UI.cl, UI.gpu_frame_arena);
// {
// i64 now_ns = TimeNs();
// i64 dt_ns = now_ns - prev_frame->time_ns;
// frame->time_ns = now_ns;
// frame->dt_ns = dt_ns;
// frame->dt = SecondsFromNs(frame->dt_ns);
// frame->tick = UI.cur_frame_tick;
// }
// // Init style stack
// {
// frame->top_stack = PushStruct(frame->arena, UI_Stack);
// UI_PushDefaults();
// }
// frame->frame_flags = frame_flags;
// //////////////////////////////
// //- Process controller events
// frame->cursor_pos = prev_frame->cursor_pos;
// frame->drag_cursor_pos = prev_frame->drag_cursor_pos;
// if (prev_frame->boxes_pre != 0)
// {
// ControllerEventsArray controller_events = frame->window_frame.controller_events;
// //- Locate boxes
// UI_Box *top_hovered_box = 0;
// UI_Box *active_box = UI_BoxFromKey(prev_frame->active_box);
// //- Update cursor pos
// for (u64 cev_index = 0; cev_index < controller_events.count; ++cev_index)
// {
// ControllerEvent *cev = &controller_events.events[cev_index];
// if (cev->kind == ControllerEventKind_CursorMove)
// {
// frame->cursor_pos = Vec2FromVec(cev->cursor_pos);
// }
// }
// //- Init box reports
// // TODO: Iterate in post order and early out
// for (u64 pre_index = UI.boxes_count; pre_index-- > 0;)
// {
// UI_Box *box = prev_frame->boxes_pre[pre_index];
// UI_BoxReport *report = &box->reports.draw;
// b32 is_cursor_in_box = 0;
// {
// // TODO: More efficient test. This logic is just copied from the renderer's SDF function for now.
// Rng2 interactable_region = IntersectRng2(box->solved_scissor, box->screen_rect);
// Vec2 p0 = interactable_region.p0;
// Vec2 p1 = interactable_region.p1;
// Vec2 point = frame->cursor_pos;
// 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 corner_edge_dist = non_corner_edge_dist;
// if (non_corner_edge_dist >= 0)
// {
// f32 tl_radius = box->rounding_tl;
// f32 tr_radius = box->rounding_tr;
// f32 br_radius = box->rounding_br;
// f32 bl_radius = box->rounding_bl;
// Vec2 tl = VEC2(p0.x + tl_radius, p0.y + tl_radius);
// Vec2 tr = VEC2(p1.x - tr_radius, p0.y + tr_radius);
// Vec2 br = VEC2(p1.x - br_radius, p1.y - br_radius);
// Vec2 bl = VEC2(p0.x + bl_radius, p1.y - bl_radius);
// if (point.x < tl.x && point.y < tl.y) corner_edge_dist = MinF32(corner_edge_dist, tl_radius - Vec2Len(SubVec2(tl, point)));
// if (point.x > tr.x && point.y < tr.y) corner_edge_dist = MinF32(corner_edge_dist, tr_radius - Vec2Len(SubVec2(tr, point)));
// if (point.x > br.x && point.y > br.y) corner_edge_dist = MinF32(corner_edge_dist, br_radius - Vec2Len(SubVec2(br, point)));
// if (point.x < bl.x && point.y > bl.y) corner_edge_dist = MinF32(corner_edge_dist, bl_radius - Vec2Len(SubVec2(bl, point)));
// }
// is_cursor_in_box = non_corner_edge_dist >= 0 && corner_edge_dist >= 0;
// }
// report->is_hovered = is_cursor_in_box;
// if (top_hovered_box == 0 && (box->desc.flags & UI_BoxFlag_CaptureMouse) && is_cursor_in_box)
// {
// top_hovered_box = box;
// }
// for (u64 button_idx = 0; button_idx < countof(report->buttons); ++button_idx)
// {
// UI_ButtonState *br = &report->buttons[button_idx];
// b32 old_held = br->held;
// {
// ZeroStruct(br);
// }
// br->held = old_held;
// }
// box->reports.old_tree_capture = box->reports.tree_capture;
// box->reports.tree_capture = UI_NilKey;
// report->is_hot = 0;
// }
// //- Update state from controller events
// i32 mouse_downs = 0;
// for (u64 cev_index = 0; cev_index < controller_events.count; ++cev_index)
// {
// ControllerEvent *cev = &controller_events.events[cev_index];
// switch (cev->kind)
// {
// default: break;
// case ControllerEventKind_ButtonDown:
// {
// if (IsClickButton(cev->button) || IsWheelButton(cev->button))
// {
// mouse_downs += 1;
// if (top_hovered_box && active_box == 0)
// {
// if (cev->button == Button_M1)
// {
// ++top_hovered_box->reports.draw.buttons[Button_M1].downs;
// top_hovered_box->reports.draw.buttons[Button_M1].held = 1;
// active_box = top_hovered_box;
// }
// else if (cev->button == Button_M2)
// {
// ++top_hovered_box->reports.draw.buttons[Button_M2].downs;
// top_hovered_box->reports.draw.buttons[Button_M2].held = 1;
// active_box = top_hovered_box;
// }
// else if (cev->button == Button_M3)
// {
// ++top_hovered_box->reports.draw.buttons[Button_M3].downs;
// top_hovered_box->reports.draw.buttons[Button_M3].held = 1;
// active_box = top_hovered_box;
// }
// else if (cev->button == Button_WheelUp)
// {
// ++top_hovered_box->reports.draw.buttons[Button_WheelUp].downs;
// ++top_hovered_box->reports.draw.buttons[Button_WheelUp].ups;
// ++top_hovered_box->reports.draw.buttons[Button_WheelUp].presses;
// }
// else if (cev->button == Button_WheelDown)
// {
// ++top_hovered_box->reports.draw.buttons[Button_WheelDown].downs;
// ++top_hovered_box->reports.draw.buttons[Button_WheelDown].ups;
// ++top_hovered_box->reports.draw.buttons[Button_WheelDown].presses;
// }
// cev->captures += 1;
// }
// }
// } break;
// case ControllerEventKind_ButtonUp:
// {
// if (IsClickButton(cev->button))
// {
// if (active_box)
// {
// if (cev->button == Button_M1)
// {
// if (active_box == top_hovered_box)
// {
// ++active_box->reports.draw.buttons[Button_M1].presses;
// }
// ++active_box->reports.draw.buttons[Button_M1].ups;
// if (active_box->reports.draw.buttons[Button_M1].held)
// {
// active_box->reports.draw.buttons[Button_M1].held = 0;
// active_box = 0;
// }
// }
// else if (cev->button == Button_M2)
// {
// if (active_box == top_hovered_box)
// {
// ++active_box->reports.draw.buttons[Button_M2].presses;
// }
// ++active_box->reports.draw.buttons[Button_M2].ups;
// if (active_box->reports.draw.buttons[Button_M2].held)
// {
// active_box->reports.draw.buttons[Button_M2].held = 0;
// active_box = 0;
// }
// }
// else if (cev->button == Button_M3)
// {
// if (active_box == top_hovered_box)
// {
// ++active_box->reports.draw.buttons[Button_M3].presses;
// }
// ++active_box->reports.draw.buttons[Button_M3].ups;
// if (active_box->reports.draw.buttons[Button_M3].held)
// {
// active_box->reports.draw.buttons[Button_M3].held = 0;
// active_box = 0;
// }
// }
// }
// }
// else if (IsWheelButton(cev->button) && top_hovered_box)
// {
// cev->captures += 1;
// }
// } break;
// case ControllerEventKind_Quit:
// {
// SignalExit(0);
// } break;
// }
// }
// UI_Box *hot_box = active_box;
// if (top_hovered_box && !active_box)
// {
// hot_box = top_hovered_box;
// }
// if (hot_box)
// {
// // hot_box->reports.tree_capture = hot_box->key;
// }
// //- Update box reports from captures
// {
// f32 lower_target = TweakFloat("UI lower blend target", -0.05, -1, 0);
// f32 upper_target = TweakFloat("UI upper blend target", 1.05, 1, 10);
// for (u64 pre_index = UI.boxes_count; pre_index-- > 0;)
// {
// UI_Box *box = prev_frame->boxes_pre[pre_index];
// UI_BoxReport *report = &box->reports.draw;
// UI_DebugBreak(box, UI_DebugBreakFlag_BuildReport);
// if (box == hot_box)
// {
// report->is_hot = 1;
// box->reports.tree_capture = box->key;
// }
// // if (!UI_MatchKey(box->reports.tree_capture, box->reports.old_tree_capture))
// // {
// // for (u64 button_idx = 0; button_idx < countof(report->buttons); ++button_idx)
// // {
// // UI_ButtonState *button = &report->buttons[button_idx];
// // if (button->held)
// // {
// // button->held = 0;
// // button->ups += 1;
// // }
// // }
// // }
// f32 target_exists = (box->last_build_tick >= (frame->tick - 1)) ? upper_target : lower_target;
// f32 target_hovered = report->is_hovered ? Inf : lower_target;
// f32 target_hot = report->is_hot ? Inf : lower_target;
// f32 target_active = box == active_box ? Inf : lower_target;
// f64 target_misc = box->desc.misc;
// // TODO: Configurable per-box blend rates
// f32 exists_blend_rate = (30 * frame->dt);
// f32 hot_blend_rate = (15 * frame->dt);
// f32 active_blend_rate = (15 * frame->dt);
// f32 hovered_blend_rate = (15 * frame->dt);
// // f64 misc_blend_rate = (30 * frame->dt);
// f64 misc_blend_rate = 1;
// report->exists = SaturateF32(LerpF32(report->exists, target_exists, exists_blend_rate));
// report->hot = SaturateF32(LerpF32(report->hot, target_hot, hot_blend_rate));
// report->active = SaturateF32(LerpF32(report->active, target_active, active_blend_rate));
// report->hovered = SaturateF32(LerpF32(report->hovered, target_hovered, hovered_blend_rate));
// report->misc = SaturateF32(LerpF32(report->misc, target_misc, misc_blend_rate));
// report->screen_rect = box->screen_rect;
// report->screen_anchor = box->screen_anchor;
// if (mouse_downs > 0)
// {
// box->reports.drag = *report;
// }
// // Propagate upwards captures to tree
// if (box->parent && !UI_IsKeyNil(box->reports.tree_capture))
// {
// box->parent->reports.tree_capture = box->reports.tree_capture;
// UI_Box *captured_child = UI_BoxFromKey(box->reports.tree_capture);
// if (box->parent->desc.flags & UI_BoxFlag_CaptureThroughChildren)
// {
// box->parent->reports.draw.captures = captured_child->reports.draw.captures;
// }
// }
// }
// }
// if (mouse_downs > 0)
// {
// frame->drag_cursor_pos = frame->cursor_pos;
// }
// frame->top_hovered_box = top_hovered_box ? top_hovered_box->key : UI_NilKey;
// frame->hot_box = hot_box ? hot_box->key : UI_NilKey;
// frame->active_box = active_box ? active_box->key : UI_NilKey;
// }
// //////////////////////////////
// //- Build root box
// {
// UI_SetNext(Width, UI_PIX(frame->window_frame.draw_size.x, 1));
// UI_SetNext(Height, UI_PIX(frame->window_frame.draw_size.y, 1));
// UI_SetNext(Parent, UI_NilKey);
// UI_BuildBoxEx(UI_RootKey);
// }
// return frame;
// }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Frame helpers //~ Frame helpers

View File

@ -209,9 +209,9 @@ Struct(UI_Stack)
}; };
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Report types //~ Input types
Struct(UI_ButtonReport) Struct(UI_ButtonState)
{ {
b32 held; // Persistent b32 held; // Persistent
i32 downs; // Mouse button down events over box causing activation i32 downs; // Mouse button down events over box causing activation
@ -219,11 +219,14 @@ Struct(UI_ButtonReport)
i32 presses; // Mouse button events while box is active and hovered i32 presses; // Mouse button events while box is active and hovered
}; };
////////////////////////////////////////////////////////////
//~ Report types
Struct(UI_CapturesReport) Struct(UI_CapturesReport)
{ {
b32 is_hovered; b32 is_hovered;
b32 is_hot; b32 is_hot;
UI_ButtonReport buttons[Button_COUNT]; UI_ButtonState buttons[Button_COUNT];
}; };
Struct(UI_BoxReport) Struct(UI_BoxReport)
@ -242,8 +245,6 @@ Struct(UI_BoxReports)
{ {
UI_BoxReport draw; // Box data used for last render UI_BoxReport draw; // Box data used for last render
UI_BoxReport drag; // Box data during last mouse button down event UI_BoxReport drag; // Box data during last mouse button down event
UI_Key tree_capture; // Key of captured child (if any)
UI_Key old_tree_capture;
}; };
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@ -253,6 +254,7 @@ Enum(UI_CmdKind)
{ {
UI_CmdKind_None, UI_CmdKind_None,
UI_CmdKind_BuildBox, UI_CmdKind_BuildBox,
UI_CmdKind_Signal,
UI_CmdKind_SetRawTexture, UI_CmdKind_SetRawTexture,
}; };
@ -297,6 +299,12 @@ Struct(UI_Cmd)
{ {
UI_BoxDesc box; UI_BoxDesc box;
struct struct
{
UI_Key key;
Button button;
UI_ButtonState button_state;
} signal;
struct
{ {
UI_Key key; UI_Key key;
G_TextureRef tex; G_TextureRef tex;
@ -544,6 +552,7 @@ UI_Key UI_BuildBoxEx(UI_Key semantic_key);
#define UI_BuildBox() UI_BuildBoxEx(UI_NilKey) #define UI_BuildBox() UI_BuildBoxEx(UI_NilKey)
void UI_SetRawTexture(UI_Key key, G_TextureRef tex, Rng2 uv); void UI_SetRawTexture(UI_Key key, G_TextureRef tex, Rng2 uv);
UI_ButtonState *UI_SignalButton(UI_Key key, Button button);
#if IsRtcEnabled #if IsRtcEnabled
#define UI_DebugBreak(box, target_flags) do { if (box->desc.debug_break_flags & target_flags) { DEBUGBREAK; } } while (0) #define UI_DebugBreak(box, target_flags) do { if (box->desc.debug_break_flags & target_flags) { DEBUGBREAK; } } while (0)