Properly calculate string lengths

Signed-off-by: AnErrupTion <anerruption@disroot.org>
This commit is contained in:
AnErrupTion
2026-02-08 22:04:09 +01:00
parent e9e2d51261
commit 941b7e0dae
8 changed files with 28 additions and 24 deletions

View File

@@ -310,7 +310,7 @@ pub fn main() !void {
// Initialize terminal buffer // Initialize terminal buffer
try log_file.info("tui", "initializing terminal buffer", .{}); try log_file.info("tui", "initializing terminal buffer", .{});
const labels_max_length = @max(lang.login.len, lang.password.len); const labels_max_length = @max(TerminalBuffer.strWidth(lang.login), TerminalBuffer.strWidth(lang.password));
var seed: u64 = undefined; var seed: u64 = undefined;
std.crypto.random.bytes(std.mem.asBytes(&seed)); // Get a random seed for the PRNG (used by animations) std.crypto.random.bytes(std.mem.asBytes(&seed)); // Get a random seed for the PRNG (used by animations)
@@ -1419,23 +1419,23 @@ fn positionComponents(state: *UiState) void {
state.clock_label.positionXY(state.edge_margin state.clock_label.positionXY(state.edge_margin
.add(TerminalBuffer.START_POSITION) .add(TerminalBuffer.START_POSITION)
.invertX(state.buffer.width) .invertX(state.buffer.width)
.removeXIf(state.clock_label.text.len, state.buffer.width > state.clock_label.text.len + state.edge_margin.x)); .removeXIf(TerminalBuffer.strWidth(state.clock_label.text), state.buffer.width > TerminalBuffer.strWidth(state.clock_label.text) + state.edge_margin.x));
state.numlock_label.positionX(state.edge_margin state.numlock_label.positionX(state.edge_margin
.add(TerminalBuffer.START_POSITION) .add(TerminalBuffer.START_POSITION)
.addYFromIf(state.clock_label.childrenPosition(), state.config.clock != null) .addYFromIf(state.clock_label.childrenPosition(), state.config.clock != null)
.removeYFromIf(state.edge_margin, state.config.clock != null) .removeYFromIf(state.edge_margin, state.config.clock != null)
.invertX(state.buffer.width) .invertX(state.buffer.width)
.removeXIf(state.numlock_label.text.len, state.buffer.width > state.numlock_label.text.len + state.edge_margin.x)); .removeXIf(TerminalBuffer.strWidth(state.numlock_label.text), state.buffer.width > TerminalBuffer.strWidth(state.numlock_label.text) + state.edge_margin.x));
state.capslock_label.positionX(state.numlock_label state.capslock_label.positionX(state.numlock_label
.childrenPosition() .childrenPosition()
.removeX(state.numlock_label.text.len + state.capslock_label.text.len + 1)); .removeX(TerminalBuffer.strWidth(state.numlock_label.text) + TerminalBuffer.strWidth(state.capslock_label.text) + 1));
state.box.positionXY(TerminalBuffer.START_POSITION); state.box.positionXY(TerminalBuffer.START_POSITION);
if (state.config.bigclock != .none) { if (state.config.bigclock != .none) {
const half_width = state.buffer.width / 2; const half_width = state.buffer.width / 2;
const half_label_width = (state.bigclock_label.text.len * (bigLabel.CHAR_WIDTH + 1)) / 2; const half_label_width = (TerminalBuffer.strWidth(state.bigclock_label.text) * (bigLabel.CHAR_WIDTH + 1)) / 2;
const half_height = (if (state.buffer.height > state.box.height) state.buffer.height - state.box.height else state.buffer.height) / 2; const half_height = (if (state.buffer.height > state.box.height) state.buffer.height - state.box.height else state.buffer.height) / 2;
state.bigclock_label.positionXY(TerminalBuffer.START_POSITION state.bigclock_label.positionXY(TerminalBuffer.START_POSITION
@@ -1453,7 +1453,7 @@ fn positionComponents(state: *UiState) void {
.addY(1)); .addY(1));
state.session.label.positionY(state.session_specifier_label state.session.label.positionY(state.session_specifier_label
.childrenPosition() .childrenPosition()
.addX(state.labels_max_length - state.session_specifier_label.text.len + 1)); .addX(state.labels_max_length - TerminalBuffer.strWidth(state.session_specifier_label.text) + 1));
state.login_label.positionX(state.session.label state.login_label.positionX(state.session.label
.childrenPosition() .childrenPosition()
@@ -1461,7 +1461,7 @@ fn positionComponents(state: *UiState) void {
.addY(1)); .addY(1));
state.login.label.positionY(state.login_label state.login.label.positionY(state.login_label
.childrenPosition() .childrenPosition()
.addX(state.labels_max_length - state.login_label.text.len + 1)); .addX(state.labels_max_length - TerminalBuffer.strWidth(state.login_label.text) + 1));
state.password_label.positionX(state.login.label state.password_label.positionX(state.login.label
.childrenPosition() .childrenPosition()
@@ -1469,7 +1469,7 @@ fn positionComponents(state: *UiState) void {
.addY(1)); .addY(1));
state.password.positionY(state.password_label state.password.positionY(state.password_label
.childrenPosition() .childrenPosition()
.addX(state.labels_max_length - state.password_label.text.len + 1)); .addX(state.labels_max_length - TerminalBuffer.strWidth(state.password_label.text) + 1));
state.version_label.positionXY(state.edge_margin state.version_label.positionXY(state.edge_margin
.add(TerminalBuffer.START_POSITION) .add(TerminalBuffer.START_POSITION)

View File

@@ -251,13 +251,16 @@ pub fn drawCharMultiple(
// Every codepoint is assumed to have a width of 1. // Every codepoint is assumed to have a width of 1.
// Since Ly is normally running in a TTY, this should be fine. // Since Ly is normally running in a TTY, this should be fine.
pub fn strWidth(str: []const u8) !u8 { pub fn strWidth(str: []const u8) usize {
const utf8view = try std.unicode.Utf8View.init(str); const utf8view = std.unicode.Utf8View.init(str) catch return str.len;
var utf8 = utf8view.iterator(); var utf8 = utf8view.iterator();
var i: c_int = 0; var length: c_int = 0;
while (utf8.nextCodepoint()) |codepoint| i += termbox.tb_wcwidth(codepoint);
return @intCast(i); while (utf8.nextCodepoint()) |codepoint| {
length += termbox.tb_wcwidth(codepoint);
}
return @intCast(length);
} }
fn clearBackBuffer() !void { fn clearBackBuffer() !void {

View File

@@ -9,7 +9,7 @@ const MessageLabel = generic.CyclableLabel(Message, Message);
const InfoLine = @This(); const InfoLine = @This();
const Message = struct { const Message = struct {
width: u8, width: usize,
text: []const u8, text: []const u8,
bg: u32, bg: u32,
fg: u32, fg: u32,
@@ -47,7 +47,7 @@ pub fn addMessage(self: *InfoLine, text: []const u8, bg: u32, fg: u32) !void {
if (text.len == 0) return; if (text.len == 0) return;
try self.label.addItem(.{ try self.label.addItem(.{
.width = try TerminalBuffer.strWidth(text), .width = TerminalBuffer.strWidth(text),
.text = text, .text = text,
.bg = bg, .bg = bg,
.fg = fg, .fg = fg,

View File

@@ -76,7 +76,7 @@ fn sessionChanged(env: Env, maybe_user_list: ?*UserList) void {
fn drawItem(label: *EnvironmentLabel, env: Env, x: usize, y: usize, width: usize) void { fn drawItem(label: *EnvironmentLabel, env: Env, x: usize, y: usize, width: usize) void {
if (width < 3) return; if (width < 3) return;
const length = @min(env.environment.name.len, width - 3); const length = @min(TerminalBuffer.strWidth(env.environment.name), width - 3);
if (length == 0) return; if (length == 0) return;
const x_offset = if (label.text_in_center and width >= length) (width - length) / 2 else 0; const x_offset = if (label.text_in_center and width >= length) (width - length) / 2 else 0;

View File

@@ -123,7 +123,7 @@ pub fn draw(self: Text) void {
if (self.maybe_mask) |mask| { if (self.maybe_mask) |mask| {
if (self.width < 1) return; if (self.width < 1) return;
const length = @min(self.text.items.len, self.width - 1); const length = @min(TerminalBuffer.strWidth(self.text.items), self.width - 1);
if (length == 0) return; if (length == 0) return;
TerminalBuffer.drawCharMultiple( TerminalBuffer.drawCharMultiple(
@@ -138,11 +138,12 @@ pub fn draw(self: Text) void {
return; return;
} }
const length = @min(self.text.items.len, self.width); const str_length = TerminalBuffer.strWidth(self.text.items);
const length = @min(str_length, self.width);
if (length == 0) return; if (length == 0) return;
const visible_slice = vs: { const visible_slice = vs: {
if (self.text.items.len > self.width and self.cursor < self.text.items.len) { if (str_length > self.width and self.cursor < str_length) {
break :vs self.text.items[self.visible_start..(self.width + self.visible_start)]; break :vs self.text.items[self.visible_start..(self.width + self.visible_start)];
} else { } else {
break :vs self.text.items[self.visible_start..]; break :vs self.text.items[self.visible_start..];

View File

@@ -98,7 +98,7 @@ fn usernameChanged(user: User, maybe_session: ?*Session) void {
fn drawItem(label: *UserLabel, user: User, x: usize, y: usize, width: usize) void { fn drawItem(label: *UserLabel, user: User, x: usize, y: usize, width: usize) void {
if (width < 3) return; if (width < 3) return;
const length = @min(user.name.len, width - 3); const length = @min(TerminalBuffer.strWidth(user.name), width - 3);
if (length == 0) return; if (length == 0) return;
const x_offset = if (label.text_in_center and width >= length) (width - length) / 2 else 0; const x_offset = if (label.text_in_center and width >= length) (width - length) / 2 else 0;

View File

@@ -113,7 +113,7 @@ pub fn BigLabel(comptime ContextType: type) type {
pub fn positionX(self: *Self, original_pos: Position) void { pub fn positionX(self: *Self, original_pos: Position) void {
self.component_pos = original_pos; self.component_pos = original_pos;
self.children_pos = original_pos.addX(self.text.len * CHAR_WIDTH); self.children_pos = original_pos.addX(TerminalBuffer.strWidth(self.text) * CHAR_WIDTH);
} }
pub fn positionY(self: *Self, original_pos: Position) void { pub fn positionY(self: *Self, original_pos: Position) void {
@@ -124,7 +124,7 @@ pub fn BigLabel(comptime ContextType: type) type {
pub fn positionXY(self: *Self, original_pos: Position) void { pub fn positionXY(self: *Self, original_pos: Position) void {
self.component_pos = original_pos; self.component_pos = original_pos;
self.children_pos = Position.init( self.children_pos = Position.init(
self.text.len * CHAR_WIDTH, TerminalBuffer.strWidth(self.text) * CHAR_WIDTH,
CHAR_HEIGHT, CHAR_HEIGHT,
).add(original_pos); ).add(original_pos);
} }

View File

@@ -71,7 +71,7 @@ pub fn Label(comptime ContextType: type) type {
pub fn positionX(self: *Self, original_pos: Position) void { pub fn positionX(self: *Self, original_pos: Position) void {
self.component_pos = original_pos; self.component_pos = original_pos;
self.children_pos = original_pos.addX(self.text.len); self.children_pos = original_pos.addX(TerminalBuffer.strWidth(self.text));
} }
pub fn positionY(self: *Self, original_pos: Position) void { pub fn positionY(self: *Self, original_pos: Position) void {
@@ -82,7 +82,7 @@ pub fn Label(comptime ContextType: type) type {
pub fn positionXY(self: *Self, original_pos: Position) void { pub fn positionXY(self: *Self, original_pos: Position) void {
self.component_pos = original_pos; self.component_pos = original_pos;
self.children_pos = Position.init( self.children_pos = Position.init(
self.text.len, TerminalBuffer.strWidth(self.text),
1, 1,
).add(original_pos); ).add(original_pos);
} }