Something went wrong. Try again.
A charm-like tui library
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180//! A `RenderBuffer` usable as a screen, plus the width method used to size//! grapheme clusters. Mirrors uv `ScreenBuffer`: the render buffer's grid ops//! (clear/fill/clone/…) are forwarded so `screen` helpers take the fast path.//!//! The embedded `screen` field implements [`screen.Screen`]: its vtable points//! at the `impl` thunks below, which recover the `ScreenBuffer` from the//! embedded interface with `@fieldParentPtr`. The public methods mirror that//! surface on the buffer itself (`bounds`/`cellAt`/`setCell`/…), forwarding to//! the render buffer.const ir = @import("root.zig");const std = @import("std");const scr = @import("screen.zig");
const RenderBuffer = ir.RenderBuffer;const Buffer = ir.Buffer;const Cell = ir.Cell;const Rectangle = ir.Rectangle;const Method = ir.width.Method;const Allocator = std.mem.Allocator;const t = std.testing;
const Screen = @This();
render_buffer: RenderBuffer,method: Method = .wcwidth,screen: scr.Screen = .{ .vtable = &.{ .bounds = vtableBounds, .cell_at = vtableCellAt, .set_cell = vtableSetCell, .width_method = impl.widthMethod, .clear = impl.clear, .clear_area = impl.clearArea, .fill = impl.fill, .fill_area = impl.fillArea, .clone = impl.clone, .clone_area = impl.cloneArea, },},
fn vtableBounds(s: *scr.Screen) Rectangle { const buffer: *Screen = @fieldParentPtr("screen", s); return buffer.render_buffer.bounds();}
fn vtableCellAt(s: *scr.Screen, x: usize, y: usize) ?*Cell { const buffer: *Screen = @fieldParentPtr("screen", s); return buffer.render_buffer.cellAt(x, y);}
fn vtableSetCell(s: *scr.Screen, x: usize, y: usize, cell: ?Cell) void { const buffer: *Screen = @fieldParentPtr("screen", s); return buffer.render_buffer.setCell(x, y, cell);}
const impl = struct { fn widthMethod(s: *scr.Screen) Method { const buffer: *Screen = @fieldParentPtr("screen", s); return buffer.method; }
fn clear(s: *scr.Screen) anyerror!void { const buffer: *Screen = @fieldParentPtr("screen", s); buffer.render_buffer.clear(); }
fn clearArea(s: *scr.Screen, area: Rectangle) anyerror!void { const buffer: *Screen = @fieldParentPtr("screen", s); buffer.render_buffer.clearArea(area); }
fn fill(s: *scr.Screen, cell: ?Cell) anyerror!void { const buffer: *Screen = @fieldParentPtr("screen", s); buffer.render_buffer.fill(cell); }
fn fillArea(s: *scr.Screen, cell: ?Cell, area: Rectangle) anyerror!void { const buffer: *Screen = @fieldParentPtr("screen", s); buffer.render_buffer.fillArea(cell, area); }
fn clone(s: *scr.Screen, _: Allocator) anyerror!Buffer { const buffer: *Screen = @fieldParentPtr("screen", s); return buffer.render_buffer.buffer.clone(); }
fn cloneArea(s: *scr.Screen, _: Allocator, area: Rectangle) anyerror!?Buffer { const buffer: *Screen = @fieldParentPtr("screen", s); return buffer.render_buffer.buffer.cloneArea(area); }};
pub fn init(allocator: Allocator, w: usize, h: usize) !Screen { return .{ .render_buffer = try .init(allocator, w, h) };}
pub fn deinit(self: *Screen) void { self.render_buffer.deinit();}
pub fn bounds(self: *Screen) Rectangle { return self.render_buffer.bounds();}
pub fn cellAt(self: *Screen, x: usize, y: usize) ?*Cell { return self.render_buffer.cellAt(x, y);}
pub fn setCell(self: *Screen, x: usize, y: usize, cell: ?Cell) void { self.render_buffer.setCell(x, y, cell);}
pub fn widthMethod(self: *Screen) Method { return self.method;}
pub fn clear(self: *Screen) void { self.render_buffer.clear();}
pub fn clearArea(self: *Screen, area: Rectangle) void { self.render_buffer.clearArea(area);}
pub fn fill(self: *Screen, cell: ?Cell) void { self.render_buffer.fill(cell);}
pub fn fillArea(self: *Screen, cell: ?Cell, area: Rectangle) void { self.render_buffer.fillArea(cell, area);}
pub fn clone(self: *Screen) !Buffer { return self.render_buffer.buffer.clone();}
pub fn cloneArea(self: *Screen, area: Rectangle) !?Buffer { return self.render_buffer.buffer.cloneArea(area);}
test "screen buffer defaults" { var buffer: Screen = try .init(t.allocator, 10, 5); defer buffer.deinit(); try t.expect(buffer.render_buffer.width() == 10); try t.expect(buffer.render_buffer.height() == 5); try t.expect(buffer.method == .wcwidth); try t.expect(buffer.bounds().width() == 10); try t.expect(buffer.bounds().height() == 5); try t.expectEqual(.wcwidth, buffer.widthMethod());}
test "screen passthrough ops participate in drawing" { var buffer: Screen = try .init(t.allocator, 6, 4); defer buffer.deinit();
buffer.fill(.of("x", 1)); try t.expectEqualStrings("x", buffer.cellAt(3, 2).?.content);
const clipped: Rectangle = .new(1, 1, 2, 2); buffer.clearArea(clipped); try t.expect(buffer.cellAt(1, 1).?.equal(.space)); try t.expect(buffer.cellAt(2, 2).?.equal(.space)); try t.expectEqualStrings("x", buffer.cellAt(3, 2).?.content);
buffer.fillArea(.of("f", 1), clipped); try t.expectEqualStrings("f", buffer.cellAt(2, 2).?.content);
var copy: Buffer = try buffer.clone(); defer copy.deinit(); try t.expectEqualStrings("f", copy.cellAt(2, 2).?.content);
var sub = (try buffer.cloneArea(.new(1, 1, 2, 2))).?; defer sub.deinit(); try t.expectEqualStrings("f", sub.cellAt(1, 1).?.content);
buffer.clear(); try t.expect(buffer.cellAt(0, 0).?.equal(.space));}