From cb58f4faef1fb16f8a66f97bb33e3766e01d790b Mon Sep 17 00:00:00 2001 From: Ed_ Date: Fri, 8 Mar 2024 18:45:27 -0500 Subject: [PATCH] Improvements to UI signal --- code/tick_update.odin | 16 ++-- code/ui.odin | 165 ++++--------------------------------- code/ui_rjf.odin | 43 ---------- code/ui_signal.odin | 184 ++++++++++++++++++++++++++++++++++++++++++ code/ui_widgets.odin | 4 + thirdparty/Odin | 2 +- 6 files changed, 212 insertions(+), 202 deletions(-) delete mode 100644 code/ui_rjf.odin create mode 100644 code/ui_signal.odin create mode 100644 code/ui_widgets.odin diff --git a/code/tick_update.odin b/code/tick_update.odin index 3ff08c9..6695787 100644 --- a/code/tick_update.odin +++ b/code/tick_update.odin @@ -252,9 +252,14 @@ update :: proc( delta_time : f64 ) -> b32 } } - config.ui_resize_border_width = 50 + config.ui_resize_border_width = 10 when Test_Draggable { + // draggable_box_layout := default_layout + // draggable_box_layout.pos = debug.draggable_box_pos + // draggable_box_layout.size = debug.draggable_box_size + // ui_set_layout(draggable_box_layout) + draggable_flags := UI_BoxFlags { .Mouse_Clickable, .Focusable, .Click_To_Focus } draggable_box := ui_box_make( draggable_flags, "Draggable Box!" ) signal := ui_signal_from_box( draggable_box ) @@ -272,25 +277,22 @@ update :: proc( delta_time : f64 ) -> b32 // Resize if signal.resizing { - if ! debug.box_resize_started { + if signal.pressed { debug.box_original_size = debug.draggable_box_size } center := debug.draggable_box_pos original_distance := linalg.distance(ui_context.cursor_active_start, center) cursor_distance := linalg.distance(signal.cursor_pos, center) - - scale_factor := cursor_distance * (1 / original_distance) + scale_factor := cursor_distance * (1 / original_distance) debug.draggable_box_size = debug.box_original_size * scale_factor } - debug.box_resize_started = cast(b32) signal.resizing if workspace.ui.hot_resizable || workspace.ui.active_resizing { draggable_box.style.bg_color = Color_Blue } - // Note(Ed): Don't necessarily need a layout if its simple... - draggable_box.style.pos = debug.draggable_box_pos + draggable_box.style.layout.pos = debug.draggable_box_pos draggable_box.style.layout.size = debug.draggable_box_size } } diff --git a/code/ui.odin b/code/ui.odin index c29b96d..10005a4 100644 --- a/code/ui.odin +++ b/code/ui.odin @@ -232,17 +232,18 @@ UI_Box :: struct { parent : ^UI_Box, num_children : i32, - flags : UI_BoxFlags, - computed : UI_Computed, - theme : UI_StyleTheme, - - style : ^UI_Style, + flags : UI_BoxFlags, + computed : UI_Computed, + prev_style : UI_Style, + style : UI_Style, // Persistent Data - first_frame : b8, - hot_delta : f32, - active_delta : f32, - style_delta : f32, + first_frame : b8, + hot_delta : f32, + active_delta : f32, + disabled_delta : f32, + style_delta : f32, + // prev_computed : UI_Computed, // prev_style : UI_Style,v mouse : UI_InteractState, @@ -286,10 +287,10 @@ UI_State :: struct { clipboard_copy : UI_Key, last_clicked : UI_Key, - cursor_active_start : Vec2, - // cursor_resize_start : Vec2, - // drag_state_arena : ^ Arena, - // drag_state data : string, + cursor_active_start : Vec2, + + hot_start_style : UI_Style, + active_start_style : UI_Style, last_pressed_key : [MouseBtn.count] UI_Key, last_pressed_key_us : [MouseBtn.count] f32, @@ -360,16 +361,7 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box) curr_box.first_frame = prev_box == nil } - // TODO(Ed) : Review this when we learn layouts more... - if prev_box != nil { - layout_dirty &= ! ui_box_equal( curr_box, prev_box ) - } - else { - layout_dirty = true - } - curr_box.flags = flags - curr_box.theme = stack_peek( & theme_stack ) curr_box.parent = stack_peek( & parent_stack ) // Clear old links @@ -434,7 +426,6 @@ ui_graph_build_begin :: proc( ui : ^ UI_State, bounds : Vec2 = {} ) } root = ui_box_make( {}, "root#001" ) - root.style = & root.theme.default ui_parent_push(root) } @@ -478,134 +469,6 @@ ui_parent :: proc( ui : ^ UI_Box) { ui_parent_push( ui ) } -ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal -{ - ui := get_state().ui_context - input := get_state().input - - frame_delta := frametime_delta32() - - signal := UI_Signal { box = box } - - // Cursor Collision - signal.cursor_pos = ui_cursor_pos() - signal.cursor_over = cast(b8) pos_within_range2( signal.cursor_pos, box.computed.bounds ) - - resize_border_width := cast(f32) get_state().config.ui_resize_border_width - resize_border_non_range := add(box.computed.bounds, range2( - { resize_border_width, -resize_border_width }, - { -resize_border_width, resize_border_width })) - - within_resize_range := cast(b8) ! pos_within_range2( signal.cursor_pos, resize_border_non_range ) - within_resize_range &= signal.cursor_over - - left_pressed := pressed( input.mouse.left ) - left_released := released( input.mouse.left ) - - mouse_clickable := UI_BoxFlag.Mouse_Clickable in box.flags - keyboard_clickable := UI_BoxFlag.Keyboard_Clickable in box.flags - - if mouse_clickable && signal.cursor_over && left_pressed - { - ui.hot = box.key - ui.active = box.key - ui.active_mouse[MouseBtn.Left] = box.key - ui.cursor_active_start = signal.cursor_pos - ui.last_pressed_key = box.key - - signal.pressed = true - // TODO(Ed) : Support double-click detection - } - - if mouse_clickable && signal.cursor_over && left_released - { - box.active_delta = 0 - ui.active = UI_Key(0) - ui.active_mouse[MouseBtn.Left] = UI_Key(0) - - signal.released = true - signal.left_clicked = true - - ui.last_clicked = box.key - } - - if mouse_clickable && ! signal.cursor_over && left_released - { - box.hot_delta = 0 - - ui.hot = UI_Key(0) - ui.active = UI_Key(0) - ui.active_mouse[MouseBtn.Left] = UI_Key(0) - - signal.released = true - signal.left_clicked = false - } - - if keyboard_clickable - { - // TODO(Ed) : Add keyboard interaction support - } - - // TODO(Ed) : Add scrolling support - if UI_BoxFlag.Scroll_X in box.flags { - - } - if UI_BoxFlag.Scroll_Y in box.flags { - - } - // TODO(Ed) : Add panning support - if UI_BoxFlag.Pan_X in box.flags { - - } - if UI_BoxFlag.Pan_Y in box.flags { - - } - - is_hot := ui.hot == box.key - is_active := ui.active == box.key - - if signal.cursor_over && - ui.hot == UI_Key(0) || is_hot && - ui.active == UI_Key(0) || is_active - { - ui.hot = box.key - is_hot = true - } - - if ! is_active { - ui.hot_resizable = cast(b32) within_resize_range - } - signal.resizing = cast(b8) is_active && (within_resize_range || ui.active_resizing) - - if is_hot { - box.hot_delta += frame_delta - } - if is_active { - box.active_delta += frame_delta - } - ui.active_resizing = cast(b32) is_active && signal.resizing - - signal.dragging = cast(b8) is_active && ( ! within_resize_range && ! ui.active_resizing) - - style_preset := UI_StylePreset.Default - // box.style = stack_peek( & ui.them_stack ).default - if box.key == ui.hot { - style_preset = UI_StylePreset.Hovered - // box.stye = stack_peek( & ui.theme_stack ).hovered - } - if box.key == ui.active { - style_preset = UI_StylePreset.Focused - // box.stye = stack_peek( & ui.theme_stack ).focused - } - if UI_BoxFlag.Disabled in box.flags { - style_preset = UI_StylePreset.Disabled - // box.style = stack_peek( & ui.theme.stack ).disabled - } - box.style = & box.theme.array[style_preset] - - return signal -} - ui_style_ref :: proc( box_state : UI_StylePreset ) -> (^ UI_Style) { return & stack_peek_ref( & get_state().ui_context.theme_stack ).array[box_state] } diff --git a/code/ui_rjf.odin b/code/ui_rjf.odin deleted file mode 100644 index 58f965b..0000000 --- a/code/ui_rjf.odin +++ /dev/null @@ -1,43 +0,0 @@ -package sectr - -when false { - ui_box_cache_insert :: proc( using cache : HMap_RJF( ^ UI_Box ), key : u64, value : ^ UI_Box ) -> ^ UI_Box { - slot := rjf_hmap_get_slot( cache, key ) - - // dll_insert_raw( nil, slot.first, slot.last, slot.last, value ) - { - new_links := & new.hash_links - - // Empty Case - if first == null { - first = new - last = new - new_links.next = null - new_links.prev = null - } - else if position == null { - // Position is not set, insert at beginning - new_links.next = first - first.first = new - first = new - new_links.prev = null - } - else if position == last { - // Positin is set to last, insert at end - last.last = new - new_links.prev = last - last = new - new_links.next = null - } - else { - // Insert around position - if position.next != null { - position.next.prev = new - } - new.next = position.next - position.next = new - new.prev = position - } - } - } -} diff --git a/code/ui_signal.odin b/code/ui_signal.odin new file mode 100644 index 0000000..b6e3363 --- /dev/null +++ b/code/ui_signal.odin @@ -0,0 +1,184 @@ +package sectr + +ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal +{ + ui := get_state().ui_context + input := get_state().input + + frame_delta := frametime_delta32() + + signal := UI_Signal { box = box } + + // Cursor Collision + signal.cursor_pos = ui_cursor_pos() + signal.cursor_over = cast(b8) pos_within_range2( signal.cursor_pos, box.computed.bounds ) + + resize_border_width := cast(f32) get_state().config.ui_resize_border_width + resize_border_non_range := add(box.computed.bounds, range2( + { resize_border_width, -resize_border_width }, + { -resize_border_width, resize_border_width })) + + within_resize_range := cast(b8) ! pos_within_range2( signal.cursor_pos, resize_border_non_range ) + within_resize_range &= signal.cursor_over + + left_pressed := pressed( input.mouse.left ) + left_released := released( input.mouse.left ) + + mouse_clickable := UI_BoxFlag.Mouse_Clickable in box.flags + keyboard_clickable := UI_BoxFlag.Keyboard_Clickable in box.flags + + was_hot := ui.hot == box.key && box.hot_delta > 0 + was_active := ui.active == box.key && box.active_delta > 0 + was_disabled := box.disabled_delta > 0 + + if mouse_clickable && signal.cursor_over && left_pressed + { + ui.hot = box.key + ui.active = box.key + ui.active_mouse[MouseBtn.Left] = box.key + ui.last_pressed_key = box.key + + ui.cursor_active_start = signal.cursor_pos + + signal.pressed = true + // TODO(Ed) : Support double-click detection + } + + if mouse_clickable && signal.cursor_over && left_released + { + box.active_delta = 0 + ui.active = UI_Key(0) + ui.active_mouse[MouseBtn.Left] = UI_Key(0) + + signal.released = true + signal.left_clicked = true + + ui.last_clicked = box.key + } + + if mouse_clickable && ! signal.cursor_over && left_released + { + box.hot_delta = 0 + + ui.hot = UI_Key(0) + ui.active = UI_Key(0) + ui.active_mouse[MouseBtn.Left] = UI_Key(0) + + signal.released = true + signal.left_clicked = false + } + + if keyboard_clickable + { + // TODO(Ed) : Add keyboard interaction support + } + + // TODO(Ed) : Add scrolling support + if UI_BoxFlag.Scroll_X in box.flags { + + } + if UI_BoxFlag.Scroll_Y in box.flags { + + } + + // TODO(Ed) : Add panning support + if UI_BoxFlag.Pan_X in box.flags { + + } + if UI_BoxFlag.Pan_Y in box.flags { + + } + + is_disabled := UI_BoxFlag.Disabled in box.flags + is_hot := ui.hot == box.key + is_active := ui.active == box.key + + if signal.cursor_over && + ui.hot == UI_Key(0) || is_hot && + ui.active == UI_Key(0) || is_active + { + ui.hot = box.key + is_hot = true + } + + if ! is_active { + ui.hot_resizable = cast(b32) within_resize_range + } + signal.resizing = cast(b8) is_active && (within_resize_range || ui.active_resizing) + + // State Deltas update + if is_hot + { + box.hot_delta += frame_delta + if was_hot { + box.style_delta += frame_delta + } + } + else { + box.hot_delta = 0 + } + if is_active + { + box.active_delta += frame_delta + if was_active { + box.style_delta += frame_delta + } + } + else { + box.active_delta = 0 + } + if is_disabled + { + box.disabled_delta += frame_delta + if was_hot { + box.style_delta += frame_delta + } + } + else { + box.disabled_delta = 0 + } + + ui.active_resizing = cast(b32) is_active && signal.resizing + signal.dragging = cast(b8) is_active && ( ! within_resize_range && ! ui.active_resizing) + + // Update style if not in default state + { + if is_hot + { + if ! was_hot { + box.prev_style = box.style + box.style_delta = 0 + } + box.style = stack_peek( & ui.theme_stack ).hovered + } + if is_active + { + if ! was_active { + box.prev_style = box.style + box.style_delta = 0 + } + box.style = stack_peek( & ui.theme_stack ).focused + } + if is_disabled + { + if ! was_disabled { + box.prev_style = box.style + box.style_delta = 0 + } + box.style = stack_peek( & ui.theme_stack ).disabled + } + + if ! is_disabled && ! is_active && ! is_hot { + if was_disabled || was_active || was_hot { + box.prev_style = box.style + box.style_delta = 0 + } + else { + box.style_delta += frame_delta + } + box.style = stack_peek( & ui.theme_stack ).default + } + } + + return signal +} diff --git a/code/ui_widgets.odin b/code/ui_widgets.odin new file mode 100644 index 0000000..86007c1 --- /dev/null +++ b/code/ui_widgets.odin @@ -0,0 +1,4 @@ +package sectr + + +ui_button :: proc( ) \ No newline at end of file diff --git a/thirdparty/Odin b/thirdparty/Odin index 242d5b8..c448b34 160000 --- a/thirdparty/Odin +++ b/thirdparty/Odin @@ -1 +1 @@ -Subproject commit 242d5b8c5c92cab6c76757af2a4acc763fed41e3 +Subproject commit c448b34143971698fa0d2a69b91dd274d21ca328