From 4e9fe28e4d2f0353194cd20ef0b8341875fb718f Mon Sep 17 00:00:00 2001 From: Synthasmagoria Date: Fri, 24 Jul 2026 00:01:19 +0200 Subject: [PATCH] Dropdown box --- program.odin | 97 ++++++++++++++++++++++++++++++++++++++++++++++------ ui.odin | 28 ++++++++------- 2 files changed, 102 insertions(+), 23 deletions(-) diff --git a/program.odin b/program.odin index 992bb2b..991057a 100644 --- a/program.odin +++ b/program.odin @@ -20,12 +20,28 @@ FontStyle :: struct { size, spacing: f32 } +ProgramState :: enum { + Normal, + Message, +} + +ProgramMessageType :: enum { + ChangeMatrixTypeClearData, +} + +@rodata program_messages := [ProgramMessageType]cstring { + .ChangeMatrixTypeClearData = "Changing the matrix type will delete all current matrices.\nAre you sure you want to do this?", +} + Program :: struct { - ui_state: UiState, + ui_state: UiContext, matrix_field_pool: FieldPool(f32), - matrix_mode: MatrixType, + matrix_mode_perceived: MatrixType, + matrix_mode_actual: MatrixType, grid: Grid, font_style: FontStyle, + state: ProgramState, + message_type: ProgramMessageType, } Grid :: struct { @@ -45,6 +61,11 @@ field_pool_get :: proc(pool: ^FieldPool($T), count: int) -> (values: []T, inputs return fixed_pool.get(&pool.values, count), fixed_pool.get(&pool.inputs, count) } +field_pool_clear :: proc(pool: ^FieldPool($T)) { + fixed_pool.clear(&pool.values) + fixed_pool.clear(&pool.inputs) +} + field_pool_init :: proc(pool: ^FieldPool($T), capacity: int, allocator := context.allocator) { fixed_pool.init(&pool.values, capacity, allocator) fixed_pool.init(&pool.inputs, capacity, allocator) @@ -193,7 +214,17 @@ init :: proc() { ui_init(&program.ui_state, ui_style, {0, 0}, context.temp_allocator) } -update :: proc() { +program_set_message_state :: proc(program: ^Program, message: ProgramMessageType) { + program.state = .Message + program.message_type = message +} + +program_matrix_mode_dropdown_callback :: proc(previous, current: i32, data: rawptr) { + program := cast(^Program)data + program_set_message_state(program, .ChangeMatrixTypeClearData) +} + +program_build_ui :: proc() -> []UiElement { matrix_add_button_callback_data := UiButton_AddMat2CallbackData { program = &program, mat = mat2{0, 1, 2, 3}, @@ -210,8 +241,12 @@ update :: proc() { ui_button(ui_button_size_top_bar, {label = "#12#"}) ui_same_line() matrix_options := enum_to_raygui_options_string(MatrixType, context.temp_allocator) - matrix_mode_ref := cast(^i32)&program.matrix_mode - ui_dropdown(ui_dropdown_size_top_bar, {label = matrix_options, active = matrix_mode_ref}) + matrix_mode_ref := cast(^i32)&program.matrix_mode_perceived + ui_dropdown(ui_dropdown_size_top_bar, { + label = matrix_options, + active = matrix_mode_ref, + callback = program_matrix_mode_dropdown_callback, + callback_data = cast(rawptr)&program}) ui_container_end() ui_container_begin({label = "Matrices"}) field_pool := &program.matrix_field_pool @@ -227,12 +262,10 @@ update :: proc() { } } ui_container_end() - ui_elements := ui_end() - - rl.BeginDrawing() - defer rl.EndDrawing() - rl.ClearBackground(rl.BLACK) + return ui_end() +} +program_render_ui :: proc(ui_elements: []UiElement) { for element in ui_elements { switch element.type { case .Button: @@ -259,17 +292,59 @@ update :: proc() { style := program.ui_state.style draw_text(data.text, rect_get_tl(element.area), style.font_style, data.color) case .DropDown: - data := element.data.(UiElementData_DropDown) + data := element.data.(UiElementData_Dropdown) edit_mode := ui_element_is_editing(program.ui_state, element.id) + active_previous := data.active^ if rl.GuiDropdownBox(element.area, data.label, data.active, edit_mode) { if edit_mode { ui_element_focus_reset(&program.ui_state) + if active_previous != data.active^ && data.callback != nil { + data.callback(active_previous, data.active^, data.callback_data) + } } else { ui_element_focus(&program.ui_state, element.id) } } } } +} + +update :: proc() { + ui_elements := program_build_ui() + + rl.BeginDrawing() + defer rl.EndDrawing() + rl.ClearBackground(rl.BLACK) + + if program.state != .Normal { + rl.GuiDisable() + } + program_render_ui(ui_elements) + if program.state != .Normal { + rl.GuiEnable() + } + + switch program.state { + case .Normal: + case .Message: + switch program.message_type { + case .ChangeMatrixTypeClearData: + size := v2{224.0, 128.0} + position := v2(iv2{rl.GetRenderWidth(), rl.GetRenderHeight()} / 2.0) - size / 2.0 + area := rect2{**position, **size} + action := rl.GuiMessageBox(area, "Message", program_messages[program.message_type], "Confirm;Cancel") + switch action { + case 0: + case 1: + field_pool_clear(&program.matrix_field_pool) + program.matrix_mode_actual = program.matrix_mode_perceived + program.state = .Normal + case 2: + program.matrix_mode_perceived = program.matrix_mode_actual + program.state = .Normal + } + } + } free_all(context.temp_allocator) } diff --git a/ui.odin b/ui.odin index eb01205..449f997 100644 --- a/ui.odin +++ b/ui.odin @@ -5,7 +5,7 @@ import "core:fmt" import "core:mem" import rl "libraries/raylib" -@(private="file") state_current: ^UiState +@(private="file") state_current: ^UiContext UiElementType :: enum { Input, @@ -20,7 +20,7 @@ UiElementData :: union { UiElementData_Input, UiElementData_Container, UiElementData_Text, - UiElementData_DropDown, + UiElementData_Dropdown, } UiElement :: struct { @@ -35,7 +35,7 @@ UiElementId :: bit_field u32 { element_index: u16 | 16, } -UiState :: struct { +UiContext :: struct { origin, position_left, position: v2, same_line: bool, style: UiStyle, @@ -51,7 +51,7 @@ UiStyle :: struct { font_style: FontStyle, } -ui_init :: proc(state: ^UiState, style: UiStyle, origin: v2, allocator: mem.Allocator) { +ui_init :: proc(state: ^UiContext, style: UiStyle, origin: v2, allocator: mem.Allocator) { state^ = { element_focus_id = ui_id_invalid(), origin = origin, @@ -61,7 +61,7 @@ ui_init :: proc(state: ^UiState, style: UiStyle, origin: v2, allocator: mem.Allo } } -ui_start :: proc(state: ^UiState) { +ui_start :: proc(state: ^UiContext) { state_current = state clear(&state.elements) state.id_current = {group_index = 0, element_index = 0} @@ -205,12 +205,16 @@ ui_mat4 :: proc(input_field_size: v2, inputs: [][]byte, min, max: f32) { ui_input(input_field_size, {label = "", input = inputs[15], min = min, max = max}) } -UiElementData_DropDown :: struct { +UiElementCallback_Dropdown :: proc(previous, current: i32, data: rawptr) + +UiElementData_Dropdown :: struct { label: cstring, active: ^i32, + callback: UiElementCallback_Dropdown, + callback_data: rawptr, } -ui_dropdown :: proc(size: v2, data: UiElementData_DropDown) { +ui_dropdown :: proc(size: v2, data: UiElementData_Dropdown) { state := state_current area := _ui_advance_element(state, size) state.id_current.element_index += 1 @@ -221,15 +225,15 @@ ui_same_line :: proc() { state_current.same_line = true } -ui_element_is_editing :: proc(state: UiState, id: UiElementId) -> bool { +ui_element_is_editing :: proc(state: UiContext, id: UiElementId) -> bool { return state.element_focus_id == id } -ui_element_focus :: proc(state: ^UiState, id: UiElementId) { +ui_element_focus :: proc(state: ^UiContext, id: UiElementId) { state.element_focus_id = id } -ui_element_focus_reset :: proc(state: ^UiState) { +ui_element_focus_reset :: proc(state: ^UiContext) { state.element_focus_id = ui_id_invalid() } @@ -237,7 +241,7 @@ ui_id_invalid :: proc() -> UiElementId { return {group_index = max(u16), element_index = max(u16)} } -_ui_advance_element :: proc(state: ^UiState, size: v2) -> rect2 { +_ui_advance_element :: proc(state: ^UiContext, size: v2) -> rect2 { if state.group_element_count > 0 { if state.same_line { previous_element := state.elements[len(state.elements) - 1] @@ -262,7 +266,7 @@ _ui_advance_element :: proc(state: ^UiState, size: v2) -> rect2 { return area } -_ui_advance_container :: proc(state: ^UiState) -> rect2 { +_ui_advance_container :: proc(state: ^UiContext) -> rect2 { state.position += state.style.margin return {**state.position, 0.0, 0.0} }