···2222cursor_col: usize = 0,
2323cursor_vis: bool = false,
24242525+/// true when we measure cells with unicode
2526unicode: bool = false,
26272728mouse_shape: Shape = .default,
+66
src/Window.zig
···11const std = @import("std");
22+const ziglyph = @import("ziglyph");
33+const WordIterator = ziglyph.WordIterator;
44+const GraphemeIterator = ziglyph.GraphemeIterator;
2536const Screen = @import("Screen.zig");
47const Cell = @import("cell.zig").Cell;
88+const Segment = @import("cell.zig").Segment;
59const gw = @import("gwidth.zig");
610711const log = std.log.scoped(.window);
···102106 self.screen.cursor_vis = true;
103107 self.screen.cursor_row = row + self.y_off;
104108 self.screen.cursor_col = col + self.x_off;
109109+}
110110+111111+/// prints text in the window with simple word wrapping.
112112+pub fn wrap(self: Window, segments: []Segment) !void {
113113+ // pub fn wrap(self: Window, str: []const u8) !void {
114114+ var row: usize = 0;
115115+ var col: usize = 0;
116116+ var wrapped: bool = false;
117117+ for (segments) |segment| {
118118+ var word_iter = try WordIterator.init(segment.text);
119119+ while (word_iter.next()) |word| {
120120+ // break lines when we need
121121+ if (isLineBreak(word.bytes)) {
122122+ row += 1;
123123+ col = 0;
124124+ wrapped = false;
125125+ continue;
126126+ }
127127+ // break lines when we can't fit this word, and the word isn't longer
128128+ // than our width
129129+ const word_width = self.gwidth(word.bytes);
130130+ if (word_width + col >= self.width and word_width < self.width) {
131131+ row += 1;
132132+ col = 0;
133133+ wrapped = true;
134134+ }
135135+ // don't print whitespace in the first column, unless we had a hard
136136+ // break
137137+ if (col == 0 and std.mem.eql(u8, word.bytes, " ") and wrapped) continue;
138138+ var iter = GraphemeIterator.init(word.bytes);
139139+ while (iter.next()) |grapheme| {
140140+ if (col >= self.width) {
141141+ row += 1;
142142+ col = 0;
143143+ wrapped = true;
144144+ }
145145+ const s = grapheme.slice(word.bytes);
146146+ const w = self.gwidth(s);
147147+ self.writeCell(col, row, .{
148148+ .char = .{
149149+ .grapheme = s,
150150+ .width = w,
151151+ },
152152+ .style = segment.style,
153153+ .link = segment.link,
154154+ });
155155+ col += w;
156156+ }
157157+ }
158158+ }
159159+}
160160+161161+fn isLineBreak(str: []const u8) bool {
162162+ if (std.mem.eql(u8, str, "\r\n")) {
163163+ return true;
164164+ } else if (std.mem.eql(u8, str, "\r")) {
165165+ return true;
166166+ } else if (std.mem.eql(u8, str, "\n")) {
167167+ return true;
168168+ } else {
169169+ return false;
170170+ }
105171}
106172107173test "Window size set" {
+7
src/cell.zig
···77 image: ?Image.Placement = null,
88};
991010+/// Segment is a contiguous run of text that has a constant style
1111+pub const Segment = struct {
1212+ text: []const u8,
1313+ style: Style = .{},
1414+ link: Hyperlink = .{},
1515+};
1616+1017pub const Character = struct {
1118 grapheme: []const u8 = " ",
1219 /// width should only be provided when the application is sure the terminal