Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 50s
There are now comptime checks in place an the corresponding triggered event will be automatically converted to the correct type to support simple ascii strings (`[]u8`) or utf-8 strings (`[]u21`).
167 lines
4.9 KiB
Zig
167 lines
4.9 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, 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 MouseDraw = struct {
|
|
position: ?zterm.Point = null,
|
|
|
|
pub fn element(this: *@This()) App.Element {
|
|
return .{
|
|
.ptr = this,
|
|
.vtable = &.{
|
|
.handle = handle,
|
|
.content = content,
|
|
},
|
|
};
|
|
}
|
|
|
|
fn handle(ctx: *anyopaque, event: App.Event) !void {
|
|
const this: *@This() = @ptrCast(@alignCast(ctx));
|
|
switch (event) {
|
|
.mouse => |mouse| this.position = .{ .x = mouse.x, .y = mouse.y },
|
|
else => this.position = null,
|
|
}
|
|
}
|
|
|
|
fn content(ctx: *anyopaque, cells: []zterm.Cell, size: zterm.Point) !void {
|
|
assert(cells.len == @as(usize, size.x) * @as(usize, size.y));
|
|
const this: *@This() = @ptrCast(@alignCast(ctx));
|
|
|
|
if (this.position) |pos| {
|
|
const idx = @as(usize, size.x) * @as(usize, pos.y) + @as(usize, pos.x);
|
|
cells[idx].cp = 'x';
|
|
cells[idx].style.fg = .red;
|
|
}
|
|
}
|
|
};
|
|
|
|
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 app: App = .init;
|
|
var renderer = zterm.Renderer.Buffered.init(allocator);
|
|
defer renderer.deinit();
|
|
|
|
var input_field: App.Input(.accept) = .init(allocator, &app.queue, .init(.black));
|
|
defer input_field.deinit();
|
|
|
|
var mouse_draw: MouseDraw = .{};
|
|
var second_mouse_draw: MouseDraw = .{};
|
|
var quit_text: QuitText = .{};
|
|
|
|
var container = try App.Container.init(allocator, .{
|
|
.rectangle = .{ .fill = .grey },
|
|
.layout = .{
|
|
.direction = .vertical,
|
|
.padding = .all(5),
|
|
},
|
|
}, quit_text.element());
|
|
defer container.deinit();
|
|
|
|
try container.append(try App.Container.init(allocator, .{
|
|
.rectangle = .{ .fill = .light_grey },
|
|
.size = .{
|
|
.grow = .horizontal,
|
|
.dim = .{ .y = 1 },
|
|
},
|
|
}, input_field.element()));
|
|
|
|
var nested_container: App.Container = try .init(allocator, .{
|
|
.border = .{
|
|
.sides = .all,
|
|
.color = .black,
|
|
},
|
|
.rectangle = .{ .fill = .light_grey },
|
|
.layout = .{
|
|
.separator = .{
|
|
.enabled = true,
|
|
.color = .black,
|
|
},
|
|
},
|
|
}, .{});
|
|
try nested_container.append(try .init(allocator, .{
|
|
.rectangle = .{ .fill = .light_grey },
|
|
}, mouse_draw.element()));
|
|
try nested_container.append(try .init(allocator, .{
|
|
.rectangle = .{ .fill = .light_grey },
|
|
}, second_mouse_draw.element()));
|
|
try container.append(nested_container);
|
|
|
|
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(),
|
|
.accept => |input| {
|
|
defer allocator.free(input);
|
|
log.debug("Accepted input '{s}'", .{input});
|
|
},
|
|
.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",
|
|
},
|
|
});
|
|
|
|
// post event handling
|
|
switch (event) {
|
|
.quit => break,
|
|
else => {},
|
|
}
|
|
|
|
container.resize(try renderer.resize());
|
|
container.reposition(.{});
|
|
try renderer.render(@TypeOf(container), &container);
|
|
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 Color = zterm.Color;
|
|
|
|
const App = zterm.App(union(enum) {
|
|
accept: []u8,
|
|
});
|