WIP: Persistent order support for rooted boxes (top-most boxes)
I wasn't expecting it to be this to bad to support it... Also: * I renamed some of the files to group better with the virtual codebase view
This commit is contained in:
parent
f693685d72
commit
595de438af
20
SectrPrototype.code-workspace
Normal file
20
SectrPrototype.code-workspace
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "code_virtual_view"
|
||||
},
|
||||
{
|
||||
"path": "scripts"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"autoHide.autoHidePanel": false,
|
||||
"autoHide.autoHideSideBar": false,
|
||||
"files.associations": {
|
||||
"*.rmd": "markdown",
|
||||
"type_traits": "cpp",
|
||||
"utf8proc.c": "cpp",
|
||||
"xtr1common": "cpp"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,275 +0,0 @@
|
||||
package wip
|
||||
|
||||
// Based off Ryan Fleury's UI Series & Epic's RAD Debugger which directly implements a version of it.
|
||||
// You will see Note(rjf) these are comments directly from the RAD Debugger codebase by Fleury.
|
||||
// TODO(Ed) If I can, I would like this to be its own package, but the nature of packages in odin may make this difficult.
|
||||
|
||||
// TODO(Ed) : This is in Raddbg base_types.h, consider moving outside of UI.
|
||||
Axis2 :: enum i32 {
|
||||
Invalid = -1,
|
||||
X = 0,
|
||||
Y = 1,
|
||||
Count,
|
||||
}
|
||||
|
||||
Corner :: enum i32 {
|
||||
Invalid = -1,
|
||||
_00,
|
||||
_01,
|
||||
_10,
|
||||
_11,
|
||||
TopLeft = _00,
|
||||
TopRight = _01,
|
||||
BottomLeft = _10,
|
||||
BottomRight = _11,
|
||||
Count = 4,
|
||||
}
|
||||
|
||||
// TODO(Ed) : From Raddbg draw.h, consider if needed or covered by raylib.
|
||||
DrawBucket :: struct {
|
||||
// passes : RenderPassList,
|
||||
stack_gen : u64,
|
||||
last_cmd_stack_gen : u64,
|
||||
// DrawBucketStackDeclares
|
||||
}
|
||||
|
||||
// TODO(Ed) : From Raddbg draw.h, consider if needed or covered by raylib.
|
||||
DrawFancyRunList :: struct {
|
||||
placeholder : int
|
||||
}
|
||||
|
||||
// TODO(Ed) : From Raddbg base_string.h, consider if needed or covered by raylib.
|
||||
FuzzyMatchRangeList :: struct {
|
||||
placeholder : int
|
||||
}
|
||||
|
||||
// TODO(Ed): This is in Raddbg os_gfx.h, consider moving outside of UI.
|
||||
OS_Cursor :: enum u32 {
|
||||
Pointer,
|
||||
IBar,
|
||||
Left_Right,
|
||||
Up_Down,
|
||||
Down_Right,
|
||||
Up_Right,
|
||||
Up_Down_left_Right,
|
||||
Hand_Point,
|
||||
Disabled,
|
||||
Count,
|
||||
}
|
||||
|
||||
Range2 :: struct #raw_union{
|
||||
using _ : struct {
|
||||
min, max : Vec2
|
||||
},
|
||||
using _ : struct {
|
||||
p0, p1 : Vec2
|
||||
},
|
||||
using _ : struct {
|
||||
x0, y0 : f32,
|
||||
x1, y1 : f32,
|
||||
},
|
||||
}
|
||||
|
||||
Side :: enum i32 {
|
||||
Invalid = -1,
|
||||
Min = 0,
|
||||
Max = 1,
|
||||
Count
|
||||
}
|
||||
|
||||
UI_FocusKind :: enum u32 {
|
||||
Null,
|
||||
Off,
|
||||
On,
|
||||
Root,
|
||||
Count,
|
||||
}
|
||||
|
||||
UI_IconKind :: enum u32 {
|
||||
Null,
|
||||
Arrow_Up,
|
||||
Arrow_Left,
|
||||
Arrow_Right,
|
||||
Arrow_Down,
|
||||
Caret_Up,
|
||||
Caret_Left,
|
||||
Caret_Right,
|
||||
Caret_Down,
|
||||
Check_Hollow,
|
||||
Check_Filled,
|
||||
Count,
|
||||
}
|
||||
|
||||
UI_IconInfo :: struct {
|
||||
placehodler : int
|
||||
}
|
||||
|
||||
UI_Key :: struct {
|
||||
opaque : [1]u64,
|
||||
}
|
||||
|
||||
UI_Layout :: struct {
|
||||
placeholder : int
|
||||
}
|
||||
|
||||
UI_Nav :: struct {
|
||||
moved : b32,
|
||||
new_p : Vec2i
|
||||
}
|
||||
|
||||
UI_NavDeltaUnit :: enum u32 {
|
||||
Element,
|
||||
Chunk,
|
||||
Whole,
|
||||
End_Point,
|
||||
Count,
|
||||
}
|
||||
|
||||
UI_NavActionFlag :: enum u32 {
|
||||
Keep_Mark,
|
||||
Delete,
|
||||
Copy,
|
||||
Paste,
|
||||
Zero_Delta_On_Select,
|
||||
Pick_Select_Side,
|
||||
Can_At_Line,
|
||||
Explicit_Directional,
|
||||
Replace_And_Commit,
|
||||
}
|
||||
UI_NavActionFlags :: bit_set[UI_NavActionFlag; u32]
|
||||
|
||||
UI_NavAction :: struct {
|
||||
flags : UI_NavActionFlags,
|
||||
delta : Vec2i,
|
||||
delta_unit : UI_NavDeltaUnit,
|
||||
insertion : string,
|
||||
}
|
||||
|
||||
UI_NavActionNode :: struct {
|
||||
next : ^ UI_NavActionNode,
|
||||
last : ^ UI_NavActionNode,
|
||||
action : UI_NavAction
|
||||
}
|
||||
|
||||
UI_NavActionList :: struct {
|
||||
first : ^ UI_NavActionNode,
|
||||
last : ^ UI_NavActionNode,
|
||||
count : u64,
|
||||
}
|
||||
|
||||
UI_NavTextOpFlag :: enum u32 {
|
||||
Invalid,
|
||||
Copy,
|
||||
}
|
||||
UI_NavTextOpFlags :: bit_set[UI_NavTextOpFlag; u32]
|
||||
|
||||
UI_ScrollPt :: struct {
|
||||
idx : i64,
|
||||
offset : f32
|
||||
}
|
||||
UI_ScrollPt2 :: [2]UI_ScrollPt
|
||||
|
||||
UI_Signal :: struct {
|
||||
box : ^ UI_Box,
|
||||
|
||||
cursor_pos : Vec2,
|
||||
drag_delta : Vec2,
|
||||
scroll : Vec2,
|
||||
|
||||
left_clicked : b8,
|
||||
right_clicked : b8,
|
||||
double_clicked : b8,
|
||||
keyboard_clicked : b8,
|
||||
|
||||
pressed : b8,
|
||||
released : b8,
|
||||
dragging : b8,
|
||||
hovering : b8,
|
||||
mouse_over : b8,
|
||||
commit : b8,
|
||||
}
|
||||
|
||||
UI_SizeKind :: enum u32 {
|
||||
Null,
|
||||
Pixels,
|
||||
Points,
|
||||
TextContent,
|
||||
PercentOfParent,
|
||||
ChildrenSum,
|
||||
Count,
|
||||
}
|
||||
|
||||
UI_Size :: struct {
|
||||
kind : UI_SizeKind,
|
||||
value : f32,
|
||||
strictness : f32,
|
||||
}
|
||||
|
||||
UI_Size2 : struct {
|
||||
kind : [Axis2.Count]UI_SizeKind,
|
||||
value : Vec2,
|
||||
strictness : Vec2,
|
||||
}
|
||||
|
||||
UI_TextAlign :: enum u32 {
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
Count
|
||||
}
|
||||
|
||||
ui_key_null :: proc() -> UI_Key {
|
||||
return {}
|
||||
}
|
||||
|
||||
ui_key_from_string :: proc( value : string ) -> UI_Key {
|
||||
return {}
|
||||
}
|
||||
|
||||
ui_key_match :: proc( a, b : UI_Key ) -> b32 {
|
||||
return false
|
||||
}
|
||||
|
||||
ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box) {
|
||||
return nil
|
||||
}
|
||||
|
||||
ui_box_equip_display_string :: proc( box : ^ UI_Box, display_string : string ) {
|
||||
|
||||
}
|
||||
|
||||
ui_box_equip_child_layout_axis :: proc( box : ^ UI_Box, axis : Axis2 ) {
|
||||
|
||||
}
|
||||
|
||||
ui_push_parent :: proc( box : ^ UI_Box ) -> (^ UI_Box) {
|
||||
return nil
|
||||
}
|
||||
|
||||
ui_pop_parent :: proc() -> (^ UI_Box) {
|
||||
return nil
|
||||
}
|
||||
|
||||
ui_signal_from_box :: proc( box : ^ UI_Box ) -> UI_Signal {
|
||||
return {}
|
||||
}
|
||||
|
||||
ui_button :: proc( label : string ) -> UI_Signal {
|
||||
button_flags : UI_BoxFlags =
|
||||
UI_BoxFlags_Clickable & {
|
||||
.Draw_Border,
|
||||
.Draw_Text,
|
||||
.Draw_Background,
|
||||
.Focus_Hot,
|
||||
.Focus_Active,
|
||||
}
|
||||
box := ui_box_make( button_flags, label )
|
||||
signal := ui_signal_from_box( box )
|
||||
return signal
|
||||
}
|
||||
|
||||
ui_spacer :: proc( label : string = UI_NullLabel ) -> UI_Signal {
|
||||
box := ui_box_make( UI_BoxFlags_Null, label )
|
||||
signal := ui_signal_from_box( box )
|
||||
return signal
|
||||
}
|
@ -1,172 +0,0 @@
|
||||
package wip
|
||||
|
||||
UI_BoxFlag :: enum u64 {
|
||||
// Note(rjf) : Interaction
|
||||
Mouse_Clickable,
|
||||
Keyboard_Clickable,
|
||||
Click_To_Focus,
|
||||
Scroll,
|
||||
View_Scroll_X,
|
||||
View_Scroll_Y,
|
||||
View_Clamp_X,
|
||||
View_Clamp_Y,
|
||||
Focus_Active,
|
||||
Focus_Active_Disabled,
|
||||
Focus_Hot,
|
||||
Focus_Hot_Disabled,
|
||||
Default_Focus_Nav_X,
|
||||
Default_Focus_Nav_Y,
|
||||
Default_Focus_Edit,
|
||||
Focus_Nav_Skip,
|
||||
Disabled,
|
||||
|
||||
// Note(rjf) : Layout
|
||||
Floating_X,
|
||||
Floating_Y,
|
||||
Fixed_Width,
|
||||
Fixed_Height,
|
||||
Allow_Overflow_X,
|
||||
Allow_Overflow_Y,
|
||||
Skip_View_Off_X,
|
||||
Skip_View_Off_Y,
|
||||
|
||||
// Note(rjf) : Appearance / Animation
|
||||
Draw_Drop_Shadow,
|
||||
Draw_Background_Blur,
|
||||
Draw_Background,
|
||||
Draw_Border,
|
||||
Draw_Side_Top,
|
||||
Draw_Side_Bottom,
|
||||
Draw_Side_Left,
|
||||
Draw_Side_Right,
|
||||
Draw_Text,
|
||||
Draw_Text_Fastpath_Codepoint,
|
||||
Draw_Hot_Effects,
|
||||
Draw_Overlay,
|
||||
Draw_Bucket,
|
||||
Clip,
|
||||
Animate_Pos_X,
|
||||
Animate_Pos_Y,
|
||||
Disable_Text_Trunc,
|
||||
Disable_ID_String,
|
||||
Disable_Focus_Viz,
|
||||
Require_Focus_Background,
|
||||
Has_Display_String,
|
||||
Has_Fuzzy_Match_Ranges,
|
||||
Round_Children_By_Parent,
|
||||
|
||||
Count,
|
||||
}
|
||||
UI_BoxFlags :: bit_set[UI_BoxFlag; u64]
|
||||
|
||||
UI_BoxFlags_Null :: UI_BoxFlags {}
|
||||
UI_BoxFlags_Clickable :: UI_BoxFlags { .Mouse_Clickable, .Keyboard_Clickable }
|
||||
|
||||
UI_NullLabel :: ""
|
||||
|
||||
UI_BoxCustomDrawProc :: #type proc( box : ^ UI_Box, user_data : rawptr )
|
||||
|
||||
UI_BoxCustomDraw :: struct {
|
||||
callback : UI_BoxCustomDrawProc,
|
||||
data : rawptr,
|
||||
}
|
||||
|
||||
UI_BoxRec :: struct {
|
||||
next : ^ UI_BoxNode,
|
||||
push_count : i32,
|
||||
pop_count : i32,
|
||||
}
|
||||
|
||||
UI_BoxNode :: struct {
|
||||
next : ^ UI_BoxNode,
|
||||
box : ^ UI_Box,
|
||||
}
|
||||
|
||||
UI_BoxList :: struct {
|
||||
first : UI_BoxNode,
|
||||
last : UI_BoxNode,
|
||||
count : u64,
|
||||
}
|
||||
|
||||
UI_BoxHashSlot :: struct {
|
||||
first, last : ^ UI_Box
|
||||
}
|
||||
|
||||
// Note(Ed) : This is called UI_Widget in the substack series, its called box in raddbg
|
||||
// This eventually gets renamed by part 4 of the series to UI_Box.
|
||||
// However, its essentially a UI_Node or UI_BaselineEntity, etc.
|
||||
// Think of godot's Control nodes I guess.
|
||||
// TODO(Ed) : We dumped all the fields present within raddbg, review which ones are actually needed.
|
||||
UI_Box :: struct {
|
||||
// Note(rjf) : persistent links
|
||||
hash : struct {
|
||||
next, prev : ^ UI_Box,
|
||||
},
|
||||
|
||||
// Note(rjf) : Per-build links & data
|
||||
// TODO(ED) : Put this in its own struct?
|
||||
first, last, prev, next : ^ UI_Box,
|
||||
num_children : i32,
|
||||
|
||||
// Note(rjf) : Key + generation info
|
||||
// TODO(ED) : Put this in its own struct?
|
||||
key : UI_Key,
|
||||
last_frame_touched_index : u64,
|
||||
|
||||
// Note(rjf) : Per-frame info provided by builders
|
||||
// TODO(ED) : Put this in its own struct?
|
||||
flags : UI_BoxFlags,
|
||||
display_str : string, // raddbg: string
|
||||
semantic_size : [Axis2.Count]UI_Size,
|
||||
text_align : UI_TextAlign,
|
||||
fixed_pos : Vec2,
|
||||
fixed_size : Vec2,
|
||||
pref_size : [Axis2.Count]UI_Size,
|
||||
child_layout_axis : Axis2,
|
||||
hover_cursor : OS_Cursor,
|
||||
fastpath_codepoint : u32,
|
||||
draw_bucket : DrawBucket, // TODO(Ed): Translate to equivalent in raylib if necessary
|
||||
custom_draw : UI_BoxCustomDraw,
|
||||
bg_color : Color,
|
||||
text_color : Color,
|
||||
border_color : Color,
|
||||
overlay_color : Color,
|
||||
font : FontID,
|
||||
font_size : f32,
|
||||
corner_radii : [Corner.Count]f32,
|
||||
blur_size : f32, // TODO(Ed) : You would need to run a custom shader with raylib or have your own rendering backend for this.
|
||||
transparency : f32,
|
||||
squish : f32,
|
||||
text_padding : f32,
|
||||
|
||||
// Note(rjf) : Per-frame artifacts by builders
|
||||
// TODO(ED) : Put this in its own struct?
|
||||
display_string_runs : DrawFancyRunList, // TODO(Ed) : Translate to equivalent in raylib if necessary
|
||||
rect : Range2,
|
||||
fixed_pos_animated : Vec2,
|
||||
pos_delta : Vec2,
|
||||
fuzzy_match_range : FuzzyMatchRangeList, // TODO(Ed) : I'm not sure this is needed
|
||||
|
||||
// Note(rjf) : Computed every frame
|
||||
// TODO(ED) : Put this in its own struct?
|
||||
computed_rel_pos : Vec2, // TODO(Ed) : Raddbg doesn't have these, check what they became or if needed
|
||||
computed_size : Vec2,
|
||||
|
||||
// Note(rjf) : Persistent data
|
||||
// TODO(ED) : Put this in its own struct?
|
||||
first_touched_build_id : u64,
|
||||
last_touched_build_id : u64,
|
||||
hot_time : f32,
|
||||
active_time : f32,
|
||||
disabled_time : f32,
|
||||
focus_hot_time : f32,
|
||||
focus_active_time : f32,
|
||||
focus_active_disabled_time : f32,
|
||||
view_off : Vec2,
|
||||
view_off_target : Vec2,
|
||||
view_bounds : Vec2,
|
||||
default_nav_focus_hot_key : UI_Key,
|
||||
default_nav_focus_active_key : UI_Key,
|
||||
default_nav_focus_next_hot_key : UI_Key,
|
||||
default_nav_focus_next_active_key : UI_Key,
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
package wip
|
||||
|
||||
import "core:os"
|
||||
|
||||
// TODO(Ed) : As with UI_Box, not all of this will be implemented at once,
|
||||
// we'll need to review this setup for our use case, we will have UI persistent of
|
||||
// a workspace (global state UI), and one for the workspace itself.
|
||||
// The UI state use in raddbg seems to be tied to an OS window and has realted things to it.
|
||||
// You may need to lift the nav actions outside of the UI_State of a workspace, etc...
|
||||
UI_State :: struct {
|
||||
arena : ^ Arena,
|
||||
|
||||
build_arenas : [2] ^ Arena,
|
||||
build_id : u64,
|
||||
|
||||
// Note(rjf) : Box cache
|
||||
// TODO(ED) : Put this in its own struct?
|
||||
first_free_box : UI_Box,
|
||||
box_table_size : u64,
|
||||
box_table : ^ UI_BoxHashSlot, // TODO(Ed) : Can the cache use HMapZPL?
|
||||
|
||||
// Note(rjf) : Build phase output
|
||||
// TODO(ED) : Put this in its own struct?
|
||||
root : ^ UI_Box,
|
||||
tooltip_root : ^ UI_Box,
|
||||
ctx_menu_root : ^ UI_Box,
|
||||
default_nav_root_key : UI_Key,
|
||||
build_box_count : u64,
|
||||
last_build_box_count : u64,
|
||||
ctx_menu_touched_this_frame : b32,
|
||||
|
||||
// Note(rjf) : Build parameters
|
||||
// icon_info : UI_IconInfo
|
||||
// window : os.Handle
|
||||
// events : OS_EventList
|
||||
nav_actions : UI_NavActionList,
|
||||
// TODO(Ed) : Do we want to keep tracvk of the cursor pos separately
|
||||
// incase we do some sequence for tutorials?
|
||||
mouse : Vec2,
|
||||
animation_delta : f32,
|
||||
external_focus_commit : b32,
|
||||
|
||||
// Note(rjf) : User Interaction State
|
||||
// TODO(ED) : Put this in its own struct?
|
||||
hot_box_key : UI_Key,
|
||||
active_box_key : [Side.Count] UI_Key,
|
||||
clipboard_copy_key : UI_Key,
|
||||
time_since_last_click : [Side.Count] f32,
|
||||
last_click_key : [Side.Count] UI_Key,
|
||||
drag_start_mouse : Vec2,
|
||||
drag_state_arena : ^ Arena,
|
||||
drag_state_data : string,
|
||||
string_hover_arena : ^ Arena,
|
||||
string_hover_string : string,
|
||||
string_hover_fancy_runs : DrawFancyRunList,
|
||||
string_hover_begin_us : u64,
|
||||
string_hover_build_index : u64,
|
||||
last_time_mouse_moved_us : u64,
|
||||
|
||||
// Note(rjf) : Tooltip State
|
||||
// TODO(ED) : Put this in its own struct?
|
||||
tool_tip_open_time : f32,
|
||||
tool_tip_open : b32,
|
||||
|
||||
// Note(rjf) : Context menu state
|
||||
// TODO(ED) : Put this in its own struct?
|
||||
ctx_menu_anchor_key : UI_Key,
|
||||
next_ctx_menu_anchor_key : UI_Key,
|
||||
ctx_menu_anchor_box_last_pos : Vec2,
|
||||
cxt_menu_anchor_off : Vec2,
|
||||
ctx_menu_open : b32,
|
||||
next_ctx_menu_open : b32,
|
||||
ctx_menu_open_time : f32,
|
||||
ctx_menu_key : UI_Key,
|
||||
ctx_menu_changed : b32,
|
||||
}
|
@ -129,7 +129,7 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
|
||||
resolution_height = 600
|
||||
refresh_rate = 0
|
||||
|
||||
cam_min_zoom = 0.25
|
||||
cam_min_zoom = 0.10
|
||||
cam_max_zoom = 30.0
|
||||
cam_zoom_mode = .Smooth
|
||||
cam_zoom_smooth_snappiness = 4.0
|
||||
@ -198,7 +198,7 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
|
||||
menu_bar.pos = Vec2(app_window.extent) * { -1, 1 }
|
||||
menu_bar.size = {200, 40}
|
||||
|
||||
settings_menu.min_size = {200, 200}
|
||||
settings_menu.min_size = {250, 200}
|
||||
}
|
||||
|
||||
// Demo project setup
|
||||
@ -338,6 +338,10 @@ tick :: proc( host_delta_time : f64, host_delta_ns : Duration ) -> b32
|
||||
|
||||
rl.PollInputEvents()
|
||||
|
||||
debug.draw_ui_box_bounds_points = false
|
||||
debug.draw_UI_padding_bounds = false
|
||||
debug.draw_ui_content_bounds = false
|
||||
|
||||
should_close = update( host_delta_time )
|
||||
render()
|
||||
|
@ -118,7 +118,7 @@ logger_interface :: proc(
|
||||
}
|
||||
|
||||
log :: proc( msg : string, level := LogLevel.Info, loc := #caller_location ) {
|
||||
core_log.log( level, msg, location = loc )
|
||||
core_log.logf( level, msg, location = loc )
|
||||
}
|
||||
|
||||
logf :: proc( fmt : string, args : ..any, level := LogLevel.Info, loc := #caller_location ) {
|
@ -10,6 +10,8 @@ import rl "vendor:raylib"
|
||||
|
||||
Str_App_State := "App State"
|
||||
|
||||
#region("Memory")
|
||||
|
||||
Memory_App : Memory
|
||||
|
||||
Memory_Base_Address_Persistent :: Terabyte * 1
|
||||
@ -133,6 +135,10 @@ MemoryConfig :: struct {
|
||||
commit_initial_filebuffer : uint,
|
||||
}
|
||||
|
||||
#endregion("Memory")
|
||||
|
||||
#region("State")
|
||||
|
||||
// ALl nobs available for this application
|
||||
AppConfig :: struct {
|
||||
using memory : MemoryConfig,
|
||||
@ -249,3 +255,5 @@ get_state :: #force_inline proc "contextless" () -> ^ State {
|
||||
// get_frametime :: #force_inline proc "contextless" () -> FrameTime {
|
||||
// return get_state().frametime
|
||||
// }
|
||||
|
||||
#endregion("State")
|
@ -17,8 +17,6 @@ DebugData :: struct {
|
||||
|
||||
// UI Vis
|
||||
draw_ui_box_bounds_points : bool,
|
||||
draw_ui_margin_bounds : bool,
|
||||
draw_ui_anchor_bounds : bool,
|
||||
draw_UI_padding_bounds : bool,
|
||||
draw_ui_content_bounds : bool,
|
||||
|
||||
|
@ -5,6 +5,8 @@ import "core:fmt"
|
||||
import "core:os"
|
||||
import "core:runtime"
|
||||
|
||||
// Test
|
||||
|
||||
file_copy_sync :: proc( path_src, path_dst: string, allocator := context.temp_allocator ) -> b32
|
||||
{
|
||||
file_size : i64
|
||||
|
@ -97,6 +97,21 @@ dll_push_back :: proc "contextless" ( current_ptr : ^(^ ($ TypeCurr)), node : ^$
|
||||
node.next = nil
|
||||
}
|
||||
|
||||
dll_pn_pop :: proc "contextless" ( node : ^$Type )
|
||||
{
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
if node.prev != nil {
|
||||
node.prev.next = nil
|
||||
node.prev = nil
|
||||
}
|
||||
if node.next != nil {
|
||||
node.next.prev = nil
|
||||
node.next = nil
|
||||
}
|
||||
}
|
||||
|
||||
dll_pop_back :: #force_inline proc "contextless" ( current_ptr : ^(^ ($ Type)) )
|
||||
{
|
||||
to_remove : ^Type = (current_ptr ^)
|
||||
@ -146,7 +161,27 @@ dll_full_insert_raw :: proc "contextless" ( null : ^($ Type), parent, pos, node
|
||||
}
|
||||
}
|
||||
|
||||
dll_full_push_back :: proc "contextless" ( null : ^($ Type), parent, node : ^ Type ) {
|
||||
dll_full_pop :: proc "contextless" ( node, parent : ^$Type ) {
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
if parent.first == node {
|
||||
parent.first = node.next
|
||||
}
|
||||
if parent.last == node {
|
||||
parent.last = node.prev
|
||||
}
|
||||
if node.prev != nil {
|
||||
node.prev.next = nil
|
||||
node.prev = nil
|
||||
}
|
||||
if node.next != nil {
|
||||
node.next.prev = nil
|
||||
node.next = nil
|
||||
}
|
||||
}
|
||||
|
||||
dll_full_push_back :: proc "contextless" ( parent, node : ^ $Type, null : ^Type ) {
|
||||
dll_full_insert_raw( null, parent, parent.last, node )
|
||||
}
|
||||
|
||||
|
@ -85,14 +85,14 @@ ws_view_draw_text_string :: proc( content : string, pos : Vec2, size : f32, colo
|
||||
zoom_adjust := px_size * project.workspace.cam.zoom
|
||||
|
||||
rl_font := to_rl_Font(font, zoom_adjust )
|
||||
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.BILINEAR)
|
||||
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.ANISOTROPIC_4X)
|
||||
rl.DrawTextCodepoints( rl_font,
|
||||
raw_data(runes), cast(i32) len(runes),
|
||||
position = transmute(rl.Vector2) pos,
|
||||
fontSize = px_size,
|
||||
spacing = 0.0,
|
||||
tint = color );
|
||||
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.ANISOTROPIC_16X)
|
||||
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.POINT)
|
||||
}
|
||||
|
||||
ws_view_draw_text_StrRunesPair :: proc( content : StrRunesPair, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default )
|
||||
@ -114,14 +114,14 @@ ws_view_draw_text_StrRunesPair :: proc( content : StrRunesPair, pos : Vec2, size
|
||||
rl_font := to_rl_Font(font, zoom_adjust )
|
||||
runes := content.runes
|
||||
|
||||
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.BILINEAR)
|
||||
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.ANISOTROPIC_4X)
|
||||
rl.DrawTextCodepoints( rl_font,
|
||||
raw_data(runes), cast(i32) len(runes),
|
||||
position = transmute(rl.Vector2) pos,
|
||||
fontSize = px_size,
|
||||
spacing = 0.0,
|
||||
tint = color );
|
||||
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.ANISOTROPIC_16X)
|
||||
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.POINT)
|
||||
}
|
||||
|
||||
// Raylib's equivalent doesn't take a length for the string (making it a pain in the ass)
|
||||
|
@ -175,10 +175,10 @@ render_mode_2d_workspace :: proc()
|
||||
line_thickness := 1 * cam_zoom_ratio
|
||||
|
||||
// profile_begin("rl.DrawRectangleRoundedLines: padding & content")
|
||||
if equal_range2(computed.content, computed.padding) {
|
||||
if debug.draw_UI_padding_bounds && equal_range2(computed.content, computed.padding) {
|
||||
draw_rectangle_lines( rect_padding, current, Color_Debug_UI_Padding_Bounds, line_thickness )
|
||||
}
|
||||
else {
|
||||
else if debug.draw_ui_content_bounds {
|
||||
draw_rectangle_lines( rect_content, current, Color_Debug_UI_Content_Bounds, line_thickness )
|
||||
}
|
||||
// profile_end()
|
||||
@ -186,6 +186,8 @@ render_mode_2d_workspace :: proc()
|
||||
point_radius := 3 * cam_zoom_ratio
|
||||
|
||||
// profile_begin("circles")
|
||||
if debug.draw_ui_box_bounds_points
|
||||
{
|
||||
// center := Vec2 {
|
||||
// render_bounds.p0.x + computed_size.x * 0.5,
|
||||
// render_bounds.p0.y - computed_size.y * 0.5,
|
||||
@ -194,6 +196,7 @@ render_mode_2d_workspace :: proc()
|
||||
|
||||
rl.DrawCircleV( render_bounds.p0, point_radius, Color_Red )
|
||||
rl.DrawCircleV( render_bounds.p1, point_radius, Color_Blue )
|
||||
}
|
||||
// profile_end()
|
||||
|
||||
if len(current.text.str) > 0 {
|
||||
@ -333,6 +336,8 @@ render_screen_ui :: proc()
|
||||
break Render_App_UI
|
||||
}
|
||||
|
||||
// Sort roots children by top-level order
|
||||
|
||||
current := root.first
|
||||
for ; current != nil; current = ui_box_tranverse_next( current )
|
||||
{
|
||||
@ -385,10 +390,10 @@ render_screen_ui :: proc()
|
||||
line_thickness : f32 = 1
|
||||
|
||||
// profile_begin("rl.DrawRectangleRoundedLines: padding & content")
|
||||
if equal_range2(computed.content, computed.padding) {
|
||||
if debug.draw_UI_padding_bounds && equal_range2(computed.content, computed.padding) {
|
||||
draw_rectangle_lines( rect_padding, current, Color_Debug_UI_Padding_Bounds, line_thickness )
|
||||
}
|
||||
else {
|
||||
else if debug.draw_ui_content_bounds {
|
||||
draw_rectangle_lines( rect_content, current, Color_Debug_UI_Content_Bounds, line_thickness )
|
||||
}
|
||||
// profile_end()
|
||||
@ -418,6 +423,8 @@ render_screen_ui :: proc()
|
||||
point_radius : f32 = 3
|
||||
|
||||
// profile_begin("circles")
|
||||
if debug.draw_ui_box_bounds_points
|
||||
{
|
||||
// center := Vec2 {
|
||||
// render_bounds.p0.x + computed_size.x * 0.5,
|
||||
// render_bounds.p0.y - computed_size.y * 0.5,
|
||||
@ -426,6 +433,7 @@ render_screen_ui :: proc()
|
||||
|
||||
rl.DrawCircleV( render_bounds.p0, point_radius, Color_Red )
|
||||
rl.DrawCircleV( render_bounds.p1, point_radius, Color_Blue )
|
||||
}
|
||||
// profile_end()
|
||||
|
||||
if len(current.text.str) > 0 && style.font.key != 0 {
|
||||
|
@ -151,12 +151,16 @@ update :: proc( delta_time : f64 ) -> b32
|
||||
// TODO(Ed): This should be per workspace view
|
||||
{
|
||||
// profile("Camera Manual Nav")
|
||||
digital_move_speed : f32 = 200.0
|
||||
digital_move_speed : f32 = 1000.0
|
||||
|
||||
if workspace.zoom_target == 0.0 {
|
||||
workspace.zoom_target = cam.zoom
|
||||
}
|
||||
|
||||
config.cam_max_zoom = 30
|
||||
config.cam_zoom_sensitivity_digital = 0.04
|
||||
config.cam_min_zoom = 0.04
|
||||
config.cam_zoom_mode = .Digital
|
||||
switch config.cam_zoom_mode
|
||||
{
|
||||
case .Smooth:
|
||||
@ -175,8 +179,8 @@ update :: proc( delta_time : f64 ) -> b32
|
||||
}
|
||||
|
||||
move_velocity : Vec2 = {
|
||||
+ cast(f32) i32(debug_actions.cam_move_left) - cast(f32) i32(debug_actions.cam_move_right),
|
||||
+ cast(f32) i32(debug_actions.cam_move_up) - cast(f32) i32(debug_actions.cam_move_down),
|
||||
- cast(f32) i32(debug_actions.cam_move_left) + cast(f32) i32(debug_actions.cam_move_right),
|
||||
- cast(f32) i32(debug_actions.cam_move_up) + cast(f32) i32(debug_actions.cam_move_down),
|
||||
}
|
||||
move_velocity *= digital_move_speed * f32(delta_time)
|
||||
cam.target += move_velocity
|
||||
@ -206,11 +210,12 @@ update :: proc( delta_time : f64 ) -> b32
|
||||
frame_style_flags : UI_LayoutFlags = {
|
||||
.Fixed_Position_X, .Fixed_Position_Y,
|
||||
.Fixed_Width, .Fixed_Height,
|
||||
.Origin_At_Anchor_Center,
|
||||
}
|
||||
default_layout := UI_Layout {
|
||||
flags = frame_style_flags,
|
||||
anchor = {},
|
||||
alignment = { 0.0, 0.0 },
|
||||
alignment = { 0.5, 0.5 },
|
||||
font_size = 30,
|
||||
text_alignment = { 0.0, 0.0 },
|
||||
// corner_radii = { 0.2, 0.2, 0.2, 0.2 },
|
||||
@ -235,7 +240,7 @@ update :: proc( delta_time : f64 ) -> b32
|
||||
// test_draggable()
|
||||
// test_text_box()
|
||||
// test_parenting( & default_layout, & frame_style_default )
|
||||
test_whitespace_ast( & default_layout, & frame_style_default )
|
||||
// test_whitespace_ast( & default_layout, & frame_style_default )
|
||||
}
|
||||
//endregion Workspace Imgui Tick
|
||||
|
||||
|
@ -143,5 +143,143 @@ ui_set_layout :: #force_inline proc( layout : UI_Layout, preset : UI_StylePreset
|
||||
Widget Layout Ops
|
||||
*/
|
||||
|
||||
ui_layout_children_horizontally :: proc( container : ^UI_Box, direction : UI_LayoutDirectionX, width_ref : ^f32 ) {
|
||||
ui_layout_children_horizontally :: proc( container : ^UI_Box, direction : UI_LayoutDirectionX, width_ref : ^f32 )
|
||||
{
|
||||
container_width : f32
|
||||
if width_ref != nil {
|
||||
container_width = width_ref ^
|
||||
}
|
||||
else {
|
||||
container_width = container.computed.content.max.x - container.computed.content.min.x
|
||||
}
|
||||
|
||||
// do layout calculations for the children
|
||||
total_stretch_ratio : f32 = 0.0
|
||||
size_req_children : f32 = 0
|
||||
for child := container.first; child != nil; child = child.next
|
||||
{
|
||||
using child.layout
|
||||
scaled_width_by_height : b32 = b32(.Scale_Width_By_Height_Ratio in flags)
|
||||
if .Fixed_Width in flags
|
||||
{
|
||||
if scaled_width_by_height {
|
||||
height := size.max.y != 0 ? size.max.y : container_width
|
||||
width := height * size.min.x
|
||||
|
||||
size_req_children += width
|
||||
continue
|
||||
}
|
||||
|
||||
size_req_children += size.min.x
|
||||
continue
|
||||
}
|
||||
|
||||
total_stretch_ratio += anchor.ratio.x
|
||||
}
|
||||
|
||||
avail_flex_space := container_width - size_req_children
|
||||
|
||||
allocate_space :: proc( child : ^UI_Box, total_stretch_ratio, avail_flex_space : f32 )
|
||||
{
|
||||
using child.layout
|
||||
if ! (.Fixed_Width in flags) {
|
||||
size.min.x = anchor.ratio.x * (1 / total_stretch_ratio) * avail_flex_space
|
||||
}
|
||||
flags |= {.Fixed_Width}
|
||||
}
|
||||
|
||||
space_used : f32 = 0.0
|
||||
switch direction{
|
||||
case .Right_To_Left:
|
||||
for child := container.last; child != nil; child = child.prev {
|
||||
allocate_space(child, total_stretch_ratio, avail_flex_space)
|
||||
using child.layout
|
||||
anchor = range2({0, 0}, {0, 0})
|
||||
alignment = { 0, 1 }// - hbox.layout.alignment
|
||||
pos.x = space_used
|
||||
space_used += size.min.x
|
||||
size.min.y = container.computed.content.max.y - container.computed.content.min.y
|
||||
}
|
||||
case .Left_To_Right:
|
||||
for child := container.first; child != nil; child = child.next {
|
||||
allocate_space(child, total_stretch_ratio, avail_flex_space)
|
||||
using child.layout
|
||||
anchor = range2({0, 0}, {0, 0})
|
||||
alignment = { 0, 1 }
|
||||
pos.x = space_used
|
||||
space_used += size.min.x
|
||||
size.min.y = container.computed.content.max.y - container.computed.content.min.y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui_layout_children_vertically :: proc( container : ^UI_Box, direction : UI_LayoutDirectionY, height_ref : ^f32 )
|
||||
{
|
||||
container_height : f32
|
||||
if height_ref != nil {
|
||||
container_height = height_ref ^
|
||||
}
|
||||
else {
|
||||
container_height = container.computed.content.max.y - container.computed.content.min.y
|
||||
}
|
||||
|
||||
// do layout calculations for the children
|
||||
total_stretch_ratio : f32 = 0.0
|
||||
size_req_children : f32 = 0
|
||||
for child := container.first; child != nil; child = child.next
|
||||
{
|
||||
using child.layout
|
||||
scaled_width_by_height : b32 = b32(.Scale_Width_By_Height_Ratio in flags)
|
||||
if .Fixed_Height in flags
|
||||
{
|
||||
if scaled_width_by_height {
|
||||
width := size.max.x != 0 ? size.max.x : container_height
|
||||
height := width * size.min.y
|
||||
|
||||
size_req_children += height
|
||||
continue
|
||||
}
|
||||
|
||||
size_req_children += size.min.y
|
||||
continue
|
||||
}
|
||||
|
||||
total_stretch_ratio += anchor.ratio.y
|
||||
}
|
||||
|
||||
avail_flex_space := container_height - size_req_children
|
||||
|
||||
allocate_space :: proc( child : ^UI_Box, total_stretch_ratio, avail_flex_space : f32 )
|
||||
{
|
||||
using child.layout
|
||||
if ! (.Fixed_Height in flags) {
|
||||
size.min.y = anchor.ratio.y * (1 / total_stretch_ratio) * avail_flex_space
|
||||
}
|
||||
flags |= {.Fixed_Height}
|
||||
alignment = {0, 0}
|
||||
}
|
||||
|
||||
space_used : f32 = 0.0
|
||||
switch direction {
|
||||
case .Top_To_Bottom:
|
||||
for child := container.last; child != nil; child = child.prev {
|
||||
allocate_space(child, total_stretch_ratio, avail_flex_space)
|
||||
using child.layout
|
||||
anchor = range2({0, 0}, {0, 1})
|
||||
// alignment = {0, 1}
|
||||
pos.y = space_used
|
||||
space_used += size.min.y
|
||||
size.min.x = container.computed.content.max.x - container.computed.content.min.x
|
||||
}
|
||||
case .Bottom_To_Top:
|
||||
for child := container.first; child != nil; child = child.next {
|
||||
allocate_space(child, total_stretch_ratio, avail_flex_space)
|
||||
using child.layout
|
||||
anchor = range2({0, 0}, {0, 1})
|
||||
// alignment = {0, 1}
|
||||
pos.y = space_used
|
||||
space_used += size.min.y
|
||||
size.min.x = container.computed.content.max.x - container.computed.content.min.x
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,178 @@ package sectr
|
||||
|
||||
// Note(Ed): This is naturally pretty expensive
|
||||
|
||||
ui_box_compute_layout :: proc( box : ^UI_Box )
|
||||
{
|
||||
profile("Layout Box")
|
||||
state := get_state()
|
||||
ui := state.ui_context
|
||||
|
||||
parent := box.parent
|
||||
// if parent != ui.root && ! parent.computed.fresh {
|
||||
// ui_box_compute_layout( parent )
|
||||
// }
|
||||
|
||||
style := box.style
|
||||
layout := & box.layout
|
||||
|
||||
// These are used to choose via multiplication weather to apply
|
||||
// position & size constraints of the parent.
|
||||
// The parent's unadjusted content bounds however are enforced for position,
|
||||
// they cannot be ignored. The user may bypass them by doing the
|
||||
// relative offset math vs world/screen space if they desire.
|
||||
fixed_pos_x : f32 = cast(f32) int(.Fixed_Position_X in layout.flags)
|
||||
fixed_pos_y : f32 = cast(f32) int(.Fixed_Position_Y in layout.flags)
|
||||
fixed_width : f32 = cast(f32) int(.Fixed_Width in layout.flags)
|
||||
fixed_height : f32 = cast(f32) int(.Fixed_Height in layout.flags)
|
||||
|
||||
size_to_text : bool = .Size_To_Text in layout.flags
|
||||
|
||||
computed := & box.computed
|
||||
|
||||
parent_content := parent.computed.content
|
||||
parent_content_size := parent_content.max - parent_content.min
|
||||
parent_center := parent_content.min + parent_content_size * 0.5
|
||||
|
||||
/*
|
||||
If fixed position (X or Y):
|
||||
* Ignore Margins
|
||||
* Ignore Anchors
|
||||
|
||||
If clampped position (X or Y):
|
||||
* Positon cannot exceed the anchors/margins bounds.
|
||||
|
||||
If fixed size (X or Y):
|
||||
* Ignore Parent constraints (can only be clipped)
|
||||
|
||||
If auto-sized:
|
||||
* Enforce parent size constraint of bounds relative to
|
||||
where the adjusted content bounds are after applying margins & anchors.
|
||||
The 'side' conflicting with the bounds will end at that bound side instead of clipping.
|
||||
|
||||
If size.min is not 0:
|
||||
* Ignore parent constraints if the bounds go below that value.
|
||||
|
||||
If size.max is 0:
|
||||
* Allow the child box to spread to entire adjusted content bounds, otherwise clampped to max size.
|
||||
*/
|
||||
|
||||
// 1. Anchors
|
||||
anchor := & layout.anchor
|
||||
anchored_bounds := range2(
|
||||
parent_content.min + parent_content_size * anchor.min,
|
||||
parent_content.max - parent_content_size * anchor.max,
|
||||
)
|
||||
// anchored_bounds_origin := (anchored_bounds.min + anchored_bounds.max) * 0.5
|
||||
|
||||
// 2. Apply Margins
|
||||
margins := range2(
|
||||
{ layout.margins.left, layout.margins.bottom },
|
||||
{ layout.margins.right, layout.margins.top },
|
||||
)
|
||||
margined_bounds := range2(
|
||||
anchored_bounds.min + margins.min,
|
||||
anchored_bounds.max - margins.max,
|
||||
)
|
||||
margined_bounds_origin := (margined_bounds.min + margined_bounds.max) * 0.5
|
||||
margined_size := margined_bounds.max - margined_bounds.min
|
||||
|
||||
// 3. Enforce Min/Max Size Constraints
|
||||
adjusted_max_size_x := layout.size.max.x > 0 ? min( margined_size.x, layout.size.max.x ) : margined_size.x
|
||||
adjusted_max_size_y := layout.size.max.y > 0 ? min( margined_size.y, layout.size.max.y ) : margined_size.y
|
||||
|
||||
adjusted_size : Vec2
|
||||
adjusted_size.x = max( adjusted_max_size_x, layout.size.min.x)
|
||||
adjusted_size.y = max( adjusted_max_size_y, layout.size.min.y)
|
||||
|
||||
if .Fixed_Width in layout.flags {
|
||||
adjusted_size.x = layout.size.min.x
|
||||
}
|
||||
if .Fixed_Height in layout.flags {
|
||||
adjusted_size.y = layout.size.min.y
|
||||
}
|
||||
|
||||
text_size : Vec2
|
||||
if layout.font_size == computed.text_size.y {
|
||||
text_size = computed.text_size
|
||||
}
|
||||
else {
|
||||
text_size = cast(Vec2) measure_text_size( box.text.str, style.font, layout.font_size, 0 )
|
||||
}
|
||||
|
||||
if size_to_text {
|
||||
adjusted_size = text_size
|
||||
}
|
||||
|
||||
// 5. Determine relative position
|
||||
|
||||
origin_center := margined_bounds_origin
|
||||
origin_top_left := Vec2 { margined_bounds.min.x, margined_bounds.max.y }
|
||||
|
||||
origin := .Origin_At_Anchor_Center in layout.flags ? origin_center : origin_top_left
|
||||
|
||||
rel_pos := origin + layout.pos
|
||||
|
||||
if .Fixed_Position_X in layout.flags {
|
||||
rel_pos.x = origin.x + layout.pos.x
|
||||
}
|
||||
if .Fixed_Position_Y in layout.flags {
|
||||
rel_pos.y = origin.y + layout.pos.y
|
||||
}
|
||||
|
||||
vec2_one := Vec2 { 1, 1 }
|
||||
|
||||
// 6. Determine the box bounds
|
||||
// Adjust Alignment of pivot position
|
||||
alignment := layout.alignment
|
||||
bounds := range2(
|
||||
rel_pos - adjusted_size * alignment,
|
||||
rel_pos + adjusted_size * (vec2_one - alignment),
|
||||
)
|
||||
|
||||
// Determine Padding's outer bounds
|
||||
border_offset := Vec2 { layout.border_width, layout.border_width }
|
||||
|
||||
padding_bounds := range2(
|
||||
bounds.min + border_offset,
|
||||
bounds.min - border_offset,
|
||||
)
|
||||
|
||||
// Determine Content Bounds
|
||||
content_bounds := range2(
|
||||
bounds.min + { layout.padding.left, layout.padding.bottom } + border_offset,
|
||||
bounds.max - { layout.padding.right, layout.padding.top } - border_offset,
|
||||
)
|
||||
|
||||
// computed.anchors = anchored_bounds
|
||||
// computed.margins = margined_bounds
|
||||
computed.bounds = bounds
|
||||
computed.padding = padding_bounds
|
||||
computed.content = content_bounds
|
||||
|
||||
if len(box.text.str) > 0
|
||||
{
|
||||
content_size := content_bounds.max - content_bounds.min
|
||||
text_pos : Vec2
|
||||
text_pos = content_bounds.min + { 0, text_size.y }
|
||||
text_pos.x += ( content_size.x - text_size.x ) * layout.text_alignment.x
|
||||
text_pos.y += ( content_size.y - text_size.y ) * layout.text_alignment.y
|
||||
|
||||
computed.text_size = text_size
|
||||
computed.text_pos = { text_pos.x, text_pos.y }
|
||||
}
|
||||
computed.fresh = true
|
||||
}
|
||||
|
||||
ui_box_compute_layout_children :: proc( box : ^UI_Box )
|
||||
{
|
||||
for current := box.first; current != nil; current = ui_box_tranverse_next( current )
|
||||
{
|
||||
if current == box do return
|
||||
if current.computed.fresh do continue
|
||||
ui_box_compute_layout( current )
|
||||
}
|
||||
}
|
||||
|
||||
ui_compute_layout :: proc( ui : ^UI_State )
|
||||
{
|
||||
profile(#procedure)
|
||||
@ -12,169 +184,16 @@ ui_compute_layout :: proc( ui : ^UI_State )
|
||||
computed := & root.computed
|
||||
style := root.style
|
||||
layout := & root.layout
|
||||
computed.bounds.min = layout.pos
|
||||
computed.bounds.max = layout.size.min
|
||||
// if ui == & state.screen_ui {
|
||||
computed.bounds.min = transmute(Vec2) state.app_window.extent * -1
|
||||
computed.bounds.max = transmute(Vec2) state.app_window.extent
|
||||
// }
|
||||
computed.content = computed.bounds
|
||||
}
|
||||
|
||||
current := root.first
|
||||
for ; current != nil;
|
||||
for current := root.first; current != nil; current = ui_box_tranverse_next( current )
|
||||
{
|
||||
// if current.computed.fresh do return
|
||||
|
||||
// TODO(Ed): Lift this to ui_box_compute_layout
|
||||
// profile("Layout Box")
|
||||
style := current.style
|
||||
layout := & current.layout
|
||||
|
||||
// These are used to choose via multiplication weather to apply
|
||||
// position & size constraints of the parent.
|
||||
// The parent's unadjusted content bounds however are enforced for position,
|
||||
// they cannot be ignored. The user may bypass them by doing the
|
||||
// relative offset math vs world/screen space if they desire.
|
||||
fixed_pos_x : f32 = cast(f32) int(.Fixed_Position_X in layout.flags)
|
||||
fixed_pos_y : f32 = cast(f32) int(.Fixed_Position_Y in layout.flags)
|
||||
fixed_width : f32 = cast(f32) int(.Fixed_Width in layout.flags)
|
||||
fixed_height : f32 = cast(f32) int(.Fixed_Height in layout.flags)
|
||||
|
||||
size_to_text : bool = .Size_To_Text in layout.flags
|
||||
|
||||
parent := current.parent
|
||||
computed := & current.computed
|
||||
|
||||
parent_content := parent.computed.content
|
||||
parent_content_size := parent_content.max - parent_content.min
|
||||
parent_center := parent_content.min + parent_content_size * 0.5
|
||||
|
||||
/*
|
||||
If fixed position (X or Y):
|
||||
* Ignore Margins
|
||||
* Ignore Anchors
|
||||
|
||||
If clampped position (X or Y):
|
||||
* Positon cannot exceed the anchors/margins bounds.
|
||||
|
||||
If fixed size (X or Y):
|
||||
* Ignore Parent constraints (can only be clipped)
|
||||
|
||||
If auto-sized:
|
||||
* Enforce parent size constraint of bounds relative to
|
||||
where the adjusted content bounds are after applying margins & anchors.
|
||||
The 'side' conflicting with the bounds will end at that bound side instead of clipping.
|
||||
|
||||
If size.min is not 0:
|
||||
* Ignore parent constraints if the bounds go below that value.
|
||||
|
||||
If size.max is 0:
|
||||
* Allow the child box to spread to entire adjusted content bounds, otherwise clampped to max size.
|
||||
*/
|
||||
|
||||
// 1. Anchors
|
||||
anchor := & layout.anchor
|
||||
anchored_bounds := range2(
|
||||
parent_content.min + parent_content_size * anchor.min,
|
||||
parent_content.max - parent_content_size * anchor.max,
|
||||
)
|
||||
// anchored_bounds_origin := (anchored_bounds.min + anchored_bounds.max) * 0.5
|
||||
|
||||
// 2. Apply Margins
|
||||
margins := range2(
|
||||
{ layout.margins.left, layout.margins.bottom },
|
||||
{ layout.margins.right, layout.margins.top },
|
||||
)
|
||||
margined_bounds := range2(
|
||||
anchored_bounds.min + margins.min,
|
||||
anchored_bounds.max - margins.max,
|
||||
)
|
||||
margined_bounds_origin := (margined_bounds.min + margined_bounds.max) * 0.5
|
||||
margined_size := margined_bounds.max - margined_bounds.min
|
||||
|
||||
// 3. Enforce Min/Max Size Constraints
|
||||
adjusted_max_size_x := layout.size.max.x > 0 ? min( margined_size.x, layout.size.max.x ) : margined_size.x
|
||||
adjusted_max_size_y := layout.size.max.y > 0 ? min( margined_size.y, layout.size.max.y ) : margined_size.y
|
||||
|
||||
adjusted_size : Vec2
|
||||
adjusted_size.x = max( adjusted_max_size_x, layout.size.min.x)
|
||||
adjusted_size.y = max( adjusted_max_size_y, layout.size.min.y)
|
||||
|
||||
if .Fixed_Width in layout.flags {
|
||||
adjusted_size.x = layout.size.min.x
|
||||
}
|
||||
if .Fixed_Height in layout.flags {
|
||||
adjusted_size.y = layout.size.min.y
|
||||
}
|
||||
|
||||
text_size : Vec2
|
||||
if layout.font_size == computed.text_size.y {
|
||||
text_size = computed.text_size
|
||||
}
|
||||
else {
|
||||
text_size = cast(Vec2) measure_text_size( current.text.str, style.font, layout.font_size, 0 )
|
||||
}
|
||||
|
||||
if size_to_text {
|
||||
adjusted_size = text_size
|
||||
}
|
||||
|
||||
// 5. Determine relative position
|
||||
|
||||
origin_center := margined_bounds_origin
|
||||
origin_top_left := Vec2 { margined_bounds.min.x, margined_bounds.max.y }
|
||||
|
||||
origin := .Origin_At_Anchor_Center in layout.flags ? origin_center : origin_top_left
|
||||
|
||||
rel_pos := origin + layout.pos
|
||||
|
||||
if .Fixed_Position_X in layout.flags {
|
||||
rel_pos.x = origin.x + layout.pos.x
|
||||
}
|
||||
if .Fixed_Position_Y in layout.flags {
|
||||
rel_pos.y = origin.y + layout.pos.y
|
||||
}
|
||||
|
||||
vec2_one := Vec2 { 1, 1 }
|
||||
|
||||
// 6. Determine the box bounds
|
||||
// Adjust Alignment of pivot position
|
||||
alignment := layout.alignment
|
||||
bounds := range2(
|
||||
rel_pos - adjusted_size * alignment,
|
||||
rel_pos + adjusted_size * (vec2_one - alignment),
|
||||
)
|
||||
|
||||
// Determine Padding's outer bounds
|
||||
border_offset := Vec2 { layout.border_width, layout.border_width }
|
||||
|
||||
padding_bounds := range2(
|
||||
bounds.min + border_offset,
|
||||
bounds.min - border_offset,
|
||||
)
|
||||
|
||||
// Determine Content Bounds
|
||||
content_bounds := range2(
|
||||
bounds.min + { layout.padding.left, layout.padding.bottom } + border_offset,
|
||||
bounds.max - { layout.padding.right, layout.padding.top } - border_offset,
|
||||
)
|
||||
|
||||
// computed.anchors = anchored_bounds
|
||||
// computed.margins = margined_bounds
|
||||
computed.bounds = bounds
|
||||
computed.padding = padding_bounds
|
||||
computed.content = content_bounds
|
||||
|
||||
if len(current.text.str) > 0
|
||||
{
|
||||
content_size := content_bounds.max - content_bounds.min
|
||||
text_pos : Vec2
|
||||
text_pos = content_bounds.min + { 0, text_size.y }
|
||||
text_pos.x += ( content_size.x - text_size.x ) * layout.text_alignment.x
|
||||
text_pos.y += ( content_size.y - text_size.y ) * layout.text_alignment.y
|
||||
|
||||
computed.text_size = text_size
|
||||
computed.text_pos = { text_pos.x, text_pos.y }
|
||||
}
|
||||
computed.fresh = true
|
||||
|
||||
current = ui_box_tranverse_next( current )
|
||||
if current.computed.fresh do continue
|
||||
ui_box_compute_layout( current )
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ UI_ScreenState :: struct
|
||||
pos, size, min_size : Vec2,
|
||||
container : UI_VBox,
|
||||
is_open : b32,
|
||||
is_maximized : b32,
|
||||
},
|
||||
}
|
||||
|
||||
@ -41,11 +42,12 @@ ui_screen_menu_bar :: proc()
|
||||
{
|
||||
using menu_bar
|
||||
ui_layout( UI_Layout {
|
||||
// flags = {.Fixed_Position_X, .Fixed_Position_Y, .Fixed_Width, .Fixed_Height},
|
||||
anchor = {},
|
||||
alignment = { 0, 1 },
|
||||
flags = {.Fixed_Position_X, .Fixed_Position_Y, .Fixed_Width, .Fixed_Height, .Origin_At_Anchor_Center},
|
||||
// anchor = range2({0.5, 0.5}, {0.5, 0.5} ),
|
||||
alignment = { 0.5, 0.5 },
|
||||
border_width = 1.0,
|
||||
font_size = 12,
|
||||
// pos = {},
|
||||
pos = menu_bar.pos,
|
||||
size = range2( menu_bar.size, {}),
|
||||
})
|
||||
@ -56,12 +58,13 @@ ui_screen_menu_bar :: proc()
|
||||
text_color = Color_White,
|
||||
})
|
||||
// ui_hbox( & container, .Left_To_Right, "App Menu Bar", { .Mouse_Clickable} )
|
||||
container = ui_hbox_begin( .Left_To_Right, "Menu Bar" )
|
||||
ui_parent(container)
|
||||
container = ui_hbox( .Left_To_Right, "Menu Bar" )
|
||||
// ui_parent(container)
|
||||
|
||||
ui_layout( UI_Layout {
|
||||
flags = {},
|
||||
anchor = {},
|
||||
text_alignment = {0.5, 0.5},
|
||||
border_width = 1.0,
|
||||
font_size = 12,
|
||||
})
|
||||
@ -101,7 +104,7 @@ ui_screen_menu_bar :: proc()
|
||||
spacer = ui_spacer("Menu Bar: End Spacer")
|
||||
spacer.layout.anchor.ratio.x = 1.0
|
||||
|
||||
ui_hbox_end( container)
|
||||
// ui_hbox_end( container)
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,18 +121,21 @@ ui_screen_settings_menu :: proc()
|
||||
|
||||
container = ui_vbox_begin( .Top_To_Bottom, "Settings Menu", {.Mouse_Clickable})
|
||||
{
|
||||
using container
|
||||
// flags = {}
|
||||
layout.flags = { .Origin_At_Anchor_Center }
|
||||
layout.pos = pos
|
||||
layout.alignment = { 0.5, 0.5 }
|
||||
layout.size = range2( size, {})
|
||||
style.bg_color = Color_BG_Panel_Translucent
|
||||
}
|
||||
ui_parent(container)
|
||||
{
|
||||
{
|
||||
using container
|
||||
// flags = {}
|
||||
layout.flags = { .Fixed_Width, .Fixed_Height, .Origin_At_Anchor_Center }
|
||||
layout.pos = pos
|
||||
layout.alignment = { 0.5, 0.5 }
|
||||
// layout.alignment = {}
|
||||
layout.size = range2( size, {})
|
||||
style.bg_color = Color_BG_Panel_Translucent
|
||||
}
|
||||
ui_parent(container)
|
||||
|
||||
ui_layout( UI_Layout {
|
||||
font_size = 16,
|
||||
alignment = {0, 1}
|
||||
})
|
||||
ui_style( UI_Style {
|
||||
bg_color = Color_Transparent,
|
||||
@ -153,9 +159,25 @@ ui_screen_settings_menu :: proc()
|
||||
}
|
||||
|
||||
ui_style(ui_style_peek())
|
||||
theme := ui_style_ref()
|
||||
theme.default.bg_color = Color_GreyRed
|
||||
theme.hot. bg_color = Color_Red
|
||||
style := ui_style_ref()
|
||||
style.default.bg_color = Color_Black
|
||||
style.hot.bg_color = Color_Frame_Hover
|
||||
maximize_btn := ui_button("Settings Menu: Maximize Btn")
|
||||
{
|
||||
using maximize_btn
|
||||
layout.flags = {.Fixed_Width}
|
||||
layout.size.min = {50, 0}
|
||||
layout.text_alignment = {0.5, 0.5}
|
||||
layout.anchor.ratio.x = 1.0
|
||||
if maximize_btn.pressed {
|
||||
settings_menu.is_maximized = ~settings_menu.is_maximized
|
||||
}
|
||||
if settings_menu.is_maximized do text = str_intern("min")
|
||||
else do text = str_intern("max")
|
||||
}
|
||||
|
||||
style.default.bg_color = Color_GreyRed
|
||||
style.hot. bg_color = Color_Red
|
||||
close_btn := ui_button("Settings Menu: Close Btn")
|
||||
{
|
||||
using close_btn
|
||||
@ -169,7 +191,7 @@ ui_screen_settings_menu :: proc()
|
||||
}
|
||||
}
|
||||
|
||||
ui_hbox_end(frame_bar, & size.x)
|
||||
ui_hbox_end(frame_bar)
|
||||
}
|
||||
if frame_bar.active {
|
||||
pos += input.mouse.delta
|
||||
@ -178,8 +200,13 @@ ui_screen_settings_menu :: proc()
|
||||
spacer := ui_spacer("Settings Menu: Spacer")
|
||||
spacer.layout.anchor.ratio.y = 1.0
|
||||
|
||||
ui_vbox_end(container, & size.y)
|
||||
ui_vbox_end(container, compute_layout = false )
|
||||
}
|
||||
if settings_menu.is_maximized {
|
||||
using settings_menu.container
|
||||
layout.flags = {.Origin_At_Anchor_Center }
|
||||
layout.pos = {}
|
||||
}
|
||||
|
||||
ui_resizable_handles( & container, & pos, & size )
|
||||
ui_resizable_handles( & container, & pos, & size)
|
||||
}
|
||||
|
@ -36,7 +36,6 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas
|
||||
signal.cursor_pos = ui_cursor_pos()
|
||||
signal.cursor_over = cast(b8) pos_within_range2( signal.cursor_pos, box.computed.bounds )
|
||||
|
||||
// TODO(Ed): We eventually need to setup a sorted root based on last active history
|
||||
UnderCheck:
|
||||
{
|
||||
if ! signal.cursor_over do break UnderCheck
|
||||
@ -45,10 +44,10 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas
|
||||
if last_root == nil do break UnderCheck
|
||||
|
||||
top_ancestor := ui_top_ancestor(box)
|
||||
if top_ancestor.parent_index != last_root.num_children - 1
|
||||
if top_ancestor.parent_index < last_root.parent_index
|
||||
{
|
||||
for curr := last_root.last; curr != nil && curr.key != box.key; curr = curr.prev {
|
||||
if pos_within_range2( signal.cursor_pos, curr.computed.bounds ) && curr.parent_index > top_ancestor.parent_index {
|
||||
if pos_within_range2( signal.cursor_pos, curr.computed.bounds ) {
|
||||
signal.cursor_over = false
|
||||
}
|
||||
}
|
||||
@ -72,7 +71,10 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas
|
||||
|
||||
if mouse_clickable && signal.cursor_over && left_pressed && was_hot
|
||||
{
|
||||
//TODO(Ed): We need to add the reorder of top-level widgets based on this interaction
|
||||
top_ancestor := ui_top_ancestor(box)
|
||||
|
||||
dll_full_pop(top_ancestor, top_ancestor.parent)
|
||||
dll_full_push_back( top_ancestor.parent, top_ancestor, nil )
|
||||
|
||||
// runtime.debug_trap()
|
||||
// ui.hot = box.key
|
||||
@ -212,7 +214,7 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas
|
||||
box.style_delta = 0
|
||||
}
|
||||
box.layout = ui_layout_peek().hot
|
||||
box.style = ui_style_peek().hot
|
||||
box.style = ui_style_peek().hot
|
||||
}
|
||||
if is_active
|
||||
{
|
||||
@ -221,7 +223,7 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas
|
||||
box.style_delta = 0
|
||||
}
|
||||
box.layout = ui_layout_peek().active
|
||||
box.style = ui_style_peek().active
|
||||
box.style = ui_style_peek().active
|
||||
}
|
||||
if is_disabled
|
||||
{
|
||||
@ -230,7 +232,7 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas
|
||||
box.style_delta = 0
|
||||
}
|
||||
box.layout = ui_layout_peek().disabled
|
||||
box.style = ui_style_peek().disabled
|
||||
box.style = ui_style_peek().disabled
|
||||
}
|
||||
|
||||
if ! is_disabled && ! is_active && ! is_hot {
|
||||
@ -242,7 +244,7 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas
|
||||
box.style_delta += frame_delta
|
||||
}
|
||||
box.layout = ui_layout_peek().default
|
||||
box.style = ui_style_peek().default
|
||||
box.style = ui_style_peek().default
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,16 +25,22 @@ test_draggable :: proc()
|
||||
ui := ui_context
|
||||
|
||||
draggable_layout := UI_Layout {
|
||||
anchor = {},
|
||||
flags = {
|
||||
.Fixed_Position_X, .Fixed_Position_Y,
|
||||
.Fixed_Width, .Fixed_Height,
|
||||
.Origin_At_Anchor_Center,
|
||||
},
|
||||
// alignment = { 0.0, 0.5 },
|
||||
alignment = { 0.5, 0.5 },
|
||||
alignment = { 0.5, 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 = range2({ 200, 200 }, {}),
|
||||
}
|
||||
ui_layout( draggable_layout )
|
||||
ui_style( UI_Style {
|
||||
corner_radii = { 0.3, 0.3, 0.3, 0.3 },
|
||||
})
|
||||
|
||||
draggable := ui_widget( "Draggable Box!", UI_BoxFlags { .Mouse_Clickable, .Mouse_Resizable } )
|
||||
if draggable.first_frame {
|
||||
@ -72,8 +78,9 @@ test_parenting :: proc( default_layout : ^UI_Layout, frame_style_default : ^UI_S
|
||||
parent_layout.padding = { 5, 10, 5, 5 }
|
||||
parent_layout.pos = { 0, 0 }
|
||||
parent_layout.flags = {
|
||||
// .Fixed_Position_X, .Fixed_Position_Y,
|
||||
.Fixed_Position_X, .Fixed_Position_Y,
|
||||
.Fixed_Width, .Fixed_Height,
|
||||
.Origin_At_Anchor_Center
|
||||
}
|
||||
ui_layout(parent_layout)
|
||||
|
||||
@ -155,7 +162,7 @@ test_whitespace_ast :: proc( default_layout : ^UI_Layout, frame_style_default :
|
||||
text_layout.flags = {
|
||||
.Origin_At_Anchor_Center,
|
||||
.Fixed_Position_X, .Fixed_Position_Y,
|
||||
// .Fixed_Width, .Fixed_Height,
|
||||
.Fixed_Width, .Fixed_Height,
|
||||
}
|
||||
text_layout.text_alignment = { 0.0, 0.5 }
|
||||
text_layout.alignment = { 0.0, 1.0 }
|
||||
|
@ -110,11 +110,11 @@ UI_Box :: struct {
|
||||
text : StrRunesPair,
|
||||
|
||||
// Regenerated per frame.
|
||||
using links : DLL_NodeFull( UI_Box ), // first, last, prev, next
|
||||
parent : ^UI_Box,
|
||||
num_children : i16,
|
||||
ancestors : i16,
|
||||
parent_index : i16,
|
||||
using links : DLL_NodeFull( UI_Box ), // first, last, prev, next
|
||||
parent : ^UI_Box,
|
||||
num_children : i32,
|
||||
ancestors : i32,
|
||||
parent_index : i32,
|
||||
|
||||
flags : UI_BoxFlags,
|
||||
computed : UI_Computed,
|
||||
@ -131,6 +131,7 @@ UI_Box :: struct {
|
||||
active_delta : f32,
|
||||
disabled_delta : f32,
|
||||
style_delta : f32,
|
||||
// root_order_id : i16,
|
||||
|
||||
// prev_computed : UI_Computed,
|
||||
// prev_style : UI_Style,v
|
||||
@ -143,7 +144,7 @@ UI_Layout_Stack_Size :: 512
|
||||
UI_Style_Stack_Size :: 512
|
||||
UI_Parent_Stack_Size :: 512
|
||||
// UI_Built_Boxes_Array_Size :: 8
|
||||
UI_Built_Boxes_Array_Size :: 4 * Kilobyte
|
||||
UI_Built_Boxes_Array_Size :: 16 * Kilobyte
|
||||
|
||||
UI_State :: struct {
|
||||
// TODO(Ed) : Use these
|
||||
@ -158,6 +159,9 @@ UI_State :: struct {
|
||||
|
||||
null_box : ^UI_Box, // Ryan had this, I don't know why yet.
|
||||
root : ^UI_Box,
|
||||
// Children of the root node are unique in that they have their order preserved per frame
|
||||
// This is to support overlapping frames
|
||||
// So long as their parent-index is non-negative they'll be rendered
|
||||
|
||||
// Do we need to recompute the layout?
|
||||
layout_dirty : b32,
|
||||
@ -195,9 +199,9 @@ ui_startup :: proc( ui : ^ UI_State, cache_allocator : Allocator /* , cache_rese
|
||||
verify( allocation_error == AllocatorError.None, "Failed to allocate box cache" )
|
||||
cache = box_cache
|
||||
}
|
||||
|
||||
ui.curr_cache = (& ui.caches[1])
|
||||
ui.prev_cache = (& ui.caches[0])
|
||||
|
||||
log("ui_startup completed")
|
||||
}
|
||||
|
||||
@ -242,14 +246,16 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box)
|
||||
|
||||
set_result : ^ UI_Box
|
||||
set_error : AllocatorError
|
||||
if prev_box != nil {
|
||||
if prev_box != nil
|
||||
{
|
||||
// Previous history was found, copy over previous state.
|
||||
set_result, set_error = zpl_hmap_set( curr_cache, cast(u64) key, (prev_box ^) )
|
||||
}
|
||||
else {
|
||||
box : UI_Box
|
||||
box.key = key
|
||||
box.label = str_intern( label )
|
||||
box.key = key
|
||||
box.label = str_intern( label )
|
||||
// set_result, set_error = zpl_hmap_set( prev_cache, cast(u64) key, box )
|
||||
set_result, set_error = zpl_hmap_set( curr_cache, cast(u64) key, box )
|
||||
}
|
||||
|
||||
@ -259,23 +265,54 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box)
|
||||
curr_box.first_frame = prev_box == nil
|
||||
}
|
||||
|
||||
curr_box.flags = flags
|
||||
curr_box.flags = flags
|
||||
|
||||
// Clear non-persistent data
|
||||
curr_box.computed.fresh = false
|
||||
curr_box.parent = nil
|
||||
curr_box.links = {}
|
||||
curr_box.num_children = 0
|
||||
curr_box.parent = nil
|
||||
// curr_box.ancestors = 0
|
||||
curr_box.parent_index = -1
|
||||
|
||||
// If there is a parent, setup the relevant references
|
||||
parent := stack_peek( & parent_stack )
|
||||
if curr_box.ancestors == 0 && prev_box != nil
|
||||
{
|
||||
set_error : AllocatorError
|
||||
if prev_box.first != nil {
|
||||
curr_box.first, set_error = zpl_hmap_set( curr_cache, cast(u64) prev_box.first.key, prev_box.first ^ )
|
||||
verify( set_error == AllocatorError.None, "Failed to set zpl_hmap due to allocator error" )
|
||||
}
|
||||
if prev_box.last != nil {
|
||||
curr_box.last, set_error = zpl_hmap_set( curr_cache, cast(u64) prev_box.last.key, prev_box.last ^ )
|
||||
verify( set_error == AllocatorError.None, "Failed to set zpl_hmap due to allocator error" )
|
||||
}
|
||||
}
|
||||
if parent != nil
|
||||
{
|
||||
dll_full_push_back( null_box, parent, curr_box )
|
||||
curr_box.parent_index = parent.num_children
|
||||
parent.num_children += 1
|
||||
curr_box.parent = parent
|
||||
curr_box.ancestors = parent.ancestors + 1
|
||||
if curr_box.ancestors != 1
|
||||
{
|
||||
dll_full_push_back( parent, curr_box, null_box )
|
||||
curr_box.parent_index = parent.num_children
|
||||
parent.num_children += 1
|
||||
curr_box.parent = parent
|
||||
curr_box.ancestors = parent.ancestors + 1
|
||||
}
|
||||
else if prev_box != nil
|
||||
{
|
||||
// Order was previously restored, restore linkage
|
||||
if prev_box.prev != nil {
|
||||
curr_box.prev = zpl_hmap_get( curr_cache, cast(u64) prev_box.prev.key )
|
||||
}
|
||||
if prev_box.next != nil {
|
||||
curr_box.next = zpl_hmap_get( curr_cache, cast(u64) prev_box.next.key )
|
||||
}
|
||||
curr_box.parent = ui.root
|
||||
curr_box.ancestors = 1
|
||||
curr_box.parent_index = ui.root.num_children
|
||||
parent.num_children += 1
|
||||
}
|
||||
}
|
||||
|
||||
ui.built_box_count += 1
|
||||
@ -314,7 +351,7 @@ ui_cursor_pos :: #force_inline proc "contextless" () -> Vec2 {
|
||||
return screen_to_ws_view_pos( input.mouse.pos )
|
||||
}
|
||||
else {
|
||||
return input.mouse.pos
|
||||
return input.mouse.pos
|
||||
}
|
||||
}
|
||||
|
||||
@ -327,9 +364,19 @@ ui_graph_build_begin :: proc( ui : ^ UI_State, bounds : Vec2 = {} )
|
||||
{
|
||||
profile(#procedure)
|
||||
|
||||
state := get_state()
|
||||
get_state().ui_context = ui
|
||||
using get_state().ui_context
|
||||
|
||||
if root != nil
|
||||
{
|
||||
// Set all top-level widgets to a negative index
|
||||
// This will be used for prunning the rooted_children order
|
||||
// for box := ui.root.first; box != nil; box = box.next {
|
||||
// box.parent_index = -1
|
||||
// }
|
||||
}
|
||||
|
||||
temp := prev_cache
|
||||
prev_cache = curr_cache
|
||||
curr_cache = temp
|
||||
@ -342,6 +389,9 @@ ui_graph_build_begin :: proc( ui : ^ UI_State, bounds : Vec2 = {} )
|
||||
|
||||
ui.built_box_count = 0
|
||||
root = ui_box_make( {}, "root#001" )
|
||||
if ui == & state.screen_ui {
|
||||
root.layout.size = range2(Vec2(state.app_window.extent) * 2, {})
|
||||
}
|
||||
ui_parent_push(root)
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package sectr
|
||||
|
||||
import lalg "core:math/linalg"
|
||||
|
||||
UI_Widget :: struct {
|
||||
using box : ^UI_Box,
|
||||
using signal : UI_Signal,
|
||||
@ -20,7 +22,7 @@ ui_button :: proc( label : string, flags : UI_BoxFlags = {} ) -> (btn : UI_Widge
|
||||
return
|
||||
}
|
||||
|
||||
//region Horizontal Box
|
||||
#region("Horizontal Box")
|
||||
/*
|
||||
Horizontal Boxes automatically manage a collection of widgets and
|
||||
attempt to slot them adjacent to each other along the x-axis.
|
||||
@ -52,82 +54,11 @@ ui_hbox_begin :: proc( direction : UI_LayoutDirectionX, label : string, flags :
|
||||
}
|
||||
|
||||
// Auto-layout children
|
||||
ui_hbox_end :: proc( hbox : UI_HBox, width_ref : ^f32 = nil )
|
||||
ui_hbox_end :: proc( hbox : UI_HBox, width_ref : ^f32 = nil, compute_layout := true )
|
||||
{
|
||||
// profile(#procedure)
|
||||
|
||||
// ui_box_compute_layout(hox.widget)
|
||||
|
||||
// ui_layout_children_horizontally( & hbox.box, hbox.direction, width_ref )
|
||||
{
|
||||
hbox_width : f32
|
||||
if width_ref != nil {
|
||||
hbox_width = width_ref ^
|
||||
}
|
||||
else {
|
||||
hbox_width = hbox.computed.content.max.x - hbox.computed.content.min.x
|
||||
}
|
||||
|
||||
// do layout calculations for the children
|
||||
total_stretch_ratio : f32 = 0.0
|
||||
size_req_children : f32 = 0
|
||||
for child := hbox.first; child != nil; child = child.next
|
||||
{
|
||||
using child.layout
|
||||
scaled_width_by_height : b32 = b32(.Scale_Width_By_Height_Ratio in flags)
|
||||
if .Fixed_Width in flags
|
||||
{
|
||||
if scaled_width_by_height {
|
||||
height := size.max.y != 0 ? size.max.y : hbox_width
|
||||
width := height * size.min.x
|
||||
|
||||
size_req_children += width
|
||||
continue
|
||||
}
|
||||
|
||||
size_req_children += size.min.x
|
||||
continue
|
||||
}
|
||||
|
||||
total_stretch_ratio += anchor.ratio.x
|
||||
}
|
||||
|
||||
avail_flex_space := hbox_width - size_req_children
|
||||
|
||||
allocate_space :: proc( child : ^UI_Box, total_stretch_ratio, avail_flex_space : f32 )
|
||||
{
|
||||
using child.layout
|
||||
if ! (.Fixed_Width in flags) {
|
||||
size.min.x = anchor.ratio.x * (1 / total_stretch_ratio) * avail_flex_space
|
||||
}
|
||||
flags |= {.Fixed_Width}
|
||||
}
|
||||
|
||||
space_used : f32 = 0.0
|
||||
switch hbox.direction{
|
||||
case .Right_To_Left:
|
||||
for child := hbox.last; child != nil; child = child.prev {
|
||||
allocate_space(child, total_stretch_ratio, avail_flex_space)
|
||||
using child.layout
|
||||
anchor = range2({0, 0}, {0, 0})
|
||||
alignment = { 0, 1 }// - hbox.layout.alignment
|
||||
pos.x = space_used
|
||||
space_used += size.min.x
|
||||
size.min.y = hbox.computed.content.max.y - hbox.computed.content.min.y
|
||||
}
|
||||
case .Left_To_Right:
|
||||
for child := hbox.first; child != nil; child = child.next {
|
||||
allocate_space(child, total_stretch_ratio, avail_flex_space)
|
||||
using child.layout
|
||||
anchor = range2({0, 0}, {0, 0})
|
||||
alignment = { 0, 1 }
|
||||
pos.x = space_used
|
||||
space_used += size.min.x
|
||||
size.min.y = hbox.computed.content.max.y - hbox.computed.content.min.y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if compute_layout do ui_box_compute_layout(hbox.box)
|
||||
ui_layout_children_horizontally( hbox.box, hbox.direction, width_ref )
|
||||
}
|
||||
|
||||
@(deferred_out = ui_hbox_end_auto)
|
||||
@ -139,18 +70,33 @@ ui_hbox :: #force_inline proc( direction : UI_LayoutDirectionX, label : string,
|
||||
|
||||
// Auto-layout children and pop parent from parent stack
|
||||
ui_hbox_end_auto :: proc( hbox : UI_HBox ) {
|
||||
ui_parent_pop()
|
||||
ui_hbox_end(hbox)
|
||||
ui_parent_pop()
|
||||
}
|
||||
//endregion Horizontal Box
|
||||
#endregion("Horizontal Box")
|
||||
|
||||
// Adds resizable handles to a widget
|
||||
// TODO(Ed): Add centered resize support (use center alignment on shift-click)
|
||||
ui_resizable_handles :: proc( parent : ^UI_Widget,
|
||||
pos, size : ^Vec2,
|
||||
handle_width : f32 = 15,
|
||||
handle_color_non_default : Color = Color_ResizeHandle,
|
||||
handle_color_default : Color = Color_Transparent,
|
||||
#region("Resizable")
|
||||
// Parameterized widget def for ui_resizable_handles
|
||||
UI_Resizable :: struct {
|
||||
using widget : UI_Widget,
|
||||
handle_width : f32,
|
||||
color_non_default : Color,
|
||||
color_default : Color,
|
||||
left : bool,
|
||||
right : bool,
|
||||
top : bool,
|
||||
bottom : bool,
|
||||
corner_tr : bool,
|
||||
corner_tl : bool,
|
||||
corner_br : bool,
|
||||
corner_bl : bool,
|
||||
compute_layout : bool
|
||||
}
|
||||
|
||||
ui_resizable_begin :: proc( label : string, flags : UI_BoxFlags = {},
|
||||
handle_width : f32 = 15,
|
||||
handle_color_non_default : Color = Color_ResizeHandle,
|
||||
handle_color_default : Color = Color_Transparent,
|
||||
left := true,
|
||||
right := true,
|
||||
top := true,
|
||||
@ -158,7 +104,67 @@ ui_resizable_handles :: proc( parent : ^UI_Widget,
|
||||
corner_tr := true,
|
||||
corner_tl := true,
|
||||
corner_br := true,
|
||||
corner_bl := true, )
|
||||
corner_bl := true,
|
||||
compute_layout := true ) -> (resizable : UI_Resizable)
|
||||
{
|
||||
resizable.box = ui_box_make(flags, label)
|
||||
resizable.signal = ui_signal_from_box(resizable.box)
|
||||
|
||||
resizable.handle_width = handle_width
|
||||
resizable.color_non_default = handle_color_non_default
|
||||
resizable.color_default = handle_color_default
|
||||
resizable.left = left
|
||||
resizable.right = right
|
||||
resizable.top = top
|
||||
resizable.bottom = bottom
|
||||
resizable.corner_tr = corner_tr
|
||||
resizable.corner_tl = corner_tl
|
||||
resizable.corner_br = corner_br
|
||||
resizable.corner_bl = corner_bl
|
||||
resizable.compute_layout = compute_layout
|
||||
return
|
||||
}
|
||||
|
||||
ui_resizable_end :: proc( resizable : ^UI_Resizable, pos, size : ^Vec2 ) {
|
||||
using resizable
|
||||
ui_resizable_handles( & widget, pos, size,
|
||||
handle_width,
|
||||
color_non_default,
|
||||
color_default,
|
||||
left,
|
||||
right,
|
||||
top,
|
||||
bottom,
|
||||
corner_tr,
|
||||
corner_tl,
|
||||
corner_br,
|
||||
corner_bl,
|
||||
compute_layout)
|
||||
}
|
||||
|
||||
ui_resizable_begin_auto :: proc() {
|
||||
|
||||
}
|
||||
|
||||
ui_resizable_end_auto :: proc() {
|
||||
|
||||
}
|
||||
|
||||
// Adds resizable handles to a widget
|
||||
// TODO(Ed): Add centered resize support (use center alignment on shift-click)
|
||||
ui_resizable_handles :: proc( parent : ^UI_Widget, pos : ^Vec2, size : ^Vec2,
|
||||
handle_width : f32 = 15,
|
||||
handle_color_non_default : Color = Color_ResizeHandle,
|
||||
handle_color_default : Color = Color_Transparent,
|
||||
left := true,
|
||||
right := true,
|
||||
top := true,
|
||||
bottom := true,
|
||||
corner_tr := true,
|
||||
corner_tl := true,
|
||||
corner_br := true,
|
||||
corner_bl := true,
|
||||
compute_layout := true)
|
||||
{
|
||||
profile(#procedure)
|
||||
handle_left : UI_Widget
|
||||
@ -284,6 +290,9 @@ ui_resizable_handles :: proc( parent : ^UI_Widget,
|
||||
|
||||
delta := get_state().input.mouse.delta
|
||||
alignment := & parent.layout.alignment
|
||||
|
||||
if compute_layout do ui_box_compute_layout_children(parent)
|
||||
|
||||
if right do process_handle_drag( & handle_right, { 1, 0 }, delta, {0, 0}, pos, size, alignment )
|
||||
if left do process_handle_drag( & handle_left, { -1, 0 }, delta, {1, 0}, pos, size, alignment )
|
||||
if top do process_handle_drag( & handle_top, { 0, 1 }, delta, {0, 0}, pos, size, alignment )
|
||||
@ -293,6 +302,7 @@ ui_resizable_handles :: proc( parent : ^UI_Widget,
|
||||
if corner_br do process_handle_drag( & handle_corner_br, { 1, -1 }, delta, {0, 1}, pos, size, alignment )
|
||||
if corner_bl do process_handle_drag( & handle_corner_bl, { -1, -1 }, delta, {1, 1}, pos, size, alignment )
|
||||
}
|
||||
#endregion("Resizable")
|
||||
|
||||
ui_spacer :: proc( label : string ) -> (widget : UI_Widget) {
|
||||
widget.box = ui_box_make( {.Mouse_Clickable}, label )
|
||||
@ -302,6 +312,8 @@ ui_spacer :: proc( label : string ) -> (widget : UI_Widget) {
|
||||
return
|
||||
}
|
||||
|
||||
#region("Text")
|
||||
|
||||
ui_text :: proc( label : string, content : StrRunesPair, flags : UI_BoxFlags = {} ) -> UI_Widget
|
||||
{
|
||||
// profile(#procedure)
|
||||
@ -343,8 +355,9 @@ ui_text_tabs :: proc( label : string, flags : UI_BoxFlags = {} ) -> UI_Widget
|
||||
box.text = tab_str
|
||||
return { box, signal }
|
||||
}
|
||||
#endregion("Text")
|
||||
|
||||
//region Vertical Box
|
||||
#region("Vertical Box")
|
||||
/*
|
||||
Vertical Boxes automatically manage a collection of widgets and
|
||||
attempt to slot them adjacent to each other along the y-axis.
|
||||
@ -375,77 +388,10 @@ ui_vbox_begin :: proc( direction : UI_LayoutDirectionY, label : string, flags :
|
||||
}
|
||||
|
||||
// Auto-layout children
|
||||
ui_vbox_end :: proc( vbox : UI_VBox, height_ref : ^f32 = nil ) {
|
||||
ui_vbox_end :: proc( vbox : UI_VBox, height_ref : ^f32 = nil, compute_layout := true ) {
|
||||
// profile(#procedure)
|
||||
|
||||
// ui_box_compute_layout(vbox)
|
||||
|
||||
// ui_layout_children_vertically( & hbox.box, hbox.direction, width_ref )
|
||||
{
|
||||
vbox_height : f32
|
||||
if height_ref != nil {
|
||||
vbox_height = height_ref ^
|
||||
}
|
||||
else {
|
||||
vbox_height = vbox.computed.bounds.max.y - vbox.computed.bounds.min.y
|
||||
}
|
||||
|
||||
// do layout calculations for the children
|
||||
total_stretch_ratio : f32 = 0.0
|
||||
size_req_children : f32 = 0
|
||||
for child := vbox.first; child != nil; child = child.next
|
||||
{
|
||||
using child.layout
|
||||
scaled_width_by_height : b32 = b32(.Scale_Width_By_Height_Ratio in flags)
|
||||
if .Fixed_Height in flags
|
||||
{
|
||||
if scaled_width_by_height {
|
||||
width := size.max.x != 0 ? size.max.x : vbox_height
|
||||
height := width * size.min.y
|
||||
|
||||
size_req_children += height
|
||||
continue
|
||||
}
|
||||
|
||||
size_req_children += size.min.y
|
||||
continue
|
||||
}
|
||||
|
||||
total_stretch_ratio += anchor.ratio.y
|
||||
}
|
||||
|
||||
avail_flex_space := vbox_height - size_req_children
|
||||
|
||||
allocate_space :: proc( child : ^UI_Box, total_stretch_ratio, avail_flex_space : f32 )
|
||||
{
|
||||
using child.layout
|
||||
if ! (.Fixed_Height in flags) {
|
||||
size.min.y = anchor.ratio.y * (1 / total_stretch_ratio) * avail_flex_space
|
||||
}
|
||||
flags |= {.Fixed_Height}
|
||||
alignment = {0, 0}
|
||||
}
|
||||
|
||||
space_used : f32 = 0.0
|
||||
switch vbox.direction {
|
||||
case .Top_To_Bottom:
|
||||
for child := vbox.last; child != nil; child = child.prev {
|
||||
allocate_space(child, total_stretch_ratio, avail_flex_space)
|
||||
using child.layout
|
||||
anchor = range2({0, 0}, {0, 1})
|
||||
pos.y = space_used
|
||||
space_used += size.min.y
|
||||
}
|
||||
case .Bottom_To_Top:
|
||||
for child := vbox.first; child != nil; child = child.next {
|
||||
allocate_space(child, total_stretch_ratio, avail_flex_space)
|
||||
using child.layout
|
||||
anchor = range2({0, 0}, {0, 1})
|
||||
pos.y = space_used
|
||||
space_used += size.min.y
|
||||
}
|
||||
}
|
||||
}
|
||||
if compute_layout do ui_box_compute_layout(vbox)
|
||||
ui_layout_children_vertically( vbox.box, vbox.direction, height_ref )
|
||||
}
|
||||
|
||||
// Auto-layout children and pop parent from parent stack
|
||||
@ -460,4 +406,4 @@ ui_vbox :: #force_inline proc( direction : UI_LayoutDirectionY, label : string,
|
||||
ui_parent_push(vbox.widget)
|
||||
return
|
||||
}
|
||||
//endregion Vertical Box
|
||||
#endregion("Vertical Box")
|
||||
|
8
ols.json
8
ols.json
@ -7,12 +7,12 @@
|
||||
}
|
||||
],
|
||||
"odin_command": "C:/projects/SectrPrototype/toolchain/Odin/odin.exe",
|
||||
"enable_document_symbols": true,
|
||||
"enable_fake_methods": false,
|
||||
"enable_document_symbols": false,
|
||||
"enable_fake_methods": true,
|
||||
"enable_format": false,
|
||||
"enable_hover": true,
|
||||
"enable_semantic_tokens": false,
|
||||
"enable_semantic_tokens": true,
|
||||
"enable_snippets": false,
|
||||
"enable_references": false,
|
||||
"enable_references": true,
|
||||
"thread_pool_count": 10
|
||||
}
|
||||
|
@ -169,8 +169,8 @@ push-location $path_root
|
||||
# $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
|
||||
$build_args += $flag_optimize_none
|
||||
# $build_args += $flag_optimize_minimal
|
||||
# $build_args += $flag_optimize_speed
|
||||
# $build_args += $falg_optimize_aggressive
|
||||
$build_args += $flag_debug
|
||||
@ -251,8 +251,8 @@ push-location $path_root
|
||||
# $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
|
||||
$build_args += $flag_optimize_none
|
||||
# $build_args += $flag_optimize_minimal
|
||||
# $build_args += $flag_optimize_speed
|
||||
# $build_args += $falg_optimize_aggressive
|
||||
$build_args += $flag_debug
|
||||
|
@ -3,21 +3,9 @@
|
||||
cls
|
||||
write-host "Build.ps1"
|
||||
|
||||
$incremental_checks = Join-Path $PSScriptRoot 'helpers/incremental_checks.ps1'
|
||||
. $incremental_checks
|
||||
write-host 'incremental_checks.ps1 imported'
|
||||
|
||||
$ini_parser = join-path $PSScriptRoot 'helpers/ini.ps1'
|
||||
. $ini_parser
|
||||
write-host 'ini.ps1 imported'
|
||||
|
||||
$path_root = git rev-parse --show-toplevel
|
||||
$path_code = join-path $path_root 'code'
|
||||
$path_build = join-path $path_root 'build'
|
||||
$path_scripts = join-path $path_root 'scripts'
|
||||
$path_thirdparty = join-path $path_root 'thirdparty'
|
||||
$path_toolchain = join-path $path_root 'toolchain'
|
||||
$path_odin = join-path $path_toolchain 'odin'
|
||||
|
||||
$path_virtual_view = join-path $path_root 'code_virtual_view'
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user