diff --git a/field_pool.odin b/field_pool.odin new file mode 100644 index 0000000..78051ef --- /dev/null +++ b/field_pool.odin @@ -0,0 +1,44 @@ +package main + +import "libraries/fixed_pool" + +Field :: struct($T: typeid) { + value: f32, + input: FieldInput, +} + +FIELD_BYTE_COUNT :: 32 +FieldInput :: [FIELD_BYTE_COUNT]byte +FieldPool :: struct($T: typeid) { + values: fixed_pool.Pool(T), + inputs: fixed_pool.Pool(FieldInput), +} + +field_input_as_cstring :: proc(field_input: ^FieldInput) -> cstring { + return cstring(&field_input[0]) +} + +field_pool_get :: proc(pool: ^FieldPool($T), count: int) -> (values: []T, inputs: []FieldInput) { + 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) +} + +field_pool_free_range :: proc(pool: ^FieldPool($T), from: int, count: int) { + fixed_pool.free_range(&pool.values, from, count) + fixed_pool.free_range(&pool.inputs, from, count) +} + +field_pool_get_allocated_elems :: proc(pool: ^FieldPool($T)) -> #soa[]Field(T) { + values := fixed_pool.get_allocated_elems(pool.values) + fields := fixed_pool.get_allocated_elems(pool.inputs) + return soa_zip(value = values, input = fields) +} diff --git a/libraries/pool/pool.odin b/libraries/fixed_pool/fixed_pool.odin similarity index 88% rename from libraries/pool/pool.odin rename to libraries/fixed_pool/fixed_pool.odin index da8d5e0..3945781 100644 --- a/libraries/pool/pool.odin +++ b/libraries/fixed_pool/fixed_pool.odin @@ -14,6 +14,10 @@ elems_available :: proc(pool: ^Pool($T), capacity: int) -> bool { return pool.usage + count <= pool.capacity } +capacity :: proc(pool: Pool($T)) -> int { + return len(pool.pool) +} + get :: proc(pool: ^Pool($T), count: int) -> []T { range_to := pool.usage + count assert(range_to <= len(pool.pool), "pool.get count reaches outside preallocated size") @@ -40,3 +44,7 @@ free_range :: proc(pool: ^Pool($T), from, count: int) { assert(to < pool.usage, "Cannot free beyond usage") mem.copy(&pool.pool[from], &pool.pool[to], (pool.usage - to) * size_of(T)) } + +raw_data :: proc(pool: ^Pool) -> rawptr { + return raw_data(pool.pool) +} diff --git a/libraries/snths_ui/ui.odin b/libraries/snths_ui/ui.odin index 8866792..5ffad7f 100644 --- a/libraries/snths_ui/ui.odin +++ b/libraries/snths_ui/ui.odin @@ -11,14 +11,13 @@ rect_get_tr :: #force_inline proc(rect: rect2) -> v2 {return {rect.x + rect.widt rect_get_br :: #force_inline proc(rect: rect2) -> v2 {return {rect.x + rect.width, rect.y + rect.height}} rect_get_bl :: #force_inline proc(rect: rect2) -> v2 {return {rect.x, rect.y + rect.height}} rect_get_size :: #force_inline proc(rect: rect2) -> v2 {return {rect.width, rect.height}} -rect_grow :: proc(rect: rect2, amount: v2) -> rect2 { - return {rect.x - amount.x / 2.0, rect.y - amount.y / 2.0, rect.width + amount.x, rect.height + amount.y} -} +rect_grow :: proc(rect: rect2, amount: v2) -> rect2 {return {rect.x - amount.x / 2.0, rect.y - amount.y / 2.0, rect.width + amount.x, rect.height + amount.y}} ctx: ^Context Context :: struct { origin: v2, + element_focus_id: int, containers: [dynamic]int, container_last_element: [dynamic]int, elements: [dynamic]Element, @@ -29,6 +28,7 @@ CONTAINER_STACK_SIZE :: 16 Element :: struct { area: rect2, + id: int, using config: ElementConfiguration, data: ElementData, type: ElementType, @@ -42,12 +42,19 @@ ElementConfiguration :: struct { } ElementData :: struct { - using float_input_data: ElementData_FloatInput, + min, max: f32, + value: ^f32, + value_string: cstring, +} + +ElementData_Slider :: struct { + min, max: f32, + value: ^f32, } ElementData_FloatInput :: struct { - min, max: f32, - value: ^f32, + value: ^f32, + value_string: cstring, } ElementType :: enum { @@ -57,6 +64,8 @@ ElementType :: enum { InputFloat, } +@rodata element_type_input := bit_set[ElementType] {.InputFloat} + Size_Pixels :: distinct f32 Size_Percentage :: distinct f32 Size_PercentageRemainder :: distinct f32 @@ -104,6 +113,36 @@ same_line :: proc() { ctx.same_line = true } +focus_element :: proc(ctx: ^Context, element: Element) { + ctx.element_focus_id = element.id +} + +unfocus_element :: proc(ctx: ^Context) { + ctx.element_focus_id = -1 +} + +is_element_focused :: proc(ctx: ^Context, element: Element) -> bool { + return ctx.element_focus_id == element.id +} + +focus_next_element :: proc(ctx: ^Context, allowed_focus := element_type_input) { + if ctx.element_focus_id == -1 { + return + } + if ctx.element_focus_id + 1 >= len(ctx.elements) { + ctx.element_focus_id = -1 + return + } + ctx.element_focus_id += 1 + for ctx.element_focus_id < len(ctx.elements) { + if ctx.elements[ctx.element_focus_id].type in allowed_focus { + return + } + ctx.element_focus_id += 1 + } + ctx.element_focus_id = -1 +} + container_begin :: proc(dimensions: Dimensions, config: ElementConfiguration) -> ^Element { area := _resolve_element_area(dimensions, config.margin) element := _append_element({area = area, config = config, type = .Container, dimensions = dimensions}) @@ -158,14 +197,14 @@ button :: proc(dimensions: Dimensions, config: ElementConfiguration) -> ^Element dimensions = dimensions}) } -slider :: proc(dimensions: Dimensions, config: ElementConfiguration, data: ElementData_FloatInput) -> ^Element { +slider :: proc(dimensions: Dimensions, config: ElementConfiguration, data: ElementData_Slider) -> ^Element { assert_no_dimensions_percentage_in_auto_container(dimensions) area := _resolve_element_area(dimensions, config.margin) return _append_element({ area = area, config = config, type = .Slider, - data = {float_input_data = data}, + data = {max = data.max, min = data.min, value = data.value}, dimensions = dimensions}) } @@ -176,7 +215,7 @@ input_float :: proc(dimensions: Dimensions, config: ElementConfiguration, data: area = area, config = config, type = .InputFloat, - data = {float_input_data = data}, + data = {value = data.value, value_string = data.value_string}, dimensions = dimensions}) } @@ -190,8 +229,11 @@ get_element_inner_area :: proc(element: Element) -> rect2 { _append_element :: proc(element: Element) -> ^Element { append(&ctx.elements, element) - ctx.container_last_element[len(ctx.containers) - 1] = len(ctx.elements) - 1 - return &ctx.elements[len(ctx.elements) - 1] + last_element_index := len(ctx.elements) - 1 + last_element := &ctx.elements[last_element_index] + last_element.id = last_element_index + ctx.container_last_element[len(ctx.containers) - 1] = last_element_index + return last_element } _resolve_element_area :: proc(dimensions: Dimensions, margin: v2) -> rect2 { @@ -222,7 +264,7 @@ _resolve_element_outer_size :: proc(dimensions: Dimensions) -> v2 { _resolve_elemment_outer_size_axis :: proc(size: Size, axis: Axis) -> f32 { switch value in size { case Size_Pixels: - return cast(f32)value + return f32(value) case Size_Percentage: inner := get_element_inner_area(_container_current()^) container_size := inner.width if axis == .X else inner.height diff --git a/program.odin b/program.odin index b73cd17..057753b 100644 --- a/program.odin +++ b/program.odin @@ -1,45 +1,9 @@ package main -import fixed_pool "libraries/pool" import rl "libraries/raylib" import ui "libraries/snths_ui" - -Field :: struct($T: typeid) { - value: f32, - input: FieldInput, -} - -FIELD_BYTE_COUNT :: 8 -FieldInput :: [FIELD_BYTE_COUNT]byte -FieldPool :: struct($T: typeid) { - values: fixed_pool.Pool(T), - inputs: fixed_pool.Pool(FieldInput), -} - -field_pool_get :: proc(pool: ^FieldPool($T), count: int) -> (values: []T, inputs: []FieldInput) { - 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) -} - -field_pool_free_range :: proc(pool: ^FieldPool($T), from: int, count: int) { - fixed_pool.free_range(&pool.values, from, count) - fixed_pool.free_range(&pool.inputs, from, count) -} - -field_pool_get_allocated_elems :: proc(pool: ^FieldPool($T)) -> #soa[]Field(T) { - values := fixed_pool.get_allocated_elems(pool.values) - fields := fixed_pool.get_allocated_elems(pool.inputs) - return soa_zip(value = values, input = fields) -} +import "libraries/fixed_pool" +import "core:fmt" FontStyle :: struct { font: rl.Font, @@ -69,6 +33,7 @@ program: Program Program :: struct { ui_context: ui.Context, matrix_field_pool: FieldPool(f32), + matrix_count: int, grid: Grid, font_style: FontStyle, state: ProgramState, @@ -108,7 +73,7 @@ program_build_ui :: proc() -> []ui.Element { } ui.start(&program.ui_context, {0, 0}, {WINDOW_WIDTH, WINDOW_HEIGHT}) - top_bar_container := ui.container_begin({ui.Size_Percentage(1.0), ui.Size_Pixels(64.0)}, container_config) + top_bar_container := ui.container_begin({ui.Size_Percentage(1.0), ui.Size_Pixels(48.0)}, container_config) top_bar_inner_height := ui.get_element_inner_area(top_bar_container^).height top_bar_button_dimensions := ui.Dimensions {ui.Size_Pixels(top_bar_inner_height), ui.Size_Pixels(top_bar_inner_height)} top_bar_button_config := ui.ElementConfiguration {margin = V2_ONE * 4} @@ -122,43 +87,29 @@ program_build_ui :: proc() -> []ui.Element { } ui.container_end() - ui.container_begin({ui.SIZE_AUTO, ui.Size_PercentageRemainder(1.0)}, container_config) - { - mat: matrix[3, 3]f32 - container_config := ui.ElementConfiguration{margin = V2_ONE * 4.0, label = ""} + ui.container_begin({ui.Size_Percentage(0.2), ui.Size_PercentageRemainder(1.0)}, container_config) + for i in 0..