WIP: add Container type with corresponding Properties configuration

The configuration of the `Container` types is very much inspired by
[clay](https://github.com/nicbarker/clay).
This commit is contained in:
2025-02-01 01:05:56 +01:00
parent bdbe05c996
commit 1293cb065d
9 changed files with 361 additions and 142 deletions

View File

@@ -7,6 +7,7 @@ const mergeTaggedUnions = event.mergeTaggedUnions;
const isTaggedUnion = event.isTaggedUnion;
const Key = @import("key.zig");
const Size = @import("size.zig");
const Queue = @import("queue.zig").Queue;
const log = std.log.scoped(.app);
@@ -15,14 +16,6 @@ const log = std.log.scoped(.app);
/// an tagged union for all the user events that can be send through the
/// applications event loop.
///
/// _R_ is the type function for the `Renderer` to use. The parameter boolean
/// will be set to the _fullscreen_ value at compile time. The corresponding
/// `Renderer` type is accessible through the generated type of this function.
///
/// _fullscreen_ will be used to configure the `App` and the `Renderer` to
/// respect the corresponding configuration whether to render a fullscreen tui
/// or an inline tui.
///
/// # Example
///
/// Create an `App` which renders using the `PlainRenderer` in fullscreen with
@@ -32,41 +25,35 @@ const log = std.log.scoped(.app);
/// const zterm = @import("zterm");
/// const App = zterm.App(
/// union(enum) {},
/// zterm.Renderer.Direct,
/// true,
/// );
/// // later on use
/// // later on create an `App` instance and start the event loop
/// var app: App = .{};
/// var renderer: App.Renderer = .{};
/// try app.start(null);
/// defer app.stop() catch unreachable;
/// ```
pub fn App(comptime E: type, comptime R: fn (comptime bool) type, comptime fullscreen: bool) type {
pub fn App(comptime E: type) type {
if (!isTaggedUnion(E)) {
@compileError("Provided user event `E` for `App(comptime E: type)` is not of type `union(enum)`.");
}
return struct {
pub const Event = mergeTaggedUnions(event.SystemEvent, E);
pub const Renderer = R(fullscreen);
pub const Layout = @import("layout.zig").Layout(Event, Renderer);
pub const Widget = @import("widget.zig").Widget(Event, Renderer);
pub const View = @import("view.zig").View(Event, Renderer);
pub const Container = @import("container.zig").Container(Event);
queue: Queue(Event, 256) = .{},
thread: ?std.Thread = null,
quit_event: std.Thread.ResetEvent = .{},
termios: ?std.posix.termios = null,
attached_handler: bool = false,
min_size: ?terminal.Size = null,
prev_size: terminal.Size = .{ .cols = 0, .rows = 0 },
min_size: ?Size = null,
prev_size: Size = .{ .cols = 0, .rows = 0 },
pub const SignalHandler = struct {
context: *anyopaque,
callback: *const fn (context: *anyopaque) void,
};
pub fn start(this: *@This(), min_size: ?terminal.Size) !void {
if (fullscreen) { // a minimal size only really makes sense if the application is rendered fullscreen
this.min_size = min_size;
}
pub fn start(this: *@This(), min_size: ?Size) !void {
this.min_size = min_size;
if (this.thread) |_| return;
if (!this.attached_handler) {
@@ -92,19 +79,17 @@ pub fn App(comptime E: type, comptime R: fn (comptime bool) type, comptime fulls
if (this.termios) |_| {} else {
this.termios = termios;
}
if (fullscreen) {
try terminal.saveScreen();
try terminal.enterAltScreen();
try terminal.hideCursor();
}
try terminal.saveScreen();
try terminal.enterAltScreen();
try terminal.hideCursor();
// post init event (as the very first element to be in the queue - event loop)
this.postEvent(.init);
}
pub fn interrupt(this: *@This()) !void {
this.quit_event.set();
if (fullscreen) {
try terminal.exitAltScreen();
try terminal.restoreScreen();
}
try terminal.exitAltScreen();
try terminal.restoreScreen();
if (this.thread) |thread| {
thread.join();
this.thread = null;
@@ -115,11 +100,9 @@ pub fn App(comptime E: type, comptime R: fn (comptime bool) type, comptime fulls
try this.interrupt();
if (this.termios) |*termios| {
try terminal.disableRawMode(termios);
if (fullscreen) {
try terminal.showCursor();
try terminal.exitAltScreen();
try terminal.restoreScreen();
}
try terminal.showCursor();
try terminal.exitAltScreen();
try terminal.restoreScreen();
}
this.termios = null;
}
@@ -322,7 +305,7 @@ pub fn App(comptime E: type, comptime R: fn (comptime bool) type, comptime fulls
// TODO: only post the event if the size has changed?
// because there might be too many resize events (which force a re-draw of the entire screen)
const size: terminal.Size = .{
const size: Size = .{
.rows = std.fmt.parseUnsigned(u16, height_char, 10) catch break,
.cols = std.fmt.parseUnsigned(u16, width_char, 10) catch break,
};