Lefted text box test to its own widget proc, fixed overlapping widget interaction!

This commit is contained in:
Edward R. Gonzalez 2024-03-09 13:55:47 -05:00
parent 635ce91a9d
commit 4a53a158e0
8 changed files with 146 additions and 98 deletions

View File

@ -8,7 +8,7 @@ import str "core:strings"
import "core:time"
import core_log "core:log"
Max_Logger_Message_Width :: 80
Max_Logger_Message_Width :: 300
LogLevel :: core_log.Level

View File

@ -89,7 +89,7 @@ draw_text_string_cached :: proc( content : StringCached, pos : Vec2, size : f32,
// Raylib's equivalent doesn't take a length for the string (making it a pain in the ass)
// So this is a 1:1 copy except it takes Odin strings
measure_text_size :: proc( text : string, font : FontID, font_size := Font_Use_Default_Size, spacing : f32 ) -> AreaSize
measure_text_size :: proc( text : string, font : FontID, font_size := Font_Use_Default_Size, spacing : f32 ) -> Vec2
{
px_size := math.round( points_to_pixels( font_size ) )
rl_font := to_rl_Font( font, font_size )
@ -98,7 +98,7 @@ measure_text_size :: proc( text : string, font : FontID, font_size := Font_Use_D
// Note(Ed) : raylib font size is in pixels so this is also.
@static text_line_spacing : f32 = 15
text_size : AreaSize
text_size : Vec2
if rl_font.texture.id == 0 || len(text) == 0 {
return text_size

View File

@ -73,6 +73,18 @@ render :: proc()
rl.DrawCircleV( cursor_pos, 10, Color_White_A125 )
}
ui := project.workspace.ui
hot_box := zpl_hmap_get( ui.curr_cache, u64(ui.hot) )
active_box := zpl_hmap_get( ui.curr_cache, u64(ui.active) )
if hot_box != nil {
debug_text("Hot Box: %v", hot_box.label.str )
}
if active_box != nil{
debug_text("Active Box: %v", active_box.label.str )
}
debug_text("Active Resizing: %v", ui.active_start_signal.resizing)
debug.draw_debug_text_y = 50
}
//endregion Render Screenspace
@ -143,10 +155,36 @@ render_mode_2d :: proc()
}
rl.DrawRectangleRounded( rect_bounds, style.layout.corner_radii[0], 9, style.bg_color )
rl.DrawRectangleRoundedLines( rect_padding, style.layout.corner_radii[0], 9, 2, Color_Debug_UI_Padding_Bounds )
rl.DrawRectangleRoundedLines( rect_content, style.layout.corner_radii[0], 9, 2, Color_Debug_UI_Content_Bounds )
rl.DrawCircleV( render_bounds.p0, 5, Color_Red )
rl.DrawCircleV( render_bounds.p1, 5, Color_Blue )
line_thickness := 1 * (1 / cam.zoom)
rl.DrawRectangleRoundedLines( rect_padding, style.layout.corner_radii[0], 9, line_thickness, Color_Debug_UI_Padding_Bounds )
rl.DrawRectangleRoundedLines( rect_content, style.layout.corner_radii[0], 9, line_thickness, Color_Debug_UI_Content_Bounds )
if .Mouse_Resizable in current.flags {
resize_border_width := cast(f32) get_state().config.ui_resize_border_width
resize_percent_width := style.size * (1.0 / resize_border_width)
resize_border_non_range := add(current.computed.bounds, range2(
{ resize_percent_width.x, -resize_percent_width.x },
{ -resize_percent_width.x, resize_percent_width.x }))
render_resize := range2(
world_to_screen_pos(resize_border_non_range.min),
world_to_screen_pos(resize_border_non_range.max),
)
rect_resize := rl.Rectangle {
render_resize.min.x,
render_resize.min.y,
render_resize.max.x - render_resize.min.x,
render_resize.max.y - render_resize.min.y,
}
rl.DrawRectangleRoundedLines( rect_resize, style.layout.corner_radii[0], 9, line_thickness, Color_Red )
}
// if current
// rl.DrawCircleV( render_bounds.p0, 5, Color_Red )
// rl.DrawCircleV( render_bounds.p1, 5, Color_Blue )
if len(current.text.str) > 0 {
draw_text_string_cached( current.text, world_to_screen_pos(computed.text_pos), style.font_size, style.text_color )
@ -163,7 +201,7 @@ render_mode_2d :: proc()
rl.DrawCircleV( world_to_screen_pos(cursor_world_pos), 5, Color_GreyRed )
}
rl.DrawCircleV( { 0, 0 }, 1, Color_White )
rl.DrawCircleV( { 0, 0 }, 1 * (1 / cam.zoom), Color_White )
rl.EndMode2D()
}

View File

@ -197,87 +197,55 @@ update :: proc( delta_time : f64 ) -> b32
.Fixed_Position_X, .Fixed_Position_Y,
.Fixed_Width, .Fixed_Height,
}
default_layout := UI_Layout {
anchor = {},
// alignment = { 0.0, 0.5 },
alignment = { 0.5, 0.5 },
text_alignment = { 0.5, 0.5 },
// alignment = { 1.0, 1.0 },
// corner_radii = { 0.3, 0.3, 0.3, 0.3 },
pos = { 0, 0 },
size = { 200, 200 },
}
frame_style_default := UI_Style {
flags = frame_style_flags,
bg_color = Color_BG_TextBox,
font = default_font,
font_size = 30,
text_color = Color_White,
layout = default_layout,
}
frame_style_disabled := UI_Style {
flags = frame_style_flags,
bg_color = Color_Frame_Disabled,
font = default_font,
font_size = 30,
text_color = Color_White,
}
frame_style_hovered := UI_Style {
flags = frame_style_flags,
bg_color = Color_Frame_Hover,
font = default_font,
font_size = 30,
text_color = Color_White,
}
frame_style_select := UI_Style {
flags = frame_style_flags,
bg_color = Color_Frame_Select,
font = default_font,
font_size = 30,
text_color = Color_White,
}
frame_theme := UI_StyleTheme { styles = {
frame_style_default,
frame_style_disabled,
frame_style_hovered,
frame_style_select,
frame_style_default,
frame_style_default,
frame_style_default,
}}
frame_theme.disabled.bg_color = Color_Frame_Disabled
frame_theme.hovered.bg_color = Color_Frame_Hover
frame_theme.focused.bg_color = Color_Frame_Select
ui_style_theme( frame_theme )
default_layout := UI_Layout {
anchor = {},
// alignment = { 0.0, 0.5 },
alignment = { 0.5, 0.5 },
text_alignment = { 1.0, 1.0 },
// alignment = { 1.0, 1.0 },
corner_radii = { 0.3, 0.3, 0.3, 0.3 },
pos = { 0, 0 },
size = { 200, 200 },
}
ui_set_layout( default_layout )
// test_hover_n_click()
// test_draggable()
config.ui_resize_border_width = 2
test_draggable()
config.ui_resize_border_width = 20
// First box with text!!!!
when true
{
@static pos : Vec2
style := ui_style_peek( .Default )
ui_style_theme( { styles = { style, style, style, style, }} )
text := str_intern( "Lorem ipsum dolor sit amet")
font_size := 30
text_size := measure_text_size( text.str, default_font, 30, 0 )
ui_style_theme( { styles = {
frame_style_default,
frame_style_default,
frame_style_default,
frame_style_default,
}})
layout := default_layout
layout.size = cast(Vec2) text_size
layout.size.y *= 4
layout.size.x *= 1.5
// layout.size.y *= 3.248
// layout.size.x *= 1.348
// layout.size.x *= 1.348
layout.padding = ui_layout_padding( 30 )
ui_set_layout( layout )
text_box := ui_widget( "TEXT BOX!", UI_BoxFlags { .Mouse_Clickable, .Focusable, .Click_To_Focus } )
text_box := ui_text("TEXT BOX!", text, 30, flags = { .Mouse_Clickable })
if text_box.first_frame {
pos = text_box.style.layout.pos
text_box.text = text
}
if text_box.dragging {
@ -286,6 +254,8 @@ update :: proc( delta_time : f64 ) -> b32
text_box.style.layout.pos = pos
}
// test_draggable()
}
//endregion Imgui Tick

View File

@ -55,10 +55,12 @@ UI_AnchorPresets :: enum u32 {
UI_BoxFlag :: enum u64 {
Disabled,
Focusable,
Click_To_Focus,
Mouse_Clickable,
Mouse_Resizable,
Keyboard_Clickable,
Click_To_Focus,
Scroll_X,
Scroll_Y,
@ -164,7 +166,6 @@ UI_Signal :: struct {
released : b8,
dragging : b8,
resizing : b8,
hovering : b8,
cursor_over : b8,
commit : b8,
}
@ -281,17 +282,16 @@ UI_State :: struct {
// flag_stack : Stack( UI_BoxFlags, UI_BoxFlags_Stack_Size ),
hot : UI_Key,
active_mouse : [MouseBtn.count] UI_Key,
active : UI_Key,
hot_resizable : b32,
active_resizing : b32, // Locks the user into a resizing state for the active box until they release the active key
hot_start_style : UI_Style,
active_mouse : [MouseBtn.count] UI_Key,
active : UI_Key,
active_start_signal : UI_Signal,
clipboard_copy : UI_Key,
last_clicked : UI_Key,
cursor_active_start : Vec2,
hot_start_style : UI_Style,
active_start_style : UI_Style,
last_pressed_key : [MouseBtn.count] UI_Key,
@ -413,7 +413,7 @@ 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.cursor_active_start
return ui_cursor_pos() - state.ui_context.active_start_signal.cursor_pos
}
ui_graph_build_begin :: proc( ui : ^ UI_State, bounds : Vec2 = {} )
@ -424,7 +424,11 @@ ui_graph_build_begin :: proc( ui : ^ UI_State, bounds : Vec2 = {} )
curr_cache, prev_cache = swap( curr_cache, prev_cache )
if ui.active == UI_Key(0) {
ui.hot = UI_Key(0)
//ui.hot = UI_Key(0)
ui.active_start_signal = {}
}
if ui.hot == UI_Key(0) {
ui.hot_resizable = false
}
root = ui_box_make( {}, "root#001" )
@ -475,6 +479,10 @@ ui_parent :: proc( ui : ^ UI_Box) {
ui_parent_push( ui )
}
ui_style_peek :: proc( box_state : UI_StylePreset ) -> UI_Style {
return stack_peek_ref( & get_state().ui_context.theme_stack ).array[box_state]
}
ui_style_ref :: proc( box_state : UI_StylePreset ) -> (^ UI_Style) {
return & stack_peek_ref( & get_state().ui_context.theme_stack ).array[box_state]
}

View File

@ -13,13 +13,15 @@ ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal
signal.cursor_pos = ui_cursor_pos()
signal.cursor_over = cast(b8) pos_within_range2( signal.cursor_pos, box.computed.bounds )
resize_border_width := cast(f32) get_state().config.ui_resize_border_width
resize_border_width := cast(f32) get_state().config.ui_resize_border_width
resize_percent_width := box.style.size * (1.0 / resize_border_width)
resize_border_non_range := add(box.computed.bounds, range2(
{ resize_border_width, -resize_border_width },
{ -resize_border_width, resize_border_width }))
{ resize_percent_width.x, -resize_percent_width.x },
{ -resize_percent_width.x, resize_percent_width.x }))
within_resize_range := cast(b8) ! pos_within_range2( signal.cursor_pos, resize_border_non_range )
within_resize_range &= signal.cursor_over
within_resize_range &= .Mouse_Resizable in box.flags
left_pressed := pressed( input.mouse.left )
left_released := released( input.mouse.left )
@ -38,8 +40,6 @@ ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal
ui.active_mouse[MouseBtn.Left] = box.key
ui.last_pressed_key = box.key
ui.cursor_active_start = signal.cursor_pos
ui.active_start_style = box.style
signal.pressed = true
@ -48,7 +48,6 @@ ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal
if mouse_clickable && signal.cursor_over && left_released
{
box.active_delta = 0
ui.active = UI_Key(0)
ui.active_mouse[MouseBtn.Left] = UI_Key(0)
@ -95,20 +94,33 @@ ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal
is_hot := ui.hot == box.key
is_active := ui.active == box.key
if signal.cursor_over &&
ui.hot == UI_Key(0) || is_hot &&
ui.active == UI_Key(0) || is_active
if signal.cursor_over
{
ui.hot = box.key
is_hot = true
hot_vacant := ui.hot == UI_Key(0)
active_vacant := ui.active == UI_Key(0)
ui.hot_start_style = box.style
if (hot_vacant || is_hot) &&
(active_vacant || is_active)
{
// prev_hot := zpl_hmap_get( ui.prev_cache, u64(ui.hot) )
// prev_hot_label := prev_hot != nil ? prev_hot.label.str : ""
// log( str_fmt_tmp("Detected HOT via CURSOR OVER: %v is_hot: %v is_active: %v prev_hot: %v", box.label.str, is_hot, is_active, prev_hot_label ))
ui.hot = box.key
is_hot = true
ui.hot_start_style = box.style
}
}
else
{
is_hot = false
if ui.hot == box.key {
ui.hot = UI_Key(0)
}
}
if ! is_active {
ui.hot_resizable = cast(b32) within_resize_range
}
signal.resizing = cast(b8) is_active && (within_resize_range || ui.active_resizing)
signal.resizing = cast(b8) is_active && (within_resize_range || ui.active_start_signal.resizing)
ui.hot_resizable = cast(b32) (is_hot && within_resize_range) || signal.resizing
// State Deltas update
if is_hot
@ -142,8 +154,7 @@ ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal
box.disabled_delta = 0
}
ui.active_resizing = cast(b32) is_active && signal.resizing
signal.dragging = cast(b8) is_active && ( ! within_resize_range && ! ui.active_resizing)
signal.dragging = cast(b8) is_active && ( ! within_resize_range && ! ui.active_start_signal.resizing)
// Update style if not in default state
{
@ -160,6 +171,7 @@ ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal
if ! was_active {
box.prev_style = box.style
box.style_delta = 0
log( str_fmt_tmp("NEW ACTIVE: %v", box.label.str))
}
box.style = stack_peek( & ui.theme_stack ).focused
}
@ -184,5 +196,8 @@ ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal
}
}
if is_active && ! was_active {
ui.active_start_signal = signal
}
return signal
}

View File

@ -23,7 +23,7 @@ test_draggable :: proc()
state := get_state(); using state
ui := ui_context
draggable := ui_widget( "Draggable Box!", UI_BoxFlags { .Mouse_Clickable, .Focusable, .Click_To_Focus } )
draggable := ui_widget( "Draggable Box!", UI_BoxFlags { .Mouse_Clickable, .Mouse_Resizable } )
if draggable.first_frame {
debug.draggable_box_pos = draggable.style.layout.pos
debug.draggable_box_size = draggable.style.layout.size
@ -40,14 +40,14 @@ test_draggable :: proc()
og_layout := ui_context.active_start_style.layout
center := debug.draggable_box_pos
original_distance := linalg.distance(ui.cursor_active_start, center)
original_distance := linalg.distance(ui.active_start_signal.cursor_pos, center)
cursor_distance := linalg.distance(draggable.cursor_pos, center)
scale_factor := cursor_distance * (1 / original_distance)
debug.draggable_box_size = og_layout.size * scale_factor
}
if ui.hot_resizable || ui.active_resizing {
if (ui.hot == draggable.key) && (ui.hot_resizable || ui.active_start_signal.resizing) {
draggable.style.bg_color = Color_Blue
}

View File

@ -20,3 +20,20 @@ ui_button :: proc( label : string, flags : UI_BoxFlags = {} ) -> (btn : UI_Widge
return
}
ui_text :: proc( label : string, content : StringCached, font_size : f32 = 24, font := Font_Default, flags : UI_BoxFlags ) -> UI_Widget
{
state := get_state(); using state
font := font
if font == Font_Default {
font = default_font
}
text_size := measure_text_size( content.str, font, font_size, 0 )
box := ui_box_make( flags, "TEXT BOX!" )
signal := ui_signal_from_box( box )
box.text = content
box.style.layout.size = text_size
return { box, signal }
}