More progress on themes + workspace resize handles support

Still need to add support for centered resize of boxes with the handles. (Will need a different alignment configuration)

Theme manipulation is getting cleaned up iteratively code is slowly looking less nosiy
This commit is contained in:
Edward R. Gonzalez 2024-05-15 18:48:20 -04:00
parent 48ee98f22d
commit 85cf9d8db2
15 changed files with 360 additions and 258 deletions

View File

@ -47,10 +47,62 @@ ui_screen_menu_bar :: proc( captures : rawptr = nil ) -> (should_raise : b32 = f
profile("App Menu Bar")
fmt :: str_fmt_alloc
@(deferred_none = ui_theme_pop)
ui_theme_app_menu_bar_default :: proc()
{
@static theme : UI_Theme
@static loaded : b32 = false
if true && ! loaded
{
layout := UI_Layout {
flags = {},
anchor = range2({},{}),
alignment = {0.5, 0.5},
text_alignment = {0.0, 1.5},
font_size = 12,
margins = {0, 0, 0, 0},
padding = {0, 0, 0, 0},
border_width = 0.6,
pos = {0, 0},
size = range2({},{})
}
style := UI_Style {
bg_color = Color_ThmDark_BG,
border_color = Color_ThmDark_Border_Default,
corner_radii = {},
blur_size = 0,
font = get_state().default_font,
text_color = Color_ThmDark_Text_Default,
cursor = {},
}
// loaded = true
layout_combo := to_ui_layout_combo(layout)
style_combo := to_ui_style_combo(style)
{
using layout_combo.hot
using style_combo.hot
bg_color = Color_ThmDark_Btn_BG_Hot
text_color = Color_ThmDark_Text_Hot
}
{
using layout_combo.active
using style_combo.active
bg_color = Color_ThmDark_Btn_BG_Active
text_color = Color_ThmDark_Text_Active
}
theme = UI_Theme {
layout_combo, style_combo
}
}
ui_layout_push(theme.layout)
ui_style_push(theme.style)
}
using state := get_state()
using screen_ui
{
using state := get_state();
using screen_ui.menu_bar
ui_theme_app_menu_bar_default()
container = ui_hbox( .Left_To_Right, "Menu Bar" )
@ -78,7 +130,7 @@ ui_screen_menu_bar :: proc( captures : rawptr = nil ) -> (should_raise : b32 = f
spacer.layout.size.min.x = 30
// TODO(Ed): Implement an external composition for theme interpolation using the settings btn
settings_btn.widget = ui_button("Settings Btn")
settings_btn.widget = ui_button("Menu Bar: Settings Btn")
{
using settings_btn
text = str_intern("Settings")
@ -109,13 +161,14 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b
if size.x < min_size.x do size.x = min_size.x
if size.y < min_size.y do size.y = min_size.y
ui_theme_transparent()
container = ui_widget("Settings Menu", {})
{
using container
layout.flags = { .Fixed_Width, .Fixed_Height, .Origin_At_Anchor_Center, .Fixed_Position_X, .Fixed_Position_Y }
layout.alignment = { 0.5, 0.5 }
// style.bg_color = Color_3D_BG
layout.flags = { .Fixed_Width, .Fixed_Height, .Fixed_Position_X, .Fixed_Position_Y, .Origin_At_Anchor_Center }
style.bg_color = Color_ThmDark_Translucent_Panel
style.border_color = { 0, 0, 0, 200 }
layout.alignment = {0.0, 0.0}
layout.border_width = 1.0
layout.pos = pos
layout.size = range2( size, {})
@ -130,7 +183,7 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b
vbox := ui_vbox_begin( .Top_To_Bottom, "Settings Menu: VBox", {.Mouse_Clickable}, compute_layout = true)
{
vbox.style.bg_color = Color_BG_Panel_Translucent
should_raise |= b32(vbox.active)
ui_parent(vbox)
ui_layout( UI_Layout {
@ -138,14 +191,14 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b
// alignment = {0, 1},
})
ui_style( UI_Style {
bg_color = Color_Transparent,
// bg_color = Color_Transparent,
font = default_font,
text_color = Color_White,
})
ui_style_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.bg_color = Color_BG_Panel
frame_bar.layout.flags = {.Fixed_Height}
frame_bar.layout.size.min.y = 50
// frame_bar.layout.anchor.ratio.y = 0.8
@ -168,8 +221,8 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b
ui_style(ui_style_peek())
style := ui_style_ref()
style.default.bg_color = Color_Black
style.hot.bg_color = Color_Frame_Hover
// style.default.bg_color = Color_Black
// style.hot.bg_color = Color_Frame_Hover
maximize_btn := ui_button("Settings Menu: Maximize Btn")
{
using maximize_btn
@ -185,8 +238,8 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b
else do text = str_intern("max")
}
style.default.bg_color = Color_GreyRed
style.hot. bg_color = Color_Red
// style.default.bg_color = Color_GreyRed
// style.hot. bg_color = Color_Red
close_btn := ui_button("Settings Menu: Close Btn")
{
using close_btn
@ -226,7 +279,7 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b
{
using drop_down_bar
text = str_intern("drop_down_bar")
style.bg_color = { 55, 55, 55, 100 }
// style.bg_color = { 55, 55, 55, 100 }
style.font = default_font
style.text_color = Color_White
layout.flags = {.Fixed_Height}
@ -243,7 +296,7 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b
style.font = default_font
style.text_color = Color_White
layout.flags = {.Origin_At_Anchor_Center}
layout.alignment = {0.5, 0.25} // ??? (Wtf is this alignment)
layout.alignment = {0.0, 0.0} // ??? (Wtf is this alignment)
layout.anchor.ratio.x = 1.0
layout.font_size = 12
layout.margins = {0,0, 15, 0}

View File

@ -41,6 +41,12 @@ Color_ThmDark_DarkLimit :: Color {10, 10, 10, 255}
Color_ThmDark_BG :: Color {33, 33, 33, 255}
Color_ThmDark_Translucent_Panel :: Color { 0, 0, 0, 60}
Color_ThmDark_ResizeHandle_Default :: Color_Transparent
Color_ThmDark_ResizeHandle_Hot :: Color { 72, 72, 72, 90}
Color_ThmDark_ResizeHandle_Active :: Color { 88, 88, 88, 90}
Color_ThmDark_Border_Default :: Color { 64, 64, 64, 255}
Color_ThmDark_Btn_BG_Default :: Color { 40, 40, 40, 255}
@ -53,4 +59,4 @@ Color_ThmDark_Text_Active :: Color {240, 240, 240, 255}
// Light Theme
// LightTheme_BG :: Color { 120, 120, 120, 255 }
// LightTheme_BG :: Color { 120, 120, 120, 255 }

View File

@ -318,9 +318,9 @@ tick :: proc( host_delta_time : f64, host_delta_ns : Duration ) -> b32
rl.PollInputEvents()
debug.draw_ui_box_bounds_points = false
debug.draw_ui_box_bounds_points = true
debug.draw_UI_padding_bounds = false
debug.draw_ui_content_bounds = false
debug.draw_ui_content_bounds = true
should_close = update( host_delta_time )
render()
@ -398,4 +398,3 @@ clean_frame :: proc()
verify( alloc_error == .None, "Failed to allocate transient slab" )
}
}

View File

@ -274,6 +274,11 @@ to_writer :: proc {
str_builder_to_writer,
}
to_ui_layout_side :: proc {
to_ui_layout_side_f32,
to_ui_layout_side_vec2,
}
ui_floating :: proc {
ui_floating_just_builder,
ui_floating_with_capture,

View File

@ -10,6 +10,10 @@ StackFixed :: struct ( $ Type : typeid, $ Size : u32 ) {
items : [ Size ] Type,
}
stack_clear :: #force_inline proc ( using stack : ^StackFixed( $Type, $Size)) {
idx = 0
}
stack_push :: #force_inline proc( using stack : ^ StackFixed( $ Type, $ Size ), value : Type ) {
verify( idx < len( items ), "Attempted to push on a full stack" )

View File

@ -210,7 +210,7 @@ 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,
.Origin_At_Anchor_Center,
}
default_layout := UI_Layout {
flags = frame_style_flags,
@ -239,7 +239,7 @@ update :: proc( delta_time : f64 ) -> b32
// test_hover_n_click()
// test_draggable()
// test_text_box()
// test_parenting( & default_layout, & frame_style_default )
test_parenting( & default_layout, & frame_style_default )
// test_whitespace_ast( & default_layout, & frame_style_default )
}
//endregion Workspace Imgui Tick

View File

@ -124,7 +124,7 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box)
parent := stack_peek( & parent_stack )
if parent != nil
{
when false {
when true {
dll_full_push_back( parent, curr_box, nil )
}
else

View File

@ -15,7 +15,6 @@ UI_FloatingManager :: struct {
using links : DLL_NodeFL(UI_Floating),
build_queue : Array(UI_Floating),
tracked : HMapChainedPtr(UI_Floating),
// tracked : HMapZPL(UI_Floating),
}
ui_floating_startup :: proc( self : ^UI_FloatingManager, allocator : Allocator, build_queue_cap, tracked_cap : u64, dbg_name : string = "" ) -> AllocatorError
@ -31,7 +30,6 @@ ui_floating_startup :: proc( self : ^UI_FloatingManager, allocator : Allocator,
}
tracked_dbg_name := str_intern(str_fmt_tmp("%s: tracked", dbg_name))
// self.tracked, error = zpl_hmap_init_reserve( UI_Floating, allocator, tracked_cap, dbg_name = tracked_dbg_name.str )
self.tracked, error = hmap_chained_init(UI_Floating, uint(tracked_cap), allocator, dbg_name = tracked_dbg_name.str )
if error != AllocatorError.None
{
@ -89,14 +87,12 @@ ui_floating_manager_end :: proc()
ui_floating_build :: proc()
{
ui := ui_context()
screen_ui := cast(^UI_ScreenState) ui
ui := ui_context()
using floating := get_state().ui_floating_context
for to_enqueue in array_to_slice( build_queue)
{
key := ui_key_from_string(to_enqueue.label)
// lookup := zpl_hmap_get( & tracked, transmute(u64) key )
lookup := hmap_chained_get( tracked, transmute(u64) key )
// Check if entry is already present
@ -109,7 +105,6 @@ ui_floating_build :: proc()
if lookup == nil {
error : AllocatorError
// lookup, error = zpl_hmap_set( & tracked, transmute(u64) key, to_enqueue )
lookup, error = hmap_chained_set( tracked, transmute(u64) key, to_enqueue )
if error != AllocatorError.None {
ensure(false, "Failed to allocate entry to hashtable")
@ -178,35 +173,5 @@ ui_floating_build :: proc()
{
dll_full_pop( to_raise, floating )
dll_full_push_back( floating, to_raise, nil )
// PopEntry:
// {
// if first == nil {
// first = to_raise
// last = to_raise
// break PopEntry
// }
// if to_raise == first
// {
// first = to_raise.next
// to_raise.next.prev = nil
// break PopEntry
// }
// if to_raise == last
// {
// // Do nothing no need to modify order
// return
// }
// left := to_raise.prev
// right := to_raise.next
// left.next = right
// right.prev = left
// }
// last.next = to_raise
// to_raise.prev = last
// last.next = nil
// last = to_raise
}
}

View File

@ -8,10 +8,15 @@ import "core:math/linalg"
// Alignment presets
LayoutAlign_OriginTL_TopLeft :: Vec2{ 0, 1}
LayoutAlign_OriginTL_TopRight :: Vec2{-1, 1}
LayoutAlign_OriginTL_Centered :: Vec2{ 0, 1}
LayoutAlign_OriginTL_BottomLeft :: Vec2{ 0, 1}
LayoutAlign_OriginTL_Top :: Vec2{0.5, 0}
LayoutAlign_OriginTL_TopLeft :: Vec2{ 0, 0}
LayoutAlign_OriginTL_TopRight :: Vec2{ 1, 0}
LayoutAlign_OriginTL_Centered :: Vec2{0.5, 0.5}
LayoutAlign_OriginTL_Bottom :: Vec2{0.5, 1}
LayoutAlign_OriginTL_BottomLeft :: Vec2{ 0, 1}
LayoutAlign_OriginTL_BottomRight :: Vec2{ 1, 1}
// LayoutAlign_OriginTL_
Layout_OriginCenter_Centered :: Vec2{0.5, 0.5}
@ -130,8 +135,9 @@ UI_LayoutCombo :: struct #raw_union {
}
}
to_ui_layout_side :: #force_inline proc( pixels : f32 ) -> UI_LayoutSide { return { pixels, pixels, pixels, pixels } }
to_ui_layout_combo :: #force_inline proc( layout : UI_Layout ) -> UI_LayoutCombo { return { layouts = {layout, layout, layout, layout} } }
to_ui_layout_side_f32 :: #force_inline proc( pixels : f32 ) -> UI_LayoutSide { return { pixels, pixels, pixels, pixels } }
to_ui_layout_side_vec2 :: #force_inline proc( v : Vec2) -> UI_LayoutSide { return { v.x, v.x, v.y, v.y} }
to_ui_layout_combo :: #force_inline proc( layout : UI_Layout ) -> UI_LayoutCombo { return { layouts = {layout, layout, layout, layout} } }
/*
Layout Interface

View File

@ -125,13 +125,22 @@ ui_box_compute_layout :: proc( box : ^UI_Box,
// 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),
)
bounds : Range2
if ! (.Origin_At_Anchor_Center in layout.flags) {
bounds.min -= { 0, adjusted_size.y }
bounds.max -= { 0, adjusted_size.y }
// The convention offset adjust the box so that the top-left point is at the top left of the anchor's bounds
tl_convention_offset := adjusted_size * {0, -1}
bounds = range2(
rel_pos - adjusted_size * alignment + tl_convention_offset,
rel_pos + adjusted_size * (vec2_one - alignment) + tl_convention_offset,
)
}
else {
centered_convention_offset := adjusted_size * -0.5
bounds = range2(
(rel_pos + centered_convention_offset) - adjusted_size * -alignment ,
(rel_pos + centered_convention_offset) + adjusted_size * (alignment + vec2_one),
)
}
// Determine Padding's outer bounds

View File

@ -11,6 +11,8 @@ UI_Signal :: struct {
right_clicked : b8,
double_clicked : b8,
keyboard_clicked : b8,
left_shift : b8,
left_ctrl : b8,
active : b8,
hot : b8,
@ -64,6 +66,8 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas
left_pressed := pressed( input.mouse.left )
left_released := released( input.mouse.left )
left_shift := pressed(input.keyboard.left_shift)
mouse_clickable := UI_BoxFlag.Mouse_Clickable in box.flags
keyboard_clickable := UI_BoxFlag.Keyboard_Clickable in box.flags
@ -84,7 +88,9 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas
ui.last_pressed_key = box.key
ui.active_start_style = box.style
signal.pressed = true
signal.pressed = true
signal.left_clicked = b8(left_pressed)
signal.left_shift = b8(left_shift)
// TODO(Ed) : Support double-click detection
}
@ -129,7 +135,7 @@ ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas
hot_vacant := ui.hot == UI_Key(0)
active_vacant := ui.active == UI_Key(0)
// (active_vacant is_active)
if signal.cursor_over
if signal.cursor_over && active_vacant
{
if ! hot_vacant {
prev := ui_box_from_key( ui.curr_cache, ui.hot )

View File

@ -73,7 +73,7 @@ test_parenting :: proc( default_layout : ^UI_Layout, frame_style_default : ^UI_S
// ui_parent(frame)
parent_layout := default_layout ^
parent_layout.size = range2( { 300, 300 }, {} )
parent_layout.alignment = { 0.5, 0.5 }
parent_layout.alignment = { 0.0, 0.0 }
// parent_layout.margins = { 100, 100, 100, 100 }
parent_layout.padding = { 5, 10, 5, 5 }
parent_layout.pos = { 0, 0 }
@ -103,6 +103,7 @@ test_parenting :: proc( default_layout : ^UI_Layout, frame_style_default : ^UI_S
parent.layout.pos = debug.draggable_box_pos
parent.layout.size.min = debug.draggable_box_size
}
ui_resizable_handles( & parent, & debug.draggable_box_pos, & debug.draggable_box_size)
child_layout := default_layout ^
child_layout.size = range2({ 100, 100 }, { 0, 0 })
@ -208,11 +209,11 @@ test_whitespace_ast :: proc( default_layout : ^UI_Layout, frame_style_default :
line_hbox.text = StrRunesPair {}
ui_parent(line_hbox)
chunk_layout := text_layout
chunk_layout := text_layout
chunk_layout.alignment = { 0.0, 1.0 }
chunk_layout.anchor = range2({ 0.0, 0 }, { 0.0, 0 })
chunk_layout.pos = {}
chunk_layout.flags = { .Fixed_Position_X, .Size_To_Text }
chunk_layout.anchor = range2({ 0.0, 0 }, { 0.0, 0 })
chunk_layout.pos = {}
chunk_layout.flags = { .Fixed_Position_X, .Size_To_Text }
chunk_style := text_style
ui_theme( to_ui_layout_combo(chunk_layout), to_ui_style_combo(chunk_style) )

View File

@ -36,7 +36,6 @@ ui_theme_via_theme :: #force_inline proc( theme : UI_Theme ) {
ui_style_push( theme.style )
}
/*
UI Themes: Comprise of UI_Box's layout & style
@ -44,93 +43,13 @@ Provides presets for themes and their interface for manipulating the combo stack
*/
// TODO(Ed): Eventually this will have a configuration wizard, and we'll save the presets
/*
UI_Theme_Template :: UI_Theme {
UI_Layout {
flags = {},
anchor = Range2{{},{}},
alignment = {},
text_alignment = {},
font_size = {},
margins = {},
padding = {},
border_width = {},
pos = {},
size = Range2{{},{}}
},
UI_Style {
bg_color = {},
corner_radii = {},
blur_size = 0,
font = {},
text_color = {},
cursor = 0,
}
}
*/
@(deferred_none = ui_theme_pop)
ui_theme_app_menu_bar_default :: proc()
{
@static theme : UI_Theme
// @static loaded : b32 = false
// if true && ! loaded
// {
layout := UI_Layout {
flags = {},
anchor = range2({},{}),
alignment = {0.5, 0.5},
text_alignment = {0.0, 1.5},
font_size = 12,
margins = {0, 0, 0, 0},
padding = {0, 0, 0, 0},
border_width = 0.6,
pos = {0, 0},
size = range2({},{})
}
style := UI_Style {
bg_color = Color_ThmDark_BG,
border_color = Color_ThmDark_Border_Default,
corner_radii = {},
blur_size = 0,
font = get_state().default_font,
text_color = Color_ThmDark_Text_Default,
cursor = {},
}
// loaded = true
layout_combo := to_ui_layout_combo(layout)
style_combo := to_ui_style_combo(style)
{
using layout_combo.hot
using style_combo.hot
bg_color = Color_ThmDark_Btn_BG_Hot
text_color = Color_ThmDark_Text_Hot
}
{
using layout_combo.active
using style_combo.active
bg_color = Color_ThmDark_Btn_BG_Active
text_color = Color_ThmDark_Text_Active
}
theme = UI_Theme {
layout_combo, style_combo
}
// }
ui_layout_push(theme.layout)
ui_style_push(theme.style)
}
@(deferred_none = ui_theme_pop)
ui_theme_btn_default :: proc()
{
@static theme : UI_Theme
// @static loaded : b32 = false
// if true && ! loaded
// {
@static loaded : b32 = false
if ! loaded
{
layout := UI_Layout {
flags = {},
anchor = range2({},{}),
@ -152,8 +71,6 @@ ui_theme_btn_default :: proc()
text_color = Color_ThmDark_Text_Default,
cursor = {},
}
// loaded = true
layout_combo := to_ui_layout_combo(layout)
style_combo := to_ui_style_combo(style)
{
@ -170,13 +87,64 @@ ui_theme_btn_default :: proc()
text_color = Color_ThmDark_Text_Active
margins = {2, 2, 2, 2}
}
theme = UI_Theme {
layout_combo, style_combo
}
loaded = true
}
ui_layout_push(theme.layout)
ui_style_push(theme.style)
}
@(deferred_none = ui_theme_pop)
ui_theme_transparent :: proc()
{
@static theme : UI_Theme
@static loaded : b32 = false
if ! loaded || true
{
layout := UI_Layout {
flags = {},
anchor = range2({},{}),
alignment = {0, 0},
text_alignment = {0.0, 0.0},
font_size = 16,
margins = {0, 0, 0, 0},
padding = {0, 0, 0, 0},
border_width = 0,
pos = {0, 0},
size = range2({},{})
}
style := UI_Style {
bg_color = Color_Transparent,
border_color = Color_Transparent,
corner_radii = {},
blur_size = 0,
font = get_state().default_font,
text_color = Color_ThmDark_Text_Default,
cursor = {},
}
layout_combo := to_ui_layout_combo(layout)
style_combo := to_ui_style_combo(style)
{
using layout_combo.disabled
using style_combo.disabled
}
{
using layout_combo.hot
using style_combo.hot
}
{
using layout_combo.active
using style_combo.active
}
theme = UI_Theme {
layout_combo, style_combo
}
// }
loaded = true
}
ui_layout_push(theme.layout)
ui_style_push(theme.style)
}

View File

@ -70,8 +70,6 @@ UI_InteractState :: struct {
UI_Key :: distinct u64
UI_Scalar :: f32
UI_ScalarConstraint :: struct {
@ -177,6 +175,11 @@ ui_cursor_pos :: #force_inline proc "contextless" () -> Vec2 {
}
}
ui_drag_delta :: #force_inline proc "contextless" () -> Vec2 {
using state := get_state()
return ui_cursor_pos() - state.ui_context.active_start_signal.cursor_pos
}
ui_ws_drag_delta :: #force_inline proc "contextless" () -> Vec2 {
using state := get_state()
return screen_to_ws_view_pos(input.mouse.pos) - state.ui_context.active_start_signal.cursor_pos
@ -190,6 +193,8 @@ ui_graph_build_begin :: proc( ui : ^ UI_State, bounds : Vec2 = {} )
get_state().ui_context = ui
using get_state().ui_context
stack_clear( & layout_combo_stack )
stack_clear( & style_combo_stack )
array_clear( render_queue )
temp := prev_cache

View File

@ -81,8 +81,7 @@ ui_hbox_end_auto :: proc( hbox : UI_HBox ) {
UI_Resizable :: struct {
using widget : UI_Widget,
handle_width : f32,
color_non_default : Color,
color_default : Color,
theme : ^UI_Theme,
left : bool,
right : bool,
top : bool,
@ -95,9 +94,8 @@ UI_Resizable :: struct {
}
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,
handle_width : f32 = 15,
theme : ^UI_Theme,
left := true,
right := true,
top := true,
@ -111,18 +109,17 @@ ui_resizable_begin :: proc( label : string, flags : UI_BoxFlags = {},
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
resizable.handle_width = handle_width
resizable.theme = theme
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
}
@ -130,8 +127,7 @@ 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,
theme,
left,
right,
top,
@ -154,9 +150,8 @@ 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,
handle_width : f32 = 15,
theme : ^UI_Theme = nil,
left := true,
right := true,
top := true,
@ -176,72 +171,114 @@ ui_resizable_handles :: proc( parent : ^UI_Widget, pos : ^Vec2, size : ^Vec2,
handle_corner_tl : UI_Widget
handle_corner_br : UI_Widget
handle_corner_bl : UI_Widget
ui_parent(parent)
flags := UI_BoxFlags { .Mouse_Clickable, .Focusable }
layout_bar_width := UI_Layout {
flags = { .Fixed_Width },
alignment = {1, 0},
margins = { handle_width, handle_width, 0, 0 },
size = range2({handle_width, 0}, {}),
@(deferred_none = ui_theme_pop)
theme_handle :: proc( base : ^UI_Theme, margins, size : Vec2, flags : UI_LayoutFlags = {})
{
layout_combo : UI_LayoutCombo
style_combo : UI_StyleCombo
if base != nil
{
layout_combo = base.layout
style_combo = base.style
{
layout_combo.default.margins = {margins.x, margins.x, margins.y, margins.y}
layout_combo.default.size.min = size
}
{
layout_combo.hot.margins = {margins.x, margins.x, margins.y, margins.y}
layout_combo.hot.size.min = size
}
{
layout_combo.active.margins = {margins.x, margins.x, margins.y, margins.y}
layout_combo.active.size.min = size
}
}
else
{
layout := UI_Layout {
flags = flags,
anchor = range2({},{}),
alignment = {0, 0},
text_alignment = {0.0, 0.0},
font_size = 16,
margins = to_ui_layout_side(margins),
padding = {0, 0, 0, 0},
border_width = 0,
pos = {0, 0},
size = range2(size,{})
}
style := UI_Style {
bg_color = Color_Transparent,
border_color = Color_Transparent,
corner_radii = {5, 0, 0, 0},
blur_size = 0,
font = get_state().default_font,
text_color = Color_ThmDark_Text_Default,
cursor = {},
}
layout_combo = to_ui_layout_combo(layout)
style_combo = to_ui_style_combo(style)
{
using layout_combo.hot
using style_combo.hot
bg_color = Color_ThmDark_ResizeHandle_Hot
}
{
using layout_combo.active
using style_combo.active
bg_color = Color_ThmDark_ResizeHandle_Active
}
}
theme := UI_Theme {
layout_combo, style_combo
}
ui_layout_push(theme.layout)
ui_style_push(theme.style)
}
style_bar := UI_Style {
bg_color = Color_ResizeHandle,
corner_radii = { 5, 0, 0, 0 }
}
theme_bar := to_ui_style_combo(style_bar)
theme_bar.default.bg_color = handle_color_default
theme_bar.default.corner_radii[0] = 0
ui_layout(layout_bar_width)
ui_style(theme_bar)
layout_bar_height := layout_bar_width
layout_bar_height.flags = {.Fixed_Height}
layout_bar_height.size.min = {0, handle_width}
layout_bar_height.margins = { 0, 0, handle_width, handle_width }
layout_bar_height.alignment = {0, -1}
context.user_ptr = & parent.label
name :: proc( ) -> StrRunesPair {
flags := UI_BoxFlags { .Mouse_Clickable }
name :: proc( label : string ) -> string {
parent_label := (transmute(^string) context.user_ptr) ^
return str_intern(str_fmt_tmp("%v: %v", ))
return str_intern(str_fmt_alloc("%v: %v", parent_label, label )).str
}
context.user_ptr = & parent.label
if left do handle_left = ui_widget("Settings Menu: Resize Left Handle", flags )
#region("Handle & Corner construction")
theme_handle( theme, {handle_width, 0}, {handle_width,0})
if left {
handle_left = ui_widget(name("resize_handle_left"), flags )
handle_left.layout.anchor.left = 0
handle_left.layout.anchor.right = 1
handle_left.layout.alignment = {1, 0}
}
if right {
handle_right = ui_widget("Settings Menu: Resize Right Handle", flags)
handle_right = ui_widget(name("resize_handle_right"), flags )
handle_right.layout.anchor.left = 1
handle_right.layout.alignment = { 0, 0 }
}
ui_layout(layout_bar_height)
ui_style_ref().default.bg_color = handle_color_default
if top do handle_top = ui_widget("Settings Menu: Resize Top Border", flags )
theme_handle( theme, {0, handle_width}, {0, handle_width})
if top {
handle_top = ui_widget(name("resize_handle_top"), flags )
handle_top.layout.anchor.bottom = 1
handle_top.layout.alignment = {0, -1}
}
if bottom {
handle_bottom = ui_widget("Settings Menu: Resize Bottom Border", flags)
using handle_bottom.layout
anchor.top = 1
alignment = { 0, 0 }
handle_bottom = ui_widget("resize_handle_bottom", flags)
handle_bottom.layout.anchor.top = 1
handle_bottom.layout.alignment = { 0, 0 }
}
layout_corner := UI_Layout {
flags = { .Fixed_Width, .Fixed_Height },
alignment = {1, -1},
size = range2({handle_width, handle_width}, {}),
theme_handle( theme, {0,0}, {handle_width, handle_width}, {.Fixed_Width, .Fixed_Height} )
if corner_tl {
handle_corner_tl = ui_widget(name("corner_top_left"), flags)
handle_corner_tl.layout.alignment = {1, -1}
}
style_corner := UI_Style {
bg_color = Color_ResizeHandle,
corner_radii = { 5, 0, 0, 0 },
}
ui_theme(layout_corner, style_corner)
ui_style_ref().default.bg_color = handle_color_default
if corner_tl do handle_corner_tl = ui_widget("Settings Menu: Corner TL", flags)
if corner_tr {
handle_corner_tr = ui_widget("Settings Menu: Corner TR", flags)
handle_corner_tr = ui_widget(name("corner_top_right"), flags)
handle_corner_tr.layout.anchor = range2({1, 0}, {})
handle_corner_tr.layout.alignment = {0, -1}
}
if corner_bl {
handle_corner_bl = ui_widget("Settings Menu: Corner BL", flags)
handle_corner_bl.layout.anchor = range2({}, {0, 1})
@ -252,27 +289,42 @@ ui_resizable_handles :: proc( parent : ^UI_Widget, pos : ^Vec2, size : ^Vec2,
handle_corner_br.layout.anchor = range2({1, 0}, {0, 1})
handle_corner_br.layout.alignment = {0, 0}
}
#endregion("Handle & Corner construction")
process_handle_drag :: #force_inline proc ( handle : ^UI_Widget,
direction : Vec2,
size_delta : Vec2,
target_alignment : Vec2,
target_center_aligned : Vec2,
pos : ^Vec2,
size : ^Vec2,
alignment : ^Vec2, ) -> b32
{
ui := get_state().ui_context
if ui.last_pressed_key != handle.key { return false }
@static active_context : ^UI_State
@static was_dragging : b32 = false
@static start_size : Vec2
size_delta := size_delta
ui := get_state().ui_context
if ui.last_pressed_key != handle.key do return false
using handle
direction := direction
target_alignment := target_alignment
size_delta := ui_drag_delta()
pos_adjust := size^ * (alignment^ - target_alignment)
@static was_dragging : b32 = false
using handle
if active
{
size^ += size_delta * direction
if pressed
{
active_context = ui
start_size = size^
if .Origin_At_Anchor_Center in parent.layout.flags {
pos_adjust = size^ * 0.5 * direction
}
}
size^ = start_size + size_delta * direction
if pressed {
pos^ -= pos_adjust
}
@ -283,26 +335,49 @@ ui_resizable_handles :: proc( parent : ^UI_Widget, pos : ^Vec2, size : ^Vec2,
}
else if released && was_dragging
{
pos^ += pos_adjust
alignment^ = target_alignment
was_dragging = false
// This needed to be added as for some reason, this was getting called in screen_ui even when we were resizing with a handle in a worksapce
if active_context != ui do return false
if .Origin_At_Anchor_Center in parent.layout.flags {
pos_adjust = size^ * 0.5 * direction
}
pos^ += pos_adjust
alignment^ = target_alignment
was_dragging = false
start_size = 0
active_context = ui
}
return was_dragging
}
delta := get_state().input.mouse.delta
state := get_state()
delta := state.input.mouse.delta //state.ui_context == & state.screen_ui ? state.input.mouse.delta : ui_ws_drag_delta()
alignment := & parent.layout.alignment
if right do drag_signal |= process_handle_drag( & handle_right, { 1, 0 }, delta, {0, 0}, pos, size, alignment )
if left do drag_signal |= process_handle_drag( & handle_left, { -1, 0 }, delta, {1, 0}, pos, size, alignment )
if top do drag_signal |= process_handle_drag( & handle_top, { 0, 1 }, delta, {0, 0}, pos, size, alignment )
if bottom do drag_signal |= process_handle_drag( & handle_bottom, { 0, -1 }, delta, {0, 1}, pos, size, alignment )
if corner_tr do drag_signal |= process_handle_drag( & handle_corner_tr, { 1, 1 }, delta, {0, 0}, pos, size, alignment )
if corner_tl do drag_signal |= process_handle_drag( & handle_corner_tl, { -1, 1 }, delta, {1, 0}, pos, size, alignment )
if corner_br do drag_signal |= process_handle_drag( & handle_corner_br, { 1, -1 }, delta, {0, 1}, pos, size, alignment )
if corner_bl do drag_signal |= process_handle_drag( & handle_corner_bl, { -1, -1 }, delta, {1, 1}, pos, size, alignment )
if .Origin_At_Anchor_Center in parent.layout.flags
{
if right do drag_signal |= process_handle_drag( & handle_right, { 1, 0}, delta, { 0.5, 0}, {}, pos, size, alignment )
if left do drag_signal |= process_handle_drag( & handle_left, {-1, 0}, delta, {-0.5, 0}, {}, pos, size, alignment )
if top do drag_signal |= process_handle_drag( & handle_top, { 0, 1}, delta, { 0, 0.5}, {}, pos, size, alignment )
if bottom do drag_signal |= process_handle_drag( & handle_bottom, { 0, -1}, delta, { 0, -0.5}, {}, pos, size, alignment )
if corner_tr do drag_signal |= process_handle_drag( & handle_corner_tr, { 1, 1}, delta, { 0.5, 0.5}, {}, pos, size, alignment )
if corner_tl do drag_signal |= process_handle_drag( & handle_corner_tl, {-1, 1}, delta, {-0.5, 0.5}, {}, pos, size, alignment )
if corner_br do drag_signal |= process_handle_drag( & handle_corner_br, { 1, -1}, delta, { 0.5, -0.5}, {}, pos, size, alignment )
if corner_bl do drag_signal |= process_handle_drag( & handle_corner_bl, {-1, -1}, delta, {-0.5, -0.5}, {}, pos, size, alignment )
}
else
{
if right do drag_signal |= process_handle_drag( & handle_right, { 1, 0 }, delta, {0, 0}, {}, pos, size, alignment )
if left do drag_signal |= process_handle_drag( & handle_left, { -1, 0 }, delta, {1, 0}, {}, pos, size, alignment )
if top do drag_signal |= process_handle_drag( & handle_top, { 0, 1 }, delta, {0, -1}, {}, pos, size, alignment )
if bottom do drag_signal |= process_handle_drag( & handle_bottom, { 0, -1 }, delta, {0, 0}, {}, pos, size, alignment )
if corner_tr do drag_signal |= process_handle_drag( & handle_corner_tr, { 1, 1 }, delta, {0, -1}, {}, pos, size, alignment )
if corner_tl do drag_signal |= process_handle_drag( & handle_corner_tl, { -1, 1 }, delta, {1, -1}, {}, pos, size, alignment )
if corner_br do drag_signal |= process_handle_drag( & handle_corner_br, { 1, -1 }, delta, {0, 0}, {}, pos, size, alignment )
if corner_bl do drag_signal |= process_handle_drag( & handle_corner_bl, { -1, -1 }, delta, {1, 0}, {}, pos, size, alignment )
}
ui_box_compute_layout(parent)
if drag_signal && compute_layout do ui_box_compute_layout(parent)
return
}
#endregion("Resizable")