this repo has no description
1const std = @import("std");
2const fmt = std.fmt;
3const heap = std.heap;
4const mem = std.mem;
5const meta = std.meta;
6
7const vaxis = @import("vaxis");
8
9const log = std.log.scoped(.main);
10
11const ActiveSection = enum {
12 top,
13 mid,
14 btm,
15};
16
17pub fn main(init: std.process.Init) !void {
18 const io = init.io;
19 const alloc = init.gpa;
20
21 // Users set up below the main function
22 const users_buf = try alloc.dupe(User, users[0..]);
23 defer alloc.free(users_buf);
24
25 var buffer: [1024]u8 = undefined;
26 var tty: vaxis.Tty = try .init(io, &buffer);
27 defer tty.deinit();
28 const tty_writer = tty.writer();
29 var vx = try vaxis.init(io, alloc, init.environ_map, .{
30 .kitty_keyboard_flags = .{ .report_events = true },
31 });
32 defer vx.deinit(alloc, tty.writer());
33
34 var loop: vaxis.Loop(union(enum) {
35 key_press: vaxis.Key,
36 winsize: vaxis.Winsize,
37 table_upd,
38 }) = .init(io, &tty, &vx);
39 try loop.start();
40 defer loop.stop();
41 try vx.enterAltScreen(tty.writer());
42 try vx.queryTerminal(tty.writer(), .fromMilliseconds(250));
43
44 const logo =
45 \\░█░█░█▀█░█░█░▀█▀░█▀▀░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░
46 \\░▀▄▀░█▀█░▄▀▄░░█░░▀▀█░░░░█░░█▀█░█▀▄░█░░░█▀▀░
47 \\░░▀░░▀░▀░▀░▀░▀▀▀░▀▀▀░░░░▀░░▀░▀░▀▀░░▀▀▀░▀▀▀░
48 ;
49 const title_logo = vaxis.Cell.Segment{
50 .text = logo,
51 .style = .{},
52 };
53 const title_info = vaxis.Cell.Segment{
54 .text = "===A Demo of the the Vaxis Table Widget!===",
55 .style = .{},
56 };
57 const title_disclaimer = vaxis.Cell.Segment{
58 .text = "(All data is non-sensical & LLM generated.)",
59 .style = .{},
60 };
61 var title_segs = [_]vaxis.Cell.Segment{ title_logo, title_info, title_disclaimer };
62
63 var cmd_input = vaxis.widgets.TextInput.init(alloc);
64 defer cmd_input.deinit();
65
66 // Colors
67 const active_bg: vaxis.Cell.Color = .{ .rgb = .{ 64, 128, 255 } };
68 const selected_bg: vaxis.Cell.Color = .{ .rgb = .{ 32, 64, 255 } };
69 const other_bg: vaxis.Cell.Color = .{ .rgb = .{ 32, 32, 48 } };
70
71 // Table Context
72 var demo_tbl: vaxis.widgets.Table.TableContext = .{
73 .active_bg = active_bg,
74 .active_fg = .{ .rgb = .{ 0, 0, 0 } },
75 .row_bg_1 = .{ .rgb = .{ 8, 8, 8 } },
76 .selected_bg = selected_bg,
77 .header_names = .{ .custom = &.{ "First", "Last", "Username", "Phone#", "Email" } },
78 //.header_align = .left,
79 .col_indexes = .{ .by_idx = &.{ 0, 1, 2, 4, 3 } },
80 //.col_align = .{ .by_idx = &.{ .left, .left, .center, .center, .left } },
81 //.col_align = .{ .all = .center },
82 //.header_borders = true,
83 //.col_borders = true,
84 //.col_width = .{ .static_all = 15 },
85 //.col_width = .{ .dynamic_header_len = 3 },
86 //.col_width = .{ .static_individual = &.{ 10, 20, 15, 25, 15 } },
87 //.col_width = .dynamic_fill,
88 //.y_off = 10,
89 };
90 defer if (demo_tbl.sel_rows) |rows| alloc.free(rows);
91
92 // TUI State
93 var active: ActiveSection = .mid;
94 var moving = false;
95 var see_content = false;
96
97 // Create an Arena Allocator for easy allocations on each Event.
98 var event_arena: heap.ArenaAllocator = .init(alloc);
99 defer event_arena.deinit();
100 while (true) {
101 defer _ = event_arena.reset(.retain_capacity);
102 defer tty_writer.flush() catch {};
103 const event_alloc = event_arena.allocator();
104 const event = try loop.nextEvent();
105
106 switch (event) {
107 .key_press => |key| keyEvt: {
108 // Close the Program
109 if (key.matches('c', .{ .ctrl = true })) {
110 break;
111 }
112 // Refresh the Screen
113 if (key.matches('l', .{ .ctrl = true })) {
114 vx.queueRefresh();
115 break :keyEvt;
116 }
117 // Enter Moving State
118 if (key.matches('w', .{ .ctrl = true })) {
119 moving = !moving;
120 break :keyEvt;
121 }
122 // Command State
123 if (active != .btm and
124 key.matchesAny(&.{ ':', '/', 'g', 'G' }, .{}))
125 {
126 active = .btm;
127 cmd_input.clearAndFree();
128 try cmd_input.update(.{ .key_press = key });
129 break :keyEvt;
130 }
131
132 switch (active) {
133 .top => {
134 if (key.matchesAny(&.{ vaxis.Key.down, 'j' }, .{}) and moving) active = .mid;
135 },
136 .mid => midEvt: {
137 if (moving) {
138 if (key.matchesAny(&.{ vaxis.Key.up, 'k' }, .{})) active = .top;
139 if (key.matchesAny(&.{ vaxis.Key.down, 'j' }, .{})) active = .btm;
140 break :midEvt;
141 }
142 // Change Row
143 if (key.matchesAny(&.{ vaxis.Key.up, 'k' }, .{})) demo_tbl.row -|= 1;
144 if (key.matchesAny(&.{ vaxis.Key.down, 'j' }, .{})) demo_tbl.row +|= 1;
145 // Change Column
146 if (key.matchesAny(&.{ vaxis.Key.left, 'h' }, .{})) demo_tbl.col -|= 1;
147 if (key.matchesAny(&.{ vaxis.Key.right, 'l' }, .{})) demo_tbl.col +|= 1;
148 // Select/Unselect Row
149 if (key.matches(vaxis.Key.space, .{})) {
150 const rows = demo_tbl.sel_rows orelse createRows: {
151 demo_tbl.sel_rows = try alloc.alloc(u16, 1);
152 break :createRows demo_tbl.sel_rows.?;
153 };
154 var rows_list = std.ArrayList(u16).fromOwnedSlice(rows);
155 for (rows_list.items, 0..) |row, idx| {
156 if (row != demo_tbl.row) continue;
157 _ = rows_list.orderedRemove(idx);
158 break;
159 } else try rows_list.append(alloc, demo_tbl.row);
160 demo_tbl.sel_rows = try rows_list.toOwnedSlice(alloc);
161 }
162 // See Row Content
163 if (key.matches(vaxis.Key.enter, .{}) or key.matches('j', .{ .ctrl = true })) see_content = !see_content;
164 },
165 .btm => {
166 if (key.matchesAny(&.{ vaxis.Key.up, 'k' }, .{}) and moving) active = .mid
167 // Run Command and Clear Command Bar
168 else if (key.matchExact(vaxis.Key.enter, .{}) or key.matchExact('j', .{ .ctrl = true })) {
169 const cmd = try cmd_input.toOwnedSlice();
170 defer alloc.free(cmd);
171 if (mem.eql(u8, ":q", cmd) or
172 mem.eql(u8, ":quit", cmd) or
173 mem.eql(u8, ":exit", cmd)) return;
174 if (mem.eql(u8, "G", cmd)) {
175 demo_tbl.row = @intCast(users_buf.len - 1);
176 active = .mid;
177 }
178 if (cmd.len >= 2 and mem.eql(u8, "gg", cmd[0..2])) {
179 const goto_row = fmt.parseInt(u16, cmd[2..], 0) catch 0;
180 demo_tbl.row = goto_row;
181 active = .mid;
182 }
183 } else try cmd_input.update(.{ .key_press = key });
184 },
185 }
186 moving = false;
187 },
188 .winsize => |ws| try vx.resize(alloc, tty.writer(), ws),
189 else => {},
190 }
191
192 // Content
193 seeRow: {
194 if (!see_content) {
195 demo_tbl.active_content_fn = null;
196 demo_tbl.active_ctx = &{};
197 break :seeRow;
198 }
199 const RowContext = struct {
200 row: []const u8,
201 bg: vaxis.Color,
202 };
203 const row_ctx = RowContext{
204 .row = try fmt.allocPrint(event_alloc, "Row #: {d}", .{demo_tbl.row}),
205 .bg = demo_tbl.active_bg,
206 };
207 demo_tbl.active_ctx = &row_ctx;
208 demo_tbl.active_content_fn = struct {
209 fn see(win: *vaxis.Window, ctx_raw: *const anyopaque) !u16 {
210 const ctx: *const RowContext = @ptrCast(@alignCast(ctx_raw));
211 win.height = 5;
212 const see_win = win.child(.{
213 .x_off = 0,
214 .y_off = 1,
215 .width = win.width,
216 .height = 4,
217 });
218 see_win.fill(.{ .style = .{ .bg = ctx.bg } });
219 const content_logo =
220 \\
221 \\░█▀▄░█▀█░█░█░░░█▀▀░█▀█░█▀█░▀█▀░█▀▀░█▀█░▀█▀
222 \\░█▀▄░█░█░█▄█░░░█░░░█░█░█░█░░█░░█▀▀░█░█░░█░
223 \\░▀░▀░▀▀▀░▀░▀░░░▀▀▀░▀▀▀░▀░▀░░▀░░▀▀▀░▀░▀░░▀░
224 ;
225 const content_segs: []const vaxis.Cell.Segment = &.{
226 .{
227 .text = ctx.row,
228 .style = .{ .bg = ctx.bg },
229 },
230 .{
231 .text = content_logo,
232 .style = .{ .bg = ctx.bg },
233 },
234 };
235 _ = see_win.print(content_segs, .{});
236 return see_win.height;
237 }
238 }.see;
239 try loop.postEvent(.table_upd);
240 }
241
242 // Sections
243 // - Window
244 const win = vx.window();
245 win.clear();
246
247 // - Top
248 const top_div = 6;
249 const top_bar = win.child(.{
250 .x_off = 0,
251 .y_off = 0,
252 .width = win.width,
253 .height = win.height / top_div,
254 });
255 for (title_segs[0..]) |*title_seg|
256 title_seg.style.bg = if (active == .top) selected_bg else other_bg;
257 top_bar.fill(.{ .style = .{
258 .bg = if (active == .top) selected_bg else other_bg,
259 } });
260 const logo_bar = vaxis.widgets.alignment.center(
261 top_bar,
262 44,
263 top_bar.height - (top_bar.height / 3),
264 );
265 _ = logo_bar.print(title_segs[0..], .{ .wrap = .word });
266
267 // - Middle
268 const middle_bar = win.child(.{
269 .x_off = 0,
270 .y_off = win.height / top_div,
271 .width = win.width,
272 .height = win.height - (top_bar.height + 1),
273 });
274 if (users_buf.len > 0) {
275 demo_tbl.active = active == .mid;
276 try vaxis.widgets.Table.drawTable(
277 null,
278 // event_alloc,
279 middle_bar,
280 //users_buf[0..],
281 //user_list,
282 users_buf,
283 &demo_tbl,
284 );
285 }
286
287 // - Bottom
288 const bottom_bar = win.child(.{
289 .x_off = 0,
290 .y_off = win.height - 1,
291 .width = win.width,
292 .height = 1,
293 });
294 if (active == .btm) bottom_bar.fill(.{ .style = .{ .bg = active_bg } });
295 cmd_input.draw(bottom_bar);
296
297 // Render the screen
298 try vx.render(tty_writer);
299 }
300}
301
302/// User Struct
303pub const User = struct {
304 first: []const u8,
305 last: []const u8,
306 user: []const u8,
307 email: ?[]const u8 = null,
308 phone: ?[]const u8 = null,
309};
310
311// Users Array
312const users = [_]User{
313 .{ .first = "Nancy", .last = "Dudley", .user = "angela73", .email = "brian47@rodriguez.biz", .phone = null },
314 .{ .first = "Emily", .last = "Thornton", .user = "mrogers", .email = null, .phone = "(558)888-8604x094" },
315 .{ .first = "Kyle", .last = "Huff", .user = "xsmith", .email = null, .phone = "301.127.0801x12398" },
316 .{ .first = "Christine", .last = "Dodson", .user = "amandabradley", .email = "cheryl21@sullivan.com", .phone = null },
317 .{ .first = "Nathaniel", .last = "Kennedy", .user = "nrobinson", .email = null, .phone = null },
318 .{ .first = "Laura", .last = "Leon", .user = "dawnjones", .email = "fjenkins@patel.com", .phone = "1833013180" },
319 .{ .first = "Patrick", .last = "Landry", .user = "michaelhutchinson", .email = "daniel17@medina-wallace.net", .phone = "+1-634-486-6444x964" },
320 .{ .first = "Tammy", .last = "Hall", .user = "jamessmith", .email = null, .phone = "(926)810-3385x22059" },
321 .{ .first = "Stephanie", .last = "Anderson", .user = "wgillespie", .email = "campbelljaime@yahoo.com", .phone = null },
322 .{ .first = "Jennifer", .last = "Williams", .user = "shawn60", .email = null, .phone = "611-385-4771x97523" },
323 .{ .first = "Elizabeth", .last = "Ortiz", .user = "jennifer76", .email = "johnbradley@delgado.info", .phone = null },
324 .{ .first = "Stacy", .last = "Mays", .user = "scottgonzalez", .email = "kramermatthew@gmail.com", .phone = null },
325 .{ .first = "Jennifer", .last = "Smith", .user = "joseph75", .email = "masseyalexander@hill-moore.net", .phone = null },
326 .{ .first = "Gary", .last = "Hammond", .user = "brittany26", .email = null, .phone = null },
327 .{ .first = "Lisa", .last = "Johnson", .user = "tina28", .email = null, .phone = "850-606-2978x1081" },
328 .{ .first = "Zachary", .last = "Hopkins", .user = "vargasmichael", .email = null, .phone = null },
329 .{ .first = "Joshua", .last = "Kidd", .user = "ghanna", .email = "jbrown@yahoo.com", .phone = null },
330 .{ .first = "Dawn", .last = "Jones", .user = "alisonlindsey", .email = null, .phone = null },
331 .{ .first = "Monica", .last = "Berry", .user = "barbara40", .email = "michael00@hotmail.com", .phone = "(295)346-6453x343" },
332 .{ .first = "Shannon", .last = "Roberts", .user = "krystal37", .email = null, .phone = "980-920-9386x454" },
333 .{ .first = "Thomas", .last = "Mitchell", .user = "williamscorey", .email = "richardduncan@roberts.com", .phone = null },
334 .{ .first = "Nicole", .last = "Shaffer", .user = "rogerstroy", .email = null, .phone = "(570)128-5662" },
335 .{ .first = "Edward", .last = "Bennett", .user = "andersonchristina", .email = null, .phone = null },
336 .{ .first = "Duane", .last = "Howard", .user = "pcarpenter", .email = "griffithwayne@parker.net", .phone = null },
337 .{ .first = "Mary", .last = "Brown", .user = "kimberlyfrost", .email = "perezsara@anderson-andrews.net", .phone = null },
338 .{ .first = "Pamela", .last = "Sloan", .user = "kvelez", .email = "huynhlacey@moore-bell.biz", .phone = "001-359-125-1393x8716" },
339 .{ .first = "Timothy", .last = "Charles", .user = "anthony04", .email = "morrissara@hawkins.info", .phone = "+1-619-369-9572" },
340 .{ .first = "Sydney", .last = "Torres", .user = "scott42", .email = "asnyder@mitchell.net", .phone = null },
341 .{ .first = "John", .last = "Jones", .user = "anthonymoore", .email = null, .phone = "701.236.0571x99622" },
342 .{ .first = "Erik", .last = "Johnson", .user = "allisonsanders", .email = null, .phone = null },
343 .{ .first = "Donna", .last = "Kirk", .user = "laurie81", .email = null, .phone = null },
344 .{ .first = "Karina", .last = "White", .user = "uperez", .email = null, .phone = null },
345 .{ .first = "Jesse", .last = "Schwartz", .user = "ryan60", .email = "latoyawilliams@gmail.com", .phone = null },
346 .{ .first = "Cindy", .last = "Romero", .user = "christopher78", .email = "faulknerchristina@gmail.com", .phone = "780.288.2319x583" },
347 .{ .first = "Tyler", .last = "Sanders", .user = "bennettjessica", .email = null, .phone = "1966269423" },
348 .{ .first = "Pamela", .last = "Carter", .user = "zsnyder", .email = null, .phone = "125-062-9130x58413" },
349};
350
351test {
352 std.testing.refAllDecls(@This());
353}