Lefted text box test to its own widget proc, fixed overlapping widget interaction!
This commit is contained in:
parent
635ce91a9d
commit
4a53a158e0
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
28
code/ui.odin
28
code/ui.odin
@ -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]
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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 }
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user