From 159aedb59217190a54ed827fe85dbd9462bd7694 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Tue, 12 Mar 2024 20:55:29 -0400 Subject: [PATCH] Memory fixes, progress towards anchors support in the UI auto-layout Also support for margins --- code/api.odin | 59 +++++++++++++++++++-------- code/collision.odin | 1 + code/font_provider.odin | 1 + code/grime.odin | 1 + code/grime_pool_allocator.odin | 27 +++++++++---- code/grime_slab_allocator.odin | 35 +++++++++++----- code/grime_string_interning.odin | 20 +++++----- code/grime_virtual_arena.odin | 34 +++++++++++----- code/host/host.odin | 1 + code/logger.odin | 2 +- code/parser_whitespace.odin | 6 +++ code/tick_update.odin | 68 ++++++++++++++++++++++++-------- code/ui.odin | 25 +++++++----- code/ui_layout.odin | 34 +++++++++++----- code/ui_signal.odin | 4 +- scripts/build.ps1 | 4 +- 16 files changed, 229 insertions(+), 93 deletions(-) diff --git a/code/api.odin b/code/api.odin index 1623e28..0b2490d 100644 --- a/code/api.odin +++ b/code/api.odin @@ -59,23 +59,48 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem alignment := uint(mem.DEFAULT_ALIGNMENT) policy_ptr := & default_slab_policy - push( policy_ptr, SlabSizeClass { 16 * Megabyte, 4 * Kilobyte, alignment }) - push( policy_ptr, SlabSizeClass { 32 * Megabyte, 16 * Kilobyte, alignment }) - push( policy_ptr, SlabSizeClass { 64 * Megabyte, 32 * Kilobyte, alignment }) - push( policy_ptr, SlabSizeClass { 64 * Megabyte, 64 * Kilobyte, alignment }) - push( policy_ptr, SlabSizeClass { 64 * Megabyte, 128 * Kilobyte, alignment }) - push( policy_ptr, SlabSizeClass { 64 * Megabyte, 256 * Kilobyte, alignment }) - push( policy_ptr, SlabSizeClass { 64 * Megabyte, 512 * Kilobyte, alignment }) - push( policy_ptr, SlabSizeClass { 64 * Megabyte, 1 * Megabyte, alignment }) - push( policy_ptr, SlabSizeClass { 64 * Megabyte, 2 * Megabyte, alignment }) - push( policy_ptr, SlabSizeClass { 64 * Megabyte, 4 * Megabyte, alignment }) - push( policy_ptr, SlabSizeClass { 64 * Megabyte, 8 * Megabyte, alignment }) - push( policy_ptr, SlabSizeClass { 64 * Megabyte, 16 * Megabyte, alignment }) - push( policy_ptr, SlabSizeClass { 64 * Megabyte, 32 * Megabyte, alignment }) - push( policy_ptr, SlabSizeClass { 256 * Megabyte, 64 * Megabyte, alignment }) - push( policy_ptr, SlabSizeClass { 256 * Megabyte, 128 * Megabyte, alignment }) - push( policy_ptr, SlabSizeClass { 512 * Megabyte, 256 * Megabyte, alignment }) - push( policy_ptr, SlabSizeClass { 512 * Megabyte, 512 * Megabyte, alignment }) + if false + { + push( policy_ptr, SlabSizeClass { 16 * Megabyte, 4 * Kilobyte, alignment }) + push( policy_ptr, SlabSizeClass { 32 * Megabyte, 16 * Kilobyte, alignment }) + push( policy_ptr, SlabSizeClass { 64 * Megabyte, 32 * Kilobyte, alignment }) + push( policy_ptr, SlabSizeClass { 64 * Megabyte, 64 * Kilobyte, alignment }) + push( policy_ptr, SlabSizeClass { 64 * Megabyte, 128 * Kilobyte, alignment }) + push( policy_ptr, SlabSizeClass { 64 * Megabyte, 256 * Kilobyte, alignment }) + push( policy_ptr, SlabSizeClass { 64 * Megabyte, 512 * Kilobyte, alignment }) + push( policy_ptr, SlabSizeClass { 64 * Megabyte, 1 * Megabyte, alignment }) + push( policy_ptr, SlabSizeClass { 64 * Megabyte, 2 * Megabyte, alignment }) + push( policy_ptr, SlabSizeClass { 64 * Megabyte, 4 * Megabyte, alignment }) + push( policy_ptr, SlabSizeClass { 64 * Megabyte, 8 * Megabyte, alignment }) + push( policy_ptr, SlabSizeClass { 64 * Megabyte, 16 * Megabyte, alignment }) + push( policy_ptr, SlabSizeClass { 64 * Megabyte, 32 * Megabyte, alignment }) + push( policy_ptr, SlabSizeClass { 256 * Megabyte, 64 * Megabyte, alignment }) + push( policy_ptr, SlabSizeClass { 256 * Megabyte, 128 * Megabyte, alignment }) + push( policy_ptr, SlabSizeClass { 512 * Megabyte, 256 * Megabyte, alignment }) + push( policy_ptr, SlabSizeClass { 512 * Megabyte, 512 * Megabyte, alignment }) + } + else + { + push( policy_ptr, SlabSizeClass { 128 * Kilobyte, 1 * Kilobyte, alignment }) + push( policy_ptr, SlabSizeClass { 256 * Kilobyte, 2 * Kilobyte, alignment }) + push( policy_ptr, SlabSizeClass { 512 * Kilobyte, 4 * Kilobyte, alignment }) + push( policy_ptr, SlabSizeClass { 1 * Megabyte, 16 * Kilobyte, alignment }) + push( policy_ptr, SlabSizeClass { 1 * Megabyte, 32 * Kilobyte, alignment }) + push( policy_ptr, SlabSizeClass { 1 * Megabyte, 64 * Kilobyte, alignment }) + push( policy_ptr, SlabSizeClass { 2 * Megabyte, 128 * Kilobyte, alignment }) + push( policy_ptr, SlabSizeClass { 2 * Megabyte, 256 * Kilobyte, alignment }) + push( policy_ptr, SlabSizeClass { 2 * Megabyte, 512 * Kilobyte, alignment }) + push( policy_ptr, SlabSizeClass { 2 * Megabyte, 1 * Megabyte, alignment }) + push( policy_ptr, SlabSizeClass { 2 * Megabyte, 2 * Megabyte, alignment }) + push( policy_ptr, SlabSizeClass { 4 * Megabyte, 4 * Megabyte, alignment }) + // push( policy_ptr, SlabSizeClass { 8 * Megabyte, 8 * Megabyte, alignment }) + // push( policy_ptr, SlabSizeClass { 16 * Megabyte, 16 * Megabyte, alignment }) + // push( policy_ptr, SlabSizeClass { 32 * Megabyte, 32 * Megabyte, alignment }) + // push( policy_ptr, SlabSizeClass { 64 * Megabyte, 64 * Megabyte, alignment }) + // push( policy_ptr, SlabSizeClass { 128 * Megabyte, 128 * Megabyte, alignment }) + // push( policy_ptr, SlabSizeClass { 256 * Megabyte, 256 * Megabyte, alignment }) + // push( policy_ptr, SlabSizeClass { 512 * Megabyte, 512 * Megabyte, alignment }) + } alloc_error : AllocatorError persistent_slab, alloc_error = slab_init( policy_ptr, allocator = persistent_allocator() ) diff --git a/code/collision.odin b/code/collision.odin index 0b7fa20..c93d74d 100644 --- a/code/collision.odin +++ b/code/collision.odin @@ -8,6 +8,7 @@ pos_within_range2 :: proc( pos : Vec2, range : Range2 ) -> b32 { return b32(within_x && within_y) } +// TODO(Ed): Do we need this? Also does it even work (looks unfinished)? is_within_screenspace :: proc( pos : Vec2 ) -> b32 { state := get_state(); using state screen_extent := state.app_window.extent diff --git a/code/font_provider.odin b/code/font_provider.odin index 1027f3d..b1c7ce3 100644 --- a/code/font_provider.odin +++ b/code/font_provider.odin @@ -98,6 +98,7 @@ font_load :: proc( path_file : string, ) -> FontID { profile(#procedure) + log( str_fmt_tmp("Loading font: %v", path_file)) font_provider_data := & get_state().font_provider_data; using font_provider_data font_data, read_succeded : = os.read_entire_file( path_file, context.temp_allocator ) diff --git a/code/grime.odin b/code/grime.odin index f1d2a0a..1676f66 100644 --- a/code/grime.odin +++ b/code/grime.odin @@ -4,6 +4,7 @@ package sectr import "base:builtin" copy :: builtin.copy import "base:intrinsics" + mem_zero :: intrinsics.mem_zero ptr_sub :: intrinsics.ptr_sub type_has_field :: intrinsics.type_has_field type_elem_type :: intrinsics.type_elem_type diff --git a/code/grime_pool_allocator.odin b/code/grime_pool_allocator.odin index 187a86f..e65f49f 100644 --- a/code/grime_pool_allocator.odin +++ b/code/grime_pool_allocator.odin @@ -14,6 +14,7 @@ The pool doesn't allocate any buckets on initialization unless the user specifes package sectr import "core:mem" +import "core:slice" Pool :: struct { using header : ^PoolHeader, @@ -32,9 +33,9 @@ PoolHeader :: struct { } PoolBucket :: struct { - blocks : [^]byte, - next_block : uint, using nodes : DLL_NodePN( PoolBucket), + next_block : uint, + blocks : [^]byte } Pool_FreeBlock :: struct { @@ -86,6 +87,7 @@ pool_destroy :: proc ( using self : Pool ) pool_allocate_buckets :: proc( using self : Pool, num_buckets : uint ) -> AllocatorError { profile(#procedure) + pool := self if num_buckets == 0 { return .Invalid_Argument } @@ -103,6 +105,8 @@ pool_allocate_buckets :: proc( using self : Pool, num_buckets : uint ) -> Alloca bucket := cast( ^PoolBucket) next_bucket_ptr bucket.blocks = memory_after_header(bucket) bucket.next_block = 0 + log( str_fmt_tmp("Pool allocated block: %p capacity: %d", raw_data(bucket_memory), bucket_capacity)) + if self.bucket_list.first == nil { self.bucket_list.first = bucket @@ -111,20 +115,22 @@ pool_allocate_buckets :: proc( using self : Pool, num_buckets : uint ) -> Alloca else { dll_push_back( & self.bucket_list.last, bucket ) } + log( str_fmt_tmp("Bucket List First: %p", self.bucket_list.first)) next_bucket_ptr = next_bucket_ptr[ bucket_capacity: ] } return alloc_error } -pool_grab :: proc( using pool : Pool ) -> ( block : []byte, alloc_error : AllocatorError ) +pool_grab :: proc( using pool : Pool, zero_memory := false ) -> ( block : []byte, alloc_error : AllocatorError ) { + pool := pool // profile(#procedure) alloc_error = .None // Check the free-list first for a block - if free_list_head != nil { - + if free_list_head != nil + { head := & pool.free_list_head // Compiler Bug? Fails to compile @@ -135,7 +141,8 @@ pool_grab :: proc( using pool : Pool ) -> ( block : []byte, alloc_error : Alloca pool.free_list_head = pool.free_list_head.next // ll_pop - block = slice_ptr( cast([^]byte) last_free, int(pool.block_size) ) + block = byte_slice( cast([^]byte) last_free, int(pool.block_size) ) + log( str_fmt_tmp("Returning free block: %p %d", raw_data(block), pool.block_size)) return } @@ -164,10 +171,12 @@ pool_grab :: proc( using pool : Pool ) -> ( block : []byte, alloc_error : Alloca // if current_bucket.next != nil { if pool.current_bucket.next != nil { // current_bucket = current_bucket.next + log( str_fmt_tmp("Bucket %p exhausted using %p", pool.current_bucket, pool.current_bucket.next)) pool.current_bucket = pool.current_bucket.next } else { + log( "All previous buckets exhausted, allocating new bucket") alloc_error := pool_allocate_buckets( pool, 1 ) if alloc_error != .None { ensure(false, "Failed to allocate bucket") @@ -180,8 +189,12 @@ pool_grab :: proc( using pool : Pool ) -> ( block : []byte, alloc_error : Alloca // Compiler Bug // block = slice_ptr( current_bucket.blocks[ current_bucket.next_block:], int(block_size) ) // self.current_bucket.next_block += block_size - block = slice_ptr( pool.current_bucket.blocks[ pool.current_bucket.next_block:], int(block_size) ) + block = byte_slice( pool.current_bucket.blocks[ pool.current_bucket.next_block:], int(block_size) ) pool.current_bucket.next_block += block_size + + if zero_memory { + slice.zero(block) + } return } diff --git a/code/grime_slab_allocator.odin b/code/grime_slab_allocator.odin index bbe55a3..dd0a5a5 100644 --- a/code/grime_slab_allocator.odin +++ b/code/grime_slab_allocator.odin @@ -106,27 +106,34 @@ slab_alloc :: proc( using self : Slab, { // profile(#procedure) pool : Pool - for id in 0 ..< pools.idx { + id : u32 = 0 + for ; id < pools.idx; id += 1 { pool = pools.items[id] if pool.block_size >= size && pool.alignment >= alignment { break } } + verify( id < pools.idx, "There is not a size class in the slab's policy to satisfy the requested allocation" ) verify( pool.header != nil, "Requested alloc not supported by the slab allocator", location = loc ) block : []byte block, alloc_error = pool_grab(pool) if alloc_error != .None { + ensure(false, "Bad block from pool") return nil, alloc_error } + log( str_fmt_tmp("Retrieved block: %p %d", raw_data(block), len(block) )) - if zero_memory { - slice.zero(block) - } + // if zero_memory { + // slice.zero(block) + // } data = byte_slice(raw_data(block), size) + if zero_memory { + slice.zero(data) + } return } @@ -177,11 +184,14 @@ slab_resize :: proc( using self : Slab, // Resize will keep block in the same size_class, just give it more of its already allocated block if pool_old == pool_resize { - new_data = byte_slice( raw_data(data), new_size ) + new_data_ptr := memory_after(data) + new_data = byte_slice( raw_data(data), new_size ) + log( str_fmt_tmp("Resize via expanding block space allocation %p %d", new_data_ptr, int(new_size - old_size))) if zero_memory && new_size > old_size { - to_zero := slice_ptr( memory_after(data), int(new_size - old_size) ) + to_zero := byte_slice( memory_after(data), int(new_size - old_size) ) slice.zero( to_zero ) + log( str_fmt_tmp("Zeroed memory - Range(%p to %p)", new_data_ptr, int(new_size - old_size))) } return } @@ -189,11 +199,18 @@ slab_resize :: proc( using self : Slab, // We'll need to provide an entirely new block, so the data will need to be copied over. new_block : []byte new_block, alloc_error = pool_grab( pool_resize ) - if alloc_error != .None do return - if zero_memory { - slice.zero( new_block ) + if new_block == nil { + ensure(false, "Retreived a null block") + return } + if alloc_error != .None do return + // if zero_memory { + // slice.zero( new_block ) + // } + + log( str_fmt_tmp("Resize via new block: %p %d (old : %p $d )", raw_data(new_block), len(new_block), raw_data(data), old_size )) + if raw_data(data) != raw_data(new_block) { copy_non_overlapping( raw_data(new_block), raw_data(data), int(old_size) ) pool_release( pool_old, data ) diff --git a/code/grime_string_interning.odin b/code/grime_string_interning.odin index 73848dd..70d78ff 100644 --- a/code/grime_string_interning.odin +++ b/code/grime_string_interning.odin @@ -30,16 +30,16 @@ str_cache_init :: proc( /*allocator : Allocator*/ ) -> ( cache : StringCache ) { policy : SlabPolicy policy_ptr := & policy - push( policy_ptr, SlabSizeClass { 8 * Megabyte, 16, alignment }) - push( policy_ptr, SlabSizeClass { 8 * Megabyte, 32, alignment }) - push( policy_ptr, SlabSizeClass { 16 * Megabyte, 64, alignment }) - push( policy_ptr, SlabSizeClass { 16 * Megabyte, 128, alignment }) - push( policy_ptr, SlabSizeClass { 16 * Megabyte, 256, alignment }) - push( policy_ptr, SlabSizeClass { 16 * Megabyte, 512, alignment }) - push( policy_ptr, SlabSizeClass { 32 * Megabyte, 1 * Kilobyte, alignment }) - push( policy_ptr, SlabSizeClass { 32 * Megabyte, 4 * Kilobyte, alignment }) - push( policy_ptr, SlabSizeClass { 64 * Megabyte, 16 * Kilobyte, alignment }) - push( policy_ptr, SlabSizeClass { 64 * Megabyte, 32 * Kilobyte, alignment }) + push( policy_ptr, SlabSizeClass { 64 * Kilobyte, 16, alignment }) + push( policy_ptr, SlabSizeClass { 64 * Kilobyte, 32, alignment }) + push( policy_ptr, SlabSizeClass { 64 * Kilobyte, 64, alignment }) + push( policy_ptr, SlabSizeClass { 64 * Kilobyte, 128, alignment }) + push( policy_ptr, SlabSizeClass { 64 * Kilobyte, 256, alignment }) + push( policy_ptr, SlabSizeClass { 64 * Kilobyte, 512, alignment }) + push( policy_ptr, SlabSizeClass { 1 * Megabyte, 1 * Kilobyte, alignment }) + push( policy_ptr, SlabSizeClass { 4 * Megabyte, 4 * Kilobyte, alignment }) + push( policy_ptr, SlabSizeClass { 16 * Megabyte, 16 * Kilobyte, alignment }) + push( policy_ptr, SlabSizeClass { 32 * Megabyte, 32 * Kilobyte, alignment }) // push( policy_ptr, SlabSizeClass { 64 * Megabyte, 64 * Kilobyte, alignment }) // push( policy_ptr, SlabSizeClass { 64 * Megabyte, 128 * Kilobyte, alignment }) // push( policy_ptr, SlabSizeClass { 64 * Megabyte, 256 * Kilobyte, alignment }) diff --git a/code/grime_virtual_arena.odin b/code/grime_virtual_arena.odin index 37496f6..a0505d5 100644 --- a/code/grime_virtual_arena.odin +++ b/code/grime_virtual_arena.odin @@ -23,11 +23,11 @@ import "core:sync" VArena_GrowthPolicyProc :: #type proc( commit_used, committed, reserved, requested_size : uint ) -> uint VArena :: struct { - using vmem : VirtualMemoryRegion, - commit_used : uint, - growth_policy : VArena_GrowthPolicyProc, - allow_any_reize : b32, - mutex : sync.Mutex, + using vmem : VirtualMemoryRegion, + commit_used : uint, + growth_policy : VArena_GrowthPolicyProc, + allow_any_reize : b32, + mutex : sync.Mutex, } varena_default_growth_policy :: proc( commit_used, committed, reserved, requested_size : uint ) -> uint @@ -74,8 +74,8 @@ varena_init :: proc( base_address : uintptr, to_reserve, to_commit : uint, return } - arena.vmem = vmem - arena.commit_used = 0 + arena.vmem = vmem + arena.commit_used = 0 if growth_policy == nil { arena.growth_policy = varena_default_growth_policy @@ -145,12 +145,21 @@ varena_alloc :: proc( using self : ^VArena, data_ptr := rawptr(current_offset + uintptr(alignment_offset)) data = byte_slice( data_ptr, int(requested_size) ) - self.commit_used += size_to_allocate + self.commit_used += size_to_allocate alloc_error = .None - if zero_memory { - slice.zero( data ) + log_backing : [Kilobyte * 16]byte + backing_slice := byte_slice( & log_backing[0], len(log_backing)) + + // log( str_fmt_buffer( backing_slice, "varena alloc - BASE: %p PTR: %X, SIZE: %d", cast(rawptr) self.base_address, & data[0], requested_size) ) + + if zero_memory + { + // log( str_fmt_buffer( backing_slice, "Zeroring data (Range: %p to %p)", raw_data(data), cast(rawptr) (uintptr(raw_data(data)) + uintptr(requested_size)))) + // slice.zero( data ) + mem_zero( data_ptr, int(requested_size) ) } + return } @@ -229,6 +238,9 @@ varena_allocator_proc :: proc( verify( old_memory_offset == current_offset || arena.allow_any_reize, "Cannot resize existing allocation in vitual arena to a larger size unless it was the last allocated" ) + log_backing : [Kilobyte * 16]byte + backing_slice := byte_slice( & log_backing[0], len(log_backing)) + if old_memory_offset != current_offset && arena.allow_any_reize { // Give it new memory and copy the old over. Old memory is unrecoverable until clear. @@ -242,6 +254,7 @@ varena_allocator_proc :: proc( copy_non_overlapping( raw_data(new_region), old_memory, int(old_size) ) data = new_region + // log( str_fmt_tmp("varena resize (new): old: %p %v new: %p %v", old_memory, old_size, (& data[0]), size)) return } @@ -254,6 +267,7 @@ varena_allocator_proc :: proc( } data = byte_slice( old_memory, size ) + // log( str_fmt_tmp("varena resize (expanded): old: %p %v new: %p %v", old_memory, old_size, (& data[0]), size)) return case .Query_Features: diff --git a/code/host/host.odin b/code/host/host.odin index 92c67c5..bc403a0 100644 --- a/code/host/host.odin +++ b/code/host/host.odin @@ -97,6 +97,7 @@ when ODIN_OS == runtime.Odin_OS_Type.Windows Path_Sectr_Debug_Symbols :: "sectr.pdb" } +// TODO(Ed): Disable the default allocators for the host, we'll be handling it instead. RuntimeState :: struct { running : b32, client_memory : ClientMemory, diff --git a/code/logger.odin b/code/logger.odin index e7c249c..89cd351 100644 --- a/code/logger.odin +++ b/code/logger.odin @@ -8,7 +8,7 @@ import str "core:strings" import "core:time" import core_log "core:log" -Max_Logger_Message_Width :: 300 +Max_Logger_Message_Width :: 180 LogLevel :: core_log.Level diff --git a/code/parser_whitespace.odin b/code/parser_whitespace.odin index 89a3dd7..c670852 100644 --- a/code/parser_whitespace.odin +++ b/code/parser_whitespace.odin @@ -118,6 +118,9 @@ PWS_LexerData :: struct { pws_parser_lex :: proc ( text : string, allocator : Allocator ) -> ( PWS_LexResult, AllocatorError ) { + bytes := transmute([]byte) text + log( str_fmt_tmp( "lexing: %v ...", (len(text) > 30 ? transmute(string) bytes[ :30] : text) )) + profile(#procedure) using lexer : PWS_LexerData context.user_ptr = & lexer @@ -235,6 +238,9 @@ PWS_ParseData :: struct { pws_parser_parse :: proc( text : string, allocator : Allocator ) -> ( PWS_ParseResult, AllocatorError ) { + bytes := transmute([]byte) text + log( str_fmt_tmp( "parsing: %v ...", (len(text) > 30 ? transmute(string) bytes[ :30] : text) )) + profile(#procedure) using parser : PWS_ParseData context.user_ptr = & result diff --git a/code/tick_update.odin b/code/tick_update.odin index 1f3eb10..adeaad1 100644 --- a/code/tick_update.odin +++ b/code/tick_update.odin @@ -204,22 +204,22 @@ update :: proc( delta_time : f64 ) -> b32 .Fixed_Width, .Fixed_Height, } default_layout := UI_Layout { - anchor = {}, - // alignment = { 0.0, 0.5 }, - alignment = { 0.0, 0.0 }, + anchor = {}, + alignment = { 0.0, 0.0 }, text_alignment = { 0.0, 0.0 }, - // alignment = { 1.0, 1.0 }, - // corner_radii = { 0.3, 0.3, 0.3, 0.3 }, - pos = { 0, 0 }, - size = { 200, 200 }, + corner_radii = { 0.2, 0.2, 0.2, 0.2 }, + pos = { 0, 0 }, + size = { 200, 200 }, } frame_style_default := UI_Style { flags = frame_style_flags, bg_color = Color_BG_TextBox, - font = default_font, - font_size = 30, + + font = default_font, + font_size = 30, text_color = Color_White, + layout = default_layout, } @@ -230,19 +230,53 @@ update :: proc( delta_time : f64 ) -> b32 frame_style_default, }} frame_theme.disabled.bg_color = Color_Frame_Disabled - frame_theme.hovered.bg_color = Color_Frame_Hover - frame_theme.focused.bg_color = Color_Frame_Select + frame_theme.hot.bg_color = Color_Frame_Hover + frame_theme.active.bg_color = Color_Frame_Select ui_style_theme( frame_theme ) config.ui_resize_border_width = 2.5 - test_draggable() + // test_draggable() // test_text_box() - // Test Parenting - + // test_parenting() + if true + { + parent_layout := default_layout + parent_layout.size = { 300, 300 } + parent_layout.alignment = { 0.0, 0.0 } + + + ui_style_theme_set_layout( parent_layout ) + parent := ui_widget( "Parent", { .Mouse_Clickable }) + ui_parent(parent) + { + if parent.first_frame { + debug.draggable_box_pos = parent.style.layout.pos + { 0, -100 } + debug.draggable_box_size = parent.style.layout.size + } + + if parent.dragging { + debug.draggable_box_pos += mouse_world_delta() + } + + parent.style.layout.pos = debug.draggable_box_pos + } + + child_layout := default_layout + child_layout.size = { 100, 100 } + child_layout.alignment = { 0.5, 0.5 } + child_layout.margins = { 20, 20, 20, 20 } + child_layout.anchor = range2( { 0.5, 0.5 }, { 0.5, 0.5 }) + + child_theme := frame_style_default + // child_theme.flags = {} + child_theme.layout = child_layout + ui_theme_via_style(child_theme) + child := ui_widget( "Child", { .Mouse_Clickable }) + } // Whitespace AST test - when false + if true { profile("Whitespace AST test") @@ -260,8 +294,8 @@ update :: proc( delta_time : f64 ) -> b32 }} text_theme.default.bg_color = Color_Transparent text_theme.disabled.bg_color = Color_Frame_Disabled - text_theme.hovered.bg_color = Color_Frame_Hover - text_theme.focused.bg_color = Color_Frame_Select + text_theme.hot.bg_color = Color_Frame_Hover + text_theme.active.bg_color = Color_Frame_Select layout_text := default_layout diff --git a/code/ui.odin b/code/ui.odin index 69c8788..e5f3bf8 100644 --- a/code/ui.odin +++ b/code/ui.odin @@ -144,13 +144,15 @@ UI_Layout :: struct { pos : Vec2, // TODO(Ed) : Should everything no matter what its parent is use a WS_Pos instead of a raw vector pos? + // TODO(Ed): Support a min/max range for the size of a box size : Vec2, + // size : Range2 // If the box is a child of the root parent, its automatically in world space and thus will use the tile_pos. tile_pos : WS_Pos, - // TODO(Ed) : Add support for size_to_content? size_to_text : b8, + // TODO(Ed) : Add support for size_to_content? // size_to_content : b8, } @@ -187,8 +189,8 @@ UI_StyleFlags :: bit_set[UI_StyleFlag; u32] UI_StylePreset :: enum u32 { Default, Disabled, - Hovered, - Focused, + Hot, + Active, Count, } @@ -214,7 +216,7 @@ UI_Style :: struct { UI_StyleTheme :: struct #raw_union { array : [UI_StylePreset.Count] UI_Style, using styles : struct { - default, disabled, hovered, focused : UI_Style, + default, disabled, hot, active : UI_Style, } } @@ -258,8 +260,8 @@ UI_Box :: struct { // UI_BoxFlags_Stack_Size :: 512 UI_Layout_Stack_Size :: 512 UI_Style_Stack_Size :: 512 -UI_Parent_Stack_Size :: 1024 * 10 -UI_Built_Boxes_Array_Size :: 1024 * 10 +UI_Parent_Stack_Size :: 512 +UI_Built_Boxes_Array_Size :: Kilobyte * 5 UI_State :: struct { // TODO(Ed) : Use these @@ -313,6 +315,7 @@ ui_startup :: proc( ui : ^ UI_State, cache_allocator : Allocator ) ui.curr_cache = & ui.caches[1] ui.prev_cache = & ui.caches[0] + log("ui_startup completed") } ui_reload :: proc( ui : ^ UI_State, cache_allocator : Allocator ) @@ -374,7 +377,6 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box) } curr_box.flags = flags - curr_box.parent = stack_peek( & parent_stack ) // Clear old links curr_box.parent = nil @@ -394,7 +396,7 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box) return curr_box } -ui_box_tranverse_next :: #force_inline proc "contextless" ( box : ^ UI_Box ) -> (^ UI_Box) +ui_box_tranverse_next :: proc "contextless" ( box : ^ UI_Box ) -> (^ UI_Box) { // If current has children, do them first if box.first != nil { @@ -509,7 +511,7 @@ ui_parent_pop :: proc() { } @(deferred_none = ui_parent_pop) -ui_parent :: proc( ui : ^ UI_Box) { +ui_parent :: proc( ui : ^UI_Box) { ui_parent_push( ui ) } @@ -542,6 +544,11 @@ ui_style_theme :: proc( preset : UI_StyleTheme ) { ui_style_theme_push( preset ) } +@(deferred_none = ui_style_theme_pop) +ui_theme_via_style :: proc ( style : UI_Style ) { + ui_style_theme_push( UI_StyleTheme { styles = { style, style, style, style } }) +} + ui_style_theme_set_layout :: proc ( layout : UI_Layout ) { for & preset in stack_peek_ref( & get_state().ui_context.theme_stack ).array { preset.layout = layout diff --git a/code/ui_layout.odin b/code/ui_layout.odin index 35153c8..7fdf1a5 100644 --- a/code/ui_layout.odin +++ b/code/ui_layout.odin @@ -31,21 +31,36 @@ ui_compute_layout :: proc() layout := & style.layout margins := range2( - parent_content.p0 + { layout.margins.left, layout.margins.top }, - parent_content.p1 - { layout.margins.right, layout.margins.bottom }, + { layout.margins.left, -layout.margins.top }, + { -layout.margins.right, layout.margins.bottom }, ) + margined_bounds := range2( + parent_content.p0 + margins.p0, + parent_content.p1 + margins.p1, + ) + + margined_size := margined_bounds.p1 - margined_bounds.p0 + + anchored_bounds := range2( + margined_bounds.p0 + margined_size * layout.anchor.p0, + margined_bounds.p0 + margined_size * layout.anchor.p1, + ) + + anchored_size := Vec2 { + anchored_bounds.max.x - anchored_bounds.min.x, + anchored_bounds.max.y - anchored_bounds.min.y, + } + anchor := & layout.anchor pos : Vec2 if UI_StyleFlag.Fixed_Position_X in style.flags { pos.x = layout.pos.x - pos.x -= margins.p0.x * anchor.x0 - pos.x += margins.p0.x * anchor.x1 + pos.x += anchored_bounds.p0.x } if UI_StyleFlag.Fixed_Position_Y in style.flags { pos.y = layout.pos.y - pos.y -= margins.p1.y * anchor.y0 - pos.y += margins.p1.y * anchor.y1 + pos.y += anchored_bounds.p0.y } text_size : Vec2 @@ -53,7 +68,8 @@ ui_compute_layout :: proc() // if computed.text_size.y == style.font_size { if current.first_frame || ! style.size_to_text || computed.text_size.y != size_range2(computed.bounds).y { text_size = cast(Vec2) measure_text_size( current.text.str, style.font, style.font_size, 0 ) - } else { + } + else { text_size = computed.text_size } @@ -62,14 +78,14 @@ ui_compute_layout :: proc() size.x = layout.size.x } else { - // TODO(Ed) : Not sure what todo here... + size.x = anchored_size.x } if UI_StyleFlag.Fixed_Height in style.flags { size.y = layout.size.y } else { - // TODO(Ed) : Not sure what todo here... + size.y = anchored_size.y } if style.size_to_text { diff --git a/code/ui_signal.odin b/code/ui_signal.odin index b9a7035..7fb734f 100644 --- a/code/ui_signal.odin +++ b/code/ui_signal.odin @@ -173,7 +173,7 @@ ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal box.prev_style = box.style box.style_delta = 0 } - box.style = stack_peek( & ui.theme_stack ).hovered + box.style = stack_peek( & ui.theme_stack ).hot } if is_active { @@ -182,7 +182,7 @@ ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal box.style_delta = 0 log( str_fmt_tmp("NEW ACTIVE: %v", box.label.str)) } - box.style = stack_peek( & ui.theme_stack ).focused + box.style = stack_peek( & ui.theme_stack ).active } if is_disabled { diff --git a/scripts/build.ps1 b/scripts/build.ps1 index c1a7ba8..546dc30 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -160,8 +160,8 @@ push-location $path_root $build_args += $flag_build_mode_dll $build_args += $flag_output_path + $module_dll # $build_args += ($flag_collection + $pkg_collection_thirdparty) - $build_args += $flag_micro_architecture_native - $build_args += $flag_use_separate_modules + # $build_args += $flag_micro_architecture_native + # $build_args += $flag_use_separate_modules $build_args += $flag_thread_count + $CoreCount_Physical $build_args += $flag_optimize_none # $build_args += $flag_optimize_minimal