Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 1m34s
268 lines
8.0 KiB
Zig
268 lines
8.0 KiB
Zig
const QuitText = struct {
|
|
const text = "Press ctrl+c to quit.";
|
|
|
|
pub fn element(this: *@This()) App.Element {
|
|
return .{ .ptr = this, .vtable = &.{ .content = content } };
|
|
}
|
|
|
|
fn content(ctx: *anyopaque, _: *const App.Model, cells: []zterm.Cell, size: zterm.Point) !void {
|
|
_ = ctx;
|
|
assert(cells.len == @as(usize, size.x) * @as(usize, size.y));
|
|
|
|
const row = 2;
|
|
const col = size.x / 2 -| (text.len / 2);
|
|
const anchor = (row * size.x) + 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 TableText = struct {
|
|
pub fn element(this: *@This()) App.Element {
|
|
return .{
|
|
.ptr = this,
|
|
.vtable = &.{
|
|
.content = content,
|
|
},
|
|
};
|
|
}
|
|
|
|
fn content(ctx: *anyopaque, _: *const App.Model, cells: []zterm.Cell, size: zterm.Point) !void {
|
|
_ = ctx;
|
|
_ = size;
|
|
var idx: usize = 0;
|
|
{
|
|
const text = "Normal ";
|
|
for (text) |cp| {
|
|
cells[idx].cp = cp;
|
|
idx += 1;
|
|
}
|
|
}
|
|
{
|
|
const text = "Bold ";
|
|
for (text) |cp| {
|
|
cells[idx].cp = cp;
|
|
idx += 1;
|
|
}
|
|
}
|
|
{
|
|
const text = "Dim ";
|
|
for (text) |cp| {
|
|
cells[idx].cp = cp;
|
|
idx += 1;
|
|
}
|
|
}
|
|
{
|
|
const text = "Italic ";
|
|
for (text) |cp| {
|
|
cells[idx].cp = cp;
|
|
idx += 1;
|
|
}
|
|
}
|
|
{
|
|
const text = "Underl ";
|
|
for (text) |cp| {
|
|
cells[idx].cp = cp;
|
|
idx += 1;
|
|
}
|
|
}
|
|
{
|
|
const text = "Blink ";
|
|
for (text) |cp| {
|
|
cells[idx].cp = cp;
|
|
idx += 1;
|
|
}
|
|
}
|
|
{
|
|
const text = "Invert ";
|
|
for (text) |cp| {
|
|
cells[idx].cp = cp;
|
|
idx += 1;
|
|
}
|
|
}
|
|
{
|
|
const text = "Hidden ";
|
|
for (text) |cp| {
|
|
cells[idx].cp = cp;
|
|
idx += 1;
|
|
}
|
|
}
|
|
{
|
|
const text = "Strikethrough";
|
|
for (text) |cp| {
|
|
cells[idx].cp = cp;
|
|
idx += 1;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
const TextStyles = struct {
|
|
const text = "Example";
|
|
|
|
pub fn element(this: *@This()) App.Element {
|
|
return .{
|
|
.ptr = this,
|
|
.vtable = &.{
|
|
.minSize = minSize,
|
|
.content = content,
|
|
},
|
|
};
|
|
}
|
|
|
|
fn minSize(ctx: *anyopaque, _: *const App.Model, size: zterm.Point) zterm.Point {
|
|
_ = ctx;
|
|
_ = size;
|
|
return .{
|
|
.x = std.meta.fields(zterm.Style.Emphasis).len * TextStyles.text.len,
|
|
.y = (std.meta.fields(zterm.Color).len - 1) * (std.meta.fields(zterm.Color).len - 2),
|
|
};
|
|
}
|
|
|
|
fn content(ctx: *anyopaque, _: *const App.Model, cells: []zterm.Cell, size: zterm.Point) !void {
|
|
@setEvalBranchQuota(10000);
|
|
_ = ctx;
|
|
assert(cells.len == @as(usize, size.x) * @as(usize, size.y));
|
|
|
|
var row: usize = 0;
|
|
var col: usize = 0;
|
|
|
|
// Color
|
|
inline for (std.meta.fields(zterm.Color)) |bg_field| {
|
|
if (bg_field.value == 0) continue; // zterm.Color.default == 0 -> skip
|
|
|
|
inline for (std.meta.fields(zterm.Color)) |fg_field| {
|
|
if (fg_field.value == 0) continue; // zterm.Color.default == 0 -> skip
|
|
if (fg_field.value == bg_field.value) continue;
|
|
|
|
// witouth any emphasis
|
|
for (text) |cp| {
|
|
cells[(row * size.x) + col].style.bg = @enumFromInt(bg_field.value);
|
|
cells[(row * size.x) + col].style.fg = @enumFromInt(fg_field.value);
|
|
cells[(row * size.x) + col].cp = cp;
|
|
col += 1;
|
|
}
|
|
|
|
// emphasis (no combinations)
|
|
inline for (std.meta.fields(zterm.Style.Emphasis)) |emp_field| {
|
|
if (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.x) + col].style.bg = @enumFromInt(bg_field.value);
|
|
cells[(row * size.x) + col].style.fg = @enumFromInt(fg_field.value);
|
|
cells[(row * size.x) + col].style.emphasis = &.{emphasis};
|
|
cells[(row * size.x) + 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.DebugAllocator(.{}) = .init;
|
|
defer if (gpa.deinit() == .leak) log.err("memory leak", .{});
|
|
|
|
const allocator = gpa.allocator();
|
|
|
|
var threaded_io: std.Io.Threaded = .init(allocator);
|
|
defer threaded_io.deinit();
|
|
|
|
var app: App = .init(threaded_io.ioBasic(), .{});
|
|
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 },
|
|
.direction = .vertical,
|
|
},
|
|
}, element);
|
|
defer container.deinit();
|
|
|
|
var table_head: TableText = .{};
|
|
try container.append(try .init(allocator, .{
|
|
.size = .{
|
|
.dim = .{ .y = 1 },
|
|
.grow = .horizontal,
|
|
},
|
|
}, table_head.element()));
|
|
|
|
var scrollable: App.Scrollable = .init(try .init(allocator, .{
|
|
.layout = .{ .direction = .vertical },
|
|
}, text_styles.element()), .enabled(.white, true));
|
|
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});
|
|
|
|
// event loop
|
|
loop: while (true) {
|
|
// batch events since last iteration
|
|
const len = blk: {
|
|
app.queue.poll();
|
|
app.queue.lock();
|
|
defer app.queue.unlock();
|
|
break :blk app.queue.len();
|
|
};
|
|
|
|
// handle events
|
|
for (0..len) |_| {
|
|
const event = app.queue.pop();
|
|
log.debug("handling event: {s}", .{@tagName(event)});
|
|
// pre event handling
|
|
switch (event) {
|
|
.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(&app.model, event) catch |err| app.postEvent(.{
|
|
.err = .{
|
|
.err = err,
|
|
.msg = "Container Event handling failed",
|
|
},
|
|
});
|
|
|
|
// post event handling
|
|
switch (event) {
|
|
.quit => break :loop,
|
|
else => {},
|
|
}
|
|
}
|
|
|
|
container.resize(&app.model, try renderer.resize());
|
|
container.reposition(&app.model, .{});
|
|
try renderer.render(@TypeOf(container), &container, App.Model, &app.model);
|
|
try renderer.flush();
|
|
}
|
|
}
|
|
|
|
pub const panic = App.panic_handler;
|
|
const log = std.log.scoped(.default);
|
|
|
|
const std = @import("std");
|
|
const assert = std.debug.assert;
|
|
const zterm = @import("zterm");
|
|
|
|
const App = zterm.App(struct {}, union(enum) {});
|