a modern tui library written in zig
1const std = @import("std");
2const vaxis = @import("../main.zig");
3const ScrollView = vaxis.widgets.ScrollView;
4const LineNumbers = vaxis.widgets.LineNumbers;
5
6pub const DrawOptions = struct {
7 highlighted_line: u16 = 0,
8 draw_line_numbers: bool = true,
9 indentation: u16 = 0,
10};
11
12pub const Buffer = vaxis.widgets.TextView.Buffer;
13
14scroll_view: ScrollView = .{ .vertical_scrollbar = null },
15highlighted_style: vaxis.Style = .{ .bg = .{ .index = 0 } },
16indentation_cell: vaxis.Cell = .{
17 .char = .{
18 .grapheme = "┆",
19 .width = 1,
20 },
21 .style = .{ .dim = true },
22},
23
24pub fn input(self: *@This(), key: vaxis.Key) void {
25 self.scroll_view.input(key);
26}
27
28pub fn draw(self: *@This(), win: vaxis.Window, buffer: Buffer, opts: DrawOptions) void {
29 const pad_left: usize = if (opts.draw_line_numbers) LineNumbers.numDigits(buffer.rows) +| 1 else 0;
30 self.scroll_view.draw(win, .{
31 .cols = buffer.cols + pad_left,
32 .rows = buffer.rows,
33 });
34 if (opts.draw_line_numbers) {
35 var nl: LineNumbers = .{
36 .highlighted_line = opts.highlighted_line,
37 .num_lines = buffer.rows +| 1,
38 };
39 nl.draw(win.child(.{
40 .x_off = 0,
41 .y_off = 0,
42 .width = pad_left,
43 .height = win.height,
44 }), self.scroll_view.scroll.y);
45 }
46 self.drawCode(win.child(.{ .x_off = pad_left }), buffer, opts);
47}
48
49fn drawCode(self: *@This(), win: vaxis.Window, buffer: Buffer, opts: DrawOptions) void {
50 const Pos = struct { x: usize = 0, y: usize = 0 };
51 var pos: Pos = .{};
52 var byte_index: usize = 0;
53 var is_indentation = true;
54 const bounds = self.scroll_view.bounds(win);
55 for (buffer.grapheme.items(.len), buffer.grapheme.items(.offset), 0..) |g_len, g_offset, index| {
56 if (bounds.above(pos.y)) {
57 break;
58 }
59
60 const cluster = buffer.content.items[g_offset..][0..g_len];
61 defer byte_index += cluster.len;
62
63 if (std.mem.eql(u8, cluster, "\n")) {
64 if (index == buffer.grapheme.len - 1) {
65 break;
66 }
67 pos.y += 1;
68 pos.x = 0;
69 is_indentation = true;
70 continue;
71 } else if (bounds.below(pos.y)) {
72 continue;
73 }
74
75 const highlighted_line = pos.y +| 1 == opts.highlighted_line;
76 var style: vaxis.Style = if (highlighted_line) self.highlighted_style else .{};
77
78 if (buffer.style_map.get(byte_index)) |meta| {
79 const tmp = style.bg;
80 style = buffer.style_list.items[meta];
81 style.bg = tmp;
82 }
83
84 const width = win.gwidth(cluster);
85 defer pos.x +|= width;
86
87 if (!bounds.colInside(pos.x)) {
88 continue;
89 }
90
91 if (opts.indentation > 0 and !std.mem.eql(u8, cluster, " ")) {
92 is_indentation = false;
93 }
94
95 if (is_indentation and opts.indentation > 0 and pos.x % opts.indentation == 0) {
96 var cell = self.indentation_cell;
97 cell.style.bg = style.bg;
98 self.scroll_view.writeCell(win, pos.x, pos.y, cell);
99 } else {
100 self.scroll_view.writeCell(win, pos.x, pos.y, .{
101 .char = .{ .grapheme = cluster, .width = @intCast(width) },
102 .style = style,
103 });
104 }
105
106 if (highlighted_line) {
107 for (pos.x +| width..bounds.x2) |x| {
108 self.scroll_view.writeCell(win, x, pos.y, .{ .style = style });
109 }
110 }
111 }
112}