diff --git a/SectrPrototype.code-workspace b/SectrPrototype.code-workspace index 639301e..349af55 100644 --- a/SectrPrototype.code-workspace +++ b/SectrPrototype.code-workspace @@ -5,6 +5,9 @@ }, { "path": "scripts" + }, + { + "path": "." } ], "settings": { diff --git a/code/engine_api.odin b/code/engine_api.odin index c95b237..358adaa 100644 --- a/code/engine_api.odin +++ b/code/engine_api.odin @@ -352,8 +352,8 @@ tick :: proc( host_delta_time : f64, host_delta_ns : Duration ) -> b32 // Timing { // profile("Client tick timing processing") - // config.engine_refresh_hz = uint(monitor_refresh_hz) - config.engine_refresh_hz = 10 + config.engine_refresh_hz = uint(monitor_refresh_hz) + // config.engine_refresh_hz = 10 frametime_target_ms = 1.0 / f64(config.engine_refresh_hz) * S_To_MS sub_ms_granularity_required := frametime_target_ms <= Frametime_High_Perf_Threshold_MS diff --git a/code/tick_render.odin b/code/tick_render.odin index c7fe880..c04ddb2 100644 --- a/code/tick_render.odin +++ b/code/tick_render.odin @@ -344,14 +344,17 @@ render_screen_ui :: proc() // Sort roots children by top-level order current := root.first - for ; current != nil; current = ui_box_tranverse_next( current, is_destructive = true ) + for ; current != nil; current = ui_box_tranverse_next( current, is_destructive = false ) { // profile("Box") parent := current.parent if parent == ui.root && current.ancestors == -1 { + // current.parent = nil + // current.first = nil + // current.last = nil // This is a deceased rooted box // Ignore it as its not constructed this frame - // continue + continue } style := current.style @@ -408,28 +411,6 @@ render_screen_ui :: proc() } // profile_end() - // if .Mouse_Resizable in current.flags - // { - // // profile("Resize Bounds") - // resize_border_width := cast(f32) get_state().config.ui_resize_border_width - // resize_percent_width := computed_size * (resize_border_width * 1.0/ 200.0) - // resize_border_non_range := add(current.computed.bounds, range2( - // { resize_percent_width.x, -resize_percent_width.x }, - // { -resize_percent_width.x, resize_percent_width.x })) - - // render_resize := range2( - // resize_border_non_range.min, - // resize_border_non_range.max, - // ) - // rect_resize := rl.Rectangle { - // render_resize.min.x, - // render_resize.min.y, - // render_resize.max.x - render_resize.min.x, - // render_resize.max.y - render_resize.min.y, - // } - // draw_rectangle_lines( rect_padding, current, Color_Red, line_thickness ) - // } - point_radius : f32 = 3 // profile_begin("circles") diff --git a/code/ui_layout.odin b/code/ui_layout.odin index a9dadcc..296dda5 100644 --- a/code/ui_layout.odin +++ b/code/ui_layout.odin @@ -6,14 +6,15 @@ import "core:math/linalg" // The UI_Box's actual positioning and sizing // There is an excess of rectangles here for debug puproses. UI_Computed :: struct { - fresh : b32, // If the auto-layout has been computed for the current frame // anchors : Range2, // Bounds for anchors within parent // margins : Range2, // Bounds for margins within parent + padding : Range2, // Bounds for padding's starting bounds (will be offset by border if there is one), only here for debug vis + bounds : Range2, // Bounds for box itself - padding : Range2, // Bounds for padding's starting bounds (will be offset by border if there is one) content : Range2, // Bounds for content (text or children) text_pos : Vec2, // Position of text within content text_size : Vec2, // Size of text within content + fresh : b32, // If the auto-layout has been computed for the current frame } UI_LayoutDirectionX :: enum(i32) { @@ -73,6 +74,7 @@ UI_LayoutFlag :: enum u32 { Size_To_Text, // TODO(Ed): Implement this! + // Wrap text around the box, text_alignment specifies the justification for its compostion when wrapping. Text_Wrap, Count, diff --git a/code/ui_layout_compute.odin b/code/ui_layout_compute.odin index f6c9050..c0eb741 100644 --- a/code/ui_layout_compute.odin +++ b/code/ui_layout_compute.odin @@ -1,14 +1,15 @@ package sectr -// Note(Ed): This is naturally pretty expensive - -ui_box_compute_layout :: proc( box : ^UI_Box ) +ui_box_compute_layout :: proc( box : ^UI_Box, + ancestors_layout_required : b32 = false, + root_layout_required : b32 = false ) { profile("Layout Box") state := get_state() ui := state.ui_context parent := box.parent + // TODO(Ed): Add support to premmtively compute ancestor's layouts // if parent != ui.root && ! parent.computed.fresh { // ui_box_compute_layout( parent ) // } @@ -45,6 +46,9 @@ ui_box_compute_layout :: proc( box : ^UI_Box ) If fixed size (X or Y): * Ignore Parent constraints (can only be clipped) + If an axis is auto-sized by a ratio of the other axis + * + If auto-sized: * Enforce parent size constraint of bounds relative to where the adjusted content bounds are after applying margins & anchors. diff --git a/code/ui_signal.odin b/code/ui_signal.odin index 50cf9e8..9b37a07 100644 --- a/code/ui_signal.odin +++ b/code/ui_signal.odin @@ -69,10 +69,11 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas // runtime.debug_trap() } + // Check to see if this box is active if mouse_clickable && signal.cursor_over && left_pressed && was_hot { top_ancestor := ui_top_ancestor(box) - if ui.root.last != top_ancestor && false + if ui.root.last != top_ancestor { // dll_full_pop(top_ancestor, top_ancestor.parent) // dll_full_push_back( top_ancestor.parent, top_ancestor, nil ) @@ -81,7 +82,7 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas right := top_ancestor.next if left != nil { - left.next = top_ancestor.prev + left.next = top_ancestor.next } else { // We are the first box on root, @@ -92,10 +93,17 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas if ui.root.last != nil { - // ui.root.last - > top_ancestor - ui.root.last.next = top_ancestor - top_ancestor.prev = ui.root.last + prev_last := ui.root.last + ui.root.last = top_ancestor + + prev_last.next = top_ancestor + top_ancestor.prev = prev_last top_ancestor.next = nil + if left == nil && right == prev_last + { + right.prev = nil + right.next = top_ancestor + } } else { @@ -105,8 +113,8 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas ui.root.first.next = top_ancestor top_ancestor.prev = ui.root.first top_ancestor.next = nil + ui.root.last = top_ancestor } - ui.root.last = top_ancestor for curr := right; curr != nil; curr = curr.next { curr.parent_index -= 1 @@ -147,26 +155,26 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas // TODO(Ed) : Add keyboard interaction support } + // TODO(Ed): Should panning and scrolling get supported here? (problably not...) // TODO(Ed) : Add scrolling support - if UI_BoxFlag.Scroll_X in box.flags { + // if UI_BoxFlag.Scroll_X in box.flags { - } - if UI_BoxFlag.Scroll_Y 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_X in box.flags { - } - if UI_BoxFlag.Pan_Y 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 + // TODO(Ed): It should be able to enter hot without mouse_clickable if mouse_clickable && signal.cursor_over && ! is_disabled { hot_vacant := ui.hot == UI_Key(0) diff --git a/code/ui_style.odin b/code/ui_style.odin index 3ec26bc..18ccacd 100644 --- a/code/ui_style.odin +++ b/code/ui_style.odin @@ -1,5 +1,6 @@ package sectr +// TODO(Ed): We problably can embedd this info into the UI_Layout with the regular text_alignment UI_TextAlign :: enum u32 { Left, Center, @@ -20,7 +21,7 @@ UI_Style :: struct { border_color : Color, // TODO(Ed): We cannot support individual corners unless we add it to raylib (or finally change the rendering backend) - corner_radii : [Corner.Count]f32, + corner_radii : [Corner.Count]f32, // TODO(Ed) : Add support for this eventually blur_size : f32, @@ -31,9 +32,8 @@ UI_Style :: struct { // TODO(Ed): Add support for custom shader // shader : UI_Shader, - font : FontID, - // TODO(Ed): Should this get moved to the layout struct? Techncially font-size is mainly - text_color : Color, + font : FontID, + text_color : Color, // TODO(Ed) : Support setting the cursor state cursor : UI_Cursor, diff --git a/code/ui_tests.odin b/code/ui_tests.odin index abdcb2a..1f80ea7 100644 --- a/code/ui_tests.odin +++ b/code/ui_tests.odin @@ -42,7 +42,7 @@ test_draggable :: proc() corner_radii = { 0.3, 0.3, 0.3, 0.3 }, }) - draggable := ui_widget( "Draggable Box!", UI_BoxFlags { .Mouse_Clickable, .Mouse_Resizable } ) + draggable := ui_widget( "Draggable Box!", UI_BoxFlags { .Mouse_Clickable } ) if draggable.first_frame { debug.draggable_box_pos = draggable.layout.pos + { 0, -100 } debug.draggable_box_size = draggable.layout.size.min @@ -87,7 +87,7 @@ test_parenting :: proc( default_layout : ^UI_Layout, frame_style_default : ^UI_S parent_style := frame_style_default ^ ui_style(parent_style) - parent := ui_widget( "Parent", { .Mouse_Clickable, .Mouse_Resizable }) + parent := ui_widget( "Parent", { .Mouse_Clickable }) ui_parent_push(parent) { if parent.first_frame { diff --git a/code/ui_ui.odin b/code/ui_ui.odin index 8d61398..9a46fc3 100644 --- a/code/ui_ui.odin +++ b/code/ui_ui.odin @@ -59,22 +59,19 @@ UI_BoxFlag :: enum u64 { Click_To_Focus, Mouse_Clickable, - Mouse_Resizable, - Keyboard_Clickable, - Scroll_X, - Scroll_Y, + // Pan_X, + // Pan_Y, - Pan_X, - Pan_Y, - - Screenspace, + // Scroll_X, + // Scroll_Y, + // Screenspace, Count, } UI_BoxFlags :: bit_set[UI_BoxFlag; u64] -UI_BoxFlag_Scroll :: UI_BoxFlags { .Scroll_X, .Scroll_Y } +// UI_BoxFlag_Scroll :: UI_BoxFlags { .Scroll_X, .Scroll_Y } UI_Cursor :: struct { placeholder : int, @@ -110,7 +107,10 @@ UI_Box :: struct { text : StrRunesPair, // Regenerated per frame. - using links : DLL_NodeFull( UI_Box ), // first, last, prev, next + + // first, last : The first and last child of this box + // prev, next : The adjacent neighboring boxes who are children of to the same parent + using links : DLL_NodeFull( UI_Box ), parent : ^UI_Box, num_children : i32, ancestors : i32, // This value for rooted widgets gets set to -1 after rendering see ui_box_make() for the reason. @@ -126,7 +126,7 @@ UI_Box :: struct { style : UI_Style, // Persistent Data - first_frame : b8, + first_frame : b8, // TODO(Ed): Move to end if keeping as b8 hot_delta : f32, active_delta : f32, disabled_delta : f32, @@ -148,8 +148,8 @@ UI_Built_Boxes_Array_Size :: 16 * Kilobyte UI_State :: struct { // TODO(Ed) : Use these - build_arenas : [2]Arena, - build_arena : ^ Arena, + // build_arenas : [2]Arena, + // build_arena : ^ Arena, built_box_count : i32, @@ -165,7 +165,7 @@ UI_State :: struct { // So long as their parent-index is non-negative they'll be rendered // Do we need to recompute the layout? - layout_dirty : b32, + // layout_dirty : b32, // TODO(Ed) : Look into using a build arena like Ryan does for these possibly (and thus have a linked-list stack) layout_combo_stack : StackFixed( UI_LayoutCombo, UI_Style_Stack_Size ), @@ -300,7 +300,10 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box) } if parent != nil { - if parent != ui.root || curr_box.first_frame + // There are three possible reasons to just add as usual: + // 1. Its not rooted, which means we don't track order + // 2. + if parent != ui.root || curr_box.first_frame //|| (prev_box.prev == nil && prev_box.next == nil) { // Only occurs when this is no prior history for rooted boxes // Otherwise regular children always complete this @@ -335,11 +338,11 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box) { // Make only todo if links are properly wiped on current set_error : AllocatorError - if curr_box.prev == nil && prev_box.prev != nil { + if curr_box.prev == nil && prev_box.prev != nil && prev_box.ancestors == 1 { curr_box.prev, set_error = zpl_hmap_set( curr_cache, cast(u64) prev_box.prev.key, prev_box.prev ^ ) verify( set_error == AllocatorError.None, "Failed to set zpl_hmap due to allocator error" ) } - if curr_box.next == nil && prev_box.next != nil { + if curr_box.next == nil && prev_box.next != nil && prev_box.ancestors == 1 { curr_box.next, set_error = zpl_hmap_set( curr_cache, cast(u64) prev_box.next.key, prev_box.next ^ ) verify( set_error == AllocatorError.None, "Failed to set zpl_hmap due to allocator error" ) }