From 22feae4b45adf69b5e1e4983c6165aa7b1747276 Mon Sep 17 00:00:00 2001 From: Synthasmagoria Date: Tue, 21 Jul 2026 19:35:21 +0200 Subject: [PATCH] imgui part 1 --- program.odin | 108 ++++++++++----------------------------------- ui.odin | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++ utility.odin | 115 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 259 insertions(+), 85 deletions(-) create mode 100644 ui.odin create mode 100644 utility.odin diff --git a/program.odin b/program.odin index b8ac268..7aff58b 100644 --- a/program.odin +++ b/program.odin @@ -1,9 +1,6 @@ package main import rl "libraries/raylib" -import "core:fmt" -import "core:strings" -import "core:mem" import "base:runtime" WINDOW_WIDTH :: 1280 @@ -11,72 +8,6 @@ WINDOW_HEIGHT :: 720 WINDOW_FRAMERATE :: 30 WINDOW_CAPTION :: "Transformation Visualizer" -v2 :: [2]f32 -rect2 :: rl.Rectangle - -UiElementType :: enum { - Input, - Button, -} - -UiElementData :: union { - UiButtonData, -} - -UiButtonData :: struct { - label: cstring, -} - -UiElement :: struct { - area: rect2, - type: UiElementType, - data: UiElementData, -} - -UiState :: struct { - origin, position: v2, - same_line: bool, - style: UiStyle, - elements: [dynamic]UiElement, -} - -UiStyle :: struct { - margin, padding: f32, -} - -ui_button :: proc(state: ^UiState, size: v2, data: UiButtonData) { - element := UiElement{area = {**state.position, **size}, type = .Button, data = data} - if state.same_line { - state.position.x += size.x + state.style.padding - state.same_line = false - } else { - state.position.y += size.y + state.style.padding - } - append(&state.elements, element) -} - -ui_same_line :: proc(state: ^UiState) { - state.same_line = true -} - -ui_start :: proc(state: ^UiState) { - clear(&state.elements) - state.position = state.origin + state.style.margin -} - -ui_end :: proc(state: UiState) -> []UiElement { - return state.elements[:] -} - -ui_init :: proc(state: ^UiState, style: UiStyle, origin: v2, allocator: mem.Allocator) { - state^ = { - origin = origin, - position = origin, - style = style, - elements = make(type_of(state.elements), 0, 512, allocator) - } -} - init :: proc() { rl.SetTraceLogLevel(.WARNING) rl.SetConfigFlags({.WINDOW_RESIZABLE, .VSYNC_HINT}) @@ -85,33 +16,40 @@ init :: proc() { } update :: proc() { - ui_style := UiStyle{margin = 4.0, padding = 4.0} - ui_state: UiState - ui_init(&ui_state, ui_style, {0, 0}, context.temp_allocator) - ui_start(&ui_state) - ui_button_size := v2{192.0, 32.0} - ui_same_line(&ui_state) - ui_button(&ui_state, ui_button_size, {label = "Hi"}) - ui_same_line(&ui_state) - ui_button(&ui_state, ui_button_size, {label = "Hi"}) - ui_same_line(&ui_state) - ui_button(&ui_state, ui_button_size, {label = "Hi"}) - ui_elements := ui_end(ui_state) - rl.BeginDrawing() defer rl.EndDrawing() rl.ClearBackground(rl.BLACK) + ui_style := UiStyle{margin = 4.0, padding = 4.0} + ui_state: UiState + ui_init(&ui_state, ui_style, {0, 0}, context.temp_allocator) + ui_start(&ui_state) + ui_container_begin(&ui_state, {label = ""}) + ui_button_size := v2{192.0, 32.0} + ui_button(&ui_state, ui_button_size, {label = "Hi"}) + ui_same_line(&ui_state) + ui_button(&ui_state, ui_button_size, {label = "Hi"}) + ui_same_line(&ui_state) + ui_button(&ui_state, ui_button_size, {label = "Hi"}) + ui_container_end(&ui_state) + ui_elements := ui_end(ui_state) + for element in ui_elements { - fmt.println(element) switch element.type { case .Button: - data := element.data.(UiButtonData) + data := element.data.(UiElementData_Button) rl.GuiButton(element.area, data.label) case .Input: + case .Container: + data := element.data.(UiElementData_Container) + if len(data.label) == 0 { + rl.DrawRectangleRoundedLinesEx(element.area, 0.1, 2, 1.0, rl.WHITE) + } else { + rl.GuiGroupBox(element.area, data.label) + } + } } - fmt.println() free_all(context.temp_allocator) } diff --git a/ui.odin b/ui.odin new file mode 100644 index 0000000..06a75b7 --- /dev/null +++ b/ui.odin @@ -0,0 +1,121 @@ +package main + +import "core:math/linalg" +import "core:fmt" +import "core:mem" + +UiElementType :: enum { + Input, + Button, + Container, +} + +UiElementData :: union { + UiElementData_Button, + UiElementData_Input, + UiElementData_Container, +} + +UiElementData_Button :: struct { + label: cstring, +} + +UiElementData_Container :: struct { + label: cstring, +} + +UiElementData_Input :: struct { + label: cstring, +} + +UiElement :: struct { + area: rect2, + type: UiElementType, + data: UiElementData, +} + +UiState :: struct { + origin, position_left, position: v2, + same_line: bool, + style: UiStyle, + elements: [dynamic]UiElement, + container: ^UiElement, + group_element_count: int, +} + +UiStyle :: struct { + margin, padding: f32, +} + +ui_button :: proc(state: ^UiState, size: v2, data: UiElementData_Button) { + area := _ui_advance_element(state, size) + append(&state.elements, UiElement{area = area, type = .Button, data = data}) +} + +ui_container_begin :: proc(state: ^UiState, data: UiElementData_Container) { + assert(state.container == nil) + append(&state.elements, UiElement{area = _ui_advance_container(state), type = .Container, data = data}) + state.container = &state.elements[len(state.elements) - 1] +} + +ui_container_end :: proc(state: ^UiState) { + assert(state.container != nil) + state.container = nil +} + +_ui_advance_element :: proc(state: ^UiState, size: v2) -> rect2 { + if state.group_element_count > 0 { + if state.same_line { + state.position.x += size.x + state.style.padding + state.same_line = false + } else { + state.position_left.y += size.y + state.style.padding + state.position = state.position_left + } + } else { + state.position += state.style.padding + } + area := rect2{**state.position, **size} + if state.container != nil { + element_bottom_left := rect_get_br(area) + container_bottom_left := rect_get_br(state.container.area) + new_br := linalg.max(element_bottom_left + state.style.padding, container_bottom_left) + state.container.area.width = new_br.x - state.container.area.x + state.container.area.height = new_br.y - state.container.area.y + } + state.group_element_count += 1 + return area +} + +_ui_advance_container :: proc(state: ^UiState) -> rect2 { + state.position += state.style.margin + return {**state.position, 0.0, 0.0} +} + +ui_input :: proc(state: ^UiState, size: v2, data: UiElementData_Input) { + area := _ui_advance_element(state, size) + append(&state.elements, UiElement{area = area, type = .Input, data = data}) +} + +ui_same_line :: proc(state: ^UiState) { + state.same_line = true +} + +ui_start :: proc(state: ^UiState) { + clear(&state.elements) + state.position_left = state.origin + state.position = state.position_left +} + +ui_end :: proc(state: UiState) -> []UiElement { + return state.elements[:] +} + +ui_init :: proc(state: ^UiState, style: UiStyle, origin: v2, allocator: mem.Allocator) { + state^ = { + origin = origin, + position = origin, + style = style, + elements = make(type_of(state.elements), 0, 512, allocator) + } +} diff --git a/utility.odin b/utility.odin new file mode 100644 index 0000000..825e7c3 --- /dev/null +++ b/utility.odin @@ -0,0 +1,115 @@ +package main + +import rl "libraries/raylib" +import "core:math/linalg" + +v1 :: [1]f32 +v2 :: [2]f32 +v3 :: [3]f32 +v4 :: [4]f32 +iv2 :: [2]i32 +iv3 :: [3]i32 +iv4 :: [4]i32 +mat2 :: matrix[2, 2]f32 +mat3 :: matrix[3, 3]f32 +mat4 :: matrix[4, 4]f32 +rect2 :: rl.Rectangle +irect2 :: struct {x, y, width, height: i32} +tri2 :: [3]v2 +Bezier :: [3]v2 + +/// ### GEOMETRY ### /// +rect_from :: proc { + rect2_from_v2, + irect2_from_iv2, +} + +@(private="file") rect2_from_v2 :: proc "contextless" (position, size: v2) -> rect2 { + return {position.x, position.y, size.x, size.y} +} + +@(private="file") irect2_from_iv2 :: proc "contextless" (position, size: iv2) -> irect2 { + return {position.x, position.y, size.x, size.y} +} + +rect_scale :: proc{ + rect2_scale, + irect2_scale, +} + +@(private="file") rect2_scale :: proc "contextless" (rect: rect2, scalar: f32) -> rect2 { + return {rect.x * scalar, rect.y * scalar, rect.width * scalar, rect.height * scalar} +} + +@(private="file") irect2_scale :: proc "contextless" (rect: irect2, scalar: i32) -> irect2 { + return {rect.x * scalar, rect.y * scalar, rect.width * scalar, rect.height * scalar} +} + +rect_grow :: proc { + rect2_grow, + rect2_grow_v2, + irect2_grow, + rect2_grow_iv2, +} + +@(private="file") rect2_grow :: proc "contextless" (rect: rect2, grow: f32) -> rect2 { + return {rect.x - grow, rect.y - grow, rect.width + grow * 2.0, rect.height + grow * 2.0} +} + +@(private="file") rect2_grow_v2 :: proc "contextless" (rect: rect2, grow: v2) -> rect2 { + return {rect.x - grow.x, rect.y - grow.y, rect.width + grow.x * 2.0, rect.height + grow.y * 2.0} +} + +@(private="file") irect2_grow :: proc "contextless" (rect: irect2, grow: i32) -> irect2 { + return {rect.x - grow, rect.y - grow, rect.width + grow * 2, rect.height + grow * 2} +} + +@(private="file") rect2_grow_iv2 :: proc "contextless" (rect: irect2, grow: iv2) -> irect2 { + return {rect.x - grow.x, rect.y - grow.y, rect.width + grow.x * 2.0, rect.height + grow.y * 2.0} +} + + +rect_from_area :: proc { + rect_from_area_v2, + rect_from_area_iv2, +} + +@(private="file") rect_from_area_v2 :: proc "contextless" (start, end: v2) -> rect2 { + tl := linalg.min(start, end) + return {**tl, **(linalg.max(start, end) - tl)} +} + +@(private="file") rect_from_area_iv2 :: proc "contextless" (start, end: iv2) -> irect2 { + tl := linalg.min(start, end) + return {**tl, **(linalg.max(start, end) - tl)} +} + +rect_from_corners :: proc { + rect2_from_corners, + irect2_from_corners, +} + +@(private="file") rect2_from_corners :: proc "contextless" (tl, tr, br, bl: v2) -> rect2 { + top_left := linalg.min(linalg.min(tl, tr, br), bl) + bottom_right := linalg.max(linalg.max(tl, tr, br), bl) + return {**top_left, **(bottom_right - top_left)} +} + +@(private="file") irect2_from_corners :: proc "contextless" (tl, tr, br, bl: iv2) -> irect2 { + top_left := linalg.min(linalg.min(tl, tr, br), bl) + bottom_right := linalg.max(linalg.max(tl, tr, br), bl) + return {**top_left, **(bottom_right - top_left)} +} + +rect_get_br :: proc { + rect2_get_br, + irect2_get_br, +} + +@(private="file") rect2_get_br :: proc(rect: rect2) -> v2 { + return {rect.x + rect.width, rect.y + rect.height} +} + +@(private="file") irect2_get_br :: proc(rect: irect2) -> iv2 { + return {rect.x + rect.width, rect.y + rect.height} +}