package main import rl "raylib" import back "back" import ui ".." import "core:fmt" WINDOW_WIDTH :: 1280 WINDOW_HEIGHT :: 720 WINDOW_CAPTION :: "Immediate mode GUI" WINDOW_FRAMERATE :: 60 INPUT_FLOAT_CHARACTERS :: 32 v2 :: [2]f32 V2_ONE :: v2{1.0, 1.0} V2_LEFT :: v2{-1.0, 0.0} V2_RIGHT :: v2{1.0, 0.0} V2_UP :: v2{0.0, -1.0} V2_DOWN :: v2{0.0, 1.0} UiContainerType :: enum { Normal, Matrix, } UiElementData_Container :: struct { type: UiContainerType, thickness: f32, } UiElementData_Slider :: struct { min, max: f32, value: ^f32, } UiElementData_FloatInput :: struct { value: ^f32, value_string: cstring, } main :: proc() { back.register_segfault_handler() _register_illegal_instruction_handler() rl.SetTraceLogLevel(.WARNING) rl.InitWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_CAPTION) defer rl.CloseWindow() rl.SetTargetFPS(WINDOW_FRAMERATE) ctx: ui.Context ui.init(&ctx, 4000) outer_container_config := ui.ElementConfiguration {margin = {4.0, 8.0}, padding = {12.0, 24.0}, color = ui.col(rl.RED), label = ""} outer_container_margin_value_string_x := cstring(make([^]u8, INPUT_FLOAT_CHARACTERS)) outer_container_margin_value_string_y := cstring(make([^]u8, INPUT_FLOAT_CHARACTERS)) inner_container_config := ui.ElementConfiguration {color = cast(ui.col)rl.GREEN, label = "", margin = {4.0, 4.0}} inner_container_margin_value_string_x := cstring(make([^]u8, INPUT_FLOAT_CHARACTERS)) inner_container_margin_value_string_y := cstring(make([^]u8, INPUT_FLOAT_CHARACTERS)) button_config := ui.ElementConfiguration {margin = {4.0, 4.0}} button_margin_value_string_x := cstring(make([^]u8, INPUT_FLOAT_CHARACTERS)) button_margin_value_string_y := cstring(make([^]u8, INPUT_FLOAT_CHARACTERS)) element_focus_id := ui.element_id_invalid() debug_element_focus_id := ui.element_id_invalid() for !rl.WindowShouldClose() { ui.start(&ctx, origin = {0, 0}, size = {WINDOW_WIDTH, WINDOW_HEIGHT}) for i in 0..<7 { ui.container_begin(dimensions = {ui.SIZE_AUTO, ui.Size_Percentage(0.1)}, config = inner_container_config) for j in 0..<10 { config := button_config config.label = fmt.ctprintf("%d", i * 10 + j) ui.element(.Button, dimensions = {ui.Size_Pixels(64.0), ui.Size_Percentage(1.0)}, config = config) ui.same_line() } ui.container_end() } container_data := new(UiElementData_Container, context.temp_allocator) container_data^ = {type = .Matrix, thickness = 2.0} ui.container_begin(dimensions = {ui.SIZE_AUTO, ui.Size_Percentage(0.2)}, config = inner_container_config, data = container_data) for j in 0..<10 { config := button_config config.label = fmt.ctprintf("%d", 70 + j) ui.element(.Button, dimensions = {ui.Size_Pixels(64.0), ui.Size_Percentage(0.5)}, config = config) ui.same_line() } ctx.same_line = false for j in 0..<10 { config := button_config config.label = fmt.ctprintf("%d", 80 + j) ui.element(.Button, dimensions = {ui.Size_Pixels(64.0), ui.Size_Percentage(0.5)}, config = config) ui.same_line() } ui.container_end() ui.container_begin(dimensions = {ui.Size_Percentage(1.0), ui.Size_PercentageRemainder(1.0)}, config = inner_container_config) for j in 0..<10 { config := button_config config.label = fmt.ctprintf("%d", 90 + j) ui.element(.Button, dimensions = {ui.Size_Percentage(0.1), ui.Size_Percentage(1.0)}, config = config) ui.same_line() } ui.container_end() elements := ui.end() rl.BeginDrawing() defer rl.EndDrawing() rl.ClearBackground(rl.BLACK) render_ui_elements(&ctx, &element_focus_id, elements) ui.start(&ctx, {0, 0}, {WINDOW_WIDTH, WINDOW_HEIGHT}) config := ui.ElementConfiguration{margin = V2_ONE * 4, padding = V2_ONE * 4, color = ui.col(rl.MAGENTA)} ui.container_begin({ui.Size_Percentage(0.3), ui.Size_Percentage(0.3)}, config) slider_config := config slider_config.label = "Outer container margin" ui_input_float_2d(1.0, 0.2, slider_config, &outer_container_config.margin, outer_container_margin_value_string_x, outer_container_margin_value_string_y) slider_config.label = "Inner container margin" ui_input_float_2d(1.0, 0.2, slider_config, &inner_container_config.margin, inner_container_margin_value_string_x, inner_container_margin_value_string_y) slider_config.label = "Button margin" ui_input_float_2d(1.0, 0.2, slider_config, &button_config.margin, button_margin_value_string_x, button_margin_value_string_y) ui.container_end() debug_elements := ui.end() if rl.IsKeyPressed(.TAB) { debug_element_focus_id = ui.focus_next_element(debug_element_focus_id, debug_elements, {.InputFloat}) } render_ui_elements(&ctx, &debug_element_focus_id, debug_elements) } } ui_slider_2d :: proc(width, height: ui.Size_Percentage, config: ui.ElementConfiguration, value: ^v2, min: f32, max: f32, allocator := context.temp_allocator) -> ^ui.Element { container := ui.container_begin(dimensions = {width, height}, config = config) dimensions := ui.Dimensions {ui.Size_Percentage(0.5), ui.Size_Percentage(1.0)} data: ^UiElementData_Slider data = new(UiElementData_Slider, allocator) data^ = {value = &value.x, min = min, max = max} ui.element(.Slider, dimensions, {margin = config.margin}, data) ui.same_line() data = new(UiElementData_Slider, allocator) data^ = {value = &value.y, min = min, max = max} ui.element(.Slider, dimensions, {margin = config.margin}, data) ui.container_end() return container } ui_input_float_2d :: proc(width, height: ui.Size_Percentage, config: ui.ElementConfiguration, value: ^v2, value_string_x, value_string_y: cstring, allocator := context.temp_allocator) -> ^ui.Element { container := ui.container_begin(dimensions = {width, height}, config = config) dimensions := ui.Dimensions {ui.Size_Percentage(0.5), ui.Size_Percentage(1.0)} data: ^UiElementData_FloatInput data = new(UiElementData_FloatInput, allocator) data^ = {value_string = value_string_x, value = &value.x} ui.element(.InputFloat, dimensions, {margin = config.margin}, data) ui.same_line() data = new(UiElementData_FloatInput, allocator) data^ = {value_string = value_string_y, value = &value.y} ui.element(.InputFloat, dimensions, {margin = config.margin}, data) ui.container_end() return container } render_ui_elements :: proc(ctx: ^ui.Context, focus_element_id: ^ui.ElementId, elements: []ui.Element) { for element in elements { area := rl.Rectangle(element.area) #partial switch element.type { case .Container: dim := rl.Color {0, 0, 0, 192} if element.data != nil { data := cast(^UiElementData_Container)element.data switch data.type { case .Normal: rl.DrawRectangleRounded(area, 0.1, 4.0, dim) rl.GuiGroupBox(area, element.label) case .Matrix: matrix_area := ui.rect_grow(ui.get_element_outer_area(element), -data.thickness * 2.0) tl, tr, br, bl := ui.rect_get_corners(matrix_area) col := rl.WHITE hook_length: f32 = 4.0 rl.DrawLineEx(tl, bl, data.thickness, col) rl.DrawLineEx(tl, tl + V2_RIGHT * hook_length, data.thickness, col) rl.DrawLineEx(bl, bl + V2_RIGHT * hook_length, data.thickness, col) rl.DrawLineEx(tr, br, data.thickness, col) rl.DrawLineEx(tr, tr + V2_LEFT * hook_length, data.thickness, col) rl.DrawLineEx(br, br + V2_LEFT * hook_length, data.thickness, col) } } else { rl.DrawRectangleRounded(area, 0.1, 4.0, dim) rl.GuiGroupBox(area, element.label) } case .Button: rl.GuiButton(area, element.label) case .Slider: data := cast(^UiElementData_Slider)element.data rl.GuiSlider(area, "", "", data.value, data.min, data.max) case .InputFloat: data := cast(^UiElementData_FloatInput)element.data if rl.GuiValueBoxFloat(area, element.label, data.value_string, data.value, element.id == focus_element_id^) == 1 { focus_element_id^ = element.id } } } }