this repo has no description
13
fork

Configure Feed

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

window: implement wrap and introduce Segment type

Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>

+75
+1
src/Screen.zig
··· 22 22 cursor_col: usize = 0, 23 23 cursor_vis: bool = false, 24 24 25 + /// true when we measure cells with unicode 25 26 unicode: bool = false, 26 27 27 28 mouse_shape: Shape = .default,
+66
src/Window.zig
··· 1 1 const std = @import("std"); 2 + const ziglyph = @import("ziglyph"); 3 + const WordIterator = ziglyph.WordIterator; 4 + const GraphemeIterator = ziglyph.GraphemeIterator; 2 5 3 6 const Screen = @import("Screen.zig"); 4 7 const Cell = @import("cell.zig").Cell; 8 + const Segment = @import("cell.zig").Segment; 5 9 const gw = @import("gwidth.zig"); 6 10 7 11 const log = std.log.scoped(.window); ··· 102 106 self.screen.cursor_vis = true; 103 107 self.screen.cursor_row = row + self.y_off; 104 108 self.screen.cursor_col = col + self.x_off; 109 + } 110 + 111 + /// prints text in the window with simple word wrapping. 112 + pub fn wrap(self: Window, segments: []Segment) !void { 113 + // pub fn wrap(self: Window, str: []const u8) !void { 114 + var row: usize = 0; 115 + var col: usize = 0; 116 + var wrapped: bool = false; 117 + for (segments) |segment| { 118 + var word_iter = try WordIterator.init(segment.text); 119 + while (word_iter.next()) |word| { 120 + // break lines when we need 121 + if (isLineBreak(word.bytes)) { 122 + row += 1; 123 + col = 0; 124 + wrapped = false; 125 + continue; 126 + } 127 + // break lines when we can't fit this word, and the word isn't longer 128 + // than our width 129 + const word_width = self.gwidth(word.bytes); 130 + if (word_width + col >= self.width and word_width < self.width) { 131 + row += 1; 132 + col = 0; 133 + wrapped = true; 134 + } 135 + // don't print whitespace in the first column, unless we had a hard 136 + // break 137 + if (col == 0 and std.mem.eql(u8, word.bytes, " ") and wrapped) continue; 138 + var iter = GraphemeIterator.init(word.bytes); 139 + while (iter.next()) |grapheme| { 140 + if (col >= self.width) { 141 + row += 1; 142 + col = 0; 143 + wrapped = true; 144 + } 145 + const s = grapheme.slice(word.bytes); 146 + const w = self.gwidth(s); 147 + self.writeCell(col, row, .{ 148 + .char = .{ 149 + .grapheme = s, 150 + .width = w, 151 + }, 152 + .style = segment.style, 153 + .link = segment.link, 154 + }); 155 + col += w; 156 + } 157 + } 158 + } 159 + } 160 + 161 + fn isLineBreak(str: []const u8) bool { 162 + if (std.mem.eql(u8, str, "\r\n")) { 163 + return true; 164 + } else if (std.mem.eql(u8, str, "\r")) { 165 + return true; 166 + } else if (std.mem.eql(u8, str, "\n")) { 167 + return true; 168 + } else { 169 + return false; 170 + } 105 171 } 106 172 107 173 test "Window size set" {
+7
src/cell.zig
··· 7 7 image: ?Image.Placement = null, 8 8 }; 9 9 10 + /// Segment is a contiguous run of text that has a constant style 11 + pub const Segment = struct { 12 + text: []const u8, 13 + style: Style = .{}, 14 + link: Hyperlink = .{}, 15 + }; 16 + 10 17 pub const Character = struct { 11 18 grapheme: []const u8 = " ", 12 19 /// width should only be provided when the application is sure the terminal
+1
src/main.zig
··· 4 4 const cell = @import("cell.zig"); 5 5 pub const Cell = cell.Cell; 6 6 pub const Style = cell.Style; 7 + pub const Segment = cell.Segment; 7 8 8 9 pub const Key = @import("Key.zig"); 9 10 pub const Mouse = @import("Mouse.zig");