From c0c0590bb91ddecf33c598d39804f492067bc997 Mon Sep 17 00:00:00 2001 From: Yves Biener Date: Fri, 21 Feb 2025 16:43:03 +0100 Subject: [PATCH] add(examples/styles): color palette to showcase all available colors to render (except for `.default`) --- README.md | 4 +- build.zig | 11 ++++ examples/layouts/vertical.zig | 1 - examples/styles/palette.zig | 97 +++++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 examples/styles/palette.zig diff --git a/README.md b/README.md index fd551ca..ae4d959 100644 --- a/README.md +++ b/README.md @@ -136,15 +136,15 @@ the primary use-case for myself to create this library in the first place. - [x] Scrollable Content (i.e. show long text of an except of something and other smaller `Container`) - [x] min size - [x] mouse scrolling aware of mouse position (i.e. through multiple different scrollable `Container`) - - [ ] Launch sub-applications (not inside of a `Container` but during the application workflow, like an editor) - [ ] Styles - [ ] Text styles - - [ ] Color palette + - [x] Color palette - [ ] Error Handling - [ ] log and show error's without crashing the application - [ ] show application error that will cause a crash - [ ] Demo - [ ] use another tui application to launch and come back to (showcase the interrupt behavior) + - [ ] Launch sub-applications (not inside of a `Container` but during the application workflow, like an editor) - [ ] Testability - [ ] snapshot ability to safe current screen (from `Renderer`) to test against - [ ] try to integrate them into the library itself such that they also serve as examples on how to test diff --git a/build.zig b/build.zig index 4adc36a..3bbd30e 100644 --- a/build.zig +++ b/build.zig @@ -15,6 +15,7 @@ pub fn build(b: *std.Build) void { grid, mixed, // styles: + palette, }; const example = b.option(Examples, "example", "Example to build and/or run. (default: vertical)") orelse .vertical; @@ -96,6 +97,15 @@ pub fn build(b: *std.Build) void { }); mixed.root_module.addImport("zterm", lib); + // styles: + const palette = b.addExecutable(.{ + .name = "palette", + .root_source_file = b.path("examples/styles/palette.zig"), + .target = target, + .optimize = optimize, + }); + palette.root_module.addImport("zterm", lib); + // mapping of user selected example to compile step const exe = switch (example) { // elements: @@ -108,6 +118,7 @@ pub fn build(b: *std.Build) void { .grid => grid, .mixed => mixed, // styles: + .palette => palette, }; b.installArtifact(exe); diff --git a/examples/layouts/vertical.zig b/examples/layouts/vertical.zig index bdc84a0..3fdb5e8 100644 --- a/examples/layouts/vertical.zig +++ b/examples/layouts/vertical.zig @@ -51,7 +51,6 @@ pub fn main() !void { const element = quit_text.element(); var container = try App.Container.init(allocator, .{ - .border = .{}, .layout = .{ .gap = 2, .padding = .{ .top = 5, .bottom = 3, .left = 3, .right = 3 }, diff --git a/examples/styles/palette.zig b/examples/styles/palette.zig new file mode 100644 index 0000000..d76a088 --- /dev/null +++ b/examples/styles/palette.zig @@ -0,0 +1,97 @@ +const std = @import("std"); +const zterm = @import("zterm"); + +const App = zterm.App(union(enum) {}); + +const log = std.log.scoped(.default); + +const QuitText = struct { + const text = "Press ctrl+c to quit."; + + pub fn element(this: *@This()) App.Element { + return .{ .ptr = this, .vtable = &.{ .content = content } }; + } + + pub fn content(ctx: *anyopaque, cells: []zterm.Cell, size: zterm.Size) !void { + _ = ctx; + std.debug.assert(cells.len == @as(usize, size.cols) * @as(usize, size.rows)); + + const row = 2; + const col = size.cols / 2 -| (text.len / 2); + const anchor = (row * size.cols) + col; + + for (text, 0..) |cp, idx| { + cells[anchor + idx].style.fg = .white; + cells[anchor + idx].style.bg = .black; + cells[anchor + idx].cp = cp; + + // NOTE: do not write over the contents of this `Container`'s `Size` + if (anchor + idx == cells.len - 1) break; + } + } +}; + +pub fn main() !void { + errdefer |err| log.err("Application Error: {any}", .{err}); + + var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; + defer if (gpa.deinit() == .leak) { + log.err("memory leak", .{}); + }; + const allocator = gpa.allocator(); + + var app: App = .init; + var renderer = zterm.Renderer.Buffered.init(allocator); + defer renderer.deinit(); + + var quit_text: QuitText = .{}; + const element = quit_text.element(); + + var container = try App.Container.init(allocator, .{ + .layout = .{ + .gap = 2, + .padding = .{ .top = 5, .bottom = 3, .left = 3, .right = 3 }, + }, + }, element); + defer container.deinit(); + + var box = try App.Container.init(allocator, .{ + .layout = .{ .direction = .horizontal }, + .min_size = .{ .cols = 3 * std.meta.fields(zterm.Color).len }, // ensure enough columns to render all colors -> scrollable otherwise + }, .{}); + defer box.deinit(); + + inline for (std.meta.fields(zterm.Color)) |field| { + if (comptime field.value == 0) continue; // zterm.Color.default == 0 -> skip + const color = std.meta.stringToEnum(zterm.Color, field.name).?; + try box.append(try App.Container.init(allocator, .{ .rectangle = .{ .fill = color } }, .{})); + } + var scrollable: App.Scrollable = .init(box); + try container.append(try App.Container.init(allocator, .{}, scrollable.element())); + + try app.start(); + defer app.stop() catch |err| log.err("Failed to stop application: {any}", .{err}); + + while (true) { + const event = app.nextEvent(); + log.debug("received event: {s}", .{@tagName(event)}); + + switch (event) { + .init => continue, + .quit => break, + .resize => |size| try renderer.resize(size), + .key => |key| if (key.eql(.{ .cp = 'c', .mod = .{ .ctrl = true } })) app.quit(), + .err => |err| log.err("Received {s} with message: {s}", .{ @errorName(err.err), err.msg }), + else => {}, + } + + container.handle(event) catch |err| app.postEvent(.{ + .err = .{ + .err = err, + .msg = "Container Event handling failed", + }, + }); + try renderer.render(@TypeOf(container), &container); + try renderer.flush(); + } +}