From 5c2ff7ca9fbaf1415976c2fc0d528003c29f572b Mon Sep 17 00:00:00 2001 From: jacob Date: Sun, 29 Mar 2026 20:33:41 -0500 Subject: [PATCH] UI scissoring --- src/pp/pp_vis/pp_vis_core.c | 925 ++++++++++++++++++++++-------------- src/pp/pp_vis/pp_vis_core.h | 2 + src/ui/ui_core.c | 67 ++- src/ui/ui_core.h | 5 +- src/ui/ui_gpu.g | 16 +- src/ui/ui_shared.cgh | 1 + 6 files changed, 624 insertions(+), 392 deletions(-) diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index 755094f6..b5c796eb 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -2519,7 +2519,7 @@ void V_TickForever(WaveLaneCtx *lane) frame->shade_cursor = MulAffineVec2(frame->af.screen_to_shade, frame->screen_cursor); frame->world_cursor = MulAffineVec2(frame->af.screen_to_world, frame->screen_cursor); - b32 show_editor_ui = TweakBool("Show editor UI", 1); + b32 show_editor_ui = TweakBool("Show editor UI", 0); frame->world_selection_start = frame->world_cursor; if (frame->is_editing) @@ -4370,13 +4370,29 @@ void V_TickForever(WaveLaneCtx *lane) V_Palette *palette = &frame->palette; { + UI_Push(Tag, HashF("developer command palette")); + UI_Size total_width = UI_FNT(40, 1); UI_Size total_height = UI_FNT(40, 1); - UI_Size header_height = UI_FNT(1.25, 1); + UI_Size header_height = UI_FNT(1.3, 1); UI_Size window_padding = UI_FNT(0.5, 1); UI_Size col0_width = UI_FNT(1.75, 1); UI_Size col1_width = UI_FNT(30, 1); UI_Size col2_width = UI_FNT(10, 1); + UI_Size scrollbar_width = UI_FNT(1, 1); + + UI_Key lister_key = UI_KeyF("lister"); + UI_Key scrollbar_key = UI_KeyF("scrollbar"); + UI_Key thumb_key = UI_KeyF("scrollbar-thumb"); + b32 scrollbar_visible = 1; + + UI_BoxReports scrollbar_reps = UI_ReportsFromKey(scrollbar_key); + UI_BoxReports thumb_reps = UI_ReportsFromKey(thumb_key); + + + f32 lister_offset = palette->scroll; + + { f32 ease_rate = TweakFloat("Debug palette ease rate", 20, 1, 100) * frame->dt; @@ -4428,7 +4444,7 @@ void V_TickForever(WaveLaneCtx *lane) UI_PushCP(UI_BuildBoxEx(palette->key)); { ////////////////////////////// - //- Build header + //- Build title bar UI_PushCP(UI_NilKey); { @@ -4464,6 +4480,7 @@ void V_TickForever(WaveLaneCtx *lane) } UI_PopCP(UI_TopCP()); + //- Window box UI_SetNext(BackgroundColor, 0); UI_SetNext(Rounding, 0); UI_SetNext(Width, UI_GROW(1, 0)); @@ -4698,9 +4715,6 @@ void V_TickForever(WaveLaneCtx *lane) UI_BuildBoxEx(caret_box); } } - - - } UI_PopCP(UI_TopCP()); } @@ -4708,423 +4722,522 @@ void V_TickForever(WaveLaneCtx *lane) UI_PopCP(UI_TopCP()); } + UI_BuildDivider(UI_PIX(1, 1), divider_color, Axis_Y); + ////////////////////////////// //- Build palette items list - UI_SetNext(Tint, 0); + // Scissor box + UI_SetNext(BackgroundColor, 0); UI_SetNext(Rounding, 0); + UI_SetNext(Width, UI_GROW(1, 0)); + UI_SetNext(Height, UI_GROW(1, 0)); + UI_SetNext(Flags, UI_BoxFlag_Scissor); UI_PushCP(UI_BuildRow()); { + // Items & Lister group UI_SetNext(Tint, 0); UI_SetNext(Rounding, 0); - UI_SetNext(Width, UI_GROW(1, 0)); - UI_PushCP(UI_BuildColumn()); + UI_PushCP(UI_BuildRow()); { - Enum(PaletteItemFlag) + // Items box + UI_SetNext(BackgroundColor, 0); + UI_SetNext(Rounding, 0); + UI_SetNext(Width, UI_GROW(1, 0)); + UI_SetNext(Height, UI_GROW(1, 0)); + UI_SetNext(FloatingPos, VEC2(0, -lister_offset)); + UI_SetNext(Flags, UI_BoxFlag_Floating | UI_BoxFlag_NoFloatingClampY); + UI_PushCP(UI_BuildRowEx(lister_key)); { - PaletteItemFlag_None = 0, - PaletteItemFlag_IsCmd = (1 << 0), - PaletteItemFlag_IsTweakVar = (2 << 0), - }; - - Struct(PaletteItem) - { - PaletteItem *next; - PaletteItem *prev; - - UI_Key key; - PaletteItemFlag flags; - - V_Hotkey hotkeys[8]; - String display_name; - - V_CmdDesc cmd_desc; - TweakVar tweak_var; - }; - PaletteItem *first_item = 0; - PaletteItem *last_item = 0; - - ////////////////////////////// - //- Push command items - - { - for (u64 cmd_desc_idx = 0; cmd_desc_idx < countof(V_cmd_descs); ++cmd_desc_idx) - { - V_CmdDesc cmd_desc = V_cmd_descs[cmd_desc_idx]; - if (!(cmd_desc.flags & V_CmdDescFlag_HideFromPalette)) - { - PaletteItem *item = PushStruct(frame->arena, PaletteItem); - { - item->key = UI_KeyF("cmd palette item %F", FmtString(cmd_desc.name)); - item->display_name = cmd_desc.display_name; - item->flags |= PaletteItemFlag_IsCmd; - item->cmd_desc = cmd_desc; - // FIXME: Attach active shortcuts instead of default hotkeys - CopyStructs(item->hotkeys, cmd_desc.default_hotkeys, MinU32(countof(item->hotkeys), countof(cmd_desc.default_hotkeys))); - } - DllQueuePush(first_item, last_item, item); - } - } - } - - ////////////////////////////// - //- Push tweak variables - - TweakVarArray tweak_vars = GetAllTweakVars(frame->arena); - - { - for (i64 tweak_idx = 0; tweak_idx < tweak_vars.count; ++tweak_idx) - { - TweakVar tweak_var = tweak_vars.v[tweak_idx]; - PaletteItem *item = PushStruct(frame->arena, PaletteItem); - { - item->key = UI_KeyF("tweak var palette item %F", FmtString(tweak_var.name)); - item->display_name = tweak_var.name; - item->flags |= PaletteItemFlag_IsTweakVar; - item->tweak_var = tweak_var; - } - DllQueuePush(first_item, last_item, item); - } - } - - ////////////////////////////// - //- Build items - - for (PaletteItem *item = first_item; item; item = item->next) - { - // Divider - UI_BuildDivider(UI_PIX(1, 1), divider_color, Axis_Y); - - UI_BoxReport item_rep = UI_ReportsFromKey(item->key).draw; - if (item_rep.m1.presses) - { - if (item->flags & PaletteItemFlag_IsCmd) - { - String cmd_name = item->cmd_desc.name; - V_PushVisCmd(cmd_name); - } - } - - // Vec4 item_color = theme.col.window_bg; - Vec4 item_color = Zi; - // Vec4 item_color = theme.col.hint; - Vec4 item_border_color = Zi; - if (item->flags & PaletteItemFlag_IsCmd) - { - item_color = LerpSrgb(item_color, theme.col.button_hot, item_rep.hot); - item_color = LerpSrgb(item_color, theme.col.button_active, item_rep.active); - item_border_color = LerpSrgb(item_border_color, theme.col.button_active, item_rep.hot); - } - else - { - item_border_color = LerpSrgb(item_border_color, theme.col.button_active, item_rep.hot); - } - UI_SetNext(Tint, 0); - // UI_SetNext(Width, UI_PIX(total_width.v - window_padding.v * 2, 1)); + UI_SetNext(Rounding, 0); UI_SetNext(Width, UI_GROW(1, 0)); - UI_SetNext(Height, UI_FNT(1.5, 1)); - UI_PushCP(UI_BuildRow()); + UI_PushCP(UI_BuildColumn()); { - UI_SetNext(BackgroundColor, item_color); - UI_SetNext(BorderColor, item_border_color); - UI_SetNext(BorderSize, 1); - UI_SetNext(Rounding, UI_RPIX(5)); - // UI_SetNext(Width, col1_width); - UI_SetNext(Width, UI_GROW(1, 0)); - // UI_SetNext(Height, UI_PIX(item_size_px, 1)); - // UI_SetNext(Height, UI_FNT(1, 0)); - UI_SetNext(ChildAlignment, UI_Region_Left); - UI_SetNext(Flags, UI_BoxFlag_DrawText | UI_BoxFlag_CaptureMouse); - UI_PushCP(UI_BuildRowEx(item->key)); + Enum(PaletteItemFlag) { - UI_Push(Tag, item->key.v); + PaletteItemFlag_None = 0, + PaletteItemFlag_IsCmd = (1 << 0), + PaletteItemFlag_IsTweakVar = (2 << 0), + }; - // Begin spacer - UI_BuildSpacer(col0_width, Axis_X); - // UI_BuildSpacer(window_padding, Axis_X); + Struct(PaletteItem) + { + PaletteItem *next; + PaletteItem *prev; - // Command label - UI_SetNext(ChildAlignment, UI_Region_Left); - UI_BuildLabel(item->display_name); + UI_Key key; + PaletteItemFlag flags; - // Middle spacer - UI_BuildSpacer(UI_GROW(1, 0), Axis_X); + V_Hotkey hotkeys[8]; + String display_name; - // Tweak - if (item->flags & PaletteItemFlag_IsTweakVar) + V_CmdDesc cmd_desc; + TweakVar tweak_var; + }; + PaletteItem *first_item = 0; + PaletteItem *last_item = 0; + + ////////////////////////////// + //- Push command items + + { + for (u64 cmd_desc_idx = 0; cmd_desc_idx < countof(V_cmd_descs); ++cmd_desc_idx) { - TweakVar tweak_var = item->tweak_var; - String old_tweak_str = tweak_var.value; - String new_tweak_str = tweak_var.value; - b32 is_default = MatchString(new_tweak_str, tweak_var.initial); - - // Reset button - if (!is_default) + V_CmdDesc cmd_desc = V_cmd_descs[cmd_desc_idx]; + if (!(cmd_desc.flags & V_CmdDescFlag_HideFromPalette)) { - // UI_BuildSpacer(UI_PIX(spacing * 0.5, 1), Axis_X); - UI_Key reset_key = UI_KeyF("reset"); - UI_BoxReport reset_rep = UI_ReportsFromKey(reset_key).draw; - - if (reset_rep.m1.downs > 0) + PaletteItem *item = PushStruct(frame->arena, PaletteItem); { - new_tweak_str = tweak_var.initial; + item->key = UI_KeyF("cmd palette item %F", FmtString(cmd_desc.name)); + item->display_name = cmd_desc.display_name; + item->flags |= PaletteItemFlag_IsCmd; + item->cmd_desc = cmd_desc; + // FIXME: Attach active shortcuts instead of default hotkeys + CopyStructs(item->hotkeys, cmd_desc.default_hotkeys, MinU32(countof(item->hotkeys), countof(cmd_desc.default_hotkeys))); } - if (reset_rep.is_hot) - { - WND_SetCursor(window_frame, WND_CursorKind_Hand); - } - - Vec4 reset_bg = Zi; - // reset_bg = LerpSrgb(reset_bg, theme.col.button_hot, reset_rep.hot); - // reset_bg = LerpSrgb(reset_bg, theme.col.button_active, reset_rep.active); - - Vec4 reset_bd = Zi; - // reset_bd = LerpSrgb(reset_bd, theme.col.button_active, reset_rep.hot); - // reset_bd = LerpSrgb(reset_bd, theme.col.text, reset_rep.hot); - - Vec4 reset_text_col = theme.col.hint; - reset_text_col = LerpSrgb(reset_text_col, theme.col.text, reset_rep.hot); - - UI_SetNext(Rounding, UI_RGROW(0.75 * theme.rounding)); - UI_SetNext(ChildAlignment, UI_Region_Bottom); - UI_SetNext(BackgroundColor, reset_bg); - UI_SetNext(BorderColor, reset_bd); - UI_SetNext(BorderSize, 0); - UI_SetNext(TextColor, reset_text_col); - UI_SetNext(Width, UI_SHRINK(0, 1)); - UI_SetNext(Height, UI_SHRINK(0, 1)); - UI_SetNext(Flags, UI_BoxFlag_CaptureMouse); - UI_SetNext(FontSize, UI_Top(FontSize) * theme.h6); - UI_BuildIconEx(reset_key, theme.icon_font, UI_Icon_Loop2); + DllQueuePush(first_item, last_item, item); } + } + } - // Tweak label + ////////////////////////////// + //- Push tweak variables + + TweakVarArray tweak_vars = GetAllTweakVars(frame->arena); + + { + for (i64 tweak_idx = 0; tweak_idx < tweak_vars.count; ++tweak_idx) + { + TweakVar tweak_var = tweak_vars.v[tweak_idx]; + PaletteItem *item = PushStruct(frame->arena, PaletteItem); { + item->key = UI_KeyF("tweak var palette item %F", FmtString(tweak_var.name)); + item->display_name = tweak_var.name; + item->flags |= PaletteItemFlag_IsTweakVar; + item->tweak_var = tweak_var; + } + DllQueuePush(first_item, last_item, item); + } + } + + ////////////////////////////// + //- Build items + + for (PaletteItem *item = first_item; item; item = item->next) + { + // Divider + if (item != first_item) + { + UI_BuildDivider(UI_PIX(1, 1), divider_color, Axis_Y); + } + + UI_BoxReport item_rep = UI_ReportsFromKey(item->key).draw; + if (item_rep.m1.presses) + { + if (item->flags & PaletteItemFlag_IsCmd) + { + String cmd_name = item->cmd_desc.name; + V_PushVisCmd(cmd_name); + } + } + + // Vec4 item_color = theme.col.window_bg; + Vec4 item_color = Zi; + // Vec4 item_color = theme.col.hint; + Vec4 item_border_color = Zi; + if (item->flags & PaletteItemFlag_IsCmd) + { + item_color = LerpSrgb(item_color, theme.col.button_hot, item_rep.hot); + item_color = LerpSrgb(item_color, theme.col.button_active, item_rep.active); + item_border_color = LerpSrgb(item_border_color, theme.col.button_active, item_rep.hot); + } + else + { + item_border_color = LerpSrgb(item_border_color, theme.col.button_active, item_rep.hot); + } + + UI_SetNext(Tint, 0); + // UI_SetNext(Width, UI_PIX(total_width.v - window_padding.v * 2, 1)); + UI_SetNext(Width, UI_GROW(1, 0)); + UI_SetNext(Height, UI_FNT(1.5, 1)); + UI_PushCP(UI_BuildRow()); + { + UI_SetNext(BackgroundColor, item_color); + UI_SetNext(BorderColor, item_border_color); + UI_SetNext(BorderSize, 1); + UI_SetNext(Rounding, UI_RPIX(5)); + // UI_SetNext(Width, col1_width); + UI_SetNext(Width, UI_GROW(1, 0)); + // UI_SetNext(Height, UI_PIX(item_size_px, 1)); + // UI_SetNext(Height, UI_FNT(1, 0)); + UI_SetNext(ChildAlignment, UI_Region_Left); + UI_SetNext(Flags, UI_BoxFlag_DrawText | UI_BoxFlag_CaptureMouse); + UI_PushCP(UI_BuildRowEx(item->key)); + { + UI_Push(Tag, item->key.v); + + // Begin spacer UI_BuildSpacer(col0_width, Axis_X); - if (is_default) - { - UI_SetNext(TextColor, theme.col.hint); - } - else - { - UI_SetNext(TextColor, Color_White); - } - UI_SetNext(FontSize, UI_Top(FontSize) * theme.h5); + // UI_BuildSpacer(window_padding, Axis_X); + + // Command label UI_SetNext(ChildAlignment, UI_Region_Left); - UI_SetNext(Width, UI_SHRINK(0, 1)); - UI_SetNext(Height, UI_SHRINK(0, 1)); - UI_SetNext(Text, new_tweak_str); - UI_SetNext(Flags, UI_BoxFlag_DrawText | UI_BoxFlag_NoTextTruncation); - UI_SetNext(BackgroundColor, 0); - UI_SetNext(BorderColor, 0); - UI_BuildBox(); - } + UI_BuildLabel(item->display_name); - UI_BuildSpacer(col0_width, Axis_X); + // Middle spacer + UI_BuildSpacer(UI_GROW(1, 0), Axis_X); - switch (tweak_var.kind) - { - // Boolean tweak - case TweakKind_Bool: + // Tweak + if (item->flags & PaletteItemFlag_IsTweakVar) { - UI_Key cb_key = UI_KeyF("tweak checkbox"); - UI_BoxReport cb_rep = UI_ReportsFromKey(cb_key).draw; + TweakVar tweak_var = item->tweak_var; + String old_tweak_str = tweak_var.value; + String new_tweak_str = tweak_var.value; + b32 is_default = MatchString(new_tweak_str, tweak_var.initial); - b32 tweak_bool = CR_BoolFromString(new_tweak_str); - if (cb_rep.m1.downs) + // Reset button + if (!is_default) { - tweak_bool = !tweak_bool; - new_tweak_str = StringFromBool(frame->arena, tweak_bool); - } + // UI_BuildSpacer(UI_PIX(spacing * 0.5, 1), Axis_X); + UI_Key reset_key = UI_KeyF("reset"); + UI_BoxReport reset_rep = UI_ReportsFromKey(reset_key).draw; - // Tweak checkbox - Vec4 cb_bg_color = Zi; - Vec4 cb_border_color = theme.col.window_bd; - cb_bg_color = LerpSrgb(cb_bg_color, theme.col.positive, tweak_bool); - cb_border_color = LerpSrgb(cb_border_color, theme.col.button_active, cb_rep.hot); - - UI_SetNext(BackgroundColor, cb_bg_color); - UI_SetNext(BorderColor, cb_border_color); - UI_SetNext(Rounding, UI_RGROW(theme.rounding)); - UI_SetNext(BorderSize, 1); - UI_SetNext(Width, UI_FNT(1.25, 1)); - UI_SetNext(Height, UI_FNT(1.25, 1)); - UI_SetNext(Flags, UI_BoxFlag_CaptureMouse); - UI_PushCP(UI_BuildRowEx(cb_key)); - { - } - UI_PopCP(UI_TopCP()); - } break; - - // Float tweak - case TweakKind_Float: - { - UI_Key slider_key = UI_KeyF("tweak slider"); - UI_Key marker_key = UI_KeyF("tweak slider marker"); - - UI_BoxReports slider_reps = UI_ReportsFromKey(slider_key); - UI_BoxReports marker_reps = UI_ReportsFromKey(marker_key); - - b32 is_hot = slider_reps.draw.is_hot || marker_reps.draw.is_hot; - b32 is_active = slider_reps.draw.m1.held || marker_reps.draw.m1.held; - f32 hot = MaxF32(slider_reps.draw.hot, marker_reps.draw.hot); - - Vec2 slider_pos = slider_reps.draw.screen_rect.p0; - Vec2 slider_dims = DimsFromRng2(slider_reps.draw.screen_rect); - Vec2 marker_dims = DimsFromRng2(marker_reps.draw.screen_rect); - Vec2 half_marker_dims = MulVec2(marker_dims, 0.5); - - f64 range_min = tweak_var.range.min; - f64 range_max = tweak_var.range.max; - if (range_max <= range_min) - { - range_max = range_min + 1; - } - - f64 tweak_float = CR_FloatFromString(new_tweak_str); - { - if (is_active) + if (reset_rep.m1.downs > 0) { - f64 initial_slider_pos = slider_reps.drag.screen_rect.p0.x; - f64 initial_marker_width = DimsFromRng2(marker_reps.drag.screen_rect).x; - f64 initial_slider_width = DimsFromRng2(slider_reps.drag.screen_rect).x - initial_marker_width; - f64 initial_cursor = ui_frame->drag_cursor_pos.x; - f64 initial_ratio = slider_reps.drag.misc; + new_tweak_str = tweak_var.initial; + } + if (reset_rep.is_hot) + { + WND_SetCursor(window_frame, WND_CursorKind_Hand); + } - f64 virtual_slider_start = initial_cursor - (initial_slider_width * initial_ratio); - f64 virtual_slider_end = virtual_slider_start + initial_slider_width; - f64 virtual_cursor_ratio = (frame->screen_cursor.x - virtual_slider_start) / (virtual_slider_end - virtual_slider_start); + Vec4 reset_bg = Zi; + // reset_bg = LerpSrgb(reset_bg, theme.col.button_hot, reset_rep.hot); + // reset_bg = LerpSrgb(reset_bg, theme.col.button_active, reset_rep.active); - tweak_float = LerpF64(range_min, range_max, virtual_cursor_ratio); - tweak_float = ClampF64(tweak_float, range_min, range_max); - if (frame->screen_cursor.x != prev_frame->screen_cursor.x) + Vec4 reset_bd = Zi; + // reset_bd = LerpSrgb(reset_bd, theme.col.button_active, reset_rep.hot); + // reset_bd = LerpSrgb(reset_bd, theme.col.text, reset_rep.hot); + + Vec4 reset_text_col = theme.col.hint; + reset_text_col = LerpSrgb(reset_text_col, theme.col.text, reset_rep.hot); + + UI_SetNext(Rounding, UI_RGROW(0.75 * theme.rounding)); + UI_SetNext(ChildAlignment, UI_Region_Bottom); + UI_SetNext(BackgroundColor, reset_bg); + UI_SetNext(BorderColor, reset_bd); + UI_SetNext(BorderSize, 0); + UI_SetNext(TextColor, reset_text_col); + UI_SetNext(Width, UI_SHRINK(0, 1)); + UI_SetNext(Height, UI_SHRINK(0, 1)); + UI_SetNext(Flags, UI_BoxFlag_CaptureMouse); + UI_SetNext(FontSize, UI_Top(FontSize) * theme.h6); + UI_BuildIconEx(reset_key, theme.icon_font, UI_Icon_Loop2); + } + + // Tweak label + { + UI_BuildSpacer(col0_width, Axis_X); + if (is_default) + { + UI_SetNext(TextColor, theme.col.hint); + } + else + { + UI_SetNext(TextColor, Color_White); + } + UI_SetNext(FontSize, UI_Top(FontSize) * theme.h5); + UI_SetNext(ChildAlignment, UI_Region_Left); + UI_SetNext(Width, UI_SHRINK(0, 1)); + UI_SetNext(Height, UI_SHRINK(0, 1)); + UI_SetNext(Text, new_tweak_str); + UI_SetNext(Flags, UI_BoxFlag_DrawText | UI_BoxFlag_NoTextTruncation); + UI_SetNext(BackgroundColor, 0); + UI_SetNext(BorderColor, 0); + UI_BuildBox(); + } + + UI_BuildSpacer(col0_width, Axis_X); + + switch (tweak_var.kind) + { + // Boolean tweak + case TweakKind_Bool: + { + UI_Key cb_key = UI_KeyF("tweak checkbox"); + UI_BoxReport cb_rep = UI_ReportsFromKey(cb_key).draw; + + b32 tweak_bool = CR_BoolFromString(new_tweak_str); + if (cb_rep.m1.downs) { - new_tweak_str = StringFromFloat(frame->arena, tweak_float, tweak_var.precision); + tweak_bool = !tweak_bool; + new_tweak_str = StringFromBool(frame->arena, tweak_bool); } - } - if (is_hot) - { - WND_SetCursor(window_frame, WND_CursorKind_HorizontalResize); - } - } - f32 ratio = 0; - ratio = (tweak_float - range_min) / (range_max - range_min); - ratio = ClampF32(ratio, 0, 1); - Vec4 slider_bg_color = theme.col.window_bg; - Vec4 slider_border_color = theme.col.window_bd; - Vec4 slider_progress_color = theme.col.positive; - Vec4 marker_bg_color = slider_progress_color; - slider_border_color = LerpSrgb(slider_border_color, theme.col.button_active, hot); - marker_bg_color = LerpSrgb(marker_bg_color, theme.col.text, hot); + // Tweak checkbox + Vec4 cb_bg_color = Zi; + Vec4 cb_border_color = theme.col.window_bd; + cb_bg_color = LerpSrgb(cb_bg_color, theme.col.positive, tweak_bool); + cb_border_color = LerpSrgb(cb_border_color, theme.col.button_active, cb_rep.hot); - UI_SetNext(BackgroundColor, slider_bg_color); - UI_SetNext(BorderColor, slider_border_color); - UI_SetNext(Rounding, UI_RGROW(theme.rounding)); - UI_SetNext(BorderSize, 1); - UI_SetNext(Width, UI_FNT(10, 1)); - UI_SetNext(Height, UI_PIX(tweak_size_px * 0.75, 1)); - UI_SetNext(Flags, UI_BoxFlag_CaptureMouse); - UI_SetNext(Misc, ratio); - UI_PushCP(UI_BuildRowEx(slider_key)); - { - f32 marker_pos = ratio * (slider_dims.x - marker_dims.x); - - // Slider progress - { - UI_SetNext(BackgroundColor, slider_progress_color); - // UI_SetNext(Rounding, UI_RGROW(theme.rounding)); - UI_SetNext(Rounding, 0); - UI_SetNext(BorderColor, 0); + UI_SetNext(BackgroundColor, cb_bg_color); + UI_SetNext(BorderColor, cb_border_color); + UI_SetNext(Rounding, UI_RGROW(theme.rounding)); UI_SetNext(BorderSize, 1); - UI_SetNext(Width, UI_PIX(marker_pos + half_marker_dims.x, 0)); - UI_SetNext(Height, UI_PIX(tweak_size_px * 0.75, 1)); - UI_BuildBox(); - } + UI_SetNext(Width, UI_FNT(1.25, 1)); + UI_SetNext(Height, UI_FNT(1.25, 1)); + UI_SetNext(Flags, UI_BoxFlag_CaptureMouse); + UI_PushCP(UI_BuildRowEx(cb_key)); + { + } + UI_PopCP(UI_TopCP()); + } break; - // Slider marker + // Float tweak + case TweakKind_Float: { - UI_SetNext(BackgroundColor, marker_bg_color); + UI_Key slider_key = UI_KeyF("tweak slider"); + UI_Key marker_key = UI_KeyF("tweak slider marker"); + + UI_BoxReports slider_reps = UI_ReportsFromKey(slider_key); + UI_BoxReports marker_reps = UI_ReportsFromKey(marker_key); + + b32 is_hot = slider_reps.draw.is_hot || marker_reps.draw.is_hot; + b32 is_active = slider_reps.draw.m1.held || marker_reps.draw.m1.held; + f32 hot = MaxF32(slider_reps.draw.hot, marker_reps.draw.hot); + + Vec2 slider_pos = slider_reps.draw.screen_rect.p0; + Vec2 slider_dims = DimsFromRng2(slider_reps.draw.screen_rect); + Vec2 marker_dims = DimsFromRng2(marker_reps.draw.screen_rect); + Vec2 half_marker_dims = MulVec2(marker_dims, 0.5); + + f64 range_min = tweak_var.range.min; + f64 range_max = tweak_var.range.max; + if (range_max <= range_min) + { + range_max = range_min + 1; + } + + f64 tweak_float = CR_FloatFromString(new_tweak_str); + { + if (is_active) + { + f64 initial_slider_pos = slider_reps.drag.screen_rect.p0.x; + f64 initial_marker_width = DimsFromRng2(marker_reps.drag.screen_rect).x; + f64 initial_slider_width = DimsFromRng2(slider_reps.drag.screen_rect).x - initial_marker_width; + f64 initial_cursor = ui_frame->drag_cursor_pos.x; + f64 initial_ratio = slider_reps.drag.misc; + + f64 virtual_slider_start = initial_cursor - (initial_slider_width * initial_ratio); + f64 virtual_slider_end = virtual_slider_start + initial_slider_width; + f64 virtual_cursor_ratio = (frame->screen_cursor.x - virtual_slider_start) / (virtual_slider_end - virtual_slider_start); + + tweak_float = LerpF64(range_min, range_max, virtual_cursor_ratio); + tweak_float = ClampF64(tweak_float, range_min, range_max); + if (frame->screen_cursor.x != prev_frame->screen_cursor.x) + { + new_tweak_str = StringFromFloat(frame->arena, tweak_float, tweak_var.precision); + } + } + if (is_hot) + { + WND_SetCursor(window_frame, WND_CursorKind_HorizontalResize); + } + } + f32 ratio = 0; + ratio = (tweak_float - range_min) / (range_max - range_min); + ratio = ClampF32(ratio, 0, 1); + + Vec4 slider_bg_color = theme.col.window_bg; + Vec4 slider_border_color = theme.col.window_bd; + Vec4 slider_progress_color = theme.col.positive; + Vec4 marker_bg_color = slider_progress_color; + slider_border_color = LerpSrgb(slider_border_color, theme.col.button_active, hot); + marker_bg_color = LerpSrgb(marker_bg_color, theme.col.text, hot); + + UI_SetNext(BackgroundColor, slider_bg_color); UI_SetNext(BorderColor, slider_border_color); UI_SetNext(Rounding, UI_RGROW(theme.rounding)); UI_SetNext(BorderSize, 1); - UI_SetNext(Width, UI_PIX(tweak_size_px, 1)); - UI_SetNext(Height, UI_PIX(tweak_size_px, 1)); - // UI_SetNext(Anchor, UI_Region_Center); - // UI_SetNext(FloatingPos, VEC2(marker_pos, (marker_size_px * 0.5))); - UI_SetNext(FloatingPos, VEC2(marker_pos, -marker_dims.y * 0.125)); - UI_SetNext(Flags, UI_BoxFlag_Floating | UI_BoxFlag_NoFloatingClamp | UI_BoxFlag_CaptureMouse); - UI_BuildBoxEx(marker_key); - } + UI_SetNext(Width, UI_FNT(10, 1)); + UI_SetNext(Height, UI_PIX(tweak_size_px * 0.75, 1)); + UI_SetNext(Flags, UI_BoxFlag_CaptureMouse); + UI_SetNext(Misc, ratio); + UI_PushCP(UI_BuildRowEx(slider_key)); + { + f32 marker_pos = ratio * (slider_dims.x - marker_dims.x); + + // Slider progress + { + UI_SetNext(BackgroundColor, slider_progress_color); + // UI_SetNext(Rounding, UI_RGROW(theme.rounding)); + UI_SetNext(Rounding, 0); + UI_SetNext(BorderColor, 0); + UI_SetNext(BorderSize, 1); + UI_SetNext(Width, UI_PIX(marker_pos + half_marker_dims.x, 0)); + UI_SetNext(Height, UI_PIX(tweak_size_px * 0.75, 1)); + UI_BuildBox(); + } + + // Slider marker + { + UI_SetNext(BackgroundColor, marker_bg_color); + UI_SetNext(BorderColor, slider_border_color); + UI_SetNext(Rounding, UI_RGROW(theme.rounding)); + UI_SetNext(BorderSize, 1); + UI_SetNext(Width, UI_PIX(tweak_size_px, 1)); + UI_SetNext(Height, UI_PIX(tweak_size_px, 1)); + // UI_SetNext(Anchor, UI_Region_Center); + // UI_SetNext(FloatingPos, VEC2(marker_pos, (marker_size_px * 0.5))); + UI_SetNext(FloatingPos, VEC2(marker_pos, -marker_dims.y * 0.125)); + UI_SetNext(Flags, UI_BoxFlag_Floating | UI_BoxFlag_NoFloatingClampX | UI_BoxFlag_NoFloatingClampY | UI_BoxFlag_CaptureMouse); + UI_BuildBoxEx(marker_key); + } + } + UI_PopCP(UI_TopCP()); + } break; } - UI_PopCP(UI_TopCP()); - } break; - } - if (!MatchString(old_tweak_str, new_tweak_str)) - { - TweakVar new_tweak_var = tweak_var; - new_tweak_var.value = new_tweak_str; - TweakEx(frame->arena, new_tweak_var, 1); - } - } - - // Command hotkey buttons - for (u64 i = 0; i < countof(item->hotkeys); ++i) - { - UI_Key hotkey_key = UI_KeyF("hotkey%F", FmtUint(i)); - UI_BoxReport hotkey_rep = UI_ReportsFromKey(hotkey_key).draw; - - Vec4 hotkey_color = Zi; - Vec4 hotkey_border_color = Zi; - { - Vec4 hovered_color = Rgb32(0x103c4c); - Vec4 pressed_color = hovered_color; - pressed_color.w = 0.2; - f32 hotkey_hot = hotkey_rep.hot; - f32 hotkey_active = hotkey_rep.active; - f32 hotkey_hovered = hotkey_rep.hovered; - 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); - } - - V_Hotkey hotkey = item->hotkeys[i]; - if (hotkey.button == Button_None) - { - break; - } - else - { - UI_BuildSpacer(UI_PIX(10, 1), Axis_X); - - String hotkey_name = V_StringFromHotkey(UI_FrameArena(), hotkey); - UI_SetNext(BackgroundColor, hotkey_color); - UI_SetNext(BorderColor, hotkey_border_color); - UI_SetNext(Text, hotkey_name); - UI_SetNext(Width, UI_SHRINK(theme.text_padding_x, 1)); - UI_SetNext(Height, UI_GROW(1, 0)); - UI_SetNext(Rounding, UI_RPIX(5)); - UI_SetNext(BorderSize, 1); - UI_SetNext(ChildAlignment, UI_Region_Center); - UI_SetNext(Flags, UI_BoxFlag_DrawText | UI_BoxFlag_CaptureMouse); - UI_PushCP(UI_BuildRowEx(hotkey_key)); - { + if (!MatchString(old_tweak_str, new_tweak_str)) + { + TweakVar new_tweak_var = tweak_var; + new_tweak_var.value = new_tweak_str; + TweakEx(frame->arena, new_tweak_var, 1); + } } - UI_PopCP(UI_TopCP()); - } - } - // End spacer - UI_BuildSpacer(col0_width, Axis_X); + // Command hotkey buttons + for (u64 i = 0; i < countof(item->hotkeys); ++i) + { + UI_Key hotkey_key = UI_KeyF("hotkey%F", FmtUint(i)); + UI_BoxReport hotkey_rep = UI_ReportsFromKey(hotkey_key).draw; + + Vec4 hotkey_color = Zi; + Vec4 hotkey_border_color = Zi; + { + Vec4 hovered_color = Rgb32(0x103c4c); + Vec4 pressed_color = hovered_color; + pressed_color.w = 0.2; + f32 hotkey_hot = hotkey_rep.hot; + f32 hotkey_active = hotkey_rep.active; + f32 hotkey_hovered = hotkey_rep.hovered; + 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); + } + + V_Hotkey hotkey = item->hotkeys[i]; + if (hotkey.button == Button_None) + { + break; + } + else + { + UI_BuildSpacer(UI_PIX(10, 1), Axis_X); + + String hotkey_name = V_StringFromHotkey(UI_FrameArena(), hotkey); + UI_SetNext(BackgroundColor, hotkey_color); + UI_SetNext(BorderColor, hotkey_border_color); + UI_SetNext(Text, hotkey_name); + UI_SetNext(Width, UI_SHRINK(theme.text_padding_x, 1)); + UI_SetNext(Height, UI_GROW(1, 0)); + UI_SetNext(Rounding, UI_RPIX(5)); + UI_SetNext(BorderSize, 1); + UI_SetNext(ChildAlignment, UI_Region_Center); + UI_SetNext(Flags, UI_BoxFlag_DrawText | UI_BoxFlag_CaptureMouse); + UI_PushCP(UI_BuildRowEx(hotkey_key)); + { + } + UI_PopCP(UI_TopCP()); + } + } + + // End spacer + UI_BuildSpacer(col0_width, Axis_X); + } + UI_PopCP(UI_TopCP()); + } + UI_PopCP(UI_TopCP()); } - UI_PopCP(UI_TopCP()); } UI_PopCP(UI_TopCP()); } + UI_PopCP(UI_TopCP()); } UI_PopCP(UI_TopCP()); + + + + + + + + + + if (scrollbar_visible) + { + Vec2 scrollbar_pos = scrollbar_reps.draw.screen_rect.p0; + Vec2 scrollbar_dims = DimsFromRng2(scrollbar_reps.draw.screen_rect); + Vec2 thumb_dims = DimsFromRng2(thumb_reps.draw.screen_rect); + // Vec2 half_thumb_dims = MulVec2(thumb_dims, 0.5); + + + // f32 range_min = tweak_var.range.min; + // f32 range_max = tweak_var.range.max; + // if (range_max <= range_min) + // { + // range_max = range_min + 1; + // } + + + + + + + // f32 ratio = 0; + // ratio = (tweak_float - range_min) / (range_max - range_min); + // ratio = ClampF32(ratio, 0, 1); + + if (thumb_reps.draw.m1.downs) + { + palette->scroll_begin = palette->scroll; + } + if (thumb_reps.draw.m1.held) + { + palette->scroll = palette->scroll_begin + (frame->screen_cursor.y - ui_frame->drag_cursor_pos.y); + } + palette->scroll = MaxF32(palette->scroll, 0); + + // f32 thumb_offset = 50; + f32 thumb_offset = palette->scroll; + + + + + + + UI_SetNext(Width, scrollbar_width); + UI_PushCP(UI_BuildBoxEx(scrollbar_key)); + { + UI_Size thumb_height = UI_FNT(10, 1); + + Vec4 thumb_color = VEC4(0, 0.5, 1, 1); + thumb_color.a = thumb_reps.draw.hot * 0.5 + 0.5; + + UI_SetNext(BackgroundColor, thumb_color); + UI_SetNext(Width, UI_GROW(1, 0)); + UI_SetNext(Height, thumb_height); + UI_SetNext(Rounding, UI_RGROW(1 * theme.rounding)); + UI_SetNext(FloatingPos, VEC2(0, thumb_offset)); + // UI_SetNext(Anchor, UI_Region_Center); + UI_SetNext(Anchor, UI_Region_Top); + UI_SetNext(Flags, UI_BoxFlag_CaptureMouse | UI_BoxFlag_Floating); + UI_SetNext(Anchor, UI_Region_Top); + UI_BuildBoxEx(thumb_key); + } + UI_PopCP(UI_TopCP()); + } } UI_PopCP(UI_TopCP()); } @@ -5132,15 +5245,89 @@ void V_TickForever(WaveLaneCtx *lane) UI_BuildSpacer(window_padding, Axis_X); // UI_BuildSpacer(UI_FNT(100, 1), Axis_Y); + + + + // FIXME: Remove this + // if (scrollbar_visible) + // { + + // Vec2 scrollbar_pos = scrollbar_reps.draw.screen_rect.p0; + // Vec2 scrollbar_dims = DimsFromRng2(scrollbar_reps.draw.screen_rect); + // Vec2 thumb_dims = DimsFromRng2(thumb_reps.draw.screen_rect); + // // Vec2 half_thumb_dims = MulVec2(thumb_dims, 0.5); + + + // // f32 range_min = tweak_var.range.min; + // // f32 range_max = tweak_var.range.max; + // // if (range_max <= range_min) + // // { + // // range_max = range_min + 1; + // // } + + + + + + + // // f32 ratio = 0; + // // ratio = (tweak_float - range_min) / (range_max - range_min); + // // ratio = ClampF32(ratio, 0, 1); + + // if (thumb_reps.draw.m1.downs) + // { + // palette->scroll_begin = palette->scroll; + // } + // if (thumb_reps.draw.m1.held) + // { + // palette->scroll = palette->scroll_begin + (frame->screen_cursor.y - ui_frame->drag_cursor_pos.y); + // } + // palette->scroll = MaxF32(palette->scroll, 0); + + // // f32 thumb_offset = 50; + // f32 thumb_offset = palette->scroll; + + + + + + + // UI_SetNext(Width, scrollbar_width); + // UI_PushCP(UI_BuildBoxEx(scrollbar_key)); + // { + // UI_Size thumb_height = UI_FNT(10, 1); + + // Vec4 thumb_color = VEC4(0, 0.5, 1, 1); + // thumb_color.a = thumb_reps.draw.hot * 0.5 + 0.5; + + // UI_SetNext(BackgroundColor, thumb_color); + // UI_SetNext(Width, UI_GROW(1, 0)); + // UI_SetNext(Height, thumb_height); + // UI_SetNext(Rounding, UI_RGROW(1 * theme.rounding)); + // UI_SetNext(FloatingPos, VEC2(0, thumb_offset)); + // // UI_SetNext(Anchor, UI_Region_Center); + // UI_SetNext(Anchor, UI_Region_Top); + // UI_SetNext(Flags, UI_BoxFlag_CaptureMouse | UI_BoxFlag_Floating); + // UI_SetNext(Anchor, UI_Region_Top); + // UI_BuildBoxEx(thumb_key); + // } + // UI_PopCP(UI_TopCP()); + // } } UI_PopCP(UI_TopCP()); // UI_BuildSpacer(window_padding, Axis_Y); + + + UI_BuildDivider(UI_PIX(1, 1), divider_color, Axis_Y); + UI_BuildSpacer(header_height, Axis_Y); } UI_PopCP(UI_TopCP()); } // UI_PopCP(UI_TopCP()); UI_PopCP(palette_cp); } + + UI_Pop(Tag); } @@ -6314,7 +6501,7 @@ void V_TickForever(WaveLaneCtx *lane) UI_SetNext(Anchor, UI_Region_Center); UI_SetNext(FloatingPos, timeline_pos); UI_SetNext(BackgroundColor, tmld_bg); - UI_SetNext(Flags, UI_BoxFlag_Floating | UI_BoxFlag_NoFloatingClamp); + UI_SetNext(Flags, UI_BoxFlag_Floating | UI_BoxFlag_NoFloatingClampX | UI_BoxFlag_NoFloatingClampY); UI_PushCP(UI_BuildRowEx(timeline_key)); { } @@ -6374,7 +6561,7 @@ void V_TickForever(WaveLaneCtx *lane) tint.a *= marker_opacity; UI_SetNext(Tint, tint); - UI_SetNext(Flags, UI_BoxFlag_Floating | UI_BoxFlag_NoFloatingClamp); + UI_SetNext(Flags, UI_BoxFlag_Floating | UI_BoxFlag_NoFloatingClampX | UI_BoxFlag_NoFloatingClampY); UI_SetNext(BackgroundColor, marker_color); UI_SetNext(Anchor, UI_Region_Center); UI_SetNext(Width, width); diff --git a/src/pp/pp_vis/pp_vis_core.h b/src/pp/pp_vis/pp_vis_core.h index 0891ea80..d7027bf5 100644 --- a/src/pp/pp_vis/pp_vis_core.h +++ b/src/pp/pp_vis/pp_vis_core.h @@ -212,6 +212,8 @@ Struct(V_Palette) UI_Key key; b32 is_showing; f32 show; + f32 scroll; + f32 scroll_begin; V_TextboxState search_state; }; diff --git a/src/ui/ui_core.c b/src/ui/ui_core.c index e65620ec..7a9b35a7 100644 --- a/src/ui/ui_core.c +++ b/src/ui/ui_core.c @@ -643,8 +643,9 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags) b32 is_cursor_in_box = 0; { // TODO: More efficient test. This logic is just copied from the renderer's SDF function for now. - Vec2 p0 = box->screen_rect.p0; - Vec2 p1 = box->screen_rect.p1; + 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)); @@ -1310,9 +1311,16 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync) } } // Solve floating violation - if (is_floating && new_size > box_size && !AnyBit(child->desc.flags, UI_BoxFlag_NoFloatingClamp)) + if (is_floating && new_size > box_size) { - new_size = MaxF32(new_size - flex, box_size); + b32 should_clamp = ( + !(axis == Axis_X && AnyBit(child->desc.flags, UI_BoxFlag_NoFloatingClampX)) && + !(axis == Axis_Y && AnyBit(child->desc.flags, UI_BoxFlag_NoFloatingClampY)) + ); + if (should_clamp) + { + new_size = MaxF32(new_size - flex, box_size); + } } if (!is_floating) { @@ -1451,13 +1459,19 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync) // Compute rect Vec2 screen_pos = parent ? AddVec2(parent->screen_rect.p0, offset) : VEC2(0, 0); - if (is_floating && !AnyBit(box->desc.flags, UI_BoxFlag_NoFloatingClamp)) + if (is_floating) { Vec2 overshoot = Zi; - overshoot.x = MaxF32(0, (screen_pos.x + box->solved_dims.x) - parent->screen_rect.p1.x); - overshoot.y = MaxF32((screen_pos.y + box->solved_dims.y) - parent->screen_rect.p1.y, 0); - screen_pos.x = MaxF32(parent->screen_rect.p0.x, screen_pos.x - overshoot.x); - screen_pos.y = MaxF32(parent->screen_rect.p0.y, screen_pos.y - overshoot.y); + if (!AnyBit(box->desc.flags, UI_BoxFlag_NoFloatingClampX)) + { + overshoot.x = MaxF32(0, (screen_pos.x + box->solved_dims.x) - parent->screen_rect.p1.x); + screen_pos.x = MaxF32(parent->screen_rect.p0.x, screen_pos.x - overshoot.x); + } + if (!AnyBit(box->desc.flags, UI_BoxFlag_NoFloatingClampY)) + { + overshoot.y = MaxF32((screen_pos.y + box->solved_dims.y) - parent->screen_rect.p1.y, 0); + screen_pos.y = MaxF32(parent->screen_rect.p0.y, screen_pos.y - overshoot.y); + } } box->screen_rect.p0 = screen_pos; box->screen_rect.p1 = AddVec2(box->screen_rect.p0, box->solved_dims); @@ -1470,6 +1484,19 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync) } } + // Solve scissor + { + box->solved_scissor = Rng2Inf; + if (box->desc.flags & UI_BoxFlag_Scissor) + { + box->solved_scissor = box->screen_rect; + } + if (parent) + { + box->solved_scissor = IntersectRng2(box->solved_scissor, parent->solved_scissor); + } + } + // Solve screen rounding { UI_Round rounding = box->desc.rounding; @@ -1500,7 +1527,8 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync) } // Clamp rounding based on parent rounding - if (parent && !AllBits(box->desc.flags, UI_BoxFlag_Floating | UI_BoxFlag_NoFloatingClamp)) + // if (parent && !AllBits(box->desc.flags, UI_BoxFlag_Floating | UI_BoxFlag_NoFloatingClamp)) + if (parent) { Vec2 vtl = SubVec2(VEC2(parent->screen_rect.p0.x, parent->screen_rect.p0.y), VEC2(box->screen_rect.p0.x, box->screen_rect.p0.y)); Vec2 vtr = SubVec2(VEC2(parent->screen_rect.p1.x, parent->screen_rect.p0.y), VEC2(box->screen_rect.p1.x, box->screen_rect.p0.y)); @@ -1529,7 +1557,7 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync) ////////////////////////////// //- Build render data - // Build rect instance data + // Build GPU rect data for (u64 pre_index = 0; pre_index < boxes_count; ++pre_index) { UI_Box *box = boxes_pre[pre_index]; @@ -1537,21 +1565,22 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync) GC_Run raw_run = UI_ScaleRun(frame->arena, raw_run_unscaled, box->solved_scale); // TODO: Check that sprite image is not empty - b32 is_visible = 1; - is_visible = is_visible && (box->desc.tint.w >= 0.0025); - is_visible = is_visible && (box->screen_rect.p1.x - box->screen_rect.p0.x > 0.0025); - is_visible = is_visible && (box->screen_rect.p1.y - box->screen_rect.p0.y > 0.0025); - is_visible = is_visible && ( + b32 should_upload = 1; + should_upload = should_upload && (box->desc.tint.w >= 0.0025); + should_upload = should_upload && (box->screen_rect.p1.x - box->screen_rect.p0.x > 0.0025); + should_upload = should_upload && (box->screen_rect.p1.y - box->screen_rect.p0.y > 0.0025); + should_upload = should_upload && ( !G_IsRefNil(box->raw_texture) || !SPR_IsSheetKeyNil(box->desc.sprite_sheet) || (AnyBit(box->desc.flags, UI_BoxFlag_DrawText) && raw_run.ready) || box->desc.tint.a * box->desc.background_color.a >= 0.0025 || box->desc.tint.a * box->desc.border_color.a >= 0.0025 ); + should_upload = should_upload && (!IsRng2Empty(IntersectRng2(box->screen_rect, box->solved_scissor))); - if (is_visible || AnyBit(frame->frame_flags, UI_FrameFlag_Debug)) + if (should_upload || AnyBit(frame->frame_flags, UI_FrameFlag_Debug)) { - Vec4 debug_lin = is_visible ? LinearFromSrgb(box->desc.debug_color) : LinearFromSrgb(box->desc.invisible_debug_color); + Vec4 debug_lin = should_upload ? LinearFromSrgb(box->desc.debug_color) : LinearFromSrgb(box->desc.invisible_debug_color); Vec4 tint_lin = LinearFromSrgb(box->desc.tint); Vec2 box_dims = DimsFromRng2(box->screen_rect); UI_RegionPair child_alignment = UI_PairFromRegion(box->desc.child_alignment); @@ -1562,6 +1591,7 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync) { UI_GpuRect *rect = PushStruct(frame->rects_arena, UI_GpuRect); rect->bounds = box->screen_rect; + rect->scissor = box->solved_scissor; rect->background_lin = LinearFromSrgb(box->desc.background_color); rect->border_lin = LinearFromSrgb(box->desc.border_color); rect->debug_lin = debug_lin; @@ -1695,6 +1725,7 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync) rect->bounds = rr.bounds; rect->bounds = AddRng2Vec2(rect->bounds, baseline); rect->bounds = AddRng2Vec2(rect->bounds, VEC2(rr.baseline_pos, 0)); + rect->scissor = box->solved_scissor; } } } diff --git a/src/ui/ui_core.h b/src/ui/ui_core.h index 9728396a..5b92d82b 100644 --- a/src/ui/ui_core.h +++ b/src/ui/ui_core.h @@ -90,7 +90,9 @@ Enum(UI_BoxFlag) UI_BoxFlag_CaptureMouse = (1 << 1), UI_BoxFlag_NoTextTruncation = (1 << 2), UI_BoxFlag_Floating = (1 << 3), - UI_BoxFlag_NoFloatingClamp = (1 << 4), + UI_BoxFlag_NoFloatingClampX = (1 << 4), + UI_BoxFlag_NoFloatingClampY = (1 << 5), + UI_BoxFlag_Scissor = (1 << 6), }; //////////////////////////////////////////////////////////// @@ -322,6 +324,7 @@ Struct(UI_Box) //- Layout data Vec2 solved_scale; + Rng2 solved_scissor; Vec2 final_children_size_accum; Vec2 solved_dims; f32 cursor; diff --git a/src/ui/ui_gpu.g b/src/ui/ui_gpu.g index 08d60e82..dc31b486 100644 --- a/src/ui/ui_gpu.g +++ b/src/ui/ui_gpu.g @@ -79,9 +79,6 @@ PixelShader(UI_DRectPS, UI_DRectPSOutput, UI_DRectPSInput input) } } - //- Compute borer color - Vec4 border_premul = input.base_border_premul; - //- Compute border dist f32 border_dist = 0; border_dist = abs(rect_dist); @@ -90,6 +87,9 @@ PixelShader(UI_DRectPS, UI_DRectPSOutput, UI_DRectPSInput input) border_dist -= rect.border_size; } + //- Compute borer color + Vec4 border_premul = input.base_border_premul; + //- Compute anti-aliased border-over-background color Vec4 composite_premul = 0; { @@ -98,8 +98,16 @@ PixelShader(UI_DRectPS, UI_DRectPSOutput, UI_DRectPSInput input) composite_premul = lerp(background_premul, border_premul, border_coverage); } - //- Finalize Vec4 result = composite_premul * input.tint_premul; + + //- Scissor + // FIXME: Inclusive + if (p.x < rect.scissor.p0.x || p.x > rect.scissor.p1.x || p.y < rect.scissor.p0.y || p.y > rect.scissor.p1.y) + { + result = 0; + } + + //- Finalize if (UI_GpuConst_DebugDraw) { result = input.debug_premul; diff --git a/src/ui/ui_shared.cgh b/src/ui/ui_shared.cgh index 699cfa16..fcc3f449 100644 --- a/src/ui/ui_shared.cgh +++ b/src/ui/ui_shared.cgh @@ -10,6 +10,7 @@ G_DeclConstant(b32, UI_GpuConst_DebugDraw, 1); Struct(UI_GpuRect) { Rng2 bounds; + Rng2 scissor; G_TextureRef tex; Rng2 tex_slice_uv;