Dragging! & basic proportional box resize frm cursor distance to box pos
Still need to add resize via 'pulling' to stretch the box out from a side or 2 sides diagonally. Also some general clenaup of code
This commit is contained in:
parent
90478bec94
commit
191d5076ea
12
Readme.md
Normal file
12
Readme.md
Normal file
@ -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.
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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 }
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
111
code/ui.odin
111
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]
|
||||
|
||||
|
8
ols.json
8
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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user