From d7c42d24623f21ea4f2ca3676bcf00ad5108ba5a Mon Sep 17 00:00:00 2001 From: jacob Date: Mon, 30 Mar 2026 23:45:29 -0500 Subject: [PATCH] refactor from per-box ui input state to global input state w/ per-box capture propagation --- src/pp/pp_vis/pp_vis_core.c | 35 +- src/ui/ui_core.c | 1097 ++++++++++++----------------------- src/ui/ui_core.h | 101 ++-- 3 files changed, 427 insertions(+), 806 deletions(-) diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index af87790a..bcf93a70 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -1149,7 +1149,7 @@ void V_TickForever(WaveLaneCtx *lane) V_TextboxDeltaList text_input_deltas = Zi; - frame->has_mouse_focus = UI_IsKeyNil(ui_frame->hot_box) || UI_MatchKey(ui_frame->hot_box, vis_game_box); + frame->has_mouse_focus = UI_IsKeyNil(ui_frame->top_hot_box) || UI_MatchKey(ui_frame->top_hot_box, vis_game_box); frame->has_keyboard_focus = 1; Vec2 mouse_delta = Zi; { @@ -3839,7 +3839,7 @@ void V_TickForever(WaveLaneCtx *lane) } // Insert ghost tab - if (V.dragging_window && UI_Hovered(panel->key)) + if (V.dragging_window && UI_HotAbsolute(panel->key)) { DrawableTab *left = 0; DrawableTab *right = first_drawable_tab; @@ -4272,7 +4272,7 @@ void V_TickForever(WaveLaneCtx *lane) UI_SetNext(AxisSize, UI_PIX(divider_size, 1), .axis = !panel->axis); UI_BuildBoxEx(panel->divider_key); - if (UI_TargetHot(panel->divider_key) || UI_Held(panel->divider_key, Button_M1)) + if (UI_HotAbsolute(panel->divider_key) || UI_Held(panel->divider_key, Button_M1)) { if (panel->axis == Axis_X) { @@ -4347,6 +4347,12 @@ void V_TickForever(WaveLaneCtx *lane) { UI_Push(Tag, HashF("developer command palette")); + // FIXME: Remove this + if (frame->tick == 1) + { + palette->pos = VEC2(1000, 1000); + } + UI_Size total_width = UI_FNT(40, 1); UI_Size total_height = UI_FNT(30, 1); UI_Size header_height = UI_FNT(1.3, 1); @@ -4570,7 +4576,7 @@ void V_TickForever(WaveLaneCtx *lane) has_focus = 0; } - if (UI_TargetHot(search_box)) + if (UI_HotAbsolute(search_box)) { WND_SetCursor(window_frame, WND_CursorKind_Text); } @@ -4948,7 +4954,7 @@ void V_TickForever(WaveLaneCtx *lane) { new_tweak_str = tweak_var.initial; } - if (UI_TargetHot(reset_key)) + if (UI_HotAbsolute(reset_key)) { WND_SetCursor(window_frame, WND_CursorKind_Hand); } @@ -5040,7 +5046,7 @@ void V_TickForever(WaveLaneCtx *lane) UI_Key slider_key = UI_KeyF("tweak slider"); UI_Key marker_key = UI_KeyF("tweak slider marker"); - b32 is_hot = UI_TargetHot(slider_key) || UI_TargetHot(marker_key); + b32 is_hot = UI_HotAbsolute(slider_key) || UI_HotAbsolute(marker_key); b32 is_active = UI_Held(slider_key, Button_M1) || UI_Held(marker_key, Button_M1); f32 hot = MaxF32(UI_Hot(slider_key), UI_Hot(marker_key)); @@ -5157,7 +5163,7 @@ void V_TickForever(WaveLaneCtx *lane) pressed_color.w = 0.2; f32 hotkey_hot = UI_Hot(hotkey_box); f32 hotkey_active = UI_Active(hotkey_box); - f32 hotkey_hovered = UI_Hovered(hotkey_box); + f32 hotkey_hovered = UI_HotAbsolute(hotkey_box); hotkey_color = LerpSrgb(hotkey_color, hovered_color, hotkey_hot); hotkey_color = LerpSrgb(hotkey_color, pressed_color, hotkey_active * hotkey_hovered); hotkey_border_color = LerpSrgb(hotkey_border_color, Rgb32(0x0078a6), hotkey_hot); @@ -5546,6 +5552,8 @@ void V_TickForever(WaveLaneCtx *lane) + + // if (profiler->is_dragging && !prev_frame->profiler.is_dragging) // { // profiler->drag_view_ns = profiler->view_ns; @@ -5637,11 +5645,12 @@ void V_TickForever(WaveLaneCtx *lane) //- Main if (do_break) { - UI_SetNext(DebugBreakFlags, UI_DebugBreakFlag_BuildReport); + UI_SetNext(DebugBreakFlags, UI_DebugBreakFlag_BuildFeedback); UI_SetNext(Text, Lit("MAIN")); } UI_SetNext(Height, main_height); UI_SetNext(Flags, UI_BoxFlag_CaptureMouse | UI_BoxFlag_CaptureThroughChildren); + // UI_SetNext(Flags, UI_BoxFlag_CaptureMouse); UI_PushDF(Parent, UI_BuildColumnEx(main_box)) { @@ -5803,6 +5812,7 @@ void V_TickForever(WaveLaneCtx *lane) UI_Key zone_box = UI_KeyF("Zone %F", FmtUint(zone_idx)); Vec4 zone_color = zone->color; + zone_color = LerpSrgb(zone_color, Color_Cyan, UI_Hot(zone_box)); // Vec4 zone_color_bd = Zi; // zone_color_bd @@ -5815,11 +5825,11 @@ void V_TickForever(WaveLaneCtx *lane) Vec2 zone_pos = VEC2(zone_offset_px, 0); UI_Size zone_width = UI_PIX(zone_len_px, 1); - - if (do_break && UI_TargetHovered(zone_box)) + UI_SetNext(Text, StringF(frame->arena, "ZONE %F", FmtUint(zone_idx))); + // if (do_break && UI_IsMouseHovered(zone_box)) + if (do_break && zone_idx == 0) { - UI_SetNext(DebugBreakFlags, UI_DebugBreakFlag_BuildReport); - UI_SetNext(Text, StringF(frame->arena, "ZONE %F", FmtUint(zone_idx))); + UI_SetNext(DebugBreakFlags, UI_DebugBreakFlag_BuildFeedback | UI_DebugBreakFlag_CheckCursorHover); } @@ -5829,6 +5839,7 @@ void V_TickForever(WaveLaneCtx *lane) UI_SetNext(BackgroundColor, zone_color); UI_SetNext(FloatingPos, zone_pos); UI_SetNext(Flags, UI_BoxFlag_Floating | UI_BoxFlag_NoFloatingClampX | UI_BoxFlag_NoFloatingClampY | UI_BoxFlag_CaptureMouse); + // UI_SetNext(Flags, UI_BoxFlag_Floating | UI_BoxFlag_NoFloatingClampX | UI_BoxFlag_NoFloatingClampY); UI_PushDF(Parent, UI_BuildBoxEx(zone_box)) { } diff --git a/src/ui/ui_core.c b/src/ui/ui_core.c index 0e6613ad..ed4ac520 100644 --- a/src/ui/ui_core.c +++ b/src/ui/ui_core.c @@ -528,31 +528,9 @@ void UI_SetRawTexture(UI_Key key, G_TextureRef tex, Rng2 uv) 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; -} - //////////////////////////////////////////////////////////// //~ Begin frame - - - - - - - - UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags) { ////////////////////////////// @@ -620,315 +598,275 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags) frame->frame_flags = frame_flags; ////////////////////////////// - //- Build feedback data + //- Build feedback & input state frame->cursor_pos = prev_frame->cursor_pos; frame->drag_cursor_pos = prev_frame->drag_cursor_pos; + frame->input = prev_frame->input; + frame->drag_input = prev_frame->drag_input; + if (prev_frame->boxes_pre != 0) + { + ControllerEventsArray controller_events = frame->window_frame.controller_events; - // if (prev_frame->boxes_pre != 0) - // { - // ControllerEventsArray controller_events = frame->window_frame.controller_events; + //- Locate boxes + UI_Box *prev_top_active_box = UI_BoxFromKey(prev_frame->top_active_box); + UI_Box *top_active_box = prev_top_active_box; + UI_Box *top_hot_box = 0; + UI_Box *top_hovered_box = 0; - // //- 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); + } + } - // //- 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); - // } - // } + //- Locate hovered box + for (u64 pre_index = UI.boxes_count; pre_index-- > 0;) + { + UI_Box *box = prev_frame->boxes_pre[pre_index]; + UI_DebugBreak(box, UI_DebugBreakFlag_CheckCursorHover); + //- Reset state + { + box->mouse_hovered = 0; + box->mouse_captured = 0; + } + //- Test for hover + 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; + if (top_hovered_box == 0 && (box->desc.flags & UI_BoxFlag_CaptureMouse) && is_cursor_in_box) + { + top_hovered_box = box; + } + } + } - // //- 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; - // } - // report->is_hot = 0; - // } + //- Reset input state + for (u32 button_idx = 0; button_idx < countof(frame->input.buttons); ++button_idx) + { + UI_ButtonState *btn = &frame->input.buttons[button_idx]; + b32 old_held = btn->held; + { + ZeroStruct(btn); + } + btn->held = old_held; + } - // //- 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; + //- 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_ButtonDown: + { + if (IsClickButton(cev->button) || IsWheelButton(cev->button)) + { + mouse_downs += 1; + if (top_hovered_box && top_active_box == 0) + { + if (cev->button == Button_M1) + { + ++frame->input.buttons[Button_M1].downs; + frame->input.buttons[Button_M1].held = 1; + top_active_box = top_hovered_box; + } + else if (cev->button == Button_M2) + { + ++frame->input.buttons[Button_M2].downs; + frame->input.buttons[Button_M2].held = 1; + top_active_box = top_hovered_box; + } + else if (cev->button == Button_M3) + { + ++frame->input.buttons[Button_M3].downs; + frame->input.buttons[Button_M3].held = 1; + top_active_box = top_hovered_box; + } + else if (cev->button == Button_WheelUp) + { + ++frame->input.buttons[Button_WheelUp].downs; + ++frame->input.buttons[Button_WheelUp].ups; + ++frame->input.buttons[Button_WheelUp].presses; + } + else if (cev->button == Button_WheelDown) + { + ++frame->input.buttons[Button_WheelDown].downs; + ++frame->input.buttons[Button_WheelDown].ups; + ++frame->input.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_ButtonUp: + { + if (IsClickButton(cev->button)) + { + if (top_active_box) + { + if (cev->button == Button_M1) + { + if (top_active_box == top_hovered_box) + { + ++frame->input.buttons[Button_M1].presses; + } + ++frame->input.buttons[Button_M1].ups; + if (frame->input.buttons[Button_M1].held) + { + frame->input.buttons[Button_M1].held = 0; + top_active_box = 0; + } + } + else if (cev->button == Button_M2) + { + if (top_active_box == top_hovered_box) + { + ++frame->input.buttons[Button_M2].presses; + } + ++frame->input.buttons[Button_M2].ups; + if (frame->input.buttons[Button_M2].held) + { + frame->input.buttons[Button_M2].held = 0; + top_active_box = 0; + } + } + else if (cev->button == Button_M3) + { + if (top_active_box == top_hovered_box) + { + ++frame->input.buttons[Button_M3].presses; + } + ++frame->input.buttons[Button_M3].ups; + if (frame->input.buttons[Button_M3].held) + { + frame->input.buttons[Button_M3].held = 0; + top_active_box = 0; + } + } + } + } + else if (IsWheelButton(cev->button) && top_hovered_box) + { + cev->captures += 1; + } + } break; - // case ControllerEventKind_Quit: - // { - // SignalExit(0); - // } break; - // } - // } + case ControllerEventKind_Quit: + { + SignalExit(0); + } break; + } + } - // UI_Box *hot_box = active_box; - // if (top_hovered_box && !active_box) - // { - // hot_box = top_hovered_box; - // } + //- Update mouse capture tree + if (top_hovered_box) + { + top_hovered_box->mouse_hovered = 1; + for (UI_Box *parent = top_hovered_box->parent; parent; parent = parent->parent) + { + if (parent->desc.flags & UI_BoxFlag_CaptureThroughChildren) + { + parent->mouse_hovered = 1; + } + } + } + if (top_active_box) + { + top_active_box->mouse_hovered = 1; + top_active_box->mouse_captured = 1; + for (UI_Box *parent = top_active_box->parent; parent; parent = parent->parent) + { + if (parent->desc.flags & UI_BoxFlag_CaptureThroughChildren) + { + parent->mouse_hovered = 1; + parent->mouse_captured = 1; + } + } + } + top_hot_box = top_active_box ? top_active_box : top_hovered_box; - // if (hot_box) - // { - // // hot_box->reports.tree_capture = hot_box->key; - // } + //- Update box feedback + { + 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_Feedback *feedback = &box->feedback; + UI_DebugBreak(box, UI_DebugBreakFlag_BuildFeedback); + feedback->active_absolute = box->mouse_captured; + feedback->hot_absolute = feedback->active_absolute || (top_active_box == 0 && box->mouse_hovered); + feedback->exists_absolute = (box->last_build_tick >= (frame->tick - 1)); + f32 target_active = feedback->active_absolute ? Inf : lower_target; + f32 target_hot = feedback->hot_absolute ? Inf : lower_target; + f32 target_exists = feedback->exists_absolute ? upper_target : lower_target; + f32 target_misc = box->desc.misc; + // TODO: Configurable per-box blend rates + f32 active_blend_rate = (15 * frame->dt); + f32 hot_blend_rate = (15 * frame->dt); + f32 exists_blend_rate = (30 * frame->dt); + // f64 misc_blend_rate = (30 * frame->dt); + f64 misc_blend_rate = 1; + feedback->active_smooth = SaturateF32(LerpF32(feedback->active_smooth, target_active, active_blend_rate)); + feedback->hot_smooth = SaturateF32(LerpF32(feedback->hot_smooth, target_hot, hot_blend_rate)); + feedback->exists_smooth = SaturateF32(LerpF32(feedback->exists_smooth, target_exists, exists_blend_rate)); + feedback->misc_smooth = SaturateF32(LerpF32(feedback->misc_smooth, target_misc, misc_blend_rate)); + feedback->screen_rect = box->screen_rect; + feedback->screen_anchor = box->screen_anchor; - // //- Update reports from signals - // for (UI_CmdNode *cmd_node = frame->first_cmd_node; cmd_node; cmd_node = cmd_node->next) - // { - // UI_Cmd cmd = cmd_node->cmd; - // 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 (mouse_downs > 0) + { + box->drag_feedback = *feedback; + } + } + } + if (mouse_downs > 0) + { + frame->drag_cursor_pos = frame->cursor_pos; + } - - - - - - - // //- Update box reports from inputs - // // { - // // 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; - // } + frame->top_active_box = top_active_box ? top_active_box->key : UI_NilKey; + frame->top_hot_box = top_hot_box ? top_hot_box->key : UI_NilKey; + frame->top_hovered_box = top_hovered_box ? top_hovered_box->key : UI_NilKey; + } ////////////////////////////// //- Build root box @@ -943,392 +881,6 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags) 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 @@ -1350,97 +902,144 @@ Arena *UI_FrameArena(void) //////////////////////////////////////////////////////////// //~ Feedback +b32 UI_IsButtonPassive(Button button) +{ + return !IsMouseButton(button) || IsWheelButton(button); +} + //- Input +b32 UI_Held(UI_Key key, Button button) +{ + i32 result = Zi; + UI_Box *box = UI_BoxFromKey(key); + if (box && (box->feedback.active_absolute || (box->feedback.hot_absolute && UI_IsButtonPassive(button)))) + { + return UI_CurrentFrame()->input.buttons[button].held; + } + return result; +} + i32 UI_Downs(UI_Key key, Button button) { i32 result = Zi; + UI_Box *box = UI_BoxFromKey(key); + if (box && (box->feedback.active_absolute || (box->feedback.hot_absolute && UI_IsButtonPassive(button)))) + { + return UI_CurrentFrame()->input.buttons[button].downs; + } return result; } -b32 UI_Held(UI_Key key, Button button) +i32 UI_Ups(UI_Key key, Button button) { - b32 result = Zi; + i32 result = Zi; + UI_Box *box = UI_BoxFromKey(key); + if (box && (box->feedback.active_absolute || (box->feedback.hot_absolute && UI_IsButtonPassive(button)))) + { + return UI_CurrentFrame()->input.buttons[button].ups; + } return result; } -b32 UI_Ups(UI_Key key, Button button) +i32 UI_Presses(UI_Key key, Button button) { - b32 result = Zi; - return result; -} - -b32 UI_Presses(UI_Key key, Button button) -{ - b32 result = Zi; + i32 result = Zi; + UI_Box *box = UI_BoxFromKey(key); + if (box && (box->feedback.active_absolute || (box->feedback.hot_absolute && UI_IsButtonPassive(button)))) + { + return UI_CurrentFrame()->input.buttons[button].presses; + } return result; } //- Interaction -f32 UI_Exists(UI_Key key) -{ - f32 result = Zi; - return result; -} - -f32 UI_TargetExists(UI_Key key) -{ - f32 result = Zi; - return result; -} - -f32 UI_Hovered(UI_Key key) -{ - f32 result = Zi; - return result; -} - -f32 UI_TargetHovered(UI_Key key) -{ - f32 result = Zi; - return result; -} - f32 UI_Active(UI_Key key) { f32 result = Zi; + UI_Box *box = UI_BoxFromKey(key); + if (box) + { + result = box->feedback.active_smooth; + } return result; } -f32 UI_TargetActive(UI_Key key) +b32 UI_ActiveAbsolute(UI_Key key) { f32 result = Zi; + UI_Box *box = UI_BoxFromKey(key); + if (box) + { + result = box->feedback.active_absolute; + } return result; } f32 UI_Hot(UI_Key key) { f32 result = Zi; + UI_Box *box = UI_BoxFromKey(key); + if (box) + { + result = box->feedback.hot_smooth; + } return result; } -f32 UI_TargetHot(UI_Key key) +b32 UI_HotAbsolute(UI_Key key) { f32 result = Zi; + UI_Box *box = UI_BoxFromKey(key); + if (box) + { + result = box->feedback.hot_absolute; + } return result; } -f32 UI_Misc(UI_Key key) +f32 UI_Exists(UI_Key key) { f32 result = Zi; + UI_Box *box = UI_BoxFromKey(key); + if (box) + { + result = box->feedback.exists_smooth; + } return result; } -f32 UI_DragMisc(UI_Key key) +b32 UI_ExistsAbsolute(UI_Key key) { f32 result = Zi; + UI_Box *box = UI_BoxFromKey(key); + if (box) + { + result = box->feedback.exists_absolute; + } return result; } -f32 UI_TargetMisc(UI_Key key) +f64 UI_Misc(UI_Key key) { - f32 result = Zi; + f64 result = Zi; + UI_Box *box = UI_BoxFromKey(key); + if (box) + { + result = box->feedback.misc_smooth; + } + return result; +} + +f64 UI_DragMisc(UI_Key key) +{ + f64 result = Zi; + UI_Box *box = UI_BoxFromKey(key); + if (box) + { + result = box->drag_feedback.misc_smooth; + } return result; } @@ -1449,37 +1048,55 @@ f32 UI_TargetMisc(UI_Key key) Vec2 UI_Anchor(UI_Key key) { Vec2 result = Zi; + UI_Box *box = UI_BoxFromKey(key); + if (box) + { + result = box->feedback.screen_anchor; + } return result; } Vec2 UI_DragAnchor(UI_Key key) { Vec2 result = Zi; + UI_Box *box = UI_BoxFromKey(key); + if (box) + { + result = box->drag_feedback.screen_anchor; + } return result; } Rng2 UI_Rect(UI_Key key) { Rng2 result = Zi; + UI_Box *box = UI_BoxFromKey(key); + if (box) + { + result = box->feedback.screen_rect; + } return result; } Rng2 UI_DragRect(UI_Key key) { Rng2 result = Zi; + UI_Box *box = UI_BoxFromKey(key); + if (box) + { + result = box->drag_feedback.screen_rect; + } return result; } Vec2 UI_CursorPos(void) { - Vec2 result = Zi; - return result; + return UI_CurrentFrame()->cursor_pos; } Vec2 UI_DragCursorPos(void) { - Vec2 result = Zi; - return result; + return UI_CurrentFrame()->drag_cursor_pos; } //////////////////////////////////////////////////////////// diff --git a/src/ui/ui_core.h b/src/ui/ui_core.h index 7de9eb2c..79888e9a 100644 --- a/src/ui/ui_core.h +++ b/src/ui/ui_core.h @@ -102,15 +102,16 @@ Enum(UI_BoxFlag) Enum(UI_DebugBreakFlag) { UI_DebugBreakFlag_None = 0, - UI_DebugBreakFlag_BuildReport = (1 << 0), - UI_DebugBreakFlag_PrepLayout = (1 << 1), - UI_DebugBreakFlag_IndependentSolve = (1 << 2), - UI_DebugBreakFlag_UpwardsDependentSolveLayoutAxis = (1 << 3), - UI_DebugBreakFlag_DownwardsDependentSolve = (1 << 4), - UI_DebugBreakFlag_UpwardsDependentSolveNonLayoutAxis = (1 << 5), - UI_DebugBreakFlag_SolveViolations = (1 << 6), - UI_DebugBreakFlag_FinalSolve = (1 << 7), - UI_DebugBreakFlag_BuildGpuData = (1 << 8), + UI_DebugBreakFlag_CheckCursorHover = (1 << 0), + UI_DebugBreakFlag_BuildFeedback = (1 << 1), + UI_DebugBreakFlag_PrepLayout = (1 << 2), + UI_DebugBreakFlag_IndependentSolve = (1 << 3), + UI_DebugBreakFlag_UpwardsDependentSolveLayoutAxis = (1 << 4), + UI_DebugBreakFlag_DownwardsDependentSolve = (1 << 5), + UI_DebugBreakFlag_UpwardsDependentSolveNonLayoutAxis = (1 << 6), + UI_DebugBreakFlag_SolveViolations = (1 << 7), + UI_DebugBreakFlag_FinalSolve = (1 << 8), + UI_DebugBreakFlag_BuildGpuData = (1 << 9), UI_DebugBreakFlag_All = 0xFFFFFFFF }; @@ -225,28 +226,23 @@ Struct(UI_InputState) }; //////////////////////////////////////////////////////////// -//~ Report types +//~ Feedback types -// Struct(UI_BoxReport) -// { -// Rng2 screen_rect; -// Vec2 screen_anchor; +Struct(UI_Feedback) +{ + Rng2 screen_rect; + Vec2 screen_anchor; -// b32 is_hovered; -// b32 is_hot; + b32 active_absolute; + b32 hot_absolute; + b32 exists_absolute; -// f32 exists; -// f32 hovered; -// f32 hot; -// f32 active; -// f64 misc; -// }; + f32 active_smooth; + f32 hot_smooth; + f32 exists_smooth; -// Struct(UI_BoxReports) -// { -// UI_BoxReport draw; // Box data used for last render -// UI_BoxReport drag; // Box data during last mouse button down event -// }; + f64 misc_smooth; +}; //////////////////////////////////////////////////////////// //~ Command types @@ -255,7 +251,6 @@ Enum(UI_CmdKind) { UI_CmdKind_None, UI_CmdKind_BuildBox, - UI_CmdKind_Signal, UI_CmdKind_SetRawTexture, }; @@ -300,12 +295,6 @@ Struct(UI_Cmd) { UI_BoxDesc box; struct - { - UI_Key key; - Button button; - UI_ButtonState button_state; - } signal; - struct { UI_Key key; G_TextureRef tex; @@ -368,6 +357,12 @@ Struct(UI_Box) f32 rounding_tr; f32 rounding_br; f32 rounding_bl; + + //- Feedback + b32 mouse_hovered; + b32 mouse_captured; + UI_Feedback feedback; + UI_Feedback drag_feedback; }; Struct(UI_BoxBin) @@ -424,12 +419,12 @@ Struct(UI_Frame) // Input Vec2 cursor_pos; Vec2 drag_cursor_pos; - UI_InputState input_state; - UI_InputState drag_input_state; + UI_InputState input; + UI_InputState drag_input; UI_Key top_hovered_box; - UI_Key hot_box; - UI_Key active_box; + UI_Key top_hot_box; + UI_Key top_active_box; // Cmds UI_FrameFlag frame_flags; @@ -554,7 +549,6 @@ UI_Key UI_BuildBoxEx(UI_Key semantic_key); #define UI_BuildBox() UI_BuildBoxEx(UI_NilKey) void UI_SetRawTexture(UI_Key key, G_TextureRef tex, Rng2 uv); -UI_ButtonState *UI_SignalButton(UI_Key key, Button button); #if IsRtcEnabled #define UI_DebugBreak(box, target_flags) do { if (box->desc.debug_break_flags & target_flags) { DEBUGBREAK; } } while (0) @@ -562,7 +556,6 @@ UI_ButtonState *UI_SignalButton(UI_Key key, Button button); #define UI_DebugBreak(...) #endif - //////////////////////////////////////////////////////////// //~ Begin frame @@ -578,30 +571,30 @@ Arena *UI_FrameArena(void); //////////////////////////////////////////////////////////// //~ Feedback +//- Passive + +b32 UI_IsButtonPassive(Button button); + //- Input -i32 UI_Downs(UI_Key key, Button button); b32 UI_Held(UI_Key key, Button button); -b32 UI_Ups(UI_Key key, Button button); -b32 UI_Presses(UI_Key key, Button button); +i32 UI_Downs(UI_Key key, Button button); +i32 UI_Ups(UI_Key key, Button button); +i32 UI_Presses(UI_Key key, Button button); //- Interaction -f32 UI_Exists(UI_Key key); -f32 UI_TargetExists(UI_Key key); - -f32 UI_Hovered(UI_Key key); -f32 UI_TargetHovered(UI_Key key); - f32 UI_Active(UI_Key key); -f32 UI_TargetActive(UI_Key key); +b32 UI_ActiveAbsolute(UI_Key key); f32 UI_Hot(UI_Key key); -f32 UI_TargetHot(UI_Key key); +b32 UI_HotAbsolute(UI_Key key); -f32 UI_Misc(UI_Key key); -f32 UI_DragMisc(UI_Key key); -f32 UI_TargetMisc(UI_Key key); +f32 UI_Exists(UI_Key key); +b32 UI_ExistsAbsolute(UI_Key); + +f64 UI_Misc(UI_Key key); +f64 UI_DragMisc(UI_Key key); //- Layout