a modern tui library written in zig
1const std = @import("std");
2const vaxis = @import("vaxis");
3const vxfw = vaxis.vxfw;
4
5const Text = vxfw.Text;
6const ListView = vxfw.ListView;
7const Widget = vxfw.Widget;
8
9const Model = struct {
10 list_view: ListView,
11
12 pub fn widget(self: *Model) Widget {
13 return .{
14 .userdata = self,
15 .eventHandler = Model.typeErasedEventHandler,
16 .drawFn = Model.typeErasedDrawFn,
17 };
18 }
19
20 pub fn typeErasedEventHandler(ptr: *anyopaque, ctx: *vxfw.EventContext, event: vxfw.Event) anyerror!void {
21 const self: *Model = @ptrCast(@alignCast(ptr));
22 try ctx.requestFocus(self.list_view.widget());
23 switch (event) {
24 .key_press => |key| {
25 if (key.matches('q', .{}) or key.matchExact('c', .{ .ctrl = true })) {
26 ctx.quit = true;
27 return;
28 }
29 },
30 else => {},
31 }
32 }
33
34 fn typeErasedDrawFn(ptr: *anyopaque, ctx: vxfw.DrawContext) std.mem.Allocator.Error!vxfw.Surface {
35 const self: *Model = @ptrCast(@alignCast(ptr));
36 const max = ctx.max.size();
37
38 const list_view: vxfw.SubSurface = .{
39 .origin = .{ .row = 1, .col = 1 },
40 .surface = try self.list_view.draw(ctx),
41 };
42
43 const children = try ctx.arena.alloc(vxfw.SubSurface, 1);
44 children[0] = list_view;
45
46 return .{
47 .size = max,
48 .widget = self.widget(),
49 .buffer = &.{},
50 .children = children,
51 };
52 }
53};
54
55pub fn main() !void {
56 var gpa = std.heap.GeneralPurposeAllocator(.{}){};
57 defer _ = gpa.deinit();
58
59 const allocator = gpa.allocator();
60
61 var app = try vxfw.App.init(allocator);
62 defer app.deinit();
63
64 const model = try allocator.create(Model);
65 defer allocator.destroy(model);
66
67 const n = 80;
68 var texts = try std.ArrayList(Widget).initCapacity(allocator, n);
69
70 var allocs = try std.ArrayList(*Text).initCapacity(allocator, n);
71 defer {
72 for (allocs.items) |tw| {
73 allocator.free(tw.text);
74 allocator.destroy(tw);
75 }
76 allocs.deinit(allocator);
77 texts.deinit(allocator);
78 }
79
80 for (0..n) |i| {
81 const t = std.fmt.allocPrint(allocator, "List Item {d}", .{i}) catch "placeholder";
82 const tw = try allocator.create(Text);
83 tw.* = .{ .text = t };
84 _ = try allocs.append(allocator, tw);
85 _ = try texts.append(allocator, tw.widget());
86 }
87
88 model.* = .{
89 .list_view = .{
90 .wheel_scroll = 3,
91 .scroll = .{
92 .wants_cursor = true,
93 },
94 .children = .{ .slice = texts.items },
95 },
96 };
97
98 try app.run(model.widget(), .{});
99}