From 941b7e0dae0f4e82809d93eb005e834f4c61d444 Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Sun, 8 Feb 2026 22:04:09 +0100 Subject: [PATCH] Properly calculate string lengths Signed-off-by: AnErrupTion --- src/main.zig | 16 ++++++++-------- src/tui/TerminalBuffer.zig | 13 ++++++++----- src/tui/components/InfoLine.zig | 4 ++-- src/tui/components/Session.zig | 2 +- src/tui/components/Text.zig | 7 ++++--- src/tui/components/UserList.zig | 2 +- src/tui/components/bigLabel.zig | 4 ++-- src/tui/components/label.zig | 4 ++-- 8 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/main.zig b/src/main.zig index 0efb5d4..f7e5777 100644 --- a/src/main.zig +++ b/src/main.zig @@ -310,7 +310,7 @@ pub fn main() !void { // Initialize 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; 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 .add(TerminalBuffer.START_POSITION) .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 .add(TerminalBuffer.START_POSITION) .addYFromIf(state.clock_label.childrenPosition(), state.config.clock != null) .removeYFromIf(state.edge_margin, state.config.clock != null) .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 .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); if (state.config.bigclock != .none) { 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; state.bigclock_label.positionXY(TerminalBuffer.START_POSITION @@ -1453,7 +1453,7 @@ fn positionComponents(state: *UiState) void { .addY(1)); state.session.label.positionY(state.session_specifier_label .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 .childrenPosition() @@ -1461,7 +1461,7 @@ fn positionComponents(state: *UiState) void { .addY(1)); state.login.label.positionY(state.login_label .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 .childrenPosition() @@ -1469,7 +1469,7 @@ fn positionComponents(state: *UiState) void { .addY(1)); state.password.positionY(state.password_label .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 .add(TerminalBuffer.START_POSITION) diff --git a/src/tui/TerminalBuffer.zig b/src/tui/TerminalBuffer.zig index 7b322b9..7f62d14 100644 --- a/src/tui/TerminalBuffer.zig +++ b/src/tui/TerminalBuffer.zig @@ -251,13 +251,16 @@ pub fn drawCharMultiple( // Every codepoint is assumed to have a width of 1. // Since Ly is normally running in a TTY, this should be fine. -pub fn strWidth(str: []const u8) !u8 { - const utf8view = try std.unicode.Utf8View.init(str); +pub fn strWidth(str: []const u8) usize { + const utf8view = std.unicode.Utf8View.init(str) catch return str.len; var utf8 = utf8view.iterator(); - var i: c_int = 0; - while (utf8.nextCodepoint()) |codepoint| i += termbox.tb_wcwidth(codepoint); + var length: c_int = 0; - return @intCast(i); + while (utf8.nextCodepoint()) |codepoint| { + length += termbox.tb_wcwidth(codepoint); + } + + return @intCast(length); } fn clearBackBuffer() !void { diff --git a/src/tui/components/InfoLine.zig b/src/tui/components/InfoLine.zig index ecff573..08d0c85 100644 --- a/src/tui/components/InfoLine.zig +++ b/src/tui/components/InfoLine.zig @@ -9,7 +9,7 @@ const MessageLabel = generic.CyclableLabel(Message, Message); const InfoLine = @This(); const Message = struct { - width: u8, + width: usize, text: []const u8, bg: 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; try self.label.addItem(.{ - .width = try TerminalBuffer.strWidth(text), + .width = TerminalBuffer.strWidth(text), .text = text, .bg = bg, .fg = fg, diff --git a/src/tui/components/Session.zig b/src/tui/components/Session.zig index d468eb0..1f0dcca 100644 --- a/src/tui/components/Session.zig +++ b/src/tui/components/Session.zig @@ -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 { 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; const x_offset = if (label.text_in_center and width >= length) (width - length) / 2 else 0; diff --git a/src/tui/components/Text.zig b/src/tui/components/Text.zig index 095253e..bc58348 100644 --- a/src/tui/components/Text.zig +++ b/src/tui/components/Text.zig @@ -123,7 +123,7 @@ pub fn draw(self: Text) void { if (self.maybe_mask) |mask| { 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; TerminalBuffer.drawCharMultiple( @@ -138,11 +138,12 @@ pub fn draw(self: Text) void { 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; 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)]; } else { break :vs self.text.items[self.visible_start..]; diff --git a/src/tui/components/UserList.zig b/src/tui/components/UserList.zig index 3bb96b3..4e22951 100644 --- a/src/tui/components/UserList.zig +++ b/src/tui/components/UserList.zig @@ -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 { 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; const x_offset = if (label.text_in_center and width >= length) (width - length) / 2 else 0; diff --git a/src/tui/components/bigLabel.zig b/src/tui/components/bigLabel.zig index 6e9feae..26b3257 100644 --- a/src/tui/components/bigLabel.zig +++ b/src/tui/components/bigLabel.zig @@ -113,7 +113,7 @@ pub fn BigLabel(comptime ContextType: type) type { pub fn positionX(self: *Self, original_pos: Position) void { 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 { @@ -124,7 +124,7 @@ pub fn BigLabel(comptime ContextType: type) type { pub fn positionXY(self: *Self, original_pos: Position) void { self.component_pos = original_pos; self.children_pos = Position.init( - self.text.len * CHAR_WIDTH, + TerminalBuffer.strWidth(self.text) * CHAR_WIDTH, CHAR_HEIGHT, ).add(original_pos); } diff --git a/src/tui/components/label.zig b/src/tui/components/label.zig index ba483bb..21b7a99 100644 --- a/src/tui/components/label.zig +++ b/src/tui/components/label.zig @@ -71,7 +71,7 @@ pub fn Label(comptime ContextType: type) type { pub fn positionX(self: *Self, original_pos: Position) void { 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 { @@ -82,7 +82,7 @@ pub fn Label(comptime ContextType: type) type { pub fn positionXY(self: *Self, original_pos: Position) void { self.component_pos = original_pos; self.children_pos = Position.init( - self.text.len, + TerminalBuffer.strWidth(self.text), 1, ).add(original_pos); }