diff --git a/code/engine_api.odin b/code/engine_api.odin index 326dc80..c95b237 100644 --- a/code/engine_api.odin +++ b/code/engine_api.odin @@ -195,7 +195,8 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem ui_startup( & screen_ui.base, cache_allocator = persistent_slab_allocator() ) using screen_ui - menu_bar.pos = Vec2(app_window.extent) * { -1, 1 } + menu_bar.pos = { -60, 0 } + // menu_bar.pos = Vec2(app_window.extent) * { -1, 1 } menu_bar.size = {200, 40} settings_menu.min_size = {250, 200} @@ -338,9 +339,9 @@ tick :: proc( host_delta_time : f64, host_delta_ns : Duration ) -> b32 rl.PollInputEvents() - debug.draw_ui_box_bounds_points = false - debug.draw_UI_padding_bounds = false - debug.draw_ui_content_bounds = false + debug.draw_ui_box_bounds_points = true + debug.draw_UI_padding_bounds = true + debug.draw_ui_content_bounds = true should_close = update( host_delta_time ) render() @@ -351,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/grime_grime.odin b/code/grime_grime.odin index 60c5e96..7f2d851 100644 --- a/code/grime_grime.odin +++ b/code/grime_grime.odin @@ -1,6 +1,8 @@ package sectr +#region("Import Aliases") + import "base:builtin" copy :: builtin.copy import "base:intrinsics" @@ -95,13 +97,17 @@ import "thirdparty:backtrace" stacktrace :: backtrace.trace stacktrace_lines :: backtrace.lines +#endregion("Import Aliases") + OS_Type :: type_of(ODIN_OS) + swap :: proc( a, b : ^ $Type ) -> ( ^ Type, ^ Type ) { return b, a } -// Proc Name Overloads Alias table +#region("Proc overload mappings") + // This has to be done on a per-module basis. add :: proc { @@ -298,3 +304,5 @@ wedge :: proc { wedge_vec3, wedge_bivec3, } + +#endregion("Proc overload mappings") diff --git a/code/grime_linked_list.odin b/code/grime_linked_list.odin index a3a9565..a4073c8 100644 --- a/code/grime_linked_list.odin +++ b/code/grime_linked_list.odin @@ -171,6 +171,9 @@ dll_full_pop :: proc "contextless" ( node, parent : ^$Type ) { if parent.last == node { parent.last = node.prev } + if parent.first == parent.last { + parent.last = nil + } if node.prev != nil { node.prev.next = nil node.prev = nil diff --git a/code/tick_render.odin b/code/tick_render.odin index 6c9454d..c7fe880 100644 --- a/code/tick_render.odin +++ b/code/tick_render.odin @@ -110,16 +110,21 @@ render_mode_2d_workspace :: proc() profile("Imgui Render") ui := & state.project.workspace.ui root := ui.root - if root.num_children == 0 { + if root == nil || root.num_children == 0 { break ImguiRender } state.ui_context = ui current := root.first - for ; current != nil; current = ui_box_tranverse_next( current ) + for ; current != nil; current = ui_box_tranverse_next( current, is_destructive = true ) { // profile("Box") parent := current.parent + if parent == ui.root && current.ancestors == -1 { + // This is a deceased rooted box + // Ignore it as its not constructed this frame + continue + } layout := current.layout style := current.style @@ -133,7 +138,7 @@ render_mode_2d_workspace :: proc() // TODO(Ed) : Render Borders - // profile_begin("Calculating Raylib rectangles") + // profile_begin("Calculating Raylib rectangles") // render_anchors := range2( // ws_view_to_render_pos(computed.anchors.min), // ws_view_to_render_pos(computed.anchors.max), @@ -160,44 +165,44 @@ render_mode_2d_workspace :: proc() rect_bounds := range2_to_rl_rect( render_bounds ) rect_padding := range2_to_rl_rect( render_padding ) rect_content := range2_to_rl_rect( render_content ) - // profile_end() + // profile_end() - // profile_begin("rl.DrawRectangleRounded( rect_bounds, style.layout.corner_radii[0], 9, style.bg_color )") - if style.bg_color.a != 0 - { - draw_rectangle( rect_bounds, current ) - } - if layout.border_width > 0 { - draw_rectangle_lines( rect_bounds, current, style.border_color, layout.border_width ) - } - // profile_end() + // profile_begin("rl.DrawRectangleRounded( rect_bounds, style.layout.corner_radii[0], 9, style.bg_color )") + if style.bg_color.a != 0 + { + draw_rectangle( rect_bounds, current ) + } + if layout.border_width > 0 { + draw_rectangle_lines( rect_bounds, current, style.border_color, layout.border_width ) + } + // profile_end() line_thickness := 1 * cam_zoom_ratio - // profile_begin("rl.DrawRectangleRoundedLines: padding & content") - if debug.draw_UI_padding_bounds && equal_range2(computed.content, computed.padding) { - draw_rectangle_lines( rect_padding, current, Color_Debug_UI_Padding_Bounds, line_thickness ) - } - else if debug.draw_ui_content_bounds { - draw_rectangle_lines( rect_content, current, Color_Debug_UI_Content_Bounds, line_thickness ) - } - // profile_end() + // profile_begin("rl.DrawRectangleRoundedLines: padding & content") + if debug.draw_UI_padding_bounds && equal_range2(computed.content, computed.padding) { + draw_rectangle_lines( rect_padding, current, Color_Debug_UI_Padding_Bounds, line_thickness ) + } + else if debug.draw_ui_content_bounds { + draw_rectangle_lines( rect_content, current, Color_Debug_UI_Content_Bounds, line_thickness ) + } + // profile_end() point_radius := 3 * cam_zoom_ratio - // profile_begin("circles") - if debug.draw_ui_box_bounds_points - { - // center := Vec2 { - // render_bounds.p0.x + computed_size.x * 0.5, - // render_bounds.p0.y - computed_size.y * 0.5, - // } - // rl.DrawCircleV( center, point_radius, Color_White ) + // profile_begin("circles") + if debug.draw_ui_box_bounds_points + { + // center := Vec2 { + // render_bounds.p0.x + computed_size.x * 0.5, + // render_bounds.p0.y - computed_size.y * 0.5, + // } + // rl.DrawCircleV( center, point_radius, Color_White ) - rl.DrawCircleV( render_bounds.p0, point_radius, Color_Red ) - rl.DrawCircleV( render_bounds.p1, point_radius, Color_Blue ) - } - // profile_end() + rl.DrawCircleV( render_bounds.p0, point_radius, Color_Red ) + rl.DrawCircleV( render_bounds.p1, point_radius, Color_Blue ) + } + // profile_end() if len(current.text.str) > 0 { ws_view_draw_text( current.text, ws_view_to_render_pos(computed.text_pos * {1, -1}), layout.font_size, style.text_color ) @@ -339,10 +344,15 @@ render_screen_ui :: proc() // Sort roots children by top-level order current := root.first - for ; current != nil; current = ui_box_tranverse_next( current ) + for ; current != nil; current = ui_box_tranverse_next( current, is_destructive = true ) { // profile("Box") parent := current.parent + if parent == ui.root && current.ancestors == -1 { + // This is a deceased rooted box + // Ignore it as its not constructed this frame + // continue + } style := current.style layout := current.layout @@ -375,28 +385,28 @@ render_screen_ui :: proc() rect_bounds := range2_to_rl_rect( render_bounds ) rect_padding := range2_to_rl_rect( render_padding ) rect_content := range2_to_rl_rect( render_content ) - // profile_end() + // profile_end() - // profile_begin("rl.DrawRectangleRounded( rect_bounds, style.layout.corner_radii[0], 9, style.bg_color )") - if style.bg_color.a != 0 - { - draw_rectangle( rect_bounds, current ) - } - if layout.border_width > 0 { - draw_rectangle_lines( rect_bounds, current, style.border_color, layout.border_width ) - } - // profile_end() + // profile_begin("rl.DrawRectangleRounded( rect_bounds, style.layout.corner_radii[0], 9, style.bg_color )") + if style.bg_color.a != 0 + { + draw_rectangle( rect_bounds, current ) + } + if layout.border_width > 0 { + draw_rectangle_lines( rect_bounds, current, style.border_color, layout.border_width ) + } + // profile_end() line_thickness : f32 = 1 - // profile_begin("rl.DrawRectangleRoundedLines: padding & content") - if debug.draw_UI_padding_bounds && equal_range2(computed.content, computed.padding) { - draw_rectangle_lines( rect_padding, current, Color_Debug_UI_Padding_Bounds, line_thickness ) - } - else if debug.draw_ui_content_bounds { - draw_rectangle_lines( rect_content, current, Color_Debug_UI_Content_Bounds, line_thickness ) - } - // profile_end() + // profile_begin("rl.DrawRectangleRoundedLines: padding & content") + if debug.draw_UI_padding_bounds && equal_range2(computed.content, computed.padding) { + draw_rectangle_lines( rect_padding, current, Color_Debug_UI_Padding_Bounds, line_thickness ) + } + else if debug.draw_ui_content_bounds { + draw_rectangle_lines( rect_content, current, Color_Debug_UI_Content_Bounds, line_thickness ) + } + // profile_end() // if .Mouse_Resizable in current.flags // { @@ -422,19 +432,19 @@ render_screen_ui :: proc() point_radius : f32 = 3 - // profile_begin("circles") - if debug.draw_ui_box_bounds_points - { - // center := Vec2 { - // render_bounds.p0.x + computed_size.x * 0.5, - // render_bounds.p0.y - computed_size.y * 0.5, - // } - // rl.DrawCircleV( center, point_radius, Color_White ) + // profile_begin("circles") + if debug.draw_ui_box_bounds_points + { + // center := Vec2 { + // render_bounds.p0.x + computed_size.x * 0.5, + // render_bounds.p0.y - computed_size.y * 0.5, + // } + // rl.DrawCircleV( center, point_radius, Color_White ) - rl.DrawCircleV( render_bounds.p0, point_radius, Color_Red ) - rl.DrawCircleV( render_bounds.p1, point_radius, Color_Blue ) - } - // profile_end() + rl.DrawCircleV( render_bounds.p0, point_radius, Color_Red ) + rl.DrawCircleV( render_bounds.p1, point_radius, Color_Blue ) + } + // profile_end() if len(current.text.str) > 0 && style.font.key != 0 { draw_text_screenspace( current.text, screen_to_render_pos(computed.text_pos), layout.font_size, style.text_color ) diff --git a/code/tick_update.odin b/code/tick_update.odin index a902e15..66b1802 100644 --- a/code/tick_update.odin +++ b/code/tick_update.odin @@ -200,6 +200,7 @@ update :: proc( delta_time : f64 ) -> b32 ui_screen_tick() //region WorkspaceImgui Tick + if false { profile("Workspace Imgui") diff --git a/code/ui_signal.odin b/code/ui_signal.odin index ea3a951..50cf9e8 100644 --- a/code/ui_signal.odin +++ b/code/ui_signal.odin @@ -72,9 +72,52 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas if mouse_clickable && signal.cursor_over && left_pressed && was_hot { top_ancestor := ui_top_ancestor(box) - - dll_full_pop(top_ancestor, top_ancestor.parent) - dll_full_push_back( top_ancestor.parent, top_ancestor, nil ) + if ui.root.last != top_ancestor && false + { + // dll_full_pop(top_ancestor, top_ancestor.parent) + // dll_full_push_back( top_ancestor.parent, top_ancestor, nil ) + + left := top_ancestor.prev + right := top_ancestor.next + + if left != nil { + left.next = top_ancestor.prev + } + else { + // We are the first box on root, + ui.root.first = right + } + // right should never be null since top_ancestor is not the last node + right.prev = left + + if ui.root.last != nil + { + // ui.root.last - > top_ancestor + ui.root.last.next = top_ancestor + top_ancestor.prev = ui.root.last + top_ancestor.next = nil + } + else + { + // vvv + // ui.root.first - > ui.root.last + ui.root.last = top_ancestor + ui.root.first.next = top_ancestor + top_ancestor.prev = ui.root.first + top_ancestor.next = nil + } + ui.root.last = top_ancestor + + for curr := right; curr != nil; curr = curr.next { + curr.parent_index -= 1 + } + + // Fix up left & right references + // if left != nil && right != nil { + // right.prev = left + // left.next = right + // } + } // runtime.debug_trap() // ui.hot = box.key diff --git a/code/ui_ui.odin b/code/ui_ui.odin index 40d346b..8d61398 100644 --- a/code/ui_ui.odin +++ b/code/ui_ui.odin @@ -113,7 +113,7 @@ UI_Box :: struct { using links : DLL_NodeFull( UI_Box ), // first, last, prev, next parent : ^UI_Box, num_children : i32, - ancestors : i32, + ancestors : i32, // This value for rooted widgets gets set to -1 after rendering see ui_box_make() for the reason. parent_index : i32, flags : UI_BoxFlags, @@ -157,7 +157,8 @@ UI_State :: struct { prev_cache : ^HMapZPL( UI_Box ), curr_cache : ^HMapZPL( UI_Box ), - null_box : ^UI_Box, // Ryan had this, I don't know why yet. + null_box : ^UI_Box, // This was used with the Linked list interface... + // TODO(Ed): Should we change our convention for null boxes to use the above and nil as an invalid state? root : ^UI_Box, // Children of the root node are unique in that they have their order preserved per frame // This is to support overlapping frames @@ -239,7 +240,17 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box) key := ui_key_from_string( label ) + links_perserved : DLL_NodeFull( UI_Box ) + curr_box : (^ UI_Box) + curr_box = zpl_hmap_get( curr_cache, cast(u64) key ) + if curr_box != nil && curr_box.ancestors == 1 { + // top_ancestor has had its neighboring links updated this frame + // preserve them from the refresh + links_perserved.prev = curr_box.links.prev + links_perserved.next = curr_box.links.next + } + prev_box := zpl_hmap_get( prev_cache, cast(u64) key ) { // profile("Assigning current box") @@ -269,15 +280,12 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box) // Clear non-persistent data curr_box.computed.fresh = false - curr_box.links = {} + curr_box.links = links_perserved curr_box.num_children = 0 - curr_box.parent = nil - // curr_box.ancestors = 0 - curr_box.parent_index = -1 // If there is a parent, setup the relevant references parent := stack_peek( & parent_stack ) - if curr_box.ancestors == 0 && prev_box != nil + if parent == nil && ! curr_box.first_frame { set_error : AllocatorError if prev_box.first != nil { @@ -288,12 +296,36 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box) curr_box.last, set_error = zpl_hmap_set( curr_cache, cast(u64) prev_box.last.key, prev_box.last ^ ) verify( set_error == AllocatorError.None, "Failed to set zpl_hmap due to allocator error" ) } + curr_box.ancestors = 0 } if parent != nil { - if curr_box.ancestors != 1 + if parent != ui.root || curr_box.first_frame { - dll_full_push_back( parent, curr_box, null_box ) + // Only occurs when this is no prior history for rooted boxes + // Otherwise regular children always complete this + // dll_full_push_back( parent, curr_box, nil ) + when true + { + // | + // v + // parent.first + if parent.first == nil { + parent.first = curr_box + parent.last = curr_box + curr_box.next = nil + curr_box.prev = nil + } + else { + // Positin is set to last, insert at end + // curr_box + parent.last.next = curr_box + curr_box.prev = parent.last + parent.last = curr_box + curr_box.next = nil + } + } + curr_box.parent_index = parent.num_children parent.num_children += 1 curr_box.parent = parent @@ -301,16 +333,18 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box) } else if prev_box != nil { - // Order was previously restored, restore linkage - if prev_box.prev != nil { - curr_box.prev = zpl_hmap_get( curr_cache, cast(u64) prev_box.prev.key ) + // Make only todo if links are properly wiped on current + set_error : AllocatorError + if curr_box.prev == nil && prev_box.prev != nil { + 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 prev_box.next != nil { - curr_box.next = zpl_hmap_get( curr_cache, cast(u64) prev_box.next.key ) + if curr_box.next == nil && prev_box.next != nil { + 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" ) } - curr_box.parent = ui.root + curr_box.parent = parent curr_box.ancestors = 1 - curr_box.parent_index = ui.root.num_children parent.num_children += 1 } } @@ -319,8 +353,17 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box) return curr_box } -ui_box_tranverse_next :: proc "contextless" ( box : ^ UI_Box ) -> (^ UI_Box) +ui_box_tranverse_next :: proc "contextless" ( box : ^ UI_Box, is_destructive : b32 = false ) -> (^ UI_Box) { + parent := box.parent + + // Marking this box as deceased with no position in the box graph + if is_destructive { + // box.parent = nil + box.num_children = -1 + box.ancestors = -1 + } + // Check to make sure parent is present on the screen, if its not don't bother. // If current has children, do them first using state := get_state() @@ -336,9 +379,10 @@ ui_box_tranverse_next :: proc "contextless" ( box : ^ UI_Box ) -> (^ UI_Box) if box.next == nil { // There is no more adjacent nodes - if box.parent != nil { + if box.parent != nil + { // Lift back up to parent, and set it to its next. - return box.parent.next + return parent.next } } @@ -395,7 +439,6 @@ ui_graph_build_begin :: proc( ui : ^ UI_State, bounds : Vec2 = {} ) ui_parent_push(root) } -// TODO(Ed) :: Is this even needed? ui_graph_build_end :: proc( ui : ^UI_State ) { profile(#procedure)