Prepping to add matrices part 2

This commit is contained in:
Synthasmagoria 2026-08-02 13:06:41 +02:00
commit 9154823436
2 changed files with 103 additions and 80 deletions

View file

@ -16,6 +16,8 @@ rect_grow :: #force_inline proc(rect: rect2, amount: v2) -> rect2 {return {rect.
ctx: ^Context ctx: ^Context
MeasureTextProc :: proc(text: cstring, data: rawptr) -> v2
Context :: struct { Context :: struct {
origin: v2, origin: v2,
container_count: u16, container_count: u16,
@ -23,6 +25,8 @@ Context :: struct {
container_last_element: [dynamic]int, container_last_element: [dynamic]int,
elements: [dynamic]Element, elements: [dynamic]Element,
same_line: bool, same_line: bool,
measure_text_callback: MeasureTextProc,
measure_text_callback_data: rawptr,
} }
CONTAINER_STACK_SIZE :: 16 CONTAINER_STACK_SIZE :: 16
@ -57,6 +61,7 @@ ElementType :: enum {
Slider, Slider,
InputFloat, InputFloat,
Dropdown, Dropdown,
Label,
Custom0, Custom0,
Custom1, Custom1,
Custom2, Custom2,
@ -89,10 +94,12 @@ Dimensions :: struct {
Axis :: enum {X, Y} Axis :: enum {X, Y}
init :: proc(state: ^Context, element_capacity: int, allocator := context.allocator) { init :: proc(ctx: ^Context, element_capacity: int, measure_text_callback: MeasureTextProc, measure_text_callback_data: rawptr, allocator := context.allocator) {
state.containers = make(type_of(state.containers), 0, CONTAINER_STACK_SIZE, allocator) ctx.containers = make(type_of(ctx.containers), 0, CONTAINER_STACK_SIZE, allocator)
state.container_last_element = make(type_of(state.container_last_element), 0, CONTAINER_STACK_SIZE, allocator) ctx.container_last_element = make(type_of(ctx.container_last_element), 0, CONTAINER_STACK_SIZE, allocator)
state.elements = make(type_of(state.elements), 0, element_capacity, 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) { 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 { 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 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 container_index := len(ctx.elements) - 1
append(&ctx.containers, container_index) append(&ctx.containers, container_index)
append(&ctx.container_last_element, -1) append(&ctx.container_last_element, -1)
return element return element_ref
} }
container_end :: proc() { container_end :: proc() {
@ -166,8 +174,7 @@ container_end :: proc() {
ctx.same_line = false ctx.same_line = false
} }
element :: proc(element_type: ElementType, dimensions: Dimensions, config: ElementConfiguration, data: rawptr = nil) -> ^Element { assert_auto_with_pixels :: proc(dimensions: Dimensions) {
assert(element_type != .Container)
container := _container_current() container := _container_current()
if _, is_auto := container.dimensions.width.(Size_Auto); is_auto { if _, is_auto := container.dimensions.width.(Size_Auto); is_auto {
_, is_pixels := dimensions.width.(Size_Pixels) _, 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) _, 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") 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) element :: proc(element_type: ElementType, dimensions: Dimensions, config: ElementConfiguration, data: rawptr = nil) -> ^Element {
return _append_element({area = area, config = config, type = element_type, dimensions = dimensions, data = data}) 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 { get_element_outer_area :: proc(element: Element) -> rect2 {
@ -199,10 +211,14 @@ _append_element :: proc(element: Element) -> ^Element {
return 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_position := _resolve_element_outer_position()
outer_size := _resolve_element_outer_size(dimensions) if !is_text {
return rect_grow(rect2{**outer_position, **outer_size}, -margin * 2.0) 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 { _resolve_element_outer_position :: proc() -> v2 {
@ -221,10 +237,10 @@ _resolve_element_outer_position :: proc() -> v2 {
} }
_resolve_element_outer_size :: proc(dimensions: Dimensions) -> 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 { switch value in size {
case Size_Pixels: case Size_Pixels:
return f32(value) return f32(value)

View file

@ -91,23 +91,16 @@ ProgramMessageType :: enum {
.ChangeMatrixTypeClearData = "Changing the matrix type will delete all current matrices.\nAre you sure you want to do this?", .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 program: Program
InputFloatField :: [32]byte InputFloatField :: [32]byte
MATRIX_POOL_SIZE :: 4096 MATRIX_POOL_SIZE :: 4096
input_float_field_as_cstring :: proc(field: ^InputFloatField) -> cstring {
return cstring(&field[0])
}
Program :: struct { Program :: struct {
ui_context: ui.Context, ui_context: ui.Context,
ui_element_focus: ui.ElementId, ui_element_focus: ui.ElementId,
ui_overlay_context: ui.Context,
ui_overlay_element_focus: ui.ElementId,
matrix_float_pool: []f32, matrix_float_pool: []f32,
matrix_value_string_pool: []InputFloatField, matrix_value_string_pool: []InputFloatField,
matrix_create_dropdown_value: MatrixTypes, matrix_create_dropdown_value: MatrixTypes,
@ -119,6 +112,7 @@ Program :: struct {
font_style: FontStyle, font_style: FontStyle,
state: ProgramState, state: ProgramState,
message_type: ProgramMessageType, message_type: ProgramMessageType,
dialog_type: ProgramDialogType,
} }
Grid :: struct { Grid :: struct {
@ -138,6 +132,16 @@ matrix_pool_get_mat3 :: proc() -> ^mat3 {
return mat 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_init :: proc(allocator := context.allocator) {
program.matrix_float_pool = make(type_of(program.matrix_float_pool), MATRIX_POOL_SIZE) 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) 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, 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() 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()
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 {}
} }
program_update :: proc() { program_update :: proc() {
@ -174,25 +172,7 @@ program_update :: proc() {
switch program.state { switch program.state {
case .Normal: case .Normal:
mouse := rl.GetMousePosition() program.mouse_state = program_handle_mouse_workspace(program.mouse_state, matrix_container)
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
}
}
if rl.IsKeyPressed(.TAB) { if rl.IsKeyPressed(.TAB) {
program.ui_element_focus = ui.focus_next_element(program.ui_element_focus, ui_elements, {.InputFloat}) program.ui_element_focus = ui.focus_next_element(program.ui_element_focus, ui_elements, {.InputFloat})
} }
@ -200,7 +180,6 @@ program_update :: proc() {
case .Dialog: case .Dialog:
} }
grid := program.grid grid := program.grid
grid_inner_position := rect_get_tl(grid.inner_area) grid_inner_position := rect_get_tl(grid.inner_area)
draw_grid(grid.area, {32.0, 32.0}, rl.GRAY) 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) program_render_ui(&program.ui_context, ui_elements)
} }
draw_grid :: proc(area: rect2, line_interval: v2, color: rl.Color) { program_handle_mouse_workspace :: proc(mouse_state: MouseState, matrix_container: ^ui.Element) -> MouseState {
br := rect_get_br(area) mouse := rl.GetMousePosition()
for x := area.x; x < br.x; x += line_interval.x { switch mouse_state {
rl.DrawLineV({x, area.y}, {x, br.y}, color) case .Hover:
} if rl.IsMouseButtonPressed(.LEFT) {
for y := area.y; y < br.y; y += line_interval.y { return .DraggingGrid
rl.DrawLineV({area.x, y}, {br.x, y}, color) } 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) { 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 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) { program_render_ui :: proc(ctx: ^ui.Context, elements: []ui.Element) {
scissor_container: ^ui.Element scissor_container: ^ui.Element
render_above: 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)
}
}