a modern tui library written in zig
at v0.2.1 3.7 kB view raw
1const std = @import("std"); 2const assert = std.debug.assert; 3const Style = @import("Cell.zig").Style; 4const Cell = @import("Cell.zig"); 5const MouseShape = @import("Mouse.zig").Shape; 6const CursorShape = Cell.CursorShape; 7 8const log = std.log.scoped(.internal_screen); 9 10const InternalScreen = @This(); 11 12pub const InternalCell = struct { 13 char: std.ArrayList(u8) = undefined, 14 style: Style = .{}, 15 uri: std.ArrayList(u8) = undefined, 16 uri_id: std.ArrayList(u8) = undefined, 17 // if we got skipped because of a wide character 18 skipped: bool = false, 19 default: bool = true, 20 21 pub fn eql(self: InternalCell, cell: Cell) bool { 22 // fastpath when both cells are default 23 if (self.default and cell.default) return true; 24 // this is actually faster than std.meta.eql on the individual items. 25 // Our strings are always small, usually less than 4 bytes so the simd 26 // usage in std.mem.eql has too much overhead vs looping the bytes 27 if (!std.mem.eql(u8, self.char.items, cell.char.grapheme)) return false; 28 if (!Style.eql(self.style, cell.style)) return false; 29 if (!std.mem.eql(u8, self.uri.items, cell.link.uri)) return false; 30 if (!std.mem.eql(u8, self.uri_id.items, cell.link.params)) return false; 31 return true; 32 } 33}; 34 35width: usize = 0, 36height: usize = 0, 37 38buf: []InternalCell = undefined, 39 40cursor_row: usize = 0, 41cursor_col: usize = 0, 42cursor_vis: bool = false, 43cursor_shape: CursorShape = .default, 44 45mouse_shape: MouseShape = .default, 46 47/// sets each cell to the default cell 48pub fn init(alloc: std.mem.Allocator, w: usize, h: usize) !InternalScreen { 49 var screen = InternalScreen{ 50 .buf = try alloc.alloc(InternalCell, w * h), 51 }; 52 for (screen.buf, 0..) |_, i| { 53 screen.buf[i] = .{ 54 .char = try std.ArrayList(u8).initCapacity(alloc, 1), 55 .uri = std.ArrayList(u8).init(alloc), 56 .uri_id = std.ArrayList(u8).init(alloc), 57 }; 58 try screen.buf[i].char.append(' '); 59 } 60 screen.width = w; 61 screen.height = h; 62 return screen; 63} 64 65pub fn deinit(self: *InternalScreen, alloc: std.mem.Allocator) void { 66 for (self.buf, 0..) |_, i| { 67 self.buf[i].char.deinit(); 68 self.buf[i].uri.deinit(); 69 self.buf[i].uri_id.deinit(); 70 } 71 72 alloc.free(self.buf); 73} 74 75/// writes a cell to a location. 0 indexed 76pub fn writeCell( 77 self: *InternalScreen, 78 col: usize, 79 row: usize, 80 cell: Cell, 81) void { 82 if (self.width < col) { 83 // column out of bounds 84 return; 85 } 86 if (self.height < row) { 87 // height out of bounds 88 return; 89 } 90 const i = (row * self.width) + col; 91 assert(i < self.buf.len); 92 self.buf[i].char.clearRetainingCapacity(); 93 self.buf[i].char.appendSlice(cell.char.grapheme) catch { 94 log.warn("couldn't write grapheme", .{}); 95 }; 96 self.buf[i].uri.clearRetainingCapacity(); 97 self.buf[i].uri.appendSlice(cell.link.uri) catch { 98 log.warn("couldn't write uri", .{}); 99 }; 100 self.buf[i].uri_id.clearRetainingCapacity(); 101 self.buf[i].uri_id.appendSlice(cell.link.params) catch { 102 log.warn("couldn't write uri_id", .{}); 103 }; 104 self.buf[i].style = cell.style; 105 self.buf[i].default = cell.default; 106} 107 108pub fn readCell(self: *InternalScreen, col: usize, row: usize) ?Cell { 109 if (self.width < col) { 110 // column out of bounds 111 return null; 112 } 113 if (self.height < row) { 114 // height out of bounds 115 return null; 116 } 117 const i = (row * self.width) + col; 118 assert(i < self.buf.len); 119 return .{ 120 .char = .{ .grapheme = self.buf[i].char.items }, 121 .style = self.buf[i].style, 122 }; 123}