All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 22s
Added corresponding test cases for padding, borders and corresponding seperators.
140 lines
4.8 KiB
Zig
140 lines
4.8 KiB
Zig
const std = @import("std");
|
|
const zterm = @import("zterm");
|
|
|
|
const input = zterm.input;
|
|
const App = zterm.App(union(enum) {});
|
|
|
|
const log = std.log.scoped(.default);
|
|
|
|
const QuitText = struct {
|
|
const text = "Press ctrl+c to quit. Press ctrl+n to launch helix.";
|
|
|
|
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});
|
|
|
|
// TODO: maybe create own allocator as some sort of arena allocator to have consistent memory usage
|
|
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 = .{};
|
|
|
|
// TODO: what should the demo application do?
|
|
// - some sort of chat? -> write messages and have them displayed in a scrollable array at the right hand side?
|
|
// - on the left some buttons?
|
|
var box = try App.Container.init(allocator, .{
|
|
.layout = .{
|
|
.gap = 1,
|
|
.padding = .vertical(2),
|
|
},
|
|
.min_size = .{ .cols = 50 },
|
|
}, .{});
|
|
try box.append(try App.Container.init(allocator, .{
|
|
.rectangle = .{ .fill = .light_green },
|
|
}, .{}));
|
|
try box.append(try App.Container.init(allocator, .{
|
|
.rectangle = .{ .fill = .light_green },
|
|
}, .{}));
|
|
try box.append(try App.Container.init(allocator, .{
|
|
.rectangle = .{ .fill = .light_green },
|
|
}, .{}));
|
|
defer box.deinit();
|
|
|
|
var scrollable: App.Scrollable = .{
|
|
.container = box,
|
|
};
|
|
|
|
var container = try App.Container.init(allocator, .{
|
|
.layout = .{
|
|
.gap = 2,
|
|
.separator = .{ .enabled = true },
|
|
.padding = .{ .top = 5, .bottom = 3, .left = 3, .right = 3 },
|
|
.direction = .horizontal,
|
|
},
|
|
}, quit_text.element());
|
|
try container.append(try App.Container.init(allocator, .{}, scrollable.element()));
|
|
|
|
try container.append(try App.Container.init(allocator, .{
|
|
.border = .{
|
|
.color = .light_blue,
|
|
.sides = .all,
|
|
},
|
|
}, .{}));
|
|
try container.append(try App.Container.init(allocator, .{
|
|
.rectangle = .{ .fill = .blue },
|
|
}, .{}));
|
|
defer container.deinit(); // also de-initializes the children
|
|
|
|
try app.start();
|
|
defer app.stop() catch |err| log.err("Failed to stop application: {any}", .{err});
|
|
|
|
// event loop
|
|
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();
|
|
|
|
if (key.eql(.{ .cp = 'n', .mod = .{ .ctrl = true } })) {
|
|
try app.interrupt();
|
|
defer app.start() catch @panic("could not start app event loop");
|
|
var child = std.process.Child.init(&.{"hx"}, allocator);
|
|
_ = child.spawnAndWait() catch |err| app.postEvent(.{
|
|
.err = .{
|
|
.err = err,
|
|
.msg = "Spawning $EDITOR failed",
|
|
},
|
|
});
|
|
continue;
|
|
}
|
|
},
|
|
// NOTE: errors could be displayed in another container in case one was received, etc. to provide the user with feedback
|
|
.err => |err| log.err("Received {s} with message: {s}", .{ @errorName(err.err), err.msg }),
|
|
else => {},
|
|
}
|
|
|
|
// NOTE: returned errors should be propagated back to the application
|
|
container.handle(event) catch |err| app.postEvent(.{
|
|
.err = .{
|
|
.err = err,
|
|
.msg = "Container Event handling failed",
|
|
},
|
|
});
|
|
try renderer.render(@TypeOf(container), &container);
|
|
try renderer.flush();
|
|
}
|
|
}
|