get started with defining layout, fix crash when removing boxes

master
Patrick Cleavelin 2023-06-22 00:17:11 -05:00
parent b6c90c9ead
commit 07ff9af114
1 changed files with 140 additions and 49 deletions

View File

@ -1,12 +1,22 @@
const std = @import("std"); const std = @import("std");
const raylib = @import("raylib"); const raylib = @import("raylib");
const UI_Flags = packed struct(u3) { const UI_Flags = packed struct(u4) {
clickable: bool = false, clickable: bool = false,
drawText: bool = false, drawText: bool = false,
drawBorder: bool = false, drawBorder: bool = false,
drawBackground: bool = false,
}; };
const UI_Direction = enum {
leftToRight,
rightToLeft,
topToBottom,
bottomToTop,
};
const UI_Layout = enum {};
const Vec2 = struct { const Vec2 = struct {
x: f32, x: f32,
y: f32, y: f32,
@ -26,6 +36,7 @@ const UI_Box = struct {
/// the assigned features /// the assigned features
flags: UI_Flags, flags: UI_Flags,
direction: UI_Direction,
/// the label? /// the label?
label: [:0]const u8, label: [:0]const u8,
@ -52,12 +63,12 @@ fn DeleteBoxChildren(box: *UI_Box, should_destroy: bool) void {
} }
} }
fn MakeBox(label: [:0]const u8, flags: UI_Flags) anyerror!bool { fn MakeBox(label: [:0]const u8, flags: UI_Flags, direction: UI_Direction) anyerror!bool {
//std.debug.print("making box '{s}'...", .{label}); //std.debug.print("making box '{s}'...", .{label});
popping_box = false; popping_box = false;
if (pushing_box) { if (pushing_box) {
const box = try PushBox(label, flags); const box = try PushBox(label, flags, direction);
pushing_box = false; pushing_box = false;
return box; return box;
@ -69,23 +80,26 @@ fn MakeBox(label: [:0]const u8, flags: UI_Flags) anyerror!bool {
if (std.mem.eql(u8, next.label, label)) { if (std.mem.eql(u8, next.label, label)) {
//std.debug.print("using cache for '{s}'\n", .{next.label}); //std.debug.print("using cache for '{s}'\n", .{next.label});
next.flags = flags; next.flags = flags;
next.direction = direction;
if (next.parent) |parent| { if (next.parent) |parent| {
parent.last = next; parent.last = next;
} }
current_box = next; current_box = next;
} else { } else {
// Invalid cache, delete next sibling while retaining the following one // Invalid cache, delete next sibling while retaining the following one
//std.debug.print("make_box: invalidating cache for '{s}' when making new box '{s}'\n", .{ next.label, label }); std.debug.print("make_box: invalidating cache for '{s}' when making new box '{s}'\n", .{ next.label, label });
const following_sibling = next.next; //const following_sibling = next.next;
DeleteBoxChildren(next, false); DeleteBoxChildren(next, false);
next.* = UI_Box{ next.* = UI_Box{
.label = label, .label = label,
.flags = flags, .flags = flags,
.direction = direction,
.first = null, .first = null,
.last = null, .last = null,
.next = following_sibling, // TODO: don't keep this null
.next = null,
.prev = null, .prev = null,
.parent = box.parent, .parent = box.parent,
.computed_pos = Vec2{ .x = 0, .y = 0 }, .computed_pos = Vec2{ .x = 0, .y = 0 },
@ -104,6 +118,7 @@ fn MakeBox(label: [:0]const u8, flags: UI_Flags) anyerror!bool {
new_box.* = UI_Box{ new_box.* = UI_Box{
.label = label, .label = label,
.flags = flags, .flags = flags,
.direction = direction,
.first = null, .first = null,
.last = null, .last = null,
@ -126,6 +141,7 @@ fn MakeBox(label: [:0]const u8, flags: UI_Flags) anyerror!bool {
new_box.* = UI_Box{ new_box.* = UI_Box{
.label = label, .label = label,
.flags = flags, .flags = flags,
.direction = direction,
.first = null, .first = null,
.last = null, .last = null,
@ -148,11 +164,11 @@ fn MakeBox(label: [:0]const u8, flags: UI_Flags) anyerror!bool {
return false; return false;
} }
fn PushBox(label: [:0]const u8, flags: UI_Flags) anyerror!bool { fn PushBox(label: [:0]const u8, flags: UI_Flags, direction: UI_Direction) anyerror!bool {
//std.debug.print("pushing box '{s}'...", .{label}); //std.debug.print("pushing box '{s}'...", .{label});
if (popping_box) { if (popping_box) {
const box = try MakeBox(label, flags); const box = try MakeBox(label, flags, direction);
pushing_box = true; pushing_box = true;
return box; return box;
@ -168,6 +184,7 @@ fn PushBox(label: [:0]const u8, flags: UI_Flags) anyerror!bool {
if (std.mem.eql(u8, first.label, label)) { if (std.mem.eql(u8, first.label, label)) {
//std.debug.print("using cache for '{s}'\n", .{first.label}); //std.debug.print("using cache for '{s}'\n", .{first.label});
first.flags = flags; first.flags = flags;
first.direction = direction;
current_box = first; current_box = first;
if (first.parent) |parent| { if (first.parent) |parent| {
@ -175,17 +192,19 @@ fn PushBox(label: [:0]const u8, flags: UI_Flags) anyerror!bool {
} }
} else { } else {
// Invalid cache // Invalid cache
//std.debug.print("push_box: invalidating cache for '{s}' when making new box '{s}'\n", .{ first.label, label }); std.debug.print("push_box: invalidating cache for '{s}' when making new box '{s}'\n", .{ first.label, label });
const following_sibling = first.next; //const following_sibling = first.next;
DeleteBoxChildren(first, false); DeleteBoxChildren(first, false);
first.* = UI_Box{ first.* = UI_Box{
.label = label, .label = label,
.flags = flags, .flags = flags,
.direction = direction,
.first = null, .first = null,
.last = null, .last = null,
.next = following_sibling, // TODO: don't keep this null
.next = null,
.prev = null, .prev = null,
.parent = current_box, .parent = current_box,
.computed_pos = Vec2{ .x = 0, .y = 0 }, .computed_pos = Vec2{ .x = 0, .y = 0 },
@ -203,6 +222,7 @@ fn PushBox(label: [:0]const u8, flags: UI_Flags) anyerror!bool {
new_box.* = UI_Box{ new_box.* = UI_Box{
.label = label, .label = label,
.flags = flags, .flags = flags,
.direction = direction,
.first = null, .first = null,
.last = null, .last = null,
@ -221,7 +241,7 @@ fn PushBox(label: [:0]const u8, flags: UI_Flags) anyerror!bool {
} }
} else { } else {
pushing_box = false; pushing_box = false;
return try MakeBox(label, flags); return try MakeBox(label, flags, direction);
} }
if (current_box) |box| { if (current_box) |box| {
@ -240,6 +260,9 @@ fn PopBox() void {
//current_box = parent.last; //current_box = parent.last;
//return; //return;
//} //}
if (box.parent) |p| {
p.last = current_box;
}
current_box = box.parent; current_box = box.parent;
popping_box = true; popping_box = true;
return; return;
@ -257,7 +280,14 @@ fn MakeButton(label: [:0]const u8) !bool {
.clickable = true, .clickable = true,
.drawText = true, .drawText = true,
.drawBorder = true, .drawBorder = true,
}); .drawBackground = true,
}, .leftToRight);
}
fn MakeLabel(label: [:0]const u8) !bool {
return try MakeBox(label, .{
.drawText = true,
}, .leftToRight);
} }
fn CountChildren(box: *UI_Box) u32 { fn CountChildren(box: *UI_Box) u32 {
@ -267,6 +297,8 @@ fn CountChildren(box: *UI_Box) u32 {
while (b) |child| { while (b) |child| {
count += 1; count += 1;
// TODO: um, somehow need to trim currently unused tree nodes
if (b == box.last) break;
b = child.next; b = child.next;
} }
@ -276,10 +308,20 @@ fn CountChildren(box: *UI_Box) u32 {
fn CountSiblings(box: *UI_Box) u32 { fn CountSiblings(box: *UI_Box) u32 {
var count: u32 = 0; var count: u32 = 0;
var b = box; var b = box;
if (b.parent) |p| {
if (b == p.last) return 0;
}
while (b.next) |next| { while (b.next) |next| {
count += 1; count += 1;
if (b.parent) |p| {
if (b == p.last) {
//std.debug.print("count siblings last askdhfksahdfklhsdaklfhf\n", .{});
break;
}
}
b = next; b = next;
} }
@ -303,22 +345,58 @@ fn DrawUI(box: *UI_Box, parent: ?*UI_Box, parent_pos: Vec2, parent_size: Vec2) v
const my_index = num_siblings - num_siblings_after_me; const my_index = num_siblings - num_siblings_after_me;
//std.debug.print("num_index {d}\n", .{my_index}); //std.debug.print("num_index {d}\n", .{my_index});
box.computed_size = Vec2{ if (parent) |p| {
.x = parent_size.x / (@intToFloat(f32, num_siblings) + 1), box.computed_size = Vec2{
.y = parent_size.y, .x = switch (p.direction) {
//.y = parent_size.y / (@intToFloat(f32, num_siblings) + 1), .leftToRight => parent_size.x / (@intToFloat(f32, num_siblings) + 1),
}; .rightToLeft => unreachable,
box.computed_pos = Vec2{ .topToBottom => parent_size.x,
.x = box.computed_size.x * @intToFloat(f32, my_index) + parent_pos.x, .bottomToTop => unreachable,
.y = parent_pos.y, },
//.y = box.computed_size.y * @intToFloat(f32, my_index) + parent_pos.y, .y = switch (p.direction) {
}; .leftToRight => parent_size.y,
.rightToLeft => unreachable,
.topToBottom => parent_size.y / (@intToFloat(f32, num_siblings) + 1),
.bottomToTop => unreachable,
},
};
} else {
box.computed_size = Vec2{
.x = parent_size.x,
.y = parent_size.y,
};
}
if (parent) |p| {
box.computed_pos = Vec2{
.x = switch (p.direction) {
.leftToRight => box.computed_size.x * @intToFloat(f32, my_index) + parent_pos.x,
.rightToLeft => unreachable,
.topToBottom => parent_pos.x,
.bottomToTop => unreachable,
},
.y = switch (p.direction) {
.leftToRight => parent_pos.y,
.rightToLeft => unreachable,
.topToBottom => box.computed_size.y * @intToFloat(f32, my_index) + parent_pos.y,
.bottomToTop => unreachable,
},
};
} else {
box.computed_pos = Vec2{
.x = parent_pos.x,
.y = parent_pos.y,
};
}
if (box.flags.drawBackground) {
raylib.DrawRectangle(@floatToInt(i32, box.computed_pos.x), @floatToInt(i32, box.computed_pos.y), @floatToInt(i32, box.computed_size.x), @floatToInt(i32, box.computed_size.y), raylib.DARKGRAY);
}
if (box.flags.drawBorder) { if (box.flags.drawBorder) {
raylib.DrawRectangleLines(@floatToInt(i32, box.computed_pos.x), @floatToInt(i32, box.computed_pos.y), @floatToInt(i32, box.computed_size.x), @floatToInt(i32, box.computed_size.y), raylib.BLUE); raylib.DrawRectangleLines(@floatToInt(i32, box.computed_pos.x), @floatToInt(i32, box.computed_pos.y), @floatToInt(i32, box.computed_size.x), @floatToInt(i32, box.computed_size.y), raylib.BLUE);
} }
if (box.flags.drawText) { if (box.flags.drawText) {
raylib.DrawText(box.label, @floatToInt(i32, box.computed_pos.x), @floatToInt(i32, box.computed_pos.y), 10, raylib.RED); raylib.DrawText(box.label, @floatToInt(i32, box.computed_pos.x), @floatToInt(i32, box.computed_pos.y), 20, raylib.RED);
} }
// draw children // draw children
@ -326,6 +404,8 @@ fn DrawUI(box: *UI_Box, parent: ?*UI_Box, parent_pos: Vec2, parent_size: Vec2) v
while (child) |c| { while (child) |c| {
DrawUI(c, box, box.computed_pos, box.computed_size); DrawUI(c, box, box.computed_pos, box.computed_size);
if (child == box.last) break;
child = c.next; child = c.next;
} }
} }
@ -340,11 +420,13 @@ pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
box_allocator = gpa.allocator(); box_allocator = gpa.allocator();
_ = try PushBox("RootContainer", .{}); _ = try PushBox("RootContainer", .{}, .leftToRight);
root_box = current_box; root_box = current_box;
//std.debug.print("Starting main loop\n", .{}); //std.debug.print("Starting main loop\n", .{});
var ran = false; var other_button_shown = false;
var show_buttons = true;
var dir: UI_Direction = .topToBottom;
while (!raylib.WindowShouldClose()) { while (!raylib.WindowShouldClose()) {
current_box = root_box; current_box = root_box;
pushing_box = false; pushing_box = false;
@ -359,34 +441,43 @@ pub fn main() !void {
raylib.ClearBackground(raylib.BLACK); raylib.ClearBackground(raylib.BLACK);
_ = try PushBox("ButtonArray", .{}); if (show_buttons) {
if (try MakeButton("click me")) { _ = try PushBox("ButtonArray", .{}, dir);
std.debug.print("button clicked\n", .{}); defer PopBox();
}
if (try MakeButton("click me 2")) {
std.debug.print("button 2 clicked\n", .{});
}
PopBox();
_ = try PushBox("TextArray", .{}); if (try MakeButton("Show Labels")) {
if (try MakeBox("This is some text", .{ other_button_shown = !other_button_shown;
.drawText = true, }
.clickable = true, if (try MakeButton("Switch Direction")) {
})) { if (dir == .topToBottom) {
std.debug.print("text clicked\n", .{}); dir = .leftToRight;
} else {
dir = .topToBottom;
}
}
}
if (other_button_shown) {
_ = try PushBox("TextArray", .{}, dir);
defer PopBox();
_ = try MakeLabel("This is some text");
_ = try MakeLabel("So is this");
if (show_buttons) {
if (try MakeButton("Remove Buttons")) {
show_buttons = false;
}
} else {
if (try MakeButton("Show Buttons")) {
show_buttons = true;
}
}
} }
_ = try MakeBox("So is this", .{ .drawText = true });
PopBox();
if (root_box) |box| { if (root_box) |box| {
//std.debug.print("====== STARTING DRAWING =====\n", .{});
DrawUI(box, null, .{ .x = 0, .y = 0 }, .{ .x = 800, .y = 600 }); DrawUI(box, null, .{ .x = 0, .y = 0 }, .{ .x = 800, .y = 600 });
} }
// raylib.DrawFPS(10, 10);
//raylib.DrawText("Hello Zooy", 100, 100, 20, raylib.YELLOW);
//if (ran) break;
ran = true;
} }
} }