start of the lib
This commit is contained in:
378
src/main.zig
Normal file
378
src/main.zig
Normal file
@@ -0,0 +1,378 @@
|
||||
const std = @import("std");
|
||||
const rl = @import("rl");
|
||||
const bmb = @import("pandas_ui_lib");
|
||||
|
||||
fn getTextWidth(string: []const u8, font: rl.Font) i32 {
|
||||
var width: c_int = 0;
|
||||
for (string) |char| {
|
||||
width +=
|
||||
@as(c_int, @intFromFloat(font.recs[char - 32].width)) +
|
||||
font.glyphs[char - 32].offsetX;
|
||||
}
|
||||
|
||||
const s = @as([:0]const u8, @ptrCast(string));
|
||||
return @as(bmb.Real, @intCast(rl.measureText(s, @as(i32, @intCast(font.baseSize)))));
|
||||
}
|
||||
|
||||
fn getTextheight(string: []const u8, font: rl.Font) i32 {
|
||||
_ = string;
|
||||
// std.debug.print("font.baseSize : {}\n", .{font.baseSize});
|
||||
return @as(bmb.Real, @intCast(font.baseSize));
|
||||
}
|
||||
|
||||
const TextType = bmb.TextType(rl.Font, getTextWidth, getTextheight);
|
||||
const TextureType = bmb.TextureType(rl.Texture2D);
|
||||
|
||||
const UI = bmb.Bamboo(TextType, TextureType);
|
||||
|
||||
fn sideBar() UI.Node {
|
||||
return UI.Element(.{
|
||||
.name = "sidebar",
|
||||
.rect = .{
|
||||
.h = 20,
|
||||
.w = 40,
|
||||
},
|
||||
.style = .{
|
||||
.background_colour = .{
|
||||
.r = 255,
|
||||
.b = 255,
|
||||
},
|
||||
.size_y = .grow,
|
||||
// .size_x = .grow,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
fn button() UI.Node {
|
||||
return UI.Element(.{
|
||||
.name = "button",
|
||||
.rect = .{
|
||||
.h = 20,
|
||||
.w = 60,
|
||||
},
|
||||
.style = .{
|
||||
.background_colour = .{
|
||||
.g = 255,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
pub fn main() anyerror!void {
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
const screenWidth = 1920;
|
||||
const screenHeight = 1080;
|
||||
|
||||
rl.initWindow(screenWidth, screenHeight, "raylib-zig [core] example - basic window");
|
||||
defer rl.closeWindow(); // Close window and OpenGL context
|
||||
|
||||
rl.setTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
std.debug.print("bmb version :{s}\n", .{bmb.VERSON});
|
||||
|
||||
const child = &[_]UI.Node{
|
||||
sideBar(),
|
||||
button(),
|
||||
UI.Element(.{
|
||||
.style = .{
|
||||
.background_colour = .{
|
||||
.b = 120,
|
||||
},
|
||||
.padding = .{
|
||||
.bottom = 10,
|
||||
.left = 10,
|
||||
.right = 10,
|
||||
.top = 10,
|
||||
},
|
||||
},
|
||||
.children = &[_]UI.Node{
|
||||
UI.Txt(
|
||||
UI.Text.init(
|
||||
"hello world",
|
||||
try rl.getFontDefault(),
|
||||
.{},
|
||||
),
|
||||
),
|
||||
UI.Txt(
|
||||
UI.Text.init(
|
||||
"hello world again",
|
||||
try rl.getFontDefault(),
|
||||
.{},
|
||||
).setBackground(.{
|
||||
.a = 255,
|
||||
.g = 255,
|
||||
}),
|
||||
),
|
||||
},
|
||||
}),
|
||||
|
||||
UI.Element(.{
|
||||
.name = "child",
|
||||
.style = .{ .background_colour = .{
|
||||
.b = 255,
|
||||
}, .padding = .{ .bottom = 5 } },
|
||||
.children = &[_]UI.Node{
|
||||
UI.Element(.{
|
||||
.name = "branch1",
|
||||
// .rect = .{ .w = 200 },
|
||||
.style = .{
|
||||
.size_x = .grow,
|
||||
.padding = .{
|
||||
.bottom = 5,
|
||||
.left = 5,
|
||||
.top = 5,
|
||||
.right = 5,
|
||||
},
|
||||
// .layout = .right_to_left,
|
||||
.child_gap = 5,
|
||||
},
|
||||
.children = &[_]UI.Node{
|
||||
UI.Element(.{
|
||||
.name = "branch11",
|
||||
.style = .{
|
||||
.background_colour = .{
|
||||
.g = 128,
|
||||
.b = 128,
|
||||
.r = 128,
|
||||
},
|
||||
.size_x = .grow,
|
||||
},
|
||||
.rect = .{
|
||||
.h = 40,
|
||||
.w = 40,
|
||||
},
|
||||
}),
|
||||
UI.Element(.{
|
||||
.name = "branch12",
|
||||
.style = .{
|
||||
.background_colour = .{
|
||||
.g = 128,
|
||||
.r = 128,
|
||||
},
|
||||
.size_x = .grow,
|
||||
},
|
||||
.rect = .{
|
||||
.h = 40,
|
||||
.w = 40,
|
||||
},
|
||||
}),
|
||||
UI.Element(.{
|
||||
.name = "branch13",
|
||||
.rect = .{
|
||||
.h = 40,
|
||||
.w = 40,
|
||||
},
|
||||
.style = .{
|
||||
.background_colour = .{
|
||||
.g = 90,
|
||||
.b = 150,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
UI.Element(.{
|
||||
.name = "branch2",
|
||||
.style = .{
|
||||
.background_colour = .{
|
||||
.r = 255,
|
||||
.g = 128,
|
||||
},
|
||||
.layout = .right_to_left,
|
||||
.child_gap = 4,
|
||||
.padding = .{
|
||||
.left = 10,
|
||||
.bottom = 10,
|
||||
.right = 10,
|
||||
.top = 10,
|
||||
},
|
||||
},
|
||||
.children = &[_]UI.Node{
|
||||
UI.Element(.{
|
||||
.name = "branch21",
|
||||
.rect = .{
|
||||
.h = 30,
|
||||
.w = 30,
|
||||
},
|
||||
.style = .{
|
||||
.rounded = 40,
|
||||
.background_colour = .{
|
||||
.r = 255,
|
||||
.b = 150,
|
||||
},
|
||||
},
|
||||
}),
|
||||
UI.Element(.{
|
||||
.name = "branch22",
|
||||
.rect = .{
|
||||
.h = 30,
|
||||
.w = 30,
|
||||
},
|
||||
.style = .{
|
||||
.rounded = 40,
|
||||
.background_colour = .{
|
||||
.r = 90,
|
||||
.b = 150,
|
||||
},
|
||||
},
|
||||
}),
|
||||
UI.Element(.{
|
||||
.name = "branch21",
|
||||
.rect = .{
|
||||
.h = 30,
|
||||
.w = 30,
|
||||
},
|
||||
.style = .{
|
||||
.rounded = 40,
|
||||
.background_colour = .{
|
||||
.r = 255,
|
||||
.b = 150,
|
||||
},
|
||||
},
|
||||
}),
|
||||
UI.Element(.{
|
||||
.name = "branch22",
|
||||
.rect = .{
|
||||
.h = 30,
|
||||
.w = 30,
|
||||
},
|
||||
.style = .{
|
||||
.rounded = 40,
|
||||
.background_colour = .{
|
||||
.r = 90,
|
||||
.b = 150,
|
||||
},
|
||||
},
|
||||
}),
|
||||
UI.Element(.{
|
||||
.name = "branch21",
|
||||
.rect = .{
|
||||
.h = 30,
|
||||
.w = 30,
|
||||
},
|
||||
.style = .{
|
||||
.rounded = 40,
|
||||
.background_colour = .{
|
||||
.r = 255,
|
||||
.b = 150,
|
||||
},
|
||||
},
|
||||
}),
|
||||
UI.Element(.{
|
||||
.name = "branch22",
|
||||
.rect = .{
|
||||
.h = 30,
|
||||
.w = 30,
|
||||
},
|
||||
.style = .{
|
||||
.rounded = 40,
|
||||
.background_colour = .{
|
||||
.r = 90,
|
||||
.b = 150,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
button(),
|
||||
};
|
||||
|
||||
const root = UI.Element(.{
|
||||
.name = "root",
|
||||
.pos = .{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
},
|
||||
.rect = .{
|
||||
.h = screenHeight,
|
||||
.w = screenWidth,
|
||||
},
|
||||
// .on_hover = callback(),
|
||||
// .on_click = callback(),
|
||||
.style = .{
|
||||
.background_colour = .{
|
||||
.r = 255,
|
||||
},
|
||||
.layout = .right_to_left,
|
||||
// .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);
|
||||
// UI.printTree(sized);
|
||||
const commands = try UI.getRenderCommands(sized, al.allocator());
|
||||
|
||||
// Main game loop
|
||||
while (!rl.windowShouldClose()) { // Detect window close button or ESC key
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
// TODO: Update your variables here
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
rl.beginDrawing();
|
||||
defer rl.endDrawing();
|
||||
|
||||
for (commands.items) |command| {
|
||||
// std.debug.print("command : {any}\n", .{command});
|
||||
switch (command) {
|
||||
.rect => |r| {
|
||||
if (r.rounding) |rounding| {
|
||||
rl.drawRectangleRounded(
|
||||
rl.Rectangle.init(
|
||||
@as(f32, @floatFromInt(r.pos.x)),
|
||||
@as(f32, @floatFromInt(r.pos.y)),
|
||||
@as(f32, @floatFromInt(r.rect.w)),
|
||||
@as(f32, @floatFromInt(r.rect.h)),
|
||||
),
|
||||
@as(f32, @floatFromInt(rounding)) / 100.0,
|
||||
10,
|
||||
rl.Color.init(
|
||||
r.colour.r,
|
||||
r.colour.g,
|
||||
r.colour.b,
|
||||
r.colour.a,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
rl.drawRectangle(r.pos.x, r.pos.y, r.rect.w, r.rect.h, rl.Color.init(
|
||||
r.colour.r,
|
||||
r.colour.g,
|
||||
r.colour.b,
|
||||
r.colour.a,
|
||||
));
|
||||
}
|
||||
},
|
||||
.text => |t| {
|
||||
const string = @as([:0]const u8, @ptrCast(t.text.string));
|
||||
rl.drawText(string, t.pos.x, t.pos.y, t.text.getTextHeight(), rl.Color.init(
|
||||
t.text.colour.r,
|
||||
t.text.colour.g,
|
||||
t.text.colour.b,
|
||||
t.text.colour.a,
|
||||
));
|
||||
},
|
||||
.texture => continue,
|
||||
}
|
||||
}
|
||||
|
||||
rl.clearBackground(.white);
|
||||
|
||||
// rl.drawRectangle(0, 0, 200, 100, .blue);
|
||||
|
||||
// rl.drawText("Congrats! You created your first window!", 190, 200, 20, .red);
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
}
|
||||
824
src/root.zig
Normal file
824
src/root.zig
Normal file
@@ -0,0 +1,824 @@
|
||||
//! By convention, root.zig is the root source file when making a library.
|
||||
const std = @import("std");
|
||||
|
||||
// base drawing functions
|
||||
// text,
|
||||
// rect,
|
||||
// texture,
|
||||
|
||||
// sissor
|
||||
pub const VERSON = "0.0.1";
|
||||
pub const Real = i32;
|
||||
const Spaceing = 4;
|
||||
|
||||
pub fn TextType(
|
||||
comptime FontType: type,
|
||||
comptime getTextWidthFunc: fn (string: []const u8, FontType) Real,
|
||||
comptime getTextHeightFunc: fn (string: []const u8, FontType) Real,
|
||||
) type {
|
||||
return struct {
|
||||
const Optional = struct {
|
||||
font: FontType,
|
||||
};
|
||||
const Self = @This();
|
||||
string: []const u8,
|
||||
colour: Colour,
|
||||
background: Colour,
|
||||
font: FontType,
|
||||
|
||||
pub fn init(string: []const u8, font: FontType, colour: Colour) Self {
|
||||
return Self{
|
||||
.string = string,
|
||||
.font = font,
|
||||
.colour = colour,
|
||||
.background = .{
|
||||
.r = 0,
|
||||
.b = 0,
|
||||
.g = 0,
|
||||
.a = 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn setBackground(self: Self, background: Colour) Self {
|
||||
var s = self;
|
||||
s.background = background;
|
||||
return s;
|
||||
}
|
||||
|
||||
pub fn getTextWidth(self: Self) Real {
|
||||
return getTextWidthFunc(self.string, self.font);
|
||||
}
|
||||
|
||||
pub fn getTextHeight(self: Self) Real {
|
||||
return getTextHeightFunc(self.string, self.font);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn TextureType(comptime TextureData: type) type {
|
||||
return struct {
|
||||
texture: TextureData,
|
||||
rect: Rect,
|
||||
};
|
||||
}
|
||||
|
||||
pub const Rect = struct {
|
||||
w: Real = 0,
|
||||
h: Real = 0,
|
||||
};
|
||||
|
||||
pub const Pos = struct {
|
||||
x: Real = 0,
|
||||
y: Real = 0,
|
||||
};
|
||||
|
||||
const Sizing = enum {
|
||||
fixed,
|
||||
grow,
|
||||
min,
|
||||
max,
|
||||
fit,
|
||||
};
|
||||
|
||||
pub const Padding = struct {
|
||||
top: Real = 0,
|
||||
bottom: Real = 0,
|
||||
left: Real = 0,
|
||||
right: Real = 0,
|
||||
};
|
||||
|
||||
pub const Layout = enum {
|
||||
right_to_left,
|
||||
top_to_bottom,
|
||||
};
|
||||
|
||||
pub const Style = struct {
|
||||
size_x: Sizing = .fit,
|
||||
size_y: Sizing = .fit,
|
||||
|
||||
background_colour: Colour = .{},
|
||||
rounded: ?Real = null,
|
||||
|
||||
// require re-draw
|
||||
padding: Padding = .{},
|
||||
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 const Colour = struct {
|
||||
r: u8 = 0,
|
||||
g: u8 = 0,
|
||||
b: u8 = 0,
|
||||
a: u8 = 255,
|
||||
};
|
||||
|
||||
pub const State = enum {
|
||||
None,
|
||||
hovered,
|
||||
clicked,
|
||||
};
|
||||
|
||||
pub fn Bamboo(
|
||||
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();
|
||||
|
||||
pub const Text = text_type;
|
||||
pub const Texture = texture_type;
|
||||
|
||||
const NodeTypes = enum {
|
||||
element,
|
||||
text,
|
||||
texture,
|
||||
};
|
||||
pub const Node = union(NodeTypes) {
|
||||
element: Ele,
|
||||
text: Text,
|
||||
texture: Texture,
|
||||
};
|
||||
|
||||
pub const Ele = struct {
|
||||
const Self = @This();
|
||||
name: []const u8 = "",
|
||||
z_index: usize = 0,
|
||||
pos: Pos = .{},
|
||||
rect: Rect = .{},
|
||||
style: Style = .{},
|
||||
children: []const Node = &[_]Node{},
|
||||
};
|
||||
|
||||
const RenderCommandType = enum {
|
||||
rect,
|
||||
text,
|
||||
texture,
|
||||
};
|
||||
pub const RenderCommand = union(RenderCommandType) {
|
||||
rect: struct {
|
||||
rect: Rect,
|
||||
pos: Pos,
|
||||
colour: Colour,
|
||||
rounding: ?Real,
|
||||
z_index: usize = 0,
|
||||
},
|
||||
text: struct {
|
||||
text: Text,
|
||||
pos: Pos,
|
||||
z_index: usize = 0,
|
||||
},
|
||||
texture: struct {
|
||||
texture: Texture,
|
||||
pos: Pos,
|
||||
z_index: usize = 0,
|
||||
},
|
||||
};
|
||||
|
||||
// /// highly suggested that you use an area for the alloc
|
||||
// pub fn init(alloc: std.mem.Allocator) Self {
|
||||
// return Self{
|
||||
// .alloc = alloc,
|
||||
// };
|
||||
// }
|
||||
|
||||
pub inline fn Element(ele: Ele) Node {
|
||||
return Node{
|
||||
.element = ele,
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn Txt(text: Text) Node {
|
||||
return Element(
|
||||
.{
|
||||
.style = .{
|
||||
.background_colour = text.background,
|
||||
},
|
||||
.children = &[_]Node{.{
|
||||
.text = text,
|
||||
}},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn closeElement(node: *Node, parent: *Node) void {
|
||||
switch (node.*) {
|
||||
.element => {
|
||||
// layout
|
||||
const child_gap: Real = parent.element.style.child_gap;
|
||||
|
||||
switch (parent.element.style.layout) {
|
||||
.right_to_left => parent.element.rect.w += child_gap,
|
||||
.top_to_bottom => parent.element.rect.h += child_gap,
|
||||
}
|
||||
|
||||
const padding = node.element.style.padding;
|
||||
|
||||
node.element.rect.w += padding.left + padding.right;
|
||||
node.element.rect.h += padding.top + padding.bottom;
|
||||
|
||||
switch (parent.element.style.layout) {
|
||||
.right_to_left => {
|
||||
parent.element.rect.h = @max(node.element.rect.h, parent.element.rect.h);
|
||||
parent.element.rect.w += node.element.rect.w;
|
||||
},
|
||||
|
||||
.top_to_bottom => {
|
||||
parent.element.rect.h += node.element.rect.h;
|
||||
parent.element.rect.w = @max(node.element.rect.w, parent.element.rect.w);
|
||||
},
|
||||
}
|
||||
},
|
||||
.text => {
|
||||
parent.element.rect.h += node.text.getTextHeight();
|
||||
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});
|
||||
},
|
||||
.texture => return,
|
||||
}
|
||||
}
|
||||
|
||||
fn deepCloneHelper(node: Node, node_location: *Node, alloc: std.mem.Allocator) !void {
|
||||
// std.debug.print("nodeloc : {}\n", .{node_location});
|
||||
node_location.* = node;
|
||||
// std.debug.print("nodeloc : {}\n", .{node_location});
|
||||
switch (node) {
|
||||
.element => |ele| {
|
||||
// BASE CASE
|
||||
// node_location.element.name = "cloned";
|
||||
if (ele.children.len == 0) return;
|
||||
const children = try alloc.alloc(Node, ele.children.len);
|
||||
// std.debug.print("children Alloc {x}\n", .{@intFromPtr(children.ptr)});
|
||||
for (ele.children, 0..) |child, i| {
|
||||
try deepCloneHelper(child, &children[i], alloc);
|
||||
// std.debug.print("base node_loc {x}\n", .{@intFromPtr(node_location.element.children.ptr)});
|
||||
node_location.*.element.children = children;
|
||||
// std.debug.print("up[date node_loc {x}\n", .{@intFromPtr(node_location.element.children.ptr)});
|
||||
}
|
||||
},
|
||||
else => return,
|
||||
}
|
||||
}
|
||||
|
||||
// we deep clone the root node as this make it easier for the temp node
|
||||
fn deepClone(node: Node, alloc: std.mem.Allocator) !Node {
|
||||
var new_node = node;
|
||||
switch (new_node) {
|
||||
.element => |ele| {
|
||||
// BASE CASE
|
||||
if (ele.children.len == 0) return new_node;
|
||||
const children = try alloc.alloc(Node, ele.children.len);
|
||||
for (ele.children, 0..) |child, i| {
|
||||
try deepCloneHelper(child, &children[i], alloc);
|
||||
new_node.element.children = children;
|
||||
}
|
||||
},
|
||||
else => return new_node,
|
||||
}
|
||||
|
||||
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 => {
|
||||
// std.debug.print("parent name : {s}\n", .{parent.element.name});
|
||||
try command_list.append(alloc, .{
|
||||
.rect = .{
|
||||
.colour = node.element.style.background_colour,
|
||||
.pos = node.element.pos,
|
||||
.rect = node.element.rect,
|
||||
.rounding = node.element.style.rounded,
|
||||
.z_index = node.element.z_index,
|
||||
},
|
||||
});
|
||||
|
||||
for (node.element.children, 0..) |_, i| {
|
||||
switch (node.element.children[i]) {
|
||||
.element => try getRenderCommandsHelper(node.element.children[i], alloc, command_list),
|
||||
.text => |text| try command_list.append(alloc, .{ .text = .{
|
||||
.pos = node.element.pos,
|
||||
.text = text,
|
||||
.z_index = node.element.z_index,
|
||||
} }),
|
||||
.texture => |texture| try command_list.append(alloc, .{ .texture = .{
|
||||
.pos = node.element.pos,
|
||||
.texture = texture,
|
||||
.z_index = node.element.z_index,
|
||||
} }),
|
||||
}
|
||||
}
|
||||
},
|
||||
else => return, // the commands get created above, since the text and texture need a pos
|
||||
// but they dont have the pos one the parent has it
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getRenderCommands(node: Node, alloc: std.mem.Allocator) !std.ArrayList(RenderCommand) {
|
||||
var render_commands: std.ArrayList(RenderCommand) = .empty;
|
||||
try getRenderCommandsHelper(node, alloc, &render_commands);
|
||||
return render_commands;
|
||||
}
|
||||
|
||||
fn computeSizes(node: *Node, parent: *Node) void {
|
||||
switch (node.*) {
|
||||
.element => {
|
||||
// std.debug.print("parent name : {s}\n", .{parent.element.name});
|
||||
|
||||
for (node.element.children, 0..) |_, i| {
|
||||
computeSizes(@constCast(&node.element.children[i]), node);
|
||||
}
|
||||
|
||||
// cheaters way to beat the fence post problem
|
||||
// TODO fix this
|
||||
switch (node.element.style.layout) {
|
||||
.right_to_left => node.element.rect.w -= node.element.style.child_gap,
|
||||
.top_to_bottom => node.element.rect.h -= node.element.style.child_gap,
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
closeElement(node, parent);
|
||||
}
|
||||
|
||||
fn filterGrowableChildren(index: *usize, node: Node) ?*Ele {
|
||||
switch (node) {
|
||||
.element => {
|
||||
if (node.element.children.len == 0) return null;
|
||||
|
||||
var i = index.*;
|
||||
while (i < node.element.children.len - 1) : (i += 1) {
|
||||
switch (node.element.children[i]) {
|
||||
.element => |e| {
|
||||
if (e.style.size_x == .grow or e.style.size_y == .grow) {
|
||||
index.* = i + 1;
|
||||
return @constCast(&node.element.children[i].element);
|
||||
}
|
||||
},
|
||||
else => continue,
|
||||
}
|
||||
}
|
||||
},
|
||||
else => return null,
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn computeRemainingHeightAndWidth(node: Node) struct { Real, Real } {
|
||||
// growable code
|
||||
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});
|
||||
|
||||
remaining_width -|= node.element.style.padding.left + node.element.style.padding.right;
|
||||
remaining_height -|= node.element.style.padding.top + node.element.style.padding.bottom;
|
||||
|
||||
if (node.element.children.len == 0) return .{
|
||||
0,
|
||||
0,
|
||||
};
|
||||
|
||||
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});
|
||||
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});
|
||||
},
|
||||
.texture => {
|
||||
switch (node.element.style.layout) {
|
||||
.top_to_bottom => {
|
||||
remaining_height -|= child.texture.rect.h;
|
||||
remaining_width = @max(remaining_width, child.texture.rect.w);
|
||||
},
|
||||
.right_to_left => {
|
||||
remaining_width -|= child.texture.rect.w;
|
||||
remaining_height = @max(remaining_height, child.texture.rect.h);
|
||||
},
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
std.debug.print("remaining_width : {d}\n", .{remaining_width});
|
||||
std.debug.print("remaining_hieght : {d}\n", .{remaining_height});
|
||||
remaining_width -|= (@as(Real, @intCast(node.element.children.len -| 1)) * node.element.style.child_gap);
|
||||
remaining_height -|= (@as(Real, @intCast(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});
|
||||
|
||||
return .{
|
||||
remaining_height,
|
||||
remaining_width,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getHeight(element: *const Ele) Real {
|
||||
return element.rect.h;
|
||||
}
|
||||
|
||||
pub fn getWidth(element: *const Ele) Real {
|
||||
return element.rect.w;
|
||||
}
|
||||
|
||||
pub fn addHeight(element: *Ele, height: Real) void {
|
||||
element.rect.h += height;
|
||||
}
|
||||
|
||||
pub fn addWidth(element: *Ele, width: Real) void {
|
||||
element.rect.w += width;
|
||||
}
|
||||
|
||||
pub fn getSizingY(element: *const Ele) Sizing {
|
||||
return element.style.size_y;
|
||||
}
|
||||
|
||||
pub fn getSizingX(element: *const Ele) Sizing {
|
||||
return element.style.size_x;
|
||||
}
|
||||
|
||||
fn growChildIndependedElements(
|
||||
node: Node,
|
||||
axis_x: bool,
|
||||
) void {
|
||||
const padding_x = node.element.style.padding.left + node.element.style.padding.right;
|
||||
const padding_y = node.element.style.padding.left + node.element.style.padding.right;
|
||||
|
||||
for (node.element.children, 0..) |child, i| {
|
||||
switch (child) {
|
||||
.element => |e| {
|
||||
if (e.style.size_x == .grow and axis_x) {
|
||||
@constCast(&node.element.children[i]).element.rect.w = node.element.rect.w - padding_x;
|
||||
}
|
||||
if (e.style.size_y == .grow and !axis_x) {
|
||||
@constCast(&node.element.children[i]).element.rect.h = node.element.rect.h - padding_y;
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn growChildDependedElements(
|
||||
node: Node,
|
||||
remaining_disance: Real,
|
||||
total_growable: usize,
|
||||
getAxis: *const fn (*const Ele) Real,
|
||||
addToAxis: *const fn (*Ele, Real) void,
|
||||
getSizing: *const fn (*const Ele) Sizing,
|
||||
) void {
|
||||
var remaining = remaining_disance;
|
||||
|
||||
while (remaining > 0) {
|
||||
var find_next_smallest_index: usize = 0;
|
||||
var smallest = filterGrowableChildren(&find_next_smallest_index, node) orelse return;
|
||||
var second_smallest = &Ele{ .rect = .{
|
||||
.h = std.math.maxInt(Real),
|
||||
.w = std.math.maxInt(Real),
|
||||
} };
|
||||
var width_to_add = remaining;
|
||||
var next_smallest_index: usize = 0;
|
||||
while (filterGrowableChildren(&next_smallest_index, node)) |e| {
|
||||
if (getAxis(e) < getAxis(smallest)) {
|
||||
second_smallest = smallest;
|
||||
smallest = e;
|
||||
}
|
||||
|
||||
if (getAxis(e) > getAxis(smallest)) {
|
||||
second_smallest = if (getAxis(second_smallest) < getAxis(e)) second_smallest else e;
|
||||
width_to_add = getAxis(second_smallest) - getAxis(smallest);
|
||||
}
|
||||
}
|
||||
width_to_add = @min(width_to_add, @divTrunc(remaining, @as(Real, @intCast(total_growable))));
|
||||
if (width_to_add == 0) return;
|
||||
var grow_em: usize = 0;
|
||||
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 });
|
||||
if (getSizing(growable) == .grow)
|
||||
addToAxis(growable, width_to_add);
|
||||
std.debug.print("after growable : {}\n", .{growable.rect});
|
||||
remaining -|= width_to_add;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn growChildern(node: *Node) void {
|
||||
// check that there are growable children
|
||||
var total_growable_index: usize = 0;
|
||||
var total_growable: usize = 0;
|
||||
while (filterGrowableChildren(&total_growable_index, node.*)) |_| : (total_growable += 1) continue;
|
||||
|
||||
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 });
|
||||
|
||||
if (node.element.style.layout == .top_to_bottom) {
|
||||
growChildIndependedElements(node.*, true);
|
||||
if (remaining_height > 0) growChildDependedElements(
|
||||
node.*,
|
||||
remaining_height,
|
||||
total_growable,
|
||||
&getHeight,
|
||||
&addHeight,
|
||||
getSizingY,
|
||||
);
|
||||
}
|
||||
|
||||
if (node.element.style.layout == .right_to_left) {
|
||||
growChildIndependedElements(node.*, false);
|
||||
|
||||
if (remaining_width > 0) growChildDependedElements(
|
||||
node.*,
|
||||
remaining_width,
|
||||
total_growable,
|
||||
&getWidth,
|
||||
&addWidth,
|
||||
getSizingX,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn computeGrowElements(node: *Node) void {
|
||||
switch (node.*) {
|
||||
.element => {
|
||||
growChildern(node);
|
||||
for (node.element.children, 0..) |_, i| {
|
||||
computeGrowElements(@constCast(&node.element.children[i]));
|
||||
}
|
||||
},
|
||||
else => return,
|
||||
}
|
||||
}
|
||||
|
||||
fn computeChildernsPostions(ele: *Ele) void {
|
||||
const layout_style = ele.style.layout;
|
||||
var children = @constCast(ele.children);
|
||||
|
||||
switch (layout_style) {
|
||||
.right_to_left => {
|
||||
var off_set_left: Real = 0;
|
||||
for (ele.children, 0..) |_, i| {
|
||||
const child_gap: Real = ele.style.child_gap;
|
||||
switch (ele.children[i]) {
|
||||
.element => {
|
||||
// off set it by the parent
|
||||
children[i].element.pos.x += ele.pos.x + ele.style.padding.left + off_set_left;
|
||||
children[i].element.pos.y += ele.pos.y + ele.style.padding.top;
|
||||
off_set_left += children[i].element.rect.w + child_gap;
|
||||
// add
|
||||
},
|
||||
else => continue,
|
||||
}
|
||||
}
|
||||
},
|
||||
.top_to_bottom => {
|
||||
var offset_top: Real = 0;
|
||||
for (ele.children, 0..) |_, i| {
|
||||
const child_gap: Real = ele.style.child_gap;
|
||||
switch (ele.children[i]) {
|
||||
.element => {
|
||||
// off set it by the parent
|
||||
children[i].element.pos.x += ele.pos.x + ele.style.padding.left;
|
||||
children[i].element.pos.y += ele.pos.y + ele.style.padding.top + offset_top;
|
||||
offset_top += children[i].element.rect.h + child_gap;
|
||||
// add
|
||||
},
|
||||
else => continue,
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn computePostionsHelper(node: *Node, index: usize) void {
|
||||
switch (node.*) {
|
||||
.element => {
|
||||
computeChildernsPostions(&node.element);
|
||||
node.element.z_index = index;
|
||||
for (node.element.children, 0..) |_, i| {
|
||||
computePostionsHelper(@constCast(&node.element.children[i]), index + 1);
|
||||
}
|
||||
},
|
||||
else => return,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn computePostions(node: *Node) void {
|
||||
computePostionsHelper(node, 0);
|
||||
}
|
||||
|
||||
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 base_node: Node = Node{ .element = Ele{
|
||||
.children = &[_]Node{
|
||||
node_1,
|
||||
},
|
||||
} };
|
||||
computeSizes(&new_tree, &base_node);
|
||||
|
||||
// printTree(new_tree);
|
||||
|
||||
computeGrowElements(&new_tree);
|
||||
computePostions(&new_tree);
|
||||
// printTree(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});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user