Something went wrong. Try again.
A charm-like tui library
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112//! Countdown timer demo: s starts/stops, r resets, q/ctrl-c quits. Quits//! automatically when the timer reaches zero.const std = @import("std");const matcha = @import("matcha");const leaves = @import("leaves");const key = leaves.key;
const Writer = std.Io.Writer;
const timeout_ns: u64 = 5 * std.time.ns_per_s;
const KeyMap = struct { start: key.Binding = .{ .keys = &.{key.rune('s')}, .help = .{ .key = "s", .desc = "start" }, .disabled = true, }, stop: key.Binding = .{ .keys = &.{key.rune('s')}, .help = .{ .key = "s", .desc = "stop" }, }, reset: key.Binding = .{ .keys = &.{key.rune('r')}, .help = .{ .key = "r", .desc = "reset" }, }, quit: key.Binding = .{ .keys = &.{ key.rune('q'), key.ctrl('c') }, .help = .{ .key = "q", .desc = "quit" }, },};
const State = struct { model: matcha.Model = .{ .vtable = &.{ .init = init, .update = update, .view = view, } }, timer: leaves.Timer = .{ .timeout_ns = timeout_ns, .interval_ns = std.time.ns_per_ms, }, help: leaves.Help = .{}, keymap: KeyMap = .{}, quitting: bool = false,
fn init(m: *matcha.Model) ?matcha.Cmd { const self: *State = @fieldParentPtr("model", m); return self.timer.start(m.gpa); }
fn update(m: *matcha.Model, msg: matcha.Msg) ?matcha.Cmd { const self: *State = @fieldParentPtr("model", m); switch (msg) { .key_press => |k| { if (self.keymap.quit.matches(k)) { self.quitting = true; return .quit; } if (self.keymap.reset.matches(k)) { self.timer.timeout_ns = timeout_ns; return null; } if (self.keymap.start.matches(k) or self.keymap.stop.matches(k)) { const cmd = self.timer.toggle(m.gpa); self.keymap.stop.disabled = !self.timer.running; self.keymap.start.disabled = self.timer.running; return cmd; } return null; }, else => { const cmd = self.timer.update(m.gpa, msg); if (self.timer.timedOut() and !self.quitting) { self.quitting = true; return .quit; } return cmd; }, } }
fn view(m: *matcha.Model, w: *Writer) Writer.Error!matcha.View { const self: *State = @fieldParentPtr("model", m); if (!self.quitting) try w.writeAll("Exiting in "); if (self.timer.timedOut()) { try w.writeAll("All done!"); } else { try self.timer.view(w); } try w.writeByte('\n'); if (!self.quitting) { try w.writeByte('\n'); const bindings = [_]key.Binding{ self.keymap.start, self.keymap.stop, self.keymap.reset, self.keymap.quit, }; try self.help.shortView(w, &bindings); try w.writeByte('\n'); } return .{}; }};
pub fn timer(init: std.process.Init) !void { var state: State = .{}; var program: matcha.Program = try .init(init, &state.model, .{}); defer program.deinit(); try program.run();}