diff --git a/code/sectr/ui/core/base.odin b/code/sectr/ui/core/base.odin index 7fb9a64..fa6dd53 100644 --- a/code/sectr/ui/core/base.odin +++ b/code/sectr/ui/core/base.odin @@ -224,7 +224,6 @@ ui_graph_build_end :: proc( ui : ^UI_State ) computed.content = computed.bounds } - // Auto-layout and initial render_queue generation render_queue := array_to_slice(ui.render_queue) for current := root.first; current != nil; current = ui_box_tranverse_next_depth_first( current ) diff --git a/code/sectr/ui/core/layout.odin b/code/sectr/ui/core/layout.odin index 704d381..e2b04db 100644 --- a/code/sectr/ui/core/layout.odin +++ b/code/sectr/ui/core/layout.odin @@ -61,12 +61,19 @@ UI_Computed :: struct { fresh : b32, // If the auto-layout has been computed for the current frame } -UI_LayoutDirectionX :: enum(i32) { +UI_LayoutDirection_XY :: enum(i32) { + Left_To_Right, + Right_to_Left, + Top_To_Bottom, + Bottom_To_Top, +} + +UI_LayoutDirection_X :: enum(i32) { Left_To_Right, Right_To_Left, } -UI_LayoutDirectionY :: enum(i32) { +UI_LayoutDirection_Y :: enum(i32) { Top_To_Bottom, Bottom_To_Top, } @@ -190,3 +197,24 @@ ui_layout_pop :: #force_inline proc() { pop( & @(deferred_none = ui_layout_pop) ui_layout_scope_via_combo :: #force_inline proc( combo : UI_LayoutCombo ) { ui_layout_push( combo) } ui_set_layout :: #force_inline proc( layout : UI_Layout, preset : UI_StylePreset ) { stack_peek_ref( & get_state().ui_context.layout_combo_stack).array[preset] = layout } + +ui_size_to_content_xy :: proc ( box : ^UI_Box) { + using box + children_bounds := ui_compute_children_overall_bounds(box) + layout.size.min = size_range2(children_bounds) + layout.flags |= { .Fixed_Width, .Fixed_Height } +} + +ui_size_to_content_x :: proc ( box : ^UI_Box) { + using box + children_bounds := ui_compute_children_overall_bounds(box) + layout.size.min.x = size_range2(children_bounds).x + layout.flags |= { .Fixed_Width } +} + +ui_size_to_content_y :: proc ( box : ^UI_Box) { + using box + children_bounds := ui_compute_children_overall_bounds(box) + layout.size.min.y = size_range2(children_bounds).y + layout.flags |= { .Fixed_Height } +} diff --git a/code/sectr/ui/layout_widget.odin b/code/sectr/ui/layout_widget.odin index df2c008..8aef556 100644 --- a/code/sectr/ui/layout_widget.odin +++ b/code/sectr/ui/layout_widget.odin @@ -7,7 +7,7 @@ import lalg "core:math/linalg" Widget Layout Ops */ -ui_layout_children_horizontally :: proc( container : ^UI_Box, direction : UI_LayoutDirectionX, width_ref : ^f32 ) +ui_layout_children_horizontally :: proc( container : ^UI_Box, direction : UI_LayoutDirection_X, width_ref : ^f32 ) { container_width : f32 if width_ref != nil { @@ -86,7 +86,7 @@ ui_layout_children_horizontally :: proc( container : ^UI_Box, direction : UI_Lay } } -ui_layout_children_vertically :: proc( container : ^UI_Box, direction : UI_LayoutDirectionY, height_ref : ^f32 ) +ui_layout_children_vertically :: proc( container : ^UI_Box, direction : UI_LayoutDirection_Y, height_ref : ^f32 ) { container_height : f32 if height_ref != nil { diff --git a/code/sectr/ui/widget_theme.odin b/code/sectr/ui/widget_theme.odin index 8612469..0e01518 100644 --- a/code/sectr/ui/widget_theme.odin +++ b/code/sectr/ui/widget_theme.odin @@ -173,8 +173,8 @@ theme_window_bar :: proc() -> UI_Theme app_color := app_color_theme() layout := UI_Layout { flags = {.Fixed_Height}, - anchor = range2({},{}), - alignment = {0, 0}, + anchor = range2({0, 1},{0, 0}), + alignment = {0, 1}, text_alignment = {0.0, 0.0}, font_size = 16, margins = {0, 0, 0, 0}, diff --git a/code/sectr/ui/widgets.odin b/code/sectr/ui/widgets.odin index 8f023aa..22d4858 100644 --- a/code/sectr/ui/widgets.odin +++ b/code/sectr/ui/widgets.odin @@ -37,7 +37,7 @@ UI_DropDown :: struct { @(deferred_out = ui_drop_down_end_auto) ui_drop_down :: proc( drop_down : ^UI_DropDown, label : string, title_text : StrRunesPair, - direction := UI_LayoutDirectionY.Top_To_Bottom, + direction := UI_LayoutDirection_Y.Top_To_Bottom, btn_flags := UI_BoxFlags{}, vb_flags := UI_BoxFlags{}, vb_compute_layout := true, @@ -55,7 +55,7 @@ ui_drop_down :: proc( drop_down : ^UI_DropDown, label : string, title_text : Str // Its assumed that the drop down has a vertical box parent already pushed ui_drop_down_begin :: proc( drop_down : ^UI_DropDown, label : string, title_text : StrRunesPair, - direction := UI_LayoutDirectionY.Top_To_Bottom, + direction := UI_LayoutDirection_Y.Top_To_Bottom, btn_flags := UI_BoxFlags{}, vb_flags := UI_BoxFlags{}, btn_theme : ^UI_Theme = nil, @@ -91,7 +91,7 @@ ui_drop_down_begin :: proc( drop_down : ^UI_DropDown, label : string, title_text if vb_parent != nil { ui_parent_push(vb_parent) } - vbox = ui_vbox_begin( direction, str_intern_fmt("%v : vbox", label).str, compute_layout = vb_compute_layout ) + vbox = ui_vbox_begin( direction, str_intern_fmt("%v.vbox", label).str, flags = {.Mouse_Clickable}, compute_layout = vb_compute_layout ) vbox.layout.anchor.ratio.y = 1.0 if vb_parent != nil { @@ -129,10 +129,10 @@ Keep in mind the stretch ratio is only respected if no size.min.x value is viola UI_HBox :: struct { using widget : UI_Widget, - direction : UI_LayoutDirectionX, + direction : UI_LayoutDirection_X, } -ui_hbox_begin :: proc( direction : UI_LayoutDirectionX, label : string, flags : UI_BoxFlags = {} ) -> (hbox : UI_HBox) { +ui_hbox_begin :: proc( direction : UI_LayoutDirection_X, label : string, flags : UI_BoxFlags = {} ) -> (hbox : UI_HBox) { // profile(#procedure) hbox.direction = direction hbox.box = ui_box_make( flags, label ) @@ -150,7 +150,7 @@ ui_hbox_end :: proc( hbox : UI_HBox, width_ref : ^f32 = nil, compute_layout := t } @(deferred_out = ui_hbox_end_auto) -ui_hbox :: #force_inline proc( direction : UI_LayoutDirectionX, label : string, flags : UI_BoxFlags = {} ) -> (hbox : UI_HBox) { +ui_hbox :: #force_inline proc( direction : UI_LayoutDirection_X, label : string, flags : UI_BoxFlags = {} ) -> (hbox : UI_HBox) { hbox = ui_hbox_begin(direction, label, flags) ui_parent_push(hbox.box) return @@ -755,10 +755,10 @@ Keep in mind the stretch ratio is only respected if no size.min.y value is viola UI_VBox :: struct { using widget : UI_Widget, - direction : UI_LayoutDirectionY, + direction : UI_LayoutDirection_Y, } -ui_vbox_begin :: proc( direction : UI_LayoutDirectionY, label : string, flags : UI_BoxFlags = {}, compute_layout := false ) -> (vbox : UI_VBox) { +ui_vbox_begin :: proc( direction : UI_LayoutDirection_Y, label : string, flags : UI_BoxFlags = {}, compute_layout := false ) -> (vbox : UI_VBox) { // profile(#procedure) vbox.direction = direction vbox.box = ui_box_make( flags, label ) @@ -781,7 +781,7 @@ ui_vbox_end_pop_parent :: proc( vbox : UI_VBox ) { } @(deferred_out = ui_vbox_end_pop_parent) -ui_vbox :: #force_inline proc( direction : UI_LayoutDirectionY, label : string, flags : UI_BoxFlags = {}, compute_layout := false ) -> (vbox : UI_VBox) { +ui_vbox :: #force_inline proc( direction : UI_LayoutDirection_Y, label : string, flags : UI_BoxFlags = {}, compute_layout := false ) -> (vbox : UI_VBox) { vbox = ui_vbox_begin(direction, label, flags, compute_layout ) ui_parent_push(vbox.widget) return diff --git a/code/sectr/ui/window.odin b/code/sectr/ui/window.odin index 3ddab5e..2cd056f 100644 --- a/code/sectr/ui/window.odin +++ b/code/sectr/ui/window.odin @@ -3,7 +3,12 @@ package sectr UI_Window :: struct { frame : UI_Widget, - frame_bar : UI_HBox, + bar : UI_HBox, + children_box : struct #raw_union { + box : UI_Widget, + hbox : UI_HBox, + vbox : UI_VBox, + }, tile_text : UI_Widget, maximize_btn : UI_Widget, close_btn : UI_Widget, @@ -14,13 +19,38 @@ UI_Window :: struct is_maximized : b32, } -ui_window_begin :: proc( window : ^UI_Window, label : string, +// Same as UI_LayoutDirection_XY but children may have the option to have their container just be a sub-box +UI_Window_ChildLayout :: enum(i32) { + None, + Left_To_Right, + Right_to_Left, + Top_To_Bottom, + Bottom_To_Top, +} + +@(deferred_in=ui_window_end_auto) +ui_window :: proc (window : ^UI_Window, label : string, title : StrRunesPair = {}, closable := true, maximizable := true, draggable := true, - resizable := true -) -> (dragged, resized, maximized : b32) + resizable := true, + child_layout : UI_Window_ChildLayout = .None +) -> (dragged, resized, maximized, closed : b32) +{ + dragged, resized, maximized, closed = ui_window_begin(window, label, title, closable, maximizable, draggable, resizable, child_layout) + ui_parent_push(window.frame) + return +} + +ui_window_begin :: proc( window : ^UI_Window, label : string, + title : StrRunesPair = {}, + closable := true, + maximizable := true, + draggable := true, + resizable := true, + child_layout : UI_Window_ChildLayout = .None +) -> (dragged, resized, maximized, closed : b32) { using window if ! is_open do return @@ -34,9 +64,7 @@ ui_window_begin :: proc( window : ^UI_Window, label : string, if ! is_maximized { layout.flags = { - // .Size_To_Content, .Fixed_Width, .Fixed_Height, - // .Min_Size_To_Content_Y, .Fixed_Position_X, .Fixed_Position_Y, .Origin_At_Anchor_Center } @@ -53,42 +81,85 @@ ui_window_begin :: proc( window : ^UI_Window, label : string, resized = ui_resizable_handles( & frame, & position, & size) } - if len(title.str) == 0 && ! closable && ! maximizable && ! draggable do return - ui_parent(frame) + if len(title.str) > 0 || closable || maximizable || draggable { + dragged, maximized, closed = ui_window_bar(window, title, closable, maximizable, draggable) + } + + return +} + +ui_window_end :: proc (window : ^UI_Window) +{ +} + +ui_window_end_auto :: proc( window : ^UI_Window, label : string, + title : StrRunesPair = {}, + closable := true, + maximizable := true, + draggable := true, + resizable := true, + child_layout : UI_Window_ChildLayout = .None +) +{ + ui_parent_pop() +} + +ui_window_bar :: proc( window : ^UI_Window, + title : StrRunesPair = {}, + closable := true, + maximizable := true, + draggable := true, +) -> (dragged, maximized, closed : b32) +{ + using window + ui_parent(frame) draggable_flag : UI_BoxFlags = draggable ? {.Mouse_Clickable} : {} scope(theme_window_bar) - frame_bar = ui_hbox(.Left_To_Right, str_intern_fmt("%v.frame_bar", label). str, draggable_flag); - ui_parent(frame_bar) + bar = ui_hbox(.Left_To_Right, str_intern_fmt("%v.bar", frame.label). str, draggable_flag); + ui_parent(bar) - scope(theme_text) - tile_text = ui_text( str_intern_fmt("%v.title_text", label).str, title, {.Disabled}); { - using tile_text - layout.anchor.ratio.x = 1.0 - layout.margins = { 0, 0, 15, 0} - layout.font_size = 14 + if len(title.str) > 0 + { + scope(theme_text) + tile_text = ui_text( str_intern_fmt("%v.title_text", bar.label).str, title, {.Disabled}); { + using tile_text + layout.anchor.ratio.x = 1.0 + layout.margins = { 0, 0, 15, 0} + layout.font_size = 14 + + } } scope(theme_window_bar_btn) - maximize_btn = ui_button( str_intern_fmt("%v.maximize_btn", label).str ); { - using maximize_btn - if maximize_btn.pressed { - is_maximized = ~is_maximized - maximized = true + if maximizable + { + maximize_btn = ui_button( str_intern_fmt("%v.maximize_btn", bar.label).str ); { + using maximize_btn + if maximize_btn.pressed { + is_maximized = ~is_maximized + maximized = true + } + if is_maximized do text = str_intern("min") + else do text = str_intern("max") } - if is_maximized do text = str_intern("min") - else do text = str_intern("max") } - close_btn = ui_button( str_intern_fmt("%v.close_btn", label).str ); { - using close_btn - text = str_intern("close") - if close_btn.hot do style.bg_color = app_color_theme().window_btn_close_bg_hot - if close_btn.pressed do is_open = false - style.corner_radii = { 0, 0, 0, 0 } + if closable + { + close_btn = ui_button( str_intern_fmt("%v.close_btn", bar.label).str ); { + using close_btn + text = str_intern("close") + if close_btn.hot do style.bg_color = app_color_theme().window_btn_close_bg_hot + if close_btn.pressed { + is_open = false + closed = true + } + style.corner_radii = { 0, 0, 0, 0 } + } } - - if frame_bar.active { + + if bar.active { position += get_input_state().mouse.delta dragged = true }