diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..d913c39 --- /dev/null +++ b/Readme.md @@ -0,0 +1,12 @@ +# Sectr Prototype + +This prototype aims to flesh out ideas I've wanted to explore futher when it came to code editing and tools for code in general. + +The project is so far in a "codebase boostrapping" phase. + +The code is organized into 2 modules sectr_host & sectr. +The host module loads the main module & its memory. Hot-reloading it's dll when it detects a change. + +The main module only depends on libraries provided by odin repo's base, core, or vendor related packages, and a ini-parsing library. + + diff --git a/code/api.odin b/code/api.odin index 8c94c94..e760a75 100644 --- a/code/api.odin +++ b/code/api.odin @@ -80,6 +80,23 @@ startup :: proc( persistent_mem, frame_mem, transient_mem, files_buffer_mem : ^V input = & input_data[1] input_prev = & input_data[0] + // Configuration Load + { + using config + resolution_width = 1000 + resolution_height = 600 + refresh_rate = 0 + + cam_min_zoom = 0.25 + cam_max_zoom = 10.0 + cam_zoom_mode = .Smooth + cam_zoom_smooth_snappiness = 4.0 + cam_zoom_sensitivity_digital = 0.2 + cam_zoom_sensitivity_smooth = 4.0 + + ui_resize_border_width = 20 + } + // rl.Odin_SetMalloc( RL_MALLOC ) rl.SetConfigFlags( { @@ -119,15 +136,6 @@ startup :: proc( persistent_mem, frame_mem, transient_mem, files_buffer_mem : ^V path_firacode := strings.concatenate( { Path_Assets, "FiraCode-Regular.ttf" }, frame_allocator() ) font_firacode = font_load( path_firacode, 24.0, "FiraCode" ) - - // font_data, read_succeded : = os.read_entire_file( path_rec_mono_semicasual_reg ) - // verify( read_succeded, fmt.tprintf("Failed to read font file for: %v", path_rec_mono_semicasual_reg) ) - - // cstr := strings.clone_to_cstring( path_rec_mono_semicasual_reg ) - // font_rec_mono_semicasual_reg = rl.LoadFontEx( cstr, cast(i32) points_to_pixels(24.0), nil, 0 ) - // delete( cstr) - - // rl.GuiSetFont( font_rec_mono_semicasual_reg ) // TODO(Ed) : Does this do anything? default_font = font_firacode log( "Default font loaded" ) } @@ -198,8 +206,8 @@ reload :: proc( persistent_mem, frame_mem, transient_mem, files_buffer_mem : ^VA // Thankfully persistent dynamic allocations are rare, and thus we know exactly which ones they are. font_provider_data := & get_state().font_provider_data - // font_provide_data.font_cache.hashes.allocator = slab_allocator() - // font_provide_data.font_cache.entries.allocator = slab_allocator() + font_provider_data.font_cache.hashes.allocator = general_slab_allocator() + font_provider_data.font_cache.entries.allocator = general_slab_allocator() ui_reload( & get_state().project.workspace.ui, cache_allocator = general_slab_allocator() ) @@ -217,7 +225,9 @@ tick :: proc( delta_time : f64, delta_ns : Duration ) -> b32 context.allocator = frame_allocator() context.temp_allocator = transient_allocator() - get_state().frametime_delta_ns = delta_ns + state := get_state() + state.frametime_delta_ns = delta_ns + state.frametime_delta_seconds = delta_time result := update( delta_time ) render() diff --git a/code/colors.odin b/code/colors.odin index 177d317..940c2a3 100644 --- a/code/colors.odin +++ b/code/colors.odin @@ -10,10 +10,10 @@ Color_White :: rl.WHITE Color_Transparent :: Color { 0, 0, 0, 0 } Color_BG :: Color { 41, 41, 45, 255 } -Color_BG_TextBox :: Color { 32, 32, 32, 255 } +Color_BG_TextBox :: Color { 32, 32, 32, 180 } Color_BG_TextBox_Green :: Color { 102, 102, 110, 255 } Color_Frame_Disabled :: Color { 22, 22, 22, 120 } -Color_Frame_Hover :: Color { 122, 122, 125, 255 } -Color_Frame_Select :: Color { 188, 188, 188, 255 } +Color_Frame_Hover :: Color { 122, 122, 125, 200 } +Color_Frame_Select :: Color { 188, 188, 188, 220 } Color_GreyRed :: Color { 220, 100, 100, 125 } Color_White_A125 :: Color { 255, 255, 255, 125 } diff --git a/code/env.odin b/code/env.odin index ac26261..35277bf 100644 --- a/code/env.odin +++ b/code/env.odin @@ -118,8 +118,15 @@ AppConfig :: struct { resolution_width : uint, resolution_height : uint, refresh_rate : uint, - min_zoom : uint, - max_zoom : uint, + + cam_min_zoom : f32, + cam_max_zoom : f32, + cam_zoom_mode : CameraZoomMode, + cam_zoom_smooth_snappiness : f32, + cam_zoom_sensitivity_smooth : f32, + cam_zoom_sensitivity_digital : f32, + + ui_resize_border_width : uint, } State :: struct { @@ -144,7 +151,8 @@ State :: struct { engine_refresh_hz : i32, engine_refresh_target : i32, - frametime_delta_ns : Duration, + frametime_delta_seconds : f64, + frametime_delta_ns : Duration, font_firacode : FontID, font_squidgy_slimes : FontID, @@ -190,7 +198,8 @@ Project :: struct { Workspace :: struct { name : string, - cam : Camera, + cam : Camera, + zoom_target : f32, // TODO(Ed) : The workspace is mainly a 'UI' conceptually... ui : UI_State, @@ -207,7 +216,15 @@ DebugData :: struct { mouse_vis : b32, last_mouse_pos : Vec2, - zoom_target : f32, - + // Test First frame_2_created : b32, + + // Test Draggable + draggable_box_pos : Vec2, + draggable_box_size : Vec2, + box_original_size : Vec2, + box_resize_started : b32, + + ui_drag_delta : Vec2, + ui_drag_start : Vec2, } diff --git a/code/font_provider.odin b/code/font_provider.odin index ef5a0e0..6ffad7d 100644 --- a/code/font_provider.odin +++ b/code/font_provider.odin @@ -47,7 +47,10 @@ FontGlyphsRender :: struct { FontDef :: struct { path_file : string, - data : [] u8, + + // TODO(Ed) : you may have to store font data in the future if we render on demand + // data : []u8, + default_size : i32, size_table : [Font_Largest_Px_Size / 2] FontGlyphsRender, } @@ -62,8 +65,7 @@ font_provider_startup :: proc() font_provider_data := & get_state().font_provider_data; using font_provider_data font_cache_alloc_error : AllocatorError - - font_cache, font_cache_alloc_error = zpl_hmap_init_reserve( FontDef, general_slab_allocator(), 8 ) + font_cache, font_cache_alloc_error = zpl_hmap_init_reserve( FontDef, general_slab_allocator(), 2 ) verify( font_cache_alloc_error == AllocatorError.None, "Failed to allocate font_cache" ) log("font_cache created") @@ -94,7 +96,7 @@ font_load :: proc( path_file : string, { font_provider_data := & get_state().font_provider_data; using font_provider_data - font_data, read_succeded : = os.read_entire_file( path_file, general_slab_allocator() ) + font_data, read_succeded : = os.read_entire_file( path_file, context.temp_allocator ) verify( b32(read_succeded), str_fmt_tmp("Failed to read font file for: %v", path_file) ) font_data_size := cast(i32) len(font_data) @@ -117,7 +119,7 @@ font_load :: proc( path_file : string, verify( set_error == AllocatorError.None, "Failed to add new font entry to cache" ) def.path_file = path_file - def.data = font_data + // def.data = font_data def.default_size = i32(points_to_pixels(default_size)) // TODO(Ed): this is slow & eats quite a bit of memory early on. Setup a more on demand load for a specific size. diff --git a/code/grime.odin b/code/grime.odin index ec06813..12f7fe8 100644 --- a/code/grime.odin +++ b/code/grime.odin @@ -80,6 +80,10 @@ OS_Type :: type_of(ODIN_OS) // Proc Name Overloads Alias table // This has to be done on a per-module basis. +add :: proc { + add_range2, +} + cm_to_pixels :: proc { f32_cm_to_pixels, vec2_cm_to_pixels, diff --git a/code/grime_hashmap_zpl.odin b/code/grime_hashmap_zpl.odin index bc0bbcf..234d773 100644 --- a/code/grime_hashmap_zpl.odin +++ b/code/grime_hashmap_zpl.odin @@ -212,7 +212,8 @@ zpl_hmap_set :: proc( using self : ^ HMapZPL( $ Type), key : u64, value : Type ) entries.data[id].value = value if zpl_hmap_full( self ) { - return & entries.data[id].value, zpl_hmap_grow( self ) + alloc_error := zpl_hmap_grow( self ) + return & entries.data[id].value, alloc_error } return & entries.data[id].value, AllocatorError.None diff --git a/code/host/host.odin b/code/host/host.odin index e017aec..d79c240 100644 --- a/code/host/host.odin +++ b/code/host/host.odin @@ -280,6 +280,8 @@ main :: proc() verify( sectr_api.lib_version != 0, "Failed to initially load the sectr module" ) } + free_all( context.temp_allocator ) + running = true; sectr_api = sectr_api sectr_api.startup( diff --git a/code/input.odin b/code/input.odin index 843fe4c..f3cc534 100644 --- a/code/input.odin +++ b/code/input.odin @@ -273,6 +273,12 @@ MouseState :: struct { vertical_wheel, horizontal_wheel : AnalogAxis } +mouse_world_delta :: #force_inline proc "contextless" () -> Vec2 { + using state := get_state() + cam := & state.project.workspace.cam + return { input.mouse.delta.x, -input.mouse.delta.y } * ( 1 / cam.zoom ) +} + InputState :: struct { keyboard : KeyboardState, mouse : MouseState diff --git a/code/math.odin b/code/math.odin index 77f25c8..cf43e18 100644 --- a/code/math.odin +++ b/code/math.odin @@ -33,6 +33,19 @@ Range2 :: struct #raw_union{ }, } +range2 :: #force_inline proc "contextless" ( a, b : Vec2 ) -> Range2 { + result := Range2 { pts = { a, b } } + return result +} + Rect :: struct { top_left, bottom_right : Vec2 } + +add_range2 :: #force_inline proc "contextless" ( a, b : Range2 ) -> Range2 { + result := Range2 { pts = { + a.p0 + b.p0, + a.p1 + b.p1, + }} + return result +} diff --git a/code/space.odin b/code/space.odin index ca969cb..3f7a029 100644 --- a/code/space.odin +++ b/code/space.odin @@ -95,6 +95,11 @@ range2_pixels_to_cm :: proc( range : Range2 ) -> Range2 { Camera :: rl.Camera2D +CameraZoomMode :: enum u32 { + Digital, + Smooth, +} + // TODO(Ed) : I'm not sure making the size and extent types distinct has made things easier or more difficult in Odin.. // The lack of operator overloads is going to make any sort of nice typesystem // for doing lots of math or phyiscs more error prone or filled with proc wrappers @@ -160,7 +165,7 @@ view_get_corners :: proc() -> BoundsCorners2 { return { top_left, top_right, bottom_left, bottom_right } } -screen_to_world :: proc(pos: Vec2) -> Vec2 { +screen_to_world :: #force_inline proc "contextless" (pos: Vec2) -> Vec2 { state := get_state(); using state cam := & project.workspace.cam result := Vec2 { cam.target.x, -cam.target.y} + Vec2 { pos.x, -pos.y } * (1 / cam.zoom) diff --git a/code/tick_render.odin b/code/tick_render.odin index 444f552..ae289b4 100644 --- a/code/tick_render.odin +++ b/code/tick_render.odin @@ -60,12 +60,20 @@ render :: proc() } } + debug_text("Zoom Target: %v", project.workspace.zoom_target) + if debug.mouse_vis { + debug_text( "Mouse Vertical Wheel: %v", input.mouse.vertical_wheel ) debug_text( "Mouse Position (Screen): %v", input.mouse.pos ) debug_text("Mouse Position (World): %v", screen_to_world(input.mouse.pos) ) cursor_pos := transmute(Vec2) state.app_window.extent + input.mouse.pos rl.DrawCircleV( cursor_pos, 10, Color_White_A125 ) } + + debug_text( "ui_drag_start : %v", debug.ui_drag_start ) + debug_text( "ui_drag_delta : %v", debug.ui_drag_delta ) + debug_text( "Draggable Box Pos: %v", debug.draggable_box_pos ) + debug.draw_debug_text_y = 50 } //endregion Render Screenspace @@ -81,6 +89,8 @@ render_mode_2d :: proc() rl.BeginMode2D( project.workspace.cam ) + debug_draw_text_world( "This is text in world space", { 0, 200 }, 16.0 ) + ImguiRender: { ui := & state.project.workspace.ui @@ -118,7 +128,6 @@ render_mode_2d :: proc() } //endregion Imgui Render - debug_draw_text_world( "This is text in world space", { 0, 200 }, 16.0 ) if debug.mouse_vis { cursor_world_pos := screen_to_world(input.mouse.pos) diff --git a/code/tick_update.odin b/code/tick_update.odin index 64f9f31..3ff08c9 100644 --- a/code/tick_update.odin +++ b/code/tick_update.odin @@ -2,6 +2,7 @@ package sectr import "base:runtime" import "core:math" +import "core:math/linalg" import rl "vendor:raylib" @@ -56,10 +57,16 @@ poll_debug_actions :: proc( actions : ^ DebugActions, input : ^ InputState ) cam_mouse_pan = mouse.right.ended_down && ! pressed(mouse.right) } +frametime_delta32 :: #force_inline proc "contextless" () -> f32 { + return cast(f32) get_state().frametime_delta_seconds +} + update :: proc( delta_time : f64 ) -> b32 { state := get_state(); using state replay := & Memory_App.replay + workspace := & project.workspace + cam := & workspace.cam if rl.IsWindowResized() { window := & state.app_window @@ -138,25 +145,30 @@ update :: proc( delta_time : f64 ) -> b32 //region Camera Manual Nav { - cam := & project.workspace.cam - digital_move_speed : f32 = 200.0 - // zoom_sensitivity : f32 = 0.2 // Digital - zoom_sensitivity : f32 = 4.0 // Smooth - if debug.zoom_target == 0.0 { - debug.zoom_target = cam.zoom + if workspace.zoom_target == 0.0 { + workspace.zoom_target = cam.zoom } - // Adjust zoom_target based on input, not the actual zoom - zoom_delta := input.mouse.vertical_wheel * zoom_sensitivity - debug.zoom_target *= 1 + zoom_delta * f32(delta_time) - debug.zoom_target = clamp(debug.zoom_target, 0.25, 10.0) + config.cam_zoom_smooth_snappiness = 10.0 + config.cam_zoom_mode = .Smooth + switch config.cam_zoom_mode + { + case .Smooth: + zoom_delta := input.mouse.vertical_wheel * config.cam_zoom_sensitivity_smooth + workspace.zoom_target *= 1 + zoom_delta * f32(delta_time) + workspace.zoom_target = clamp(workspace.zoom_target, 0.25, 10.0) - // Linearly interpolate cam.zoom towards zoom_target - lerp_factor := cast(f32) 4.0 // Adjust this value to control the interpolation speed - cam.zoom += (debug.zoom_target - cam.zoom) * lerp_factor * f32(delta_time) - cam.zoom = clamp(cam.zoom, 0.25, 10.0) // Ensure cam.zoom stays within bounds + // Linearly interpolate cam.zoom towards zoom_target + lerp_factor := config.cam_zoom_smooth_snappiness // Adjust this value to control the interpolation speed + cam.zoom += (workspace.zoom_target - cam.zoom) * lerp_factor * f32(delta_time) + cam.zoom = clamp(cam.zoom, 0.25, 10.0) // Ensure cam.zoom stays within bounds + case .Digital: + zoom_delta := input.mouse.vertical_wheel * config.cam_zoom_sensitivity_digital + workspace.zoom_target = clamp(workspace.zoom_target + zoom_delta, 0.25, 10.0) + cam.zoom = workspace.zoom_target + } move_velocity : Vec2 = { - cast(f32) i32(debug_actions.cam_move_left) + cast(f32) i32(debug_actions.cam_move_right), @@ -168,7 +180,7 @@ update :: proc( delta_time : f64 ) -> b32 if debug_actions.cam_mouse_pan { if is_within_screenspace(input.mouse.pos) { - pan_velocity := input.mouse.delta * (1/cam.zoom) + pan_velocity := input.mouse.delta * ( 1 / cam.zoom ) cam.target -= pan_velocity } } @@ -208,7 +220,7 @@ update :: proc( delta_time : f64 ) -> b32 }} ui_style_theme( frame_theme ) - first_layout := UI_Layout { + default_layout := UI_Layout { anchor = {}, // alignment = { 0.0, 0.0 }, alignment = { 0.5, 0.5 }, @@ -216,26 +228,71 @@ update :: proc( delta_time : f64 ) -> b32 pos = { 0, 0 }, size = { 200, 200 }, } - ui_set_layout( first_layout ) + ui_set_layout( default_layout ) // First Demo - when true + Test_HoverNClick :: false + Test_Draggable :: true + + when Test_HoverNClick { first_flags : UI_BoxFlags = { .Mouse_Clickable, .Focusable, .Click_To_Focus } - first_box := ui_box_make( first_flags, "FIRST BOX BOIS" ) + first_box := ui_box_make( first_flags, "FIRST BOX!" ) signal := ui_signal_from_box( first_box ) if signal.left_clicked || debug.frame_2_created { - second_layout := first_layout + second_layout := default_layout second_layout.pos = { 250, 0 } ui_set_layout( second_layout ) - second_box := ui_box_make( first_flags, "SECOND BOX BOIS" ) + second_box := ui_box_make( first_flags, "SECOND BOX!" ) signal := ui_signal_from_box( second_box ) debug.frame_2_created = true } } + + config.ui_resize_border_width = 50 + when Test_Draggable + { + 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 ) + + if draggable_box.first_frame { + debug.draggable_box_pos = draggable_box.style.layout.pos + debug.draggable_box_size = draggable_box.style.layout.size + } + + // Dragging + if signal.dragging { + debug.draggable_box_pos += mouse_world_delta() + } + + // Resize + if signal.resizing + { + if ! debug.box_resize_started { + 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) + + 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.size = debug.draggable_box_size + } } //endregion Imgui Tick diff --git a/code/ui.odin b/code/ui.odin index a76cfc0..c29b96d 100644 --- a/code/ui.odin +++ b/code/ui.odin @@ -162,6 +162,7 @@ UI_Signal :: struct { pressed : b8, released : b8, dragging : b8, + resizing : b8, hovering : b8, cursor_over : b8, commit : b8, @@ -202,7 +203,7 @@ UI_Style :: struct { cursor : UI_Cursor, - layout : UI_Layout, + using layout : UI_Layout, transition_time : f32, } @@ -228,16 +229,20 @@ UI_Box :: struct { // Regenerated per frame. using links : DLL_NodeFull( UI_Box ), // first, last, prev, next - parent : ^ UI_Box, + parent : ^UI_Box, num_children : i32, flags : UI_BoxFlags, computed : UI_Computed, theme : UI_StyleTheme, - style : ^ UI_Style, + + style : ^UI_Style, // Persistent Data - style_delta : f32, + first_frame : b8, + hot_delta : f32, + active_delta : f32, + style_delta : f32, // prev_computed : UI_Computed, // prev_style : UI_Style,v mouse : UI_InteractState, @@ -248,7 +253,7 @@ UI_Box :: struct { UI_Layout_Stack_Size :: 512 UI_Style_Stack_Size :: 512 UI_Parent_Stack_Size :: 1024 -UI_Built_Boxes_Array_Size :: 128 +UI_Built_Boxes_Array_Size :: 1024 UI_State :: struct { // TODO(Ed) : Use these @@ -258,11 +263,11 @@ UI_State :: struct { built_box_count : i32, caches : [2] HMapZPL( UI_Box ), - prev_cache : ^ HMapZPL( UI_Box ), - curr_cache : ^ HMapZPL( UI_Box ), + prev_cache : ^HMapZPL( UI_Box ), + curr_cache : ^HMapZPL( UI_Box ), - null_box : ^ UI_Box, // Ryan had this, I don't know why yet. - root : ^ UI_Box, + null_box : ^UI_Box, // Ryan had this, I don't know why yet. + root : ^UI_Box, // Do we need to recompute the layout? layout_dirty : b32, @@ -272,13 +277,17 @@ UI_State :: struct { parent_stack : StackFixed( ^UI_Box, UI_Parent_Stack_Size ), // flag_stack : Stack( UI_BoxFlags, UI_BoxFlags_Stack_Size ), - hot : UI_Key, - active_mouse : [MouseBtn.count] UI_Key, - active : UI_Key, + hot : UI_Key, + active_mouse : [MouseBtn.count] UI_Key, + active : UI_Key, + hot_resizable : b32, + active_resizing : b32, // Locks the user into a resizing state for the active box until they release the active key + clipboard_copy : UI_Key, last_clicked : UI_Key, - drag_start_mouse : Vec2, + cursor_active_start : Vec2, + // cursor_resize_start : Vec2, // drag_state_arena : ^ Arena, // drag_state data : string, @@ -347,6 +356,8 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box) verify( set_error == AllocatorError.None, "Failed to set zpl_hmap due to allocator error" ) curr_box = set_result + + curr_box.first_frame = prev_box == nil } // TODO(Ed) : Review this when we learn layouts more... @@ -396,6 +407,21 @@ ui_box_tranverse_next :: proc( box : ^ UI_Box ) -> (^ UI_Box) { return box.next } +ui_cursor_pos :: #force_inline proc "contextless" () -> Vec2 { + using state := get_state() + if ui_context == & state.project.workspace.ui { + return screen_to_world( input.mouse.pos ) + } + else { + return input.mouse.pos + } +} + +ui_drag_delta :: #force_inline proc "contextless" () -> Vec2 { + using state := get_state() + return ui_cursor_pos() - state.ui_context.cursor_active_start +} + ui_graph_build_begin :: proc( ui : ^ UI_State, bounds : Vec2 = {} ) { get_state().ui_context = ui @@ -457,15 +483,21 @@ 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 } - if ui == & get_state().project.workspace.ui { - signal.cursor_pos = screen_to_world( input.mouse.pos ) - } - else { - signal.cursor_pos = input.mouse.pos - } - signal.cursor_over = cast(b8) pos_within_range2( signal.cursor_pos, box.computed.bounds ) + // 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 ) @@ -478,7 +510,7 @@ ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal ui.hot = box.key ui.active = box.key ui.active_mouse[MouseBtn.Left] = box.key - ui.drag_start_mouse = signal.cursor_pos + ui.cursor_active_start = signal.cursor_pos ui.last_pressed_key = box.key signal.pressed = true @@ -487,18 +519,24 @@ ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal if mouse_clickable && signal.cursor_over && left_released { - ui.active = UI_Key(0) + 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 { + 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 } @@ -523,22 +561,45 @@ ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal } + is_hot := ui.hot == box.key + is_active := ui.active == box.key + if signal.cursor_over && - ui.hot == UI_Key(0) || ui.hot == box.key && - ui.active == UI_Key(0) || ui.active == box.key + 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] diff --git a/ols.json b/ols.json index 7bd1e24..488bbd9 100644 --- a/ols.json +++ b/ols.json @@ -20,11 +20,11 @@ ], "odin_command": "C:/projects/SectrPrototype/thirdparty/Odin/odin.exe", "enable_document_symbols": true, - "enable_fake_methods": true, + "enable_fake_methods": false, "enable_format": false, "enable_hover": true, - "enable_semantic_tokens": true, - "enable_snippets": true, - "enable_references": true, + "enable_semantic_tokens": false, + "enable_snippets": false, + "enable_references": false, "thread_pool_count": 10 }