add(examples/styles): text and color styling possiblities
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 39s

This also contains some minor refactoring to improve the readability
and understandability of the library (i.e. renaming of Style.Attributes
to Style.Emphasis).
This commit is contained in:
2025-02-21 19:13:11 +01:00
parent c0c0590bb9
commit 7b005ea4b1
6 changed files with 181 additions and 18 deletions

View File

@@ -63,8 +63,7 @@ pub fn main() !void {
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 } }, .{}));
try box.append(try App.Container.init(allocator, .{ .rectangle = .{ .fill = @enumFromInt(field.value) } }, .{}));
}
var scrollable: App.Scrollable = .init(box);
try container.append(try App.Container.init(allocator, .{}, scrollable.element()));

148
examples/styles/text.zig Normal file
View File

@@ -0,0 +1,148 @@
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;
}
}
};
const TextStyles = struct {
const text = "Example";
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 {
@setEvalBranchQuota(50000);
_ = ctx;
std.debug.assert(cells.len == @as(usize, size.cols) * @as(usize, size.rows));
var row: usize = 0;
var col: usize = 0;
// Color
inline for (std.meta.fields(zterm.Color)) |bg_field| {
if (comptime bg_field.value == 0) continue; // zterm.Color.default == 0 -> skip
inline for (std.meta.fields(zterm.Color)) |fg_field| {
if (comptime fg_field.value == 0) continue; // zterm.Color.default == 0 -> skip
if (comptime fg_field.value == bg_field.value) continue;
// witouth any emphasis
for (text) |cp| {
cells[(row * size.cols) + col].style.bg = @enumFromInt(bg_field.value);
cells[(row * size.cols) + col].style.fg = @enumFromInt(fg_field.value);
cells[(row * size.cols) + col].cp = cp;
col += 1;
}
// emphasis (no combinations)
inline for (std.meta.fields(zterm.Style.Emphasis)) |emp_field| {
if (comptime emp_field.value == 0) continue; // zterm.Style.Emphasis.reset == 0 -> skip
const emphasis: zterm.Style.Emphasis = @enumFromInt(emp_field.value);
for (text) |cp| {
cells[(row * size.cols) + col].style.bg = @enumFromInt(bg_field.value);
cells[(row * size.cols) + col].style.fg = @enumFromInt(fg_field.value);
cells[(row * size.cols) + col].style.emphasis = &.{emphasis};
cells[(row * size.cols) + col].cp = cp;
col += 1;
}
}
row += 1;
col = 0;
}
}
}
};
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 text_styles: TextStyles = .{};
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 = .vertical },
.min_size = .{
.rows = (std.meta.fields(zterm.Color).len - 1) * (std.meta.fields(zterm.Color).len - 2),
.cols = std.meta.fields(zterm.Style.Emphasis).len * TextStyles.text.len,
}, // ensure enough rows and/or columns to render all text styles -> scrollable otherwise
}, text_styles.element());
defer box.deinit();
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();
}
}