mirror of
https://github.com/fairyglade/ly.git
synced 2026-03-21 22:43:38 +00:00
Completely refactor widget placement code
Signed-off-by: AnErrupTion <anerruption@disroot.org>
This commit is contained in:
133
src/main.zig
133
src/main.zig
@@ -30,6 +30,7 @@ const DisplayServer = enums.DisplayServer;
|
||||
const Environment = @import("Environment.zig");
|
||||
const Entry = Environment.Entry;
|
||||
const Animation = @import("tui/Animation.zig");
|
||||
const CenteredBox = @import("tui/components/CenteredBox.zig");
|
||||
const InfoLine = @import("tui/components/InfoLine.zig");
|
||||
const Session = @import("tui/components/Session.zig");
|
||||
const Text = @import("tui/components/Text.zig");
|
||||
@@ -63,9 +64,11 @@ const UiState = struct {
|
||||
auth_fails: u64,
|
||||
update: bool,
|
||||
buffer: *TerminalBuffer,
|
||||
labels_max_length: usize,
|
||||
animation_timed_out: bool,
|
||||
animation: *?Animation,
|
||||
can_draw_battery: bool,
|
||||
box: *CenteredBox,
|
||||
info_line: *InfoLine,
|
||||
animate: bool,
|
||||
resolution_changed: bool,
|
||||
@@ -300,11 +303,7 @@ pub fn main() !void {
|
||||
.fg = config.fg,
|
||||
.bg = config.bg,
|
||||
.border_fg = config.border_fg,
|
||||
.margin_box_h = config.margin_box_h,
|
||||
.margin_box_v = config.margin_box_v,
|
||||
.input_len = config.input_len,
|
||||
.full_color = config.full_color,
|
||||
.labels_max_length = labels_max_length,
|
||||
.is_tty = true,
|
||||
};
|
||||
var buffer = try TerminalBuffer.init(buffer_options, &log_file, random);
|
||||
@@ -321,7 +320,23 @@ pub fn main() !void {
|
||||
std.posix.sigaction(std.posix.SIG.TERM, &act, null);
|
||||
|
||||
// Initialize components
|
||||
var info_line = InfoLine.init(allocator, &buffer);
|
||||
var box = CenteredBox.init(
|
||||
&buffer,
|
||||
config.margin_box_h,
|
||||
config.margin_box_v,
|
||||
(2 * config.margin_box_h) + config.input_len + 1 + labels_max_length,
|
||||
7 + (2 * config.margin_box_v),
|
||||
!config.hide_borders,
|
||||
config.blank_box,
|
||||
config.box_title,
|
||||
null,
|
||||
);
|
||||
|
||||
var info_line = InfoLine.init(
|
||||
allocator,
|
||||
&buffer,
|
||||
box.width - 2 * box.horizontal_margin,
|
||||
);
|
||||
defer info_line.deinit();
|
||||
|
||||
if (maybe_res == null) {
|
||||
@@ -365,10 +380,24 @@ pub fn main() !void {
|
||||
|
||||
var login: UserList = undefined;
|
||||
|
||||
var session = Session.init(allocator, &buffer, &login);
|
||||
var session = Session.init(
|
||||
allocator,
|
||||
&buffer,
|
||||
&login,
|
||||
box.width - 2 * box.horizontal_margin - labels_max_length - 1,
|
||||
config.text_in_center,
|
||||
);
|
||||
defer session.deinit();
|
||||
|
||||
login = try UserList.init(allocator, &buffer, usernames, &saved_users, &session);
|
||||
login = try UserList.init(
|
||||
allocator,
|
||||
&buffer,
|
||||
usernames,
|
||||
&saved_users,
|
||||
&session,
|
||||
box.width - 2 * box.horizontal_margin - labels_max_length - 1,
|
||||
config.text_in_center,
|
||||
);
|
||||
defer login.deinit();
|
||||
|
||||
addOtherEnvironment(&session, lang, .shell, null) catch |err| {
|
||||
@@ -430,7 +459,13 @@ pub fn main() !void {
|
||||
try log_file.err("sys", "no users found", .{});
|
||||
}
|
||||
|
||||
var password = Text.init(allocator, &buffer, true, config.asterisk);
|
||||
var password = Text.init(
|
||||
allocator,
|
||||
&buffer,
|
||||
true,
|
||||
config.asterisk,
|
||||
box.width - 2 * box.horizontal_margin - labels_max_length - 1,
|
||||
);
|
||||
defer password.deinit();
|
||||
|
||||
var is_autologin = false;
|
||||
@@ -467,9 +502,11 @@ pub fn main() !void {
|
||||
.auth_fails = 0,
|
||||
.update = true,
|
||||
.buffer = &buffer,
|
||||
.labels_max_length = labels_max_length,
|
||||
.animation_timed_out = false,
|
||||
.animation = &animation,
|
||||
.can_draw_battery = true,
|
||||
.box = &box,
|
||||
.info_line = &info_line,
|
||||
.animate = config.animation != .none,
|
||||
.resolution_changed = false,
|
||||
@@ -512,25 +549,21 @@ pub fn main() !void {
|
||||
}
|
||||
}
|
||||
|
||||
// Place components on the screen
|
||||
{
|
||||
buffer.drawBoxCenter(!config.hide_borders, config.blank_box);
|
||||
// Position components
|
||||
state.box.position(TerminalBuffer.START_POSITION);
|
||||
state.info_line.label.positionY(state.box.childrenPosition());
|
||||
state.session.label.positionY(state.info_line.label.childrenPosition().addY(1).addX(state.labels_max_length + 1));
|
||||
state.login.label.positionY(state.session.label.childrenPosition().addY(1));
|
||||
state.password.positionY(state.login.label.childrenPosition().addY(1));
|
||||
|
||||
const coordinates = buffer.calculateComponentCoordinates();
|
||||
info_line.label.position(coordinates.start_x, coordinates.y, coordinates.full_visible_length, null);
|
||||
session.label.position(coordinates.x, coordinates.y + 2, coordinates.visible_length, config.text_in_center);
|
||||
login.label.position(coordinates.x, coordinates.y + 4, coordinates.visible_length, config.text_in_center);
|
||||
password.position(coordinates.x, coordinates.y + 6, coordinates.visible_length);
|
||||
|
||||
switch (state.active_input) {
|
||||
.info_line => info_line.label.handle(null, state.insert_mode),
|
||||
.session => session.label.handle(null, state.insert_mode),
|
||||
.login => login.label.handle(null, state.insert_mode),
|
||||
.password => password.handle(null, state.insert_mode) catch |err| {
|
||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||
try log_file.err("tui", "failed to handle password input: {s}", .{@errorName(err)});
|
||||
},
|
||||
}
|
||||
switch (state.active_input) {
|
||||
.info_line => info_line.label.handle(null, state.insert_mode),
|
||||
.session => session.label.handle(null, state.insert_mode),
|
||||
.login => login.label.handle(null, state.insert_mode),
|
||||
.password => password.handle(null, state.insert_mode) catch |err| {
|
||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||
try log_file.err("tui", "failed to handle password input: {s}", .{@errorName(err)});
|
||||
},
|
||||
}
|
||||
|
||||
// Initialize the animation, if any
|
||||
@@ -782,7 +815,7 @@ pub fn main() !void {
|
||||
if (!config.allow_empty_password and password.text.items.len == 0) {
|
||||
// Let's not log this message for security reasons
|
||||
try info_line.addMessage(lang.err_empty_password, config.error_bg, config.error_fg);
|
||||
InfoLine.clearRendered(allocator, buffer) catch |err| {
|
||||
info_line.clearRendered(allocator) catch |err| {
|
||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||
try log_file.err("tui", "failed to clear info line: {s}", .{@errorName(err)});
|
||||
};
|
||||
@@ -792,7 +825,7 @@ pub fn main() !void {
|
||||
}
|
||||
|
||||
try info_line.addMessage(lang.authenticating, config.bg, config.fg);
|
||||
InfoLine.clearRendered(allocator, buffer) catch |err| {
|
||||
info_line.clearRendered(allocator) catch |err| {
|
||||
try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg);
|
||||
try log_file.err("tui", "failed to clear info line: {s}", .{@errorName(err)});
|
||||
};
|
||||
@@ -1002,7 +1035,7 @@ fn drawUi(config: Config, lang: Lang, log_file: *LogFile, state: *UiState) !bool
|
||||
state.can_draw_battery = true;
|
||||
}
|
||||
|
||||
if (config.bigclock != .none and state.buffer.box_height + (bigclock.HEIGHT + 2) * 2 < state.buffer.height) {
|
||||
if (config.bigclock != .none and state.box.height + (bigclock.HEIGHT + 2) * 2 < state.buffer.height) {
|
||||
var format_buf: [16:0]u8 = undefined;
|
||||
var clock_buf: [32:0]u8 = undefined;
|
||||
// We need the slice/c-string returned by `bufPrintZ`.
|
||||
@@ -1013,7 +1046,7 @@ fn drawUi(config: Config, lang: Lang, log_file: *LogFile, state: *UiState) !bool
|
||||
if (config.bigclock_12hr) "%P" else "",
|
||||
});
|
||||
const xo = state.buffer.width / 2 - @min(state.buffer.width, (format.len * (bigclock.WIDTH + 1))) / 2;
|
||||
const yo = (state.buffer.height - state.buffer.box_height) / 2 - bigclock.HEIGHT - 2;
|
||||
const yo = (state.buffer.height - state.box.height) / 2 - bigclock.HEIGHT - 2;
|
||||
|
||||
const clock_str = interop.timeAsString(&clock_buf, format);
|
||||
|
||||
@@ -1024,14 +1057,14 @@ fn drawUi(config: Config, lang: Lang, log_file: *LogFile, state: *UiState) !bool
|
||||
}
|
||||
}
|
||||
|
||||
state.buffer.drawBoxCenter(!config.hide_borders, config.blank_box);
|
||||
state.box.draw();
|
||||
|
||||
if (state.resolution_changed) {
|
||||
const coordinates = state.buffer.calculateComponentCoordinates();
|
||||
state.info_line.label.position(coordinates.start_x, coordinates.y, coordinates.full_visible_length, null);
|
||||
state.session.label.position(coordinates.x, coordinates.y + 2, coordinates.visible_length, config.text_in_center);
|
||||
state.login.label.position(coordinates.x, coordinates.y + 4, coordinates.visible_length, config.text_in_center);
|
||||
state.password.position(coordinates.x, coordinates.y + 6, coordinates.visible_length);
|
||||
state.box.position(TerminalBuffer.START_POSITION);
|
||||
state.info_line.label.positionY(state.box.childrenPosition());
|
||||
state.session.label.positionY(state.info_line.label.childrenPosition().addY(1).addX(state.labels_max_length + 1));
|
||||
state.login.label.positionY(state.session.label.childrenPosition().addY(1));
|
||||
state.password.positionY(state.login.label.childrenPosition().addY(1));
|
||||
|
||||
state.resolution_changed = false;
|
||||
}
|
||||
@@ -1062,11 +1095,22 @@ fn drawUi(config: Config, lang: Lang, log_file: *LogFile, state: *UiState) !bool
|
||||
state.buffer.drawLabel(clock_str, state.buffer.width - @min(state.buffer.width, clock_str.len) - config.edge_margin, config.edge_margin);
|
||||
}
|
||||
|
||||
const label_x = state.buffer.box_x + state.buffer.margin_box_h;
|
||||
const label_y = state.buffer.box_y + state.buffer.margin_box_v;
|
||||
|
||||
state.buffer.drawLabel(lang.login, label_x, label_y + 4);
|
||||
state.buffer.drawLabel(lang.password, label_x, label_y + 6);
|
||||
const env = state.session.label.list.items[state.session.label.current];
|
||||
state.buffer.drawLabel(
|
||||
env.environment.specifier,
|
||||
state.box.childrenPosition().x,
|
||||
state.session.label.component_pos.y,
|
||||
);
|
||||
state.buffer.drawLabel(
|
||||
lang.login,
|
||||
state.box.childrenPosition().x,
|
||||
state.login.label.component_pos.y,
|
||||
);
|
||||
state.buffer.drawLabel(
|
||||
lang.password,
|
||||
state.box.childrenPosition().x,
|
||||
state.password.component_pos.y,
|
||||
);
|
||||
|
||||
state.info_line.label.draw();
|
||||
|
||||
@@ -1122,13 +1166,8 @@ fn drawUi(config: Config, lang: Lang, log_file: *LogFile, state: *UiState) !bool
|
||||
}
|
||||
}
|
||||
|
||||
if (config.box_title) |title| {
|
||||
state.buffer.drawConfinedLabel(title, state.buffer.box_x, state.buffer.box_y - 1, state.buffer.box_width);
|
||||
}
|
||||
|
||||
if (config.vi_mode) {
|
||||
const label_txt = if (state.insert_mode) lang.insert else lang.normal;
|
||||
state.buffer.drawLabel(label_txt, state.buffer.box_x, state.buffer.box_y + state.buffer.box_height);
|
||||
state.box.bottom_title = if (state.insert_mode) lang.insert else lang.normal;
|
||||
}
|
||||
|
||||
if (!config.hide_keyboard_locks and state.can_get_lock_state) draw_lock_state: {
|
||||
|
||||
32
src/tui/Position.zig
Normal file
32
src/tui/Position.zig
Normal file
@@ -0,0 +1,32 @@
|
||||
const Position = @This();
|
||||
|
||||
x: usize,
|
||||
y: usize,
|
||||
|
||||
pub fn init(x: usize, y: usize) Position {
|
||||
return .{
|
||||
.x = x,
|
||||
.y = y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn add(self: Position, other: Position) Position {
|
||||
return .{
|
||||
.x = self.x + other.x,
|
||||
.y = self.y + other.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn addX(self: Position, x: usize) Position {
|
||||
return .{
|
||||
.x = self.x + x,
|
||||
.y = self.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn addY(self: Position, y: usize) Position {
|
||||
return .{
|
||||
.x = self.x,
|
||||
.y = self.y + y,
|
||||
};
|
||||
}
|
||||
@@ -7,6 +7,7 @@ const LogFile = ly_core.LogFile;
|
||||
pub const termbox = @import("termbox2");
|
||||
|
||||
const Cell = @import("Cell.zig");
|
||||
const Position = @import("Position.zig");
|
||||
|
||||
const TerminalBuffer = @This();
|
||||
|
||||
@@ -14,11 +15,7 @@ pub const InitOptions = struct {
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
border_fg: u32,
|
||||
margin_box_h: u8,
|
||||
margin_box_v: u8,
|
||||
input_len: u8,
|
||||
full_color: bool,
|
||||
labels_max_length: usize,
|
||||
is_tty: bool,
|
||||
};
|
||||
|
||||
@@ -60,6 +57,8 @@ pub const Color = struct {
|
||||
pub const ECOL_WHITE = 8;
|
||||
};
|
||||
|
||||
pub const START_POSITION = Position.init(0, 0);
|
||||
|
||||
random: Random,
|
||||
width: usize,
|
||||
height: usize,
|
||||
@@ -76,13 +75,6 @@ box_chars: struct {
|
||||
left: u32,
|
||||
right: u32,
|
||||
},
|
||||
labels_max_length: usize,
|
||||
box_x: usize,
|
||||
box_y: usize,
|
||||
box_width: usize,
|
||||
box_height: usize,
|
||||
margin_box_v: u8,
|
||||
margin_box_h: u8,
|
||||
blank_cell: Cell,
|
||||
full_color: bool,
|
||||
termios: ?std.posix.termios,
|
||||
@@ -134,13 +126,6 @@ pub fn init(options: InitOptions, log_file: *LogFile, random: Random) !TerminalB
|
||||
.left = '|',
|
||||
.right = '|',
|
||||
},
|
||||
.labels_max_length = options.labels_max_length,
|
||||
.box_x = 0,
|
||||
.box_y = 0,
|
||||
.box_width = (2 * options.margin_box_h) + options.input_len + 1 + options.labels_max_length,
|
||||
.box_height = 7 + (2 * options.margin_box_v),
|
||||
.margin_box_v = options.margin_box_v,
|
||||
.margin_box_h = options.margin_box_h,
|
||||
.blank_cell = Cell.init(' ', options.fg, options.bg),
|
||||
.full_color = options.full_color,
|
||||
// Needed to reclaim the TTY after giving up its control
|
||||
@@ -216,70 +201,6 @@ pub fn cascade(self: TerminalBuffer) bool {
|
||||
return changed;
|
||||
}
|
||||
|
||||
pub fn drawBoxCenter(self: *TerminalBuffer, show_borders: bool, blank_box: bool) void {
|
||||
if (self.width < 2 or self.height < 2) return;
|
||||
const x1 = (self.width - @min(self.width - 2, self.box_width)) / 2;
|
||||
const y1 = (self.height - @min(self.height - 2, self.box_height)) / 2;
|
||||
const x2 = (self.width + @min(self.width, self.box_width)) / 2;
|
||||
const y2 = (self.height + @min(self.height, self.box_height)) / 2;
|
||||
|
||||
self.box_x = x1;
|
||||
self.box_y = y1;
|
||||
|
||||
if (show_borders) {
|
||||
_ = termbox.tb_set_cell(@intCast(x1 - 1), @intCast(y1 - 1), self.box_chars.left_up, self.border_fg, self.bg);
|
||||
_ = termbox.tb_set_cell(@intCast(x2), @intCast(y1 - 1), self.box_chars.right_up, self.border_fg, self.bg);
|
||||
_ = termbox.tb_set_cell(@intCast(x1 - 1), @intCast(y2), self.box_chars.left_down, self.border_fg, self.bg);
|
||||
_ = termbox.tb_set_cell(@intCast(x2), @intCast(y2), self.box_chars.right_down, self.border_fg, self.bg);
|
||||
|
||||
var c1 = Cell.init(self.box_chars.top, self.border_fg, self.bg);
|
||||
var c2 = Cell.init(self.box_chars.bottom, self.border_fg, self.bg);
|
||||
|
||||
for (0..self.box_width) |i| {
|
||||
c1.put(x1 + i, y1 - 1);
|
||||
c2.put(x1 + i, y2);
|
||||
}
|
||||
|
||||
c1.ch = self.box_chars.left;
|
||||
c2.ch = self.box_chars.right;
|
||||
|
||||
for (0..self.box_height) |i| {
|
||||
c1.put(x1 - 1, y1 + i);
|
||||
c2.put(x2, y1 + i);
|
||||
}
|
||||
}
|
||||
|
||||
if (blank_box) {
|
||||
for (0..self.box_height) |y| {
|
||||
for (0..self.box_width) |x| {
|
||||
self.blank_cell.put(x1 + x, y1 + y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calculateComponentCoordinates(self: TerminalBuffer) struct {
|
||||
start_x: usize,
|
||||
x: usize,
|
||||
y: usize,
|
||||
full_visible_length: usize,
|
||||
visible_length: usize,
|
||||
} {
|
||||
const start_x = self.box_x + self.margin_box_h;
|
||||
const x = start_x + self.labels_max_length + 1;
|
||||
const y = self.box_y + self.margin_box_v;
|
||||
const full_visible_length = self.box_x + self.box_width - self.margin_box_h - start_x;
|
||||
const visible_length = self.box_x + self.box_width - self.margin_box_h - x;
|
||||
|
||||
return .{
|
||||
.start_x = start_x,
|
||||
.x = x,
|
||||
.y = y,
|
||||
.full_visible_length = full_visible_length,
|
||||
.visible_length = visible_length,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn drawLabel(self: TerminalBuffer, text: []const u8, x: usize, y: usize) void {
|
||||
drawColorLabel(text, x, y, self.fg, self.bg);
|
||||
}
|
||||
|
||||
147
src/tui/components/CenteredBox.zig
Normal file
147
src/tui/components/CenteredBox.zig
Normal file
@@ -0,0 +1,147 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const Cell = @import("../Cell.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const Position = @import("../Position.zig");
|
||||
const termbox = TerminalBuffer.termbox;
|
||||
|
||||
const CenteredBox = @This();
|
||||
|
||||
buffer: *TerminalBuffer,
|
||||
horizontal_margin: usize,
|
||||
vertical_margin: usize,
|
||||
width: usize,
|
||||
height: usize,
|
||||
show_borders: bool,
|
||||
blank_box: bool,
|
||||
top_title: ?[]const u8,
|
||||
bottom_title: ?[]const u8,
|
||||
left_pos: Position,
|
||||
right_pos: Position,
|
||||
children_pos: Position,
|
||||
|
||||
pub fn init(
|
||||
buffer: *TerminalBuffer,
|
||||
horizontal_margin: usize,
|
||||
vertical_margin: usize,
|
||||
width: usize,
|
||||
height: usize,
|
||||
show_borders: bool,
|
||||
blank_box: bool,
|
||||
top_title: ?[]const u8,
|
||||
bottom_title: ?[]const u8,
|
||||
) CenteredBox {
|
||||
return .{
|
||||
.buffer = buffer,
|
||||
.horizontal_margin = horizontal_margin,
|
||||
.vertical_margin = vertical_margin,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.show_borders = show_borders,
|
||||
.blank_box = blank_box,
|
||||
.top_title = top_title,
|
||||
.bottom_title = bottom_title,
|
||||
.left_pos = TerminalBuffer.START_POSITION,
|
||||
.right_pos = TerminalBuffer.START_POSITION,
|
||||
.children_pos = TerminalBuffer.START_POSITION,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn position(self: *CenteredBox, original_pos: Position) void {
|
||||
if (self.buffer.width < 2 or self.buffer.height < 2) return;
|
||||
|
||||
self.left_pos = Position.init(
|
||||
(self.buffer.width - @min(self.buffer.width - 2, self.width)) / 2,
|
||||
(self.buffer.height - @min(self.buffer.height - 2, self.height)) / 2,
|
||||
).add(original_pos);
|
||||
|
||||
self.right_pos = Position.init(
|
||||
(self.buffer.width + @min(self.buffer.width, self.width)) / 2,
|
||||
(self.buffer.height + @min(self.buffer.height, self.height)) / 2,
|
||||
).add(original_pos);
|
||||
|
||||
self.children_pos = Position.init(
|
||||
self.left_pos.x + self.horizontal_margin,
|
||||
self.left_pos.y + self.vertical_margin,
|
||||
).add(original_pos);
|
||||
}
|
||||
|
||||
pub fn childrenPosition(self: CenteredBox) Position {
|
||||
return self.children_pos;
|
||||
}
|
||||
|
||||
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),
|
||||
self.buffer.box_chars.left_up,
|
||||
self.buffer.border_fg,
|
||||
self.buffer.bg,
|
||||
);
|
||||
_ = termbox.tb_set_cell(
|
||||
@intCast(self.right_pos.x),
|
||||
@intCast(self.left_pos.y - 1),
|
||||
self.buffer.box_chars.right_up,
|
||||
self.buffer.border_fg,
|
||||
self.buffer.bg,
|
||||
);
|
||||
_ = termbox.tb_set_cell(
|
||||
@intCast(self.left_pos.x - 1),
|
||||
@intCast(self.right_pos.y),
|
||||
self.buffer.box_chars.left_down,
|
||||
self.buffer.border_fg,
|
||||
self.buffer.bg,
|
||||
);
|
||||
_ = termbox.tb_set_cell(
|
||||
@intCast(self.right_pos.x),
|
||||
@intCast(self.right_pos.y),
|
||||
self.buffer.box_chars.right_down,
|
||||
self.buffer.border_fg,
|
||||
self.buffer.bg,
|
||||
);
|
||||
|
||||
var c1 = Cell.init(self.buffer.box_chars.top, self.buffer.border_fg, self.buffer.bg);
|
||||
var c2 = Cell.init(self.buffer.box_chars.bottom, self.buffer.border_fg, self.buffer.bg);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
c1.ch = self.buffer.box_chars.left;
|
||||
c2.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);
|
||||
}
|
||||
}
|
||||
|
||||
if (self.blank_box) {
|
||||
for (0..self.height) |y| {
|
||||
for (0..self.width) |x| {
|
||||
self.buffer.blank_cell.put(self.left_pos.x + x, self.left_pos.y + y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (self.top_title) |title| {
|
||||
self.buffer.drawConfinedLabel(
|
||||
title,
|
||||
self.left_pos.x,
|
||||
self.left_pos.y - 1,
|
||||
self.width,
|
||||
);
|
||||
}
|
||||
|
||||
if (self.bottom_title) |title| {
|
||||
self.buffer.drawConfinedLabel(
|
||||
title,
|
||||
self.left_pos.x,
|
||||
self.left_pos.y + self.height,
|
||||
self.width,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,21 @@ const Message = struct {
|
||||
|
||||
label: MessageLabel,
|
||||
|
||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer) InfoLine {
|
||||
pub fn init(
|
||||
allocator: Allocator,
|
||||
buffer: *TerminalBuffer,
|
||||
width: usize,
|
||||
) InfoLine {
|
||||
return .{
|
||||
.label = MessageLabel.init(allocator, buffer, drawItem, null, null),
|
||||
.label = MessageLabel.init(
|
||||
allocator,
|
||||
buffer,
|
||||
drawItem,
|
||||
null,
|
||||
null,
|
||||
width,
|
||||
true,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,23 +50,31 @@ pub fn addMessage(self: *InfoLine, text: []const u8, bg: u32, fg: u32) !void {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn clearRendered(allocator: Allocator, buffer: TerminalBuffer) !void {
|
||||
pub fn clearRendered(self: InfoLine, allocator: Allocator) !void {
|
||||
// Draw over the area
|
||||
const y = buffer.box_y + buffer.margin_box_v;
|
||||
const spaces = try allocator.alloc(u8, buffer.box_width);
|
||||
const spaces = try allocator.alloc(u8, self.label.width - 2);
|
||||
defer allocator.free(spaces);
|
||||
|
||||
@memset(spaces, ' ');
|
||||
|
||||
buffer.drawLabel(spaces, buffer.box_x, y);
|
||||
self.label.buffer.drawLabel(
|
||||
spaces,
|
||||
self.label.component_pos.x + 2,
|
||||
self.label.component_pos.y,
|
||||
);
|
||||
}
|
||||
|
||||
fn drawItem(label: *MessageLabel, message: Message, _: usize, _: usize) bool {
|
||||
if (message.width == 0 or label.buffer.box_width <= message.width) return false;
|
||||
fn drawItem(label: *MessageLabel, message: Message, x: usize, y: usize, width: usize) void {
|
||||
if (message.width == 0 or width <= message.width) return;
|
||||
|
||||
const x = label.buffer.box_x + ((label.buffer.box_width - message.width) / 2);
|
||||
label.first_char_x = x + message.width;
|
||||
const x_offset = if (label.text_in_center) (width - message.width) / 2 else 0;
|
||||
|
||||
TerminalBuffer.drawColorLabel(message.text, x, label.y, message.fg, message.bg);
|
||||
return true;
|
||||
label.item_width = message.width + x_offset;
|
||||
TerminalBuffer.drawColorLabel(
|
||||
message.text,
|
||||
x + x_offset,
|
||||
y,
|
||||
message.fg,
|
||||
message.bg,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,9 +19,23 @@ const Session = @This();
|
||||
label: EnvironmentLabel,
|
||||
user_list: *UserList,
|
||||
|
||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, user_list: *UserList) Session {
|
||||
pub fn init(
|
||||
allocator: Allocator,
|
||||
buffer: *TerminalBuffer,
|
||||
user_list: *UserList,
|
||||
width: usize,
|
||||
text_in_center: bool,
|
||||
) Session {
|
||||
return .{
|
||||
.label = EnvironmentLabel.init(allocator, buffer, drawItem, sessionChanged, user_list),
|
||||
.label = EnvironmentLabel.init(
|
||||
allocator,
|
||||
buffer,
|
||||
drawItem,
|
||||
sessionChanged,
|
||||
user_list,
|
||||
width,
|
||||
text_in_center,
|
||||
),
|
||||
.user_list = user_list,
|
||||
};
|
||||
}
|
||||
@@ -55,14 +69,12 @@ fn sessionChanged(env: Env, maybe_user_list: ?*UserList) void {
|
||||
}
|
||||
}
|
||||
|
||||
fn drawItem(label: *EnvironmentLabel, env: Env, x: usize, y: usize) bool {
|
||||
const length = @min(env.environment.name.len, label.visible_length - 3);
|
||||
if (length == 0) return false;
|
||||
fn drawItem(label: *EnvironmentLabel, env: Env, x: usize, y: usize, width: usize) void {
|
||||
const length = @min(env.environment.name.len, width - 3);
|
||||
if (length == 0) return;
|
||||
|
||||
const nx = if (label.text_in_center) (label.x + (label.visible_length - env.environment.name.len) / 2) else (label.x + 2);
|
||||
label.first_char_x = nx + env.environment.name.len;
|
||||
const x_offset = if (label.text_in_center) (width - length) / 2 else 0;
|
||||
|
||||
label.buffer.drawLabel(env.environment.specifier, x, y);
|
||||
label.buffer.drawLabel(env.environment.name, nx, label.y);
|
||||
return true;
|
||||
label.item_width = length + x_offset;
|
||||
label.buffer.drawLabel(env.environment.name, x + x_offset, y);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const Position = @import("../Position.zig");
|
||||
const termbox = TerminalBuffer.termbox;
|
||||
|
||||
const DynamicString = std.ArrayListUnmanaged(u8);
|
||||
@@ -14,13 +15,19 @@ text: DynamicString,
|
||||
end: usize,
|
||||
cursor: usize,
|
||||
visible_start: usize,
|
||||
visible_length: usize,
|
||||
x: usize,
|
||||
y: usize,
|
||||
width: usize,
|
||||
component_pos: Position,
|
||||
children_pos: Position,
|
||||
masked: bool,
|
||||
maybe_mask: ?u32,
|
||||
|
||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, masked: bool, maybe_mask: ?u32) Text {
|
||||
pub fn init(
|
||||
allocator: Allocator,
|
||||
buffer: *TerminalBuffer,
|
||||
masked: bool,
|
||||
maybe_mask: ?u32,
|
||||
width: usize,
|
||||
) Text {
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.buffer = buffer,
|
||||
@@ -28,9 +35,9 @@ pub fn init(allocator: Allocator, buffer: *TerminalBuffer, masked: bool, maybe_m
|
||||
.end = 0,
|
||||
.cursor = 0,
|
||||
.visible_start = 0,
|
||||
.visible_length = 0,
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = width,
|
||||
.component_pos = TerminalBuffer.START_POSITION,
|
||||
.children_pos = TerminalBuffer.START_POSITION,
|
||||
.masked = masked,
|
||||
.maybe_mask = maybe_mask,
|
||||
};
|
||||
@@ -40,10 +47,26 @@ pub fn deinit(self: *Text) void {
|
||||
self.text.deinit(self.allocator);
|
||||
}
|
||||
|
||||
pub fn position(self: *Text, x: usize, y: usize, visible_length: usize) void {
|
||||
self.x = x;
|
||||
self.y = y;
|
||||
self.visible_length = visible_length;
|
||||
pub fn positionX(self: *Text, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = original_pos.addX(self.width);
|
||||
}
|
||||
|
||||
pub fn positionY(self: *Text, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = original_pos.addY(1);
|
||||
}
|
||||
|
||||
pub fn positionXY(self: *Text, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = Position.init(
|
||||
self.width,
|
||||
1,
|
||||
).add(original_pos);
|
||||
}
|
||||
|
||||
pub fn childrenPosition(self: Text) Position {
|
||||
return self.children_pos;
|
||||
}
|
||||
|
||||
pub fn handle(self: *Text, maybe_event: ?*termbox.tb_event, insert_mode: bool) !void {
|
||||
@@ -79,36 +102,44 @@ pub fn handle(self: *Text, maybe_event: ?*termbox.tb_event, insert_mode: bool) !
|
||||
}
|
||||
|
||||
if (self.masked and self.maybe_mask == null) {
|
||||
_ = termbox.tb_set_cursor(@intCast(self.x), @intCast(self.y));
|
||||
_ = termbox.tb_set_cursor(@intCast(self.component_pos.x), @intCast(self.component_pos.y));
|
||||
return;
|
||||
}
|
||||
|
||||
_ = termbox.tb_set_cursor(@intCast(self.x + (self.cursor - self.visible_start)), @intCast(self.y));
|
||||
_ = termbox.tb_set_cursor(
|
||||
@intCast(self.component_pos.x + (self.cursor - self.visible_start)),
|
||||
@intCast(self.component_pos.y),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn draw(self: Text) void {
|
||||
if (self.masked) {
|
||||
if (self.maybe_mask) |mask| {
|
||||
const length = @min(self.text.items.len, self.visible_length - 1);
|
||||
const length = @min(self.text.items.len, self.width - 1);
|
||||
if (length == 0) return;
|
||||
|
||||
self.buffer.drawCharMultiple(mask, self.x, self.y, length);
|
||||
self.buffer.drawCharMultiple(
|
||||
mask,
|
||||
self.component_pos.x,
|
||||
self.component_pos.y,
|
||||
length,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const length = @min(self.text.items.len, self.visible_length);
|
||||
const length = @min(self.text.items.len, self.width);
|
||||
if (length == 0) return;
|
||||
|
||||
const visible_slice = vs: {
|
||||
if (self.text.items.len > self.visible_length and self.cursor < self.text.items.len) {
|
||||
break :vs self.text.items[self.visible_start..(self.visible_length + self.visible_start)];
|
||||
if (self.text.items.len > self.width and self.cursor < self.text.items.len) {
|
||||
break :vs self.text.items[self.visible_start..(self.width + self.visible_start)];
|
||||
} else {
|
||||
break :vs self.text.items[self.visible_start..];
|
||||
}
|
||||
};
|
||||
|
||||
self.buffer.drawLabel(visible_slice, self.x, self.y);
|
||||
self.buffer.drawLabel(visible_slice, self.component_pos.x, self.component_pos.y);
|
||||
}
|
||||
|
||||
pub fn clear(self: *Text) void {
|
||||
@@ -127,7 +158,7 @@ fn goLeft(self: *Text) void {
|
||||
|
||||
fn goRight(self: *Text) void {
|
||||
if (self.cursor >= self.end) return;
|
||||
if (self.cursor - self.visible_start == self.visible_length - 1) self.visible_start += 1;
|
||||
if (self.cursor - self.visible_start == self.width - 1) self.visible_start += 1;
|
||||
|
||||
self.cursor += 1;
|
||||
}
|
||||
|
||||
@@ -19,9 +19,25 @@ const UserList = @This();
|
||||
|
||||
label: UserLabel,
|
||||
|
||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, usernames: StringList, saved_users: *SavedUsers, session: *Session) !UserList {
|
||||
pub fn init(
|
||||
allocator: Allocator,
|
||||
buffer: *TerminalBuffer,
|
||||
usernames: StringList,
|
||||
saved_users: *SavedUsers,
|
||||
session: *Session,
|
||||
width: usize,
|
||||
text_in_center: bool,
|
||||
) !UserList {
|
||||
var userList = UserList{
|
||||
.label = UserLabel.init(allocator, buffer, drawItem, usernameChanged, session),
|
||||
.label = UserLabel.init(
|
||||
allocator,
|
||||
buffer,
|
||||
drawItem,
|
||||
usernameChanged,
|
||||
session,
|
||||
width,
|
||||
text_in_center,
|
||||
),
|
||||
};
|
||||
|
||||
for (usernames.items) |username| {
|
||||
@@ -75,13 +91,12 @@ fn usernameChanged(user: User, maybe_session: ?*Session) void {
|
||||
}
|
||||
}
|
||||
|
||||
fn drawItem(label: *UserLabel, user: User, _: usize, _: usize) bool {
|
||||
const length = @min(user.name.len, label.visible_length - 3);
|
||||
if (length == 0) return false;
|
||||
fn drawItem(label: *UserLabel, user: User, x: usize, y: usize, width: usize) void {
|
||||
const length = @min(user.name.len, width - 3);
|
||||
if (length == 0) return;
|
||||
|
||||
const x = if (label.text_in_center) (label.x + (label.visible_length - user.name.len) / 2) else (label.x + 2);
|
||||
label.first_char_x = x + user.name.len;
|
||||
const x_offset = if (label.text_in_center) (width - length) / 2 else 0;
|
||||
|
||||
label.buffer.drawLabel(user.name, x, label.y);
|
||||
return true;
|
||||
label.item_width = length + x_offset;
|
||||
label.buffer.drawLabel(user.name, x + x_offset, y);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
const std = @import("std");
|
||||
|
||||
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) bool;
|
||||
const DrawItemFn = *const fn (*Self, ItemType, usize, usize, usize) void;
|
||||
const ChangeItemFn = *const fn (ItemType, ?ChangeItemType) void;
|
||||
|
||||
const termbox = TerminalBuffer.termbox;
|
||||
@@ -17,26 +18,34 @@ pub fn CyclableLabel(comptime ItemType: type, comptime ChangeItemType: type) typ
|
||||
buffer: *TerminalBuffer,
|
||||
list: ItemList,
|
||||
current: usize,
|
||||
visible_length: usize,
|
||||
x: usize,
|
||||
y: usize,
|
||||
first_char_x: usize,
|
||||
width: usize,
|
||||
component_pos: Position,
|
||||
children_pos: Position,
|
||||
text_in_center: bool,
|
||||
item_width: usize,
|
||||
draw_item_fn: DrawItemFn,
|
||||
change_item_fn: ?ChangeItemFn,
|
||||
change_item_arg: ?ChangeItemType,
|
||||
|
||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, draw_item_fn: DrawItemFn, change_item_fn: ?ChangeItemFn, change_item_arg: ?ChangeItemType) Self {
|
||||
pub fn init(
|
||||
allocator: Allocator,
|
||||
buffer: *TerminalBuffer,
|
||||
draw_item_fn: DrawItemFn,
|
||||
change_item_fn: ?ChangeItemFn,
|
||||
change_item_arg: ?ChangeItemType,
|
||||
width: usize,
|
||||
text_in_center: bool,
|
||||
) Self {
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.buffer = buffer,
|
||||
.list = .empty,
|
||||
.current = 0,
|
||||
.visible_length = 0,
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.first_char_x = 0,
|
||||
.text_in_center = false,
|
||||
.width = width,
|
||||
.component_pos = TerminalBuffer.START_POSITION,
|
||||
.children_pos = TerminalBuffer.START_POSITION,
|
||||
.text_in_center = text_in_center,
|
||||
.item_width = 0,
|
||||
.draw_item_fn = draw_item_fn,
|
||||
.change_item_fn = change_item_fn,
|
||||
.change_item_arg = change_item_arg,
|
||||
@@ -47,14 +56,29 @@ pub fn CyclableLabel(comptime ItemType: type, comptime ChangeItemType: type) typ
|
||||
self.list.deinit(self.allocator);
|
||||
}
|
||||
|
||||
pub fn position(self: *Self, x: usize, y: usize, visible_length: usize, text_in_center: ?bool) void {
|
||||
self.x = x;
|
||||
self.y = y;
|
||||
self.visible_length = visible_length;
|
||||
self.first_char_x = x + 2;
|
||||
if (text_in_center) |value| {
|
||||
self.text_in_center = value;
|
||||
}
|
||||
pub fn positionX(self: *Self, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.item_width = 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.item_width = 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.item_width = 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 {
|
||||
@@ -81,28 +105,51 @@ pub fn CyclableLabel(comptime ItemType: type, comptime ChangeItemType: type) typ
|
||||
}
|
||||
}
|
||||
|
||||
_ = termbox.tb_set_cursor(@intCast(self.first_char_x), @intCast(self.y));
|
||||
_ = termbox.tb_set_cursor(
|
||||
@intCast(self.component_pos.x + self.item_width + 2),
|
||||
@intCast(self.component_pos.y),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn draw(self: *Self) void {
|
||||
if (self.list.items.len == 0) return;
|
||||
|
||||
_ = termbox.tb_set_cell(
|
||||
@intCast(self.component_pos.x),
|
||||
@intCast(self.component_pos.y),
|
||||
'<',
|
||||
self.buffer.fg,
|
||||
self.buffer.bg,
|
||||
);
|
||||
_ = termbox.tb_set_cell(
|
||||
@intCast(self.component_pos.x + self.width - 1),
|
||||
@intCast(self.component_pos.y),
|
||||
'>',
|
||||
self.buffer.fg,
|
||||
self.buffer.bg,
|
||||
);
|
||||
|
||||
const current_item = self.list.items[self.current];
|
||||
const x = self.buffer.box_x + self.buffer.margin_box_h;
|
||||
const y = self.buffer.box_y + self.buffer.margin_box_v + 2;
|
||||
const x = self.component_pos.x + 2;
|
||||
const y = self.component_pos.y;
|
||||
const width = self.width - 2;
|
||||
|
||||
const continue_drawing = @call(.auto, self.draw_item_fn, .{ self, current_item, x, y });
|
||||
if (!continue_drawing) return;
|
||||
|
||||
_ = termbox.tb_set_cell(@intCast(self.x), @intCast(self.y), '<', self.buffer.fg, self.buffer.bg);
|
||||
_ = termbox.tb_set_cell(@intCast(self.x + self.visible_length - 1), @intCast(self.y), '>', self.buffer.fg, self.buffer.bg);
|
||||
@call(
|
||||
.auto,
|
||||
self.draw_item_fn,
|
||||
.{ self, current_item, x, y, width },
|
||||
);
|
||||
}
|
||||
|
||||
fn goLeft(self: *Self) void {
|
||||
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 });
|
||||
@call(
|
||||
.auto,
|
||||
change_item_fn,
|
||||
.{ self.list.items[self.current], self.change_item_arg },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +157,11 @@ pub fn CyclableLabel(comptime ItemType: type, comptime ChangeItemType: type) typ
|
||||
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 });
|
||||
@call(
|
||||
.auto,
|
||||
change_item_fn,
|
||||
.{ self.list.items[self.current], self.change_item_arg },
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user