feat(layout): Framing and Padding implementation
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 33s

The implementation of `Layout.Framing` however does not use the renderer
and instead writes directly to the terminal (which it should not and
instead use the renderer). The example has been enhanced with both usage
of `Layout.Framing` and `Layout.Padding` Layouts to showcase the
`Layout` implementations.
This commit is contained in:
2024-11-10 22:21:40 +01:00
parent 6edbbe285c
commit 4c67a86c27
4 changed files with 108 additions and 33 deletions

View File

@@ -50,8 +50,23 @@ pub fn Layout(comptime Event: type, comptime Renderer: type) type {
switch (event) {
.resize => |size| {
this.size = size;
log.debug("Event .resize: {{ .anchor = {{ .col = {d}, .row = {d} }}, .cols = {d}, .rows = {d} }}", .{
size.anchor.col,
size.anchor.row,
size.cols,
size.rows,
});
// adjust size according to the containing elements
const sub_event = event;
const sub_event: Event = .{
.resize = .{
.anchor = .{
.col = size.anchor.col + 1,
.row = size.anchor.row + 1,
},
.cols = size.cols -| 2,
.rows = size.rows -| 2,
},
};
switch ((&this.element).*) {
.layout => |*layout| {
const events = try layout.handle(sub_event);
@@ -82,15 +97,52 @@ pub fn Layout(comptime Event: type, comptime Renderer: type) type {
}
pub fn render(this: *@This(), renderer: Renderer) !void {
// TODO: padding contents accordingly
// TODO: render frame around the "1" padding
try renderer.clear(this.size);
// NOTE: round corners: .{ "╭", "─", "╮", "│", "╯", "╰" }
// NOTE: hard corners: .{ "┌", "─", "┐", "│", "┘", "└" }
// render top: +---+
try terminal.setCursorPosition(this.size.anchor);
_ = try terminal.write("");
for (0..this.size.cols -| 2) |_| {
_ = try terminal.write("");
}
_ = try terminal.write("");
// render left: |
for (1..this.size.rows -| 1) |r| {
const row: u16 = @truncate(r);
try terminal.setCursorPosition(.{
.col = this.size.anchor.col,
.row = this.size.anchor.row + row,
});
_ = try terminal.write("");
}
// render right: |
for (1..this.size.rows -| 1) |r| {
const row: u16 = @truncate(r);
try terminal.setCursorPosition(.{
.col = this.size.anchor.col + this.size.cols -| 1,
.row = this.size.anchor.row + row,
});
_ = try terminal.write("");
}
// render bottom: +---+
try terminal.setCursorPosition(.{
.col = this.size.anchor.col,
.row = this.size.anchor.row + this.size.rows,
});
_ = try terminal.write("");
for (0..this.size.cols -| 2) |_| {
_ = try terminal.write("");
}
_ = try terminal.write("");
switch ((&this.element).*) {
.layout => |*layout| {
try layout.render(renderer);
},
.widget => |*widget| {
const content = try widget.content();
// TODO: use renderer
_ = try terminal.write(content);
try widget.render(renderer);
},
}
}

View File

@@ -24,11 +24,13 @@ pub fn Layout(comptime Event: type, comptime Renderer: type) type {
size: terminal.Size = undefined,
element: Element = undefined,
events: Events = undefined,
padding: u16 = undefined,
pub fn init(allocator: std.mem.Allocator, element: Element) @This() {
pub fn init(allocator: std.mem.Allocator, padding: u16, element: Element) @This() {
return .{
.element = element,
.events = Events.init(allocator),
.padding = padding,
};
}
@@ -50,8 +52,23 @@ pub fn Layout(comptime Event: type, comptime Renderer: type) type {
switch (event) {
.resize => |size| {
this.size = size;
log.debug("Event .resize: {{ .anchor = {{ .col = {d}, .row = {d} }}, .cols = {d}, .rows = {d} }}", .{
size.anchor.col,
size.anchor.row,
size.cols,
size.rows,
});
const sub_event: Event = .{
.resize = .{
.anchor = .{
.col = size.anchor.col + this.padding,
.row = size.anchor.row + this.padding,
},
.cols = size.cols -| this.padding -| this.padding,
.rows = size.rows -| this.padding -| this.padding,
},
};
// adjust size according to the containing elements
const sub_event = event;
switch ((&this.element).*) {
.layout => |*layout| {
const events = try layout.handle(sub_event);
@@ -82,15 +99,12 @@ pub fn Layout(comptime Event: type, comptime Renderer: type) type {
}
pub fn render(this: *@This(), renderer: Renderer) !void {
// TODO: padding contents accordingly
switch ((&this.element).*) {
.layout => |*layout| {
try layout.render(renderer);
},
.widget => |*widget| {
const content = try widget.content();
// TODO: use renderer
_ = try terminal.write(content);
try widget.render(renderer);
},
}
}

View File

@@ -36,7 +36,12 @@ pub fn main() !void {
var spacer = Widget.Spacer.init();
break :blk &spacer;
}),
Layout.createFrom(vstack: {
Layout.createFrom(framing: {
var framing = Layout.Framing.init(allocator, .{
.layout = Layout.createFrom(
padding: {
var padding = Layout.Padding.init(allocator, 2, .{
.layout = Layout.createFrom(vstack: {
var vstack = Layout.VStack.init(allocator, .{
Widget.createFrom(blk: {
const file = try std.fs.cwd().openFile("./src/app.zig", .{});
@@ -57,6 +62,13 @@ pub fn main() !void {
});
break :vstack &vstack;
}),
});
break :padding &padding;
},
),
});
break :framing &framing;
}),
Widget.createFrom(blk: {
var spacer = Widget.Spacer.init();
break :blk &spacer;

View File

@@ -79,19 +79,16 @@ pub fn Widget(comptime Event: type, comptime Renderer: type) type {
try renderer.clear(this.size);
try terminal.setCursorPosition(this.size.anchor);
if (this.size.rows >= this.line_index.items.len) {
log.debug("render: {s}", .{this.contents.items});
try renderer.render(this.size, this.contents.items);
} else {
// more rows than we can display
const i = this.line_index.items[this.line];
const e = this.size.rows + this.line;
if (e > this.line_index.items.len) {
log.debug("render: {s}", .{this.contents.items[i..]});
try renderer.render(this.size, this.contents.items[i..]);
return;
}
const x = this.line_index.items[e];
log.debug("render: {s}", .{this.contents.items[i..x]});
try renderer.render(this.size, this.contents.items[i..x]);
}
}