Got this thing to compile and seem operable at runtime
There was quite a few errors with the hashtable (not suprised). I need to use it more to see if it fails to work properly. For now it should be fine enough for prototyping
This commit is contained in:
parent
7332644515
commit
9b3bc6fd68
@ -143,13 +143,8 @@ startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^
|
||||
// projection = rl.CameraProjection.ORTHOGRAPHIC,
|
||||
// }
|
||||
|
||||
frame_1.color = Color_BG_TextBox
|
||||
box_set_size( & frame_1, { 100, 50 } * CM_Per_Point )
|
||||
|
||||
frame_2.color = Color_BG_TextBox_Green
|
||||
box_set_size( & frame_2, { 60, 100 } * CM_Per_Point )
|
||||
|
||||
|
||||
// Setup workspace UI state
|
||||
ui_startup( & workspace.ui, persistent_allocator() )
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -208,6 +203,9 @@ reload :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ L
|
||||
// font_provider_data := & get_state().font_provider_data
|
||||
// font_provider_data.font_cache.allocator = arena_allocator( & font_provider_data.font_arena )
|
||||
|
||||
// Have to reload allocators for all dynamic allocating data-structures.
|
||||
ui_reload( & get_state().project.workspace.ui, persistent_allocator() )
|
||||
|
||||
log("Module reloaded")
|
||||
}
|
||||
|
||||
|
@ -115,6 +115,11 @@ State :: struct {
|
||||
font_squidgy_slimes : FontID,
|
||||
font_rec_mono_semicasual_reg : FontID,
|
||||
default_font : FontID,
|
||||
|
||||
// There are two potential UI contextes for this prototype so far,
|
||||
// the screen-space UI and the current workspace UI.
|
||||
// This is used so that the ui api doesn't need to have the user pass the context every single time.
|
||||
ui_context : UI_State,
|
||||
}
|
||||
|
||||
get_state :: proc "contextless" () -> ^ State {
|
||||
|
@ -4,12 +4,14 @@ package sectr
|
||||
|
||||
import "base:builtin"
|
||||
import "base:runtime"
|
||||
import c "core:c/libc"
|
||||
import "core:hash"
|
||||
import "core:mem"
|
||||
import "core:mem/virtual"
|
||||
import "core:os"
|
||||
import "core:path/filepath"
|
||||
|
||||
import c "core:c/libc"
|
||||
|
||||
Byte :: 1
|
||||
Kilobyte :: 1024 * Byte
|
||||
Megabyte :: 1024 * Kilobyte
|
||||
@ -32,6 +34,7 @@ terabyte :: proc ( tb : $ integer_type ) -> integer_type {
|
||||
}
|
||||
|
||||
copy :: builtin.copy
|
||||
crc32 :: hash.crc32
|
||||
Allocator :: mem.Allocator
|
||||
AllocatorError :: mem.Allocator_Error
|
||||
alloc :: mem.alloc
|
||||
@ -53,6 +56,39 @@ get_bounds :: proc {
|
||||
view_get_bounds,
|
||||
}
|
||||
|
||||
//region Stack - Basic fixed-size stack container
|
||||
Stack :: struct ( $ Type : typeid, $ Size : i32 ) {
|
||||
idx : i32,
|
||||
items : [ Size ] Type,
|
||||
}
|
||||
|
||||
stack_push :: proc( stack : ^ $ StackType / Stack( $ Type, $ Size ), value : Type ) {
|
||||
using stack
|
||||
verify( idx < len( items ), "Attempted to push on a full stack" )
|
||||
|
||||
items[ idx ] = value
|
||||
idx += 1
|
||||
}
|
||||
|
||||
stack_pop :: proc( stack : ^ $ StackType / Stack( $ Type, $ Size ) ) {
|
||||
using stack
|
||||
verify( idx > 0, "Attempted to pop an empty stack" )
|
||||
|
||||
idx -= 1
|
||||
}
|
||||
|
||||
stack_peek :: proc ( stack : ^ Stack( $ Type, $ Size ) ) -> ^ Type {
|
||||
using stack
|
||||
return & items[idx]
|
||||
}
|
||||
//endregion Stack
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO(Ed) : This is extremely jank, Raylib requires a 'heap' allocator with the way it works.
|
||||
|
@ -6,6 +6,7 @@ package sectr
|
||||
|
||||
import "core:c/libc"
|
||||
import "core:mem"
|
||||
import "core:slice"
|
||||
|
||||
Array :: struct ( $ Type : typeid ) {
|
||||
allocator : Allocator,
|
||||
@ -28,7 +29,7 @@ array_init :: proc( $ Type : typeid, allocator : Allocator ) -> ( Array(Type), A
|
||||
|
||||
array_init_reserve :: proc( $ Type : typeid, allocator : Allocator, capacity : u64 ) -> ( Array(Type), AllocatorError )
|
||||
{
|
||||
raw_data, result_code = alloc( capacity * size_of(Type), allocator = allocator )
|
||||
raw_data, result_code := alloc( int(capacity) * size_of(Type), allocator = allocator )
|
||||
result : Array( Type)
|
||||
result.data = cast( [^] Type ) raw_data
|
||||
result.allocator = allocator
|
||||
@ -157,6 +158,10 @@ array_fill :: proc ( array : ^ Array( $ Type ), begin, end : u64, value : Type )
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO(Ed) : Bench this?
|
||||
// data_slice := slice_ptr( ptr_offset( data, begin ), end - begin )
|
||||
// slice.fill( data_slice, cast(int) value )
|
||||
|
||||
for id := begin; id < end; id += 1 {
|
||||
data[ id ] = value
|
||||
}
|
||||
@ -172,7 +177,7 @@ array_free :: proc( array : ^ Array( $ Type ) ) {
|
||||
array_grow :: proc( array : ^ Array( $ Type ), min_capacity : u64 ) -> AllocatorError
|
||||
{
|
||||
using array
|
||||
new_capacity = grow_formula( capacity )
|
||||
new_capacity := array_grow_formula( capacity )
|
||||
|
||||
if new_capacity < min_capacity {
|
||||
new_capacity = min_capacity
|
||||
@ -211,7 +216,7 @@ array_resize :: proc ( array : ^ Array( $ Type ), num : u64 ) -> AllocatorError
|
||||
{
|
||||
if array.capacity < num
|
||||
{
|
||||
grow_result := array_grow( array, capacity )
|
||||
grow_result := array_grow( array, array.capacity )
|
||||
if grow_result != AllocatorError.None {
|
||||
return grow_result
|
||||
}
|
||||
@ -225,14 +230,14 @@ array_set_capacity :: proc( array : ^ Array( $ Type ), new_capacity : u64 ) -> A
|
||||
{
|
||||
using array
|
||||
if new_capacity == capacity {
|
||||
return true
|
||||
return AllocatorError.None
|
||||
}
|
||||
if new_capacity < num {
|
||||
num = new_capacity
|
||||
return true
|
||||
return AllocatorError.None
|
||||
}
|
||||
|
||||
raw_data, result_code = alloc( new_capacity * size_of(Type), allocator = allocator )
|
||||
raw_data, result_code := alloc( cast(int) new_capacity * size_of(Type), allocator = allocator )
|
||||
data = cast( [^] Type ) raw_data
|
||||
capacity = new_capacity
|
||||
return result_code
|
||||
|
@ -3,6 +3,9 @@
|
||||
// with hot-reloads...
|
||||
package sectr
|
||||
|
||||
import "core:slice"
|
||||
|
||||
|
||||
// Note(Ed) : See core:hash for hasing procs.
|
||||
|
||||
// This might be problematic...
|
||||
@ -17,11 +20,11 @@ HT_FindResult :: struct {
|
||||
|
||||
HashTable_Entry :: struct ( $ Type : typeid) {
|
||||
key : u64,
|
||||
next : u64,
|
||||
next : i64,
|
||||
value : Type,
|
||||
}
|
||||
|
||||
HashTable :: struct ( $ Type : typeid) {
|
||||
HashTable :: struct ( $ Type : typeid ) {
|
||||
hashes : Array( i64 ),
|
||||
entries : Array( HashTable_Entry(Type) ),
|
||||
}
|
||||
@ -30,7 +33,7 @@ hashtable_init :: proc( $ Type : typeid, allocator : Allocator ) -> ( HashTable(
|
||||
return hashtable_init_reserve( Type, allocator )
|
||||
}
|
||||
|
||||
hashtable_init_reserve :: proc ( $ Type : typeid, allcoator : Allocator, num : u64 ) -> ( HashTable( Type), AllocatorError )
|
||||
hashtable_init_reserve :: proc ( $ Type : typeid, allocator : Allocator, num : u64 ) -> ( HashTable( Type), AllocatorError )
|
||||
{
|
||||
result : HashTable(Type)
|
||||
hashes_result, entries_result : AllocatorError
|
||||
@ -40,8 +43,11 @@ hashtable_init_reserve :: proc ( $ Type : typeid, allcoator : Allocator, num : u
|
||||
ensure( false, "Failed to allocate hashes array" )
|
||||
return result, hashes_result
|
||||
}
|
||||
array_resize( & result.hashes, num )
|
||||
slice.fill( slice_ptr( result.hashes.data, cast(int) result.hashes.num), -1 )
|
||||
// array_fill( result.hashes, 0, num - 1, -1 )
|
||||
|
||||
result.entries, entries_result = array_init_reserve( allocator, num )
|
||||
result.entries, entries_result = array_init_reserve( HashTable_Entry(Type), allocator, num )
|
||||
if entries_result != AllocatorError.None {
|
||||
ensure( false, "Failed to allocate entries array" )
|
||||
return result, entries_result
|
||||
@ -59,10 +65,10 @@ hashtable_clear :: proc( ht : ^ HashTable( $ Type ) ) {
|
||||
array_clear( entries )
|
||||
}
|
||||
|
||||
hashtable_destroy :: proc( ht : ^ HashTable( $ Type ) ) {
|
||||
if hashes.data && hashes.capacity {
|
||||
array_free( hashes )
|
||||
array_free( entries )
|
||||
hashtable_destroy :: proc( using ht : ^ HashTable( $ Type ) ) {
|
||||
if hashes.data != nil && hashes.capacity > 0 {
|
||||
array_free( & hashes )
|
||||
array_free( & entries )
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,9 +76,9 @@ hashtable_get :: proc( ht : ^ HashTable( $ Type ), key : u64 ) -> ^ Type
|
||||
{
|
||||
using ht
|
||||
|
||||
id := hashtable_find( key ).entry_index
|
||||
id := hashtable_find( ht, key ).entry_index
|
||||
if id >= 0 {
|
||||
return & entries[id].value
|
||||
return & entries.data[id].value
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -95,11 +101,12 @@ hashtable_map_mut :: proc( ht : ^ HashTable( $ Type), map_proc : HT_MapMutProc )
|
||||
}
|
||||
|
||||
hashtable_grow :: proc( ht : ^ HashTable( $ Type ) ) -> AllocatorError {
|
||||
using ht
|
||||
new_num := array_grow_formula( entries.num )
|
||||
return rehash( ht, new_num )
|
||||
return hashtable_rehash( ht, new_num )
|
||||
}
|
||||
|
||||
hashtable_rehash :: proc ( ht : ^ HashTable( $ Type ), new_num : i64 ) -> AllocatorError
|
||||
hashtable_rehash :: proc ( ht : ^ HashTable( $ Type ), new_num : u64 ) -> AllocatorError
|
||||
{
|
||||
last_added_index : i64
|
||||
|
||||
@ -109,30 +116,27 @@ hashtable_rehash :: proc ( ht : ^ HashTable( $ Type ), new_num : i64 ) -> Alloca
|
||||
return init_result
|
||||
}
|
||||
|
||||
for id := 0; id < new_ht.hashes.num; id += 1 {
|
||||
new_ht.hashes[id] = -1
|
||||
}
|
||||
// for id : u64 = 0; id < new_ht.hashes.num; id += 1 {
|
||||
// new_ht.hashes.data[id] = -1
|
||||
// }
|
||||
slice.fill( slice_ptr( new_ht.hashes.data, cast(int) new_ht.hashes.num ), -1 )
|
||||
|
||||
for id := 0; id < ht.entries.num; id += 1 {
|
||||
for id : u64 = 0; id < ht.entries.num; id += 1 {
|
||||
find_result : HT_FindResult
|
||||
|
||||
if new_ht.hashes.num == 0 {
|
||||
hashtable_grow( new_ht )
|
||||
}
|
||||
|
||||
entry = & entries[id]
|
||||
entry := & ht.entries.data[id]
|
||||
find_result = hashtable_find( & new_ht, entry.key )
|
||||
last_added_index = hashtable_add_entry( & new_ht, entry.key )
|
||||
|
||||
if find_result.prev_index < 0 {
|
||||
new_ht.hashes[ find_result.hash_index ] = last_added_index
|
||||
new_ht.hashes.data[ find_result.hash_index ] = last_added_index
|
||||
}
|
||||
else {
|
||||
new_ht.hashes[ find_result.prev_index ].next = last_added_index
|
||||
new_ht.entries.data[ find_result.prev_index ].next = last_added_index
|
||||
}
|
||||
|
||||
new_ht.entries[ last_added_index ].next = find_result.entry_index
|
||||
new_ht.entries[ last_added_index ].value = entry.value
|
||||
new_ht.entries.data[ last_added_index ].next = find_result.entry_index
|
||||
new_ht.entries.data[ last_added_index ].value = entry.value
|
||||
}
|
||||
|
||||
hashtable_destroy( ht )
|
||||
@ -177,22 +181,22 @@ hashtable_remove_entry :: proc( ht : ^ HashTable( $ Type ), id : i64 ) {
|
||||
array_remove_at( & ht.entries, id )
|
||||
}
|
||||
|
||||
hashtable_set :: proc( ht : ^ HashTable( $ Type), key : u64, value : Type ) -> AllocatorError
|
||||
hashtable_set :: proc( ht : ^ HashTable( $ Type), key : u64, value : Type ) -> (^ Type, AllocatorError)
|
||||
{
|
||||
using ht
|
||||
|
||||
id := 0
|
||||
id : i64 = 0
|
||||
find_result : HT_FindResult
|
||||
|
||||
if hashes.num == 0
|
||||
if hashtable_full( ht )
|
||||
{
|
||||
grow_result := hashtable_grow( ht )
|
||||
grow_result := hashtable_grow(ht)
|
||||
if grow_result != AllocatorError.None {
|
||||
return grow_result
|
||||
return nil, grow_result
|
||||
}
|
||||
}
|
||||
|
||||
find_result = hashtable_find( key )
|
||||
find_result = hashtable_find( ht, key )
|
||||
if find_result.entry_index >= 0 {
|
||||
id = find_result.entry_index
|
||||
}
|
||||
@ -200,26 +204,26 @@ hashtable_set :: proc( ht : ^ HashTable( $ Type), key : u64, value : Type ) -> A
|
||||
{
|
||||
id = hashtable_add_entry( ht, key )
|
||||
if find_result.prev_index >= 0 {
|
||||
entries[ find_result.prev_index ].next = id
|
||||
entries.data[ find_result.prev_index ].next = id
|
||||
}
|
||||
else {
|
||||
hashes[ find_result.hash_index ] = id
|
||||
hashes.data[ find_result.hash_index ] = id
|
||||
}
|
||||
}
|
||||
|
||||
entries[id].value = value
|
||||
entries.data[id].value = value
|
||||
|
||||
if hashtable_full( ht ) {
|
||||
return hashtable_grow( ht )
|
||||
return & entries.data[id].value, hashtable_grow( ht )
|
||||
}
|
||||
|
||||
return AllocatorError.None
|
||||
return & entries.data[id].value, AllocatorError.None
|
||||
}
|
||||
|
||||
hashtable_slot :: proc( ht : ^ HashTable( $ Type), key : u64 ) -> i64 {
|
||||
using ht
|
||||
for id := 0; id < hashes.num; id += 1 {
|
||||
if hashes[id] == key {
|
||||
for id : i64 = 0; id < hashes.num; id += 1 {
|
||||
if hashes.data[id] == key {
|
||||
return id
|
||||
}
|
||||
}
|
||||
@ -228,33 +232,34 @@ hashtable_slot :: proc( ht : ^ HashTable( $ Type), key : u64 ) -> i64 {
|
||||
|
||||
hashtable_add_entry :: proc( ht : ^ HashTable( $ Type), key : u64 ) -> i64 {
|
||||
using ht
|
||||
entry : HashTable_Entry = { key, -1 }
|
||||
id := entries.num
|
||||
array_append( entries, entry )
|
||||
entry : HashTable_Entry(Type) = { key, -1, {} }
|
||||
id := cast(i64) entries.num
|
||||
array_append( & entries, entry )
|
||||
return id
|
||||
}
|
||||
|
||||
hashtable_find :: proc( ht : ^ HashTable( $ Type), key : u64 ) -> HT_FindResult
|
||||
{
|
||||
using ht
|
||||
find_result : HT_FindResult = { -1, -1, -1 }
|
||||
result : HT_FindResult = { -1, -1, -1 }
|
||||
|
||||
if hashes.num > 0 {
|
||||
result.hash_index = key % hash.num
|
||||
result.entry_index = hashes[ result.hash_index ]
|
||||
result.hash_index = cast(i64)( key % hashes.num )
|
||||
result.entry_index = hashes.data[ result.hash_index ]
|
||||
|
||||
for ; result.entry_index >= 0; {
|
||||
if entries[ result.entry_index ].key == key {
|
||||
for ; result.entry_index >= 0; {
|
||||
if entries.data[ result.entry_index ].key == key {
|
||||
break
|
||||
}
|
||||
|
||||
result.prev_index = result.entry_index
|
||||
result.entry_index = entries[ result.entry_index ].next
|
||||
result.entry_index = entries.data[ result.entry_index ].next
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
hashtable_full :: proc( ht : ^ HashTable( $ Type) ) -> b32 {
|
||||
return 0.75 * hashes.num < entries.num
|
||||
hashtable_full :: proc( using ht : ^ HashTable( $ Type) ) -> b32 {
|
||||
result : b32 = entries.num > u64(0.75 * cast(f64) hashes.num)
|
||||
return result
|
||||
}
|
||||
|
@ -176,8 +176,14 @@ update :: proc( delta_time : f64 ) -> b32
|
||||
|
||||
//region Imgui Tick
|
||||
{
|
||||
// Layout
|
||||
ui_context := & state.project.workspace.ui
|
||||
|
||||
// Build Graph (Determines if layout is dirty)
|
||||
ui_graph_build_begin( ui_context )
|
||||
|
||||
|
||||
|
||||
// Regnerate compute if layout is dirty.
|
||||
}
|
||||
// endregion
|
||||
|
||||
|
92
code/ui.odin
92
code/ui.odin
@ -138,7 +138,11 @@ UI_Layout :: struct {
|
||||
|
||||
corner_radii : [Corner.Count]f32,
|
||||
|
||||
size : UI_ScalarConstraint,
|
||||
// TODO(Ed) I problably won't use this as I can determine
|
||||
// the size of content manually when needed and make helper procs...
|
||||
// size_to_content : b32,
|
||||
|
||||
size : Vec2,
|
||||
}
|
||||
|
||||
UI_Style :: struct {
|
||||
@ -187,11 +191,17 @@ UI_Box :: struct {
|
||||
disabled_time : f32,
|
||||
}
|
||||
|
||||
Layout_Stack_Size :: 512
|
||||
Style_Stack_Size :: 512
|
||||
|
||||
UI_State :: struct {
|
||||
box_cache : HashTable( UI_Box ),
|
||||
|
||||
box_tree_dirty : b32,
|
||||
root : ^ UI_Box,
|
||||
root : ^ UI_Box,
|
||||
|
||||
layout_dirty : b32,
|
||||
layout_stack : Stack( UI_Layout, Layout_Stack_Size ),
|
||||
style_stack : Stack( UI_Style, Style_Stack_Size ),
|
||||
|
||||
hot : UI_Key,
|
||||
active : UI_Key,
|
||||
@ -202,7 +212,83 @@ UI_State :: struct {
|
||||
// drag_state data : string,
|
||||
}
|
||||
|
||||
ui_key_from_string :: proc ( value : string ) -> UI_Key {
|
||||
key := cast(UI_Key) crc32( transmute([]byte) value )
|
||||
return key
|
||||
}
|
||||
|
||||
ui_box_equal :: proc ( a, b : ^ UI_Box ) -> b32 {
|
||||
BoxSize :: size_of(UI_Box)
|
||||
hash_a := crc32( transmute([]u8) slice_ptr( a, BoxSize ) )
|
||||
hash_b := crc32( transmute([]u8) slice_ptr( b, BoxSize ) )
|
||||
result : b32 = hash_a == hash_b
|
||||
return result
|
||||
}
|
||||
|
||||
ui_startup :: proc ( ui : ^ UI_State, cache_allocator : Allocator ) {
|
||||
ui := ui
|
||||
ui^ = {}
|
||||
|
||||
box_cache, allocation_error := hashtable_init_reserve( UI_Box, cache_allocator, Kilobyte * 8 )
|
||||
verify( allocation_error != AllocatorError.None, "Failed to allocate box cache" )
|
||||
ui.box_cache = box_cache
|
||||
}
|
||||
|
||||
ui_reload :: proc ( ui : ^ UI_State, cache_allocator : Allocator ) {
|
||||
// We need to repopulate Allocator references
|
||||
ui.box_cache.entries.allocator = cache_allocator
|
||||
ui.box_cache.hashes.allocator = cache_allocator
|
||||
}
|
||||
|
||||
// TODO(Ed) : Is this even needed?
|
||||
ui_shutdown :: proc () {
|
||||
}
|
||||
|
||||
ui_graph_build_begin :: proc ( ui : ^ UI_State )
|
||||
{
|
||||
ui_context := & get_state().ui_context
|
||||
ui_context = ui
|
||||
using ui_context
|
||||
|
||||
box : UI_Box = {}
|
||||
box.label = "root#001"
|
||||
box.key = ui_key_from_string( box.label )
|
||||
box.layout = stack_peek( & layout_stack ) ^
|
||||
box.style = stack_peek( & style_stack ) ^
|
||||
|
||||
cached_box := hashtable_get( & box_cache, cast(u64) box.key )
|
||||
|
||||
if cached_box != nil {
|
||||
layout_dirty &= ! ui_box_equal( & box, cached_box )
|
||||
}
|
||||
else {
|
||||
layout_dirty = true
|
||||
}
|
||||
|
||||
set_result, set_error: = hashtable_set( & box_cache, cast(u64) box.key, box )
|
||||
verify( set_error != AllocatorError.None, "Failed to set hashtable due to allocator error" )
|
||||
root = set_result
|
||||
}
|
||||
|
||||
ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box)
|
||||
{
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
ui_layout_push :: proc ( preset : UI_Layout ) {
|
||||
|
||||
}
|
||||
|
||||
ui_layout_pop :: proc () {
|
||||
|
||||
}
|
||||
|
||||
ui_layout_push_size :: proc( size : Vec2 ) {
|
||||
|
||||
}
|
||||
|
||||
ui_style_push :: proc ( preset : UI_Style ) {
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user