mirror of
https://github.com/fairyglade/ly.git
synced 2026-03-21 22:43:38 +00:00
Add the cascade animation as a separate widget
Signed-off-by: AnErrupTion <anerruption@disroot.org>
This commit is contained in:
81
src/animations/Cascade.zig
Normal file
81
src/animations/Cascade.zig
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const math = std.math;
|
||||||
|
|
||||||
|
const Cell = @import("../tui/Cell.zig");
|
||||||
|
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
|
||||||
|
const Widget = @import("../tui/Widget.zig");
|
||||||
|
|
||||||
|
const Cascade = @This();
|
||||||
|
|
||||||
|
buffer: *TerminalBuffer,
|
||||||
|
current_auth_fails: *usize,
|
||||||
|
max_auth_fails: usize,
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
buffer: *TerminalBuffer,
|
||||||
|
current_auth_fails: *usize,
|
||||||
|
max_auth_fails: usize,
|
||||||
|
) Cascade {
|
||||||
|
return .{
|
||||||
|
.buffer = buffer,
|
||||||
|
.current_auth_fails = current_auth_fails,
|
||||||
|
.max_auth_fails = max_auth_fails,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn widget(self: *Cascade) Widget {
|
||||||
|
return Widget.init(
|
||||||
|
self,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
draw,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(self: *Cascade) void {
|
||||||
|
while (self.current_auth_fails.* >= self.max_auth_fails) {
|
||||||
|
std.Thread.sleep(std.time.ns_per_ms * 10);
|
||||||
|
|
||||||
|
var changed = false;
|
||||||
|
var y = self.buffer.height - 2;
|
||||||
|
|
||||||
|
while (y > 0) : (y -= 1) {
|
||||||
|
for (0..self.buffer.width) |x| {
|
||||||
|
const cell = TerminalBuffer.getCell(x, y - 1);
|
||||||
|
const cell_under = TerminalBuffer.getCell(x, y);
|
||||||
|
|
||||||
|
// This shouldn't happen under normal circumstances, but because
|
||||||
|
// this is a *secret* animation, there's no need to care that much
|
||||||
|
if (cell == null or cell_under == null) continue;
|
||||||
|
|
||||||
|
const char: u8 = @truncate(cell.?.ch);
|
||||||
|
if (std.ascii.isWhitespace(char)) continue;
|
||||||
|
|
||||||
|
const char_under: u8 = @truncate(cell_under.?.ch);
|
||||||
|
if (!std.ascii.isWhitespace(char_under)) continue;
|
||||||
|
|
||||||
|
changed = true;
|
||||||
|
|
||||||
|
if ((self.buffer.random.int(u16) % 10) > 7) continue;
|
||||||
|
|
||||||
|
cell.?.put(x, y);
|
||||||
|
|
||||||
|
var space = Cell.init(
|
||||||
|
' ',
|
||||||
|
cell_under.?.fg,
|
||||||
|
cell_under.?.bg,
|
||||||
|
);
|
||||||
|
space.put(x, y - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!changed) {
|
||||||
|
std.Thread.sleep(std.time.ns_per_s * 7);
|
||||||
|
self.current_auth_fails.* = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminalBuffer.presentBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,14 +17,22 @@ fn length(vec: Vec2) f32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
terminal_buffer: *TerminalBuffer,
|
terminal_buffer: *TerminalBuffer,
|
||||||
|
timeout: *bool,
|
||||||
frames: u64,
|
frames: u64,
|
||||||
pattern_cos_mod: f32,
|
pattern_cos_mod: f32,
|
||||||
pattern_sin_mod: f32,
|
pattern_sin_mod: f32,
|
||||||
palette: [palette_len]Cell,
|
palette: [palette_len]Cell,
|
||||||
|
|
||||||
pub fn init(terminal_buffer: *TerminalBuffer, col1: u32, col2: u32, col3: u32) ColorMix {
|
pub fn init(
|
||||||
|
terminal_buffer: *TerminalBuffer,
|
||||||
|
col1: u32,
|
||||||
|
col2: u32,
|
||||||
|
col3: u32,
|
||||||
|
timeout: *bool,
|
||||||
|
) ColorMix {
|
||||||
return .{
|
return .{
|
||||||
.terminal_buffer = terminal_buffer,
|
.terminal_buffer = terminal_buffer,
|
||||||
|
.timeout = timeout,
|
||||||
.frames = 0,
|
.frames = 0,
|
||||||
.pattern_cos_mod = terminal_buffer.random.float(f32) * math.pi * 2.0,
|
.pattern_cos_mod = terminal_buffer.random.float(f32) * math.pi * 2.0,
|
||||||
.pattern_sin_mod = terminal_buffer.random.float(f32) * math.pi * 2.0,
|
.pattern_sin_mod = terminal_buffer.random.float(f32) * math.pi * 2.0,
|
||||||
@@ -57,6 +65,8 @@ pub fn widget(self: *ColorMix) Widget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn draw(self: *ColorMix) void {
|
fn draw(self: *ColorMix) void {
|
||||||
|
if (self.timeout.*) return;
|
||||||
|
|
||||||
self.frames +%= 1;
|
self.frames +%= 1;
|
||||||
const time: f32 = @as(f32, @floatFromInt(self.frames)) * time_scale;
|
const time: f32 = @as(f32, @floatFromInt(self.frames)) * time_scale;
|
||||||
|
|
||||||
|
|||||||
@@ -13,12 +13,22 @@ pub const SPREAD_MAX = 4;
|
|||||||
|
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
terminal_buffer: *TerminalBuffer,
|
terminal_buffer: *TerminalBuffer,
|
||||||
|
timeout: *bool,
|
||||||
buffer: []u8,
|
buffer: []u8,
|
||||||
height: u8,
|
height: u8,
|
||||||
spread: u8,
|
spread: u8,
|
||||||
fire: [STEPS + 1]Cell,
|
fire: [STEPS + 1]Cell,
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, top_color: u32, middle_color: u32, bottom_color: u32, fire_height: u8, fire_spread: u8) !Doom {
|
pub fn init(
|
||||||
|
allocator: Allocator,
|
||||||
|
terminal_buffer: *TerminalBuffer,
|
||||||
|
top_color: u32,
|
||||||
|
middle_color: u32,
|
||||||
|
bottom_color: u32,
|
||||||
|
fire_height: u8,
|
||||||
|
fire_spread: u8,
|
||||||
|
timeout: *bool,
|
||||||
|
) !Doom {
|
||||||
const buffer = try allocator.alloc(u8, terminal_buffer.width * terminal_buffer.height);
|
const buffer = try allocator.alloc(u8, terminal_buffer.width * terminal_buffer.height);
|
||||||
initBuffer(buffer, terminal_buffer.width);
|
initBuffer(buffer, terminal_buffer.width);
|
||||||
|
|
||||||
@@ -42,6 +52,7 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, top_color: u
|
|||||||
return .{
|
return .{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.terminal_buffer = terminal_buffer,
|
.terminal_buffer = terminal_buffer,
|
||||||
|
.timeout = timeout,
|
||||||
.buffer = buffer,
|
.buffer = buffer,
|
||||||
.height = @min(HEIGHT_MAX, fire_height),
|
.height = @min(HEIGHT_MAX, fire_height),
|
||||||
.spread = @min(SPREAD_MAX, fire_spread),
|
.spread = @min(SPREAD_MAX, fire_spread),
|
||||||
@@ -71,6 +82,8 @@ fn realloc(self: *Doom) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn draw(self: *Doom) void {
|
fn draw(self: *Doom) void {
|
||||||
|
if (self.timeout.*) return;
|
||||||
|
|
||||||
for (0..self.terminal_buffer.width) |x| {
|
for (0..self.terminal_buffer.width) |x| {
|
||||||
// We start from 1 so that we always have the topmost line when spreading fire
|
// We start from 1 so that we always have the topmost line when spreading fire
|
||||||
for (1..self.terminal_buffer.height) |y| {
|
for (1..self.terminal_buffer.height) |y| {
|
||||||
|
|||||||
@@ -307,6 +307,7 @@ frames: u64,
|
|||||||
frame_size: UVec2,
|
frame_size: UVec2,
|
||||||
start_pos: IVec2,
|
start_pos: IVec2,
|
||||||
full_color: bool,
|
full_color: bool,
|
||||||
|
timeout: *bool,
|
||||||
frame_time: u32,
|
frame_time: u32,
|
||||||
time_previous: i64,
|
time_previous: i64,
|
||||||
is_color_format_16: bool,
|
is_color_format_16: bool,
|
||||||
@@ -357,7 +358,17 @@ fn calc_frame_size(terminal_buffer: *TerminalBuffer, dur_movie: *DurFormat) UVec
|
|||||||
return .{ frame_width, frame_height };
|
return .{ frame_width, frame_height };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, log_file: *LogFile, file_path: []const u8, offset_alignment: DurOffsetAlignment, x_offset: i32, y_offset: i32, full_color: bool) !DurFile {
|
pub fn init(
|
||||||
|
allocator: Allocator,
|
||||||
|
terminal_buffer: *TerminalBuffer,
|
||||||
|
log_file: *LogFile,
|
||||||
|
file_path: []const u8,
|
||||||
|
offset_alignment: DurOffsetAlignment,
|
||||||
|
x_offset: i32,
|
||||||
|
y_offset: i32,
|
||||||
|
full_color: bool,
|
||||||
|
timeout: *bool,
|
||||||
|
) !DurFile {
|
||||||
var dur_movie: DurFormat = .init(allocator);
|
var dur_movie: DurFormat = .init(allocator);
|
||||||
|
|
||||||
dur_movie.create_from_file(allocator, file_path) catch |err| switch (err) {
|
dur_movie.create_from_file(allocator, file_path) catch |err| switch (err) {
|
||||||
@@ -395,6 +406,7 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, log_file: *L
|
|||||||
.frame_size = frame_size,
|
.frame_size = frame_size,
|
||||||
.start_pos = start_pos,
|
.start_pos = start_pos,
|
||||||
.full_color = full_color,
|
.full_color = full_color,
|
||||||
|
.timeout = timeout,
|
||||||
.dur_movie = dur_movie,
|
.dur_movie = dur_movie,
|
||||||
.frame_time = frame_time,
|
.frame_time = frame_time,
|
||||||
.is_color_format_16 = eql(u8, dur_movie.colorFormat.?, "16"),
|
.is_color_format_16 = eql(u8, dur_movie.colorFormat.?, "16"),
|
||||||
@@ -425,6 +437,8 @@ fn realloc(self: *DurFile) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn draw(self: *DurFile) void {
|
fn draw(self: *DurFile) void {
|
||||||
|
if (self.timeout.*) return;
|
||||||
|
|
||||||
const current_frame = self.dur_movie.frames.items[self.frames];
|
const current_frame = self.dur_movie.frames.items[self.frames];
|
||||||
|
|
||||||
const buf_width: u32 = @intCast(self.terminal_buffer.width);
|
const buf_width: u32 = @intCast(self.terminal_buffer.width);
|
||||||
|
|||||||
@@ -26,11 +26,20 @@ fg_color: u32,
|
|||||||
entropy_interval: usize,
|
entropy_interval: usize,
|
||||||
frame_delay: usize,
|
frame_delay: usize,
|
||||||
initial_density: f32,
|
initial_density: f32,
|
||||||
|
timeout: *bool,
|
||||||
dead_cell: Cell,
|
dead_cell: Cell,
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fg_color: u32, entropy_interval: usize, frame_delay: usize, initial_density: f32) !GameOfLife {
|
pub fn init(
|
||||||
|
allocator: Allocator,
|
||||||
|
terminal_buffer: *TerminalBuffer,
|
||||||
|
fg_color: u32,
|
||||||
|
entropy_interval: usize,
|
||||||
|
frame_delay: usize,
|
||||||
|
initial_density: f32,
|
||||||
|
timeout: *bool,
|
||||||
|
) !GameOfLife {
|
||||||
const width = terminal_buffer.width;
|
const width = terminal_buffer.width;
|
||||||
const height = terminal_buffer.height;
|
const height = terminal_buffer.height;
|
||||||
const grid_size = width * height;
|
const grid_size = width * height;
|
||||||
@@ -49,6 +58,7 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fg_color: u3
|
|||||||
.entropy_interval = entropy_interval,
|
.entropy_interval = entropy_interval,
|
||||||
.frame_delay = frame_delay,
|
.frame_delay = frame_delay,
|
||||||
.initial_density = initial_density,
|
.initial_density = initial_density,
|
||||||
|
.timeout = timeout,
|
||||||
.dead_cell = .{ .ch = DEAD_CHAR, .fg = @intCast(TerminalBuffer.Color.DEFAULT), .bg = terminal_buffer.bg },
|
.dead_cell = .{ .ch = DEAD_CHAR, .fg = @intCast(TerminalBuffer.Color.DEFAULT), .bg = terminal_buffer.bg },
|
||||||
.width = width,
|
.width = width,
|
||||||
.height = height,
|
.height = height,
|
||||||
@@ -94,6 +104,8 @@ fn realloc(self: *GameOfLife) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn draw(self: *GameOfLife) void {
|
fn draw(self: *GameOfLife) void {
|
||||||
|
if (self.timeout.*) return;
|
||||||
|
|
||||||
// Update game state at controlled frame rate
|
// Update game state at controlled frame rate
|
||||||
self.frame_counter += 1;
|
self.frame_counter += 1;
|
||||||
if (self.frame_counter >= self.frame_delay) {
|
if (self.frame_counter >= self.frame_delay) {
|
||||||
|
|||||||
@@ -34,9 +34,18 @@ fg: u32,
|
|||||||
head_col: u32,
|
head_col: u32,
|
||||||
min_codepoint: u16,
|
min_codepoint: u16,
|
||||||
max_codepoint: u16,
|
max_codepoint: u16,
|
||||||
|
timeout: *bool,
|
||||||
default_cell: Cell,
|
default_cell: Cell,
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fg: u32, head_col: u32, min_codepoint: u16, max_codepoint: u16) !Matrix {
|
pub fn init(
|
||||||
|
allocator: Allocator,
|
||||||
|
terminal_buffer: *TerminalBuffer,
|
||||||
|
fg: u32,
|
||||||
|
head_col: u32,
|
||||||
|
min_codepoint: u16,
|
||||||
|
max_codepoint: u16,
|
||||||
|
timeout: *bool,
|
||||||
|
) !Matrix {
|
||||||
const dots = try allocator.alloc(Dot, terminal_buffer.width * (terminal_buffer.height + 1));
|
const dots = try allocator.alloc(Dot, terminal_buffer.width * (terminal_buffer.height + 1));
|
||||||
const lines = try allocator.alloc(Line, terminal_buffer.width);
|
const lines = try allocator.alloc(Line, terminal_buffer.width);
|
||||||
|
|
||||||
@@ -53,6 +62,7 @@ pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer, fg: u32, hea
|
|||||||
.head_col = head_col,
|
.head_col = head_col,
|
||||||
.min_codepoint = min_codepoint,
|
.min_codepoint = min_codepoint,
|
||||||
.max_codepoint = max_codepoint - min_codepoint,
|
.max_codepoint = max_codepoint - min_codepoint,
|
||||||
|
.timeout = timeout,
|
||||||
.default_cell = .{ .ch = ' ', .fg = fg, .bg = terminal_buffer.bg },
|
.default_cell = .{ .ch = ' ', .fg = fg, .bg = terminal_buffer.bg },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -84,6 +94,8 @@ fn realloc(self: *Matrix) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn draw(self: *Matrix) void {
|
fn draw(self: *Matrix) void {
|
||||||
|
if (self.timeout.*) return;
|
||||||
|
|
||||||
const buf_height = self.terminal_buffer.height;
|
const buf_height = self.terminal_buffer.height;
|
||||||
const buf_width = self.terminal_buffer.width;
|
const buf_width = self.terminal_buffer.width;
|
||||||
self.count += 1;
|
self.count += 1;
|
||||||
|
|||||||
135
src/main.zig
135
src/main.zig
@@ -15,6 +15,7 @@ const LogFile = ly_core.LogFile;
|
|||||||
const SharedError = ly_core.SharedError;
|
const SharedError = ly_core.SharedError;
|
||||||
const IniParser = ly_core.IniParser;
|
const IniParser = ly_core.IniParser;
|
||||||
|
|
||||||
|
const Cascade = @import("animations/Cascade.zig");
|
||||||
const ColorMix = @import("animations/ColorMix.zig");
|
const ColorMix = @import("animations/ColorMix.zig");
|
||||||
const Doom = @import("animations/Doom.zig");
|
const Doom = @import("animations/Doom.zig");
|
||||||
const DurFile = @import("animations/DurFile.zig");
|
const DurFile = @import("animations/DurFile.zig");
|
||||||
@@ -74,7 +75,6 @@ const UiState = struct {
|
|||||||
buffer: TerminalBuffer,
|
buffer: TerminalBuffer,
|
||||||
labels_max_length: usize,
|
labels_max_length: usize,
|
||||||
animation_timed_out: bool,
|
animation_timed_out: bool,
|
||||||
animation: ?Widget,
|
|
||||||
shutdown_label: Label,
|
shutdown_label: Label,
|
||||||
restart_label: Label,
|
restart_label: Label,
|
||||||
sleep_label: Label,
|
sleep_label: Label,
|
||||||
@@ -517,6 +517,7 @@ pub fn main() !void {
|
|||||||
state.buffer.border_fg,
|
state.buffer.border_fg,
|
||||||
state.buffer.fg,
|
state.buffer.fg,
|
||||||
state.buffer.bg,
|
state.buffer.bg,
|
||||||
|
&updateBox,
|
||||||
);
|
);
|
||||||
|
|
||||||
state.info_line = InfoLine.init(
|
state.info_line = InfoLine.init(
|
||||||
@@ -867,10 +868,9 @@ pub fn main() !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the animation, if any
|
// Initialize the animation, if any
|
||||||
|
var animation: ?Widget = null;
|
||||||
switch (state.config.animation) {
|
switch (state.config.animation) {
|
||||||
.none => {
|
.none => {},
|
||||||
state.animation = null;
|
|
||||||
},
|
|
||||||
.doom => {
|
.doom => {
|
||||||
var doom = try Doom.init(
|
var doom = try Doom.init(
|
||||||
state.allocator,
|
state.allocator,
|
||||||
@@ -880,8 +880,9 @@ pub fn main() !void {
|
|||||||
state.config.doom_bottom_color,
|
state.config.doom_bottom_color,
|
||||||
state.config.doom_fire_height,
|
state.config.doom_fire_height,
|
||||||
state.config.doom_fire_spread,
|
state.config.doom_fire_spread,
|
||||||
|
&state.animation_timed_out,
|
||||||
);
|
);
|
||||||
state.animation = doom.widget();
|
animation = doom.widget();
|
||||||
},
|
},
|
||||||
.matrix => {
|
.matrix => {
|
||||||
var matrix = try Matrix.init(
|
var matrix = try Matrix.init(
|
||||||
@@ -891,8 +892,9 @@ pub fn main() !void {
|
|||||||
state.config.cmatrix_head_col,
|
state.config.cmatrix_head_col,
|
||||||
state.config.cmatrix_min_codepoint,
|
state.config.cmatrix_min_codepoint,
|
||||||
state.config.cmatrix_max_codepoint,
|
state.config.cmatrix_max_codepoint,
|
||||||
|
&state.animation_timed_out,
|
||||||
);
|
);
|
||||||
state.animation = matrix.widget();
|
animation = matrix.widget();
|
||||||
},
|
},
|
||||||
.colormix => {
|
.colormix => {
|
||||||
var color_mix = ColorMix.init(
|
var color_mix = ColorMix.init(
|
||||||
@@ -900,8 +902,9 @@ pub fn main() !void {
|
|||||||
state.config.colormix_col1,
|
state.config.colormix_col1,
|
||||||
state.config.colormix_col2,
|
state.config.colormix_col2,
|
||||||
state.config.colormix_col3,
|
state.config.colormix_col3,
|
||||||
|
&state.animation_timed_out,
|
||||||
);
|
);
|
||||||
state.animation = color_mix.widget();
|
animation = color_mix.widget();
|
||||||
},
|
},
|
||||||
.gameoflife => {
|
.gameoflife => {
|
||||||
var game_of_life = try GameOfLife.init(
|
var game_of_life = try GameOfLife.init(
|
||||||
@@ -911,8 +914,9 @@ pub fn main() !void {
|
|||||||
state.config.gameoflife_entropy_interval,
|
state.config.gameoflife_entropy_interval,
|
||||||
state.config.gameoflife_frame_delay,
|
state.config.gameoflife_frame_delay,
|
||||||
state.config.gameoflife_initial_density,
|
state.config.gameoflife_initial_density,
|
||||||
|
&state.animation_timed_out,
|
||||||
);
|
);
|
||||||
state.animation = game_of_life.widget();
|
animation = game_of_life.widget();
|
||||||
},
|
},
|
||||||
.dur_file => {
|
.dur_file => {
|
||||||
var dur = try DurFile.init(
|
var dur = try DurFile.init(
|
||||||
@@ -924,11 +928,18 @@ pub fn main() !void {
|
|||||||
state.config.dur_x_offset,
|
state.config.dur_x_offset,
|
||||||
state.config.dur_y_offset,
|
state.config.dur_y_offset,
|
||||||
state.config.full_color,
|
state.config.full_color,
|
||||||
|
&state.animation_timed_out,
|
||||||
);
|
);
|
||||||
state.animation = dur.widget();
|
animation = dur.widget();
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
defer if (state.animation) |*a| a.deinit();
|
defer if (animation) |*a| a.deinit();
|
||||||
|
|
||||||
|
var cascade = Cascade.init(
|
||||||
|
&state.buffer,
|
||||||
|
&state.auth_fails,
|
||||||
|
state.config.auth_fails,
|
||||||
|
);
|
||||||
|
|
||||||
state.auth_fails = 0;
|
state.auth_fails = 0;
|
||||||
state.run = true;
|
state.run = true;
|
||||||
@@ -966,9 +977,14 @@ pub fn main() !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Layer system where we can put widgets in specific layers (to
|
||||||
|
// allow certain widgets to be below or above others, like animations)
|
||||||
var widgets: std.ArrayList(Widget) = .empty;
|
var widgets: std.ArrayList(Widget) = .empty;
|
||||||
defer widgets.deinit(state.allocator);
|
defer widgets.deinit(state.allocator);
|
||||||
|
|
||||||
|
if (animation) |a| {
|
||||||
|
try widgets.append(state.allocator, a);
|
||||||
|
}
|
||||||
if (!state.config.hide_key_hints) {
|
if (!state.config.hide_key_hints) {
|
||||||
try widgets.append(state.allocator, state.shutdown_label.widget());
|
try widgets.append(state.allocator, state.shutdown_label.widget());
|
||||||
try widgets.append(state.allocator, state.restart_label.widget());
|
try widgets.append(state.allocator, state.restart_label.widget());
|
||||||
@@ -1006,9 +1022,12 @@ pub fn main() !void {
|
|||||||
if (!state.config.hide_version_string) {
|
if (!state.config.hide_version_string) {
|
||||||
try widgets.append(state.allocator, state.version_label.widget());
|
try widgets.append(state.allocator, state.version_label.widget());
|
||||||
}
|
}
|
||||||
|
if (state.config.auth_fails > 0) {
|
||||||
|
try widgets.append(state.allocator, cascade.widget());
|
||||||
|
}
|
||||||
|
|
||||||
// Position components and place cursor accordingly
|
// Position components and place cursor accordingly
|
||||||
try updateComponents(&state, widgets);
|
for (widgets.items) |*widget| try widget.update(&state);
|
||||||
positionComponents(&state);
|
positionComponents(&state);
|
||||||
|
|
||||||
switch (state.active_input) {
|
switch (state.active_input) {
|
||||||
@@ -1085,9 +1104,11 @@ pub fn main() !void {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state.is_autologin) _ = try authenticate(&state);
|
||||||
|
|
||||||
while (state.run) {
|
while (state.run) {
|
||||||
if (state.update) {
|
if (state.update) {
|
||||||
try updateComponents(&state, widgets);
|
for (widgets.items) |*widget| try widget.update(&state);
|
||||||
|
|
||||||
switch (state.active_input) {
|
switch (state.active_input) {
|
||||||
.info_line => state.info_line.label.handle(null, state.insert_mode),
|
.info_line => state.info_line.label.handle(null, state.insert_mode),
|
||||||
@@ -1099,7 +1120,11 @@ pub fn main() !void {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!try drawUi(&state, widgets)) continue;
|
try TerminalBuffer.clearScreen(false);
|
||||||
|
|
||||||
|
for (widgets.items) |*widget| widget.draw();
|
||||||
|
|
||||||
|
TerminalBuffer.presentBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
var timeout: i32 = -1;
|
var timeout: i32 = -1;
|
||||||
@@ -1113,7 +1138,7 @@ pub fn main() !void {
|
|||||||
|
|
||||||
if (state.config.animation_timeout_sec > 0 and time.seconds - animation_time_start.seconds > state.config.animation_timeout_sec) {
|
if (state.config.animation_timeout_sec > 0 and time.seconds - animation_time_start.seconds > state.config.animation_timeout_sec) {
|
||||||
state.animation_timed_out = true;
|
state.animation_timed_out = true;
|
||||||
if (state.animation) |*a| a.deinit();
|
if (animation) |*a| a.deinit();
|
||||||
}
|
}
|
||||||
} else if (state.config.bigclock != .none and state.config.clock == null) {
|
} else if (state.config.bigclock != .none and state.config.clock == null) {
|
||||||
const time = try interop.getTimeOfDay();
|
const time = try interop.getTimeOfDay();
|
||||||
@@ -1155,25 +1180,11 @@ pub fn main() !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip event polling if autologin is set, use simulated Enter key press instead
|
const event_error = if (timeout == -1) termbox.tb_poll_event(&event) else termbox.tb_peek_event(&event, timeout);
|
||||||
if (state.is_autologin) {
|
|
||||||
event = .{
|
|
||||||
.type = termbox.TB_EVENT_KEY,
|
|
||||||
.key = termbox.TB_KEY_ENTER,
|
|
||||||
.ch = 0,
|
|
||||||
.w = 0,
|
|
||||||
.h = 0,
|
|
||||||
.x = 0,
|
|
||||||
.y = 0,
|
|
||||||
.mod = 0,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
const event_error = if (timeout == -1) termbox.tb_poll_event(&event) else termbox.tb_peek_event(&event, timeout);
|
|
||||||
|
|
||||||
state.update = timeout != -1;
|
state.update = timeout != -1;
|
||||||
|
|
||||||
if (event_error < 0) continue;
|
if (event_error < 0) continue;
|
||||||
}
|
|
||||||
|
|
||||||
// Input of some kind was detected, so reset the inactivity timer
|
// Input of some kind was detected, so reset the inactivity timer
|
||||||
inactivity_time_start = try interop.getTimeOfDay();
|
inactivity_time_start = try interop.getTimeOfDay();
|
||||||
@@ -1184,7 +1195,7 @@ pub fn main() !void {
|
|||||||
|
|
||||||
try state.log_file.info("tui", "screen resolution updated to {d}x{d}", .{ state.buffer.width, state.buffer.height });
|
try state.log_file.info("tui", "screen resolution updated to {d}x{d}", .{ state.buffer.width, state.buffer.height });
|
||||||
|
|
||||||
if (state.animation) |*a| a.realloc() catch |err| {
|
if (animation) |*a| a.realloc() catch |err| {
|
||||||
try state.info_line.addMessage(
|
try state.info_line.addMessage(
|
||||||
state.lang.err_alloc,
|
state.lang.err_alloc,
|
||||||
state.config.error_bg,
|
state.config.error_bg,
|
||||||
@@ -1197,6 +1208,21 @@ pub fn main() !void {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
for (widgets.items) |*widget| {
|
||||||
|
widget.realloc() catch |err| {
|
||||||
|
try state.info_line.addMessage(
|
||||||
|
state.lang.err_alloc,
|
||||||
|
state.config.error_bg,
|
||||||
|
state.config.error_fg,
|
||||||
|
);
|
||||||
|
try state.log_file.err(
|
||||||
|
"tui",
|
||||||
|
"failed to reallocate widget: {s}",
|
||||||
|
.{@errorName(err)},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
positionComponents(&state);
|
positionComponents(&state);
|
||||||
|
|
||||||
state.update = true;
|
state.update = true;
|
||||||
@@ -1617,43 +1643,6 @@ fn increaseBrightnessCmd(ptr: *anyopaque) !bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn updateComponents(state: *UiState, widgets: std.ArrayList(Widget)) !void {
|
|
||||||
for (widgets.items) |*widget| {
|
|
||||||
try widget.update(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn drawUi(state: *UiState, widgets: std.ArrayList(Widget)) !bool {
|
|
||||||
// If the user entered a wrong password 10 times in a row, play a cascade animation, else update normally
|
|
||||||
if (state.config.auth_fails > 0 and state.auth_fails >= state.config.auth_fails) {
|
|
||||||
std.Thread.sleep(std.time.ns_per_ms * 10);
|
|
||||||
state.update = state.buffer.cascade();
|
|
||||||
|
|
||||||
if (!state.update) {
|
|
||||||
std.Thread.sleep(std.time.ns_per_s * 7);
|
|
||||||
state.auth_fails = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
TerminalBuffer.presentBuffer();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try TerminalBuffer.clearScreen(false);
|
|
||||||
|
|
||||||
if (!state.animation_timed_out) if (state.animation) |*a| a.draw();
|
|
||||||
|
|
||||||
for (widgets.items) |*widget| {
|
|
||||||
widget.draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.config.vi_mode) {
|
|
||||||
state.box.bottom_title = if (state.insert_mode) state.lang.insert else state.lang.normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
TerminalBuffer.presentBuffer();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn updateNumlock(self: *Label, ptr: *anyopaque) !void {
|
fn updateNumlock(self: *Label, ptr: *anyopaque) !void {
|
||||||
var state: *UiState = @ptrCast(@alignCast(ptr));
|
var state: *UiState = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
@@ -1765,6 +1754,14 @@ fn updateBigClock(self: *BigLabel, ptr: *anyopaque) !void {
|
|||||||
self.setText(clock_str);
|
self.setText(clock_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn updateBox(self: *CenteredBox, ptr: *anyopaque) !void {
|
||||||
|
const state: *UiState = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
|
if (state.config.vi_mode) {
|
||||||
|
self.bottom_title = if (state.insert_mode) state.lang.insert else state.lang.normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn updateSessionSpecifier(self: *Label, ptr: *anyopaque) !void {
|
fn updateSessionSpecifier(self: *Label, ptr: *anyopaque) !void {
|
||||||
const state: *UiState = @ptrCast(@alignCast(ptr));
|
const state: *UiState = @ptrCast(@alignCast(ptr));
|
||||||
|
|
||||||
|
|||||||
@@ -17,5 +17,5 @@ pub fn init(ch: u32, fg: u32, bg: u32) Cell {
|
|||||||
pub fn put(self: Cell, x: usize, y: usize) void {
|
pub fn put(self: Cell, x: usize, y: usize) void {
|
||||||
if (self.ch == 0) return;
|
if (self.ch == 0) return;
|
||||||
|
|
||||||
TerminalBuffer.setCell(x, y, self.ch, self.fg, self.bg);
|
TerminalBuffer.setCell(x, y, self);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -172,19 +172,29 @@ pub fn presentBuffer() void {
|
|||||||
_ = termbox.tb_present();
|
_ = termbox.tb_present();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setCell(
|
pub fn getCell(x: usize, y: usize) ?Cell {
|
||||||
x: usize,
|
var maybe_cell: ?*termbox.tb_cell = undefined;
|
||||||
y: usize,
|
_ = termbox.tb_get_cell(
|
||||||
ch: u32,
|
@intCast(x),
|
||||||
fg: u32,
|
@intCast(y),
|
||||||
bg: u32,
|
1,
|
||||||
) void {
|
&maybe_cell,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (maybe_cell) |cell| {
|
||||||
|
return Cell.init(cell.ch, cell.fg, cell.bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setCell(x: usize, y: usize, cell: Cell) void {
|
||||||
_ = termbox.tb_set_cell(
|
_ = termbox.tb_set_cell(
|
||||||
@intCast(x),
|
@intCast(x),
|
||||||
@intCast(y),
|
@intCast(y),
|
||||||
ch,
|
cell.ch,
|
||||||
fg,
|
cell.fg,
|
||||||
bg,
|
cell.bg,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,40 +211,6 @@ pub fn reclaim(self: TerminalBuffer) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cascade(self: TerminalBuffer) bool {
|
|
||||||
var changed = false;
|
|
||||||
var y = self.height - 2;
|
|
||||||
|
|
||||||
while (y > 0) : (y -= 1) {
|
|
||||||
for (0..self.width) |x| {
|
|
||||||
var cell: ?*termbox.tb_cell = undefined;
|
|
||||||
var cell_under: ?*termbox.tb_cell = undefined;
|
|
||||||
|
|
||||||
_ = termbox.tb_get_cell(@intCast(x), @intCast(y - 1), 1, &cell);
|
|
||||||
_ = termbox.tb_get_cell(@intCast(x), @intCast(y), 1, &cell_under);
|
|
||||||
|
|
||||||
// This shouldn't happen under normal circumstances, but because
|
|
||||||
// this is a *secret* animation, there's no need to care that much
|
|
||||||
if (cell == null or cell_under == null) continue;
|
|
||||||
|
|
||||||
const char: u8 = @truncate(cell.?.ch);
|
|
||||||
if (std.ascii.isWhitespace(char)) continue;
|
|
||||||
|
|
||||||
const char_under: u8 = @truncate(cell_under.?.ch);
|
|
||||||
if (!std.ascii.isWhitespace(char_under)) continue;
|
|
||||||
|
|
||||||
changed = true;
|
|
||||||
|
|
||||||
if ((self.random.int(u16) % 10) > 7) continue;
|
|
||||||
|
|
||||||
_ = termbox.tb_set_cell(@intCast(x), @intCast(y), cell.?.ch, cell.?.fg, cell.?.bg);
|
|
||||||
_ = termbox.tb_set_cell(@intCast(x), @intCast(y - 1), ' ', cell_under.?.fg, cell_under.?.bg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn registerKeybind(self: *TerminalBuffer, keybind: []const u8, callback: KeybindCallbackFn) !void {
|
pub fn registerKeybind(self: *TerminalBuffer, keybind: []const u8, callback: KeybindCallbackFn) !void {
|
||||||
var key = std.mem.zeroes(keyboard.Key);
|
var key = std.mem.zeroes(keyboard.Key);
|
||||||
var iterator = std.mem.splitScalar(u8, keybind, '+');
|
var iterator = std.mem.splitScalar(u8, keybind, '+');
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ pub fn childrenPosition(self: BigLabel) Position {
|
|||||||
return self.children_pos;
|
return self.children_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(self: *BigLabel) void {
|
fn draw(self: *BigLabel) void {
|
||||||
for (self.text, 0..) |c, i| {
|
for (self.text, 0..) |c, i| {
|
||||||
const clock_cell = clockCell(
|
const clock_cell = clockCell(
|
||||||
c,
|
c,
|
||||||
@@ -159,7 +159,7 @@ pub fn draw(self: *BigLabel) void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(self: *BigLabel, context: *anyopaque) !void {
|
fn update(self: *BigLabel, context: *anyopaque) !void {
|
||||||
if (self.update_fn) |update_fn| {
|
if (self.update_fn) |update_fn| {
|
||||||
return @call(
|
return @call(
|
||||||
.auto,
|
.auto,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ bottom_title: ?[]const u8,
|
|||||||
border_fg: u32,
|
border_fg: u32,
|
||||||
title_fg: u32,
|
title_fg: u32,
|
||||||
bg: u32,
|
bg: u32,
|
||||||
|
update_fn: ?*const fn (*CenteredBox, *anyopaque) anyerror!void,
|
||||||
left_pos: Position,
|
left_pos: Position,
|
||||||
right_pos: Position,
|
right_pos: Position,
|
||||||
children_pos: Position,
|
children_pos: Position,
|
||||||
@@ -36,6 +37,7 @@ pub fn init(
|
|||||||
border_fg: u32,
|
border_fg: u32,
|
||||||
title_fg: u32,
|
title_fg: u32,
|
||||||
bg: u32,
|
bg: u32,
|
||||||
|
update_fn: ?*const fn (*CenteredBox, *anyopaque) anyerror!void,
|
||||||
) CenteredBox {
|
) CenteredBox {
|
||||||
return .{
|
return .{
|
||||||
.buffer = buffer,
|
.buffer = buffer,
|
||||||
@@ -50,6 +52,7 @@ pub fn init(
|
|||||||
.border_fg = border_fg,
|
.border_fg = border_fg,
|
||||||
.title_fg = title_fg,
|
.title_fg = title_fg,
|
||||||
.bg = bg,
|
.bg = bg,
|
||||||
|
.update_fn = update_fn,
|
||||||
.left_pos = TerminalBuffer.START_POSITION,
|
.left_pos = TerminalBuffer.START_POSITION,
|
||||||
.right_pos = TerminalBuffer.START_POSITION,
|
.right_pos = TerminalBuffer.START_POSITION,
|
||||||
.children_pos = TerminalBuffer.START_POSITION,
|
.children_pos = TerminalBuffer.START_POSITION,
|
||||||
@@ -90,7 +93,7 @@ pub fn childrenPosition(self: CenteredBox) Position {
|
|||||||
return self.children_pos;
|
return self.children_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(self: *CenteredBox) void {
|
fn draw(self: *CenteredBox) void {
|
||||||
if (self.show_borders) {
|
if (self.show_borders) {
|
||||||
var left_up = Cell.init(
|
var left_up = Cell.init(
|
||||||
self.buffer.box_chars.left_up,
|
self.buffer.box_chars.left_up,
|
||||||
@@ -172,3 +175,13 @@ pub fn draw(self: *CenteredBox) void {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update(self: *CenteredBox, ctx: *anyopaque) !void {
|
||||||
|
if (self.update_fn) |update_fn| {
|
||||||
|
return @call(
|
||||||
|
.auto,
|
||||||
|
update_fn,
|
||||||
|
.{ self, ctx },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -56,14 +56,6 @@ pub fn widget(self: *InfoLine) Widget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
pub fn addMessage(self: *InfoLine, text: []const u8, bg: u32, fg: u32) !void {
|
||||||
if (text.len == 0) return;
|
if (text.len == 0) return;
|
||||||
|
|
||||||
@@ -91,6 +83,14 @@ pub fn clearRendered(self: InfoLine, allocator: Allocator) !void {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn draw(self: *InfoLine) void {
|
||||||
|
self.label.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle(self: *InfoLine, maybe_key: ?keyboard.Key, insert_mode: bool) !void {
|
||||||
|
self.label.handle(maybe_key, insert_mode);
|
||||||
|
}
|
||||||
|
|
||||||
fn drawItem(label: *MessageLabel, message: Message, x: usize, y: usize, width: usize) void {
|
fn drawItem(label: *MessageLabel, message: Message, x: usize, y: usize, width: usize) void {
|
||||||
if (message.width == 0) return;
|
if (message.width == 0) return;
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ pub fn childrenPosition(self: Label) Position {
|
|||||||
return self.children_pos;
|
return self.children_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(self: *Label) void {
|
fn draw(self: *Label) void {
|
||||||
if (self.max_width) |width| {
|
if (self.max_width) |width| {
|
||||||
TerminalBuffer.drawConfinedText(
|
TerminalBuffer.drawConfinedText(
|
||||||
self.text,
|
self.text,
|
||||||
@@ -120,7 +120,7 @@ pub fn draw(self: *Label) void {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(self: *Label, ctx: *anyopaque) !void {
|
fn update(self: *Label, ctx: *anyopaque) !void {
|
||||||
if (self.update_fn) |update_fn| {
|
if (self.update_fn) |update_fn| {
|
||||||
return @call(
|
return @call(
|
||||||
.auto,
|
.auto,
|
||||||
|
|||||||
@@ -66,14 +66,6 @@ pub fn widget(self: *Session) Widget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
pub fn addEnvironment(self: *Session, environment: Environment) !void {
|
||||||
const env = Env{ .environment = environment, .index = self.label.list.items.len };
|
const env = Env{ .environment = environment, .index = self.label.list.items.len };
|
||||||
|
|
||||||
@@ -81,6 +73,14 @@ pub fn addEnvironment(self: *Session, environment: Environment) !void {
|
|||||||
addedSession(env, self.user_list);
|
addedSession(env, self.user_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn draw(self: *Session) void {
|
||||||
|
self.label.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle(self: *Session, maybe_key: ?keyboard.Key, insert_mode: bool) !void {
|
||||||
|
self.label.handle(maybe_key, insert_mode);
|
||||||
|
}
|
||||||
|
|
||||||
fn addedSession(env: Env, user_list: *UserList) void {
|
fn addedSession(env: Env, user_list: *UserList) void {
|
||||||
const user = user_list.label.list.items[user_list.label.current];
|
const user = user_list.label.list.items[user_list.label.current];
|
||||||
if (!user.first_run) return;
|
if (!user.first_run) return;
|
||||||
|
|||||||
@@ -87,6 +87,13 @@ pub fn childrenPosition(self: Text) Position {
|
|||||||
return self.children_pos;
|
return self.children_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clear(self: *Text) void {
|
||||||
|
self.text.clearRetainingCapacity();
|
||||||
|
self.end = 0;
|
||||||
|
self.cursor = 0;
|
||||||
|
self.visible_start = 0;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle(self: *Text, maybe_key: ?keyboard.Key, insert_mode: bool) !void {
|
pub fn handle(self: *Text, maybe_key: ?keyboard.Key, insert_mode: bool) !void {
|
||||||
if (maybe_key) |key| {
|
if (maybe_key) |key| {
|
||||||
if (key.left or (!insert_mode and (key.h or key.backspace))) {
|
if (key.left or (!insert_mode and (key.h or key.backspace))) {
|
||||||
@@ -117,7 +124,7 @@ pub fn handle(self: *Text, maybe_key: ?keyboard.Key, insert_mode: bool) !void {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(self: *Text) void {
|
fn draw(self: *Text) void {
|
||||||
if (self.masked) {
|
if (self.masked) {
|
||||||
if (self.maybe_mask) |mask| {
|
if (self.maybe_mask) |mask| {
|
||||||
if (self.width < 1) return;
|
if (self.width < 1) return;
|
||||||
@@ -158,13 +165,6 @@ pub fn draw(self: *Text) void {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(self: *Text) void {
|
|
||||||
self.text.clearRetainingCapacity();
|
|
||||||
self.end = 0;
|
|
||||||
self.cursor = 0;
|
|
||||||
self.visible_start = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn goLeft(self: *Text) void {
|
fn goLeft(self: *Text) void {
|
||||||
if (self.cursor == 0) return;
|
if (self.cursor == 0) return;
|
||||||
if (self.visible_start > 0) self.visible_start -= 1;
|
if (self.visible_start > 0) self.visible_start -= 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user