diff --git a/src/root.zig b/src/root.zig index 2c0338a..ad1c663 100644 --- a/src/root.zig +++ b/src/root.zig @@ -7,7 +7,16 @@ const std = @import("std"); // texture, // sissor -pub const VERSON = "0.0.1"; +pub const VERSION_MAJOR = 0; +pub const VERSION_MINOR = 1; +pub const VERSION_PATCH = 0; + +pub const VERSION = std.fmt.comptimePrint("{}.{}.{}", .{ + VERSION_MAJOR, + VERSION_MINOR, + VERSION_PATCH, +}); + pub const Real = f32; const Spaceing = 4; @@ -86,6 +95,36 @@ pub const Padding = struct { bottom: Real = 0, left: Real = 0, right: Real = 0, + + pub fn all(padding: Real) Padding { + return .{ + .top = padding, + .bottom = padding, + .left = padding, + .right = padding, + }; + } + + pub fn topf(padding: Real) Padding { + return .{ + .top = padding, + }; + } + pub fn bottomf(padding: Real) Padding { + return .{ + .bottom = padding, + }; + } + pub fn leftf(padding: Real) Padding { + return .{ + .left = padding, + }; + } + pub fn rightf(padding: Real) Padding { + return .{ + .right = padding, + }; + } }; pub const Layout = enum { @@ -93,6 +132,11 @@ pub const Layout = enum { top_to_bottom, }; +fn printWithLevel(writer: *std.Io.Writer, level: usize, comptime fmt: []const u8, args: anytype) !void { + for (0..level * Spaceing) |_| try writer.writeByte(' '); + try writer.print(fmt, args); +} + pub const Style = struct { size_x: Sizing = .fit, size_y: Sizing = .fit, @@ -105,26 +149,23 @@ pub const Style = struct { child_gap: Real = 0, layout: Layout = .top_to_bottom, - // not using formatting so i can print it nicely - pub fn printStyle(style: Style, level: usize) void { - for (0..level * Spaceing) |_| std.debug.print(" ", .{}); - std.debug.print("style : .{{\n", .{}); - for (0..(level + 1) * Spaceing) |_| std.debug.print(" ", .{}); - std.debug.print("size_x : {any},\n", .{style.size_x}); - for (0..(level + 1) * Spaceing) |_| std.debug.print(" ", .{}); - std.debug.print("size_y : {any},\n", .{style.size_y}); - for (0..(level + 1) * Spaceing) |_| std.debug.print(" ", .{}); - std.debug.print("background_colour : {any},\n", .{style.background_colour}); - for (0..(level + 1) * Spaceing) |_| std.debug.print(" ", .{}); - std.debug.print("rounded : {any},\n", .{style.rounded}); - for (0..(level + 1) * Spaceing) |_| std.debug.print(" ", .{}); - std.debug.print("padding : {any},\n", .{style.padding}); - for (0..(level + 1) * Spaceing) |_| std.debug.print(" ", .{}); - std.debug.print("child_gap : {any},\n", .{style.child_gap}); - for (0..(level + 1) * Spaceing) |_| std.debug.print(" ", .{}); - std.debug.print("layout : {any},\n", .{style.layout}); - for (0..(level) * Spaceing) |_| std.debug.print(" ", .{}); - std.debug.print("}},\n", .{}); + pub fn format( + self: @This(), + writer: *std.Io.Writer, + ) std.Io.Writer.Error!void { + try printWithLevel(writer, 0, "style : .{{\n", .{}); + try self.printStyle(writer, 0); + } + + pub fn printStyle(style: Style, writer: *std.Io.Writer, level: usize) std.Io.Writer.Error!void { + try printWithLevel(writer, level + 1, "size_x : {any},\n", .{style.size_x}); + try printWithLevel(writer, level + 1, "size_y : {any},\n", .{style.size_y}); + try printWithLevel(writer, level + 1, "background_colour : {any},\n", .{style.background_colour}); + try printWithLevel(writer, level + 1, "rounded : {any},\n", .{style.rounded}); + try printWithLevel(writer, level + 1, "padding : {any},\n", .{style.padding}); + try printWithLevel(writer, level + 1, "child_gap : {any},\n", .{style.child_gap}); + try printWithLevel(writer, level + 1, "layout : {any},\n", .{style.layout}); + try printWithLevel(writer, level, "}},\n", .{}); } }; @@ -135,18 +176,34 @@ pub const Colour = struct { a: u8 = 255, }; -pub const State = enum { - None, - hovered, - clicked, +pub const MouseState = struct { + pos: Pos, + left: bool, + right: bool, + middle: bool, }; -pub fn Bamboo( +pub fn pointinRect(point: Pos, rect_pos: Pos, rect: Rect) bool { + if (point.x > rect_pos.x and + point.x < rect_pos.x + rect.w and + point.y > rect_pos.y and + point.y < rect_pos.y + rect.h) return true; + return false; +} + +// might add an easy conversion +// comptime mouse_type: type, +// comptime getMouseState: fn (mouse_type) MouseState, + +// comptime getKeyboardInput: fn(keyboard_type) []const u8, + +// comptime renderTextFunc: fn (text_type, Pos) void, +// comptime renderRectFunc: fn (Rect, Pos, Colour, rounding: ?Real) void, +// comptime renderTextureFunc: fn (texture_type, Pos) void, + +pub fn Shoots( comptime text_type: type, comptime texture_type: type, - // comptime renderTextFunc: fn (text_type, Pos) void, - // comptime renderRectFunc: fn (Rect, Pos, Colour, rounding: ?Real) void, - // comptime renderTextureFunc: fn (texture_type, Pos) void, ) type { return struct { const Self = @This(); @@ -163,6 +220,41 @@ pub fn Bamboo( element: Ele, text: Text, texture: Texture, + + fn printTreeHelper(writer: *std.Io.Writer, node: Node, level: usize) !void { + switch (node) { + .element => { + try printWithLevel(writer, level, "{s} : Ele{{\n", .{node.element.name}); + try printWithLevel(writer, level + 1, "pos : {any},\n", .{node.element.pos}); + try printWithLevel(writer, level + 1, "on_click : {?},\n", .{node.element.on_click}); + try printWithLevel(writer, level + 1, "rect : {any},\n", .{node.element.rect}); + try printWithLevel(writer, level + 1, "style : .{{\n", .{}); + try node.element.style.printStyle(writer, level + 1); + try printWithLevel(writer, level + 1, "children : {{\n", .{}); + for (node.element.children, 0..) |_, i| { + try printTreeHelper(writer, node.element.children[i], (level + 2)); + } + try printWithLevel(writer, level + 1, "}},\n", .{}); + try printWithLevel(writer, level, "}},\n", .{}); + }, + + .text => { + try printWithLevel(writer, level, "text : {s}.\n", .{node.text.string}); + return; + }, + .texture => { + try printWithLevel(writer, level, "texture : {any},\n", .{node.texture}); + return; + }, + } + } + + pub fn format( + self: Node, + writer: *std.Io.Writer, + ) std.Io.Writer.Error!void { + try printTreeHelper(writer, self, 0); + } }; pub const Ele = struct { @@ -172,7 +264,20 @@ pub fn Bamboo( pos: Pos = .{}, rect: Rect = .{}, style: Style = .{}, + children: []const Node = &[_]Node{}, + + on_hover: ?Interact = null, + on_click: ?Interact = null, + // the on hover call back will be ran + allow_on_hover_when_occluded: bool = true, + allow_on_click_when_occluded: bool = false, + }; + + pub const Interact = struct { + // returns true if the ui must be drawn again + func: *const fn (self: *Ele, mouse_state: MouseState, *anyopaque) bool, + data: *anyopaque, }; const RenderCommandType = enum { @@ -180,6 +285,7 @@ pub fn Bamboo( text, texture, }; + pub const RenderCommand = union(RenderCommandType) { rect: struct { rect: Rect, @@ -200,23 +306,34 @@ pub fn Bamboo( }, }; + // focused: *Node, + // /// highly suggested that you use an area for the alloc - // pub fn init(alloc: std.mem.Allocator) Self { + // pub fn init() Self { // return Self{ // .alloc = alloc, // }; // } - pub inline fn ElementWborder(boarder_width: Padding, boarder_colour: Colour, ele: Ele) Node { + pub inline fn ElementWborder( + boarder_width: Padding, + boarder_colour: Colour, + ele: Ele, + ) Node { + var internal = ele; + internal.rect.h -= boarder_width.bottom + boarder_width.top; + internal.rect.w -= boarder_width.right + boarder_width.left; + return Node{ .element = .{ .name = ele.name ++ "_boarder", .style = .{ .padding = boarder_width, .background_colour = boarder_colour, + .rounded = ele.style.rounded, }, .children = &[_]Node{ - Element(ele), + Element(internal), }, }, }; @@ -271,9 +388,9 @@ pub fn Bamboo( }, .text => { parent.element.rect.h += node.text.getTextHeight(); - std.debug.print("parent.element.rect.h : {}\n", .{parent.element.rect.h}); + // std.debug.print("parent.element.rect.h : {}\n", .{parent.element.rect.h}); parent.element.rect.w += node.text.getTextWidth(); - std.debug.print("parent.element.rect.w : {}\n", .{parent.element.rect.w}); + // std.debug.print("parent.element.rect.w : {}\n", .{parent.element.rect.w}); }, .texture => return, } @@ -320,39 +437,6 @@ pub fn Bamboo( return new_node; } - fn printTreeHelper(node: Node, level: usize) void { - switch (node) { - .element => { - for (0..level * 4) |_| std.debug.print(" ", .{}); - std.debug.print("{s} : Ele{{\n", .{node.element.name}); - for (0..(level + 1) * 4) |_| std.debug.print(" ", .{}); - std.debug.print("pos : {any},\n", .{node.element.pos}); - for (0..(level + 1) * 4) |_| std.debug.print(" ", .{}); - std.debug.print("rect : {any},\n", .{node.element.rect}); - node.element.style.printStyle(level + 1); - for (0..(level + 1) * 4) |_| std.debug.print(" ", .{}); - std.debug.print("children : {{\n", .{}); - for (node.element.children, 0..) |_, i| { - printTreeHelper(node.element.children[i], (level + 2)); - } - for (0..(level + 1) * 4) |_| std.debug.print(" ", .{}); - std.debug.print("}},\n", .{}); - for (0..(level) * 4) |_| std.debug.print(" ", .{}); - std.debug.print("}},\n", .{}); - }, - .text => { - for (0..level * 4) |_| std.debug.print(" ", .{}); - std.debug.print("text : {s}.\n", .{node.text.string}); - return; - }, - .texture => return, - } - } - - pub fn printTree(node: Node) void { - printTreeHelper(node, 0); - } - pub fn getRenderCommandsHelper(node: Node, alloc: std.mem.Allocator, command_list: *std.ArrayList(RenderCommand)) !void { switch (node) { .element => { @@ -440,11 +524,11 @@ pub fn Bamboo( fn computeRemainingHeightAndWidth(node: Node) struct { Real, Real } { // growable code - std.debug.print("node.element.rect.w : {d}\n", .{node.element.rect.w}); + // std.debug.print("node.element.rect.w : {d}\n", .{node.element.rect.w}); var remaining_width = node.element.rect.w; var remaining_height = node.element.rect.h; - std.debug.print("remaining_width : {d}\n", .{remaining_width}); - std.debug.print("remaining_hieght : {d}\n", .{remaining_height}); + // std.debug.print("remaining_width : {d}\n", .{remaining_width}); + // std.debug.print("remaining_hieght : {d}\n", .{remaining_height}); remaining_width -= node.element.style.padding.left + node.element.style.padding.right; remaining_height -= node.element.style.padding.top + node.element.style.padding.bottom; @@ -454,18 +538,18 @@ pub fn Bamboo( 0, }; - std.debug.print("remaining_width : {d}\n", .{remaining_width}); - std.debug.print("remaining_hieght : {d}\n", .{remaining_height}); + // std.debug.print("remaining_width : {d}\n", .{remaining_width}); + // std.debug.print("remaining_hieght : {d}\n", .{remaining_height}); // are there too many indentaions here maybe for (node.element.children) |child| { - std.debug.print("child : {}\n", .{child}); + // std.debug.print("child : {}\n", .{child}); switch (child) { .element => { remaining_height -= child.element.rect.h; remaining_width -= child.element.rect.w; - std.debug.print("remaining_width : {d}\n", .{remaining_width}); - std.debug.print("remaining_hieght : {d}\n", .{remaining_height}); + // std.debug.print("remaining_width : {d}\n", .{remaining_width}); + // std.debug.print("remaining_hieght : {d}\n", .{remaining_height}); }, .texture => { switch (node.element.style.layout) { @@ -482,12 +566,12 @@ pub fn Bamboo( else => {}, } } - std.debug.print("remaining_width : {d}\n", .{remaining_width}); - std.debug.print("remaining_hieght : {d}\n", .{remaining_height}); + // std.debug.print("remaining_width : {d}\n", .{remaining_width}); + // std.debug.print("remaining_hieght : {d}\n", .{remaining_height}); remaining_width -= (@as(Real, @floatFromInt(node.element.children.len -| 1)) * node.element.style.child_gap); remaining_height -= (@as(Real, @floatFromInt(node.element.children.len -| 1)) * node.element.style.child_gap); - std.debug.print("remaining_width : {d}\n", .{remaining_width}); - std.debug.print("remaining_hieght : {d}\n", .{remaining_height}); + // std.debug.print("remaining_width : {d}\n", .{remaining_width}); + // std.debug.print("remaining_hieght : {d}\n", .{remaining_height}); return .{ remaining_height, @@ -549,6 +633,7 @@ pub fn Bamboo( addToAxis: *const fn (*Ele, Real) void, getSizing: *const fn (*const Ele) Sizing, ) void { + // this algorithm is based on the one used in clay : https://github.com/nicbarker/clay var remaining = remaining_disance; while (remaining > 0) { @@ -574,13 +659,13 @@ pub fn Bamboo( width_to_add = @min(width_to_add, remaining / @as(Real, @floatFromInt(total_growable))); if (width_to_add == 0) return; var grow_em: usize = 0; - std.debug.print("smallest ; {}\n", .{smallest.rect}); + // std.debug.print("smallest ; {}\n", .{smallest.rect}); while (filterGrowableChildren(&grow_em, node)) |growable| { if (getAxis(growable) == getAxis(smallest)) { - std.debug.print("{s} before growable : {} + width_to_add {}\n", .{ growable.name, growable.rect, width_to_add }); + // std.debug.print("{s} before growable : {} + width_to_add {}\n", .{ growable.name, growable.rect, width_to_add }); if (getSizing(growable) == .grow) addToAxis(growable, width_to_add); - std.debug.print("after growable : {}\n", .{growable.rect}); + // std.debug.print("after growable : {}\n", .{growable.rect}); remaining -= width_to_add; } } @@ -596,7 +681,7 @@ pub fn Bamboo( if (total_growable == 0) return; const remaining_height, const remaining_width = computeRemainingHeightAndWidth(node.*); - std.debug.print("remaining_height remaining_width : {} {}\n", .{ remaining_height, remaining_width }); + // std.debug.print("remaining_height remaining_width : {} {}\n", .{ remaining_height, remaining_width }); if (node.element.style.layout == .top_to_bottom) { growChildIndependedElements(node.*, true); @@ -689,151 +774,93 @@ pub fn Bamboo( } } - pub fn computePostions(node: *Node) void { + pub fn computePositions(node: *Node) void { computePostionsHelper(node, 0); } + // this could be done real quick + fn filterHoveredNodes(node: Node, pos: Pos) ?*Node { + switch (node) { + .element => { + for (node.element.children, 0..) |value, i| { + switch (value) { + .element => |ele| { + if (pointinRect(pos, ele.pos, ele.rect)) { + return @constCast(&node.element.children[i]); + } + }, + else => continue, + } + } + }, + else => return null, + } + return null; + } + + pub fn processInteractions(root: *Node, mouse_state: MouseState) bool { + var needs_redraw = false; + needs_redraw = true; + var current_node = root; + const clicked = mouse_state.left or mouse_state.middle or mouse_state.right; + // std.debug.print("state : {}\n", .{mouse_state}); + while (filterHoveredNodes(current_node.*, mouse_state.pos)) |node| { + switch (node.*) { + .element => |e| { + // std.debug.print("clicked : {} hovered node name : {s}\n", .{ clicked, e.name }); + + if (e.on_hover != null and e.allow_on_hover_when_occluded) { + needs_redraw |= node.element.on_hover.?.func(&node.element, mouse_state, node.element.on_click.?.data); + } + + if (e.on_click != null and clicked and e.allow_on_click_when_occluded) { + needs_redraw |= node.element.on_click.?.func(&node.element, mouse_state, node.element.on_click.?.data); + } + }, + // .text => |t| std.debug.print("hovered node name : {s}\n", .{t.string}), + else => {}, + } + current_node = node; + } + + switch (current_node.*) { + .element => { + if (current_node.element.on_hover != null and !current_node.element.allow_on_hover_when_occluded) { + needs_redraw |= current_node.element.on_hover.?.func( + ¤t_node.element, + mouse_state, + current_node.element.on_click.?.data, + ); + } + + if (current_node.element.on_click != null and clicked and !current_node.element.allow_on_click_when_occluded) { + needs_redraw |= current_node.element.on_click.?.func( + ¤t_node.element, + mouse_state, + current_node.element.on_click.?.data, + ); + } + }, + else => {}, + } + + return needs_redraw; + } + pub fn resolveSizing(alloc: std.mem.Allocator, root: Node) !Node { - // var resolved_root: Node = undefined; - // var current_node: Node = root; - // var pass_queue: std.ArrayList([]const u8) = .empty; - - const node_1 = root; - var new_tree: Node = try deepClone(node_1, alloc); - - // printTree(node_1); - // printTree(new_tree); + var new_tree: Node = try deepClone(root, alloc); var base_node: Node = Node{ .element = Ele{ .children = &[_]Node{ - node_1, + root, }, } }; + computeSizes(&new_tree, &base_node); - - // printTree(new_tree); - computeGrowElements(&new_tree); - computePostions(&new_tree); - // printTree(new_tree); + computePositions(&new_tree); return new_tree; } - - // pub fn freeTree(alloc: std.mem.Allocator, root: Node) void {} - - // pub fn render(self: Self, root: Node) void { - // return null; - // } }; } - -fn callback(dat: anytype) void { - _ = dat; - return null; -} - -pub fn sideBar() UI.Node { - return UI.Element(.{ - .name = "sidebar", - }); -} - -pub fn button() UI.Node { - return UI.Element(.{ - .name = "button", - }); -} - -fn func(string: []const u8, font: void) Real { - _ = font; - return @as(Real, @intCast(string.len)); -} - -const UI = Bamboo(TextType(void, func, func), void); - -test "temp" { - const child = &[_]UI.Node{ - sideBar(), - button(), - UI.Node{ - .text = UI.Text.init( - "hello world", - void{}, - .{}, - ), - }, - UI.Element(.{ - .name = "child", - .children = &[_]UI.Node{ - UI.Element(.{ - .name = "branch1", - .children = &[_]UI.Node{ - UI.Element(.{ - .name = "branch11", - .rect = .{ - .h = 400, - .w = 400, - }, - }), - UI.Element(.{ - .name = "branch12", - .rect = .{ - .h = 400, - .w = 400, - }, - }), - }, - }), - UI.Element(.{ - .name = "branch2", - .children = &[_]UI.Node{ - UI.Element(.{ - .name = "branch21", - .rect = .{ - .h = 300, - .w = 300, - }, - }), - UI.Element(.{ - .name = "branch22", - .rect = .{ - .h = 300, - .w = 300, - }, - }), - }, - }), - }, - }), - button(), - }; - - const root = UI.Element(.{ - .name = "root", - .pos = Pos{ - .x = 0, - .y = 0, - }, - // .on_hover = callback(), - // .on_click = callback(), - .style = .{ - .background_colour = .{}, - .layout = .right_to_left, - // .size = .fit(), - // .size_x = .fit(), - // .size_y = .fixed(), - }, - .children = child, - }); - - // const arena = [1024]u8{0}; - - var al = std.heap.ArenaAllocator.init(std.heap.smp_allocator); - - const sized = try UI.resolveSizing(al.allocator(), root); - const commands = try UI.getRenderCommands(sized, al.allocator()); - for (commands.items) |value| { - std.debug.print("command : {any}\n", .{value}); - } -}