WIP: container rendering for borders + container element rendering

This commit is contained in:
2025-02-03 19:55:33 +01:00
parent 0bf79dc236
commit 2bfacc0e98
7 changed files with 189 additions and 219 deletions

View File

@@ -10,8 +10,10 @@ const log = std.log.scoped(.container);
/// Border configuration struct
pub const Border = struct {
pub const rounded_border: [6]u21 = .{ '╭', '─', '╮', '│', '╰', '╯' };
pub const squared_border: [6]u21 = .{ '┌', '─', '┐', '│', '└', '┘' };
/// Color to use for the border
color: Color = .default,
color: Color = .{ .ansi = .reset },
/// Configure the corner type to be used for the border
corners: enum(u1) {
squared,
@@ -27,13 +29,55 @@ pub const Border = struct {
/// Configure separator borders between child element to added to the layout
separator: struct {
enabled: bool = false,
color: Color = .default,
color: Color = .{ .ansi = .reset },
line: enum {
line,
dotted,
// TODO: add more variations which could be used for the separator
} = .line,
} = .{},
// NOTE: caller owns `cells` slice and ensures that `cells.len == size.cols * size.rows`
pub fn contents(this: @This(), cells: []Cell, size: Size) void {
std.debug.assert(cells.len == size.cols * size.rows);
const frame = switch (this.corners) {
.rounded => Border.rounded_border,
.squared => Border.squared_border,
};
std.debug.assert(frame.len == 6);
// TODO: respect color configuration
// TODO: respect sides configuration
// render top and bottom border
for (0..size.cols) |col| {
const last_row = (size.rows - 1) * size.cols;
if (col == 0) {
// top left corner
cells[col].cp = frame[0];
// bottom left corner
cells[last_row + col].cp = frame[4];
} else if (col == size.cols - 1) {
// top right corner
cells[col].cp = frame[2];
// bottom left corner
cells[last_row + col].cp = frame[5];
} else {
// top side
cells[col].cp = frame[1];
// bottom side
cells[last_row + col].cp = frame[1];
}
// TODO: fix rendering of styling?
cells[col].style = .{ .fg = .{ .ansi = .red }, .attributes = &.{} };
cells[last_row + col].style = .{ .fg = .{ .ansi = .red }, .attributes = &.{} };
}
// render left and right border
for (1..size.rows - 1) |row| {
const idx = (row * size.cols);
cells[idx].cp = frame[3]; // left
cells[idx + size.cols - 1].cp = frame[3]; // right
}
}
};
/// Rectangle configuration struct
@@ -41,7 +85,7 @@ pub const Rectangle = struct {
/// `Color` to use to fill the `Rectangle` with
/// NOTE: used as background color when rendering! such that it renders the
/// children accordingly without removing the coloring of the `Rectangle`
fill: Color = .default,
fill: Color = .{ .ansi = .reset },
/// Configure the corners of the `Rectangle`
corners: enum(u1) {
squared,
@@ -105,6 +149,7 @@ pub fn Container(comptime Event: type) type {
@compileError("Provided user event `Event` for `Container(comptime Event: type)`");
}
return struct {
allocator: std.mem.Allocator,
size: Size,
properties: Properties,
elements: std.ArrayList(@This()),
@@ -121,6 +166,7 @@ pub fn Container(comptime Event: type) type {
pub fn init(allocator: std.mem.Allocator, properties: Properties) !@This() {
return .{
.allocator = allocator,
.size = .{ .cols = 0, .rows = 0 },
.properties = properties,
.elements = std.ArrayList(@This()).init(allocator),
@@ -197,7 +243,25 @@ pub fn Container(comptime Event: type) type {
offset += rows;
},
}
// TODO; adjust size according to the layout of the `Container`
// border resizing
const sides = this.properties.border.sides;
if (sides.top) {
element_size.anchor.row += 1;
element_size.rows -|= 1;
}
if (sides.bottom) {
element_size.rows -|= 1;
}
if (sides.left) {
element_size.anchor.col += 1;
element_size.cols -|= 1;
}
if (sides.right) {
element_size.cols -|= 1;
}
// TODO: adjust size according to the layout of the `Container`
if (element.handle(.{ .resize = element_size })) |e| {
_ = e;
}
@@ -219,8 +283,10 @@ pub fn Container(comptime Event: type) type {
pub fn contents(this: *const @This()) []const Cell {
// TODO: use the size and the corresponding contents to determine what should be show in form of a `Cell` array
_ = this;
return &[0]Cell{};
const cells = this.allocator.alloc(Cell, this.size.cols * this.size.rows) catch @panic("Container::contents: Out of memory.");
@memset(cells, .{}); // reset all cells
this.properties.border.contents(cells, this.size);
return cells;
}
};
}