mirror of
https://github.com/fairyglade/ly.git
synced 2026-03-21 22:43:38 +00:00
Add central Widget struct + clean up code
In particular, move all termbox2 usage to TerminalBuffer.zig & keyboard.zig Signed-off-by: AnErrupTion <anerruption@disroot.org>
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
const std = @import("std");
|
||||
const math = std.math;
|
||||
|
||||
const Animation = @import("../tui/Animation.zig");
|
||||
const Cell = @import("../tui/Cell.zig");
|
||||
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
|
||||
const Widget = @import("../tui/Widget.zig");
|
||||
|
||||
const ColorMix = @This();
|
||||
|
||||
@@ -45,14 +45,17 @@ pub fn init(terminal_buffer: *TerminalBuffer, col1: u32, col2: u32, col3: u32) C
|
||||
};
|
||||
}
|
||||
|
||||
pub fn animation(self: *ColorMix) Animation {
|
||||
return Animation.init(self, deinit, realloc, draw);
|
||||
pub fn widget(self: *ColorMix) Widget {
|
||||
return Widget.init(
|
||||
self,
|
||||
null,
|
||||
null,
|
||||
draw,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
fn deinit(_: *ColorMix) void {}
|
||||
|
||||
fn realloc(_: *ColorMix) anyerror!void {}
|
||||
|
||||
fn draw(self: *ColorMix) void {
|
||||
self.frames +%= 1;
|
||||
const time: f32 = @as(f32, @floatFromInt(self.frames)) * time_scale;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const Animation = @import("../tui/Animation.zig");
|
||||
const Cell = @import("../tui/Cell.zig");
|
||||
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
|
||||
const Widget = @import("../tui/Widget.zig");
|
||||
|
||||
const Doom = @This();
|
||||
|
||||
@@ -49,15 +49,22 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, top_color: u
|
||||
};
|
||||
}
|
||||
|
||||
pub fn animation(self: *Doom) Animation {
|
||||
return Animation.init(self, deinit, realloc, draw);
|
||||
pub fn widget(self: *Doom) Widget {
|
||||
return Widget.init(
|
||||
self,
|
||||
deinit,
|
||||
realloc,
|
||||
draw,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
fn deinit(self: *Doom) void {
|
||||
self.allocator.free(self.buffer);
|
||||
}
|
||||
|
||||
fn realloc(self: *Doom) anyerror!void {
|
||||
fn realloc(self: *Doom) !void {
|
||||
const buffer = try self.allocator.realloc(self.buffer, self.terminal_buffer.width * self.terminal_buffer.height);
|
||||
initBuffer(buffer, self.terminal_buffer.width);
|
||||
self.buffer = buffer;
|
||||
|
||||
@@ -9,11 +9,11 @@ const LogFile = ly_core.LogFile;
|
||||
|
||||
const enums = @import("../enums.zig");
|
||||
const DurOffsetAlignment = enums.DurOffsetAlignment;
|
||||
const Animation = @import("../tui/Animation.zig");
|
||||
const Cell = @import("../tui/Cell.zig");
|
||||
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
|
||||
const Color = TerminalBuffer.Color;
|
||||
const Styling = TerminalBuffer.Styling;
|
||||
const Widget = @import("../tui/Widget.zig");
|
||||
|
||||
fn read_decompress_file(allocator: Allocator, file_path: []const u8) ![]u8 {
|
||||
const file_buffer = std.fs.cwd().openFile(file_path, .{}) catch {
|
||||
@@ -403,15 +403,22 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, log_file: *L
|
||||
};
|
||||
}
|
||||
|
||||
pub fn animation(self: *DurFile) Animation {
|
||||
return Animation.init(self, deinit, realloc, draw);
|
||||
pub fn widget(self: *DurFile) Widget {
|
||||
return Widget.init(
|
||||
self,
|
||||
deinit,
|
||||
realloc,
|
||||
draw,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
fn deinit(self: *DurFile) void {
|
||||
self.dur_movie.deinit();
|
||||
}
|
||||
|
||||
fn realloc(self: *DurFile) anyerror!void {
|
||||
fn realloc(self: *DurFile) !void {
|
||||
// when terminal size changes, we need to recalculate the start_pos and frame_size based on the new size
|
||||
self.start_pos = calc_start_position(self.terminal_buffer, &self.dur_movie, self.offset_alignment, self.offset);
|
||||
self.frame_size = calc_frame_size(self.terminal_buffer, &self.dur_movie);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const Animation = @import("../tui/Animation.zig");
|
||||
const Cell = @import("../tui/Cell.zig");
|
||||
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
|
||||
const Widget = @import("../tui/Widget.zig");
|
||||
|
||||
const GameOfLife = @This();
|
||||
|
||||
@@ -60,8 +60,15 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fg_color: u3
|
||||
return game;
|
||||
}
|
||||
|
||||
pub fn animation(self: *GameOfLife) Animation {
|
||||
return Animation.init(self, deinit, realloc, draw);
|
||||
pub fn widget(self: *GameOfLife) Widget {
|
||||
return Widget.init(
|
||||
self,
|
||||
deinit,
|
||||
realloc,
|
||||
draw,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
fn deinit(self: *GameOfLife) void {
|
||||
@@ -69,7 +76,7 @@ fn deinit(self: *GameOfLife) void {
|
||||
self.allocator.free(self.next_grid);
|
||||
}
|
||||
|
||||
fn realloc(self: *GameOfLife) anyerror!void {
|
||||
fn realloc(self: *GameOfLife) !void {
|
||||
const new_width = self.terminal_buffer.width;
|
||||
const new_height = self.terminal_buffer.height;
|
||||
const new_size = new_width * new_height;
|
||||
|
||||
@@ -2,9 +2,9 @@ const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Random = std.Random;
|
||||
|
||||
const Animation = @import("../tui/Animation.zig");
|
||||
const Cell = @import("../tui/Cell.zig");
|
||||
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
|
||||
const Widget = @import("../tui/Widget.zig");
|
||||
|
||||
pub const FRAME_DELAY: usize = 8;
|
||||
|
||||
@@ -57,8 +57,15 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fg: u32, hea
|
||||
};
|
||||
}
|
||||
|
||||
pub fn animation(self: *Matrix) Animation {
|
||||
return Animation.init(self, deinit, realloc, draw);
|
||||
pub fn widget(self: *Matrix) Widget {
|
||||
return Widget.init(
|
||||
self,
|
||||
deinit,
|
||||
realloc,
|
||||
draw,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
fn deinit(self: *Matrix) void {
|
||||
@@ -66,7 +73,7 @@ fn deinit(self: *Matrix) void {
|
||||
self.allocator.free(self.lines);
|
||||
}
|
||||
|
||||
fn realloc(self: *Matrix) anyerror!void {
|
||||
fn realloc(self: *Matrix) !void {
|
||||
const dots = try self.allocator.realloc(self.dots, self.terminal_buffer.width * (self.terminal_buffer.height + 1));
|
||||
const lines = try self.allocator.realloc(self.lines, self.terminal_buffer.width);
|
||||
|
||||
|
||||
1188
src/main.zig
1188
src/main.zig
File diff suppressed because it is too large
Load Diff
@@ -1,61 +0,0 @@
|
||||
const Animation = @This();
|
||||
|
||||
const VTable = struct {
|
||||
deinit_fn: *const fn (ptr: *anyopaque) void,
|
||||
realloc_fn: *const fn (ptr: *anyopaque) anyerror!void,
|
||||
draw_fn: *const fn (ptr: *anyopaque) void,
|
||||
};
|
||||
|
||||
pointer: *anyopaque,
|
||||
vtable: VTable,
|
||||
|
||||
pub fn init(
|
||||
pointer: anytype,
|
||||
comptime deinit_fn: fn (ptr: @TypeOf(pointer)) void,
|
||||
comptime realloc_fn: fn (ptr: @TypeOf(pointer)) anyerror!void,
|
||||
comptime draw_fn: fn (ptr: @TypeOf(pointer)) void,
|
||||
) Animation {
|
||||
const Pointer = @TypeOf(pointer);
|
||||
const Impl = struct {
|
||||
pub fn deinitImpl(ptr: *anyopaque) void {
|
||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||
return @call(.always_inline, deinit_fn, .{impl});
|
||||
}
|
||||
|
||||
pub fn reallocImpl(ptr: *anyopaque) anyerror!void {
|
||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||
return @call(.always_inline, realloc_fn, .{impl});
|
||||
}
|
||||
|
||||
pub fn drawImpl(ptr: *anyopaque) void {
|
||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||
return @call(.always_inline, draw_fn, .{impl});
|
||||
}
|
||||
|
||||
const vtable = VTable{
|
||||
.deinit_fn = deinitImpl,
|
||||
.realloc_fn = reallocImpl,
|
||||
.draw_fn = drawImpl,
|
||||
};
|
||||
};
|
||||
|
||||
return .{
|
||||
.pointer = pointer,
|
||||
.vtable = Impl.vtable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Animation) void {
|
||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||
return @call(.auto, self.vtable.deinit_fn, .{impl});
|
||||
}
|
||||
|
||||
pub fn realloc(self: *Animation) anyerror!void {
|
||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||
return @call(.auto, self.vtable.realloc_fn, .{impl});
|
||||
}
|
||||
|
||||
pub fn draw(self: *Animation) void {
|
||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||
return @call(.auto, self.vtable.draw_fn, .{impl});
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
const TerminalBuffer = @import("TerminalBuffer.zig");
|
||||
const termbox = TerminalBuffer.termbox;
|
||||
|
||||
const Cell = @This();
|
||||
|
||||
@@ -18,5 +17,5 @@ pub fn init(ch: u32, fg: u32, bg: u32) Cell {
|
||||
pub fn put(self: Cell, x: usize, y: usize) void {
|
||||
if (self.ch == 0) return;
|
||||
|
||||
_ = termbox.tb_set_cell(@intCast(x), @intCast(y), self.ch, self.fg, self.bg);
|
||||
TerminalBuffer.setCell(x, y, self.ch, self.fg, self.bg);
|
||||
}
|
||||
|
||||
@@ -144,34 +144,50 @@ pub fn init(allocator: Allocator, options: InitOptions, log_file: *LogFile, rand
|
||||
|
||||
pub fn deinit(self: *TerminalBuffer) void {
|
||||
self.keybinds.deinit();
|
||||
TerminalBuffer.shutdownStatic();
|
||||
TerminalBuffer.shutdown();
|
||||
}
|
||||
|
||||
pub fn getWidthStatic() usize {
|
||||
pub fn getWidth() usize {
|
||||
return @intCast(termbox.tb_width());
|
||||
}
|
||||
|
||||
pub fn getHeightStatic() usize {
|
||||
pub fn getHeight() usize {
|
||||
return @intCast(termbox.tb_height());
|
||||
}
|
||||
|
||||
pub fn setCursorStatic(x: usize, y: usize) void {
|
||||
pub fn setCursor(x: usize, y: usize) void {
|
||||
_ = termbox.tb_set_cursor(@intCast(x), @intCast(y));
|
||||
}
|
||||
|
||||
pub fn clearScreenStatic(clear_back_buffer: bool) !void {
|
||||
pub fn clearScreen(clear_back_buffer: bool) !void {
|
||||
_ = termbox.tb_clear();
|
||||
if (clear_back_buffer) try clearBackBuffer();
|
||||
}
|
||||
|
||||
pub fn shutdownStatic() void {
|
||||
pub fn shutdown() void {
|
||||
_ = termbox.tb_shutdown();
|
||||
}
|
||||
|
||||
pub fn presentBufferStatic() void {
|
||||
pub fn presentBuffer() void {
|
||||
_ = termbox.tb_present();
|
||||
}
|
||||
|
||||
pub fn setCell(
|
||||
x: usize,
|
||||
y: usize,
|
||||
ch: u32,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
) void {
|
||||
_ = termbox.tb_set_cell(
|
||||
@intCast(x),
|
||||
@intCast(y),
|
||||
ch,
|
||||
fg,
|
||||
bg,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn reclaim(self: TerminalBuffer) !void {
|
||||
if (self.termios) |termios| {
|
||||
// Take back control of the TTY
|
||||
@@ -257,17 +273,22 @@ pub fn handleKeybind(
|
||||
allocator: Allocator,
|
||||
tb_event: termbox.tb_event,
|
||||
context: *anyopaque,
|
||||
) !bool {
|
||||
) !?std.ArrayList(keyboard.Key) {
|
||||
var keys = try keyboard.getKeyList(allocator, tb_event);
|
||||
defer keys.deinit(allocator);
|
||||
|
||||
for (keys.items) |key| {
|
||||
if (self.keybinds.get(key)) |callback| {
|
||||
return @call(.auto, callback, .{context});
|
||||
const passthrough_event = try @call(.auto, callback, .{context});
|
||||
if (!passthrough_event) {
|
||||
keys.deinit(allocator);
|
||||
return null;
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return keys;
|
||||
}
|
||||
|
||||
pub fn drawText(
|
||||
|
||||
150
src/tui/Widget.zig
Normal file
150
src/tui/Widget.zig
Normal file
@@ -0,0 +1,150 @@
|
||||
const Widget = @This();
|
||||
|
||||
const keyboard = @import("keyboard.zig");
|
||||
const TerminalBuffer = @import("TerminalBuffer.zig");
|
||||
|
||||
const VTable = struct {
|
||||
deinit_fn: *const fn (ptr: *anyopaque) void,
|
||||
realloc_fn: *const fn (ptr: *anyopaque) anyerror!void,
|
||||
draw_fn: *const fn (ptr: *anyopaque) void,
|
||||
update_fn: *const fn (ptr: *anyopaque, ctx: *anyopaque) anyerror!void,
|
||||
handle_fn: *const fn (ptr: *anyopaque, maybe_key: ?keyboard.Key, insert_mode: bool) anyerror!void,
|
||||
};
|
||||
|
||||
pointer: *anyopaque,
|
||||
vtable: VTable,
|
||||
|
||||
pub fn init(
|
||||
pointer: anytype,
|
||||
comptime deinit_fn: ?fn (ptr: @TypeOf(pointer)) void,
|
||||
comptime realloc_fn: ?fn (ptr: @TypeOf(pointer)) anyerror!void,
|
||||
comptime draw_fn: ?fn (ptr: @TypeOf(pointer)) void,
|
||||
comptime update_fn: ?fn (ptr: @TypeOf(pointer), ctx: *anyopaque) anyerror!void,
|
||||
comptime handle_fn: ?fn (ptr: @TypeOf(pointer), maybe_key: ?keyboard.Key, insert_mode: bool) anyerror!void,
|
||||
) Widget {
|
||||
const Pointer = @TypeOf(pointer);
|
||||
const Impl = struct {
|
||||
pub fn deinitImpl(ptr: *anyopaque) void {
|
||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||
|
||||
if (deinit_fn) |func| {
|
||||
return @call(
|
||||
.always_inline,
|
||||
func,
|
||||
.{impl},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reallocImpl(ptr: *anyopaque) !void {
|
||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||
|
||||
if (realloc_fn) |func| {
|
||||
return @call(
|
||||
.always_inline,
|
||||
func,
|
||||
.{impl},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn drawImpl(ptr: *anyopaque) void {
|
||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||
|
||||
if (draw_fn) |func| {
|
||||
return @call(
|
||||
.always_inline,
|
||||
func,
|
||||
.{impl},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn updateImpl(ptr: *anyopaque, ctx: *anyopaque) !void {
|
||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||
|
||||
if (update_fn) |func| {
|
||||
return @call(
|
||||
.always_inline,
|
||||
func,
|
||||
.{ impl, ctx },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handleImpl(ptr: *anyopaque, maybe_key: ?keyboard.Key, insert_mode: bool) !void {
|
||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||
|
||||
if (handle_fn) |func| {
|
||||
return @call(
|
||||
.always_inline,
|
||||
func,
|
||||
.{ impl, maybe_key, insert_mode },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const vtable = VTable{
|
||||
.deinit_fn = deinitImpl,
|
||||
.realloc_fn = reallocImpl,
|
||||
.draw_fn = drawImpl,
|
||||
.update_fn = updateImpl,
|
||||
.handle_fn = handleImpl,
|
||||
};
|
||||
};
|
||||
|
||||
return .{
|
||||
.pointer = pointer,
|
||||
.vtable = Impl.vtable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Widget) void {
|
||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||
|
||||
return @call(
|
||||
.auto,
|
||||
self.vtable.deinit_fn,
|
||||
.{impl},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn realloc(self: *Widget) !void {
|
||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||
|
||||
return @call(
|
||||
.auto,
|
||||
self.vtable.realloc_fn,
|
||||
.{impl},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn draw(self: *Widget) void {
|
||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||
|
||||
return @call(
|
||||
.auto,
|
||||
self.vtable.draw_fn,
|
||||
.{impl},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn update(self: *Widget, ctx: *anyopaque) !void {
|
||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||
|
||||
return @call(
|
||||
.auto,
|
||||
self.vtable.update_fn,
|
||||
.{ impl, ctx },
|
||||
);
|
||||
}
|
||||
|
||||
pub fn handle(self: *Widget, maybe_key: ?keyboard.Key, insert_mode: bool) !void {
|
||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||
|
||||
return @call(
|
||||
.auto,
|
||||
self.vtable.handle_fn,
|
||||
.{ impl, maybe_key, insert_mode },
|
||||
);
|
||||
}
|
||||
215
src/tui/components/BigLabel.zig
Normal file
215
src/tui/components/BigLabel.zig
Normal file
@@ -0,0 +1,215 @@
|
||||
const BigLabel = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const ly_core = @import("ly-core");
|
||||
const interop = ly_core.interop;
|
||||
|
||||
const en = @import("bigLabelLocales/en.zig");
|
||||
const fa = @import("bigLabelLocales/fa.zig");
|
||||
const Cell = @import("../Cell.zig");
|
||||
const Position = @import("../Position.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const Widget = @import("../Widget.zig");
|
||||
|
||||
pub const CHAR_WIDTH = 5;
|
||||
pub const CHAR_HEIGHT = 5;
|
||||
pub const CHAR_SIZE = CHAR_WIDTH * CHAR_HEIGHT;
|
||||
pub const X: u32 = if (ly_core.interop.supportsUnicode()) 0x2593 else '#';
|
||||
pub const O: u32 = 0;
|
||||
|
||||
// zig fmt: off
|
||||
pub const LocaleChars = struct {
|
||||
ZERO: [CHAR_SIZE]u21,
|
||||
ONE: [CHAR_SIZE]u21,
|
||||
TWO: [CHAR_SIZE]u21,
|
||||
THREE: [CHAR_SIZE]u21,
|
||||
FOUR: [CHAR_SIZE]u21,
|
||||
FIVE: [CHAR_SIZE]u21,
|
||||
SIX: [CHAR_SIZE]u21,
|
||||
SEVEN: [CHAR_SIZE]u21,
|
||||
EIGHT: [CHAR_SIZE]u21,
|
||||
NINE: [CHAR_SIZE]u21,
|
||||
S: [CHAR_SIZE]u21,
|
||||
E: [CHAR_SIZE]u21,
|
||||
P: [CHAR_SIZE]u21,
|
||||
A: [CHAR_SIZE]u21,
|
||||
M: [CHAR_SIZE]u21,
|
||||
};
|
||||
// zig fmt: on
|
||||
|
||||
pub const BigLabelLocale = enum {
|
||||
en,
|
||||
fa,
|
||||
};
|
||||
|
||||
allocator: ?Allocator = null,
|
||||
buffer: *TerminalBuffer,
|
||||
text: []const u8,
|
||||
max_width: ?usize,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
locale: BigLabelLocale,
|
||||
update_fn: ?*const fn (*BigLabel, *anyopaque) anyerror!void,
|
||||
component_pos: Position,
|
||||
children_pos: Position,
|
||||
|
||||
pub fn init(
|
||||
buffer: *TerminalBuffer,
|
||||
text: []const u8,
|
||||
max_width: ?usize,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
locale: BigLabelLocale,
|
||||
update_fn: ?*const fn (*BigLabel, *anyopaque) anyerror!void,
|
||||
) BigLabel {
|
||||
return .{
|
||||
.allocator = null,
|
||||
.buffer = buffer,
|
||||
.text = text,
|
||||
.max_width = max_width,
|
||||
.fg = fg,
|
||||
.bg = bg,
|
||||
.locale = locale,
|
||||
.update_fn = update_fn,
|
||||
.component_pos = TerminalBuffer.START_POSITION,
|
||||
.children_pos = TerminalBuffer.START_POSITION,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *BigLabel) void {
|
||||
if (self.allocator) |allocator| allocator.free(self.text);
|
||||
}
|
||||
|
||||
pub fn widget(self: *BigLabel) Widget {
|
||||
return Widget.init(
|
||||
self,
|
||||
deinit,
|
||||
null,
|
||||
draw,
|
||||
update,
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setTextAlloc(
|
||||
self: *BigLabel,
|
||||
allocator: Allocator,
|
||||
comptime fmt: []const u8,
|
||||
args: anytype,
|
||||
) !void {
|
||||
self.text = try std.fmt.allocPrint(allocator, fmt, args);
|
||||
self.allocator = allocator;
|
||||
}
|
||||
|
||||
pub fn setTextBuf(
|
||||
self: *BigLabel,
|
||||
buffer: []u8,
|
||||
comptime fmt: []const u8,
|
||||
args: anytype,
|
||||
) !void {
|
||||
self.text = try std.fmt.bufPrint(buffer, fmt, args);
|
||||
self.allocator = null;
|
||||
}
|
||||
|
||||
pub fn setText(self: *BigLabel, text: []const u8) void {
|
||||
self.text = text;
|
||||
self.allocator = null;
|
||||
}
|
||||
|
||||
pub fn positionX(self: *BigLabel, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = original_pos.addX(TerminalBuffer.strWidth(self.text) * CHAR_WIDTH);
|
||||
}
|
||||
|
||||
pub fn positionY(self: *BigLabel, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = original_pos.addY(CHAR_HEIGHT);
|
||||
}
|
||||
|
||||
pub fn positionXY(self: *BigLabel, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = Position.init(
|
||||
TerminalBuffer.strWidth(self.text) * CHAR_WIDTH,
|
||||
CHAR_HEIGHT,
|
||||
).add(original_pos);
|
||||
}
|
||||
|
||||
pub fn childrenPosition(self: BigLabel) Position {
|
||||
return self.children_pos;
|
||||
}
|
||||
|
||||
pub fn draw(self: *BigLabel) void {
|
||||
for (self.text, 0..) |c, i| {
|
||||
const clock_cell = clockCell(
|
||||
c,
|
||||
self.fg,
|
||||
self.bg,
|
||||
self.locale,
|
||||
);
|
||||
|
||||
alphaBlit(
|
||||
self.component_pos.x + i * (CHAR_WIDTH + 1),
|
||||
self.component_pos.y,
|
||||
self.buffer.width,
|
||||
self.buffer.height,
|
||||
clock_cell,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(self: *BigLabel, context: *anyopaque) !void {
|
||||
if (self.update_fn) |update_fn| {
|
||||
return @call(
|
||||
.auto,
|
||||
update_fn,
|
||||
.{ self, context },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn clockCell(char: u8, fg: u32, bg: u32, locale: BigLabelLocale) [CHAR_SIZE]Cell {
|
||||
var cells: [CHAR_SIZE]Cell = undefined;
|
||||
|
||||
//@divTrunc(time.microseconds, 500000) != 0)
|
||||
const clock_chars = toBigNumber(char, locale);
|
||||
for (0..cells.len) |i| cells[i] = Cell.init(clock_chars[i], fg, bg);
|
||||
|
||||
return cells;
|
||||
}
|
||||
|
||||
fn alphaBlit(x: usize, y: usize, tb_width: usize, tb_height: usize, cells: [CHAR_SIZE]Cell) void {
|
||||
if (x + CHAR_WIDTH >= tb_width or y + CHAR_HEIGHT >= tb_height) return;
|
||||
|
||||
for (0..CHAR_HEIGHT) |yy| {
|
||||
for (0..CHAR_WIDTH) |xx| {
|
||||
const cell = cells[yy * CHAR_WIDTH + xx];
|
||||
cell.put(x + xx, y + yy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn toBigNumber(char: u8, locale: BigLabelLocale) [CHAR_SIZE]u21 {
|
||||
const locale_chars = switch (locale) {
|
||||
.fa => fa.locale_chars,
|
||||
.en => en.locale_chars,
|
||||
};
|
||||
return switch (char) {
|
||||
'0' => locale_chars.ZERO,
|
||||
'1' => locale_chars.ONE,
|
||||
'2' => locale_chars.TWO,
|
||||
'3' => locale_chars.THREE,
|
||||
'4' => locale_chars.FOUR,
|
||||
'5' => locale_chars.FIVE,
|
||||
'6' => locale_chars.SIX,
|
||||
'7' => locale_chars.SEVEN,
|
||||
'8' => locale_chars.EIGHT,
|
||||
'9' => locale_chars.NINE,
|
||||
'p', 'P' => locale_chars.P,
|
||||
'a', 'A' => locale_chars.A,
|
||||
'm', 'M' => locale_chars.M,
|
||||
':' => locale_chars.S,
|
||||
else => locale_chars.E,
|
||||
};
|
||||
}
|
||||
@@ -3,7 +3,7 @@ const std = @import("std");
|
||||
const Cell = @import("../Cell.zig");
|
||||
const Position = @import("../Position.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const termbox = TerminalBuffer.termbox;
|
||||
const Widget = @import("../Widget.zig");
|
||||
|
||||
const CenteredBox = @This();
|
||||
|
||||
@@ -56,6 +56,17 @@ pub fn init(
|
||||
};
|
||||
}
|
||||
|
||||
pub fn widget(self: *CenteredBox) Widget {
|
||||
return Widget.init(
|
||||
self,
|
||||
null,
|
||||
null,
|
||||
draw,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn positionXY(self: *CenteredBox, original_pos: Position) void {
|
||||
if (self.buffer.width < 2 or self.buffer.height < 2) return;
|
||||
|
||||
@@ -79,51 +90,55 @@ pub fn childrenPosition(self: CenteredBox) Position {
|
||||
return self.children_pos;
|
||||
}
|
||||
|
||||
pub fn draw(self: CenteredBox) void {
|
||||
pub fn draw(self: *CenteredBox) void {
|
||||
if (self.show_borders) {
|
||||
_ = termbox.tb_set_cell(
|
||||
@intCast(self.left_pos.x - 1),
|
||||
@intCast(self.left_pos.y - 1),
|
||||
var left_up = Cell.init(
|
||||
self.buffer.box_chars.left_up,
|
||||
self.border_fg,
|
||||
self.bg,
|
||||
);
|
||||
_ = termbox.tb_set_cell(
|
||||
@intCast(self.right_pos.x),
|
||||
@intCast(self.left_pos.y - 1),
|
||||
var right_up = Cell.init(
|
||||
self.buffer.box_chars.right_up,
|
||||
self.border_fg,
|
||||
self.bg,
|
||||
);
|
||||
_ = termbox.tb_set_cell(
|
||||
@intCast(self.left_pos.x - 1),
|
||||
@intCast(self.right_pos.y),
|
||||
var left_down = Cell.init(
|
||||
self.buffer.box_chars.left_down,
|
||||
self.border_fg,
|
||||
self.bg,
|
||||
);
|
||||
_ = termbox.tb_set_cell(
|
||||
@intCast(self.right_pos.x),
|
||||
@intCast(self.right_pos.y),
|
||||
var right_down = Cell.init(
|
||||
self.buffer.box_chars.right_down,
|
||||
self.border_fg,
|
||||
self.bg,
|
||||
);
|
||||
var top = Cell.init(
|
||||
self.buffer.box_chars.top,
|
||||
self.border_fg,
|
||||
self.bg,
|
||||
);
|
||||
var bottom = Cell.init(
|
||||
self.buffer.box_chars.bottom,
|
||||
self.border_fg,
|
||||
self.bg,
|
||||
);
|
||||
|
||||
var c1 = Cell.init(self.buffer.box_chars.top, self.border_fg, self.bg);
|
||||
var c2 = Cell.init(self.buffer.box_chars.bottom, self.border_fg, self.bg);
|
||||
left_up.put(self.left_pos.x - 1, self.left_pos.y - 1);
|
||||
right_up.put(self.right_pos.x, self.left_pos.y - 1);
|
||||
left_down.put(self.left_pos.x - 1, self.right_pos.y);
|
||||
right_down.put(self.right_pos.x, self.right_pos.y);
|
||||
|
||||
for (0..self.width) |i| {
|
||||
c1.put(self.left_pos.x + i, self.left_pos.y - 1);
|
||||
c2.put(self.left_pos.x + i, self.right_pos.y);
|
||||
top.put(self.left_pos.x + i, self.left_pos.y - 1);
|
||||
bottom.put(self.left_pos.x + i, self.right_pos.y);
|
||||
}
|
||||
|
||||
c1.ch = self.buffer.box_chars.left;
|
||||
c2.ch = self.buffer.box_chars.right;
|
||||
top.ch = self.buffer.box_chars.left;
|
||||
bottom.ch = self.buffer.box_chars.right;
|
||||
|
||||
for (0..self.height) |i| {
|
||||
c1.put(self.left_pos.x - 1, self.left_pos.y + i);
|
||||
c2.put(self.right_pos.x, self.left_pos.y + i);
|
||||
top.put(self.left_pos.x - 1, self.left_pos.y + i);
|
||||
bottom.put(self.right_pos.x, self.left_pos.y + i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const keyboard = @import("../keyboard.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const Widget = @import("../Widget.zig");
|
||||
const generic = @import("generic.zig");
|
||||
|
||||
const MessageLabel = generic.CyclableLabel(Message, Message);
|
||||
@@ -43,6 +45,25 @@ pub fn deinit(self: *InfoLine) void {
|
||||
self.label.deinit();
|
||||
}
|
||||
|
||||
pub fn widget(self: *InfoLine) Widget {
|
||||
return Widget.init(
|
||||
self,
|
||||
deinit,
|
||||
null,
|
||||
draw,
|
||||
null,
|
||||
handle,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn draw(self: *InfoLine) void {
|
||||
self.label.draw();
|
||||
}
|
||||
|
||||
pub fn handle(self: *InfoLine, maybe_key: ?keyboard.Key, insert_mode: bool) !void {
|
||||
self.label.handle(maybe_key, insert_mode);
|
||||
}
|
||||
|
||||
pub fn addMessage(self: *InfoLine, text: []const u8, bg: u32, fg: u32) !void {
|
||||
if (text.len == 0) return;
|
||||
|
||||
|
||||
131
src/tui/components/Label.zig
Normal file
131
src/tui/components/Label.zig
Normal file
@@ -0,0 +1,131 @@
|
||||
const Label = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const Cell = @import("../Cell.zig");
|
||||
const Position = @import("../Position.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const Widget = @import("../Widget.zig");
|
||||
|
||||
allocator: ?Allocator,
|
||||
text: []const u8,
|
||||
max_width: ?usize,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
update_fn: ?*const fn (*Label, *anyopaque) anyerror!void,
|
||||
component_pos: Position,
|
||||
children_pos: Position,
|
||||
|
||||
pub fn init(
|
||||
text: []const u8,
|
||||
max_width: ?usize,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
update_fn: ?*const fn (*Label, *anyopaque) anyerror!void,
|
||||
) Label {
|
||||
return .{
|
||||
.allocator = null,
|
||||
.text = text,
|
||||
.max_width = max_width,
|
||||
.fg = fg,
|
||||
.bg = bg,
|
||||
.update_fn = update_fn,
|
||||
.component_pos = TerminalBuffer.START_POSITION,
|
||||
.children_pos = TerminalBuffer.START_POSITION,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Label) void {
|
||||
if (self.allocator) |allocator| allocator.free(self.text);
|
||||
}
|
||||
|
||||
pub fn widget(self: *Label) Widget {
|
||||
return Widget.init(
|
||||
self,
|
||||
deinit,
|
||||
null,
|
||||
draw,
|
||||
update,
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setTextAlloc(
|
||||
self: *Label,
|
||||
allocator: Allocator,
|
||||
comptime fmt: []const u8,
|
||||
args: anytype,
|
||||
) !void {
|
||||
self.text = try std.fmt.allocPrint(allocator, fmt, args);
|
||||
self.allocator = allocator;
|
||||
}
|
||||
|
||||
pub fn setTextBuf(
|
||||
self: *Label,
|
||||
buffer: []u8,
|
||||
comptime fmt: []const u8,
|
||||
args: anytype,
|
||||
) !void {
|
||||
self.text = try std.fmt.bufPrint(buffer, fmt, args);
|
||||
self.allocator = null;
|
||||
}
|
||||
|
||||
pub fn setText(self: *Label, text: []const u8) void {
|
||||
self.text = text;
|
||||
self.allocator = null;
|
||||
}
|
||||
|
||||
pub fn positionX(self: *Label, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = original_pos.addX(TerminalBuffer.strWidth(self.text));
|
||||
}
|
||||
|
||||
pub fn positionY(self: *Label, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = original_pos.addY(1);
|
||||
}
|
||||
|
||||
pub fn positionXY(self: *Label, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = Position.init(
|
||||
TerminalBuffer.strWidth(self.text),
|
||||
1,
|
||||
).add(original_pos);
|
||||
}
|
||||
|
||||
pub fn childrenPosition(self: Label) Position {
|
||||
return self.children_pos;
|
||||
}
|
||||
|
||||
pub fn draw(self: *Label) void {
|
||||
if (self.max_width) |width| {
|
||||
TerminalBuffer.drawConfinedText(
|
||||
self.text,
|
||||
self.component_pos.x,
|
||||
self.component_pos.y,
|
||||
width,
|
||||
self.fg,
|
||||
self.bg,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
TerminalBuffer.drawText(
|
||||
self.text,
|
||||
self.component_pos.x,
|
||||
self.component_pos.y,
|
||||
self.fg,
|
||||
self.bg,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn update(self: *Label, ctx: *anyopaque) !void {
|
||||
if (self.update_fn) |update_fn| {
|
||||
return @call(
|
||||
.auto,
|
||||
update_fn,
|
||||
.{ self, ctx },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,9 @@ const Allocator = std.mem.Allocator;
|
||||
const enums = @import("../../enums.zig");
|
||||
const DisplayServer = enums.DisplayServer;
|
||||
const Environment = @import("../../Environment.zig");
|
||||
const keyboard = @import("../keyboard.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const Widget = @import("../Widget.zig");
|
||||
const generic = @import("generic.zig");
|
||||
const UserList = @import("UserList.zig");
|
||||
|
||||
@@ -53,6 +55,25 @@ pub fn deinit(self: *Session) void {
|
||||
self.label.deinit();
|
||||
}
|
||||
|
||||
pub fn widget(self: *Session) Widget {
|
||||
return Widget.init(
|
||||
self,
|
||||
deinit,
|
||||
null,
|
||||
draw,
|
||||
null,
|
||||
handle,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn draw(self: *Session) void {
|
||||
self.label.draw();
|
||||
}
|
||||
|
||||
pub fn handle(self: *Session, maybe_key: ?keyboard.Key, insert_mode: bool) !void {
|
||||
self.label.handle(maybe_key, insert_mode);
|
||||
}
|
||||
|
||||
pub fn addEnvironment(self: *Session, environment: Environment) !void {
|
||||
const env = Env{ .environment = environment, .index = self.label.list.items.len };
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const keyboard = @import("../keyboard.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const Position = @import("../Position.zig");
|
||||
const termbox = TerminalBuffer.termbox;
|
||||
const Widget = @import("../Widget.zig");
|
||||
|
||||
const DynamicString = std.ArrayListUnmanaged(u8);
|
||||
|
||||
@@ -53,6 +54,17 @@ pub fn deinit(self: *Text) void {
|
||||
self.text.deinit(self.allocator);
|
||||
}
|
||||
|
||||
pub fn widget(self: *Text) Widget {
|
||||
return Widget.init(
|
||||
self,
|
||||
deinit,
|
||||
null,
|
||||
draw,
|
||||
null,
|
||||
handle,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn positionX(self: *Text, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = original_pos.addX(self.width);
|
||||
@@ -75,50 +87,37 @@ pub fn childrenPosition(self: Text) Position {
|
||||
return self.children_pos;
|
||||
}
|
||||
|
||||
pub fn handle(self: *Text, maybe_event: ?*termbox.tb_event, insert_mode: bool) !void {
|
||||
if (maybe_event) |event| blk: {
|
||||
if (event.type != termbox.TB_EVENT_KEY) break :blk;
|
||||
|
||||
switch (event.key) {
|
||||
termbox.TB_KEY_ARROW_LEFT => self.goLeft(),
|
||||
termbox.TB_KEY_ARROW_RIGHT => self.goRight(),
|
||||
termbox.TB_KEY_DELETE => self.delete(),
|
||||
termbox.TB_KEY_BACKSPACE, termbox.TB_KEY_BACKSPACE2 => {
|
||||
if (insert_mode) {
|
||||
self.backspace();
|
||||
} else {
|
||||
pub fn handle(self: *Text, maybe_key: ?keyboard.Key, insert_mode: bool) !void {
|
||||
if (maybe_key) |key| {
|
||||
if (key.left or (!insert_mode and (key.h or key.backspace))) {
|
||||
self.goLeft();
|
||||
}
|
||||
},
|
||||
termbox.TB_KEY_SPACE => try self.write(' '),
|
||||
else => {
|
||||
if (event.ch > 31 and event.ch < 127) {
|
||||
if (insert_mode) {
|
||||
try self.write(@intCast(event.ch));
|
||||
} else {
|
||||
switch (event.ch) {
|
||||
'h' => self.goLeft(),
|
||||
'l' => self.goRight(),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
} else if (key.right or (!insert_mode and key.l)) {
|
||||
self.goRight();
|
||||
} else if (key.delete) {
|
||||
self.delete();
|
||||
} else if (key.backspace) {
|
||||
self.backspace();
|
||||
} else if (insert_mode) {
|
||||
const maybe_character = key.getEnabledPrintableAscii();
|
||||
if (maybe_character) |character| try self.write(character);
|
||||
}
|
||||
}
|
||||
|
||||
if (self.masked and self.maybe_mask == null) {
|
||||
_ = termbox.tb_set_cursor(@intCast(self.component_pos.x), @intCast(self.component_pos.y));
|
||||
TerminalBuffer.setCursor(
|
||||
self.component_pos.x,
|
||||
self.component_pos.y,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
_ = termbox.tb_set_cursor(
|
||||
@intCast(self.component_pos.x + (self.cursor - self.visible_start)),
|
||||
@intCast(self.component_pos.y),
|
||||
TerminalBuffer.setCursor(
|
||||
self.component_pos.x + (self.cursor - self.visible_start),
|
||||
self.component_pos.y,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn draw(self: Text) void {
|
||||
pub fn draw(self: *Text) void {
|
||||
if (self.masked) {
|
||||
if (self.maybe_mask) |mask| {
|
||||
if (self.width < 1) return;
|
||||
|
||||
@@ -2,7 +2,9 @@ const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const SavedUsers = @import("../../config/SavedUsers.zig");
|
||||
const keyboard = @import("../keyboard.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const Widget = @import("../Widget.zig");
|
||||
const generic = @import("generic.zig");
|
||||
const Session = @import("Session.zig");
|
||||
|
||||
@@ -85,10 +87,29 @@ pub fn deinit(self: *UserList) void {
|
||||
self.label.deinit();
|
||||
}
|
||||
|
||||
pub fn widget(self: *UserList) Widget {
|
||||
return Widget.init(
|
||||
self,
|
||||
deinit,
|
||||
null,
|
||||
draw,
|
||||
null,
|
||||
handle,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn getCurrentUsername(self: UserList) []const u8 {
|
||||
return self.label.list.items[self.label.current].name;
|
||||
}
|
||||
|
||||
fn draw(self: *UserList) void {
|
||||
self.label.draw();
|
||||
}
|
||||
|
||||
fn handle(self: *UserList, maybe_key: ?keyboard.Key, insert_mode: bool) !void {
|
||||
self.label.handle(maybe_key, insert_mode);
|
||||
}
|
||||
|
||||
fn usernameChanged(user: User, maybe_session: ?*Session) void {
|
||||
if (maybe_session) |session| {
|
||||
session.label.current = @min(user.session_index.*, session.label.list.items.len - 1);
|
||||
|
||||
@@ -1,210 +0,0 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const ly_core = @import("ly-core");
|
||||
const interop = ly_core.interop;
|
||||
|
||||
const en = @import("bigLabelLocales/en.zig");
|
||||
const fa = @import("bigLabelLocales/fa.zig");
|
||||
const Cell = @import("../Cell.zig");
|
||||
const Position = @import("../Position.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const termbox = TerminalBuffer.termbox;
|
||||
|
||||
pub const CHAR_WIDTH = 5;
|
||||
pub const CHAR_HEIGHT = 5;
|
||||
pub const CHAR_SIZE = CHAR_WIDTH * CHAR_HEIGHT;
|
||||
pub const X: u32 = if (ly_core.interop.supportsUnicode()) 0x2593 else '#';
|
||||
pub const O: u32 = 0;
|
||||
|
||||
// zig fmt: off
|
||||
pub const LocaleChars = struct {
|
||||
ZERO: [CHAR_SIZE]u21,
|
||||
ONE: [CHAR_SIZE]u21,
|
||||
TWO: [CHAR_SIZE]u21,
|
||||
THREE: [CHAR_SIZE]u21,
|
||||
FOUR: [CHAR_SIZE]u21,
|
||||
FIVE: [CHAR_SIZE]u21,
|
||||
SIX: [CHAR_SIZE]u21,
|
||||
SEVEN: [CHAR_SIZE]u21,
|
||||
EIGHT: [CHAR_SIZE]u21,
|
||||
NINE: [CHAR_SIZE]u21,
|
||||
S: [CHAR_SIZE]u21,
|
||||
E: [CHAR_SIZE]u21,
|
||||
P: [CHAR_SIZE]u21,
|
||||
A: [CHAR_SIZE]u21,
|
||||
M: [CHAR_SIZE]u21,
|
||||
};
|
||||
// zig fmt: on
|
||||
|
||||
pub const BigLabelLocale = enum {
|
||||
en,
|
||||
fa,
|
||||
};
|
||||
|
||||
pub fn BigLabel(comptime ContextType: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
buffer: *TerminalBuffer,
|
||||
text: []const u8,
|
||||
max_width: ?usize,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
locale: BigLabelLocale,
|
||||
update_fn: ?*const fn (*Self, ContextType) anyerror!void,
|
||||
is_text_allocated: bool,
|
||||
component_pos: Position,
|
||||
children_pos: Position,
|
||||
|
||||
pub fn init(
|
||||
buffer: *TerminalBuffer,
|
||||
text: []const u8,
|
||||
max_width: ?usize,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
locale: BigLabelLocale,
|
||||
update_fn: ?*const fn (*Self, ContextType) anyerror!void,
|
||||
) Self {
|
||||
return .{
|
||||
.buffer = buffer,
|
||||
.text = text,
|
||||
.max_width = max_width,
|
||||
.fg = fg,
|
||||
.bg = bg,
|
||||
.locale = locale,
|
||||
.update_fn = update_fn,
|
||||
.is_text_allocated = false,
|
||||
.component_pos = TerminalBuffer.START_POSITION,
|
||||
.children_pos = TerminalBuffer.START_POSITION,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn setTextAlloc(
|
||||
self: *Self,
|
||||
allocator: Allocator,
|
||||
comptime fmt: []const u8,
|
||||
args: anytype,
|
||||
) !void {
|
||||
self.text = try std.fmt.allocPrint(allocator, fmt, args);
|
||||
self.is_text_allocated = true;
|
||||
}
|
||||
|
||||
pub fn setTextBuf(
|
||||
self: *Self,
|
||||
buffer: []u8,
|
||||
comptime fmt: []const u8,
|
||||
args: anytype,
|
||||
) !void {
|
||||
self.text = try std.fmt.bufPrint(buffer, fmt, args);
|
||||
self.is_text_allocated = false;
|
||||
}
|
||||
|
||||
pub fn setText(self: *Self, text: []const u8) void {
|
||||
self.text = text;
|
||||
self.is_text_allocated = false;
|
||||
}
|
||||
|
||||
pub fn deinit(self: Self, allocator: ?Allocator) void {
|
||||
if (self.is_text_allocated) {
|
||||
if (allocator) |alloc| alloc.free(self.text);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn positionX(self: *Self, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = original_pos.addX(TerminalBuffer.strWidth(self.text) * CHAR_WIDTH);
|
||||
}
|
||||
|
||||
pub fn positionY(self: *Self, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = original_pos.addY(CHAR_HEIGHT);
|
||||
}
|
||||
|
||||
pub fn positionXY(self: *Self, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = Position.init(
|
||||
TerminalBuffer.strWidth(self.text) * CHAR_WIDTH,
|
||||
CHAR_HEIGHT,
|
||||
).add(original_pos);
|
||||
}
|
||||
|
||||
pub fn childrenPosition(self: Self) Position {
|
||||
return self.children_pos;
|
||||
}
|
||||
|
||||
pub fn draw(self: Self) void {
|
||||
for (self.text, 0..) |c, i| {
|
||||
const clock_cell = clockCell(
|
||||
c,
|
||||
self.fg,
|
||||
self.bg,
|
||||
self.locale,
|
||||
);
|
||||
|
||||
alphaBlit(
|
||||
self.component_pos.x + i * (CHAR_WIDTH + 1),
|
||||
self.component_pos.y,
|
||||
self.buffer.width,
|
||||
self.buffer.height,
|
||||
clock_cell,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(self: *Self, context: ContextType) !void {
|
||||
if (self.update_fn) |update_fn| {
|
||||
return @call(
|
||||
.auto,
|
||||
update_fn,
|
||||
.{ self, context },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn clockCell(char: u8, fg: u32, bg: u32, locale: BigLabelLocale) [CHAR_SIZE]Cell {
|
||||
var cells: [CHAR_SIZE]Cell = undefined;
|
||||
|
||||
//@divTrunc(time.microseconds, 500000) != 0)
|
||||
const clock_chars = toBigNumber(char, locale);
|
||||
for (0..cells.len) |i| cells[i] = Cell.init(clock_chars[i], fg, bg);
|
||||
|
||||
return cells;
|
||||
}
|
||||
|
||||
fn alphaBlit(x: usize, y: usize, tb_width: usize, tb_height: usize, cells: [CHAR_SIZE]Cell) void {
|
||||
if (x + CHAR_WIDTH >= tb_width or y + CHAR_HEIGHT >= tb_height) return;
|
||||
|
||||
for (0..CHAR_HEIGHT) |yy| {
|
||||
for (0..CHAR_WIDTH) |xx| {
|
||||
const cell = cells[yy * CHAR_WIDTH + xx];
|
||||
cell.put(x + xx, y + yy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn toBigNumber(char: u8, locale: BigLabelLocale) [CHAR_SIZE]u21 {
|
||||
const locale_chars = switch (locale) {
|
||||
.fa => fa.locale_chars,
|
||||
.en => en.locale_chars,
|
||||
};
|
||||
return switch (char) {
|
||||
'0' => locale_chars.ZERO,
|
||||
'1' => locale_chars.ONE,
|
||||
'2' => locale_chars.TWO,
|
||||
'3' => locale_chars.THREE,
|
||||
'4' => locale_chars.FOUR,
|
||||
'5' => locale_chars.FIVE,
|
||||
'6' => locale_chars.SIX,
|
||||
'7' => locale_chars.SEVEN,
|
||||
'8' => locale_chars.EIGHT,
|
||||
'9' => locale_chars.NINE,
|
||||
'p', 'P' => locale_chars.P,
|
||||
'a', 'A' => locale_chars.A,
|
||||
'm', 'M' => locale_chars.M,
|
||||
':' => locale_chars.S,
|
||||
else => locale_chars.E,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
const bigLabel = @import("../bigLabel.zig");
|
||||
const LocaleChars = bigLabel.LocaleChars;
|
||||
const X = bigLabel.X;
|
||||
const O = bigLabel.O;
|
||||
const BigLabel = @import("../BigLabel.zig");
|
||||
const LocaleChars = BigLabel.LocaleChars;
|
||||
const X = BigLabel.X;
|
||||
const O = BigLabel.O;
|
||||
|
||||
// zig fmt: off
|
||||
pub const locale_chars = LocaleChars{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const bigLabel = @import("../bigLabel.zig");
|
||||
const LocaleChars = bigLabel.LocaleChars;
|
||||
const X = bigLabel.X;
|
||||
const O = bigLabel.O;
|
||||
const BigLabel = @import("../BigLabel.zig");
|
||||
const LocaleChars = BigLabel.LocaleChars;
|
||||
const X = BigLabel.X;
|
||||
const O = BigLabel.O;
|
||||
|
||||
// zig fmt: off
|
||||
pub const locale_chars = LocaleChars{
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
const std = @import("std");
|
||||
|
||||
const Cell = @import("../Cell.zig");
|
||||
const keyboard = @import("../keyboard.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const Position = @import("../Position.zig");
|
||||
|
||||
@@ -10,8 +12,6 @@ pub fn CyclableLabel(comptime ItemType: type, comptime ChangeItemType: type) typ
|
||||
const DrawItemFn = *const fn (*Self, ItemType, usize, usize, usize) void;
|
||||
const ChangeItemFn = *const fn (ItemType, ?ChangeItemType) void;
|
||||
|
||||
const termbox = TerminalBuffer.termbox;
|
||||
|
||||
const Self = @This();
|
||||
|
||||
allocator: Allocator,
|
||||
@@ -92,28 +92,18 @@ pub fn CyclableLabel(comptime ItemType: type, comptime ChangeItemType: type) typ
|
||||
self.current = self.list.items.len - 1;
|
||||
}
|
||||
|
||||
pub fn handle(self: *Self, maybe_event: ?*termbox.tb_event, insert_mode: bool) void {
|
||||
if (maybe_event) |event| blk: {
|
||||
if (event.type != termbox.TB_EVENT_KEY) break :blk;
|
||||
|
||||
switch (event.key) {
|
||||
termbox.TB_KEY_ARROW_LEFT, termbox.TB_KEY_CTRL_H => self.goLeft(),
|
||||
termbox.TB_KEY_ARROW_RIGHT, termbox.TB_KEY_CTRL_L => self.goRight(),
|
||||
else => {
|
||||
if (!insert_mode) {
|
||||
switch (event.ch) {
|
||||
'h' => self.goLeft(),
|
||||
'l' => self.goRight(),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
},
|
||||
pub fn handle(self: *Self, maybe_key: ?keyboard.Key, insert_mode: bool) void {
|
||||
if (maybe_key) |key| {
|
||||
if (key.left or (key.ctrl and key.h) or (!insert_mode and key.h)) {
|
||||
self.goLeft();
|
||||
} else if (key.right or (key.ctrl and key.l) or (!insert_mode and key.l)) {
|
||||
self.goRight();
|
||||
}
|
||||
}
|
||||
|
||||
_ = termbox.tb_set_cursor(
|
||||
@intCast(self.component_pos.x + self.cursor + 2),
|
||||
@intCast(self.component_pos.y),
|
||||
TerminalBuffer.setCursor(
|
||||
self.component_pos.x + self.cursor + 2,
|
||||
self.component_pos.y,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -121,19 +111,13 @@ pub fn CyclableLabel(comptime ItemType: type, comptime ChangeItemType: type) typ
|
||||
if (self.list.items.len == 0) return;
|
||||
if (self.width < 2) return;
|
||||
|
||||
_ = termbox.tb_set_cell(
|
||||
@intCast(self.component_pos.x),
|
||||
@intCast(self.component_pos.y),
|
||||
'<',
|
||||
self.fg,
|
||||
self.bg,
|
||||
);
|
||||
_ = termbox.tb_set_cell(
|
||||
@intCast(self.component_pos.x + self.width - 1),
|
||||
@intCast(self.component_pos.y),
|
||||
'>',
|
||||
self.fg,
|
||||
self.bg,
|
||||
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);
|
||||
right_arrow.put(
|
||||
self.component_pos.x + self.width - 1,
|
||||
self.component_pos.y,
|
||||
);
|
||||
|
||||
const current_item = self.list.items[self.current];
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const Cell = @import("../Cell.zig");
|
||||
const Position = @import("../Position.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const termbox = TerminalBuffer.termbox;
|
||||
|
||||
pub fn Label(comptime ContextType: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
text: []const u8,
|
||||
max_width: ?usize,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
update_fn: ?*const fn (*Self, ContextType) anyerror!void,
|
||||
is_text_allocated: bool,
|
||||
component_pos: Position,
|
||||
children_pos: Position,
|
||||
|
||||
pub fn init(
|
||||
text: []const u8,
|
||||
max_width: ?usize,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
update_fn: ?*const fn (*Self, ContextType) anyerror!void,
|
||||
) Self {
|
||||
return .{
|
||||
.text = text,
|
||||
.max_width = max_width,
|
||||
.fg = fg,
|
||||
.bg = bg,
|
||||
.update_fn = update_fn,
|
||||
.is_text_allocated = false,
|
||||
.component_pos = TerminalBuffer.START_POSITION,
|
||||
.children_pos = TerminalBuffer.START_POSITION,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn setTextAlloc(
|
||||
self: *Self,
|
||||
allocator: Allocator,
|
||||
comptime fmt: []const u8,
|
||||
args: anytype,
|
||||
) !void {
|
||||
self.text = try std.fmt.allocPrint(allocator, fmt, args);
|
||||
self.is_text_allocated = true;
|
||||
}
|
||||
|
||||
pub fn setTextBuf(
|
||||
self: *Self,
|
||||
buffer: []u8,
|
||||
comptime fmt: []const u8,
|
||||
args: anytype,
|
||||
) !void {
|
||||
self.text = try std.fmt.bufPrint(buffer, fmt, args);
|
||||
self.is_text_allocated = false;
|
||||
}
|
||||
|
||||
pub fn setText(self: *Self, text: []const u8) void {
|
||||
self.text = text;
|
||||
self.is_text_allocated = false;
|
||||
}
|
||||
|
||||
pub fn deinit(self: Self, allocator: ?Allocator) void {
|
||||
if (self.is_text_allocated) {
|
||||
if (allocator) |alloc| alloc.free(self.text);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn positionX(self: *Self, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = original_pos.addX(TerminalBuffer.strWidth(self.text));
|
||||
}
|
||||
|
||||
pub fn positionY(self: *Self, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = original_pos.addY(1);
|
||||
}
|
||||
|
||||
pub fn positionXY(self: *Self, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = Position.init(
|
||||
TerminalBuffer.strWidth(self.text),
|
||||
1,
|
||||
).add(original_pos);
|
||||
}
|
||||
|
||||
pub fn childrenPosition(self: Self) Position {
|
||||
return self.children_pos;
|
||||
}
|
||||
|
||||
pub fn draw(self: Self) void {
|
||||
if (self.max_width) |width| {
|
||||
TerminalBuffer.drawConfinedText(
|
||||
self.text,
|
||||
self.component_pos.x,
|
||||
self.component_pos.y,
|
||||
width,
|
||||
self.fg,
|
||||
self.bg,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
TerminalBuffer.drawText(
|
||||
self.text,
|
||||
self.component_pos.x,
|
||||
self.component_pos.y,
|
||||
self.fg,
|
||||
self.bg,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn update(self: *Self, context: ContextType) !void {
|
||||
if (self.update_fn) |update_fn| {
|
||||
return @call(
|
||||
.auto,
|
||||
update_fn,
|
||||
.{ self, context },
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -36,8 +36,8 @@ pub const Key = packed struct {
|
||||
tab: bool,
|
||||
backspace: bool,
|
||||
enter: bool,
|
||||
space: bool,
|
||||
|
||||
@" ": bool,
|
||||
@"!": bool,
|
||||
@"`": bool,
|
||||
esc: bool,
|
||||
@@ -109,6 +109,18 @@ pub const Key = packed struct {
|
||||
x: bool,
|
||||
y: bool,
|
||||
z: bool,
|
||||
|
||||
pub fn getEnabledPrintableAscii(self: Key) ?u8 {
|
||||
if (self.ctrl or self.shift or self.alt) return null;
|
||||
|
||||
inline for (std.meta.fields(Key)) |field| {
|
||||
if (field.name.len == 1 and std.ascii.isPrint(field.name[0]) and @field(self, field.name)) {
|
||||
return field.name[0];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn getKeyList(allocator: Allocator, tb_event: termbox.tb_event) !KeyList {
|
||||
@@ -326,7 +338,7 @@ pub fn getKeyList(allocator: Allocator, tb_event: termbox.tb_event) !KeyList {
|
||||
key._ = true;
|
||||
},
|
||||
32 => {
|
||||
key.space = true;
|
||||
key.@" " = true;
|
||||
},
|
||||
33 => {
|
||||
key.shift = true;
|
||||
|
||||
Reference in New Issue
Block a user