window: add print method
Add a print method with multiple print options Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
This commit is contained in:
parent
2fab89f2cb
commit
e281a67a43
1 changed files with 123 additions and 46 deletions
169
src/Window.zig
169
src/Window.zig
|
@ -108,54 +108,131 @@ pub fn showCursor(self: Window, col: usize, row: usize) void {
|
|||
self.screen.cursor_col = col + self.x_off;
|
||||
}
|
||||
|
||||
/// Options to use when printing Segments to a window
|
||||
pub const PrintOptions = struct {
|
||||
/// vertical offset to start printing at
|
||||
row_offset: usize = 0,
|
||||
|
||||
/// wrap behavior for printing
|
||||
wrap: enum {
|
||||
/// wrap at grapheme boundaries
|
||||
grapheme,
|
||||
/// wrap at word boundaries
|
||||
word,
|
||||
/// stop printing after one line
|
||||
none,
|
||||
} = .grapheme,
|
||||
};
|
||||
|
||||
/// prints segments to the window. returns true if the text overflowed with the
|
||||
/// given wrap strategy and size.
|
||||
pub fn print(self: Window, segments: []Segment, opts: PrintOptions) !bool {
|
||||
var row = opts.row_offset;
|
||||
switch (opts.wrap) {
|
||||
.grapheme => {
|
||||
var col: usize = 0;
|
||||
for (segments) |segment| {
|
||||
var iter = GraphemeIterator.init(segment.text);
|
||||
while (iter.next()) |grapheme| {
|
||||
if (col >= self.width) {
|
||||
row += 1;
|
||||
col = 0;
|
||||
}
|
||||
if (row >= self.height) return true;
|
||||
const s = grapheme.slice(segment.text);
|
||||
if (std.mem.eql(u8, s, "\n")) {
|
||||
row += 1;
|
||||
col = 0;
|
||||
continue;
|
||||
}
|
||||
const w = self.gwidth(s);
|
||||
self.writeCell(col, row, .{
|
||||
.char = .{
|
||||
.grapheme = s,
|
||||
.width = w,
|
||||
},
|
||||
.style = segment.style,
|
||||
.link = segment.link,
|
||||
});
|
||||
col += w;
|
||||
}
|
||||
}
|
||||
},
|
||||
.word => {
|
||||
var col: usize = 0;
|
||||
var wrapped: bool = false;
|
||||
for (segments) |segment| {
|
||||
var word_iter = try WordIterator.init(segment.text);
|
||||
while (word_iter.next()) |word| {
|
||||
// break lines when we need
|
||||
if (word.bytes[0] == '\r' or word.bytes[0] == '\n') {
|
||||
row += 1;
|
||||
col = 0;
|
||||
wrapped = false;
|
||||
continue;
|
||||
}
|
||||
// break lines when we can't fit this word, and the word isn't longer
|
||||
// than our width
|
||||
const word_width = self.gwidth(word.bytes);
|
||||
if (word_width + col >= self.width and word_width < self.width) {
|
||||
row += 1;
|
||||
col = 0;
|
||||
wrapped = true;
|
||||
}
|
||||
if (row >= self.height) return true;
|
||||
// don't print whitespace in the first column, unless we had a hard
|
||||
// break
|
||||
if (col == 0 and std.mem.eql(u8, word.bytes, " ") and wrapped) continue;
|
||||
var iter = GraphemeIterator.init(word.bytes);
|
||||
while (iter.next()) |grapheme| {
|
||||
if (col >= self.width) {
|
||||
row += 1;
|
||||
col = 0;
|
||||
wrapped = true;
|
||||
}
|
||||
const s = grapheme.slice(word.bytes);
|
||||
const w = self.gwidth(s);
|
||||
self.writeCell(col, row, .{
|
||||
.char = .{
|
||||
.grapheme = s,
|
||||
.width = w,
|
||||
},
|
||||
.style = segment.style,
|
||||
.link = segment.link,
|
||||
});
|
||||
col += w;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
.none => {
|
||||
var col: usize = 0;
|
||||
for (segments) |segment| {
|
||||
var iter = GraphemeIterator.init(segment.text);
|
||||
while (iter.next()) |grapheme| {
|
||||
if (col >= self.width) return true;
|
||||
const s = grapheme.slice(segment.text);
|
||||
if (std.mem.eql(u8, s, "\n")) return true;
|
||||
const w = self.gwidth(s);
|
||||
self.writeCell(col, row, .{
|
||||
.char = .{
|
||||
.grapheme = s,
|
||||
.width = w,
|
||||
},
|
||||
.style = segment.style,
|
||||
.link = segment.link,
|
||||
});
|
||||
col += w;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// prints text in the window with simple word wrapping.
|
||||
pub fn wrap(self: Window, segments: []Segment) !void {
|
||||
// pub fn wrap(self: Window, str: []const u8) !void {
|
||||
var row: usize = 0;
|
||||
var col: usize = 0;
|
||||
var wrapped: bool = false;
|
||||
for (segments) |segment| {
|
||||
var word_iter = try WordIterator.init(segment.text);
|
||||
while (word_iter.next()) |word| {
|
||||
// break lines when we need
|
||||
if (word.bytes[0] == '\r' or word.bytes[0] == '\n') {
|
||||
row += 1;
|
||||
col = 0;
|
||||
wrapped = false;
|
||||
continue;
|
||||
}
|
||||
// break lines when we can't fit this word, and the word isn't longer
|
||||
// than our width
|
||||
const word_width = self.gwidth(word.bytes);
|
||||
if (word_width + col >= self.width and word_width < self.width) {
|
||||
row += 1;
|
||||
col = 0;
|
||||
wrapped = true;
|
||||
}
|
||||
// don't print whitespace in the first column, unless we had a hard
|
||||
// break
|
||||
if (col == 0 and std.mem.eql(u8, word.bytes, " ") and wrapped) continue;
|
||||
var iter = GraphemeIterator.init(word.bytes);
|
||||
while (iter.next()) |grapheme| {
|
||||
if (col >= self.width) {
|
||||
row += 1;
|
||||
col = 0;
|
||||
wrapped = true;
|
||||
}
|
||||
const s = grapheme.slice(word.bytes);
|
||||
const w = self.gwidth(s);
|
||||
self.writeCell(col, row, .{
|
||||
.char = .{
|
||||
.grapheme = s,
|
||||
.width = w,
|
||||
},
|
||||
.style = segment.style,
|
||||
.link = segment.link,
|
||||
});
|
||||
col += w;
|
||||
}
|
||||
}
|
||||
}
|
||||
return self.print(segments, .{ .wrap = .word });
|
||||
}
|
||||
|
||||
test "Window size set" {
|
||||
|
|
Loading…
Reference in a new issue