a modern tui library written in zig
1const std = @import("std");
2const vaxis = @import("vaxis");
3const Cell = vaxis.Cell;
4const TextInput = vaxis.widgets.TextInput;
5
6const log = std.log.scoped(.main);
7pub fn main() !void {
8 var gpa = std.heap.GeneralPurposeAllocator(.{}){};
9 defer {
10 const deinit_status = gpa.deinit();
11 if (deinit_status == .leak) {
12 log.err("memory leak", .{});
13 }
14 }
15 const alloc = gpa.allocator();
16
17 var buffer: [1024]u8 = undefined;
18 var tty = try vaxis.Tty.init(&buffer);
19 defer tty.deinit();
20
21 var vx = try vaxis.init(alloc, .{});
22 defer vx.deinit(alloc, tty.writer());
23
24 var loop: vaxis.Loop(Event) = .{ .tty = &tty, .vaxis = &vx };
25 try loop.init();
26
27 try loop.start();
28 defer loop.stop();
29
30 try vx.queryTerminal(tty.writer(), 1 * std.time.ns_per_s);
31
32 var text_input = TextInput.init(alloc);
33 defer text_input.deinit();
34
35 var selected_option: ?usize = null;
36
37 const options = [_][]const u8{
38 "option 1",
39 "option 2",
40 "option 3",
41 };
42
43 // The main event loop. Vaxis provides a thread safe, blocking, buffered
44 // queue which can serve as the primary event queue for an application
45 while (true) {
46 // nextEvent blocks until an event is in the queue
47 const event = loop.nextEvent();
48 // exhaustive switching ftw. Vaxis will send events if your Event
49 // enum has the fields for those events (ie "key_press", "winsize")
50 switch (event) {
51 .key_press => |key| {
52 if (key.codepoint == 'c' and key.mods.ctrl) {
53 break;
54 } else if (key.matches(vaxis.Key.tab, .{})) {
55 if (selected_option == null) {
56 selected_option = 0;
57 } else {
58 selected_option.? = @min(options.len - 1, selected_option.? + 1);
59 }
60 } else if (key.matches(vaxis.Key.tab, .{ .shift = true })) {
61 if (selected_option == null) {
62 selected_option = 0;
63 } else {
64 selected_option.? = selected_option.? -| 1;
65 }
66 } else if (key.matches(vaxis.Key.enter, .{}) or key.matches('j', .{ .ctrl = true })) {
67 if (selected_option) |i| {
68 log.err("enter", .{});
69 try text_input.insertSliceAtCursor(options[i]);
70 selected_option = null;
71 }
72 } else {
73 if (selected_option == null)
74 try text_input.update(.{ .key_press = key });
75 }
76 },
77 .winsize => |ws| {
78 try vx.resize(alloc, tty.writer(), ws);
79 },
80 else => {},
81 }
82
83 const win = vx.window();
84 win.clear();
85
86 text_input.draw(win);
87
88 if (selected_option) |i| {
89 win.hideCursor();
90 for (options, 0..) |opt, j| {
91 log.err("i = {d}, j = {d}, opt = {s}", .{ i, j, opt });
92 var seg = [_]vaxis.Segment{.{
93 .text = opt,
94 .style = if (j == i) .{ .reverse = true } else .{},
95 }};
96 _ = win.print(&seg, .{ .row_offset = @intCast(j + 1) });
97 }
98 }
99 try vx.render(tty.writer());
100 }
101}
102
103// Our Event. This can contain internal events as well as Vaxis events.
104// Internal events can be posted into the same queue as vaxis events to allow
105// for a single event loop with exhaustive switching. Booya
106const Event = union(enum) {
107 key_press: vaxis.Key,
108 winsize: vaxis.Winsize,
109 focus_in,
110 foo: u8,
111};