feat(renderer): render cells instead of raw u8 array contents
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 39s
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 39s
This commit is contained in:
@@ -8,7 +8,7 @@ const terminal = @import("../terminal.zig");
|
||||
const isTaggedUnion = @import("../event.zig").isTaggedUnion;
|
||||
const Error = @import("../event.zig").Error;
|
||||
const Key = terminal.Key;
|
||||
const Style = terminal.Style;
|
||||
const Style = terminal.Cell.Style;
|
||||
|
||||
const log = std.log.scoped(.layout_framing);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ const std = @import("std");
|
||||
const terminal = @import("terminal.zig");
|
||||
|
||||
const Contents = std.ArrayList(u8); // TODO: this may contain more than just a single character! (i.e. styled)
|
||||
const Cells = []const terminal.Cell;
|
||||
const Position = terminal.Position;
|
||||
const Size = terminal.Size;
|
||||
|
||||
@@ -136,16 +137,18 @@ pub fn Direct(comptime _: bool) type {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(this: *@This(), size: Size, contents: []u8) !void {
|
||||
pub fn render(this: *@This(), size: Size, cells: Cells) !void {
|
||||
_ = this;
|
||||
try terminal.setCursorPosition(size.anchor);
|
||||
var row: u16 = 0;
|
||||
var idx: usize = 0;
|
||||
var skip_next_line = false;
|
||||
for (contents, 0..) |item, i| {
|
||||
if (item == '\n') { // do not print newlines
|
||||
if (!skip_next_line) {
|
||||
_ = try terminal.write(contents[idx..i]); // does not include '\n' at position _i_
|
||||
var remaining_cols = size.cols;
|
||||
const writer = terminal.writer();
|
||||
for (cells) |cell| {
|
||||
var idx: usize = 0;
|
||||
print_cell: while (true) {
|
||||
const cell_len = cell.len(idx);
|
||||
if (cell_len > remaining_cols) {
|
||||
const result = try cell.writeUpToNewline(writer, idx, idx + remaining_cols);
|
||||
row += 1;
|
||||
if (row >= size.rows) {
|
||||
return; // we are done
|
||||
@@ -154,34 +157,46 @@ pub fn Direct(comptime _: bool) type {
|
||||
.col = size.anchor.col,
|
||||
.row = size.anchor.row + row,
|
||||
});
|
||||
}
|
||||
skip_next_line = false;
|
||||
idx = i + 1; // skip over '\n'
|
||||
continue;
|
||||
}
|
||||
if (i - idx == size.cols) {
|
||||
// FIXME: this will introduce another additional line which is not accounted for and will cut off these lines in the end from rendering
|
||||
// -> *current solution*: cut of the line and skip that remaining content (not sure if that should be done)
|
||||
// - however the widget has actually knowledge about the size and could change the reported contents which fit to the size or simply don't care and leave it to the renderer?
|
||||
// flush line
|
||||
if (!skip_next_line) {
|
||||
_ = try terminal.write(contents[idx..i]);
|
||||
row += 1;
|
||||
if (row >= size.rows) {
|
||||
return; // we are done
|
||||
remaining_cols = size.cols;
|
||||
idx = result.idx;
|
||||
if (result.newline) {
|
||||
idx += 1; // skip over newline
|
||||
} else {
|
||||
// there is still content to the newline (which will not be printed)
|
||||
for (idx..cell.content.len) |i| {
|
||||
if (cell.content[i] == '\n') {
|
||||
idx = i + 1;
|
||||
continue :print_cell;
|
||||
}
|
||||
}
|
||||
break; // go to next cell (as we went to the end of the cell and do not print on the next line)
|
||||
}
|
||||
} else {
|
||||
// print rest of cell
|
||||
const result = try cell.writeUpToNewline(writer, idx, idx + cell_len);
|
||||
if (result.newline) {
|
||||
row += 1;
|
||||
if (row >= size.rows) {
|
||||
return; // we are done
|
||||
}
|
||||
try terminal.setCursorPosition(.{
|
||||
.col = size.anchor.col,
|
||||
.row = size.anchor.row + row,
|
||||
});
|
||||
remaining_cols = size.cols;
|
||||
idx = result.idx + 1; // skip over newline
|
||||
} else {
|
||||
remaining_cols -= @truncate(cell_len - idx);
|
||||
idx = 0;
|
||||
break; // go to next cell
|
||||
}
|
||||
try terminal.setCursorPosition(.{
|
||||
.col = size.anchor.col,
|
||||
.row = size.anchor.row + row,
|
||||
});
|
||||
}
|
||||
skip_next_line = true;
|
||||
idx = i;
|
||||
// written all cell contents
|
||||
if (idx >= cell.content.len) {
|
||||
break; // go to next cell
|
||||
}
|
||||
}
|
||||
}
|
||||
if (idx < contents.len) {
|
||||
_ = try terminal.write(contents[idx..]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flush(this: @This()) !void {
|
||||
|
||||
@@ -2,7 +2,7 @@ const std = @import("std");
|
||||
pub const Key = @import("terminal/Key.zig");
|
||||
pub const Size = @import("terminal/Size.zig");
|
||||
pub const Position = @import("terminal/Position.zig");
|
||||
pub const Style = @import("terminal/Style.zig");
|
||||
pub const Cell = @import("terminal/Cell.zig");
|
||||
pub const code_point = @import("code_point");
|
||||
|
||||
const log = std.log.scoped(.terminal);
|
||||
@@ -229,5 +229,5 @@ fn getReportMode(ps: u8) ReportMode {
|
||||
}
|
||||
|
||||
test {
|
||||
_ = Style;
|
||||
_ = Cell;
|
||||
}
|
||||
|
||||
63
src/terminal/Cell.zig
Normal file
63
src/terminal/Cell.zig
Normal file
@@ -0,0 +1,63 @@
|
||||
const std = @import("std");
|
||||
pub const Style = @import("Style.zig");
|
||||
|
||||
style: Style = .{},
|
||||
content: []u8 = undefined,
|
||||
|
||||
pub const Result = struct {
|
||||
idx: usize,
|
||||
newline: bool,
|
||||
};
|
||||
|
||||
pub fn len(this: @This(), start: usize) usize {
|
||||
std.debug.assert(this.content.len > start);
|
||||
return this.content[start..].len;
|
||||
}
|
||||
|
||||
pub fn write(this: @This(), writer: anytype, start: usize, end: usize) !void {
|
||||
std.debug.assert(this.content.len > start);
|
||||
std.debug.assert(this.content.len >= end);
|
||||
std.debug.assert(start < end);
|
||||
|
||||
try this.style.value(writer, this.content[start..end]);
|
||||
}
|
||||
|
||||
pub fn writeUpToNewline(this: @This(), writer: anytype, start: usize, end: usize) !Result {
|
||||
std.debug.assert(this.content.len > start);
|
||||
std.debug.assert(this.content.len >= end);
|
||||
std.debug.assert(start < end);
|
||||
|
||||
for (start..end) |i| {
|
||||
if (this.content[i] == '\n') {
|
||||
if (start < i) {
|
||||
// this is just an empty line with a newline
|
||||
try this.value(writer, start, i);
|
||||
}
|
||||
return .{
|
||||
.idx = i,
|
||||
.newline = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
try this.write(writer, start, end);
|
||||
return .{
|
||||
.idx = end,
|
||||
.newline = false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn value(this: @This(), writer: anytype, start: usize, end: usize) !void {
|
||||
std.debug.assert(start < this.content.len);
|
||||
std.debug.assert(this.content.len >= end);
|
||||
std.debug.assert(start < end);
|
||||
try this.style.value(writer, this.content[start..end]);
|
||||
}
|
||||
|
||||
// not really supported
|
||||
pub fn format(this: @This(), writer: anytype, comptime fmt: []const u8, args: anytype) !void {
|
||||
try this.style.format(writer, fmt, args); // NOTE: args should contain this.content[start..end] or this.content
|
||||
}
|
||||
|
||||
test {
|
||||
_ = Style;
|
||||
}
|
||||
@@ -78,17 +78,23 @@ pub fn Widget(comptime Event: type, comptime Renderer: type) type {
|
||||
pub fn render(this: *@This(), renderer: *Renderer) !void {
|
||||
try renderer.clear(this.size);
|
||||
if (this.size.rows >= this.line_index.items.len) {
|
||||
try renderer.render(this.size, this.contents.items);
|
||||
try renderer.render(this.size, &[_]terminal.Cell{
|
||||
.{ .content = this.contents.items, .style = .{ .dim = true, .fg = .{ .index = 8 } } },
|
||||
});
|
||||
} else {
|
||||
// more rows than we can display
|
||||
const i = this.line_index.items[this.line];
|
||||
const e = this.size.rows + this.line;
|
||||
if (e > this.line_index.items.len) {
|
||||
try renderer.render(this.size, this.contents.items[i..]);
|
||||
try renderer.render(this.size, &[_]terminal.Cell{
|
||||
.{ .content = this.contents.items[i..], .style = .{ .dim = true, .fg = .{ .index = 7 } } },
|
||||
});
|
||||
return;
|
||||
}
|
||||
const x = this.line_index.items[e];
|
||||
try renderer.render(this.size, this.contents.items[i..x]);
|
||||
try renderer.render(this.size, &[_]terminal.Cell{
|
||||
.{ .content = this.contents.items[i..x], .style = .{ .dim = true, .fg = .{ .index = 9 } } },
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ pub const Renderer = @import("render.zig");
|
||||
pub const Key = terminal.Key;
|
||||
pub const Position = terminal.Position;
|
||||
pub const Size = terminal.Size;
|
||||
pub const Style = terminal.Style;
|
||||
pub const Cell = terminal.Cell;
|
||||
|
||||
test {
|
||||
_ = @import("terminal.zig");
|
||||
|
||||
Reference in New Issue
Block a user