From 2a302f7b1d1800def4e490016ddaf8fb630d5822 Mon Sep 17 00:00:00 2001 From: Synthasmagoria Date: Thu, 27 Aug 2026 12:26:38 +0200 Subject: [PATCH] Tooltip functionality --- TODO.MD | 7 +++++++ libraries/snths_ui/ui.odin | 1 + program.odin | 31 ++++++++++++++++++++++++++----- utils.odin | 34 ++++++++++++++++++++++++++++------ 4 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 TODO.MD diff --git a/TODO.MD b/TODO.MD new file mode 100644 index 0000000..4718e46 --- /dev/null +++ b/TODO.MD @@ -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 diff --git a/libraries/snths_ui/ui.odin b/libraries/snths_ui/ui.odin index cbd539d..badf8cc 100644 --- a/libraries/snths_ui/ui.odin +++ b/libraries/snths_ui/ui.odin @@ -51,6 +51,7 @@ element_id_invalid :: proc() -> ElementId { ElementConfiguration :: struct { label: cstring, + tooltip: cstring, color: col, margin, padding: v2, } diff --git a/program.odin b/program.odin index ed3712a..5a08f33 100644 --- a/program.odin +++ b/program.odin @@ -214,6 +214,7 @@ Program :: struct { ui_overlay_context: ui.Context, ui_overlay_element_focus: ui.ElementId, ui_overlay_queue_focus_first: bool, + ui_tooltip_offset: v2, matrix_float_pool: []f32, matrix_value_string_pool: []InputFloatField, matrix_create_dropdown_value: HotkeyTypes, @@ -305,6 +306,7 @@ program_init :: proc(allocator := context.allocator) { program.ui_element_focus = ui.element_id_invalid() 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_tooltip_offset = 20.0 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_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_data: ^UiElementData_Button 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} ui.element(.Button, button_dimensions, button_config, button_data) button_data = new(UiElementData_Button, context.temp_allocator) button_config.label = "T" + button_config = {margin = button_margin, label = "T", tooltip = "Add translation\nmatrix"} button_data^ = {type = .AddTranslation} ui.same_line() ui.element(.Button, button_dimensions, button_config, button_data) 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} ui.same_line() ui.element(.Button, button_dimensions, button_config, button_data) 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} ui.same_line() 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) { + tooltip_text: cstring + tooltip_position: v2 + scissor_container: ^ui.Element render_above: ui.Element 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) } 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 data := cast(^UiElementData_Button)element.data 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 } diff --git a/utils.odin b/utils.odin index ddebc77..e32d65b 100644 --- a/utils.odin +++ b/utils.odin @@ -22,7 +22,7 @@ write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (succ } /// ### 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) } @@ -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) } -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, Middle, Right, } -TextValign :: enum { +JustificationValign :: enum { Top, Center, 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) + 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 switch halign { 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 .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) text: cstring if fractional == 0.0 {