add: Direct Renderer implementation rendering the fixed size of the root Container
A corresponding example has been added, which is very minimalistic as of now, but will add further functionality to test the corresponding workflow that is usual in `zterm` such that it in the best case only a swap in the renderer to switch from alternate mode drawing to direct drawing.
This commit is contained in:
@@ -24,6 +24,8 @@ pub fn build(b: *std.Build) void {
|
||||
palette,
|
||||
// error handling
|
||||
errors,
|
||||
// non alternate screen applications
|
||||
direct,
|
||||
};
|
||||
|
||||
const example = b.option(Examples, "example", "Example to build and/or run. (default: all)") orelse .all;
|
||||
@@ -73,6 +75,8 @@ pub fn build(b: *std.Build) void {
|
||||
.palette => "examples/styles/palette.zig",
|
||||
// error handling
|
||||
.errors => "examples/errors.zig",
|
||||
// non-alternate screen
|
||||
.direct => "examples/direct.zig",
|
||||
.all => unreachable, // should never happen
|
||||
}),
|
||||
.target = target,
|
||||
|
||||
46
examples/direct.zig
Normal file
46
examples/direct.zig
Normal file
@@ -0,0 +1,46 @@
|
||||
pub fn main() !void {
|
||||
errdefer |err| log.err("Application Error: {any}", .{err});
|
||||
|
||||
var allocator: std.heap.DebugAllocator(.{}) = .init;
|
||||
defer if (allocator.deinit() == .leak) log.err("memory leak", .{});
|
||||
|
||||
const gpa = allocator.allocator();
|
||||
|
||||
var app: App = .init(.{}, .{});
|
||||
var renderer = zterm.Renderer.Direct.init(gpa);
|
||||
defer renderer.deinit();
|
||||
|
||||
var container: App.Container = try .init(gpa, .{
|
||||
.layout = .{
|
||||
.direction = .horizontal,
|
||||
.separator = .{ .enabled = true },
|
||||
},
|
||||
.size = .{
|
||||
.dim = .{ .y = 30 },
|
||||
.grow = .horizontal_only,
|
||||
},
|
||||
}, .{});
|
||||
defer container.deinit();
|
||||
|
||||
try container.append(try .init(gpa, .{
|
||||
.rectangle = .{ .fill = .blue },
|
||||
}, .{}));
|
||||
|
||||
try container.append(try .init(gpa, .{
|
||||
.rectangle = .{ .fill = .green },
|
||||
}, .{}));
|
||||
|
||||
container.resize(&app.model, try renderer.resize());
|
||||
container.reposition(&app.model, .{});
|
||||
try renderer.render(@TypeOf(container), &container, App.Model, &app.model);
|
||||
try renderer.flush();
|
||||
}
|
||||
|
||||
pub const panic = App.panic_handler;
|
||||
const log = std.log.scoped(.default);
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const zterm = @import("zterm");
|
||||
const input = zterm.input;
|
||||
const App = zterm.App(struct {}, union(enum) {});
|
||||
@@ -91,6 +91,7 @@ pub const Buffered = struct {
|
||||
var writer = terminal.writer();
|
||||
const s = this.screen;
|
||||
const vs = this.virtual_screen;
|
||||
|
||||
for (0..this.size.y) |row| {
|
||||
for (0..this.size.x) |col| {
|
||||
const idx = (row * this.size.x) + col;
|
||||
@@ -123,6 +124,87 @@ pub const Buffered = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const Direct = struct {
|
||||
gpa: Allocator,
|
||||
size: Point,
|
||||
resized: bool,
|
||||
screen: []Cell,
|
||||
|
||||
pub fn init(gpa: Allocator) @This() {
|
||||
return .{
|
||||
.gpa = gpa,
|
||||
.size = .{},
|
||||
.resized = true,
|
||||
.screen = undefined,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(this: *@This()) void {
|
||||
this.gpa.free(this.screen);
|
||||
}
|
||||
|
||||
pub fn resize(this: *@This()) !Point {
|
||||
// TODO the size in the y-axis is *not fixed* and *not limited*
|
||||
// how can I achive this?
|
||||
this.size = .{};
|
||||
if (!this.resized) {
|
||||
this.gpa.free(this.screen);
|
||||
this.screen = undefined;
|
||||
}
|
||||
this.resized = true;
|
||||
return terminal.getTerminalSize();
|
||||
}
|
||||
|
||||
pub fn clear(this: *@This()) !void {
|
||||
_ = this;
|
||||
try terminal.clearScreen();
|
||||
}
|
||||
|
||||
/// Render provided cells at size (anchor and dimension) into the *virtual screen*.
|
||||
pub fn render(this: *@This(), comptime Container: type, container: *Container, comptime Model: type, model: *const Model) !void {
|
||||
const size: Point = container.size;
|
||||
const origin: Point = container.origin;
|
||||
|
||||
if (this.resized) {
|
||||
this.size = size;
|
||||
const n = @as(usize, this.size.x) * @as(usize, this.size.y);
|
||||
this.screen = try this.gpa.alloc(Cell, n);
|
||||
@memset(this.screen, .{});
|
||||
this.resized = false;
|
||||
}
|
||||
|
||||
const cells: []const Cell = try container.content(model);
|
||||
|
||||
var idx: usize = 0;
|
||||
var vs = this.screen;
|
||||
const anchor: usize = (@as(usize, origin.y) * @as(usize, this.size.x)) + @as(usize, origin.x);
|
||||
|
||||
|
||||
blk: for (0..size.y) |row| {
|
||||
for (0..size.x) |col| {
|
||||
vs[anchor + (row * this.size.x) + col] = cells[idx];
|
||||
idx += 1;
|
||||
|
||||
if (cells.len == idx) break :blk;
|
||||
}
|
||||
}
|
||||
// free immediately
|
||||
container.allocator.free(cells);
|
||||
for (container.elements.items) |*element| try this.render(Container, element, Model, model);
|
||||
}
|
||||
|
||||
pub fn flush(this: *@This()) !void {
|
||||
var writer = terminal.writer();
|
||||
for (0..this.size.y) |row| {
|
||||
for (0..this.size.x) |col| {
|
||||
const idx = (row * this.size.x) + col;
|
||||
const cvs = this.screen[idx];
|
||||
try cvs.value(&writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const std = @import("std");
|
||||
const meta = std.meta;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
Reference in New Issue
Block a user