ly/ly-ui/src/components/generic.zig

173 lines
5.7 KiB
Zig

const std = @import("std");
const Cell = @import("../Cell.zig");
const keyboard = @import("../keyboard.zig");
const TerminalBuffer = @import("../TerminalBuffer.zig");
const Position = @import("../Position.zig");
pub fn CyclableLabel(comptime ItemType: type, comptime ChangeItemType: type) type {
return struct {
const Allocator = std.mem.Allocator;
const ItemList = std.ArrayListUnmanaged(ItemType);
const DrawItemFn = *const fn (*Self, ItemType, usize, usize, usize) void;
const ChangeItemFn = *const fn (ItemType, ?ChangeItemType) void;
const Self = @This();
allocator: Allocator,
buffer: *TerminalBuffer,
list: ItemList,
current: usize,
width: usize,
component_pos: Position,
children_pos: Position,
text_in_center: bool,
fg: u32,
bg: u32,
cursor: usize,
draw_item_fn: DrawItemFn,
change_item_fn: ?ChangeItemFn,
change_item_arg: ?ChangeItemType,
keybinds: TerminalBuffer.KeybindMap,
pub fn init(
allocator: Allocator,
io: std.Io,
buffer: *TerminalBuffer,
draw_item_fn: DrawItemFn,
change_item_fn: ?ChangeItemFn,
change_item_arg: ?ChangeItemType,
width: usize,
text_in_center: bool,
fg: u32,
bg: u32,
) !*Self {
var self = try allocator.create(Self);
self.* = .{
.allocator = allocator,
.buffer = buffer,
.list = .empty,
.current = 0,
.width = width,
.component_pos = TerminalBuffer.START_POSITION,
.children_pos = TerminalBuffer.START_POSITION,
.text_in_center = text_in_center,
.fg = fg,
.bg = bg,
.cursor = 0,
.draw_item_fn = draw_item_fn,
.change_item_fn = change_item_fn,
.change_item_arg = change_item_arg,
.keybinds = .init(allocator),
};
try buffer.registerKeybind(io, &self.keybinds, "Left", &goLeft, self);
try buffer.registerKeybind(io, &self.keybinds, "Ctrl+H", &goLeft, self);
try buffer.registerKeybind(io, &self.keybinds, "Right", &goRight, self);
try buffer.registerKeybind(io, &self.keybinds, "Ctrl+L", &goRight, self);
return self;
}
pub fn deinit(self: *Self) void {
self.list.deinit(self.allocator);
self.keybinds.deinit();
self.allocator.destroy(self);
}
pub fn positionX(self: *Self, original_pos: Position) void {
self.component_pos = original_pos;
self.cursor = self.component_pos.x + 2;
self.children_pos = original_pos.addX(self.width);
}
pub fn positionY(self: *Self, original_pos: Position) void {
self.component_pos = original_pos;
self.cursor = self.component_pos.x + 2;
self.children_pos = original_pos.addY(1);
}
pub fn positionXY(self: *Self, original_pos: Position) void {
self.component_pos = original_pos;
self.cursor = self.component_pos.x + 2;
self.children_pos = Position.init(
self.width,
1,
).add(original_pos);
}
pub fn childrenPosition(self: Self) Position {
return self.children_pos;
}
pub fn addItem(self: *Self, item: ItemType) !void {
try self.list.append(self.allocator, item);
self.current = self.list.items.len - 1;
}
pub fn handle(self: *Self, _: ?keyboard.Key) !void {
try TerminalBuffer.setCursor(
self.component_pos.x + self.cursor + 2,
self.component_pos.y,
);
}
pub fn draw(self: *Self) void {
if (self.list.items.len == 0) return;
if (self.width < 2) return;
var left_arrow = Cell.init('<', self.fg, self.bg);
var right_arrow = Cell.init('>', self.fg, self.bg);
left_arrow.put(self.component_pos.x, self.component_pos.y) catch {};
right_arrow.put(
self.component_pos.x + self.width - 1,
self.component_pos.y,
) catch {};
const current_item = self.list.items[self.current];
const x = self.component_pos.x + 2;
const y = self.component_pos.y;
const width = self.width - 2;
@call(
.auto,
self.draw_item_fn,
.{ self, current_item, x, y, width },
);
}
fn goLeft(ptr: *anyopaque) !bool {
var self: *Self = @ptrCast(@alignCast(ptr));
self.current = if (self.current == 0) self.list.items.len - 1 else self.current - 1;
if (self.change_item_fn) |change_item_fn| {
@call(
.auto,
change_item_fn,
.{ self.list.items[self.current], self.change_item_arg },
);
}
return false;
}
fn goRight(ptr: *anyopaque) !bool {
var self: *Self = @ptrCast(@alignCast(ptr));
self.current = if (self.current == self.list.items.len - 1) 0 else self.current + 1;
if (self.change_item_fn) |change_item_fn| {
@call(
.auto,
change_item_fn,
.{ self.list.items[self.current], self.change_item_arg },
);
}
return false;
}
};
}