mirror of https://github.com/pcleavelin/zooy
Compare commits
4 Commits
1d1b4abc8e
...
ad8ddae790
Author | SHA1 | Date |
---|---|---|
|
ad8ddae790 | |
|
8e7a5bb3a8 | |
|
570830f91c | |
|
0d27a14687 |
|
@ -1 +1 @@
|
||||||
Subproject commit 85750745b3a621e2107083e0589cf5fc3a1a73e7
|
Subproject commit f3a27866a77862a93f10f29fe5d69b5f16a38a3b
|
|
@ -0,0 +1,73 @@
|
||||||
|
const ui = @import("lib.zig");
|
||||||
|
|
||||||
|
const raylib = ui.raylib;
|
||||||
|
|
||||||
|
pub const Grid = struct {
|
||||||
|
fn MakeColumnHeader(ctx: *ui.UIContext, columns: [][]const u8) !void {
|
||||||
|
_ = try ctx.PushBox("GridColumnHeader", .{ .drawBackground = true, .drawBorder = false }, .leftToRight, .fitToChildren);
|
||||||
|
defer ctx.PopBox();
|
||||||
|
|
||||||
|
try ctx.PushStyle(.{ //
|
||||||
|
.color = raylib.WHITE, //
|
||||||
|
.text_color = .{ .r = 0x18, .g = 0x90, .b = 0xff, .a = 0xff }, //
|
||||||
|
.text_size = 12, //
|
||||||
|
.text_padding = 12, //
|
||||||
|
});
|
||||||
|
defer ctx.PopStyle();
|
||||||
|
|
||||||
|
for (columns) |column| {
|
||||||
|
try MakeLabel(ctx, column, @as(i32, @intCast(columns.len)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn MakeButton(ctx: *ui.UIContext, label: []const u8, items: i32) !bool {
|
||||||
|
return try ctx.MakeBox(label, .{
|
||||||
|
.clickable = true,
|
||||||
|
.drawText = true,
|
||||||
|
}, .leftToRight, .{ .percentOfParent = ui.Vec2{ .x = 1.0 / @as(f32, @floatFromInt(items)), .y = 1.0 } });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn MakeFormattedLabel(ctx: *ui.UIContext, comptime str: []const u8, args: anytype, items: i32) !void {
|
||||||
|
_ = try ctx.MakeFormattedLabelWithLayout(str, args, .{ .percentOfParent = ui.Vec2{ .x = 1.0 / @as(f32, @floatFromInt(items)), .y = 1.0 } });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn MakeLabel(ctx: *ui.UIContext, label: []const u8, items: i32) !void {
|
||||||
|
_ = try ctx.MakeLabelWithLayout(label, .{ .percentOfParent = ui.Vec2{ .x = 1.0 / @as(f32, @floatFromInt(items)), .y = 1.0 } });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn MakeGrid(ctx: *ui.UIContext, comptime T: type, columns: [][]const u8, data: []T, MakeBody: *const fn (ctx: *ui.UIContext, data: *const T, size: i32) anyerror!void) !void {
|
||||||
|
_ = try ctx.PushBox("GridContainer", .{ .drawBackground = true }, .topToBottom, .fill);
|
||||||
|
defer ctx.PopBox();
|
||||||
|
|
||||||
|
try MakeColumnHeader(ctx, columns);
|
||||||
|
|
||||||
|
{
|
||||||
|
try ctx.PushStyle(.{ //
|
||||||
|
.color = raylib.WHITE, //
|
||||||
|
.hover_color = .{ .r = 0x1a, .g = 0x7c, .b = 0xd3, .a = 0xff }, //
|
||||||
|
.text_color = raylib.BLACK, //
|
||||||
|
.text_size = 12, //
|
||||||
|
.text_padding = 12, //
|
||||||
|
});
|
||||||
|
defer ctx.PopStyle();
|
||||||
|
|
||||||
|
_ = try ctx.PushBox("Grid", .{ .drawBackground = true, .scrollable = true }, .topToBottom, .fill);
|
||||||
|
defer ctx.PopBox();
|
||||||
|
|
||||||
|
try ctx.PushStyle(.{ //
|
||||||
|
.color = raylib.WHITE, //
|
||||||
|
.hover_color = raylib.LIGHTGRAY, //
|
||||||
|
.text_color = raylib.BLACK, //
|
||||||
|
.text_size = 12, //
|
||||||
|
.text_padding = 12, //
|
||||||
|
});
|
||||||
|
for (data) |item| {
|
||||||
|
_ = try ctx.PushBox("GridItem", .{ .drawBackground = true, .drawBorder = false, .hoverable = true }, .leftToRight, .fitToChildren);
|
||||||
|
defer ctx.PopBox();
|
||||||
|
|
||||||
|
try MakeBody(ctx, &item, @as(i32, @intCast(columns.len)));
|
||||||
|
}
|
||||||
|
ctx.PopStyle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
651
src/lib.zig
651
src/lib.zig
|
@ -3,85 +3,83 @@ const std = @import("std");
|
||||||
// TODO: abstract raylib away to allow for consumers to use whatever they want.
|
// TODO: abstract raylib away to allow for consumers to use whatever they want.
|
||||||
// I'm also rexporting here because the zig build system hurts
|
// I'm also rexporting here because the zig build system hurts
|
||||||
pub const raylib = @import("raylib");
|
pub const raylib = @import("raylib");
|
||||||
|
pub const components = @import("components.zig");
|
||||||
|
|
||||||
// TODO: don't just make these public
|
pub const UIContext = struct {
|
||||||
pub var box_allocator: std.mem.Allocator = undefined;
|
const Self = @This();
|
||||||
|
const scroll_speed: f32 = 1.125;
|
||||||
|
|
||||||
pub var root_box: ?*UI_Box = null;
|
box_allocator: std.mem.Allocator,
|
||||||
pub var current_box: ?*UI_Box = null;
|
|
||||||
pub var current_style: std.ArrayList(UI_Style) = undefined;
|
|
||||||
|
|
||||||
pub var pushing_box: bool = false;
|
// Double-buffered allocator to hold per-frame heap-data
|
||||||
pub var popping_box: bool = false;
|
current_frame_allocator: usize = 0,
|
||||||
|
frame_allocators: [2]std.heap.ArenaAllocator,
|
||||||
|
|
||||||
pub var mouse_x: i32 = 0;
|
root_box: ?*UI_Box = null,
|
||||||
pub var mouse_y: i32 = 0;
|
current_box: ?*UI_Box = null,
|
||||||
pub var mouse_released: bool = false;
|
current_style: std.ArrayList(UI_Style) = undefined,
|
||||||
pub var mouse_hovering_clickable: bool = false;
|
|
||||||
|
|
||||||
pub const UI_Flags = packed struct(u5) {
|
hot: ?*UI_Box = null,
|
||||||
clickable: bool = false,
|
active: ?*UI_Box = null,
|
||||||
hoverable: bool = false,
|
|
||||||
drawText: bool = false,
|
pushing_box: bool = false,
|
||||||
drawBorder: bool = false,
|
popping_box: bool = false,
|
||||||
drawBackground: bool = false,
|
|
||||||
|
// PLEASE DON'T DO THIS
|
||||||
|
font20: raylib.Font = undefined,
|
||||||
|
font10: raylib.Font = undefined,
|
||||||
|
|
||||||
|
// TODO: do this better
|
||||||
|
mouse_x: i32 = 0,
|
||||||
|
mouse_y: i32 = 0,
|
||||||
|
mouse_scroll: f32 = 0,
|
||||||
|
mouse_released: bool = false,
|
||||||
|
mouse_hovering_clickable: bool = false,
|
||||||
|
|
||||||
|
pub fn init(allocator: std.mem.Allocator, font20: raylib.Font, font10: raylib.Font) Self {
|
||||||
|
return .{
|
||||||
|
.box_allocator = allocator,
|
||||||
|
.frame_allocators = .{
|
||||||
|
std.heap.ArenaAllocator.init(allocator),
|
||||||
|
std.heap.ArenaAllocator.init(allocator),
|
||||||
|
},
|
||||||
|
.current_style = std.ArrayList(UI_Style).init(allocator),
|
||||||
|
.font20 = font20,
|
||||||
|
.font10 = font10,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub const UI_Layout = union(enum) {
|
fn frame_allocator(self: *Self) std.mem.Allocator {
|
||||||
fitToText,
|
return self.frame_allocators[self.current_frame_allocator].allocator();
|
||||||
fitToChildren,
|
}
|
||||||
fill,
|
|
||||||
percentOfParent: Vec2,
|
|
||||||
exactSize: Vec2,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const UI_Direction = enum {
|
fn swap_frame_allocators(self: *Self) void {
|
||||||
leftToRight,
|
self.current_frame_allocator = (self.current_frame_allocator + 1) % self.frame_allocators.len;
|
||||||
rightToLeft,
|
}
|
||||||
topToBottom,
|
|
||||||
bottomToTop,
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: don't couple to raylib
|
pub fn NewFrame(self: *Self, width: i32, height: i32, mouse_x: i32, mouse_y: i32, mouse_scroll: f32, mouse_released: bool) !void {
|
||||||
pub const UI_Style = struct {
|
self.current_box = self.root_box;
|
||||||
color: raylib.Color = raylib.LIGHTGRAY,
|
self.pushing_box = false;
|
||||||
hover_color: raylib.Color = raylib.WHITE,
|
self.popping_box = false;
|
||||||
border_color: raylib.Color = raylib.DARKGRAY,
|
self.mouse_hovering_clickable = false;
|
||||||
|
self.current_style.clearRetainingCapacity();
|
||||||
|
// TODO: really shouldn't be necessary?
|
||||||
|
try self.current_style.append(.{});
|
||||||
|
|
||||||
text_color: raylib.Color = raylib.BLACK,
|
if (self.root_box) |box| {
|
||||||
text_size: i32 = 20,
|
box.computed_size.x = @floatFromInt(width);
|
||||||
text_padding: i32 = 8,
|
box.computed_size.y = @floatFromInt(height);
|
||||||
};
|
}
|
||||||
|
|
||||||
pub const Vec2 = struct {
|
self.mouse_x = mouse_x;
|
||||||
x: f32,
|
self.mouse_y = mouse_y;
|
||||||
y: f32,
|
self.mouse_scroll = mouse_scroll;
|
||||||
};
|
self.mouse_released = mouse_released;
|
||||||
|
|
||||||
pub const UI_Box = struct {
|
//std.debug.print("frame allocator[0] capacity: {d} - ", .{self.frame_allocators[0].queryCapacity()});
|
||||||
/// the first child
|
//std.debug.print("frame allocator[1] capacity: {d}\n", .{self.frame_allocators[1].queryCapacity()});
|
||||||
first: ?*UI_Box,
|
_ = self.frame_allocators[self.current_frame_allocator].reset(.{ .retain_with_limit = 500_000 });
|
||||||
last: ?*UI_Box,
|
}
|
||||||
|
|
||||||
/// the next sibling
|
|
||||||
next: ?*UI_Box,
|
|
||||||
prev: ?*UI_Box,
|
|
||||||
|
|
||||||
parent: ?*UI_Box,
|
|
||||||
|
|
||||||
/// the assigned features
|
|
||||||
flags: UI_Flags,
|
|
||||||
direction: UI_Direction,
|
|
||||||
style: UI_Style,
|
|
||||||
layout: UI_Layout,
|
|
||||||
|
|
||||||
/// the label?
|
|
||||||
label: [:0]const u8,
|
|
||||||
|
|
||||||
/// the final computed position and size of this primitive (in pixels)
|
|
||||||
computed_pos: Vec2,
|
|
||||||
computed_size: Vec2,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn CountChildren(box: *UI_Box) u32 {
|
fn CountChildren(box: *UI_Box) u32 {
|
||||||
var count: u32 = 0;
|
var count: u32 = 0;
|
||||||
|
@ -120,58 +118,133 @@ fn CountSiblings(box: *UI_Box) u32 {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn TestBoxHover(box: *UI_Box) bool {
|
fn TestBoxHover(self: *Self, box: *UI_Box) bool {
|
||||||
return @intToFloat(f32, mouse_x) >= box.computed_pos.x and @intToFloat(f32, mouse_x) <= box.computed_pos.x + box.computed_size.x and @intToFloat(f32, mouse_y) >= box.computed_pos.y and @intToFloat(f32, mouse_y) <= box.computed_pos.y + box.computed_size.y;
|
return @as(f32, @floatFromInt(self.mouse_x)) >= box.computed_pos.x and @as(f32, @floatFromInt(self.mouse_x)) <= box.computed_pos.x + box.computed_size.x and @as(f32, @floatFromInt(self.mouse_y)) >= box.computed_pos.y and @as(f32, @floatFromInt(self.mouse_y)) <= box.computed_pos.y + box.computed_size.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn TestBoxClick(box: *UI_Box) bool {
|
fn TestBoxClick(self: *Self, box: *UI_Box) bool {
|
||||||
return mouse_released and TestBoxHover(box);
|
return self.mouse_released and self.TestBoxHover(box);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn DeleteBoxChildren(box: *UI_Box, should_destroy: bool) void {
|
fn ScrollBox(self: *Self, box: *UI_Box) void {
|
||||||
if (box.first) |child| {
|
if (self.TestBoxHover(box)) {
|
||||||
DeleteBoxChildren(child, true);
|
if (box.scroll_top == null) {
|
||||||
} else if (should_destroy) {
|
box.scroll_top = box.first;
|
||||||
box_allocator.destroy(box);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (box.scroll_top) |top| {
|
||||||
|
if ((self.mouse_scroll > 0 and top.prev != null) or (self.mouse_scroll < 0 and top.next != null) or box.scroll_fract != 0) {
|
||||||
|
box.scroll_fract -= self.mouse_scroll * scroll_speed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const n = std.math.modf(box.scroll_fract);
|
||||||
|
box.scroll_fract = n.fpart;
|
||||||
|
|
||||||
|
if (n.ipart > 0) {
|
||||||
|
var index = n.ipart;
|
||||||
|
var child = box.scroll_top;
|
||||||
|
while (child) |c| {
|
||||||
|
if (index <= 0) break;
|
||||||
|
|
||||||
|
if (c.next) |next| {
|
||||||
|
child = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
index -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
box.scroll_top = child;
|
||||||
|
} else if (box.scroll_fract < 0.0) {
|
||||||
|
box.scroll_fract = 1 + box.scroll_fract;
|
||||||
|
|
||||||
|
var index = -n.ipart;
|
||||||
|
var child = box.scroll_top;
|
||||||
|
while (child) |c| {
|
||||||
|
if (c.prev) |prev| {
|
||||||
|
child = prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index <= 0) break;
|
||||||
|
|
||||||
|
index -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
box.scroll_top = child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn TestBoxInteraction(self: *Self, box: *UI_Box) bool {
|
||||||
|
if (box.flags.clickable) {
|
||||||
|
return self.TestBoxClick(box);
|
||||||
|
}
|
||||||
|
if (box.flags.scrollable) {
|
||||||
|
self.ScrollBox(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn DeleteBoxChildren(self: *Self, box: *UI_Box, should_destroy: bool) void {
|
||||||
|
var child = box.first;
|
||||||
|
while (child) |c| {
|
||||||
|
child = c.next;
|
||||||
|
self.DeleteBoxChildren(c, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_destroy) {
|
||||||
|
self.box_allocator.destroy(box);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn MakeBox(self: *Self, str: []const u8, flags: UI_Flags, direction: UI_Direction, layout: UI_Layout) anyerror!bool {
|
||||||
|
const label = try std.fmt.allocPrintZ(self.frame_allocator(), "{s}", .{str});
|
||||||
|
return try self._MakeBox(label, flags, direction, layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn PushBox(self: *Self, str: []const u8, flags: UI_Flags, direction: UI_Direction, layout: UI_Layout) anyerror!bool {
|
||||||
|
const label = try std.fmt.allocPrintZ(self.frame_allocator(), "{s}", .{str});
|
||||||
|
return try self._PushBox(label, flags, direction, layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove all footguns by compressing code
|
// TODO: remove all footguns by compressing code
|
||||||
pub fn MakeBox(label: [:0]const u8, flags: UI_Flags, direction: UI_Direction, layout: UI_Layout) anyerror!bool {
|
fn _MakeBox(self: *Self, label: [:0]const u8, flags: UI_Flags, direction: UI_Direction, layout: UI_Layout) anyerror!bool {
|
||||||
//std.debug.print("making box '{s}'...", .{label});
|
//std.debug.print("making box '{s}'...", .{label});
|
||||||
|
|
||||||
// TODO: Please remove this state machine, there should be a way to do it without it
|
// TODO: Please remove this state machine, there should be a way to do it without it
|
||||||
popping_box = false;
|
self.popping_box = false;
|
||||||
|
|
||||||
if (pushing_box) {
|
if (self.pushing_box) {
|
||||||
const box = try PushBox(label, flags, direction, layout);
|
const box = try self._PushBox(label, flags, direction, layout);
|
||||||
pushing_box = false;
|
self.pushing_box = false;
|
||||||
|
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_box) |box| {
|
if (self.current_box) |box| {
|
||||||
if (box.next) |next| {
|
if (box.next) |next| {
|
||||||
// Attempt to re-use cache
|
// Attempt to re-use cache
|
||||||
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;
|
next.direction = direction;
|
||||||
|
next.label = @constCast(label);
|
||||||
if (next.parent) |parent| {
|
if (next.parent) |parent| {
|
||||||
parent.last = next;
|
parent.last = next;
|
||||||
}
|
}
|
||||||
current_box = next;
|
self.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);
|
self.DeleteBoxChildren(next, false);
|
||||||
|
|
||||||
next.* = UI_Box{
|
next.* = UI_Box{
|
||||||
.label = label,
|
.label = @constCast(label),
|
||||||
.flags = flags,
|
.flags = flags,
|
||||||
.direction = direction,
|
.direction = direction,
|
||||||
.style = current_style.getLast(),
|
.style = self.current_style.getLast(),
|
||||||
.layout = layout,
|
.layout = layout,
|
||||||
|
|
||||||
.first = null,
|
.first = null,
|
||||||
|
@ -183,20 +256,20 @@ pub fn MakeBox(label: [:0]const u8, flags: UI_Flags, direction: UI_Direction, la
|
||||||
.computed_size = Vec2{ .x = 0, .y = 0 },
|
.computed_size = Vec2{ .x = 0, .y = 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
current_box = next;
|
self.current_box = next;
|
||||||
if (next.parent) |parent| {
|
if (next.parent) |parent| {
|
||||||
parent.last = next;
|
parent.last = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No existing cache, create new box
|
// No existing cache, create new box
|
||||||
std.debug.print("make_box: allocating new box: {s}\n", .{label});
|
//std.debug.print("make_box: allocating new box: {s}\n", .{label});
|
||||||
var new_box = try box_allocator.create(UI_Box);
|
var new_box = try self.box_allocator.create(UI_Box);
|
||||||
new_box.* = UI_Box{
|
new_box.* = UI_Box{
|
||||||
.label = label,
|
.label = @constCast(label),
|
||||||
.flags = flags,
|
.flags = flags,
|
||||||
.direction = direction,
|
.direction = direction,
|
||||||
.style = current_style.getLast(),
|
.style = self.current_style.getLast(),
|
||||||
.layout = layout,
|
.layout = layout,
|
||||||
|
|
||||||
.first = null,
|
.first = null,
|
||||||
|
@ -209,19 +282,19 @@ pub fn MakeBox(label: [:0]const u8, flags: UI_Flags, direction: UI_Direction, la
|
||||||
};
|
};
|
||||||
|
|
||||||
box.next = new_box;
|
box.next = new_box;
|
||||||
current_box = new_box;
|
self.current_box = new_box;
|
||||||
if (new_box.parent) |parent| {
|
if (new_box.parent) |parent| {
|
||||||
parent.last = new_box;
|
parent.last = new_box;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
std.debug.print("make_box: allocating new box: {s}\n", .{label});
|
//std.debug.print("make_box: allocating new box: {s}\n", .{label});
|
||||||
var new_box = try box_allocator.create(UI_Box);
|
var new_box = try self.box_allocator.create(UI_Box);
|
||||||
new_box.* = UI_Box{
|
new_box.* = UI_Box{
|
||||||
.label = label,
|
.label = @constCast(label),
|
||||||
.flags = flags,
|
.flags = flags,
|
||||||
.direction = direction,
|
.direction = direction,
|
||||||
.style = current_style.getLast(),
|
.style = self.current_style.getLast(),
|
||||||
.layout = layout,
|
.layout = layout,
|
||||||
|
|
||||||
.first = null,
|
.first = null,
|
||||||
|
@ -233,33 +306,31 @@ pub fn MakeBox(label: [:0]const u8, flags: UI_Flags, direction: UI_Direction, la
|
||||||
.computed_size = Vec2{ .x = 0, .y = 0 },
|
.computed_size = Vec2{ .x = 0, .y = 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
current_box = new_box;
|
self.current_box = new_box;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_box) |box| {
|
if (self.current_box) |box| {
|
||||||
if (box.flags.clickable) {
|
return self.TestBoxInteraction(box);
|
||||||
return TestBoxClick(box);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn PushBox(label: [:0]const u8, flags: UI_Flags, direction: UI_Direction, layout: UI_Layout) anyerror!bool {
|
pub fn _PushBox(self: *Self, label: [:0]const u8, flags: UI_Flags, direction: UI_Direction, layout: UI_Layout) anyerror!bool {
|
||||||
//std.debug.print("pushing box '{s}'...", .{label});
|
//std.debug.print("pushing box '{s}'...", .{label});
|
||||||
|
|
||||||
// TODO: Please remove this state machine, there should be a way to do it without it
|
// TODO: Please remove this state machine, there should be a way to do it without it
|
||||||
if (popping_box) {
|
if (self.popping_box) {
|
||||||
const box = try MakeBox(label, flags, direction, layout);
|
const box = try self._MakeBox(label, flags, direction, layout);
|
||||||
pushing_box = true;
|
self.pushing_box = true;
|
||||||
|
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
if (!pushing_box) {
|
if (!self.pushing_box) {
|
||||||
pushing_box = true;
|
self.pushing_box = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_box) |box| {
|
if (self.current_box) |box| {
|
||||||
// Attempt to re-use cache
|
// Attempt to re-use cache
|
||||||
if (box.first) |first| {
|
if (box.first) |first| {
|
||||||
// check if the same
|
// check if the same
|
||||||
|
@ -267,105 +338,106 @@ pub fn PushBox(label: [:0]const u8, flags: UI_Flags, direction: UI_Direction, la
|
||||||
//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;
|
first.direction = direction;
|
||||||
current_box = first;
|
first.label = @constCast(label);
|
||||||
|
self.current_box = first;
|
||||||
|
|
||||||
if (first.parent) |parent| {
|
if (first.parent) |parent| {
|
||||||
parent.last = first;
|
parent.last = first;
|
||||||
}
|
}
|
||||||
} 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);
|
self.DeleteBoxChildren(first, false);
|
||||||
|
|
||||||
first.* = UI_Box{
|
first.* = UI_Box{
|
||||||
.label = label,
|
.label = @constCast(label),
|
||||||
.flags = flags,
|
.flags = flags,
|
||||||
.direction = direction,
|
.direction = direction,
|
||||||
.style = current_style.getLast(),
|
.style = self.current_style.getLast(),
|
||||||
.layout = layout,
|
.layout = layout,
|
||||||
|
|
||||||
.first = null,
|
.first = null,
|
||||||
.last = null,
|
.last = null,
|
||||||
.next = following_sibling,
|
.next = following_sibling,
|
||||||
.prev = null,
|
.prev = null,
|
||||||
.parent = current_box,
|
.parent = self.current_box,
|
||||||
.computed_pos = Vec2{ .x = 0, .y = 0 },
|
.computed_pos = Vec2{ .x = 0, .y = 0 },
|
||||||
.computed_size = Vec2{ .x = 0, .y = 0 },
|
.computed_size = Vec2{ .x = 0, .y = 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
current_box = first;
|
self.current_box = first;
|
||||||
if (first.parent) |parent| {
|
if (first.parent) |parent| {
|
||||||
parent.last = first;
|
parent.last = first;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
std.debug.print("push_box: allocating new box: {s}\n", .{label});
|
//std.debug.print("push_box: allocating new box: {s}\n", .{label});
|
||||||
var new_box = try box_allocator.create(UI_Box);
|
var new_box = try self.box_allocator.create(UI_Box);
|
||||||
new_box.* = UI_Box{
|
new_box.* = UI_Box{
|
||||||
.label = label,
|
.label = @constCast(label),
|
||||||
.flags = flags,
|
.flags = flags,
|
||||||
.direction = direction,
|
.direction = direction,
|
||||||
.style = current_style.getLast(),
|
.style = self.current_style.getLast(),
|
||||||
.layout = layout,
|
.layout = layout,
|
||||||
|
|
||||||
.first = null,
|
.first = null,
|
||||||
.last = null,
|
.last = null,
|
||||||
.next = null,
|
.next = null,
|
||||||
.prev = null,
|
.prev = null,
|
||||||
.parent = current_box,
|
.parent = self.current_box,
|
||||||
.computed_pos = Vec2{ .x = 0, .y = 0 },
|
.computed_pos = Vec2{ .x = 0, .y = 0 },
|
||||||
.computed_size = Vec2{ .x = 0, .y = 0 },
|
.computed_size = Vec2{ .x = 0, .y = 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
box.first = new_box;
|
box.first = new_box;
|
||||||
current_box = new_box;
|
self.current_box = new_box;
|
||||||
if (new_box.parent) |parent| {
|
if (new_box.parent) |parent| {
|
||||||
parent.last = new_box;
|
parent.last = new_box;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pushing_box = false;
|
self.pushing_box = false;
|
||||||
return try MakeBox(label, flags, direction, layout);
|
return try self._MakeBox(label, flags, direction, layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_box) |box| {
|
if (self.current_box) |box| {
|
||||||
if (box.flags.clickable) {
|
return self.TestBoxInteraction(box);
|
||||||
return TestBoxClick(box);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn PopBox() void {
|
pub fn PopBox(self: *Self) void {
|
||||||
//std.debug.print("popping box...", .{});
|
//std.debug.print("popping box...", .{});
|
||||||
if (current_box) |box| {
|
if (self.current_box) |box| {
|
||||||
//if (box.parent) |parent| {
|
//if (box.parent) |parent| {
|
||||||
//current_box = parent.last;
|
//current_box = parent.last;
|
||||||
//return;
|
//return;
|
||||||
//}
|
//}
|
||||||
if (box.parent) |p| {
|
if (box.parent) |p| {
|
||||||
p.last = current_box;
|
p.last = self.current_box;
|
||||||
}
|
}
|
||||||
current_box = box.parent;
|
self.current_box = box.parent;
|
||||||
popping_box = true;
|
self.popping_box = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//std.debug.print("couldn't pop box\n", .{});
|
//std.debug.print("couldn't pop box\n", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn PushStyle(style: UI_Style) !void {
|
pub fn PushStyle(self: *Self, style: UI_Style) !void {
|
||||||
try current_style.append(style);
|
try self.current_style.append(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn PopStyle() void {
|
pub fn PopStyle(self: *Self) void {
|
||||||
_ = current_style.popOrNull();
|
_ = self.current_style.popOrNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn MakeButtonWithLayout(label: [:0]const u8, layout: UI_Layout) !bool {
|
pub fn MakeButtonWithLayout(self: *Self, str: []const u8, layout: UI_Layout) !bool {
|
||||||
return try MakeBox(label, .{
|
// TODO: replace with frame allocator
|
||||||
|
const label = try std.fmt.allocPrintZ(self.frame_allocator(), "{s}", .{str});
|
||||||
|
return try self._MakeBox(label, .{
|
||||||
.clickable = true,
|
.clickable = true,
|
||||||
.hoverable = true,
|
.hoverable = true,
|
||||||
.drawText = true,
|
.drawText = true,
|
||||||
|
@ -373,18 +445,36 @@ pub fn MakeButtonWithLayout(label: [:0]const u8, layout: UI_Layout) !bool {
|
||||||
}, .leftToRight, layout);
|
}, .leftToRight, layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn MakeButton(label: [:0]const u8) !bool {
|
pub fn MakeButton(self: *Self, label: []const u8) !bool {
|
||||||
return try MakeButtonWithLayout(label, .fitToText);
|
return try self.MakeButtonWithLayout(label, .fitToText);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn MakeLabelWithLayout(label: [:0]const u8, layout: UI_Layout) !void {
|
pub fn MakeFormattedLabelWithLayout(self: *Self, comptime str: []const u8, args: anytype, layout: UI_Layout) !void {
|
||||||
_ = try MakeBox(label, .{
|
// TODO: replace with frame allocator
|
||||||
|
const label = try std.fmt.allocPrintZ(self.frame_allocator(), str, args);
|
||||||
|
_ = try self._MakeBox(label, .{
|
||||||
.drawText = true,
|
.drawText = true,
|
||||||
}, .leftToRight, layout);
|
}, .leftToRight, layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn MakeLabel(label: [:0]const u8) !void {
|
pub fn MakeLabelWithLayout(self: *Self, str: []const u8, layout: UI_Layout) !void {
|
||||||
_ = try MakeLabelWithLayout(label, .fitToText);
|
// TODO: replace with frame allocator
|
||||||
|
const label = try std.fmt.allocPrintZ(self.frame_allocator(), "{s}", .{str});
|
||||||
|
_ = try self._MakeBox(label, .{
|
||||||
|
.drawText = true,
|
||||||
|
}, .leftToRight, layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn MakeLabelInt(self: *Self, value: anytype) !void {
|
||||||
|
// TODO: replace with frame allocator
|
||||||
|
const label = try std.fmt.allocPrintZ(self.frame_allocator(), "{d}", .{value});
|
||||||
|
_ = try self.MakeLabelWithLayout(label, .fitToText);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn MakeLabel(self: *Self, str: []const u8) !void {
|
||||||
|
// TODO: replace with frame allocator
|
||||||
|
const label = try std.fmt.allocPrintZ(self.frame_allocator(), "{s}", .{str});
|
||||||
|
_ = try self.MakeLabelWithLayout(label, .fitToText);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ComputeChildrenSize(box: *UI_Box) Vec2 {
|
fn ComputeChildrenSize(box: *UI_Box) Vec2 {
|
||||||
|
@ -432,10 +522,12 @@ fn ComputeSiblingSize(box: *UI_Box) Vec2 {
|
||||||
// get siblings size so we know to big to get
|
// get siblings size so we know to big to get
|
||||||
var total_sibling_size = Vec2{ .x = 0, .y = 0 };
|
var total_sibling_size = Vec2{ .x = 0, .y = 0 };
|
||||||
var n = box.next;
|
var n = box.next;
|
||||||
|
|
||||||
|
const direction = if (box.parent) |p| p.direction else box.direction;
|
||||||
while (n) |next| {
|
while (n) |next| {
|
||||||
const sibling_size = ComputeLayout(next);
|
const sibling_size = ComputeLayout(next);
|
||||||
|
|
||||||
switch (box.direction) {
|
switch (direction) {
|
||||||
.leftToRight => {
|
.leftToRight => {
|
||||||
total_sibling_size.x += sibling_size.x;
|
total_sibling_size.x += sibling_size.x;
|
||||||
if (sibling_size.y > total_sibling_size.y) {
|
if (sibling_size.y > total_sibling_size.y) {
|
||||||
|
@ -480,42 +572,54 @@ pub fn ComputeLayout(box: *UI_Box) Vec2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var compute_children = blk: {
|
||||||
switch (box.layout) {
|
switch (box.layout) {
|
||||||
.fitToText => {
|
.fitToText => {
|
||||||
box.computed_size = Vec2{
|
box.computed_size = Vec2{
|
||||||
.x = @intToFloat(f32, raylib.MeasureText(box.label, box.style.text_size) + box.style.text_padding * 2),
|
.x = @floatFromInt(raylib.MeasureText(box.label, box.style.text_size) + box.style.text_padding * 2),
|
||||||
.y = @intToFloat(f32, box.style.text_size + box.style.text_padding * 2),
|
.y = @floatFromInt(box.style.text_size + box.style.text_padding * 2),
|
||||||
};
|
};
|
||||||
|
|
||||||
_ = ComputeChildrenSize(box);
|
//_ = ComputeChildrenSize(box);
|
||||||
|
break :blk true;
|
||||||
},
|
},
|
||||||
.fitToChildren => {
|
.fitToChildren => {
|
||||||
// TODO: chicken before the egg :sigh:
|
// TODO: chicken before the egg :sigh:
|
||||||
box.computed_size = ComputeChildrenSize(box);
|
box.computed_size = ComputeChildrenSize(box);
|
||||||
//box.computed_size = total_size;
|
//box.computed_size = total_size;
|
||||||
|
|
||||||
|
break :blk false;
|
||||||
},
|
},
|
||||||
.fill => {
|
.fill => {
|
||||||
const total_sibling_size = ComputeSiblingSize(box);
|
const total_sibling_size = ComputeSiblingSize(box);
|
||||||
|
|
||||||
if (box.parent) |p| {
|
if (box.parent) |p| {
|
||||||
|
if (p.layout == .fitToChildren) {
|
||||||
|
//box.layout = .fitToText;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, "GridContainer", box.label)) {
|
||||||
|
const calculated_size = p.computed_size.y - total_sibling_size.y - (box.computed_pos.y - p.computed_pos.y);
|
||||||
|
std.debug.print("Grid sibling size: {d}, {d}, '{s}'_height: {d}, calculated height: {d}\n", .{ total_sibling_size.x, total_sibling_size.y, p.label, p.computed_size.y, calculated_size });
|
||||||
|
}
|
||||||
|
|
||||||
box.computed_size = Vec2{
|
box.computed_size = Vec2{
|
||||||
.x = switch (p.direction) {
|
.x = switch (p.direction) {
|
||||||
.leftToRight => p.computed_size.x - total_sibling_size.x - box.computed_pos.x,
|
.leftToRight => p.computed_size.x - total_sibling_size.x - (box.computed_pos.x - p.computed_pos.x),
|
||||||
.topToBottom => if (total_sibling_size.x == 0) p.computed_size.x else total_sibling_size.x,
|
.topToBottom => if (total_sibling_size.x == 0) p.computed_size.x else total_sibling_size.x,
|
||||||
|
//.topToBottom => 0, //p.computed_size.x,
|
||||||
.rightToLeft, .bottomToTop => unreachable,
|
.rightToLeft, .bottomToTop => unreachable,
|
||||||
},
|
},
|
||||||
.y = switch (p.direction) {
|
.y = switch (p.direction) {
|
||||||
.leftToRight => if (total_sibling_size.y == 0) p.computed_size.y else total_sibling_size.y,
|
.leftToRight => if (total_sibling_size.y == 0) p.computed_size.y else total_sibling_size.y,
|
||||||
.topToBottom => p.computed_size.y - total_sibling_size.y - box.computed_pos.y,
|
//.leftToRight => 0, //p.computed_size.y,
|
||||||
|
.topToBottom => p.computed_size.y - total_sibling_size.y - (box.computed_pos.y - p.computed_pos.y),
|
||||||
.rightToLeft, .bottomToTop => unreachable,
|
.rightToLeft, .bottomToTop => unreachable,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
} else {
|
|
||||||
// TODO: somehow need to get these values
|
|
||||||
//box.computed_size = Vec2{ .x = 1280, .y = 720 };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = ComputeChildrenSize(box);
|
//_ = ComputeChildrenSize(box);
|
||||||
|
break :blk true;
|
||||||
},
|
},
|
||||||
.percentOfParent => |size| {
|
.percentOfParent => |size| {
|
||||||
//const total_sibling_size = ComputeSiblingSize(box);
|
//const total_sibling_size = ComputeSiblingSize(box);
|
||||||
|
@ -525,71 +629,262 @@ pub fn ComputeLayout(box: *UI_Box) Vec2 {
|
||||||
box.computed_size = Vec2{
|
box.computed_size = Vec2{
|
||||||
.x = switch (p.direction) { //
|
.x = switch (p.direction) { //
|
||||||
.leftToRight => p.computed_size.x * size.x,
|
.leftToRight => p.computed_size.x * size.x,
|
||||||
.topToBottom => p.computed_size.x, //if (total_sibling_size.x == 0) @intToFloat(f32, raylib.MeasureText(box.label, box.style.text_size) + box.style.text_padding * 2) else total_sibling_size.x,
|
.topToBottom => p.computed_size.x, //if (total_sibling_size.x == 0) @floatFromInt(raylib.MeasureText(box.label, box.style.text_size) + box.style.text_padding * 2) else total_sibling_size.x,
|
||||||
.rightToLeft, .bottomToTop => unreachable,
|
.rightToLeft, .bottomToTop => unreachable,
|
||||||
},
|
},
|
||||||
.y = switch (p.direction) {
|
.y = switch (p.direction) {
|
||||||
.leftToRight => @intToFloat(f32, box.style.text_size + box.style.text_padding * 2),
|
.leftToRight => @floatFromInt(box.style.text_size + box.style.text_padding * 2),
|
||||||
.topToBottom => p.computed_size.y * size.y,
|
.topToBottom => p.computed_size.y * size.y,
|
||||||
.rightToLeft, .bottomToTop => unreachable,
|
.rightToLeft, .bottomToTop => unreachable,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = ComputeChildrenSize(box);
|
//_ = ComputeChildrenSize(box);
|
||||||
|
break :blk true;
|
||||||
},
|
},
|
||||||
.exactSize => |_| unreachable,
|
.exactSize => |size| {
|
||||||
|
box.computed_size = size;
|
||||||
|
break :blk true;
|
||||||
|
},
|
||||||
|
.floating => |layout| {
|
||||||
|
box.computed_pos = .{ .x = 0, .y = 0 };
|
||||||
|
switch (layout) {
|
||||||
|
.fitToText => {
|
||||||
|
box.computed_size = Vec2{
|
||||||
|
.x = @floatFromInt(raylib.MeasureText(box.label, box.style.text_size) + box.style.text_padding * 2),
|
||||||
|
.y = @floatFromInt(box.style.text_size + box.style.text_padding * 2),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
.fitToChildren => {
|
||||||
|
box.computed_size = ComputeChildrenSize(box);
|
||||||
|
},
|
||||||
|
.exactSize => |size| {
|
||||||
|
box.computed_size = size;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_ = ComputeChildrenSize(box);
|
||||||
|
|
||||||
|
return .{ .x = 0, .y = 0 };
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (box.parent) |p| {
|
||||||
|
if (p.scroll_top) |top| {
|
||||||
|
if (box == top) {
|
||||||
|
// TODO: switch on direction
|
||||||
|
box.computed_pos.x = p.computed_pos.x;
|
||||||
|
box.computed_pos.y = p.computed_pos.y - p.scroll_fract * box.computed_size.y;
|
||||||
|
}
|
||||||
|
// TODO: figure check and egg problem
|
||||||
|
//_ = ComputeChildrenSize(box);
|
||||||
|
compute_children = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this is what is causing the columns to shrink, something to do with the parent size changing between the previous call in .fitToChildren and this one
|
||||||
|
if (compute_children) {
|
||||||
|
_ = ComputeChildrenSize(box);
|
||||||
}
|
}
|
||||||
|
|
||||||
return box.computed_size;
|
return box.computed_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn DrawUI(box: *UI_Box) void {
|
pub fn DrawUI(self: *Self, box: *UI_Box) void {
|
||||||
const is_hovering = TestBoxHover(box);
|
const is_hovering = self.TestBoxHover(box);
|
||||||
if (box.flags.clickable and is_hovering) {
|
if (box.flags.clickable and is_hovering) {
|
||||||
mouse_hovering_clickable = true;
|
self.mouse_hovering_clickable = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pos_x: i32 = @intFromFloat(box.computed_pos.x);
|
||||||
|
const pos_y: i32 = @intFromFloat(box.computed_pos.y);
|
||||||
|
|
||||||
|
if (box.layout != .floating) {
|
||||||
|
if (box.parent) |p| {
|
||||||
|
const parent_pos_x: i32 = @intFromFloat(p.computed_pos.x);
|
||||||
|
const parent_pos_y: i32 = @intFromFloat(p.computed_pos.y);
|
||||||
|
const parent_size_x: i32 = @intFromFloat(p.computed_size.x);
|
||||||
|
const parent_size_y: i32 = @intFromFloat(p.computed_size.y);
|
||||||
|
|
||||||
|
raylib.BeginScissorMode(parent_pos_x, parent_pos_y, parent_size_x, parent_size_y);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (box.flags.drawBackground) {
|
if (box.flags.drawBackground) {
|
||||||
const color = if (box.flags.hoverable and is_hovering) box.style.hover_color else box.style.color;
|
const color = if (box.flags.hoverable and is_hovering) box.style.hover_color else box.style.color;
|
||||||
|
|
||||||
raylib.DrawRectangle( //
|
raylib.DrawRectangle( //
|
||||||
@floatToInt(i32, box.computed_pos.x), //
|
pos_x, //
|
||||||
@floatToInt(i32, box.computed_pos.y), //
|
pos_y, //
|
||||||
@floatToInt(i32, box.computed_size.x), //
|
@intFromFloat(box.computed_size.x), //
|
||||||
@floatToInt(i32, box.computed_size.y), //
|
@intFromFloat(box.computed_size.y), //
|
||||||
color //
|
color //
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (box.flags.drawBorder) {
|
if (box.flags.drawBorder) {
|
||||||
raylib.DrawRectangleLines( //
|
raylib.DrawRectangleLines( //
|
||||||
@floatToInt(i32, box.computed_pos.x), //
|
pos_x, //
|
||||||
@floatToInt(i32, box.computed_pos.y), //
|
pos_y, //
|
||||||
@floatToInt(i32, box.computed_size.x), //
|
@intFromFloat(box.computed_size.x), //
|
||||||
@floatToInt(i32, box.computed_size.y), //
|
@intFromFloat(box.computed_size.y), //
|
||||||
box.style.border_color //
|
box.style.border_color //
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint)
|
||||||
if (box.flags.drawText) {
|
if (box.flags.drawText) {
|
||||||
raylib.DrawText( //
|
var bb: f32 = 0;
|
||||||
|
var color: raylib.Color = box.style.text_color;
|
||||||
|
if (box.parent) |p| {
|
||||||
|
if (p.scroll_top != null and p.scroll_top == box) {
|
||||||
|
bb = 36;
|
||||||
|
color = raylib.RED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
raylib.DrawTextEx( //
|
||||||
|
if (box.style.text_size == 20) self.font20 else if (box.style.text_size == 12) self.font10 else self.font10, //
|
||||||
box.label, //
|
box.label, //
|
||||||
@floatToInt(i32, box.computed_pos.x) + box.style.text_padding, //
|
.{
|
||||||
@floatToInt(i32, box.computed_pos.y) + box.style.text_padding, //
|
.x = box.computed_pos.x + @as(f32, @floatFromInt(box.style.text_padding)), //
|
||||||
box.style.text_size, //
|
.y = box.computed_pos.y - bb + @as(f32, @floatFromInt(box.style.text_padding)), //
|
||||||
box.style.text_color //
|
}, //
|
||||||
|
@as(f32, @floatFromInt(box.style.text_size)), //
|
||||||
|
1.0, color //
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (box.layout != .floating) {
|
||||||
|
if (box.parent) |_| {
|
||||||
|
raylib.EndScissorMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// draw children
|
// draw children
|
||||||
const children = CountChildren(box);
|
const children = CountChildren(box);
|
||||||
if (children > 0) {
|
if (children > 0) {
|
||||||
var child = box.first;
|
var child = blk: {
|
||||||
|
if (box.flags.scrollable) {
|
||||||
|
if (box.scroll_top) |top| {
|
||||||
|
break :blk top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break :blk box.first;
|
||||||
|
};
|
||||||
|
|
||||||
|
var child_size: f32 = 0;
|
||||||
|
// TODO: replace with non-raylib function, also figure out why this doesn't clip text drawn with `DrawText`
|
||||||
while (child) |c| {
|
while (child) |c| {
|
||||||
DrawUI(c);
|
self.DrawUI(c);
|
||||||
|
|
||||||
if (child == box.last) break;
|
if (child == box.last) break;
|
||||||
|
|
||||||
|
if (box.flags.scrollable) {
|
||||||
|
switch (box.direction) {
|
||||||
|
.leftToRight => {
|
||||||
|
child_size += c.computed_size.x;
|
||||||
|
// TODO: don't multiply this by two, use the last child size or something
|
||||||
|
if (child_size > box.computed_size.x + c.computed_size.x) break;
|
||||||
|
},
|
||||||
|
.rightToLeft, .bottomToTop => unreachable,
|
||||||
|
.topToBottom => {
|
||||||
|
child_size += c.computed_size.y;
|
||||||
|
// TODO: don't multiply this by two, use the last child size or something
|
||||||
|
if (child_size > box.computed_size.y + c.computed_size.y) break;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
child = c.next;
|
child = c.next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn Draw(self: *Self) void {
|
||||||
|
if (self.root_box) |box| {
|
||||||
|
_ = ComputeLayout(box);
|
||||||
|
self.DrawUI(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.swap_frame_allocators();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const UI_Flags = packed struct {
|
||||||
|
clickable: bool = false,
|
||||||
|
hoverable: bool = false,
|
||||||
|
scrollable: bool = false,
|
||||||
|
drawText: bool = false,
|
||||||
|
drawBorder: bool = false,
|
||||||
|
drawBackground: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const FloatingLayout = union(enum) {
|
||||||
|
fitToText,
|
||||||
|
fitToChildren,
|
||||||
|
exactSize: Vec2,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const UI_Layout = union(enum) {
|
||||||
|
fitToText,
|
||||||
|
fitToChildren,
|
||||||
|
fill,
|
||||||
|
percentOfParent: Vec2,
|
||||||
|
exactSize: Vec2,
|
||||||
|
floating: FloatingLayout,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const UI_Direction = enum {
|
||||||
|
leftToRight,
|
||||||
|
rightToLeft,
|
||||||
|
topToBottom,
|
||||||
|
bottomToTop,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: don't couple to raylib
|
||||||
|
pub const UI_Style = struct {
|
||||||
|
color: raylib.Color = raylib.LIGHTGRAY,
|
||||||
|
hover_color: raylib.Color = raylib.WHITE,
|
||||||
|
border_color: raylib.Color = raylib.DARKGRAY,
|
||||||
|
|
||||||
|
text_color: raylib.Color = raylib.BLACK,
|
||||||
|
text_size: i32 = 20,
|
||||||
|
text_padding: i32 = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Vec2 = struct {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const UI_Box = struct {
|
||||||
|
/// the first child
|
||||||
|
first: ?*UI_Box,
|
||||||
|
last: ?*UI_Box,
|
||||||
|
|
||||||
|
/// the next sibling
|
||||||
|
next: ?*UI_Box,
|
||||||
|
prev: ?*UI_Box,
|
||||||
|
|
||||||
|
parent: ?*UI_Box,
|
||||||
|
|
||||||
|
/// the assigned features
|
||||||
|
flags: UI_Flags,
|
||||||
|
direction: UI_Direction,
|
||||||
|
style: UI_Style,
|
||||||
|
layout: UI_Layout,
|
||||||
|
|
||||||
|
/// the label
|
||||||
|
label: [:0]u8,
|
||||||
|
|
||||||
|
/// the final computed position and size of this primitive (in pixels)
|
||||||
|
computed_pos: Vec2,
|
||||||
|
computed_size: Vec2,
|
||||||
|
|
||||||
|
// whether or not this primitive is currently being interacted with
|
||||||
|
//active: bool = false,
|
||||||
|
// whether or not this primitive is *about* to be interacted with
|
||||||
|
//hot: bool = false,
|
||||||
|
|
||||||
|
// specific scrollable settings
|
||||||
|
scroll_fract: f32 = 0,
|
||||||
|
scroll_top: ?*UI_Box = null,
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue