Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 30s
The App.Renderer is used for the new `Layout.render` method. Each layout renders itself now with corresponding renderers which might only update parts of the screen, etc.
96 lines
3.5 KiB
Zig
96 lines
3.5 KiB
Zig
const std = @import("std");
|
|
const terminal = @import("../terminal.zig");
|
|
|
|
const isTaggedUnion = @import("../event.zig").isTaggedUnion;
|
|
const Error = @import("../event.zig").Error;
|
|
const Key = terminal.Key;
|
|
const Style = terminal.Style;
|
|
|
|
const log = std.log.scoped(.widget_rawtext);
|
|
|
|
pub fn Widget(comptime Event: type) type {
|
|
if (!isTaggedUnion(Event)) {
|
|
@compileError("Provided user event `Event` for `Layout(comptime Event: type)` is not of type `union(enum)`.");
|
|
}
|
|
const Contents = std.ArrayList(u8);
|
|
return struct {
|
|
contents: Contents = undefined,
|
|
line_index: std.ArrayList(usize) = undefined,
|
|
line: usize = 0,
|
|
size: terminal.Size = undefined,
|
|
|
|
pub fn init(allocator: std.mem.Allocator, file: std.fs.File) @This() {
|
|
var contents = Contents.init(allocator);
|
|
var line_index = std.ArrayList(usize).init(allocator);
|
|
file.reader().readAllArrayList(&contents, std.math.maxInt(usize)) catch {};
|
|
line_index.append(0) catch {};
|
|
for (contents.items, 0..) |item, i| {
|
|
if (item == '\n') {
|
|
line_index.append(i + 1) catch {};
|
|
}
|
|
}
|
|
return .{
|
|
.contents = contents,
|
|
.line_index = line_index,
|
|
};
|
|
}
|
|
|
|
pub fn deinit(this: *@This()) void {
|
|
this.contents.deinit();
|
|
this.line_index.deinit();
|
|
this.* = undefined;
|
|
}
|
|
|
|
pub fn handle(this: *@This(), event: Event) ?Event {
|
|
switch (event) {
|
|
// store the received size
|
|
.resize => |size| {
|
|
this.size = size;
|
|
if (this.line > this.line_index.items.len -| 1 -| size.rows) {
|
|
this.line = this.line_index.items.len -| 1 -| size.rows;
|
|
}
|
|
},
|
|
.key => |key| {
|
|
if (key.matches(.{ .cp = 'g' })) {
|
|
// top
|
|
this.line = 0;
|
|
}
|
|
if (key.matches(.{ .cp = 'G' })) {
|
|
// bottom
|
|
this.line = this.line_index.items.len -| 1 -| this.size.rows;
|
|
}
|
|
if (key.matches(.{ .cp = 'j' })) {
|
|
// down
|
|
if (this.line < this.line_index.items.len -| 1 -| this.size.rows) {
|
|
this.line +|= 1;
|
|
}
|
|
}
|
|
if (key.matches(.{ .cp = 'k' })) {
|
|
// up
|
|
this.line -|= 1;
|
|
}
|
|
},
|
|
else => {},
|
|
}
|
|
return null;
|
|
}
|
|
|
|
pub fn content(this: *@This()) ![]u8 {
|
|
if (this.size.rows >= this.line_index.items.len) {
|
|
return this.contents.items;
|
|
} else {
|
|
// more rows than we can display
|
|
const i = this.line_index.items[this.line];
|
|
const e = this.size.rows + this.line + 1;
|
|
if (e >= this.line_index.items.len) {
|
|
return this.contents.items[i..];
|
|
}
|
|
// last line should not end with the last character (likely a newline character)
|
|
// FIX: what about files which do not end with a newline?
|
|
const x = this.line_index.items[e] - 1;
|
|
return this.contents.items[i..x];
|
|
}
|
|
}
|
|
};
|
|
}
|