this repo has no description
1//! A View is effectively an "oversized" Window that can be written to and rendered in pieces.
2
3const std = @import("std");
4const mem = std.mem;
5
6const View = @This();
7
8const gw = @import("../gwidth.zig");
9
10const Screen = @import("../Screen.zig");
11const Window = @import("../Window.zig");
12const Unicode = @import("../Unicode.zig");
13const Cell = @import("../Cell.zig");
14
15/// View Allocator
16alloc: mem.Allocator,
17
18/// Underlying Screen
19screen: Screen,
20
21/// View Initialization Config
22pub const Config = struct {
23 width: u16,
24 height: u16,
25};
26
27/// Initialize a new View
28pub fn init(alloc: mem.Allocator, unicode: *const Unicode, config: Config) mem.Allocator.Error!View {
29 const screen = try Screen.init(
30 alloc,
31 .{
32 .cols = config.width,
33 .rows = config.height,
34 .x_pixel = 0,
35 .y_pixel = 0,
36 },
37 unicode,
38 );
39 return .{
40 .alloc = alloc,
41 .screen = screen,
42 };
43}
44
45pub fn window(self: *View) Window {
46 return .{
47 .x_off = 0,
48 .y_off = 0,
49 .parent_x_off = 0,
50 .parent_y_off = 0,
51 .width = self.screen.width,
52 .height = self.screen.height,
53 .screen = &self.screen,
54 };
55}
56
57/// Deinitialize this View
58pub fn deinit(self: *View) void {
59 self.screen.deinit(self.alloc);
60}
61
62pub const DrawOptions = struct {
63 x_off: u16 = 0,
64 y_off: u16 = 0,
65};
66
67pub fn draw(self: *View, win: Window, opts: DrawOptions) void {
68 if (opts.x_off >= self.screen.width) return;
69 if (opts.y_off >= self.screen.height) return;
70
71 const width = @min(win.width, self.screen.width - opts.x_off);
72 const height = @min(win.height, self.screen.height - opts.y_off);
73
74 for (0..height) |_row| {
75 const row: i17 = @intCast(_row);
76 const src_start: usize = @intCast(opts.x_off + ((row + opts.y_off) * self.screen.width));
77 const src_end: usize = @intCast(src_start + width);
78 const dst_start: usize = @intCast(win.x_off + ((row + win.y_off) * win.screen.width));
79 const dst_end: usize = @intCast(dst_start + width);
80 @memcpy(win.screen.buf[dst_start..dst_end], self.screen.buf[src_start..src_end]);
81 }
82}
83
84/// Render Config for `toWin()`
85pub const RenderConfig = struct {
86 x: u16 = 0,
87 y: u16 = 0,
88 width: Extent = .fit,
89 height: Extent = .fit,
90
91 pub const Extent = union(enum) {
92 fit,
93 max: u16,
94 };
95};
96
97/// Render a portion of this View to the provided Window (`win`).
98/// This will return the bounded X (col), Y (row) coordinates based on the rendering.
99pub fn toWin(self: *View, win: Window, config: RenderConfig) !struct { u16, u16 } {
100 var x = @min(self.screen.width - 1, config.x);
101 var y = @min(self.screen.height - 1, config.y);
102 const width = width: {
103 var width = switch (config.width) {
104 .fit => win.width,
105 .max => |w| @min(win.width, w),
106 };
107 width = @min(width, self.screen.width);
108 break :width @min(width, self.screen.width -| 1 -| x +| win.width);
109 };
110 const height = height: {
111 var height = switch (config.height) {
112 .fit => win.height,
113 .max => |h| @min(win.height, h),
114 };
115 height = @min(height, self.screen.height);
116 break :height @min(height, self.screen.height -| 1 -| y +| win.height);
117 };
118 x = @min(x, self.screen.width -| width);
119 y = @min(y, self.screen.height -| height);
120 const child = win.child(.{
121 .width = width,
122 .height = height,
123 });
124 self.draw(child, .{ .x_off = x, .y_off = y });
125 return .{ x, y };
126}
127
128/// Writes a cell to the location in the View
129pub fn writeCell(self: *View, col: u16, row: u16, cell: Cell) void {
130 self.screen.writeCell(col, row, cell);
131}
132
133/// Reads a cell at the location in the View
134pub fn readCell(self: *const View, col: u16, row: u16) ?Cell {
135 return self.screen.readCell(col, row);
136}
137
138/// Fills the View with the default cell
139pub fn clear(self: View) void {
140 self.fill(.{ .default = true });
141}
142
143/// Returns the width of the grapheme. This depends on the terminal capabilities
144pub fn gwidth(self: View, str: []const u8) u16 {
145 return gw.gwidth(str, self.screen.width_method, &self.screen.unicode.width_data);
146}
147
148/// Fills the View with the provided cell
149pub fn fill(self: View, cell: Cell) void {
150 @memset(self.screen.buf, cell);
151}
152
153/// Prints segments to the View. Returns true if the text overflowed with the
154/// given wrap strategy and size.
155pub fn print(self: *View, segments: []const Cell.Segment, opts: Window.PrintOptions) Window.PrintResult {
156 return self.window().print(segments, opts);
157}
158
159/// Print a single segment. This is just a shortcut for print(&.{segment}, opts)
160pub fn printSegment(self: *View, segment: Cell.Segment, opts: Window.PrintOptions) Window.PrintResult {
161 return self.print(&.{segment}, opts);
162}