Progress on fleshing out rendering (Getting ui ready to render in "layer batches")
This commit is contained in:
parent
136fef65c4
commit
ce1d31f0d4
@ -172,7 +172,8 @@ LRU_reload :: proc( cache : ^LRU_Cache, allocator : Allocator )
|
||||
|
||||
LRU_hash_key :: #force_inline proc( key : u64 ) -> ( hash : u64 ) {
|
||||
bytes := transmute( [8]byte ) key
|
||||
hash = fnv64a( bytes[:] )
|
||||
// hash = fnv64a( bytes[:] )
|
||||
hash = cast(u64) crc64( bytes[:] )
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -754,8 +754,8 @@ reset_batch_codepoint_state :: proc( ctx : ^Context ) {
|
||||
shape_text_cached :: proc( ctx : ^Context, font : FontID, text_utf8 : string ) -> ^ShapedText
|
||||
{
|
||||
font := font
|
||||
// hash := cast(u64) crc32( transmute([]u8) text_utf8 )
|
||||
hash := label_hash( text_utf8 )
|
||||
hash := cast(u64) crc32( transmute([]u8) text_utf8 )
|
||||
// hash := label_hash( text_utf8 )
|
||||
|
||||
shape_cache := & ctx.shape_cache
|
||||
state := & ctx.shape_cache.state
|
||||
|
@ -1,6 +1,7 @@
|
||||
package VEFontCache
|
||||
|
||||
import "core:hash"
|
||||
crc64 :: hash.crc64_xz
|
||||
crc32 :: hash.crc32
|
||||
fnv64a :: hash.fnv64a
|
||||
|
||||
|
@ -64,7 +64,7 @@ hmap_chained_init :: proc( $HMapChainedType : typeid/HMapChained($Type), lookup_
|
||||
) -> (table : HMapChained(Type), error : AllocatorError)
|
||||
{
|
||||
header_size := size_of(HMapChainedHeader(Type))
|
||||
size := header_size + int(lookup_capacity) * size_of( ^HMapChainedSlot(Type))
|
||||
size := header_size + int(lookup_capacity) * size_of( ^HMapChainedSlot(Type)) + size_of(int)
|
||||
|
||||
raw_mem : rawptr
|
||||
raw_mem, error = alloc( size, allocator = allocator )
|
||||
@ -72,7 +72,7 @@ hmap_chained_init :: proc( $HMapChainedType : typeid/HMapChained($Type), lookup_
|
||||
|
||||
pool_bucket_cap := pool_bucket_cap
|
||||
if pool_bucket_cap == 0 {
|
||||
pool_bucket_cap = cast(uint) int(lookup_capacity) * size_of( HMapChainedSlot(Type))
|
||||
pool_bucket_cap = cast(uint) int(lookup_capacity) * size_of( HMapChainedSlot(Type)) * 2
|
||||
}
|
||||
|
||||
table.header = cast( ^HMapChainedHeader(Type)) raw_mem
|
||||
@ -205,7 +205,9 @@ hmap_chained_set :: proc( self : HMapChained($Type), key : u64, value : Type ) -
|
||||
slot_size := size_of(HMapChainedSlot(Type))
|
||||
if surface_slot == nil
|
||||
{
|
||||
block, error := pool_grab(pool, false)
|
||||
raw_mem, error := alloc( size_of(HMapChainedSlot(Type)), allocator = pool.backing)
|
||||
block := slice_ptr(transmute([^]byte) raw_mem, slot_size)
|
||||
// block, error := pool_grab(pool, false)
|
||||
surface_slot := transmute( ^HMapChainedSlot(Type)) raw_data(block)
|
||||
surface_slot^ = {}
|
||||
|
||||
@ -240,8 +242,11 @@ hmap_chained_set :: proc( self : HMapChained($Type), key : u64, value : Type ) -
|
||||
error : AllocatorError
|
||||
if slot.next == nil
|
||||
{
|
||||
raw_mem : rawptr
|
||||
block : []byte
|
||||
block, error = pool_grab(pool, false)
|
||||
raw_mem, error = alloc( size_of(HMapChainedSlot(Type)), allocator = pool.backing)
|
||||
block = slice_ptr(transmute([^]byte) raw_mem, slot_size)
|
||||
// block, error = pool_grab(pool, false)
|
||||
next := transmute( ^HMapChainedSlot(Type)) raw_data(block)
|
||||
|
||||
slot.next = next
|
||||
|
@ -19,6 +19,8 @@ Color_Blue :: RGBA8 { 90, 90, 230, 255 }
|
||||
Color_Red :: RGBA8 { 230, 90, 90, 255 }
|
||||
Color_White :: RGBA8 { 255, 255, 255, 255 }
|
||||
|
||||
Color_Screen_Center_Dot :: RGBA8 { 180, 180, 180, 10}
|
||||
|
||||
Color_Transparent :: RGBA8 { 0, 0, 0, 0 }
|
||||
Color_BG :: RGBA8 { 55, 55, 60, 255 }
|
||||
Color_BG_TextBox :: RGBA8 { 32, 32, 32, 180 }
|
||||
|
@ -241,6 +241,10 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
|
||||
load_action = .CLEAR,
|
||||
clear_value = { 0, 0, 0, 1 }
|
||||
}
|
||||
render_data.pass_actions.empty_action.colors[0] = sokol_gfx.Color_Attachment_Action {
|
||||
load_action = .LOAD,
|
||||
clear_value = { 0, 0, 0, 1 }
|
||||
}
|
||||
}
|
||||
|
||||
// Setup sokol_gp
|
||||
@ -277,7 +281,7 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
|
||||
}
|
||||
|
||||
// Setup the screen ui state
|
||||
if false
|
||||
if true
|
||||
{
|
||||
ui_startup( & screen_ui.base, cache_allocator = persistent_slab_allocator() )
|
||||
ui_floating_startup( & screen_ui.floating, 1 * Kilobyte, 1 * Kilobyte, persistent_slab_allocator(), "screen ui floating manager" )
|
||||
@ -292,7 +296,7 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
|
||||
|
||||
// Demo project setup
|
||||
// TODO(Ed): This will eventually have to occur when the user either creates or loads a workspace. I don't know
|
||||
if false
|
||||
if true
|
||||
{
|
||||
using project
|
||||
path = str_intern("./")
|
||||
|
@ -11,13 +11,45 @@ import gp "thirdparty:sokol/gp"
|
||||
|
||||
PassActions :: struct {
|
||||
bg_clear_black : gfx.Pass_Action,
|
||||
|
||||
empty_action : gfx.Pass_Action,
|
||||
}
|
||||
|
||||
RenderState :: struct {
|
||||
pass_actions : PassActions,
|
||||
}
|
||||
|
||||
gp_set_color :: #force_inline proc( color : RGBA8 ) {
|
||||
color := normalize_rgba8(color);
|
||||
gp.set_color( color.r, color.g, color.b, color.a )
|
||||
}
|
||||
|
||||
draw_filled_circle :: proc(x, y, radius: f32, edges: int) {
|
||||
if edges < 3 do return // Need at least 3 edges to form a shape
|
||||
|
||||
triangles := make([]gp.Triangle, edges)
|
||||
// defer delete(triangles)
|
||||
|
||||
center := gp.Point{x, y}
|
||||
|
||||
for i in 0..<edges {
|
||||
angle1 := 2 * math.PI * f32(i) / f32(edges)
|
||||
angle2 := 2 * math.PI * f32(i+1) / f32(edges)
|
||||
|
||||
p1 := gp.Point{
|
||||
x + radius * math.cos(angle1),
|
||||
y + radius * math.sin(angle1),
|
||||
}
|
||||
p2 := gp.Point{
|
||||
x + radius * math.cos(angle2),
|
||||
y + radius * math.sin(angle2),
|
||||
}
|
||||
|
||||
triangles[i] = gp.Triangle{center, p1, p2}
|
||||
}
|
||||
|
||||
gp.draw_filled_triangles(raw_data(triangles), u32(len(triangles)))
|
||||
}
|
||||
|
||||
// Draw text using a string and normalized screen coordinates
|
||||
draw_text_string_pos_norm :: proc( content : string, id : FontID, size : f32, pos : Vec2, color := Color_White )
|
||||
{
|
||||
@ -69,6 +101,28 @@ render_mode_2d_workspace :: proc()
|
||||
profile(#procedure)
|
||||
state := get_state(); using state // TODO(Ed): Prefer passing static context to through the callstack
|
||||
cam := & project.workspace.cam
|
||||
|
||||
screen_extent := app_window.extent
|
||||
screen_size := app_window.extent * 2
|
||||
screen_ratio := screen_size.x * ( 1.0 / screen_size.y )
|
||||
|
||||
cam_zoom_ratio := 1.0 / cam.zoom
|
||||
|
||||
gp.begin( i32(screen_size.x), i32(screen_size.y) )
|
||||
gp.viewport(0, 0, i32(screen_size.x), i32(screen_size.y))
|
||||
gp.project( -screen_extent.x, screen_extent.x, screen_extent.y, -screen_extent.y )
|
||||
|
||||
gp.translate( cam.position.x * cam.zoom, cam.position.y * cam.zoom )
|
||||
gp.scale( cam.zoom, cam.zoom )
|
||||
|
||||
gp_set_color(Color_White)
|
||||
draw_filled_circle(0, 0, 4, 24)
|
||||
|
||||
// Enqueue gp pass to sokol gfx
|
||||
gfx.begin_pass( gfx.Pass { action = render_data.pass_actions.empty_action, swapchain = sokol_glue.swapchain() })
|
||||
gp.flush()
|
||||
gp.end()
|
||||
gfx.end_pass()
|
||||
}
|
||||
|
||||
render_mode_screenspace :: proc()
|
||||
@ -83,6 +137,23 @@ render_mode_screenspace :: proc()
|
||||
|
||||
render_screen_ui()
|
||||
|
||||
screen_extent := app_window.extent
|
||||
screen_size := app_window.extent * 2
|
||||
screen_ratio := screen_size.x * ( 1.0 / screen_size.y )
|
||||
|
||||
gp.begin( i32(screen_size.x), i32(screen_size.y) )
|
||||
gp.viewport(0, 0, i32(screen_size.x), i32(screen_size.y))
|
||||
gp.project( -screen_extent.x, screen_extent.x, screen_extent.y, -screen_extent.y )
|
||||
|
||||
gp_set_color(Color_Screen_Center_Dot)
|
||||
draw_filled_circle(0, 0, 2, 24)
|
||||
|
||||
gfx.begin_pass( gfx.Pass { action = render_data.pass_actions.empty_action, swapchain = sokol_glue.swapchain() })
|
||||
gp.flush()
|
||||
gp.end()
|
||||
gfx.end_pass()
|
||||
|
||||
|
||||
debug_draw_text :: proc( content : string, pos : Vec2, size : f32, color := Color_White, font : FontID = Font_Default )
|
||||
{
|
||||
state := get_state(); using state
|
||||
@ -125,7 +196,7 @@ render_mode_screenspace :: proc()
|
||||
debug.debug_text_vis = true
|
||||
if debug.debug_text_vis
|
||||
{
|
||||
fps_msg := str_fmt( "FPS: %d", frame)
|
||||
fps_msg := str_fmt( "FPS: %0.2f", fps_avg)
|
||||
fps_msg_width := measure_text_size( fps_msg, default_font, 12.0, 0.0 ).x
|
||||
fps_msg_pos := screen_get_corners().top_right - { fps_msg_width, 0 } - { 5, 5 }
|
||||
debug_draw_text( fps_msg, fps_msg_pos, 38.0, color = Color_Red )
|
||||
@ -133,7 +204,7 @@ render_mode_screenspace :: proc()
|
||||
// debug_text( "Screen Width : %v", rl.GetScreenWidth () )
|
||||
// debug_text( "Screen Height: %v", rl.GetScreenHeight() )
|
||||
// debug_text( "frametime_target_ms : %f ms", frametime_target_ms )
|
||||
debug_text( "frametime : %d ms", frame )
|
||||
debug_text( "frametime : %0.3f ms", frametime_delta32() )
|
||||
// debug_text( "frametime_last_elapsed_ms : %f ms", frametime_elapsed_ms )
|
||||
if replay.mode == ReplayMode.Record {
|
||||
debug_text( "Recording Input")
|
||||
@ -153,6 +224,40 @@ render_mode_screenspace :: proc()
|
||||
// rl.DrawCircleV( screen_to_render_pos(input.mouse.pos), 2, Color_BG )
|
||||
}
|
||||
|
||||
if false
|
||||
{
|
||||
ui := & project.workspace.ui
|
||||
|
||||
debug_text("Box Count (Workspace): %v", ui.built_box_count )
|
||||
|
||||
hot_box := ui_box_from_key( ui.curr_cache, ui.hot )
|
||||
active_box := ui_box_from_key( ui.curr_cache, ui.active )
|
||||
if hot_box != nil {
|
||||
debug_text("Worksapce Hot Box : %v", hot_box.label.str )
|
||||
debug_text("Workspace Hot Range2: %v", hot_box.computed.bounds.pts)
|
||||
}
|
||||
if active_box != nil{
|
||||
debug_text("Workspace Active Box: %v", active_box.label.str )
|
||||
}
|
||||
}
|
||||
|
||||
if false
|
||||
{
|
||||
ui := & screen_ui
|
||||
|
||||
debug_text("Box Count: %v", ui.built_box_count )
|
||||
|
||||
hot_box := ui_box_from_key( ui.curr_cache, ui.hot )
|
||||
active_box := ui_box_from_key( ui.curr_cache, ui.active )
|
||||
if hot_box != nil {
|
||||
debug_text("Hot Box : %v", hot_box.label.str )
|
||||
debug_text("Hot Range2: %v", hot_box.computed.bounds.pts)
|
||||
}
|
||||
if active_box != nil{
|
||||
debug_text("Active Box: %v", active_box.label.str )
|
||||
}
|
||||
}
|
||||
|
||||
render_text_layer()
|
||||
}
|
||||
|
||||
|
@ -166,8 +166,8 @@ update :: proc( delta_time : f64 ) -> b32
|
||||
|
||||
config.cam_max_zoom = 30
|
||||
config.cam_zoom_sensitivity_digital = 0.04
|
||||
config.cam_min_zoom = 0.04
|
||||
config.cam_zoom_mode = .Digital
|
||||
// config.cam_min_zoom = 0.04
|
||||
config.cam_zoom_mode = .Smooth
|
||||
switch config.cam_zoom_mode
|
||||
{
|
||||
case .Smooth:
|
||||
@ -196,7 +196,7 @@ update :: proc( delta_time : f64 ) -> b32
|
||||
{
|
||||
if is_within_screenspace(input.mouse.pos) {
|
||||
pan_velocity := input.mouse.delta * vec2(1, -1) * ( 1 / cam.zoom )
|
||||
cam.position -= pan_velocity
|
||||
cam.position += pan_velocity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,11 +65,15 @@ UI_Parent_Stack_Size :: 512
|
||||
// UI_Built_Boxes_Array_Size :: 8
|
||||
UI_Built_Boxes_Array_Size :: 128 * Kilobyte
|
||||
|
||||
UI_RenderQueueEntry :: struct {
|
||||
using links : DLL_NodePN(UI_RenderQueueEntry),
|
||||
box : ^UI_Box,
|
||||
UI_RenderEntry :: struct {
|
||||
info : UI_RenderBoxInfo,
|
||||
using links : DLL_NodeFull(UI_RenderEntry),
|
||||
parent : ^UI_RenderEntry,
|
||||
layer_id : i32,
|
||||
}
|
||||
|
||||
UI_RenderLayer :: DLL_NodeFL(UI_RenderEntry)
|
||||
|
||||
// TODO(Ed): Rename to UI_Context
|
||||
UI_State :: struct {
|
||||
// TODO(Ed) : Use these?
|
||||
@ -83,7 +87,7 @@ UI_State :: struct {
|
||||
curr_cache : ^HMapZPL( UI_Box ),
|
||||
|
||||
// render_queue_builder : SubArena,
|
||||
render_queue : DLL_NodeFL(UI_RenderQueueEntry),
|
||||
render_queue : Array(UI_RenderLayer),
|
||||
render_list : Array(UI_RenderBoxInfo),
|
||||
|
||||
null_box : ^UI_Box, // This was used with the Linked list interface...
|
||||
@ -146,7 +150,6 @@ ui_reload :: proc( ui : ^ UI_State, cache_allocator : Allocator )
|
||||
ui_shutdown :: proc() {
|
||||
}
|
||||
|
||||
|
||||
ui_graph_build_begin :: proc( ui : ^ UI_State, bounds : Vec2 = {} )
|
||||
{
|
||||
profile(#procedure)
|
||||
@ -195,76 +198,186 @@ ui_graph_build_end :: proc( ui : ^UI_State )
|
||||
computed.content = computed.bounds
|
||||
}
|
||||
|
||||
// Auto-layout and initial render_queue generation
|
||||
render_queue := array_to_slice(ui.render_queue)
|
||||
for current := root.first; current != nil; current = ui_box_tranverse_next( current )
|
||||
{
|
||||
if ! current.computed.fresh {
|
||||
ui_box_compute_layout( current )
|
||||
}
|
||||
|
||||
// TODO(Ed): Eventually put this into a sub-arena
|
||||
entry, error := new(UI_RenderEntry)
|
||||
(entry^) = UI_RenderEntry {
|
||||
info = {
|
||||
current.computed,
|
||||
current.style,
|
||||
current.text,
|
||||
current.layout.font_size,
|
||||
current.layout.border_width,
|
||||
},
|
||||
layer_id = current.ancestors -1,
|
||||
}
|
||||
|
||||
if entry.layer_id >= i32(ui.render_queue.num) {
|
||||
append( & ui.render_queue, UI_RenderLayer {})
|
||||
render_queue = array_to_slice(ui.render_queue)
|
||||
}
|
||||
|
||||
// push_back to next layer
|
||||
layer := & render_queue[entry.layer_id]
|
||||
if layer.first == nil {
|
||||
layer.first = entry
|
||||
}
|
||||
else if layer.last == nil {
|
||||
layer.first.next = entry
|
||||
entry.prev = layer.first
|
||||
layer.last = entry
|
||||
}
|
||||
else {
|
||||
layer.last.next = entry
|
||||
entry.prev = layer.last
|
||||
layer.last = entry
|
||||
}
|
||||
// dll_full_push_back( layer, entry, nil )
|
||||
|
||||
// If there is a parent entry, give it a reference to the child entry
|
||||
parent_entry : ^UI_RenderEntry
|
||||
if entry.layer_id > 0 {
|
||||
parent_layer := & render_queue[entry.layer_id - 1]
|
||||
parent_entry = parent_layer.last
|
||||
entry.parent = parent_entry
|
||||
|
||||
// dll_fl_append( parent_entry, entry )
|
||||
|
||||
if parent_entry.first == nil {
|
||||
parent_entry.first = entry
|
||||
}
|
||||
else {
|
||||
parent_entry.last = entry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// render_queue overlap corrections & render_list generation
|
||||
render_queue = array_to_slice(ui.render_queue)
|
||||
for & layer in render_queue
|
||||
{
|
||||
to_increment, error := make( Array(^UI_RenderEntry), 4 * Kilo )
|
||||
verify( error == .None, "Faied to make to_increment array.")
|
||||
|
||||
// Render order is "layer-based" and thus needs a new traversal done
|
||||
// ui.render_queue.first = new(UI_RenderQueueEntry)
|
||||
// ui.render_queue.first.box = root.first
|
||||
// for current := root.first.next; current != nil; current = ui_box_traverse_next_layer_based( current )
|
||||
// {
|
||||
// entry := new(UI_RenderQueueEntry)
|
||||
// entry.box = current
|
||||
// // Enqueue box to render
|
||||
// // Would be best to use a linked list tied to a sub-arena
|
||||
// dll_push_back( & ui.render_queue.first, entry )
|
||||
// ui.render_queue.last = entry
|
||||
// }
|
||||
for entry := layer.first; entry != nil; entry = entry.next
|
||||
{
|
||||
for neighbor := entry.next; neighbor != nil; neighbor = neighbor.next
|
||||
{
|
||||
if ! intersects_range2( entry.info.computed.bounds, neighbor.info.computed.bounds) do continue
|
||||
|
||||
// enqueued_id : i32 = 0
|
||||
// for enqueued := ui.render_queue.first; enqueued != nil; enqueued = enqueued.next
|
||||
// {
|
||||
// /*
|
||||
// For each enqueue box:
|
||||
// 1. Check to see if any overlap (traverse all Queued Entries and check to see if their bounds overlap)
|
||||
// 2. If they do, the box & its children must be popped off the current ancestry layer and moved to the start of the next
|
||||
// (This could problably be an iteration where we just pop and push_back until we reach the adjacent box of the same layer)
|
||||
// */
|
||||
// // other_id : i32 = 0
|
||||
// for other := enqueued.next; other != nil; other = other.next
|
||||
// {
|
||||
// if intersects_range2( enqueued.box.computed.bounds, other.box.computed.bounds)
|
||||
// {
|
||||
// // Intersection occured, other's box is on top, it and its children must be deferred to the next layer
|
||||
// next_layer_start := other.next
|
||||
// for ; next_layer_start != nil; next_layer_start = next_layer_start.next
|
||||
// {
|
||||
// if next_layer_start.box == other.box.first do break
|
||||
// }
|
||||
append( & to_increment, neighbor )
|
||||
} // for neighbor := entry.next; neighbor != nil; neighbor = neighbor.next
|
||||
} // for entry := layer.first; entry != nil; entry = entry.next
|
||||
|
||||
// other.next = next_layer_start
|
||||
// other.prev = next_layer_start.prev
|
||||
// next_layer_start.prev = other
|
||||
// enqueued.next = other.prev
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
to_inc_slice := array_to_slice(to_increment)
|
||||
for entry in to_inc_slice
|
||||
{
|
||||
pop_layer := render_queue[entry.layer_id]
|
||||
entry.layer_id += 1
|
||||
if entry.layer_id >= i32(ui.render_queue.num) {
|
||||
append( & ui.render_queue, UI_RenderLayer {} )
|
||||
render_queue = array_to_slice(ui.render_queue)
|
||||
}
|
||||
push_layer := render_queue[entry.layer_id]
|
||||
|
||||
// Queue should be optimized now to generate the final draw list
|
||||
// for enqueued := ui.render_queue.first; enqueued != nil; enqueued = enqueued.next
|
||||
// {
|
||||
// using enqueued.box
|
||||
// // Enqueue for rendering
|
||||
// array_append( & ui.render_list, UI_RenderBoxInfo {
|
||||
// computed,
|
||||
// style,
|
||||
// text,
|
||||
// layout.font_size,
|
||||
// layout.border_width,
|
||||
// })
|
||||
// }
|
||||
|
||||
// pop entry from layer
|
||||
prev := entry.prev
|
||||
prev.next = entry.next
|
||||
if entry == pop_layer.last {
|
||||
pop_layer.last = prev
|
||||
}
|
||||
|
||||
// push entry to next layer
|
||||
if push_layer.first == nil {
|
||||
push_layer.first = entry
|
||||
}
|
||||
else if push_layer.last == nil {
|
||||
push_layer.last = entry
|
||||
entry.prev = push_layer.first
|
||||
push_layer.first.next = entry
|
||||
entry.next = nil
|
||||
}
|
||||
else {
|
||||
push_layer.last.next = entry
|
||||
entry.prev = push_layer.last
|
||||
push_layer.last = entry
|
||||
entry.next = nil
|
||||
}
|
||||
|
||||
// increment children's layers
|
||||
if entry.first != nil
|
||||
{
|
||||
for child := entry.first; child != nil; child = ui_render_entry_tranverse( child )
|
||||
{
|
||||
pop_layer := render_queue[child.layer_id]
|
||||
child.layer_id += 1
|
||||
|
||||
if child.layer_id >= i32(ui.render_queue.num) {
|
||||
append( & ui.render_queue, UI_RenderLayer {})
|
||||
render_queue = array_to_slice(ui.render_queue)
|
||||
}
|
||||
push_layer := render_queue[child.layer_id]
|
||||
|
||||
// pop from current layer
|
||||
if child == pop_layer.first {
|
||||
pop_layer.first = nil
|
||||
}
|
||||
if child == pop_layer.last {
|
||||
pop_layer.last = child.prev
|
||||
}
|
||||
|
||||
// push_back to next layer
|
||||
if push_layer.first == nil {
|
||||
push_layer.first = child
|
||||
}
|
||||
else if push_layer.last == nil {
|
||||
push_layer.first.next = child
|
||||
child.prev = push_layer.first
|
||||
push_layer.last = child
|
||||
}
|
||||
else {
|
||||
push_layer.last.next = child
|
||||
child.prev = push_layer.last
|
||||
push_layer.last = child
|
||||
}
|
||||
} // for child := neighbor.first; child != nil; child = ui_render_entry_traverse_depth( child )
|
||||
} // if entry.first != nil
|
||||
} // for entry in to_inc_slice
|
||||
} // for & layer in render_queue
|
||||
}
|
||||
|
||||
get_state().ui_context = nil
|
||||
}
|
||||
|
||||
ui_render_entry_tranverse :: proc( entry : ^UI_RenderEntry ) -> ^UI_RenderEntry
|
||||
{
|
||||
// using state := get_state()
|
||||
parent := entry.parent
|
||||
if parent != nil
|
||||
{
|
||||
if parent.last != entry
|
||||
{
|
||||
if entry.next != nil do return entry.next
|
||||
}
|
||||
}
|
||||
|
||||
// If there any children, do them next.
|
||||
if entry.first != nil {
|
||||
return entry.first
|
||||
}
|
||||
|
||||
// Iteration exhausted
|
||||
return nil
|
||||
}
|
||||
|
||||
@(deferred_in = ui_graph_build_end)
|
||||
ui_graph_build :: #force_inline proc( ui : ^ UI_State ) { ui_graph_build_begin( ui ) }
|
||||
|
||||
|
@ -124,15 +124,18 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box)
|
||||
|
||||
ui_prev_cached_box :: #force_inline proc( box : ^UI_Box ) -> ^UI_Box { return hmap_zpl_get( ui_context().prev_cache, cast(u64) box.key ) }
|
||||
|
||||
// TODO(Ed): Rename to ui_box_tranverse_view_next
|
||||
// Traveral pritorizes immeidate children
|
||||
ui_box_tranverse_next :: proc "contextless" ( box : ^ UI_Box ) -> (^ UI_Box)
|
||||
ui_box_tranverse_next :: proc "contextless" ( box : ^ UI_Box, bypass_intersection_test := false ) -> (^ UI_Box)
|
||||
{
|
||||
using state := get_state()
|
||||
// If current has children, do them first
|
||||
if box.first != nil
|
||||
{
|
||||
// Check to make sure parent is present on the screen, if its not don't bother.
|
||||
is_app_ui := ui_context == & screen_ui
|
||||
if bypass_intersection_test {
|
||||
return box.first
|
||||
}
|
||||
if intersects_range2( ui_view_bounds(), box.computed.bounds) {
|
||||
return box.first
|
||||
}
|
||||
@ -156,7 +159,7 @@ ui_box_tranverse_next :: proc "contextless" ( box : ^ UI_Box ) -> (^ UI_Box)
|
||||
}
|
||||
|
||||
// Traveral pritorizes traversing a "anestry layer"
|
||||
ui_box_traverse_next_layer_based :: proc "contextless" ( box : ^UI_Box, skip_intersection_test := false ) -> (^UI_Box)
|
||||
ui_box_traverse_next_layer_based :: proc "contextless" ( box : ^UI_Box, bypass_intersection_test := false ) -> (^UI_Box)
|
||||
{
|
||||
using state := get_state()
|
||||
|
||||
@ -166,8 +169,8 @@ ui_box_traverse_next_layer_based :: proc "contextless" ( box : ^UI_Box, skip_int
|
||||
if parent.last != box
|
||||
{
|
||||
if box.next != nil do return box.next
|
||||
// There are no more adjacent nodes
|
||||
}
|
||||
// There are no more adjacent nodes
|
||||
}
|
||||
|
||||
// Either is root OR
|
||||
@ -176,7 +179,10 @@ ui_box_traverse_next_layer_based :: proc "contextless" ( box : ^UI_Box, skip_int
|
||||
if box.first != nil
|
||||
{
|
||||
// Check to make sure parent is present on the screen, if its not don't bother.
|
||||
if ! skip_intersection_test && intersects_range2( ui_view_bounds(), box.computed.bounds) {
|
||||
if bypass_intersection_test {
|
||||
return box.first
|
||||
}
|
||||
if intersects_range2( ui_view_bounds(), box.computed.bounds) {
|
||||
return box.first
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import "base:runtime"
|
||||
import lalg "core:math/linalg"
|
||||
|
||||
// Problably cursed way to setup a 'scope' for a widget
|
||||
ui_build :: #force_inline proc( captures : $Type, $maker : #type proc(captures : Type) -> $ReturnType ) -> ReturnType { return maker(captures) }
|
||||
// ui_build :: #force_inline proc( captures : $Type, $maker : #type proc(captures : Type) -> $ReturnType ) -> ReturnType { return maker(captures) }
|
||||
|
||||
UI_Widget :: struct {
|
||||
using box : ^UI_Box,
|
||||
|
Loading…
Reference in New Issue
Block a user