Files
zterm/src/widget/Text.zig
Yves Biener c2c3f41ff3
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 4m26s
mod(memory): do not create items on the stack instead using the provided allocator
2025-01-06 21:56:04 +01:00

156 lines
6.3 KiB
Zig

const std = @import("std");
const terminal = @import("../terminal.zig");
const isTaggedUnion = @import("../event.zig").isTaggedUnion;
const Error = @import("../event.zig").Error;
const Cell = terminal.Cell;
const log = std.log.scoped(.widget_text);
pub fn Widget(comptime Event: type, comptime Renderer: type) type {
if (!isTaggedUnion(Event)) {
@compileError("Provided user event `Event` for `Layout(comptime Event: type)` is not of type `union(enum)`.");
}
return struct {
allocator: std.mem.Allocator,
alignment: Alignment,
contents: []const Cell,
size: terminal.Size,
require_render: bool,
const Alignment = enum {
default,
center,
top,
bottom,
left,
right,
};
pub fn init(allocator: std.mem.Allocator, alignment: Alignment, contents: []const Cell) *@This() {
var this = allocator.create(@This()) catch @panic("Text.zig: Failed to create");
this.allocator = allocator;
this.require_render = true;
this.alignment = alignment;
this.contents = contents;
return this;
}
pub fn deinit(this: *@This()) void {
this.allocator.destroy(this);
}
pub fn handle(this: *@This(), event: Event) ?Event {
switch (event) {
// store the received size
.resize => |size| {
this.size = size;
this.require_render = true;
},
else => {},
}
return null;
}
pub fn render(this: *@This(), renderer: *Renderer) !void {
if (!this.require_render) {
return;
}
try renderer.clear(this.size);
// update size for aligned contents, default will not change size
const size: terminal.Size = blk: {
switch (this.alignment) {
.default => break :blk this.size,
.center => {
var length_usize: usize = 0;
for (this.contents) |content| {
length_usize += content.content.len;
}
const length: u16 = @truncate(length_usize);
const cols = @min(length, this.size.cols);
const rows = cols / length;
break :blk .{
.anchor = .{
.col = this.size.anchor.col + @divTrunc(this.size.cols, 2) - @divTrunc(cols, 2),
.row = this.size.anchor.row + @divTrunc(this.size.rows, 2),
},
.rows = rows,
.cols = cols,
};
},
.top => {
var length_usize: usize = 0;
for (this.contents) |content| {
length_usize += content.content.len;
}
const length: u16 = @truncate(length_usize);
const cols = @min(length, this.size.cols);
const rows = cols / length;
break :blk .{
.anchor = .{
.col = this.size.anchor.col + @divTrunc(this.size.cols, 2) - @divTrunc(cols, 2),
.row = this.size.anchor.row,
},
.rows = rows,
.cols = cols,
};
},
.bottom => {
var length_usize: usize = 0;
for (this.contents) |content| {
length_usize += content.content.len;
}
const length: u16 = @truncate(length_usize);
const cols = @min(length, this.size.cols);
const rows = cols / length;
break :blk .{
.anchor = .{
.col = this.size.anchor.col + @divTrunc(this.size.cols, 2) - @divTrunc(cols, 2),
.row = this.size.anchor.row + this.size.rows - rows,
},
.rows = rows,
.cols = cols,
};
},
.left => {
var length_usize: usize = 0;
for (this.contents) |content| {
length_usize += content.content.len;
}
const length: u16 = @truncate(length_usize);
const cols = @min(length, this.size.cols);
const rows = cols / length;
break :blk .{
.anchor = .{
.col = this.size.anchor.col,
.row = this.size.anchor.row + @divTrunc(this.size.rows, 2) - @divTrunc(rows, 2),
},
.rows = rows,
.cols = cols,
};
},
.right => {
var length_usize: usize = 0;
for (this.contents) |content| {
length_usize += content.content.len;
}
const length: u16 = @truncate(length_usize);
const cols = @min(length, this.size.cols);
const rows = cols / length;
break :blk .{
.anchor = .{
.col = this.size.anchor.col + this.size.cols - cols,
.row = this.size.anchor.row + @divTrunc(this.size.rows, 2) - @divTrunc(rows, 2),
},
.rows = rows,
.cols = cols,
};
},
}
};
try renderer.render(size, this.contents);
this.require_render = false;
}
};
}