Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 55s
The renderer will provide `resize`, `reposition` and `minSize` (for the `Scrollable` `Element`) with a read-only pointer to the model of the application (similar to how it is already done for `handle` and `content`). Every interface function now has the same data that it can each use for implementing its corresponding task based on local and shared variables through the element instance and model pointer.
200 lines
6.4 KiB
Zig
200 lines
6.4 KiB
Zig
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, _: *const App.Model, cells: []zterm.Cell, size: zterm.Point) !void {
|
|
_ = ctx;
|
|
assert(cells.len == @as(usize, size.x) * @as(usize, size.y));
|
|
|
|
const y = 2;
|
|
const x = size.x / 2 -| (text.len / 2);
|
|
const anchor = (y * size.x) + x;
|
|
|
|
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.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 = .{};
|
|
|
|
// 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, .{
|
|
.border = .{
|
|
.color = .blue,
|
|
.sides = .all,
|
|
},
|
|
.layout = .{
|
|
.gap = 1,
|
|
.padding = .vertical(2),
|
|
.direction = .vertical,
|
|
},
|
|
.size = .{
|
|
.dim = .{ .y = 90 },
|
|
},
|
|
}, .{});
|
|
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 = .init(box, .disabled);
|
|
|
|
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()));
|
|
|
|
var nested_container: App.Container = try .init(allocator, .{
|
|
.layout = .{
|
|
.direction = .vertical,
|
|
.separator = .{
|
|
.enabled = true,
|
|
},
|
|
},
|
|
}, .{});
|
|
var inner_container: App.Container = try .init(allocator, .{
|
|
.layout = .{
|
|
.direction = .vertical,
|
|
},
|
|
.border = .{
|
|
.color = .light_blue,
|
|
.sides = .all,
|
|
},
|
|
}, .{});
|
|
try inner_container.append(try .init(allocator, .{
|
|
.rectangle = .{
|
|
.fill = .blue,
|
|
},
|
|
.size = .{
|
|
.grow = .horizontal,
|
|
.dim = .{ .y = 5 },
|
|
},
|
|
}, .{}));
|
|
try inner_container.append(try .init(allocator, .{
|
|
.rectangle = .{
|
|
.fill = .red,
|
|
},
|
|
.size = .{
|
|
.grow = .horizontal,
|
|
.dim = .{ .y = 5 },
|
|
},
|
|
}, .{}));
|
|
try inner_container.append(try .init(allocator, .{
|
|
.rectangle = .{
|
|
.fill = .green,
|
|
},
|
|
}, .{}));
|
|
try nested_container.append(inner_container);
|
|
try nested_container.append(try .init(allocator, .{
|
|
.size = .{
|
|
.grow = .horizontal,
|
|
.dim = .{ .y = 1 },
|
|
},
|
|
}, .{}));
|
|
try container.append(nested_container);
|
|
try container.append(try App.Container.init(allocator, .{
|
|
.rectangle = .{ .fill = .blue },
|
|
.size = .{
|
|
.dim = .{ .x = 30 },
|
|
},
|
|
}, .{}));
|
|
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)});
|
|
|
|
// pre event handling
|
|
switch (event) {
|
|
.key => |key| {
|
|
if (key.eql(.{ .cp = 'c', .mod = .{ .ctrl = true } })) app.quit();
|
|
|
|
if (key.eql(.{ .cp = 'n', .mod = .{ .ctrl = true } })) {
|
|
try app.interrupt();
|
|
renderer.size = .{}; // reset size, such that next resize will cause a full re-draw!
|
|
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(&app.model, event) catch |err| app.postEvent(.{
|
|
.err = .{
|
|
.err = err,
|
|
.msg = "Container Event handling failed",
|
|
},
|
|
});
|
|
|
|
// post event handling
|
|
switch (event) {
|
|
.quit => break,
|
|
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 input = zterm.input;
|
|
const App = zterm.App(struct {}, union(enum) {});
|