From 915482343606e4b5140ef0fb60b0a9508fece9c3 Mon Sep 17 00:00:00 2001 From: Synthasmagoria Date: Sun, 2 Aug 2026 13:06:41 +0200 Subject: [PATCH] Prepping to add matrices part 2 --- libraries/snths_ui/ui.odin | 48 ++++++++----- program.odin | 135 +++++++++++++++++++------------------ 2 files changed, 103 insertions(+), 80 deletions(-) diff --git a/libraries/snths_ui/ui.odin b/libraries/snths_ui/ui.odin index cae15df..24b87ea 100644 --- a/libraries/snths_ui/ui.odin +++ b/libraries/snths_ui/ui.odin @@ -16,6 +16,8 @@ rect_grow :: #force_inline proc(rect: rect2, amount: v2) -> rect2 {return {rect. ctx: ^Context +MeasureTextProc :: proc(text: cstring, data: rawptr) -> v2 + Context :: struct { origin: v2, container_count: u16, @@ -23,6 +25,8 @@ Context :: struct { container_last_element: [dynamic]int, elements: [dynamic]Element, same_line: bool, + measure_text_callback: MeasureTextProc, + measure_text_callback_data: rawptr, } CONTAINER_STACK_SIZE :: 16 @@ -57,6 +61,7 @@ ElementType :: enum { Slider, InputFloat, Dropdown, + Label, Custom0, Custom1, Custom2, @@ -89,10 +94,12 @@ Dimensions :: struct { Axis :: enum {X, Y} -init :: proc(state: ^Context, element_capacity: int, allocator := context.allocator) { - state.containers = make(type_of(state.containers), 0, CONTAINER_STACK_SIZE, allocator) - state.container_last_element = make(type_of(state.container_last_element), 0, CONTAINER_STACK_SIZE, allocator) - state.elements = make(type_of(state.elements), 0, element_capacity, allocator) +init :: proc(ctx: ^Context, element_capacity: int, measure_text_callback: MeasureTextProc, measure_text_callback_data: rawptr, allocator := context.allocator) { + ctx.containers = make(type_of(ctx.containers), 0, CONTAINER_STACK_SIZE, allocator) + ctx.container_last_element = make(type_of(ctx.container_last_element), 0, CONTAINER_STACK_SIZE, allocator) + ctx.elements = make(type_of(ctx.elements), 0, element_capacity, allocator) + ctx.measure_text_callback = measure_text_callback + ctx.measure_text_callback_data = measure_text_callback_data } start :: proc(ui_context: ^Context, origin: v2, size: v2) { @@ -134,13 +141,14 @@ focus_next_element :: proc(id: ElementId, elements: []Element, allowed_focus: bi } container_begin :: proc(dimensions: Dimensions, config: ElementConfiguration, data: rawptr = nil) -> ^Element { - area := _resolve_element_area(dimensions, config.margin) + element := Element{config = config, type = .Container, dimensions = dimensions, data = data} + element.area = _resolve_element_area(element, is_text = false) ctx.container_count += 1 - element := _append_element({area = area, config = config, type = .Container, dimensions = dimensions, data = data}) + element_ref := _append_element(element) container_index := len(ctx.elements) - 1 append(&ctx.containers, container_index) append(&ctx.container_last_element, -1) - return element + return element_ref } container_end :: proc() { @@ -166,8 +174,7 @@ container_end :: proc() { ctx.same_line = false } -element :: proc(element_type: ElementType, dimensions: Dimensions, config: ElementConfiguration, data: rawptr = nil) -> ^Element { - assert(element_type != .Container) +assert_auto_with_pixels :: proc(dimensions: Dimensions) { container := _container_current() if _, is_auto := container.dimensions.width.(Size_Auto); is_auto { _, is_pixels := dimensions.width.(Size_Pixels) @@ -177,9 +184,14 @@ element :: proc(element_type: ElementType, dimensions: Dimensions, config: Eleme _, is_pixels := dimensions.height.(Size_Pixels) assert(is_pixels, "Any height that is not Size_Pixels is disallowed in a container using Size_Auto for height") } +} - area := _resolve_element_area(dimensions, config.margin) - return _append_element({area = area, config = config, type = element_type, dimensions = dimensions, data = data}) +element :: proc(element_type: ElementType, dimensions: Dimensions, config: ElementConfiguration, data: rawptr = nil) -> ^Element { + assert(element_type != .Container) + assert_auto_with_pixels(dimensions) + element := Element{config = config, type = element_type, dimensions = dimensions, data = data} + element.area = _resolve_element_area(element, is_text = (element.type == .Label)) + return _append_element(element) } get_element_outer_area :: proc(element: Element) -> rect2 { @@ -199,10 +211,14 @@ _append_element :: proc(element: Element) -> ^Element { return element } -_resolve_element_area :: proc(dimensions: Dimensions, margin: v2) -> rect2 { +_resolve_element_area :: proc(element: Element, is_text: bool) -> rect2 { outer_position := _resolve_element_outer_position() - outer_size := _resolve_element_outer_size(dimensions) - return rect_grow(rect2{**outer_position, **outer_size}, -margin * 2.0) + if !is_text { + outer_size := _resolve_element_outer_size(element.dimensions) + return rect_grow(rect2{**outer_position, **outer_size}, -element.margin * 2.0) + } + inner_position := ctx.measure_text_callback(element.label, ctx.measure_text_callback_data) + return {**(outer_position + element.margin), **(inner_position + element.margin)} } _resolve_element_outer_position :: proc() -> v2 { @@ -221,10 +237,10 @@ _resolve_element_outer_position :: proc() -> v2 { } _resolve_element_outer_size :: proc(dimensions: Dimensions) -> v2 { - return {_resolve_elemment_outer_size_axis(dimensions.width, .X), _resolve_elemment_outer_size_axis(dimensions.height, .Y)} + return {_resolve_element_outer_size_axis(dimensions.width, .X), _resolve_element_outer_size_axis(dimensions.height, .Y)} } -_resolve_elemment_outer_size_axis :: proc(size: Size, axis: Axis) -> f32 { +_resolve_element_outer_size_axis :: proc(size: Size, axis: Axis) -> f32 { switch value in size { case Size_Pixels: return f32(value) diff --git a/program.odin b/program.odin index f261122..d0a5f8f 100644 --- a/program.odin +++ b/program.odin @@ -91,23 +91,16 @@ ProgramMessageType :: enum { .ChangeMatrixTypeClearData = "Changing the matrix type will delete all current matrices.\nAre you sure you want to do this?", } -program_set_message_state :: proc(program: ^Program, message: ProgramMessageType) { - program.state = .Message - program.message_type = message -} - program: Program InputFloatField :: [32]byte MATRIX_POOL_SIZE :: 4096 -input_float_field_as_cstring :: proc(field: ^InputFloatField) -> cstring { - return cstring(&field[0]) -} - Program :: struct { ui_context: ui.Context, ui_element_focus: ui.ElementId, + ui_overlay_context: ui.Context, + ui_overlay_element_focus: ui.ElementId, matrix_float_pool: []f32, matrix_value_string_pool: []InputFloatField, matrix_create_dropdown_value: MatrixTypes, @@ -119,6 +112,7 @@ Program :: struct { font_style: FontStyle, state: ProgramState, message_type: ProgramMessageType, + dialog_type: ProgramDialogType, } Grid :: struct { @@ -138,6 +132,16 @@ matrix_pool_get_mat3 :: proc() -> ^mat3 { return mat } +program_show_dialog :: proc(program: ^Program, dialog_type: ProgramDialogType) { + program.state = .Dialog + program.dialog_type = dialog_type +} + +program_show_mesasge :: proc(program: ^Program, message: ProgramMessageType) { + program.state = .Message + program.message_type = message +} + program_init :: proc(allocator := context.allocator) { program.matrix_float_pool = make(type_of(program.matrix_float_pool), MATRIX_POOL_SIZE) program.matrix_value_string_pool = make(type_of(program.matrix_value_string_pool), MATRIX_POOL_SIZE) @@ -153,16 +157,10 @@ program_init :: proc(allocator := context.allocator) { zoom = 1.0, } - ui.init(&program.ui_context, 4000, allocator) + ui.init(&program.ui_context, 4000, ui_measure_text, &program.font_style, allocator) program.ui_element_focus = ui.element_id_invalid() -} - -world_to_grid_matrix :: proc(grid: Grid) -> mat3 { - return matrix3_translate2(-rect_get_tl(grid.inner_area)) -} - -grid_to_world_matrix :: proc(grid: Grid) -> mat3 { - return {} + ui.init(&program.ui_overlay_context, 200, ui_measure_text, &program.font_style, allocator) + program.ui_overlay_element_focus = ui.element_id_invalid() } program_update :: proc() { @@ -174,25 +172,7 @@ program_update :: proc() { switch program.state { case .Normal: - mouse := rl.GetMousePosition() - switch program.mouse_state { - case .Hover: - if rl.IsMouseButtonPressed(.LEFT) { - program.mouse_state = .DraggingGrid - } else if rect_has_point(rect2(matrix_container.area), mouse) { - program.mouse_state = .HoverMatrices - } - case .DraggingGrid: - program.grid.inner_area = rect_translate(program.grid.inner_area, rl.GetMouseDelta()) - if !rl.IsMouseButtonDown(.LEFT) { - program.mouse_state = .Hover - } - case .HoverMatrices: - program.matrix_scroll = max(0, program.matrix_scroll - rl.GetMouseWheelMove() * program.matrix_scroll_speed) - if !rect_has_point(rect2(matrix_container.area), mouse) { - program.mouse_state = .Hover - } - } + program.mouse_state = program_handle_mouse_workspace(program.mouse_state, matrix_container) if rl.IsKeyPressed(.TAB) { program.ui_element_focus = ui.focus_next_element(program.ui_element_focus, ui_elements, {.InputFloat}) } @@ -200,7 +180,6 @@ program_update :: proc() { case .Dialog: } - grid := program.grid grid_inner_position := rect_get_tl(grid.inner_area) draw_grid(grid.area, {32.0, 32.0}, rl.GRAY) @@ -208,14 +187,27 @@ program_update :: proc() { program_render_ui(&program.ui_context, ui_elements) } -draw_grid :: proc(area: rect2, line_interval: v2, color: rl.Color) { - br := rect_get_br(area) - for x := area.x; x < br.x; x += line_interval.x { - rl.DrawLineV({x, area.y}, {x, br.y}, color) - } - for y := area.y; y < br.y; y += line_interval.y { - rl.DrawLineV({area.x, y}, {br.x, y}, color) +program_handle_mouse_workspace :: proc(mouse_state: MouseState, matrix_container: ^ui.Element) -> MouseState { + mouse := rl.GetMousePosition() + switch mouse_state { + case .Hover: + if rl.IsMouseButtonPressed(.LEFT) { + return .DraggingGrid + } else if rect_has_point(rect2(matrix_container.area), mouse) { + return .HoverMatrices + } + case .DraggingGrid: + program.grid.inner_area = rect_translate(program.grid.inner_area, rl.GetMouseDelta()) + if !rl.IsMouseButtonDown(.LEFT) { + return .Hover + } + case .HoverMatrices: + program.matrix_scroll = max(0, program.matrix_scroll - rl.GetMouseWheelMove() * program.matrix_scroll_speed) + if !rect_has_point(rect2(matrix_container.area), mouse) { + return .Hover + } } + return mouse_state } program_build_ui :: proc() -> (elements: []ui.Element, matrix_container: ^ui.Element) { @@ -261,25 +253,6 @@ program_build_ui :: proc() -> (elements: []ui.Element, matrix_container: ^ui.Ele return } -ui_mat3 :: proc(mat: []f32, value_strings: []InputFloatField, dimensions: ui.Dimensions, config: ui.ElementConfiguration, allocator := context.temp_allocator) { - container_data := new(UiElementData_Container, allocator) - container_data^ = {type = .Matrix, thickness = 2.0} - ui.container_begin(dimensions, config, container_data) - matrix_input_config := ui.ElementConfiguration {margin = V2_ONE * 4.0} - matrix_input_dimensions := ui.Dimensions {ui.Size_Percentage(1.0 / 3.0), ui.Size_Percentage(1.0 / 3.0)} - #unroll for i in 0..<3 { - #unroll for j in 0..<3 { - data := new(UiElementData_FloatInput, allocator) - index := i * 3 + j - data^ = {value = &mat[index], value_string = input_float_field_as_cstring(&value_strings[index])} - ui.element(.InputFloat, matrix_input_dimensions, matrix_input_config, data) - ui.same_line() - } - program.ui_context.same_line = false - } - ui.container_end() -} - program_render_ui :: proc(ctx: ^ui.Context, elements: []ui.Element) { scissor_container: ^ui.Element render_above: ui.Element @@ -375,3 +348,37 @@ program_render_ui :: proc(ctx: ^ui.Context, elements: []ui.Element) { } } } + +ui_measure_text :: proc(str: cstring, font_data: rawptr) -> ui.v2 { + style := cast(^FontStyle)font_data + return rl.MeasureTextEx(style.font, str, style.size, style.spacing) +} + +ui_mat3 :: proc(mat: []f32, value_strings: []InputFloatField, dimensions: ui.Dimensions, config: ui.ElementConfiguration, allocator := context.temp_allocator) { + container_data := new(UiElementData_Container, allocator) + container_data^ = {type = .Matrix, thickness = 2.0} + ui.container_begin(dimensions, config, container_data) + matrix_input_config := ui.ElementConfiguration {margin = V2_ONE * 4.0} + matrix_input_dimensions := ui.Dimensions {ui.Size_Percentage(1.0 / 3.0), ui.Size_Percentage(1.0 / 3.0)} + #unroll for i in 0..<3 { + #unroll for j in 0..<3 { + data := new(UiElementData_FloatInput, allocator) + index := i * 3 + j + data^ = {value = &mat[index], value_string = cstring(&value_strings[index][0])} + ui.element(.InputFloat, matrix_input_dimensions, matrix_input_config, data) + ui.same_line() + } + program.ui_context.same_line = false + } + ui.container_end() +} + +draw_grid :: proc(area: rect2, line_interval: v2, color: rl.Color) { + br := rect_get_br(area) + for x := area.x; x < br.x; x += line_interval.x { + rl.DrawLineV({x, area.y}, {x, br.y}, color) + } + for y := area.y; y < br.y; y += line_interval.y { + rl.DrawLineV({area.x, y}, {br.x, y}, color) + } +}