From 7250456db54bd6098493eac5e6f6de951a55fcb6 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Thu, 16 May 2024 02:14:54 -0400 Subject: [PATCH] Got centered resize working for the handles Impl feels jank... but thats what I get for supporting two origins for the auto-layout --- code/app_screen.odin | 29 +++------ code/ui_layout_compute.odin | 39 +++++++----- code/ui_signal.odin | 12 ++-- code/ui_ui.odin | 6 +- code/ui_widgets.odin | 121 +++++++++++++++++++++++------------- scripts/build.ps1 | 4 +- 6 files changed, 125 insertions(+), 86 deletions(-) diff --git a/code/app_screen.odin b/code/app_screen.odin index 6590b44..a7a25c0 100644 --- a/code/app_screen.odin +++ b/code/app_screen.odin @@ -179,7 +179,8 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b layout.flags = {.Origin_At_Anchor_Center } layout.pos = {} } - should_raise |= ui_resizable_handles( & container, & pos, & size) + should_raise |= ui_resizable_handles( & container, & pos, & size/*, compute_layout = true*/) + // ui_box_compute_layout(container) vbox := ui_vbox_begin( .Top_To_Bottom, "Settings Menu: VBox", {.Mouse_Clickable}, compute_layout = true) { @@ -198,10 +199,8 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b ui_style_ref().hot.bg_color = Color_Blue frame_bar := ui_hbox_begin(.Left_To_Right, "Settings Menu: Frame Bar", { .Mouse_Clickable, .Focusable, .Click_To_Focus }) { - // frame_bar.style.bg_color = Color_BG_Panel frame_bar.layout.flags = {.Fixed_Height} frame_bar.layout.size.min.y = 50 - // frame_bar.layout.anchor.ratio.y = 0.8 ui_parent(frame_bar) ui_layout( UI_Layout { @@ -221,8 +220,6 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b ui_style(ui_style_peek()) style := ui_style_ref() - // style.default.bg_color = Color_Black - // style.hot.bg_color = Color_Frame_Hover maximize_btn := ui_button("Settings Menu: Maximize Btn") { using maximize_btn @@ -237,9 +234,6 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b if settings_menu.is_maximized do text = str_intern("min") else do text = str_intern("max") } - - // style.default.bg_color = Color_GreyRed - // style.hot. bg_color = Color_Red close_btn := ui_button("Settings Menu: Close Btn") { using close_btn @@ -274,6 +268,7 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b // bg_color = Color_GreyRed }) drop_down_bar := ui_hbox_begin(.Left_To_Right, "settings_menu.vbox: config drop_down_bar", {.Mouse_Clickable}) + btn : UI_Widget { drop_down_bar.layout.anchor.ratio.y = 0.1 { @@ -289,21 +284,22 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b } ui_parent(drop_down_bar) - btn := ui_text("pls", str_intern("Lets figure this out...")) + btn = ui_text("pls", str_intern("Lets figure this out...")) { using btn text = str_intern("Config") style.font = default_font style.text_color = Color_White - layout.flags = {.Origin_At_Anchor_Center} - layout.alignment = {0.0, 0.0} // ??? (Wtf is this alignment) + // layout.flags = {.Origin_At_Anchor_Center} + layout.alignment = {0.0, 0.0} layout.anchor.ratio.x = 1.0 layout.font_size = 12 layout.margins = {0,0, 15, 0} layout.size.min.y = 35 } - ui_hbox_end(drop_down_bar, compute_layout = false) - ui_box_compute_layout(btn) + um := ui_spacer("um...") + um.layout.anchor.ratio.x = 1.0 + ui_hbox_end(drop_down_bar, compute_layout = true) } // ui_layout(UI_Layout { @@ -315,15 +311,10 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b // res_width_hbox := ui_hbox_begin(.Left_To_Right, "settings_menu.vbox: config.resolution_width: hbox", {}) // ui_parent(res_width_hbox) - // ui_layout_ref().default.flags = {.Fixed_Width, .Fixed_Height, .Fixed_Position_Y} - // ui_layout_ref().default.size.min = {50, 50} spacer := ui_spacer("Settings Menu: Spacer") spacer.layout.anchor.ratio.y = 1.0 - // spacer.layout.flags = {.Origin_At_Anchor_Center} - // spacer.layout.alignment = {0.5, 0.5} - // spacer.style.bg_color = Color_Red - ui_vbox_end(vbox, compute_layout = true ) + ui_vbox_end(vbox, compute_layout = false ) } return } diff --git a/code/ui_layout_compute.odin b/code/ui_layout_compute.odin index ae9a746..f7234fa 100644 --- a/code/ui_layout_compute.odin +++ b/code/ui_layout_compute.odin @@ -1,6 +1,7 @@ package sectr ui_box_compute_layout :: proc( box : ^UI_Box, + dont_mark_fresh : b32 = false, ancestors_layout_required : b32 = false, root_layout_required : b32 = false ) { @@ -43,7 +44,7 @@ ui_box_compute_layout :: proc( box : ^UI_Box, * Ignore Parent constraints (can only be clipped) If an axis is auto-sized by a ratio of the other axis - * + * Using the referenced axis, set the size of the ratio'd axis by that ratio. If auto-sized: * Enforce parent size constraint of bounds relative to @@ -85,13 +86,6 @@ ui_box_compute_layout :: proc( box : ^UI_Box, adjusted_size.x = max( adjusted_max_size_x, layout.size.min.x) adjusted_size.y = max( adjusted_max_size_y, layout.size.min.y) - if .Fixed_Width in layout.flags { - adjusted_size.x = layout.size.min.x - } - if .Fixed_Height in layout.flags { - adjusted_size.y = layout.size.min.y - } - text_size : Vec2 if layout.font_size == computed.text_size.y { text_size = computed.text_size @@ -104,6 +98,22 @@ ui_box_compute_layout :: proc( box : ^UI_Box, adjusted_size = text_size } + if .Size_To_Content in layout.flags { + // Preemtively traverse the children of this parent and have them compute their layout. + // This parent will just set its size to the max bounding area of those children. + // This will recursively occur if child also depends on their content size from their children, etc. + ui_box_compute_layout_children(box) + //ui_compute_children_bounding_area(box) + } + + // TODO(Ed): Should this force override all of the previous auto-sizing possible? + if .Fixed_Width in layout.flags { + adjusted_size.x = layout.size.min.x + } + if .Fixed_Height in layout.flags { + adjusted_size.y = layout.size.min.y + } + // 5. Determine relative position origin_center := margined_bounds_origin @@ -166,18 +176,19 @@ ui_box_compute_layout :: proc( box : ^UI_Box, content_size := content_bounds.max - content_bounds.min text_pos : Vec2 text_pos = content_bounds.min + { 0, text_size.y } - text_pos.x += ( content_size.x - text_size.x ) * layout.text_alignment.x - text_pos.y += ( content_size.y - text_size.y ) * layout.text_alignment.y + // text_pos.x += ( content_size.x - text_size.x ) * layout.text_alignment.x + // text_pos.y += ( content_size.y - text_size.y ) * layout.text_alignment.y + text_pos += (content_size - text_size) * layout.text_alignment computed.text_size = text_size computed.text_pos = { text_pos.x, text_pos.y } } - computed.fresh = true + computed.fresh = true && !dont_mark_fresh } ui_box_compute_layout_children :: proc( box : ^UI_Box ) { - for current := box.first; current != nil; current = ui_box_tranverse_next( current ) + for current := box.first; current != nil && current.prev != box; current = ui_box_tranverse_next( current ) { if current == box do return if current.computed.fresh do continue @@ -195,10 +206,10 @@ ui_compute_layout :: proc( ui : ^UI_State ) computed := & root.computed style := root.style layout := & root.layout - // if ui == & state.screen_ui { + if ui == & state.screen_ui { computed.bounds.min = transmute(Vec2) state.app_window.extent * -1 computed.bounds.max = transmute(Vec2) state.app_window.extent - // } + } computed.content = computed.bounds } diff --git a/code/ui_signal.odin b/code/ui_signal.odin index bae9469..741e930 100644 --- a/code/ui_signal.odin +++ b/code/ui_signal.odin @@ -11,8 +11,8 @@ UI_Signal :: struct { right_clicked : b8, double_clicked : b8, keyboard_clicked : b8, - left_shift : b8, - left_ctrl : b8, + left_shift_held : b8, + left_ctrl_held : b8, active : b8, hot : b8, @@ -66,7 +66,7 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas left_pressed := pressed( input.mouse.left ) left_released := released( input.mouse.left ) - left_shift := pressed(input.keyboard.left_shift) + signal.left_shift_held = b8(input.keyboard.left_shift.ended_down) mouse_clickable := UI_BoxFlag.Mouse_Clickable in box.flags keyboard_clickable := UI_BoxFlag.Keyboard_Clickable in box.flags @@ -74,9 +74,9 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas was_hot := (box.hot_delta > 0) was_active := (ui.active == box.key) && (box.active_delta > 0) was_disabled := box.disabled_delta > 0 - if was_hot { + // if was_hot { // runtime.debug_trap() - } + // } // Check to see if this box is active if mouse_clickable && signal.cursor_over && left_pressed && was_hot @@ -90,7 +90,6 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas signal.pressed = true signal.left_clicked = b8(left_pressed) - signal.left_shift = b8(left_shift) // TODO(Ed) : Support double-click detection } @@ -102,7 +101,6 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas ui.active_mouse[MouseBtn.Left] = UI_Key(0) signal.released = true - signal.left_clicked = false } if keyboard_clickable diff --git a/code/ui_ui.odin b/code/ui_ui.odin index f2b07b0..0c941b9 100644 --- a/code/ui_ui.odin +++ b/code/ui_ui.odin @@ -208,7 +208,7 @@ ui_graph_build_begin :: proc( ui : ^ UI_State, bounds : Vec2 = {} ) } ui.built_box_count = 0 - root = ui_box_make( {}, "root#001" ) + root = ui_box_make( {}, str_intern(str_fmt_tmp("%s: root#001", ui == & state.screen_ui ? "Screen" : "Workspace" )).str) if ui == & state.screen_ui { root.layout.size = range2(Vec2(state.app_window.extent) * 2, {}) } @@ -271,6 +271,10 @@ ui_parent_pop :: proc() { @(deferred_none = ui_parent_pop) ui_parent :: #force_inline proc( ui : ^UI_Box) { ui_parent_push( ui ) } +ui_prev_cached_box :: #force_inline proc( box : ^UI_Box ) -> ^UI_Box { + return zpl_hmap_get( ui_context().prev_cache, cast(u64) box.key ) +} + // Topmost ancestor that is not the root ui_top_ancestor :: #force_inline proc "contextless" ( box : ^UI_Box ) -> (^UI_Box) { using ui := get_state().ui_context diff --git a/code/ui_widgets.odin b/code/ui_widgets.odin index a7cb86c..6c0eafc 100644 --- a/code/ui_widgets.odin +++ b/code/ui_widgets.odin @@ -1,5 +1,6 @@ package sectr +import "base:runtime" import lalg "core:math/linalg" UI_Widget :: struct { @@ -58,7 +59,7 @@ ui_hbox_begin :: proc( direction : UI_LayoutDirectionX, label : string, flags : ui_hbox_end :: proc( hbox : UI_HBox, width_ref : ^f32 = nil, compute_layout := true ) { // profile(#procedure) - if compute_layout do ui_box_compute_layout(hbox.box) + if compute_layout do ui_box_compute_layout(hbox.box, dont_mark_fresh = true) ui_layout_children_horizontally( hbox.box, hbox.direction, width_ref ) } @@ -148,7 +149,6 @@ ui_resizable_end_auto :: proc() { } // Adds resizable handles to a widget -// TODO(Ed): Add centered resize support (use center alignment on shift-click) ui_resizable_handles :: proc( parent : ^UI_Widget, pos : ^Vec2, size : ^Vec2, handle_width : f32 = 15, theme : ^UI_Theme = nil, @@ -280,12 +280,12 @@ ui_resizable_handles :: proc( parent : ^UI_Widget, pos : ^Vec2, size : ^Vec2, handle_corner_tr.layout.alignment = {0, -1} } if corner_bl { - handle_corner_bl = ui_widget("Settings Menu: Corner BL", flags) + handle_corner_bl = ui_widget("corner_bottom_left", flags) handle_corner_bl.layout.anchor = range2({}, {0, 1}) handle_corner_bl.layout.alignment = { 1, 0 } } if corner_br { - handle_corner_br = ui_widget("Settings Menu: Corner BR", flags) + handle_corner_br = ui_widget("corner_bottom_right", flags) handle_corner_br.layout.anchor = range2({1, 0}, {0, 1}) handle_corner_br.layout.alignment = {0, 0} } @@ -293,26 +293,32 @@ ui_resizable_handles :: proc( parent : ^UI_Widget, pos : ^Vec2, size : ^Vec2, process_handle_drag :: #force_inline proc ( handle : ^UI_Widget, direction : Vec2, - size_delta : Vec2, target_alignment : Vec2, target_center_aligned : Vec2, pos : ^Vec2, size : ^Vec2, alignment : ^Vec2, ) -> b32 { - @static active_context : ^UI_State - @static was_dragging : b32 = false - @static start_size : Vec2 + @static active_context : ^UI_State + @static was_dragging : b32 = false + @static start_size : Vec2 + @static prev_left_shift_held : b8 + @static prev_alignment : Vec2 ui := get_state().ui_context - if ui.last_pressed_key != handle.key do return false using handle + if ui.last_pressed_key != key || (!active && (!released || !was_dragging)) do return false direction := direction - target_alignment := target_alignment + align_adjsutment := left_shift_held ? target_center_aligned : target_alignment - size_delta := ui_drag_delta() - pos_adjust := size^ * (alignment^ - target_alignment) + size_delta := ui_drag_delta() + pos_adjust := size^ * (alignment^ - align_adjsutment) + pos_reverse := size^ * (alignment^ - prev_alignment) + + shift_changed := (left_shift_held != prev_left_shift_held) + + need_to_change_alignment_and_pos := pressed || shift_changed if active { @@ -320,16 +326,41 @@ ui_resizable_handles :: proc( parent : ^UI_Widget, pos : ^Vec2, size : ^Vec2, { active_context = ui start_size = size^ - if .Origin_At_Anchor_Center in parent.layout.flags { - pos_adjust = size^ * 0.5 * direction - } + prev_left_shift_held = left_shift_held } - size^ = start_size + size_delta * direction - if pressed { + if (.Origin_At_Anchor_Center in parent.layout.flags) && !left_shift_held { + pos_adjust = size^ * 0.5 * direction + pos_reverse = size^ * 0.5 * direction + } + + latest_size := start_size + size_delta * direction + + if pressed + { pos^ -= pos_adjust } - else { - alignment^ = target_alignment + else if shift_changed + { + if (.Origin_At_Anchor_Center in parent.layout.flags) { + pos^ -= pos_reverse + alignment^ = !left_shift_held ? target_center_aligned : target_alignment + } + else + { + if !left_shift_held { + pos^ -= size^ * direction * 0.5 + alignment^ = target_center_aligned + } + else { + pos^ += size^ * direction * 0.5 // Right + alignment^ = target_alignment + } + } + } + else + { + size^ = latest_size + alignment^ = align_adjsutment } was_dragging = true } @@ -338,43 +369,47 @@ ui_resizable_handles :: proc( parent : ^UI_Widget, pos : ^Vec2, size : ^Vec2, // This needed to be added as for some reason, this was getting called in screen_ui even when we were resizing with a handle in a worksapce if active_context != ui do return false - if .Origin_At_Anchor_Center in parent.layout.flags { - pos_adjust = size^ * 0.5 * direction + if (.Origin_At_Anchor_Center in parent.layout.flags) && !left_shift_held { + pos_adjust = size^ * 0.5 * direction + pos_reverse = size^ * 0.5 * direction } pos^ += pos_adjust - alignment^ = target_alignment + alignment^ = align_adjsutment was_dragging = false start_size = 0 - active_context = ui } + // text = active_context.root.label + // style.text_color = Color_White + + prev_left_shift_held = handle.left_shift_held + prev_alignment = align_adjsutment return was_dragging } - state := get_state() - delta := state.input.mouse.delta //state.ui_context == & state.screen_ui ? state.input.mouse.delta : ui_ws_drag_delta() + state := get_state() alignment := & parent.layout.alignment if .Origin_At_Anchor_Center in parent.layout.flags { - if right do drag_signal |= process_handle_drag( & handle_right, { 1, 0}, delta, { 0.5, 0}, {}, pos, size, alignment ) - if left do drag_signal |= process_handle_drag( & handle_left, {-1, 0}, delta, {-0.5, 0}, {}, pos, size, alignment ) - if top do drag_signal |= process_handle_drag( & handle_top, { 0, 1}, delta, { 0, 0.5}, {}, pos, size, alignment ) - if bottom do drag_signal |= process_handle_drag( & handle_bottom, { 0, -1}, delta, { 0, -0.5}, {}, pos, size, alignment ) - if corner_tr do drag_signal |= process_handle_drag( & handle_corner_tr, { 1, 1}, delta, { 0.5, 0.5}, {}, pos, size, alignment ) - if corner_tl do drag_signal |= process_handle_drag( & handle_corner_tl, {-1, 1}, delta, {-0.5, 0.5}, {}, pos, size, alignment ) - if corner_br do drag_signal |= process_handle_drag( & handle_corner_br, { 1, -1}, delta, { 0.5, -0.5}, {}, pos, size, alignment ) - if corner_bl do drag_signal |= process_handle_drag( & handle_corner_bl, {-1, -1}, delta, {-0.5, -0.5}, {}, pos, size, alignment ) + if right do drag_signal |= process_handle_drag( & handle_right, { 1, 0}, { 0.5, 0}, {0, 0}, pos, size, alignment ) + if left do drag_signal |= process_handle_drag( & handle_left, {-1, 0}, {-0.5, 0}, {0, 0}, pos, size, alignment ) + if top do drag_signal |= process_handle_drag( & handle_top, { 0, 1}, { 0, 0.5}, {0, 0}, pos, size, alignment ) + if bottom do drag_signal |= process_handle_drag( & handle_bottom, { 0, -1}, { 0, -0.5}, {0, 0}, pos, size, alignment ) + if corner_tr do drag_signal |= process_handle_drag( & handle_corner_tr, { 1, 1}, { 0.5, 0.5}, {0, 0}, pos, size, alignment ) + if corner_tl do drag_signal |= process_handle_drag( & handle_corner_tl, {-1, 1}, {-0.5, 0.5}, {0, 0}, pos, size, alignment ) + if corner_br do drag_signal |= process_handle_drag( & handle_corner_br, { 1, -1}, { 0.5, -0.5}, {0, 0}, pos, size, alignment ) + if corner_bl do drag_signal |= process_handle_drag( & handle_corner_bl, {-1, -1}, {-0.5, -0.5}, {0, 0}, pos, size, alignment ) } else { - if right do drag_signal |= process_handle_drag( & handle_right, { 1, 0 }, delta, {0, 0}, {}, pos, size, alignment ) - if left do drag_signal |= process_handle_drag( & handle_left, { -1, 0 }, delta, {1, 0}, {}, pos, size, alignment ) - if top do drag_signal |= process_handle_drag( & handle_top, { 0, 1 }, delta, {0, -1}, {}, pos, size, alignment ) - if bottom do drag_signal |= process_handle_drag( & handle_bottom, { 0, -1 }, delta, {0, 0}, {}, pos, size, alignment ) - if corner_tr do drag_signal |= process_handle_drag( & handle_corner_tr, { 1, 1 }, delta, {0, -1}, {}, pos, size, alignment ) - if corner_tl do drag_signal |= process_handle_drag( & handle_corner_tl, { -1, 1 }, delta, {1, -1}, {}, pos, size, alignment ) - if corner_br do drag_signal |= process_handle_drag( & handle_corner_br, { 1, -1 }, delta, {0, 0}, {}, pos, size, alignment ) - if corner_bl do drag_signal |= process_handle_drag( & handle_corner_bl, { -1, -1 }, delta, {1, 0}, {}, pos, size, alignment ) + if right do drag_signal |= process_handle_drag( & handle_right, { 1, 0 }, {0, 0}, { 0.5, 0}, pos, size, alignment ) + if left do drag_signal |= process_handle_drag( & handle_left, { -1, 0 }, {1, 0}, { 0.5, 0}, pos, size, alignment ) + if top do drag_signal |= process_handle_drag( & handle_top, { 0, 1 }, {0, -1}, { 0.0, -0.5}, pos, size, alignment ) + if bottom do drag_signal |= process_handle_drag( & handle_bottom, { 0, -1 }, {0, 0}, { 0.0, -0.5}, pos, size, alignment ) + if corner_tr do drag_signal |= process_handle_drag( & handle_corner_tr, { 1, 1 }, {0, -1}, { 0.5, -0.5}, pos, size, alignment ) + if corner_tl do drag_signal |= process_handle_drag( & handle_corner_tl, { -1, 1 }, {1, -1}, { 0.5, -0.5}, pos, size, alignment ) + if corner_br do drag_signal |= process_handle_drag( & handle_corner_br, { 1, -1 }, {0, 0}, { 0.5, -0.5}, pos, size, alignment ) + if corner_bl do drag_signal |= process_handle_drag( & handle_corner_bl, { -1, -1 }, {1, 0}, { 0.5, -0.5}, pos, size, alignment ) } if drag_signal && compute_layout do ui_box_compute_layout(parent) @@ -462,14 +497,14 @@ ui_vbox_begin :: proc( direction : UI_LayoutDirectionY, label : string, flags : vbox.direction = direction vbox.box = ui_box_make( flags, label ) vbox.signal = ui_signal_from_box( vbox.box ) - if compute_layout do ui_box_compute_layout(vbox) + if compute_layout do ui_box_compute_layout(vbox, dont_mark_fresh = true) return } // Auto-layout children ui_vbox_end :: proc( vbox : UI_VBox, height_ref : ^f32 = nil, compute_layout := true ) { // profile(#procedure) - if compute_layout do ui_box_compute_layout(vbox) + if compute_layout do ui_box_compute_layout(vbox, dont_mark_fresh = true) ui_layout_children_vertically( vbox.box, vbox.direction, height_ref ) } diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 7cad078..9787847 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -169,8 +169,8 @@ push-location $path_root # $build_args += $flag_micro_architecture_native # $build_args += $flag_use_separate_modules # $build_args += $flag_thread_count + $CoreCount_Physical - # $build_args += $flag_optimize_none - $build_args += $flag_optimize_minimal + $build_args += $flag_optimize_none + # $build_args += $flag_optimize_minimal # $build_args += $flag_optimize_speed # $build_args += $falg_optimize_aggressive $build_args += $flag_debug