Tooltip functionality

This commit is contained in:
Synthasmagoria 2026-08-27 12:26:38 +02:00
commit 2a302f7b1d
4 changed files with 62 additions and 11 deletions

7
TODO.MD Normal file
View file

@ -0,0 +1,7 @@
- Scale resolution
- Tooltips
- Reset zoom
- Save grid and animation data to file
- Load project
- Fix focus on submenu
- Centered justification in UI

View file

@ -51,6 +51,7 @@ element_id_invalid :: proc() -> ElementId {
ElementConfiguration :: struct { ElementConfiguration :: struct {
label: cstring, label: cstring,
tooltip: cstring,
color: col, color: col,
margin, padding: v2, margin, padding: v2,
} }

View file

@ -214,6 +214,7 @@ Program :: struct {
ui_overlay_context: ui.Context, ui_overlay_context: ui.Context,
ui_overlay_element_focus: ui.ElementId, ui_overlay_element_focus: ui.ElementId,
ui_overlay_queue_focus_first: bool, ui_overlay_queue_focus_first: bool,
ui_tooltip_offset: v2,
matrix_float_pool: []f32, matrix_float_pool: []f32,
matrix_value_string_pool: []InputFloatField, matrix_value_string_pool: []InputFloatField,
matrix_create_dropdown_value: HotkeyTypes, matrix_create_dropdown_value: HotkeyTypes,
@ -305,6 +306,7 @@ program_init :: proc(allocator := context.allocator) {
program.ui_element_focus = ui.element_id_invalid() program.ui_element_focus = ui.element_id_invalid()
ui.init(&program.ui_overlay_context, 200, ui_measure_text, &program.font_style, allocator) ui.init(&program.ui_overlay_context, 200, ui_measure_text, &program.font_style, allocator)
program.ui_overlay_element_focus = ui.element_id_invalid() program.ui_overlay_element_focus = ui.element_id_invalid()
program.ui_tooltip_offset = 20.0
animation_init(&program.animation, program.matrix_float_pool) animation_init(&program.animation, program.matrix_float_pool)
@ -567,29 +569,31 @@ program_build_workspace_ui :: proc(ctx: ^ui.Context) ->
top_bar_dropdown_dimensions := ui.Dimensions {ui.Size_Pixels(160.0), ui.Size_Percentage(1.0)} top_bar_dropdown_dimensions := ui.Dimensions {ui.Size_Pixels(160.0), ui.Size_Percentage(1.0)}
top_bar_dropdown_config := ui.ElementConfiguration {margin = top_bar_element_margin} top_bar_dropdown_config := ui.ElementConfiguration {margin = top_bar_element_margin}
{ {
button_config := top_bar_button_config button_margin := top_bar_element_margin
button_config: ui.ElementConfiguration
button_dimensions := top_bar_button_dimensions button_dimensions := top_bar_button_dimensions
button_data: ^UiElementData_Button button_data: ^UiElementData_Button
button_data = new(UiElementData_Button, context.temp_allocator) button_data = new(UiElementData_Button, context.temp_allocator)
button_config.label = "I" button_config = {margin = button_margin, label = "I", tooltip = "Add identity\nmatrix"}
button_data^ = {type = .AddIdentity} button_data^ = {type = .AddIdentity}
ui.element(.Button, button_dimensions, button_config, button_data) ui.element(.Button, button_dimensions, button_config, button_data)
button_data = new(UiElementData_Button, context.temp_allocator) button_data = new(UiElementData_Button, context.temp_allocator)
button_config.label = "T" button_config.label = "T"
button_config = {margin = button_margin, label = "T", tooltip = "Add translation\nmatrix"}
button_data^ = {type = .AddTranslation} button_data^ = {type = .AddTranslation}
ui.same_line() ui.same_line()
ui.element(.Button, button_dimensions, button_config, button_data) ui.element(.Button, button_dimensions, button_config, button_data)
button_data = new(UiElementData_Button, context.temp_allocator) button_data = new(UiElementData_Button, context.temp_allocator)
button_config.label = "S" button_config = {margin = button_margin, label = "S", tooltip = "Add scale\nmatrix"}
button_data^ = {type = .AddScale} button_data^ = {type = .AddScale}
ui.same_line() ui.same_line()
ui.element(.Button, button_dimensions, button_config, button_data) ui.element(.Button, button_dimensions, button_config, button_data)
button_data = new(UiElementData_Button, context.temp_allocator) button_data = new(UiElementData_Button, context.temp_allocator)
button_config.label = "R" button_config = {margin = button_margin, label = "R", tooltip = "Add rotation\nmatrix"}
button_data^ = {type = .AddRotation} button_data^ = {type = .AddRotation}
ui.same_line() ui.same_line()
ui.element(.Button, button_dimensions, button_config, button_data) ui.element(.Button, button_dimensions, button_config, button_data)
@ -738,6 +742,9 @@ program_handle_mouse :: proc(mouse_state: MouseState, top_bar_container, matrix_
} }
program_render_ui_and_handle_focus :: proc(ctx: ^ui.Context, focus_element_id: ui.ElementId, font_style: FontStyle, elements: []ui.Element) -> (focus: ui.ElementId) { program_render_ui_and_handle_focus :: proc(ctx: ^ui.Context, focus_element_id: ui.ElementId, font_style: FontStyle, elements: []ui.Element) -> (focus: ui.ElementId) {
tooltip_text: cstring
tooltip_position: v2
scissor_container: ^ui.Element scissor_container: ^ui.Element
render_above: ui.Element render_above: ui.Element
render_above.id = ui.element_id_invalid() render_above.id = ui.element_id_invalid()
@ -794,7 +801,12 @@ program_render_ui_and_handle_focus :: proc(ctx: ^ui.Context, focus_element_id: u
rl.GuiGroupBox(area, element.label) rl.GuiGroupBox(area, element.label)
} }
case .Button: case .Button:
if (rl.GuiButton(area, element.label)) { if len(element.tooltip) > 0 && rect_has_point(area, rl.GetMousePosition()) {
tooltip_text = element.tooltip
// TODO (synthas): implement keeping the rectangle within screen borders
tooltip_position = rl.GetMousePosition()
}
if rl.GuiButton(area, element.label) {
if element.data == nil do break if element.data == nil do break
data := cast(^UiElementData_Button)element.data data := cast(^UiElementData_Button)element.data
switch data.type { switch data.type {
@ -919,6 +931,15 @@ program_render_ui_and_handle_focus :: proc(ctx: ^ui.Context, focus_element_id: u
} }
} }
if len(tooltip_text) > 0 {
position := tooltip_position + program.ui_tooltip_offset
size := measure_text(tooltip_text, program.font_style)
bounds := rect_grow(rect_from(position, size), 4.0)
rl.DrawRectangleRec(bounds, rl.BLACK)
rl.DrawRectangleLinesEx(bounds, 1.0, rl.WHITE)
draw_multiline_text_centered(tooltip_text, position, program.font_style, rl.WHITE, context.temp_allocator)
}
return focus_element_id return focus_element_id
} }

View file

@ -22,7 +22,7 @@ write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (succ
} }
/// ### RAYLIB DRAWING UTILS ### /// /// ### RAYLIB DRAWING UTILS ### ///
measure_text :: proc(text: cstring, style: FontStyle) -> v2 { @require_results measure_text :: proc(text: cstring, style: FontStyle) -> v2 {
return rl.MeasureTextEx(style.font, text, style.size, style.spacing) return rl.MeasureTextEx(style.font, text, style.size, style.spacing)
} }
@ -30,20 +30,42 @@ draw_text :: proc(text: cstring, position: v2, style: FontStyle, color: rl.Color
rl.DrawTextEx(style.font, text, position, style.size, style.spacing, color) rl.DrawTextEx(style.font, text, position, style.size, style.spacing, color)
} }
TextHalign :: enum { draw_multiline_text_centered :: proc(text: cstring, position: v2, style: FontStyle, color: rl.Color, temp_allocator := context.temp_allocator) {
size := measure_text(text, style)
split := strings.split(strings.clone_from_cstring(text), "\n", temp_allocator)
for line, i in split {
line := strings.clone_to_cstring(line, temp_allocator)
line_size := measure_text(line, style)
offset := v2{(size.x - line_size.x) * 0.5, line_size.y * f32(i)}
draw_text(line, position + offset, style, color)
}
}
JustificationHalign :: enum {
Left, Left,
Middle, Middle,
Right, Right,
} }
TextValign :: enum { JustificationValign :: enum {
Top, Top,
Center, Center,
Bottom, Bottom,
} }
draw_text_aligned :: proc(text: cstring, position: v2, style: FontStyle, halign: TextHalign, valign: TextValign, color: rl.Color) { JustificationAlign :: enum {
Shallow,
Middle,
Deep,
}
draw_text_aligned :: proc(text: cstring, position: v2, style: FontStyle, halign: JustificationHalign, valign: JustificationValign, color: rl.Color) {
size := measure_text(text, style) size := measure_text(text, style)
draw_text(text, justify_2d(position, size, halign, valign), style, color)
}
@require_results justify_2d :: proc(position: v2, size: v2, halign: JustificationHalign, valign: JustificationValign) -> v2 {
position := position position := position
switch halign { switch halign {
case .Left: case .Left:
@ -55,10 +77,10 @@ draw_text_aligned :: proc(text: cstring, position: v2, style: FontStyle, halign:
case .Center: position.y -= size.y / 2.0 case .Center: position.y -= size.y / 2.0
case .Bottom: position.y -= size.y case .Bottom: position.y -= size.y
} }
draw_text(text, position, style, color) return position
} }
draw_float_aligned :: proc(value: f32, position: v2, style: FontStyle, halign: TextHalign, valign: TextValign, color: rl.Color) { draw_float_aligned :: proc(value: f32, position: v2, style: FontStyle, halign: JustificationHalign, valign: JustificationValign, color: rl.Color) {
fractional := fract(value) fractional := fract(value)
text: cstring text: cstring
if fractional == 0.0 { if fractional == 0.0 {