From 05d5193c01c02e11dad5d7fe3224e2fd9b001c6e Mon Sep 17 00:00:00 2001 From: San Jacobs Date: Fri, 3 Oct 2025 01:33:01 +0200 Subject: Rewritten and simplified down to a rectcut-style layout system --- src/main.odin | 218 ++++++++++++----------- src/tafl/tafl.odin | 508 ++++++++++++++++------------------------------------- 2 files changed, 262 insertions(+), 464 deletions(-) diff --git a/src/main.odin b/src/main.odin index 334ac08..47f7ff3 100644 --- a/src/main.odin +++ b/src/main.odin @@ -3,6 +3,8 @@ package main import "core:fmt" import t "tafl" +BUTTON_SIZE : [2]int : {120, 40} + main :: proc() { width, height : int = 1920, 1080 @@ -17,8 +19,8 @@ main :: proc() { { t.tafl( // ROOT tafl - sizing_width=t.FIXED(int(width)), - sizing_height=t.FIXED(int(height)), + width=width, + height=height, layout=.LEFT_TO_RIGHT, color=colors.background, padding={5,5,5,5}, @@ -26,20 +28,17 @@ main :: proc() { ) { // Left bar t.tafl(color=colors.panel_blackground, - sizing_height=t.GROW, - sizing_width=t.FIT, - layout=.TOP_TO_BOTTOM) - { - t.tafl(color={0,0,0,0}, - sizing_height=t.GROW, - sizing_width=t.GROW) - } - slider() + width=BUTTON_SIZE.x*3 + 8*4, + cut_from=.START, + layout=.TOP_TO_BOTTOM, + ) { t.tafl( + height=BUTTON_SIZE.y+8*2, padding={8,8,8,8}, child_gap=8, color={.0, .0, .0, 0.4}, + cut_from=.END, ) { if button("Yeet", "yeet").clicked { @@ -53,66 +52,64 @@ main :: proc() { } } } - + slider_h() } - { // Middle section - t.tafl(color=colors.panel_blackground, - sizing_width=t.GROW, - sizing_height=t.GROW, - position_horizontal=.MIDDLE, - position_vertical=.MIDDLE, - child_gap=5, - layout=.TOP_TO_BOTTOM,) - {// Red square - t.tafl(color={1,0,0,1}, - sizing_width=t.FIXED(300), - sizing_height=t.FIXED(300), - padding={1,1,1,1},) - { - t.tafl(color={0,0,0,0.5}, - sizing_width=t.GROW, - sizing_height=t.GROW) - } - } - {// Red square - t.tafl(color={1,0,0,1}, - sizing_width=t.FIXED(200), - sizing_height=t.FIXED(200), - padding={1,1,1,1},) - { - t.tafl(color={0,0,0,0.5}, - sizing_width=t.GROW, - sizing_height=t.GROW) - } - } - } { // Right bar t.tafl(color=colors.panel_blackground, - sizing_width=t.FIXED(300), - sizing_height=t.GROW, + width=300, child_gap=20, - layout=.TOP_TO_BOTTOM) + layout=.TOP_TO_BOTTOM, + cut_from=.END,) { {t.tafl(color={.5, .5, .5, 1}, - sizing_height=t.FIXED(50), - sizing_width=t.GROW, + height=50, )} {t.tafl(color={.5, .5, .5, 1}, - sizing_height=t.FIXED(50), - sizing_width=t.GROW, - )} - {t.tafl(color={.5, .5, .8, 1}, - sizing_height=t.GROW, - sizing_width=t.GROW, + height=50, )} {t.tafl(color={.5, .5, .5, 1}, - sizing_height=t.FIXED(50), - sizing_width=t.GROW, + height=50, + cut_from=.END, + )} + {t.tafl(color={.5, .5, .8, 1}, )} } } + + { // Middle section + big_square_size := 300 + small_square_size := 200 + total_size := big_square_size+small_square_size+5 + t.tafl(color=colors.panel_blackground, + layout=.TOP_TO_BOTTOM,) + { + t.tafl(height=total_size, + layout=.TOP_TO_BOTTOM, + align_children=.MIDDLE, + cut_from=.MIDDLE, + child_gap=5,) + {// Red square (BIG) + t.tafl(color={1,0,0,1}, + width=big_square_size, + height=big_square_size, + padding={1,1,1,1},) + { + t.tafl(color={0,0,0,0.5},) + } + } + {// Red square (Small) + t.tafl(color={1,0,0,1}, + width=small_square_size, + height=small_square_size, + padding={1,1,1,1},) + { + t.tafl(color={0,0,0,0.5},) + } + } + } + } } t.render() @@ -122,76 +119,89 @@ main :: proc() { button :: proc(text : string, id : string) -> t.Com { - com := t.tafl(sizing_width=t.FIXED(120), - sizing_height=t.FIXED(40), + com := t.tafl(width=BUTTON_SIZE.x, + height=BUTTON_SIZE.y, color=colors.button_outline, padding={2,2,2,2}, - flags=t.BUTTON, + flags={.HIT_CHECK}, + align_children=.MIDDLE, id=id) color : t.Color = {.1,.1,.1, 1} if com.hover do color = {.2,.2,.2, 1} if com.clicked || com.is_down do color = {.05,.05,.05, 1} - t.tafl(sizing_width=t.GROW, - sizing_height=t.GROW, - color=color, - position_horizontal=.MIDDLE, - position_vertical=.MIDDLE,) + t.tafl(color=color,) - t.tafl(text=text) + t.tafl(text=text, + cut_from=.MIDDLE, + width=0, + height=0,) return com } slider_root : int = 0 slider_delta : int = 0 slider_current : int = 0 -slider :: proc(id : string = "a_slider") -> t.Com { - com : t.Com - t.tafl(padding={2,2,2,2}, - sizing_width=t.GROW) - t.tafl(sizing_width=t.GROW, - sizing_height=t.FIXED(30), - color={.1,.1,.1,1}, - padding={5,5,5,5}, - layout=.LEFT_TO_RIGHT, - position_horizontal=.END, - position_vertical=.END) - - { - com = t.tafl(sizing_width=t.FIXED(80), - sizing_height=t.GROW, - color={.5,.5,.5,1}, +slider_grabbed : bool = false +slider_output : f32 = 0 +slider_h :: proc(id : string = "a_slider") { + t.tafl( + cut_from=.END, padding={2,2,2,2}, - flags={.DRAGGABLE, .CLICKABLE, .HOVERABLE}, - id=id) + height=35, + ) + outer_com := t.tafl( + layout=.LEFT_TO_RIGHT, + color={.1,.1,.1,1}, + padding={2,2,2,2} + ) + {// Space before + t.tafl( + width=slider_current, + cut_from=.START) + } + {// Scroll handle + com:=t.tafl( + width=90, + cut_from=.START, + color={.4,.4,.4,1}, + padding={2,2,2,2}, + flags={.HIT_CHECK}, + id=id, + ) - core_color : t.Color = {.2,.2,.2,1} - if com.hover { - core_color = {.3,.3,.3,1} - } if com.pressed_down { - slider_root = slider_root+slider_delta + slider_root = slider_current + slider_grabbed = true } - if com.dragging { - core_color = {.12,.12,.12,1} - fmt.println(com.drag_delta) - slider_delta = com.drag_delta.x - slider_current = slider_root+slider_delta - fmt.println(slider_current) + + if t.mouse_left_released { + slider_grabbed = false } - { - t.tafl(sizing_height=t.GROW, - sizing_width=t.GROW, - color=core_color) + + slider_max := outer_com.tafl.width - com.tafl.width - outer_com.tafl.padding.left - outer_com.tafl.padding.right + + if slider_grabbed { + slider_delta = t.mouse_position.x - t.mouse_left_press_position.x + slider_current = slider_root + slider_delta + slider_current = max(slider_current, 0) + slider_current = min(slider_current, slider_max) + slider_output = f32(slider_current)/f32(slider_max) + fmt.printfln("slider_output: {}", slider_output) } + + color : t.Color = {.2,.2,.2,1} + if com.hover do color={.3,.3,.3,1} + if com.is_down do color={.13,.13,.13,1} + t.tafl( + color=color, + ) } - - - { - t.tafl(sizing_width=t.FIXED(-slider_current), - sizing_height=t.GROW) + {// Area after + t.tafl( + width=slider_current, + cut_from=.START) } - return com } diff --git a/src/tafl/tafl.odin b/src/tafl/tafl.odin index 2b3993e..83fc670 100644 --- a/src/tafl/tafl.odin +++ b/src/tafl/tafl.odin @@ -33,11 +33,12 @@ drag_delta : [2]int mouse_left_pressed := false mouse_left_is_down := false mouse_left_released := false -mouse_press_position : [2]int = {-1, -1} +mouse_left_press_position : [2]int = {-1, -1} clicked_pressed_id : string clicked_release_id : string hovered_id : string +something_is_hovered := false DEFAULT_FONT : rl.Font FONT_SIZE : int : 24 @@ -51,14 +52,10 @@ clear_layout :: proc() { @(deferred_out=tafl_close) tafl :: proc( - /*width : int = 0, - height : int = 0, - x : int = 0, - y : int = 0,*/ - sizing_width : Sizing_Dimension = FIT, - sizing_height : Sizing_Dimension = FIT, - position_horizontal : Position = .START, - position_vertical : Position = .START, + width : int = -1, + height : int = -1, + align_children : Position = .MIDDLE, + cut_from : Position = .START, layout : Layout = .LEFT_TO_RIGHT, padding : Sides = {0,0,0,0}, child_gap : int = 0, @@ -69,70 +66,124 @@ tafl :: proc( ) -> Com { return tafl_open( - /*width , - height, - x, - y,*/ - sizing_width, - sizing_height, - position_horizontal, - position_vertical, - layout, - padding, - child_gap, - color, - text, - id, - flags,) + width = width, + height = height, + align_children = align_children, + cut_from = cut_from, + layout = layout, + padding = padding, + child_gap = child_gap, + color = color, + text = text, + id = id, + flags = flags, + ) } tafl_open :: proc( - /*width : int = 0, - height : int = 0, - x : int = 0, - y : int = 0,*/ - sizing_width : Sizing_Dimension = FIT, - sizing_height : Sizing_Dimension = FIT, - position_horizontal : Position = .START, - position_vertical : Position = .START, + width : int = -1, + height : int = -1, + align_children : Position = .MIDDLE, + cut_from : Position = .START, layout : Layout = .LEFT_TO_RIGHT, padding : Sides = {0,0,0,0}, child_gap : int = 0, color : Color = {0,0,0,0}, - text : = "", + text := "", id := "", flags : Feature_Flags = {}, ) -> Com { - sizing_height := sizing_height - sizing_width := sizing_width + width := width + height := height - parent_ptr : ^Tafl = nil + parent : ^Tafl = nil if tafl_stack_depth > 0 { - parent_ptr = &tafl_elements[tafl_stack[tafl_stack_depth-1]] + parent = &tafl_elements[tafl_stack[tafl_stack_depth-1]] + } + + if width == -1 { + width = parent.inner.width + } + if height == -1 { + height = parent.inner.height } if text != "" { measurement := rl.MeasureTextEx(DEFAULT_FONT, strings.clone_to_cstring(text, allocator=context.temp_allocator), f32(FONT_SIZE), 0) - sizing_width = {.FIXED, int(measurement.x), int(measurement.x)} - sizing_height = {.FIXED, FONT_SIZE, FONT_SIZE} + width = max(width, int(measurement.x)) + height = max(height, int(measurement.y)) + } + + x, y : int + if parent != nil { + switch parent.layout { + case .LEFT_TO_RIGHT: + switch cut_from { + case .START: + x = parent.inner.x + parent.inner.x += width + parent.child_gap + parent.inner.width -= width + parent.child_gap + case .MIDDLE: + x = parent.inner.x + parent.inner.width/2 - width/2 + case .END: + x = parent.inner.x + parent.inner.width - width + parent.inner.width -= width + parent.child_gap + } + switch parent.align_children { + case .START: + y = parent.inner.y + case .MIDDLE: + y = parent.inner.y + parent.inner.height/2 - height/2 + case .END: + y = parent.inner.y + parent.inner.height - height + } + case .TOP_TO_BOTTOM: + switch cut_from { + case .START: + y = parent.inner.y + parent.inner.y += height + parent.child_gap + parent.inner.height -= height + parent.child_gap + case .MIDDLE: + y = parent.inner.y + parent.inner.height/2 - height/2 + case .END: + y = parent.inner.y + parent.inner.height - height + parent.inner.height -= height + parent.child_gap + } + switch parent.align_children { + case .START: + x = parent.inner.x + case .MIDDLE: + x = parent.inner.x + parent.inner.width/2 - width/2 + case .END: + x = parent.inner.x + parent.inner.width - width + } + } + } + + inner : Box = { + x = x + padding.left, + y = y + padding.top, + width = width - (padding.left + padding.right), + height = height - (padding.top + padding.bottom), } tafl_elements[tafl_elements_count] = { - /*width = width, + width = width, height = height, x = x, - y = y,*/ - sizing = {sizing_width, sizing_height}, + y = y, + inner = inner, layout = layout, padding = padding, child_gap = child_gap, - positioning = {position_horizontal, position_vertical}, + align_children = align_children, + cut_from = cut_from, color = color, - parent = parent_ptr, + parent = parent, text = text, id = id, @@ -142,13 +193,6 @@ tafl_open :: proc( if id == "" do this_tafl.id = text - if this_tafl.sizing.width.type == .FIXED { - this_tafl.width = this_tafl.sizing.width.max - } - if this_tafl.sizing.height.type == .FIXED { - this_tafl.height = this_tafl.sizing.height.max - } - tafl_stack[tafl_stack_depth] = tafl_elements_count this_tafl.own_index = tafl_elements_count this_tafl.own_depth = tafl_stack_depth @@ -164,9 +208,36 @@ tafl_open :: proc( output_com : Com - output_com.__tafl_index = this_tafl.own_index + output_com.tafl = this_tafl - if .CLICKABLE in flags { + if .HIT_CHECK in flags { + + output_com.hover = collide(mouse_position, this_tafl^) + + // Click + + if output_com.hover { + + hover_changed : bool = hovered_id != this_tafl.id + if hover_changed do fmt.printfln("Hovering over: {}", this_tafl.id) + + something_is_hovered = true + if hovered_id != "" do delete(hovered_id) + hovered_id = strings.clone(this_tafl.id) + + if mouse_left_pressed { + if clicked_pressed_id != "" do delete(clicked_pressed_id) + clicked_pressed_id = strings.clone(this_tafl.id) + fmt.printfln("Pressed! {}", this_tafl.id) + } + if mouse_left_released { + if clicked_release_id != "" do delete(clicked_release_id) + clicked_release_id = strings.clone(this_tafl.id) + fmt.printfln("Released! {}", this_tafl.id) + } + } + + if mouse_left_released { if clicked_pressed_id != "" && clicked_release_id != "" { if clicked_pressed_id == this_tafl.id && clicked_release_id == this_tafl.id { @@ -180,69 +251,20 @@ tafl_open :: proc( } output_com.pressed_down = mouse_left_pressed && clicked_pressed_id != "" && clicked_pressed_id == this_tafl.id output_com.press_released = mouse_left_released && clicked_release_id != "" && clicked_release_id == this_tafl.id - } - if .HOVERABLE in flags { - if hovered_id == this_tafl.id { - output_com.hover = true - if mouse_left_is_down && clicked_pressed_id == this_tafl.id { - output_com.is_down = true - } - } - } - if .DRAGGABLE in flags { - if dragging_id != "" { - if dragging_id == this_tafl.id { - output_com.dragging = true - if mouse_left_is_down && dragging_id == this_tafl.id { - output_com.drag_root = drag_root - output_com.drag_delta = drag_delta - } - } + + if mouse_left_is_down && clicked_pressed_id == this_tafl.id { + output_com.is_down = true } } - return output_com } tafl_close :: proc(com : Com) { - tafl : ^Tafl = &tafl_elements[com.__tafl_index] - - total_child_gap := max(tafl.children.len - 1, 0) * tafl.child_gap - switch tafl.layout { - case .LEFT_TO_RIGHT: - if tafl.sizing.width.type == .FIT { - tafl.width += total_child_gap - } - case .TOP_TO_BOTTOM: - if tafl.sizing.height.type == .FIT { - tafl.height += total_child_gap - } - } + tafl : ^Tafl = com.tafl parent : ^Tafl = tafl.parent - if parent != nil { - tafl.width += tafl.padding.left + tafl.padding.right - tafl.height += tafl.padding.top + tafl.padding.bottom - - // Not entirely sure yet, because Nic's video doesn't say you need this - // max(), but I have a feeling that childless tafls will cause negative - // total_child_gaps, which would be bad. - // - // ALSO: Wtf? Why are we basing this off of the tafls number of siblings? - // I do not understand this. - - switch parent.layout { - case .LEFT_TO_RIGHT: - parent.width = min(parent.width + tafl.width, parent.sizing.width.max) - parent.height = min(max(tafl.height, parent.height), parent.sizing.height.max) - case .TOP_TO_BOTTOM: - parent.height = min(parent.height + tafl.height, parent.sizing.height.max) - parent.width = min(max(tafl.width, parent.width), parent.sizing.width.max) - } - } - for i in 0.. 0 { parent_scissor := scissor_stack[tafl.own_depth-1] - if parent_scissor.size.x <= 0 || parent_scissor.size.y <= 0 { + if parent_scissor.width <= 0 || parent_scissor.height <= 0 { return } - own_scissor : Box = {position={tafl.x, tafl.y}, size={tafl.width, tafl.height}} + own_scissor : Box = tafl.box current_scissor = box_clamp(parent_scissor, own_scissor) } else { - current_scissor = {position={tafl.x, tafl.y}, size={tafl.width, tafl.height}} + current_scissor = tafl } scissor_stack[tafl.own_depth] = current_scissor - rl.BeginScissorMode(c.int(current_scissor.position.x), c.int(current_scissor.position.y), c.int(current_scissor.size.x), c.int(current_scissor.size.y),) + rl.BeginScissorMode(c.int(current_scissor.x), c.int(current_scissor.y), c.int(current_scissor.width), c.int(current_scissor.height),) /* indent(tafl.own_depth) fmt.printfln("%02d Drawing %02d\t\t{}", tafl.own_depth, tafl.own_index, scissor_stack[tafl.own_depth]) @@ -478,24 +364,6 @@ recursive_draw :: proc(index : int) { } } -grow_pass :: proc() { - #reverse for tafl_index in child_index_buffer[:child_index_buffer_len] { - tafl := &tafl_elements[tafl_index] - grow_children_width(tafl) - } - #reverse for tafl_index in child_index_buffer[:child_index_buffer_len] { - tafl := &tafl_elements[tafl_index] - grow_children_height(tafl) - } -} - -position_pass :: proc() { - #reverse for tafl_index in child_index_buffer[:child_index_buffer_len] { - tafl := &tafl_elements[tafl_index] - position_children(tafl) - } -} - start_window :: proc(width, height : int, title : cstring, fps_target : int = 60) { rl.SetWindowMinSize(800,600) rl.SetTargetFPS(60) @@ -518,6 +386,7 @@ get_window_size :: proc() -> (int, int) { process_features :: proc() { + // When the mouse has both pressed and released, but the two don't match, clear the IDs. if clicked_pressed_id != "" && clicked_release_id != "" && clicked_pressed_id != clicked_release_id { delete(clicked_pressed_id) delete(clicked_release_id) @@ -525,55 +394,6 @@ process_features :: proc() { clicked_release_id = "" } - something_is_hovered := false - - for tafl in tafl_elements { - if .CLICKABLE in tafl.flags { - if mouse_left_pressed { - if collide(mouse_position, tafl) { - if clicked_pressed_id != "" do delete(clicked_pressed_id) - clicked_pressed_id = strings.clone(tafl.id) - fmt.printfln("Pressed! {}", tafl.id) - } - } - if mouse_left_released { - if collide(mouse_position, tafl) { - if clicked_release_id != "" do delete(clicked_release_id) - clicked_release_id = strings.clone(tafl.id) - fmt.printfln("Released! {}", tafl.id) - } - } - } - - if .HOVERABLE in tafl.flags { - if collide(mouse_position, tafl) { - something_is_hovered = true - if hovered_id != tafl.id { - if hovered_id != "" do delete(hovered_id) - hovered_id = strings.clone(tafl.id) - fmt.printfln("Hovering over: {}", tafl.id) - } - - } - } - - if .DRAGGABLE in tafl.flags { - if mouse_left_pressed { - if collide(mouse_position, tafl) { - drag_root = mouse_position - dragging_id = strings.clone(tafl.id) - } - } - if mouse_left_is_down && dragging_id==tafl.id { - drag_delta = mouse_position-drag_root - } - if mouse_left_released && dragging_id==tafl.id { - delete(dragging_id) - dragging_id = "" - drag_delta = mouse_position-drag_root - } - } - } if !something_is_hovered { if hovered_id != "" { @@ -582,15 +402,8 @@ process_features :: proc() { fmt.printfln("Hovering over: {}", "nil") } } -} - -Sizing_Dimension :: struct { - type : enum{ - FIT, - GROW, - FIXED - }, - min, max : int + + something_is_hovered = false } Layout :: enum { @@ -612,8 +425,7 @@ Color :: struct { } Tafl :: struct { - width, height : int, - x, y : int, + using box : Box, own_index : int, // Don't ship this own_depth : int, // Maybe don't ship this @@ -623,14 +435,9 @@ Tafl :: struct { index, len : int }, - sizing : struct { - width : Sizing_Dimension, - height : Sizing_Dimension, - }, - positioning : struct { - horizontal : Position, - vertical : Position, - }, + inner : Box, + align_children : Position, + cut_from : Position, layout : Layout, padding : Sides, child_gap : int, @@ -650,14 +457,12 @@ Tafl_Cache :: struct{ } Feature_Flag :: enum { - CLICKABLE, - HOVERABLE, - DRAGGABLE, + HIT_CHECK, } Feature_Flags :: bit_set[Feature_Flag] Com :: struct { - __tafl_index : int, + tafl : ^Tafl, clicked : bool, press_released : bool, pressed_down : bool, @@ -673,14 +478,13 @@ Com :: struct { } Box :: struct { - position : [2]int, - size : [2]int, + x, y, width, height : int, } box_clamp :: proc(parent, child : Box) -> (output : Box) { - output.position.x = clamp(child.position.x, parent.position.x, parent.position.x+parent.size.x) - output.position.y = clamp(child.position.y, parent.position.y, parent.position.y+parent.size.y) - output.size.x = clamp(child.size.x, 0, parent.position.x+parent.size.x-output.position.x) - output.size.y = clamp(child.size.y, 0, parent.position.y+parent.size.y-output.position.y) + output.x = clamp(child.x, parent.x, parent.x+parent.width) + output.y = clamp(child.y, parent.y, parent.y+parent.height) + output.width = clamp(child.width, 0, parent.x+parent.width-output.x) + output.height = clamp(child.height, 0, parent.y+parent.height-output.y) return } @@ -712,22 +516,6 @@ indent :: proc(x : int) { } } -GROW : Sizing_Dimension : {.GROW, 0, max(type_of(Sizing_Dimension{}.max))} - -FIT : Sizing_Dimension : {.FIT, 0, max(type_of(Sizing_Dimension{}.max))} - -FIXED :: proc(x: int) -> Sizing_Dimension { - return {.FIXED, x, x} -} -/*FRACTIONAL :: proc(x, y: f64) -> Sizing_Dimension { - return {.FRACTIONAL, x, x} -}*/ - -BUTTON : Feature_Flags : { - .CLICKABLE, - .HOVERABLE -} - u8_clamp :: proc(input: f32) -> u8 { mult := input*255 output := u8(mult) -- cgit v1.2.1