this repo has no description
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 01605eebf65be21caa85ee9d840047f864553308 191 lines 6.1 kB view raw
1const std = @import("std"); 2const vaxis = @import("../main.zig"); 3const grapheme = @import("grapheme"); 4const DisplayWidth = @import("DisplayWidth"); 5const ScrollView = vaxis.widgets.ScrollView; 6 7pub const BufferWriter = struct { 8 pub const Error = error{OutOfMemory}; 9 pub const Writer = std.io.GenericWriter(@This(), Error, write); 10 11 allocator: std.mem.Allocator, 12 buffer: *Buffer, 13 gd: *const grapheme.GraphemeData, 14 wd: *const DisplayWidth.DisplayWidthData, 15 16 pub fn write(self: @This(), bytes: []const u8) Error!usize { 17 try self.buffer.append(self.allocator, .{ 18 .bytes = bytes, 19 .gd = self.gd, 20 .wd = self.wd, 21 }); 22 return bytes.len; 23 } 24 25 pub fn writer(self: @This()) Writer { 26 return .{ .context = self }; 27 } 28}; 29 30pub const Buffer = struct { 31 const StyleList = std.ArrayListUnmanaged(vaxis.Style); 32 const StyleMap = std.HashMapUnmanaged(usize, usize, std.hash_map.AutoContext(usize), std.hash_map.default_max_load_percentage); 33 34 pub const Content = struct { 35 bytes: []const u8, 36 gd: *const grapheme.GraphemeData, 37 wd: *const DisplayWidth.DisplayWidthData, 38 }; 39 40 pub const Style = struct { 41 begin: usize, 42 end: usize, 43 style: vaxis.Style, 44 }; 45 46 pub const Error = error{OutOfMemory}; 47 48 grapheme: std.MultiArrayList(grapheme.Grapheme) = .{}, 49 content: std.ArrayListUnmanaged(u8) = .{}, 50 style_list: StyleList = .{}, 51 style_map: StyleMap = .{}, 52 rows: usize = 0, 53 cols: usize = 0, 54 // used when appending to a buffer 55 last_cols: usize = 0, 56 57 pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void { 58 self.style_map.deinit(allocator); 59 self.style_list.deinit(allocator); 60 self.grapheme.deinit(allocator); 61 self.content.deinit(allocator); 62 self.* = undefined; 63 } 64 65 /// Clears all buffer data. 66 pub fn clear(self: *@This(), allocator: std.mem.Allocator) void { 67 self.deinit(allocator); 68 self.* = .{}; 69 } 70 71 /// Replaces contents of the buffer, all previous buffer data is lost. 72 pub fn update(self: *@This(), allocator: std.mem.Allocator, content: Content) Error!void { 73 self.clear(allocator); 74 errdefer self.clear(allocator); 75 try self.append(allocator, content); 76 } 77 78 /// Appends content to the buffer. 79 pub fn append(self: *@This(), allocator: std.mem.Allocator, content: Content) Error!void { 80 var cols: usize = self.last_cols; 81 var iter = grapheme.Iterator.init(content.bytes, content.gd); 82 const dw: DisplayWidth = .{ .data = content.wd }; 83 while (iter.next()) |g| { 84 try self.grapheme.append(allocator, .{ 85 .len = g.len, 86 .offset = @as(u32, @intCast(self.content.items.len)) + g.offset, 87 }); 88 const cluster = g.bytes(content.bytes); 89 if (std.mem.eql(u8, cluster, "\n")) { 90 self.cols = @max(self.cols, cols); 91 cols = 0; 92 continue; 93 } 94 cols +|= dw.strWidth(cluster); 95 } 96 try self.content.appendSlice(allocator, content.bytes); 97 self.last_cols = cols; 98 self.cols = @max(self.cols, cols); 99 self.rows +|= std.mem.count(u8, content.bytes, "\n"); 100 } 101 102 /// Clears all styling data. 103 pub fn clearStyle(self: *@This(), allocator: std.mem.Allocator) void { 104 self.style_list.deinit(allocator); 105 self.style_map.deinit(allocator); 106 } 107 108 /// Update style for range of the buffer contents. 109 pub fn updateStyle(self: *@This(), allocator: std.mem.Allocator, style: Style) Error!void { 110 const style_index = blk: { 111 for (self.style_list.items, 0..) |s, i| { 112 if (std.meta.eql(s, style.style)) { 113 break :blk i; 114 } 115 } 116 try self.style_list.append(allocator, style.style); 117 break :blk self.style_list.items.len - 1; 118 }; 119 for (style.begin..style.end) |i| { 120 try self.style_map.put(allocator, i, style_index); 121 } 122 } 123 124 pub fn writer( 125 self: *@This(), 126 allocator: std.mem.Allocator, 127 gd: *const grapheme.GraphemeData, 128 wd: *const DisplayWidth.DisplayWidthData, 129 ) BufferWriter.Writer { 130 return .{ 131 .context = .{ 132 .allocator = allocator, 133 .buffer = self, 134 .gd = gd, 135 .wd = wd, 136 }, 137 }; 138 } 139}; 140 141scroll_view: ScrollView = .{}, 142 143pub fn input(self: *@This(), key: vaxis.Key) void { 144 self.scroll_view.input(key); 145} 146 147pub fn draw(self: *@This(), win: vaxis.Window, buffer: Buffer) void { 148 self.scroll_view.draw(win, .{ .cols = buffer.cols, .rows = buffer.rows }); 149 const Pos = struct { x: usize = 0, y: usize = 0 }; 150 var pos: Pos = .{}; 151 var byte_index: usize = 0; 152 const bounds = self.scroll_view.bounds(win); 153 for (buffer.grapheme.items(.len), buffer.grapheme.items(.offset), 0..) |g_len, g_offset, index| { 154 if (bounds.above(pos.y)) { 155 break; 156 } 157 158 const cluster = buffer.content.items[g_offset..][0..g_len]; 159 defer byte_index += cluster.len; 160 161 if (std.mem.eql(u8, cluster, "\n")) { 162 if (index == buffer.grapheme.len - 1) { 163 break; 164 } 165 pos.y +|= 1; 166 pos.x = 0; 167 continue; 168 } else if (bounds.below(pos.y)) { 169 continue; 170 } 171 172 const width = win.gwidth(cluster); 173 defer pos.x +|= width; 174 175 if (!bounds.colInside(pos.x)) { 176 continue; 177 } 178 179 const style: vaxis.Style = blk: { 180 if (buffer.style_map.get(byte_index)) |style_index| { 181 break :blk buffer.style_list.items[style_index]; 182 } 183 break :blk .{}; 184 }; 185 186 self.scroll_view.writeCell(win, pos.x, pos.y, .{ 187 .char = .{ .grapheme = cluster, .width = @intCast(width) }, 188 .style = style, 189 }); 190 } 191}