SectrPrototype/code/ui_floating.odin

190 lines
4.2 KiB
Odin

package sectr
UI_Floating :: struct {
label : string,
using links : DLL_NodePN(UI_Floating),
captures : rawptr,
builder : UI_FloatingBuilder,
queued : b32,
}
UI_FloatingBuilder :: #type proc( captures : rawptr ) -> (became_active : b32)
// Overlapping/Stacking window/frame manager
// AKA: Stacking or Floating Window Manager
UI_FloatingManager :: struct {
using links : DLL_NodeFL(UI_Floating),
build_queue : Array(UI_Floating),
tracked : HMapZPL(UI_Floating),
}
ui_floating_startup :: proc( self : ^UI_FloatingManager, allocator : Allocator, build_queue_cap, tracked_cap : u64, dbg_name : string = "" ) -> AllocatorError
{
error : AllocatorError
queue_dbg_name := str_intern(str_fmt_tmp("%s: build_queue", dbg_name))
self.build_queue, error = array_init_reserve( UI_Floating, allocator, build_queue_cap, dbg_name = queue_dbg_name.str )
if error != AllocatorError.None
{
ensure(false, "Failed to allocate the build_queue")
return error
}
tracked_dbg_name := str_intern(str_fmt_tmp("%s: tracked", dbg_name))
self.tracked, error = zpl_hmap_init_reserve( UI_Floating, allocator, tracked_cap, dbg_name = tracked_dbg_name.str )
if error != AllocatorError.None
{
ensure(false, "Failed to allocate tracking table")
return error
}
return error
}
ui_floating_just_builder :: #force_inline proc( label : string, builder : UI_FloatingBuilder ) -> ^UI_Floating
{
No_Captures : rawptr = nil
return ui_floating_with_capture(label, No_Captures, builder)
}
ui_floating_with_capture :: proc( label : string, captures : rawptr = nil, builder : UI_FloatingBuilder ) -> ^UI_Floating
{
entry := UI_Floating {
label = label,
captures = captures,
builder = builder,
}
floating := get_state().ui_floating_context
array_append( & floating.build_queue, entry )
return nil
}
@(deferred_none = ui_floating_manager_end)
ui_floating_manager :: proc ( manager : ^UI_FloatingManager )
{
ui_floating_manager_begin(manager)
}
ui_floating_manager_begin :: proc ( manager : ^UI_FloatingManager )
{
state := get_state()
state.ui_floating_context = manager
}
ui_floating_manager_end :: proc()
{
state := get_state()
floating := & state.ui_floating_context
ui_floating_build()
floating = nil
}
ui_floating_build :: proc()
{
ui := ui_context()
screen_ui := cast(^UI_ScreenState) ui
using floating := get_state().ui_floating_context
for to_enqueue in array_to_slice_num( build_queue)
{
key := ui_key_from_string(to_enqueue.label)
lookup := zpl_hmap_get( & tracked, transmute(u64) key )
// Check if entry is already present
if lookup != nil && lookup.prev != nil && lookup.next != nil {
lookup.captures = to_enqueue.captures
lookup.builder = to_enqueue.builder
lookup.queued = true
continue
}
if lookup == nil {
error : AllocatorError
lookup, error = zpl_hmap_set( & tracked, transmute(u64) key, to_enqueue )
if error != AllocatorError.None {
ensure(false, "Failed to allocate entry to hashtable")
continue
}
lookup.queued = true
}
else {
lookup.captures = to_enqueue.captures
lookup.builder = to_enqueue.builder
lookup.queued = true
continue
}
if first == nil {
first = lookup
last = lookup
continue
}
last.next = lookup
last = lookup
}
array_clear(build_queue)
for entry := first; entry != nil; entry = entry.next
{
using entry
if ! queued
{
if entry == first
{
first = entry.next
entry.next = nil
continue
}
if entry == last
{
last = last.prev
last.prev = nil
entry.prev = nil
continue
}
left := entry.prev
right := entry.next
left.next = right
right.prev = left
entry.prev = nil
entry.next = nil
}
if builder( captures ) && entry != last
{
PopEntry:
{
if first == nil {
first = entry
last = entry
break PopEntry
}
if entry == first
{
first = entry.next
entry.next = nil
break PopEntry
}
if entry == last
{
last = last.prev
last.prev = nil
entry.prev = nil
break PopEntry
}
left := entry.prev
right := entry.next
left.next = right
right.prev = left
}
last.next = entry
last = entry
}
queued = false
}
}