From 4567963ff27f257932acf3a5611c2745adf2226d Mon Sep 17 00:00:00 2001 From: Yves Biener Date: Sun, 2 Nov 2025 16:10:11 +0100 Subject: [PATCH] mod(example/text): improve event loop; add stick table heading for used emphasis This should be the way it should be done all the time, such that you are not rendering for every input, but instead handle all `App.Event`s that happened between the last render and the current. This shares similarities with the continuous event loops, which also batches the events only with the exception that it instead blocks (see `App.Queue.poll`). --- examples/styles/text.zig | 140 +++++++++++++++++++++++++++++++++------ 1 file changed, 120 insertions(+), 20 deletions(-) diff --git a/examples/styles/text.zig b/examples/styles/text.zig index 969a5da..d244510 100644 --- a/examples/styles/text.zig +++ b/examples/styles/text.zig @@ -24,6 +24,86 @@ const QuitText = struct { } }; +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"; @@ -109,12 +189,21 @@ pub fn main() !void { var container = try App.Container.init(allocator, .{ .layout = .{ - .gap = 2, + // .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 box = try App.Container.init(allocator, .{ .layout = .{ .direction = .vertical }, }, text_styles.element()); @@ -126,28 +215,39 @@ pub fn main() !void { try app.start(); defer app.stop() catch |err| log.err("Failed to stop application: {any}", .{err}); - while (true) { - const event = app.nextEvent(); - log.debug("received event: {s}", .{@tagName(event)}); + // 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(); + }; - // 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 => {}, - } + // 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", - }, - }); + 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 => {}, + // post event handling + switch (event) { + .quit => break :loop, + else => {}, + } } container.resize(try renderer.resize());