AppUI lifted to its own file, Got horizontal and vertical boxes working

This commit is contained in:
Edward R. Gonzalez 2024-05-10 19:20:50 -04:00
parent 5b24e591eb
commit 2b1565e35b
15 changed files with 408 additions and 260 deletions

View File

@ -191,7 +191,15 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
}
// Setup the app ui state
ui_startup( & app_ui, cache_allocator = persistent_slab_allocator() )
{
ui_startup( & app_ui.base, cache_allocator = persistent_slab_allocator() )
using app_ui
menu_bar.pos = Vec2(app_window.extent) * { -1, 1 }
menu_bar.size = {200, 40}
settings_menu.min_size = {200, 200}
}
// Demo project setup
{
@ -340,7 +348,7 @@ tick :: proc( host_delta_time : f64, host_delta_ns : Duration ) -> b32
{
// profile("Client tick timing processing")
config.engine_refresh_hz = uint(monitor_refresh_hz)
// config.engine_refresh_hz = 10
// config.engine_refresh_hz = 30
frametime_target_ms = 1.0 / f64(config.engine_refresh_hz) * S_To_MS
sub_ms_granularity_required := frametime_target_ms <= Frametime_High_Perf_Threshold_MS

185
code/app_ui.odin Normal file
View File

@ -0,0 +1,185 @@
package sectr
App_UI_State :: struct
{
using base : UI_State,
menu_bar : struct
{
pos, size : Vec2,
container : UI_HBox,
settings_btn : struct
{
using widget : UI_Widget,
is_open : b32,
}
},
settings_menu : struct
{
pos, size, min_size : Vec2,
container : UI_VBox,
},
}
ui_app_menu_bar :: proc()
{
profile("App Menu Bar")
fmt :: str_fmt_alloc
using state := get_state()
using app_ui
{
using menu_bar
ui_theme_via_style({
flags = {},
bg_color = { 0, 0, 0, 30 },
border_color = { 0, 0, 0, 200 },
font = default_font,
text_color = Color_White,
layout = {
anchor = {},
alignment = { 0, 1 },
border_width = 1.0,
font_size = 12,
pos = menu_bar.pos,
size = range2( menu_bar.size, {}),
},
})
container = ui_hbox( .Left_To_Right, "App Menu Bar", { .Mouse_Clickable} )
theme := to_ui_styletheme({
bg_color = Color_Frame_Disabled,
font = default_font,
text_color = Color_White,
layout = {
anchor = range2( {0, 0}, {0, 0} ),
alignment = { 0.0, 0.0 },
font_size = 18,
text_alignment = { 0.5, 0.5 },
size = range2({25, 0}, {0, 0})
}
})
theme.hot.bg_color = Color_Blue
theme.active.bg_color = Color_Frame_Select
ui_style_theme(theme)
move_box := ui_button("Move Box");
{
using move_box
if active {
menu_bar.pos += input.mouse.delta
}
style.anchor.ratio.x = 0.2
}
spacer := ui_spacer("Menu Bar: Move Spacer")
spacer.style.flags |= {.Fixed_Width}
spacer.style.size.min.x = 50
// spacer.style.bg_color = Color_Red
settings_btn.widget = ui_button("Settings Btn")
settings_btn.text = str_intern("Settings")
settings_btn.style.flags = {
// .Scale_Width_By_Height_Ratio,
.Fixed_Width
}
settings_btn.style.size.min.x = 100
if settings_btn.pressed {
settings_btn.is_open = true
}
spacer = ui_spacer("Menu Bar: End Spacer")
spacer.style.anchor.ratio.x = 1.0
// spacer.style.bg_color = Color_Red
}
ui_app_settings_menu()
}
ui_app_settings_menu :: proc()
{
profile("Settings Menu")
using state := get_state()
using state.app_ui
if menu_bar.settings_btn.pressed || ! menu_bar.settings_btn.is_open {
return
}
using settings_menu
if size.x < min_size.x {
size.x = min_size.x
}
if size.y < min_size.y {
size.y = min_size.y
}
container = ui_vbox_begin( .Top_To_Bottom, "Settings Menu", {.Mouse_Clickable})
{
using container
style.flags = {
// .Origin_At_Anchor_Center
}
style.pos = pos
style.alignment = { 0.5, 0.5 }
style.bg_color = Color_BG_Panel_Translucent
style.size = range2( size, {})
}
ui_parent(container)
{
ui_theme_via_style({
bg_color = Color_Transparent,
font = default_font,
text_color = Color_White,
layout = { font_size = 16 },
})
ui_style_theme_ref().hot.bg_color = Color_Blue
frame_bar := ui_hbox_begin(.Left_To_Right, "Settings Menu: Frame Bar", { .Mouse_Clickable, .Focusable, .Click_To_Focus })
{
frame_bar.style.bg_color = Color_BG_Panel
frame_bar.style.flags = {.Fixed_Height}
frame_bar.style.alignment = { 0, 0 }
// frame_bar.style.anchor.ratio.y = 0.25
frame_bar.style.size.min.y = 50
if frame_bar.active {
pos += input.mouse.delta
}
ui_parent(frame_bar)
title := ui_text("Settings Menu: Title", str_intern("Settings Menu"), {.Disabled})
{
using title
// style.alignment = {0, 1}
style.margins = { 0, 0, 15, 0}
style.text_alignment = {0 , 0.5}
style.anchor.ratio.x = 1.0
}
ui_style_theme(ui_style_theme_peek())
theme := ui_style_theme_ref()
theme.default.bg_color = Color_GreyRed
theme.hot. bg_color = Color_Red
close_btn := ui_button("Settings Menu: Close Btn")
{
using close_btn
text = str_intern("close")
style.flags = {.Fixed_Width}
// style.bg_color = Color_GreyRed
style.size.min = {50, 0}
// style.anchor = range2( {1.0, 0}, {})
// style.alignment = {0, 0}
style.text_alignment = {0.5, 0.5}
style.anchor.ratio.x = 1.0
if close_btn.pressed {
menu_bar.settings_btn.is_open = false
}
}
ui_hbox_end(frame_bar, & size.x)
}
spacer := ui_spacer("Settings Menu: Spacer")
spacer.style.anchor.ratio.y = 1.0
ui_vbox_end(container, & size.y)
}
ui_resizable_handles( & container, & pos, & size )
}

View File

@ -21,7 +21,7 @@ Color_GreyRed :: Color { 220, 100, 100, 50 }
Color_White_A125 :: Color { 255, 255, 255, 165 }
Color_Black :: Color { 0, 0, 0, 255 }
Color_Green :: Color { 0, 180, 0, 255 }
Color_ResizeHandle :: Color { 20, 40, 50, 80 }
Color_ResizeHandle :: Color { 80, 80, 90, 180 }
Color_3D_BG :: Color { 188, 182 , 170, 255 }

View File

@ -177,7 +177,7 @@ State :: struct {
config : AppConfig,
app_window : AppWindow,
app_ui : UI_State,
app_ui : App_UI_State,
monitor_id : i32,
monitor_refresh_hz : i32,

View File

@ -26,12 +26,14 @@ debug_draw_text :: proc( content : string, pos : Vec2, size : f32, color : rl.Co
px_size := size
rl_font := to_rl_Font(font, px_size )
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.POINT)
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.POINT)
}
draw_text_screenspace :: proc( content : StrRunesPair, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default )
@ -51,7 +53,7 @@ draw_text_screenspace :: proc( content : StrRunesPair, pos : Vec2, size : f32, c
rl_font := to_rl_Font(font, size )
runes := content.runes
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.TRILINEAR)
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.POINT)
rl.DrawTextCodepoints( rl_font,
raw_data(runes), cast(i32) len(runes),
position = transmute(rl.Vector2) pos,
@ -83,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.TRILINEAR)
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.BILINEAR)
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.POINT)
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.ANISOTROPIC_16X)
}
ws_view_draw_text_StrRunesPair :: proc( content : StrRunesPair, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default )
@ -112,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.TRILINEAR)
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.BILINEAR)
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.POINT)
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.ANISOTROPIC_16X)
}
// Raylib's equivalent doesn't take a length for the string (making it a pain in the ass)

View File

@ -111,6 +111,7 @@ render_mode_2d_workspace :: proc()
if root.num_children == 0 {
break ImguiRender
}
state.ui_context = ui
current := root.first
for ; current != nil; current = ui_box_tranverse_next( current )
@ -173,10 +174,10 @@ render_mode_2d_workspace :: proc()
// profile_begin("rl.DrawRectangleRoundedLines: padding & content")
if equal_range2(computed.content, computed.padding) {
// draw_rectangle_lines( rect_padding, style, Color_Debug_UI_Padding_Bounds, line_thickness )
draw_rectangle_lines( rect_padding, style, Color_Debug_UI_Padding_Bounds, line_thickness )
}
else {
// draw_rectangle_lines( rect_content, style, Color_Debug_UI_Content_Bounds, line_thickness )
draw_rectangle_lines( rect_content, style, Color_Debug_UI_Content_Bounds, line_thickness )
}
// profile_end()
@ -278,7 +279,7 @@ render_mode_screenspace :: proc ()
ui := & project.workspace.ui
debug_text("Box Count: %v", ui.built_box_count )
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 )
@ -324,6 +325,7 @@ render_app_ui :: proc()
{
profile("App UI")
ui := & state.app_ui
state.ui_context = ui
root := ui.root
if root.num_children == 0 {
break Render_App_UI
@ -423,7 +425,7 @@ render_app_ui :: proc()
// rl.DrawCircleV( render_bounds.p1, point_radius, Color_Blue )
// profile_end()
if len(current.text.str) > 0 {
if len(current.text.str) > 0 && style.font.key != 0 {
draw_text_screenspace( current.text, surface_to_render_pos(computed.text_pos), style.layout.font_size, style.text_color )
}
}

View File

@ -200,209 +200,7 @@ update :: proc( delta_time : f64 ) -> b32
ui_graph_build( & state.app_ui )
ui := ui_context
/*
Prototype app menu
This is a menu bar for the app for now inside the same ui as the workspace's UI state
Eventually this will get moved out to its own UI state for the app itself.
*/
if true
{
profile("App Menu Bar")
fmt :: str_fmt_alloc
@static bar_pos := Vec2{0, 100}
bar_size := vec2( 400, 40 )
menu_bar : UI_Widget
{
theme := UI_Style {
flags = {
},
bg_color = { 0, 0, 0, 30 },
border_color = { 0, 0, 0, 200 },
font = default_font,
text_color = Color_White,
layout = UI_Layout {
anchor = {},
border_width = 1.0,
font_size = 12,
pos = bar_pos,
size = range2( bar_size, {}),
// padding = { 10, 10, 10, 10 },
},
}
ui_theme_via_style(theme)
menu_bar = ui_widget("App Menu Bar", { .Mouse_Clickable} )
menu_bar.text = to_str_runes_pair( fmt("%v", bar_pos))
if (menu_bar.first_frame) {
// bar_pos = screen_get_corners().top_right - vec2(0, app_window.extent.y * 0.5)
}
}
// Setup Children
settings_btn : UI_Widget
{
ui_parent(menu_bar)
style := UI_Style {
flags = {
// .Origin_At_Anchor_Center
.Fixed_Height
},
bg_color = Color_Frame_Disabled,
font = default_font,
text_color = Color_White,
layout = UI_Layout {
anchor = range2( {0, 0}, {0.0, 0} ),
alignment = { 0.0, 1.0 },
font_size = 18,
text_alignment = { 0.5, 0.5 },
pos = { 0, 0 },
size = range2( {25, bar_size.y}, {0, 0})
}
}
theme := to_ui_styletheme(style)
theme.disabled.bg_color = Color_Frame_Disabled
theme.hot.bg_color = Color_Red
theme.active.bg_color = Color_Frame_Select
ui_style_theme(theme)
move_box : UI_Widget
{
move_box = ui_button("Move Box")
if move_box.active {
bar_pos += input.mouse.delta
}
using move_box
hot := ui_box_from_key(ui.curr_cache, ui.hot)
if hot != nil {
text = to_str_runes_pair(str_fmt_tmp("Hot box: %v %v", hot.label.str, hot.hot_delta))
style.font = default_font
style.font_size = 12
style.text_color = Color_White
style.text_alignment = {0, 1}
}
}
move_settings_spacer := ui_widget("Move-Settings Spacer", {})
{
using move_settings_spacer
text = str_intern("spacer")
style.bg_color = Color_Transparent
style.layout.font_size = 10
}
settings_btn = ui_button("Settings Btn")
settings_btn.text = str_intern("Settings")
settings_btn.style.flags = {
.Scale_Width_By_Height_Ratio,
}
// HBox layout calculation?
{
hb_space_ratio_move_box := 0.1
hb_space_ratio_move_settings_spacer := 0.05
hb_space_ratio_settings_btn := 1.0
style := & move_box.box.style
style.anchor.max.x = 0.9
style = & move_settings_spacer.box.style
style.anchor.min.x = 0.1
style.anchor.max.x = 0.8
style = & settings_btn.box.style
style.anchor.min.x = 0.2
style.anchor.max.x = 0.55
}
}
@static settings_open := true
if settings_btn.pressed || settings_open
{
profile("Settings Menu")
settings_open = true
resize_border_width : f32 = 20
@static pos := Vec2 {0, 0}
@static size := Vec2 { 200, 200 }
if size.x < 200 {
size.x = 200
}
if size.y < 200 {
size.y = 200
}
settings_menu := ui_widget("Settings Menu", {.Mouse_Clickable})
{
using settings_menu
style.flags = {
// .Origin_At_Anchor_Center
}
style.pos = pos
style.alignment = { 1.0, 0.5 }
style.bg_color = Color_BG_Panel_Translucent
style.size = range2( size, {})
text = to_str_runes_pair(str_fmt_tmp("Hot Delta: %v", hot_delta))
style.font = default_font
style.font_size = 12
style.text_color = Color_White
}
ui_parent(settings_menu)
ui_theme_via_style({
bg_color = Color_Transparent,
font = default_font,
text_color = Color_White,
size = range2({0, 40}, {0, 40}), // TODO(Ed): Implment ratio scaling for height
layout = { font_size = 16 },
})
ui_style_theme_ref().hot.bg_color = Color_Blue
frame_bar := ui_widget("Settings Menu: Frame Bar", { .Mouse_Clickable, .Focusable, .Click_To_Focus })
{
using frame_bar
style.bg_color = Color_BG_Panel
style.flags = {}
style.alignment = { 0, 1 }
}
ui_parent(frame_bar)
if frame_bar.active {
pos += input.mouse.delta
}
title := ui_text("Settings Menu: Title", str_intern("Settings Menu"), {.Disabled})
{
using title
style.alignment = {0, 1}
style.margins = { 0, 0, 15, 0}
style.text_alignment = {0 , 0.5}
}
ui_style_theme(ui_style_theme_peek())
ui_style_theme_ref().default.bg_color = Color_GreyRed
ui_style_theme_ref().hot. bg_color = Color_Red
close_btn := ui_button("Settings Menu: Close Btn")
{
using close_btn
text = str_intern("close")
// style.bg_color = Color_GreyRed
style.size.min = {50, 0}
style.anchor = range2( {1.0, 0}, {})
style.alignment = {1, 1}
style.text_alignment = {0.5, 0.5}
if close_btn.pressed {
settings_open = false
}
}
ui_resizable_handles( & settings_menu, & pos, & size )
}
}
ui_app_menu_bar()
}
//endregion App UI Tick

View File

@ -291,9 +291,14 @@ ui_box_tranverse_next :: proc "contextless" ( box : ^ UI_Box ) -> (^ UI_Box)
{
// Check to make sure parent is present on the screen, if its not don't bother.
// If current has children, do them first
if intersects_range2( view_get_bounds(), box.computed.bounds) && box.first != nil
using state := get_state()
if box.first != nil
{
return box.first
is_app_ui := ui_context == & app_ui
if is_app_ui || intersects_range2( view_get_bounds(), box.computed.bounds)
{
return box.first
}
}
if box.next == nil

View File

@ -21,7 +21,7 @@ UI_Signal :: struct {
commit : b8,
}
ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal
ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas := true ) -> UI_Signal
{
// profile(#procedure)
ui := get_state().ui_context
@ -199,6 +199,7 @@ ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal
// logf("was_active: %v", was_active)
// Update style if not in default state
if update_style
{
// profile("Update style")

View File

@ -1,5 +1,15 @@
package sectr
UI_LayoutDirectionX :: enum(i32) {
Left_To_Right,
Right_To_Left,
}
UI_LayoutDirectionY :: enum(i32) {
Top_To_Bottom,
Bottom_To_Top,
}
UI_LayoutSide :: struct {
// using _ : struct {
top, bottom : UI_Scalar,

View File

@ -202,7 +202,7 @@ test_whitespace_ast :: proc( default_layout : ^UI_Layout, frame_style_default :
}
ui_style_theme_set_layout( layout_text )
line_hbox := ui_widget(str_fmt_alloc( "line %v", line_id ), {})
line_hbox := ui_widget(str_fmt_alloc( "line %v", line_id ), {.Mouse_Clickable})
if line_hbox.key == ui.hot
{

View File

@ -7,8 +7,6 @@ UI_Widget :: struct {
ui_widget :: proc( label : string, flags : UI_BoxFlags ) -> (widget : UI_Widget)
{
// profile(#procedure)
widget.box = ui_box_make( flags, label )
widget.signal = ui_signal_from_box( widget.box )
return
@ -16,8 +14,6 @@ ui_widget :: proc( label : string, flags : UI_BoxFlags ) -> (widget : UI_Widget)
ui_button :: proc( label : string, flags : UI_BoxFlags = {} ) -> (btn : UI_Widget)
{
// profile(#procedure)
btn_flags := UI_BoxFlags { .Mouse_Clickable, .Focusable, .Click_To_Focus }
btn.box = ui_box_make( btn_flags | flags, label )
btn.signal = ui_signal_from_box( btn.box )
@ -32,7 +28,7 @@ attempt to slot them adjacent to each other along the x-axis.
The user must provide the direction that the hbox will append entries.
How the widgets will be scaled will be based on the individual entires style flags.
All the usual behaviors that the style and box flags do apply when manage by the box widget.
All the usual behaviors that the style and box flags do apply when managed by the box widget.
Whether or not the horizontal box will scale the widget's width is if:
fixed size or "scale by ratio" flags are not used for the width.
The hbox will use the anchor's (range2) ratio.x value to determine the "stretch ratio".
@ -40,18 +36,31 @@ The hbox will use the anchor's (range2) ratio.x value to determine the "stretch
Keep in mind the stretch ratio is only respected if no size.min.x value is violated for each of the widgets.
*/
ui_hbox_begin :: proc( label : string, flags : UI_BoxFlags = {}
//, direction
) -> (widget : UI_Widget) {
// profile(#procedure)
// Horizontal Widget
UI_HBox :: struct {
using widget : UI_Widget,
direction : UI_LayoutDirectionX,
}
widget.box = ui_box_make( flags, label )
widget.signal = ui_signal_from_box( widget.box )
// Boilerplate creation
ui_hbox_begin :: proc( direction : UI_LayoutDirectionX, label : string, flags : UI_BoxFlags = {} ) -> (hbox : UI_HBox) {
// profile(#procedure)
hbox.direction = direction
hbox.box = ui_box_make( flags, label )
hbox.signal = ui_signal_from_box( hbox.box )
return
}
ui_hbox_end :: proc( hbox : UI_Widget ) -> UI_Widget {
hbox_width := hbox.computed.content.max.y - hbox.computed.content.min.y
// Auto-layout children
ui_hbox_end :: proc( hbox : UI_HBox, width_ref : ^f32 = nil ) {
// profile(#procedure)
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
@ -74,20 +83,53 @@ ui_hbox_end :: proc( hbox : UI_Widget ) -> UI_Widget {
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.style
if ! (.Fixed_Width in child.style.flags) {
size.min.x = anchor.ratio.x * (1 / total_stretch_ratio) * avail_flex_space
}
flags |= {.Fixed_Width}
alignment = {0, 1}
}
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.style
anchor = range2({0, 0}, {0, 0})
pos.x = space_used
space_used += size.min.x
}
case .Left_To_Right:
for child := hbox.first; child != nil; child = child.next {
allocate_space(child, total_stretch_ratio, avail_flex_space)
using child.style
anchor = range2({0, 0}, {0, 0})
pos.x = space_used
space_used += size.min.x
}
}
availble_flexible_space := hbox_width - size_req_children
return hbox
}
ui_hbox_auto_end :: proc( vbox : UI_Widget ) {
ui_hbox_end(vbox)
// Auto-layout children and pop parent from parent stack
ui_hbox_end_pop_parent :: proc( hbox : UI_HBox ) {
ui_hbox_end(hbox)
ui_parent_pop()
}
@(deferred_out = ui_hbox_end)
ui_hbox :: #force_inline proc( label : string, flags : UI_BoxFlags = {} ) -> (widget : UI_Widget) {
widget = ui_hbox_begin(label, flags)
ui_parent(widget)
@(deferred_out = ui_hbox_end_pop_parent)
ui_hbox :: #force_inline proc( direction : UI_LayoutDirectionX, label : string, flags : UI_BoxFlags = {} ) -> (hbox : UI_HBox) {
hbox = ui_hbox_begin(direction, label, flags)
ui_parent_push(hbox.widget)
return
}
//endregion Horizontal Box
@ -235,6 +277,14 @@ ui_resizable_handles :: proc( parent : ^UI_Widget,
if corner_bl do process_handle_drag( & handle_corner_bl, { -1, -1 }, delta, {1, 1}, pos, size, alignment )
}
ui_spacer :: proc( label : string ) -> (widget : UI_Widget) {
widget.box = ui_box_make( {.Mouse_Clickable}, label )
widget.signal = ui_signal_from_box( widget.box )
widget.style.bg_color = Color_Transparent
return
}
ui_text :: proc( label : string, content : StrRunesPair, flags : UI_BoxFlags = {} ) -> UI_Widget
{
// profile(#procedure)
@ -278,28 +328,115 @@ ui_text_tabs :: proc( label : string, flags : UI_BoxFlags = {} ) -> UI_Widget
}
//region Vertical Box
ui_vbox_begin :: proc( label : string, flags : UI_BoxFlags = {} ) -> (widget : UI_Widget) {
// profile(#procedure)
/*
Vertical Boxes automatically manage a collection of widgets and
attempt to slot them adjacent to each other along the y-axis.
widget.box = ui_box_make( flags, label )
// widget.signal = ui_signal_from_box( widget.box )
The user must provide the direction that the vbox will append entries.
How the widgets will be scaled will be based on the individual entires style flags.
All the usual behaviors that the style and box flags do apply when managed by the box widget.
Whether or not the horizontal box will scale the widget's width is if:
fixed size or "scale by ratio" flags are not used for the width.
The hbox will use the anchor's (range2) ratio.y value to determine the "stretch ratio".
Keep in mind the stretch ratio is only respected if no size.min.y value is violated for each of the widgets.
*/
UI_VBox :: struct {
using widget : UI_Widget,
direction : UI_LayoutDirectionY,
}
// Boilerplate creation
ui_vbox_begin :: proc( direction : UI_LayoutDirectionY, label : string, flags : UI_BoxFlags = {} ) -> (vbox : UI_VBox) {
// profile(#procedure)
vbox.direction = direction
vbox.box = ui_box_make( flags, label )
vbox.signal = ui_signal_from_box( vbox.box )
return
}
ui_vbox_end :: proc( hbox : UI_Widget ) -> UI_Widget {
// Auto-layout children
ui_vbox_end :: proc( vbox : UI_VBox, height_ref : ^f32 = nil ) {
// profile(#procedure)
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
return hbox
total_stretch_ratio : f32 = 0.0
size_req_children : f32 = 0
for child := vbox.first; child != nil; child = child.next
{
using child
using style.layout
scaled_width_by_height : b32 = b32(.Scale_Width_By_Height_Ratio in style.flags)
if .Fixed_Height in style.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.style
if ! (.Fixed_Height in child.style.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.style
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.style
anchor = range2({0, 0}, {0, 1})
pos.y = space_used
space_used += size.min.y
}
}
}
ui_vbox_auto_end :: proc( hbox : UI_Widget ) {
ui_vbox_end(hbox)
// Auto-layout children and pop parent from parent stack
ui_vbox_end_pop_parent :: proc( vbox : UI_VBox ) {
ui_vbox_end(vbox)
ui_parent_pop()
}
// ui_vbox_append( widget : UI_Widget )
@(deferred_out = ui_vbox_auto_end)
ui_vbox :: #force_inline proc( label : string, flags : UI_BoxFlags = {} ) -> (widget : UI_Widget) {
widget = ui_vbox_begin(label, flags)
ui_parent_push(widget)
@(deferred_out = ui_vbox_end_pop_parent)
ui_vbox :: #force_inline proc( direction : UI_LayoutDirectionY, label : string, flags : UI_BoxFlags = {} ) -> (vbox : UI_VBox) {
vbox = ui_vbox_begin(direction, label, flags)
ui_parent_push(vbox.widget)
return
}
//endregion Vertical Box

0
docs/Persistent Normal file
View File

View File

@ -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

@ -1 +1 @@
Subproject commit e1b5ccf2dc07b6218c8d7c2ec3184594d085af89
Subproject commit e462116f9428c3a871663eae4363c8d7da35497e