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