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 isTaggedUnion = @import("../event.zig").isTaggedUnion;
|
||||||
const Error = @import("../event.zig").Error;
|
const Error = @import("../event.zig").Error;
|
||||||
const Key = terminal.Key;
|
const Key = terminal.Key;
|
||||||
const Style = terminal.Style;
|
const Style = terminal.Cell.Style;
|
||||||
|
|
||||||
const log = std.log.scoped(.layout_framing);
|
const log = std.log.scoped(.layout_framing);
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const std = @import("std");
|
|||||||
const terminal = @import("terminal.zig");
|
const terminal = @import("terminal.zig");
|
||||||
|
|
||||||
const Contents = std.ArrayList(u8); // TODO: this may contain more than just a single character! (i.e. styled)
|
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 Position = terminal.Position;
|
||||||
const Size = terminal.Size;
|
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;
|
_ = this;
|
||||||
try terminal.setCursorPosition(size.anchor);
|
try terminal.setCursorPosition(size.anchor);
|
||||||
var row: u16 = 0;
|
var row: u16 = 0;
|
||||||
|
var remaining_cols = size.cols;
|
||||||
|
const writer = terminal.writer();
|
||||||
|
for (cells) |cell| {
|
||||||
var idx: usize = 0;
|
var idx: usize = 0;
|
||||||
var skip_next_line = false;
|
print_cell: while (true) {
|
||||||
for (contents, 0..) |item, i| {
|
const cell_len = cell.len(idx);
|
||||||
if (item == '\n') { // do not print newlines
|
if (cell_len > remaining_cols) {
|
||||||
if (!skip_next_line) {
|
const result = try cell.writeUpToNewline(writer, idx, idx + remaining_cols);
|
||||||
_ = try terminal.write(contents[idx..i]); // does not include '\n' at position _i_
|
|
||||||
row += 1;
|
row += 1;
|
||||||
if (row >= size.rows) {
|
if (row >= size.rows) {
|
||||||
return; // we are done
|
return; // we are done
|
||||||
@@ -154,18 +157,24 @@ pub fn Direct(comptime _: bool) type {
|
|||||||
.col = size.anchor.col,
|
.col = size.anchor.col,
|
||||||
.row = size.anchor.row + row,
|
.row = size.anchor.row + row,
|
||||||
});
|
});
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
skip_next_line = false;
|
|
||||||
idx = i + 1; // skip over '\n'
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
if (i - idx == size.cols) {
|
break; // go to next cell (as we went to the end of the cell and do not print on the next line)
|
||||||
// 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)
|
} else {
|
||||||
// - 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?
|
// print rest of cell
|
||||||
// flush line
|
const result = try cell.writeUpToNewline(writer, idx, idx + cell_len);
|
||||||
if (!skip_next_line) {
|
if (result.newline) {
|
||||||
_ = try terminal.write(contents[idx..i]);
|
|
||||||
row += 1;
|
row += 1;
|
||||||
if (row >= size.rows) {
|
if (row >= size.rows) {
|
||||||
return; // we are done
|
return; // we are done
|
||||||
@@ -174,13 +183,19 @@ pub fn Direct(comptime _: bool) type {
|
|||||||
.col = size.anchor.col,
|
.col = size.anchor.col,
|
||||||
.row = size.anchor.row + row,
|
.row = size.anchor.row + row,
|
||||||
});
|
});
|
||||||
}
|
remaining_cols = size.cols;
|
||||||
skip_next_line = true;
|
idx = result.idx + 1; // skip over newline
|
||||||
idx = i;
|
} else {
|
||||||
|
remaining_cols -= @truncate(cell_len - idx);
|
||||||
|
idx = 0;
|
||||||
|
break; // go to next cell
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// written all cell contents
|
||||||
|
if (idx >= cell.content.len) {
|
||||||
|
break; // go to next cell
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (idx < contents.len) {
|
|
||||||
_ = try terminal.write(contents[idx..]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ const std = @import("std");
|
|||||||
pub const Key = @import("terminal/Key.zig");
|
pub const Key = @import("terminal/Key.zig");
|
||||||
pub const Size = @import("terminal/Size.zig");
|
pub const Size = @import("terminal/Size.zig");
|
||||||
pub const Position = @import("terminal/Position.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");
|
pub const code_point = @import("code_point");
|
||||||
|
|
||||||
const log = std.log.scoped(.terminal);
|
const log = std.log.scoped(.terminal);
|
||||||
@@ -229,5 +229,5 @@ fn getReportMode(ps: u8) ReportMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test {
|
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 {
|
pub fn render(this: *@This(), renderer: *Renderer) !void {
|
||||||
try renderer.clear(this.size);
|
try renderer.clear(this.size);
|
||||||
if (this.size.rows >= this.line_index.items.len) {
|
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 {
|
} else {
|
||||||
// more rows than we can display
|
// more rows than we can display
|
||||||
const i = this.line_index.items[this.line];
|
const i = this.line_index.items[this.line];
|
||||||
const e = this.size.rows + this.line;
|
const e = this.size.rows + this.line;
|
||||||
if (e > this.line_index.items.len) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
const x = this.line_index.items[e];
|
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 Key = terminal.Key;
|
||||||
pub const Position = terminal.Position;
|
pub const Position = terminal.Position;
|
||||||
pub const Size = terminal.Size;
|
pub const Size = terminal.Size;
|
||||||
pub const Style = terminal.Style;
|
pub const Cell = terminal.Cell;
|
||||||
|
|
||||||
test {
|
test {
|
||||||
_ = @import("terminal.zig");
|
_ = @import("terminal.zig");
|
||||||
|
|||||||
Reference in New Issue
Block a user