intermediate #1
@@ -3,7 +3,7 @@ const zterm = @import("zterm");
|
|||||||
|
|
||||||
const App = zterm.App(
|
const App = zterm.App(
|
||||||
union(enum) {},
|
union(enum) {},
|
||||||
zterm.Renderer.Direct,
|
zterm.Renderer.Buffered,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
const Key = zterm.Key;
|
const Key = zterm.Key;
|
||||||
@@ -26,7 +26,8 @@ pub fn main() !void {
|
|||||||
const allocator = gpa.allocator();
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
var app: App = .{};
|
var app: App = .{};
|
||||||
var renderer: App.Renderer = .{};
|
var renderer = App.Renderer.init(allocator);
|
||||||
|
defer renderer.deinit();
|
||||||
// TODO: when not running fullscreen, the application needs to screen down accordingly to display the contents
|
// TODO: when not running fullscreen, the application needs to screen down accordingly to display the contents
|
||||||
// -> size hint how much should it use?
|
// -> size hint how much should it use?
|
||||||
|
|
||||||
@@ -75,17 +76,19 @@ pub fn main() !void {
|
|||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.layout = Layout.createFrom(Layout.VStack.init(allocator, .{
|
.layout = Layout.createFrom(Layout.VStack.init(allocator, .{
|
||||||
Widget.createFrom(blk: {
|
// Widget.createFrom(blk: {
|
||||||
const file = try std.fs.cwd().openFile("./examples/stack.zig", .{});
|
// const file = try std.fs.cwd().openFile("./examples/stack.zig", .{});
|
||||||
defer file.close();
|
// defer file.close();
|
||||||
break :blk Widget.RawText.init(allocator, file);
|
// break :blk Widget.RawText.init(allocator, file);
|
||||||
}),
|
// }),
|
||||||
Widget.createFrom(Widget.Spacer.init(allocator)),
|
Widget.createFrom(Widget.Spacer.init(allocator)),
|
||||||
Widget.createFrom(blk: {
|
Widget.createFrom(Widget.Spacer.init(allocator)),
|
||||||
const file = try std.fs.cwd().openFile("./examples/stack.zig", .{});
|
Widget.createFrom(Widget.Spacer.init(allocator)),
|
||||||
defer file.close();
|
// Widget.createFrom(blk: {
|
||||||
break :blk Widget.RawText.init(allocator, file);
|
// const file = try std.fs.cwd().openFile("./examples/stack.zig", .{});
|
||||||
}),
|
// defer file.close();
|
||||||
|
// break :blk Widget.RawText.init(allocator, file);
|
||||||
|
// }),
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const App = zterm.App(
|
|||||||
tui, // view instance to the corresponding view for 'tui'
|
tui, // view instance to the corresponding view for 'tui'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
zterm.Renderer.Direct,
|
zterm.Renderer.Buffered,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
const Cell = zterm.Cell;
|
const Cell = zterm.Cell;
|
||||||
@@ -24,49 +24,26 @@ const Tui = struct {
|
|||||||
pub fn init(allocator: std.mem.Allocator) *Tui {
|
pub fn init(allocator: std.mem.Allocator) *Tui {
|
||||||
var tui = allocator.create(Tui) catch @panic("Out of memory: tui.zig");
|
var tui = allocator.create(Tui) catch @panic("Out of memory: tui.zig");
|
||||||
tui.allocator = allocator;
|
tui.allocator = allocator;
|
||||||
// FIXME: the layout creates an 'incorrect alignment'?
|
tui.layout = Layout.createFrom(Layout.VStack.init(allocator, .{
|
||||||
tui.layout = Layout.createFrom(Layout.VContainer.init(allocator, .{
|
Layout.createFrom(Layout.HStack.init(allocator, .{
|
||||||
.{
|
Widget.createFrom(Widget.Text.init(allocator, .left, &[1]Cell{
|
||||||
Layout.createFrom(Layout.Framing.init(allocator, .{
|
.{ .rune = 'Y', .style = .{ .fg = .{ .index = 6 }, .bold = true } },
|
||||||
.title = .{
|
|
||||||
.str = "Welcome to my terminal website",
|
|
||||||
.style = .{
|
|
||||||
.ul = .{ .index = 6 },
|
|
||||||
.ul_style = .single,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, .{
|
|
||||||
.layout = Layout.createFrom(Layout.HContainer.init(allocator, .{
|
|
||||||
.{
|
|
||||||
Widget.createFrom(Widget.Text.init(allocator, .left, &[1]Cell{
|
|
||||||
.{ .content = "Yves Biener", .style = .{ .bold = true } },
|
|
||||||
})),
|
|
||||||
25,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
Widget.createFrom(Widget.Text.init(allocator, .left, &[1]Cell{
|
|
||||||
.{ .content = "File name", .style = .{ .bold = true } },
|
|
||||||
})),
|
|
||||||
50,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
Widget.createFrom(Widget.Text.init(allocator, .left, &[1]Cell{
|
|
||||||
.{ .content = "Contacts", .style = .{ .bold = true } },
|
|
||||||
})),
|
|
||||||
25,
|
|
||||||
},
|
|
||||||
})),
|
|
||||||
})),
|
})),
|
||||||
10,
|
Widget.createFrom(Widget.Text.init(allocator, .left, &[1]Cell{
|
||||||
},
|
.{ .rune = 'F', .style = .{ .fg = .{ .index = 6 }, .bold = true } },
|
||||||
.{
|
|
||||||
Layout.createFrom(Layout.Margin.init(allocator, .{ .left = 15, .right = 15 }, .{
|
|
||||||
.widget = Widget.createFrom(Widget.Text.init(allocator, .default, &[1]Cell{
|
|
||||||
.{ .content = "Does this change anything", .style = .{ .ul = .default, .ul_style = .single } },
|
|
||||||
})),
|
|
||||||
})),
|
})),
|
||||||
90,
|
Widget.createFrom(Widget.Text.init(allocator, .left, &[1]Cell{
|
||||||
},
|
.{ .rune = 'C', .style = .{ .fg = .{ .index = 6 }, .bold = true } },
|
||||||
|
})),
|
||||||
|
})),
|
||||||
|
// .{
|
||||||
|
// Layout.createFrom(Layout.Margin.init(allocator, .{ .left = 15, .right = 15 }, .{
|
||||||
|
// .widget = Widget.createFrom(Widget.Text.init(allocator, .default, &[1]Cell{
|
||||||
|
// .{ .rune = 'D', .style = .{ .ul = .default, .ul_style = .single } },
|
||||||
|
// })),
|
||||||
|
// })),
|
||||||
|
// 90,
|
||||||
|
// },
|
||||||
}));
|
}));
|
||||||
return tui;
|
return tui;
|
||||||
}
|
}
|
||||||
@@ -76,6 +53,14 @@ const Tui = struct {
|
|||||||
this.allocator.destroy(this);
|
this.allocator.destroy(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn enable(this: *Tui) void {
|
||||||
|
_ = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable(this: *Tui) void {
|
||||||
|
_ = this;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle(this: *Tui, event: App.Event) !*Events {
|
pub fn handle(this: *Tui, event: App.Event) !*Events {
|
||||||
return try this.layout.handle(event);
|
return try this.layout.handle(event);
|
||||||
}
|
}
|
||||||
@@ -99,7 +84,9 @@ pub fn main() !void {
|
|||||||
const allocator = arena.allocator();
|
const allocator = arena.allocator();
|
||||||
|
|
||||||
var app: App = .{};
|
var app: App = .{};
|
||||||
var renderer: App.Renderer = .{};
|
var renderer = App.Renderer.init(allocator);
|
||||||
|
defer renderer.deinit();
|
||||||
|
|
||||||
var view: View = undefined;
|
var view: View = undefined;
|
||||||
|
|
||||||
var tui_view = View.createFrom(Tui.init(allocator));
|
var tui_view = View.createFrom(Tui.init(allocator));
|
||||||
@@ -147,5 +134,6 @@ pub fn main() !void {
|
|||||||
app.postEvent(e);
|
app.postEvent(e);
|
||||||
}
|
}
|
||||||
try view.render(&renderer);
|
try view.render(&renderer);
|
||||||
|
try renderer.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,8 +126,8 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
|
|||||||
return &this.events;
|
return &this.events;
|
||||||
}
|
}
|
||||||
|
|
||||||
const round_frame = .{ "╭", "─", "╮", "│", "╰", "╯" };
|
const round_frame: [6][]const u8 = .{ "╭", "─", "╮", "│", "╰", "╯" };
|
||||||
const square_frame = .{ "┌", "─", "┐", "│", "└", "┘" };
|
const square_frame: [6][]const u8 = .{ "┌", "─", "┐", "│", "└", "┘" };
|
||||||
|
|
||||||
fn renderFrame(this: *@This(), renderer: *Renderer) !void {
|
fn renderFrame(this: *@This(), renderer: *Renderer) !void {
|
||||||
// FIXME: use renderer instead!
|
// FIXME: use renderer instead!
|
||||||
@@ -140,14 +140,14 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
|
|||||||
// render top: +---+
|
// render top: +---+
|
||||||
try terminal.setCursorPosition(this.size.anchor);
|
try terminal.setCursorPosition(this.size.anchor);
|
||||||
const writer = terminal.writer();
|
const writer = terminal.writer();
|
||||||
try this.config.style.value(writer, frame[0]);
|
// try this.config.style.value(writer, frame[0]);
|
||||||
if (this.config.title.str.len > 0) {
|
for (0..this.config.title.str.len) |i| {
|
||||||
try this.config.title.style.value(writer, this.config.title.str);
|
try this.config.title.style.value(writer, this.config.title.str[i]);
|
||||||
}
|
}
|
||||||
for (0..this.size.cols -| 2 -| this.config.title.str.len) |_| {
|
for (0..this.size.cols -| 2 -| this.config.title.str.len) |_| {
|
||||||
try this.config.style.value(writer, frame[1]);
|
// try this.config.style.value(writer, frame[1]);
|
||||||
}
|
}
|
||||||
try this.config.style.value(writer, frame[2]);
|
// try this.config.style.value(writer, frame[2]);
|
||||||
// render left: |
|
// render left: |
|
||||||
for (1..this.size.rows -| 1) |r| {
|
for (1..this.size.rows -| 1) |r| {
|
||||||
const row: u16 = @truncate(r);
|
const row: u16 = @truncate(r);
|
||||||
@@ -155,7 +155,7 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
|
|||||||
.col = this.size.anchor.col,
|
.col = this.size.anchor.col,
|
||||||
.row = this.size.anchor.row + row,
|
.row = this.size.anchor.row + row,
|
||||||
});
|
});
|
||||||
try this.config.style.value(writer, frame[3]);
|
// try this.config.style.value(writer, frame[3]);
|
||||||
}
|
}
|
||||||
// render right: |
|
// render right: |
|
||||||
for (1..this.size.rows -| 1) |r| {
|
for (1..this.size.rows -| 1) |r| {
|
||||||
@@ -164,18 +164,18 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
|
|||||||
.col = this.size.anchor.col + this.size.cols -| 1,
|
.col = this.size.anchor.col + this.size.cols -| 1,
|
||||||
.row = this.size.anchor.row + row,
|
.row = this.size.anchor.row + row,
|
||||||
});
|
});
|
||||||
try this.config.style.value(writer, frame[3]);
|
// try this.config.style.value(writer, frame[3]);
|
||||||
}
|
}
|
||||||
// render bottom: +---+
|
// render bottom: +---+
|
||||||
try terminal.setCursorPosition(.{
|
try terminal.setCursorPosition(.{
|
||||||
.col = this.size.anchor.col,
|
.col = this.size.anchor.col,
|
||||||
.row = this.size.anchor.row + this.size.rows - 1,
|
.row = this.size.anchor.row + this.size.rows - 1,
|
||||||
});
|
});
|
||||||
try this.config.style.value(writer, frame[4]);
|
// try this.config.style.value(writer, frame[4]);
|
||||||
for (0..this.size.cols -| 2) |_| {
|
for (0..this.size.cols -| 2) |_| {
|
||||||
try this.config.style.value(writer, frame[1]);
|
// try this.config.style.value(writer, frame[1]);
|
||||||
}
|
}
|
||||||
try this.config.style.value(writer, frame[5]);
|
// try this.config.style.value(writer, frame[5]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(this: *@This(), renderer: *Renderer) !void {
|
pub fn render(this: *@This(), renderer: *Renderer) !void {
|
||||||
|
|||||||
168
src/render.zig
168
src/render.zig
@@ -10,97 +10,107 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const terminal = @import("terminal.zig");
|
const terminal = @import("terminal.zig");
|
||||||
|
|
||||||
const Cells = []const terminal.Cell;
|
const Cell = terminal.Cell;
|
||||||
const Position = terminal.Position;
|
const Position = terminal.Position;
|
||||||
const Size = terminal.Size;
|
const Size = terminal.Size;
|
||||||
|
|
||||||
pub fn Direct(comptime fullscreen: bool) type {
|
/// Double-buffered intermediate rendering pipeline
|
||||||
const log = std.log.scoped(.renderer_direct);
|
pub fn Buffered(comptime fullscreen: bool) type {
|
||||||
_ = log;
|
const log = std.log.scoped(.renderer_buffered);
|
||||||
|
// _ = log;
|
||||||
_ = fullscreen;
|
_ = fullscreen;
|
||||||
return struct {
|
return struct {
|
||||||
size: Size = undefined,
|
allocator: std.mem.Allocator,
|
||||||
|
created: bool,
|
||||||
|
size: Size,
|
||||||
|
screen: []Cell,
|
||||||
|
virtual_screen: []Cell,
|
||||||
|
|
||||||
pub fn resize(this: *@This(), size: Size) void {
|
pub fn init(allocator: std.mem.Allocator) @This() {
|
||||||
this.size = size;
|
return .{
|
||||||
|
.allocator = allocator,
|
||||||
|
.created = false,
|
||||||
|
.size = undefined,
|
||||||
|
.screen = undefined,
|
||||||
|
.virtual_screen = undefined,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(this: *@This(), size: Size) !void {
|
pub fn deinit(this: *@This()) void {
|
||||||
_ = this;
|
if (this.created) {
|
||||||
// NOTE: clear on the entire screen may introduce too much overhead and could instead clear the entire screen instead.
|
this.allocator.free(this.screen);
|
||||||
// - it could also then try to optimize for further *clear* calls, that result in pretty much a nop? -> how to identify those clear calls?
|
this.allocator.free(this.virtual_screen);
|
||||||
// TODO: this should instead by dynamic and correct of size (terminal could be too large currently)
|
|
||||||
std.debug.assert(1028 > size.cols);
|
|
||||||
var buf: [1028]u8 = undefined;
|
|
||||||
@memset(buf[0..], ' ');
|
|
||||||
for (0..size.rows) |r| {
|
|
||||||
const row: u16 = @truncate(r);
|
|
||||||
try terminal.setCursorPosition(.{
|
|
||||||
.col = size.anchor.col,
|
|
||||||
.row = size.anchor.row + row,
|
|
||||||
});
|
|
||||||
_ = try terminal.write(buf[0..size.cols]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(this: *@This(), size: Size, cells: Cells) !void {
|
pub fn resize(this: *@This(), size: Size) void {
|
||||||
_ = this;
|
this.size = size;
|
||||||
try terminal.setCursorPosition(size.anchor);
|
if (!this.created) {
|
||||||
var row: u16 = 0;
|
this.screen = this.allocator.alloc(Cell, size.cols * size.rows) catch @panic("render.zig: Out of memory.");
|
||||||
var remaining_cols = size.cols;
|
this.virtual_screen = this.allocator.alloc(Cell, size.cols * size.rows) catch @panic("render.zig: Out of memory.");
|
||||||
|
this.created = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.allocator.resize(this.screen, size.cols * size.rows)) {
|
||||||
|
@panic("render.zig: Could not resize `screen` buffer");
|
||||||
|
}
|
||||||
|
if (this.allocator.resize(this.virtual_screen, size.cols * size.rows)) {
|
||||||
|
@panic("render.zig: Could not resize `virtual screen` buffer.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(this: *@This(), size: Size) !void {
|
||||||
|
log.debug("renderer::clear", .{});
|
||||||
|
var vs = this.virtual_screen;
|
||||||
|
const anchor = (size.anchor.row * this.size.rows) + size.anchor.col;
|
||||||
|
for (0..size.rows) |row| {
|
||||||
|
for (0..size.cols) |col| {
|
||||||
|
vs[anchor + (row * this.size.cols) + col].reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render provided cells at size (anchor and dimension) into the *virtual screen*.
|
||||||
|
pub fn render(this: *@This(), size: Size, cells: []const Cell) !void {
|
||||||
|
log.debug("renderer:render: cells: {any}", .{cells});
|
||||||
|
std.debug.assert(cells.len > 0);
|
||||||
|
|
||||||
|
var idx: usize = 0;
|
||||||
|
var vs = this.virtual_screen;
|
||||||
|
const anchor = (size.anchor.row * this.size.rows) + size.anchor.col;
|
||||||
|
|
||||||
|
for (0..size.rows) |row| {
|
||||||
|
for (0..size.cols) |col| {
|
||||||
|
const cell = cells[idx];
|
||||||
|
idx += 1;
|
||||||
|
|
||||||
|
vs[anchor + (row * this.size.cols) + col].style = cell.style;
|
||||||
|
vs[anchor + (row * this.size.cols) + col].rune = cell.rune;
|
||||||
|
|
||||||
|
if (cells.len == idx) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write *virtual screen* to alternate screen (should be called once and last during each render loop iteration in the main loop).
|
||||||
|
pub fn flush(this: *@This()) !void {
|
||||||
|
log.debug("renderer::flush", .{});
|
||||||
const writer = terminal.writer();
|
const writer = terminal.writer();
|
||||||
for (cells) |cell| {
|
const s = this.screen;
|
||||||
var idx: usize = 0;
|
const vs = this.virtual_screen;
|
||||||
print_cell: while (true) {
|
for (0..this.size.rows) |row| {
|
||||||
const cell_len = cell.len(idx);
|
for (0..this.size.cols) |col| {
|
||||||
if (cell_len > remaining_cols) {
|
const idx = (row * this.size.cols) + col;
|
||||||
const result = try cell.writeUpToNewline(writer, idx, idx + remaining_cols);
|
const cs = s[idx];
|
||||||
row += 1;
|
const cvs = vs[idx];
|
||||||
if (row >= size.rows) {
|
if (cs.eql(cvs))
|
||||||
return; // we are done
|
continue;
|
||||||
}
|
// render differences found in virtual screen
|
||||||
try terminal.setCursorPosition(.{
|
// TODO: improve the writing speed (many unecessary writes (i.e. the style for every character..))
|
||||||
.col = size.anchor.col,
|
try terminal.setCursorPosition(.{ .row = @truncate(row), .col = @truncate(col) });
|
||||||
.row = size.anchor.row + row,
|
try cvs.value(writer);
|
||||||
});
|
// update screen to be the virtual screen for the next frame
|
||||||
remaining_cols = size.cols;
|
s[idx] = vs[idx];
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// written all cell contents
|
|
||||||
if (idx >= cell.content.len) {
|
|
||||||
break; // go to next cell
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ pub const ReportMode = enum {
|
|||||||
pub fn getTerminalSize() Size {
|
pub fn getTerminalSize() Size {
|
||||||
var ws: std.posix.winsize = undefined;
|
var ws: std.posix.winsize = undefined;
|
||||||
_ = std.posix.system.ioctl(std.posix.STDIN_FILENO, std.posix.T.IOCGWINSZ, @intFromPtr(&ws));
|
_ = std.posix.system.ioctl(std.posix.STDIN_FILENO, std.posix.T.IOCGWINSZ, @intFromPtr(&ws));
|
||||||
return .{ .cols = ws.col, .rows = ws.row };
|
return .{ .cols = ws.col - 1, .rows = ws.row - 1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn saveScreen() !void {
|
pub fn saveScreen() !void {
|
||||||
@@ -126,8 +126,8 @@ pub fn getCursorPosition() !Position {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.row = try std.fmt.parseInt(u16, row[0..ridx], 10),
|
.row = try std.fmt.parseInt(u16, row[0..ridx], 10) - 1,
|
||||||
.col = try std.fmt.parseInt(u16, col[0..cidx], 10),
|
.col = try std.fmt.parseInt(u16, col[0..cidx], 10) - 1,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,60 +2,24 @@ const std = @import("std");
|
|||||||
pub const Style = @import("Style.zig");
|
pub const Style = @import("Style.zig");
|
||||||
|
|
||||||
style: Style = .{},
|
style: Style = .{},
|
||||||
content: []const u8 = undefined,
|
rune: u8 = ' ',
|
||||||
|
|
||||||
pub const Result = struct {
|
pub const Character = struct {
|
||||||
idx: usize,
|
grapheme: []const u8,
|
||||||
newline: bool,
|
width: u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn len(this: @This(), start: usize) usize {
|
pub fn eql(this: @This(), other: @This()) bool {
|
||||||
std.debug.assert(this.content.len > start);
|
return this.rune == other.rune and this.style.eql(other.style);
|
||||||
return this.content[start..].len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(this: @This(), writer: anytype, start: usize, end: usize) !void {
|
pub fn reset(this: *@This()) void {
|
||||||
std.debug.assert(this.content.len > start);
|
this.style = .{ .fg = .default, .bg = .default, .ul = .default, .ul_style = .off };
|
||||||
std.debug.assert(this.content.len >= end);
|
this.rune = ' ';
|
||||||
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 {
|
pub fn value(this: @This(), writer: anytype) !void {
|
||||||
std.debug.assert(this.content.len > start);
|
try this.style.value(writer, this.rune);
|
||||||
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 {
|
test {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
const Position = @import("Position.zig");
|
const Position = @import("Position.zig");
|
||||||
|
|
||||||
anchor: Position = .{ .col = 1, .row = 1 }, // top left corner by default
|
anchor: Position = .{ .col = 0, .row = 0 }, // top left corner by default
|
||||||
cols: u16,
|
cols: u16,
|
||||||
rows: u16,
|
rows: u16,
|
||||||
|
|||||||
@@ -113,6 +113,20 @@ reverse: bool = false,
|
|||||||
invisible: bool = false,
|
invisible: bool = false,
|
||||||
strikethrough: bool = false,
|
strikethrough: bool = false,
|
||||||
|
|
||||||
|
pub fn eql(this: @This(), other: @This()) bool {
|
||||||
|
return this.fg.eql(other.fg) and
|
||||||
|
this.bg.eql(other.bg) and
|
||||||
|
this.ul.eql(other.ul) and
|
||||||
|
other.ul_style == this.ul_style and
|
||||||
|
other.bold == this.bold and
|
||||||
|
other.dim == this.dim and
|
||||||
|
other.italic == this.italic and
|
||||||
|
other.blink == this.blink and
|
||||||
|
other.reverse == this.reverse and
|
||||||
|
other.invisible == this.invisible and
|
||||||
|
other.strikethrough == this.strikethrough;
|
||||||
|
}
|
||||||
|
|
||||||
/// Merge _other_ `Style` to _this_ style and overwrite _this_ `Style`'s value
|
/// Merge _other_ `Style` to _this_ style and overwrite _this_ `Style`'s value
|
||||||
/// if the _other_ value differs from the default value.
|
/// if the _other_ value differs from the default value.
|
||||||
pub fn merge(this: *@This(), other: @This()) void {
|
pub fn merge(this: *@This(), other: @This()) void {
|
||||||
@@ -284,15 +298,9 @@ fn end(this: @This(), writer: anytype) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format(this: @This(), writer: anytype, comptime content: []const u8, args: anytype) !void {
|
pub fn value(this: @This(), writer: anytype, content: u8) !void {
|
||||||
try this.start(writer);
|
try this.start(writer);
|
||||||
try std.fmt.format(writer, content, args);
|
_ = try writer.write(&[_]u8{content});
|
||||||
try this.end(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn value(this: @This(), writer: anytype, content: []const u8) !void {
|
|
||||||
try this.start(writer);
|
|
||||||
_ = try writer.write(content);
|
|
||||||
try this.end(writer);
|
try this.end(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,10 +62,7 @@ pub fn Widget(comptime Event: type, comptime Renderer: type) type {
|
|||||||
switch (this.alignment) {
|
switch (this.alignment) {
|
||||||
.default => break :blk this.size,
|
.default => break :blk this.size,
|
||||||
.center => {
|
.center => {
|
||||||
var length_usize: usize = 0;
|
const length_usize = this.contents.len;
|
||||||
for (this.contents) |content| {
|
|
||||||
length_usize += content.content.len;
|
|
||||||
}
|
|
||||||
const length: u16 = @truncate(length_usize);
|
const length: u16 = @truncate(length_usize);
|
||||||
const cols = @min(length, this.size.cols);
|
const cols = @min(length, this.size.cols);
|
||||||
const rows = cols / length;
|
const rows = cols / length;
|
||||||
@@ -79,10 +76,7 @@ pub fn Widget(comptime Event: type, comptime Renderer: type) type {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
.top => {
|
.top => {
|
||||||
var length_usize: usize = 0;
|
const length_usize = this.contents.len;
|
||||||
for (this.contents) |content| {
|
|
||||||
length_usize += content.content.len;
|
|
||||||
}
|
|
||||||
const length: u16 = @truncate(length_usize);
|
const length: u16 = @truncate(length_usize);
|
||||||
const cols = @min(length, this.size.cols);
|
const cols = @min(length, this.size.cols);
|
||||||
const rows = cols / length;
|
const rows = cols / length;
|
||||||
@@ -96,10 +90,7 @@ pub fn Widget(comptime Event: type, comptime Renderer: type) type {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
.bottom => {
|
.bottom => {
|
||||||
var length_usize: usize = 0;
|
const length_usize = this.contents.len;
|
||||||
for (this.contents) |content| {
|
|
||||||
length_usize += content.content.len;
|
|
||||||
}
|
|
||||||
const length: u16 = @truncate(length_usize);
|
const length: u16 = @truncate(length_usize);
|
||||||
const cols = @min(length, this.size.cols);
|
const cols = @min(length, this.size.cols);
|
||||||
const rows = cols / length;
|
const rows = cols / length;
|
||||||
@@ -113,10 +104,7 @@ pub fn Widget(comptime Event: type, comptime Renderer: type) type {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
.left => {
|
.left => {
|
||||||
var length_usize: usize = 0;
|
const length_usize = this.contents.len;
|
||||||
for (this.contents) |content| {
|
|
||||||
length_usize += content.content.len;
|
|
||||||
}
|
|
||||||
const length: u16 = @truncate(length_usize);
|
const length: u16 = @truncate(length_usize);
|
||||||
const cols = @min(length, this.size.cols);
|
const cols = @min(length, this.size.cols);
|
||||||
const rows = cols / length;
|
const rows = cols / length;
|
||||||
@@ -130,10 +118,7 @@ pub fn Widget(comptime Event: type, comptime Renderer: type) type {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
.right => {
|
.right => {
|
||||||
var length_usize: usize = 0;
|
const length_usize = this.contents.len;
|
||||||
for (this.contents) |content| {
|
|
||||||
length_usize += content.content.len;
|
|
||||||
}
|
|
||||||
const length: u16 = @truncate(length_usize);
|
const length: u16 = @truncate(length_usize);
|
||||||
const cols = @min(length, this.size.cols);
|
const cols = @min(length, this.size.cols);
|
||||||
const rows = cols / length;
|
const rows = cols / length;
|
||||||
@@ -148,6 +133,7 @@ pub fn Widget(comptime Event: type, comptime Renderer: type) type {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
log.debug("Text.contents: {any}", .{this.contents});
|
||||||
try renderer.render(size, this.contents);
|
try renderer.render(size, this.contents);
|
||||||
this.require_render = false;
|
this.require_render = false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user