From 4c67a86c27fffd49c5c8a431a2eb9d84cc70b94b Mon Sep 17 00:00:00 2001 From: Yves Biener Date: Sun, 10 Nov 2024 22:21:40 +0100 Subject: [PATCH] feat(layout): Framing and Padding implementation 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. --- src/layout/Framing.zig | 62 ++++++++++++++++++++++++++++++++++++++---- src/layout/Padding.zig | 26 ++++++++++++++---- src/main.zig | 50 +++++++++++++++++++++------------- src/widget/RawText.zig | 3 -- 4 files changed, 108 insertions(+), 33 deletions(-) diff --git a/src/layout/Framing.zig b/src/layout/Framing.zig index ce17fd9..90030f3 100644 --- a/src/layout/Framing.zig +++ b/src/layout/Framing.zig @@ -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); }, } } diff --git a/src/layout/Padding.zig b/src/layout/Padding.zig index bb8e702..aea498f 100644 --- a/src/layout/Padding.zig +++ b/src/layout/Padding.zig @@ -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); }, } } diff --git a/src/main.zig b/src/main.zig index 0057492..2d17406 100644 --- a/src/main.zig +++ b/src/main.zig @@ -36,26 +36,38 @@ pub fn main() !void { var spacer = Widget.Spacer.init(); break :blk &spacer; }), - Layout.createFrom(vstack: { - var vstack = Layout.VStack.init(allocator, .{ - Widget.createFrom(blk: { - const file = try std.fs.cwd().openFile("./src/app.zig", .{}); - defer file.close(); - var widget = Widget.RawText.init(allocator, file); - break :blk &widget; - }), - Widget.createFrom(blk: { - var spacer = Widget.Spacer.init(); - break :blk &spacer; - }), - Widget.createFrom(blk: { - const file = try std.fs.cwd().openFile("./src/main.zig", .{}); - defer file.close(); - var widget = Widget.RawText.init(allocator, file); - break :blk &widget; - }), + 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", .{}); + defer file.close(); + var widget = Widget.RawText.init(allocator, file); + break :blk &widget; + }), + Widget.createFrom(blk: { + var spacer = Widget.Spacer.init(); + break :blk &spacer; + }), + Widget.createFrom(blk: { + const file = try std.fs.cwd().openFile("./src/main.zig", .{}); + defer file.close(); + var widget = Widget.RawText.init(allocator, file); + break :blk &widget; + }), + }); + break :vstack &vstack; + }), + }); + break :padding &padding; + }, + ), }); - break :vstack &vstack; + break :framing &framing; }), Widget.createFrom(blk: { var spacer = Widget.Spacer.init(); diff --git a/src/widget/RawText.zig b/src/widget/RawText.zig index 9cb51f7..a9749b9 100644 --- a/src/widget/RawText.zig +++ b/src/widget/RawText.zig @@ -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]); } }