From 293c0c646fc24ed6bfd319bcaefb07e4ab71d0a8 Mon Sep 17 00:00:00 2001 From: Luna Date: Thu, 16 Apr 2026 22:32:20 +0200 Subject: [PATCH 1/2] feature: add corner customization --- res/config.ini | 29 ++++++ src/config/Config.zig | 4 + src/main.zig | 232 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 265 insertions(+) diff --git a/res/config.ini b/res/config.ini index 86f44b4..5ac5df7 100644 --- a/res/config.ini +++ b/res/config.ini @@ -143,6 +143,35 @@ colormix_col2 = 0x000000FF # Color mixing animation third color id colormix_col3 = 0x20000000 +# Screen corners customization +# Keywords: +# keys -> Power management keys (shutdown, restart, etc.) +# clock -> Clock (format defined by 'clock' option) +# tty -> Active TTY number +# battery -> Battery percentage +# version -> Ly version string +# numlock -> Numlock state +# capslock -> Capslock state +# locks -> Both numlock and capslock +# labels -> All custom info labels (lbl:) +# binds -> All custom keybind hints (cmd:) +# lbl:name -> Specific custom info label +# cmd:key -> Specific custom keybind hint +# +# The order defines the vertical stack (first item is at the edge). + +# Top left corner configuration +corner_top_left = keys + +# Top right corner configuration +corner_top_right = clock tty + +# Bottom left corner configuration +corner_bottom_left = version + +# Bottom right corner configuration +corner_bottom_right = labels + # For custom binds: the horizontal limit in characters for each # line of custom binds before moving on to the next. # If null, defaults to the width of the terminal instead. diff --git a/src/config/Config.zig b/src/config/Config.zig index 2233cb4..d4852a3 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -37,6 +37,10 @@ cmatrix_max_codepoint: u16 = 0x7B, colormix_col1: u32 = 0x00FF0000, colormix_col2: u32 = 0x000000FF, colormix_col3: u32 = 0x20000000, +corner_bottom_left: []const u8 = "version", +corner_bottom_right: []const u8 = "labels", +corner_top_left: []const u8 = "keys", +corner_top_right: []const u8 = "clock tty", custom_bind_width: ?u32 = null, custom_sessions: []const u8 = build_options.config_directory ++ "/ly/custom-sessions", default_input: Input = .login, diff --git a/src/main.zig b/src/main.zig index bd9294c..22ea9e0 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1881,6 +1881,238 @@ fn updateSessionSpecifier(self: *Label, ptr: *anyopaque) !void { self.setText(env.environment.specifier); } +const Corner = enum { + corner_bottom_left, + corner_bottom_right, + corner_top_left, + corner_top_right, +}; + +const PositionedWidgets = struct { + binds: []bool, + labels: []bool, +}; + +fn positionItem(state: *UiState, item: []const u8, current_x: *usize, current_y: usize, is_left: bool, is_top: bool, positioned: *PositionedWidgets) !bool { + const base_x = state.edge_margin.x; + + if (std.mem.eql(u8, item, "keys")) { + if (state.config.hide_key_hints) return false; + + var local_x = current_x.*; + var local_y = current_y; + var lines_consumed: usize = 0; + + const labels = [_]?*Label{ + &state.shutdown_label, + &state.restart_label, + if (state.config.sleep_cmd != null) &state.sleep_label else null, + if (state.config.hibernate_cmd != null) &state.hibernate_label else null, + &state.toggle_password_label, + if (state.config.brightness_down_key != null) &state.brightness_down_label else null, + if (state.config.brightness_up_key != null) &state.brightness_up_label else null, + }; + + for (labels) |maybe_label| { + const label = maybe_label orelse continue; + const width = TerminalBuffer.strWidth(label.text); + + if (is_left) { + if (local_x + width > state.buffer.width - state.edge_margin.x) { + local_x = base_x; + if (is_top) local_y += 1 else local_y -= 1; + lines_consumed += 1; + } + label.positionXY(Position.init(local_x, local_y)); + local_x += width + 1; + } else { + if (width + state.edge_margin.x > local_x) { + local_x = state.buffer.width - state.edge_margin.x; + if (is_top) local_y += 1 else local_y -= 1; + lines_consumed += 1; + } + label.positionXY(Position.init(local_x - width, local_y)); + local_x -= width + 1; + } + } + current_x.* = local_x; + return true; + } else if (std.mem.eql(u8, item, "clock") or std.mem.eql(u8, item, "time")) { + if (state.config.clock == null) return false; + const width = TerminalBuffer.strWidth(state.clock_label.text); + if (is_left) { + state.clock_label.positionXY(Position.init(current_x.*, current_y)); + current_x.* += width + 1; + } else { + state.clock_label.positionXY(Position.init(current_x.* - width, current_y)); + current_x.* -= width + 1; + } + return true; + } else if (std.mem.eql(u8, item, "tty")) { + if (!state.config.show_tty) return false; + const width = TerminalBuffer.strWidth(state.tty_label.text); + if (is_left) { + state.tty_label.positionXY(Position.init(current_x.*, current_y)); + current_x.* += width + 1; + } else { + state.tty_label.positionXY(Position.init(current_x.* - width, current_y)); + current_x.* -= width + 1; + } + return true; + } else if (std.mem.eql(u8, item, "battery")) { + if (state.config.battery_id == null) return false; + const width = TerminalBuffer.strWidth(state.battery_label.text); + if (is_left) { + state.battery_label.positionXY(Position.init(current_x.*, current_y)); + current_x.* += width + 1; + } else { + state.battery_label.positionXY(Position.init(current_x.* - width, current_y)); + current_x.* -= width + 1; + } + return true; + } else if (std.mem.eql(u8, item, "version")) { + if (state.config.hide_version_string) return false; + const width = TerminalBuffer.strWidth(state.version_label.text); + if (is_left) { + state.version_label.positionXY(Position.init(current_x.*, current_y)); + current_x.* += width + 1; + } else { + state.version_label.positionXY(Position.init(current_x.* - width, current_y)); + current_x.* -= width + 1; + } + return true; + } else if (std.mem.eql(u8, item, "numlock")) { + if (state.config.hide_keyboard_locks) return false; + const width = TerminalBuffer.strWidth(state.lang.numlock); + if (is_left) { + state.numlock_label.positionXY(Position.init(current_x.*, current_y)); + current_x.* += width + 1; + } else { + state.numlock_label.positionXY(Position.init(current_x.* - width, current_y)); + current_x.* -= width + 1; + } + return true; + } else if (std.mem.eql(u8, item, "capslock")) { + if (state.config.hide_keyboard_locks) return false; + const width = TerminalBuffer.strWidth(state.lang.capslock); + if (is_left) { + state.capslock_label.positionXY(Position.init(current_x.*, current_y)); + current_x.* += width + 1; + } else { + state.capslock_label.positionXY(Position.init(current_x.* - width, current_y)); + current_x.* -= width + 1; + } + return true; + } else if (std.mem.eql(u8, item, "locks")) { + if (state.config.hide_keyboard_locks) return false; + const num_w = TerminalBuffer.strWidth(state.lang.numlock); + const caps_w = TerminalBuffer.strWidth(state.lang.capslock); + if (is_left) { + state.numlock_label.positionXY(Position.init(current_x.*, current_y)); + state.capslock_label.positionXY(Position.init(current_x.* + num_w + 1, current_y)); + current_x.* += num_w + 1 + caps_w + 1; + } else { + state.capslock_label.positionXY(Position.init(current_x.* - caps_w, current_y)); + state.numlock_label.positionXY(Position.init(current_x.* - caps_w - 1 - num_w, current_y)); + current_x.* -= caps_w + 1 + num_w + 1; + } + return true; + } else if (std.mem.eql(u8, item, "labels")) { + for (state.custom_info.items, 0..) |*info, i| { + if (positioned.labels[i]) continue; + const width = TerminalBuffer.strWidth(info.lbl.text); + if (is_left) { + info.lbl.positionXY(Position.init(current_x.*, current_y)); + current_x.* += width + 1; + } else { + info.lbl.positionXY(Position.init(current_x.* - width, current_y)); + current_x.* -= width + 1; + } + positioned.labels[i] = true; + } + return true; + } else if (std.mem.eql(u8, item, "binds")) { + var local_x = current_x.*; + var local_y = current_y; + + for (state.custom_binds.items, 0..) |*bind, i| { + if (positioned.binds[i]) continue; + const width = TerminalBuffer.strWidth(bind.lbl.text); + + if (is_left) { + if (local_x + width > (state.config.custom_bind_width orelse state.buffer.width) - state.edge_margin.x) { + local_x = base_x; + if (is_top) local_y += 1 else local_y -= 1; + } + bind.lbl.positionXY(Position.init(local_x, local_y)); + local_x += width + 1; + } else { + if (width + state.edge_margin.x > local_x) { + local_x = state.buffer.width - state.edge_margin.x; + if (is_top) local_y += 1 else local_y -= 1; + } + bind.lbl.positionXY(Position.init(local_x - width, local_y)); + local_x -= width + 1; + } + positioned.binds[i] = true; + } + current_x.* = local_x; + return true; + } else if (std.mem.startsWith(u8, item, "lbl:")) { + const name = item[4..]; + for (state.custom_info.items, 0..) |*info, i| { + if (std.mem.eql(u8, info.info.name, name)) { + if (positioned.labels[i]) return false; + const width = TerminalBuffer.strWidth(info.lbl.text); + if (is_left) { + info.lbl.positionXY(Position.init(current_x.*, current_y)); + current_x.* += width + 1; + } else { + info.lbl.positionXY(Position.init(current_x.* - width, current_y)); + current_x.* -= width + 1; + } + positioned.labels[i] = true; + return true; + } + } + } else if (std.mem.startsWith(u8, item, "cmd:")) { + const key = item[4..]; + for (state.custom_binds.items, 0..) |*bind, i| { + if (std.mem.eql(u8, bind.key, key)) { + if (positioned.binds[i]) return false; + const width = TerminalBuffer.strWidth(bind.lbl.text); + if (is_left) { + bind.lbl.positionXY(Position.init(current_x.*, current_y)); + current_x.* += width + 1; + } else { + bind.lbl.positionXY(Position.init(current_x.* - width, current_y)); + current_x.* -= width + 1; + } + positioned.binds[i] = true; + return true; + } + } + } + return false; +} + +fn positionCorner(state: *UiState, config_str: []const u8, corner: Corner, positioned: *PositionedWidgets) !void { + const is_left = corner == .corner_top_left or corner == .corner_bottom_left; + const is_top = corner == .corner_top_right or corner == .corner_bottom_right; + + var y_offset: usize = 0; + var it = std.mem.tokenizeAny(u8, config_str, " []\""); + + while (it.next()) |item| { + var current_x = if (is_left) state.edge_margin.x else state.buffer.width - state.edge_margin.x; + const current_y = if (is_top) state.edge_margin.y + y_offset else state.buffer.height - 1 - state.edge_margin.y - y_offset; + + if (try positionItem(state, item, corner, ¤t_x, current_y, is_left, is_top, positioned)) { + y_offset += 1; + } + } +} + fn positionWidgets(ptr: *anyopaque) !void { var state: *UiState = @ptrCast(@alignCast(ptr)); From 0867ed85d3a2c8317300f2019d468b618cda312b Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 18 Apr 2026 15:00:47 +0200 Subject: [PATCH 2/2] feature: more flexibility in widget positioning --- res/config.ini | 9 +- src/main.zig | 262 +++++++++++++++++++++++++++++++------------------ 2 files changed, 168 insertions(+), 103 deletions(-) diff --git a/res/config.ini b/res/config.ini index 5ac5df7..340c93d 100644 --- a/res/config.ini +++ b/res/config.ini @@ -152,7 +152,6 @@ colormix_col3 = 0x20000000 # version -> Ly version string # numlock -> Numlock state # capslock -> Capslock state -# locks -> Both numlock and capslock # labels -> All custom info labels (lbl:) # binds -> All custom keybind hints (cmd:) # lbl:name -> Specific custom info label @@ -160,16 +159,16 @@ colormix_col3 = 0x20000000 # # The order defines the vertical stack (first item is at the edge). -# Top left corner configuration +# Top left corner_top_left = keys -# Top right corner configuration +# Top right corner_top_right = clock tty -# Bottom left corner configuration +# Bottom left corner_bottom_left = version -# Bottom right corner configuration +# Bottom right corner_bottom_right = labels # For custom binds: the horizontal limit in characters for each diff --git a/src/main.zig b/src/main.zig index 22ea9e0..0989366 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1891,9 +1891,16 @@ const Corner = enum { const PositionedWidgets = struct { binds: []bool, labels: []bool, + keys: bool = false, + clock: bool = false, + tty: bool = false, + battery: bool = false, + version: bool = false, + numlock: bool = false, + capslock: bool = false, }; -fn positionItem(state: *UiState, item: []const u8, current_x: *usize, current_y: usize, is_left: bool, is_top: bool, positioned: *PositionedWidgets) !bool { +fn positionSingleWidget(state: *UiState, item: []const u8, current_x: *usize, current_y: usize, is_left: bool, is_top: bool, positioned: *PositionedWidgets) !bool { const base_x = state.edge_margin.x; if (std.mem.eql(u8, item, "keys")) { @@ -1936,6 +1943,7 @@ fn positionItem(state: *UiState, item: []const u8, current_x: *usize, current_y: } } current_x.* = local_x; + positioned.keys = true; return true; } else if (std.mem.eql(u8, item, "clock") or std.mem.eql(u8, item, "time")) { if (state.config.clock == null) return false; @@ -1947,6 +1955,7 @@ fn positionItem(state: *UiState, item: []const u8, current_x: *usize, current_y: state.clock_label.positionXY(Position.init(current_x.* - width, current_y)); current_x.* -= width + 1; } + positioned.clock = true; return true; } else if (std.mem.eql(u8, item, "tty")) { if (!state.config.show_tty) return false; @@ -1958,6 +1967,7 @@ fn positionItem(state: *UiState, item: []const u8, current_x: *usize, current_y: state.tty_label.positionXY(Position.init(current_x.* - width, current_y)); current_x.* -= width + 1; } + positioned.tty = true; return true; } else if (std.mem.eql(u8, item, "battery")) { if (state.config.battery_id == null) return false; @@ -1969,6 +1979,7 @@ fn positionItem(state: *UiState, item: []const u8, current_x: *usize, current_y: state.battery_label.positionXY(Position.init(current_x.* - width, current_y)); current_x.* -= width + 1; } + positioned.battery = true; return true; } else if (std.mem.eql(u8, item, "version")) { if (state.config.hide_version_string) return false; @@ -1980,6 +1991,7 @@ fn positionItem(state: *UiState, item: []const u8, current_x: *usize, current_y: state.version_label.positionXY(Position.init(current_x.* - width, current_y)); current_x.* -= width + 1; } + positioned.version = true; return true; } else if (std.mem.eql(u8, item, "numlock")) { if (state.config.hide_keyboard_locks) return false; @@ -1991,6 +2003,7 @@ fn positionItem(state: *UiState, item: []const u8, current_x: *usize, current_y: state.numlock_label.positionXY(Position.init(current_x.* - width, current_y)); current_x.* -= width + 1; } + positioned.numlock = true; return true; } else if (std.mem.eql(u8, item, "capslock")) { if (state.config.hide_keyboard_locks) return false; @@ -2002,20 +2015,7 @@ fn positionItem(state: *UiState, item: []const u8, current_x: *usize, current_y: state.capslock_label.positionXY(Position.init(current_x.* - width, current_y)); current_x.* -= width + 1; } - return true; - } else if (std.mem.eql(u8, item, "locks")) { - if (state.config.hide_keyboard_locks) return false; - const num_w = TerminalBuffer.strWidth(state.lang.numlock); - const caps_w = TerminalBuffer.strWidth(state.lang.capslock); - if (is_left) { - state.numlock_label.positionXY(Position.init(current_x.*, current_y)); - state.capslock_label.positionXY(Position.init(current_x.* + num_w + 1, current_y)); - current_x.* += num_w + 1 + caps_w + 1; - } else { - state.capslock_label.positionXY(Position.init(current_x.* - caps_w, current_y)); - state.numlock_label.positionXY(Position.init(current_x.* - caps_w - 1 - num_w, current_y)); - current_x.* -= caps_w + 1 + num_w + 1; - } + positioned.capslock = true; return true; } else if (std.mem.eql(u8, item, "labels")) { for (state.custom_info.items, 0..) |*info, i| { @@ -2096,18 +2096,130 @@ fn positionItem(state: *UiState, item: []const u8, current_x: *usize, current_y: return false; } +fn positionItem(state: *UiState, item: []const u8, current_x: *usize, current_y: usize, is_left: bool, is_top: bool, positioned: *PositionedWidgets) !bool { + // Check if item contains a comma for compound widgets + if (std.mem.indexOf(u8, item, ",") != null) { + var it = std.mem.tokenizeAny(u8, item, ","); + var success = false; + + // First, collect all subitems that will be positioned + var subitems: [8][]const u8 = undefined; + var count: usize = 0; + + while (it.next()) |subitem| { + const trimmed = std.mem.trim(u8, subitem, " "); + if (count < subitems.len) { + subitems[count] = trimmed; + count += 1; + } + } + + if (count == 0) return false; + + // Check if we can fit all subitems on this line + var total_width: usize = 0; + for (subitems[0..count]) |subitem| { + // Get width without positioning + const width = try getWidgetWidth(state, subitem); + if (width == 0) continue; + total_width += width + 1; // +1 for spacing + } + if (total_width > 0) total_width -= 1; // Remove last spacing + + // Check if we need to wrap to next line + var line_x = current_x.*; + + // Now position each subitem + for (subitems[0..count]) |subitem| { + if (try positionSingleWidget(state, subitem, &line_x, current_y, is_left, is_top, positioned)) { + success = true; + } + } + + if (success) { + current_x.* = line_x; + } + return success; + } else { + return positionSingleWidget(state, item, current_x, current_y, is_left, is_top, positioned); + } +} + +// Helper function to get widget width without positioning +fn getWidgetWidth(state: *UiState, item: []const u8) !usize { + if (std.mem.eql(u8, item, "keys")) { + if (state.config.hide_key_hints) return 0; + var total: usize = 0; + const labels = [_]?*Label{ + &state.shutdown_label, + &state.restart_label, + if (state.config.sleep_cmd != null) &state.sleep_label else null, + if (state.config.hibernate_cmd != null) &state.hibernate_label else null, + &state.toggle_password_label, + if (state.config.brightness_down_key != null) &state.brightness_down_label else null, + if (state.config.brightness_up_key != null) &state.brightness_up_label else null, + }; + for (labels) |maybe_label| { + const label = maybe_label orelse continue; + total += TerminalBuffer.strWidth(label.text) + 1; + } + return if (total > 0) total - 1 else 0; + } else if (std.mem.eql(u8, item, "numlock")) { + if (state.config.hide_keyboard_locks) return 0; + return TerminalBuffer.strWidth(state.lang.numlock); + } else if (std.mem.eql(u8, item, "capslock")) { + if (state.config.hide_keyboard_locks) return 0; + return TerminalBuffer.strWidth(state.lang.capslock); + } else if (std.mem.eql(u8, item, "clock") or std.mem.eql(u8, item, "time")) { + if (state.config.clock == null) return 0; + return TerminalBuffer.strWidth(state.clock_label.text); + } else if (std.mem.eql(u8, item, "tty")) { + if (!state.config.show_tty) return 0; + return TerminalBuffer.strWidth(state.tty_label.text); + } else if (std.mem.eql(u8, item, "battery")) { + if (state.config.battery_id == null) return 0; + return TerminalBuffer.strWidth(state.battery_label.text); + } else if (std.mem.eql(u8, item, "version")) { + if (state.config.hide_version_string) return 0; + return TerminalBuffer.strWidth(state.version_label.text); + } + return 0; +} + fn positionCorner(state: *UiState, config_str: []const u8, corner: Corner, positioned: *PositionedWidgets) !void { const is_left = corner == .corner_top_left or corner == .corner_bottom_left; - const is_top = corner == .corner_top_right or corner == .corner_bottom_right; + const is_top = corner == .corner_top_left or corner == .corner_top_right; var y_offset: usize = 0; - var it = std.mem.tokenizeAny(u8, config_str, " []\""); + var i: usize = 0; + const len = config_str.len; + + while (i < len) { + // Skip whitespace and brackets + while (i < len and (config_str[i] == ' ' or config_str[i] == '\t' or config_str[i] == '[' or config_str[i] == ']')) { + i += 1; + } + if (i >= len) break; + + var token: []const u8 = undefined; + if (config_str[i] == '"') { + i += 1; + const start = i; + while (i < len and config_str[i] != '"') i += 1; + token = config_str[start..i]; + if (i < len) i += 1; // skip closing quote + } else { + const start = i; + while (i < len and config_str[i] != ' ' and config_str[i] != '\t' and config_str[i] != '[' and config_str[i] != ']') { + i += 1; + } + token = config_str[start..i]; + } - while (it.next()) |item| { var current_x = if (is_left) state.edge_margin.x else state.buffer.width - state.edge_margin.x; const current_y = if (is_top) state.edge_margin.y + y_offset else state.buffer.height - 1 - state.edge_margin.y - y_offset; - if (try positionItem(state, item, corner, ¤t_x, current_y, is_left, is_top, positioned)) { + if (try positionItem(state, token, ¤t_x, current_y, is_left, is_top, positioned)) { y_offset += 1; } } @@ -2116,86 +2228,44 @@ fn positionCorner(state: *UiState, config_str: []const u8, corner: Corner, posit fn positionWidgets(ptr: *anyopaque) !void { var state: *UiState = @ptrCast(@alignCast(ptr)); - if (!state.config.hide_key_hints) { - state.shutdown_label.positionX(state.edge_margin - .add(TerminalBuffer.START_POSITION)); - var last_label = state.shutdown_label; - state.restart_label.positionX(last_label - .childrenPosition() - .addX(1)); - last_label = state.restart_label; - state.sleep_label.positionX(last_label - .childrenPosition() - .addX(1)); - if (state.config.sleep_cmd != null) { - last_label = state.sleep_label; - } - state.hibernate_label.positionX(last_label - .childrenPosition() - .addX(1)); - if (state.config.hibernate_cmd != null) { - last_label = state.hibernate_label; - } - state.toggle_password_label.positionX(last_label - .childrenPosition() - .addX(1)); - last_label = state.toggle_password_label; - state.brightness_down_label.positionX(last_label - .childrenPosition() - .addX(1)); - if (state.config.brightness_down_key != null) { - last_label = state.brightness_down_label; - } - state.brightness_up_label.positionXY(last_label - .childrenPosition() - .addX(1)); - var x_offset: usize = 0; - var y_offset: usize = 1; - for (state.custom_binds.items) |*item| { - item.lbl.positionXY(state.edge_margin - .addY(y_offset) - .addX(x_offset)); - x_offset += item.lbl.text.len + 1; - if (x_offset + item.lbl.text.len > state.config.custom_bind_width orelse state.buffer.width) { - x_offset = 0; - y_offset += 1; - } - } + const offscreen = Position.init(9999, 9999); + + // Reset all potential corner widgets to offscreen + state.shutdown_label.positionXY(offscreen); + state.restart_label.positionXY(offscreen); + state.sleep_label.positionXY(offscreen); + state.hibernate_label.positionXY(offscreen); + state.toggle_password_label.positionXY(offscreen); + state.brightness_down_label.positionXY(offscreen); + state.brightness_up_label.positionXY(offscreen); + state.clock_label.positionXY(offscreen); + state.tty_label.positionXY(offscreen); + state.battery_label.positionXY(offscreen); + state.version_label.positionXY(offscreen); + state.numlock_label.positionXY(offscreen); + state.capslock_label.positionXY(offscreen); + + for (state.custom_binds.items) |*bind| { + bind.lbl.positionXY(offscreen); } - for (state.custom_info.items, 0..) |*item, i| { - item.lbl.positionXY(state.edge_margin - .invertX(state.buffer.width) - .removeX(item.lbl.text.len) - .invertY(state.buffer.height) - .removeY(state.custom_info.items.len) - .addY(i)); + for (state.custom_info.items) |*info| { + info.lbl.positionXY(offscreen); } - state.battery_label.positionXY(state.edge_margin - .add(TerminalBuffer.START_POSITION) - .addYFromIf(state.brightness_up_label.childrenPosition(), !state.config.hide_key_hints) - .removeYFromIf(state.edge_margin, !state.config.hide_key_hints)); + var positioned = PositionedWidgets{ + .binds = try state.allocator.alloc(bool, state.custom_binds.items.len), + .labels = try state.allocator.alloc(bool, state.custom_info.items.len), + }; + defer state.allocator.free(positioned.binds); + defer state.allocator.free(positioned.labels); - const tty_label_width = if (state.config.show_tty) TerminalBuffer.strWidth(state.tty_label.text) else 0; - const tty_label_gap = if (state.config.show_tty and state.config.clock != null) @as(usize, 1) else 0; - state.tty_label.positionXY(state.edge_margin - .add(TerminalBuffer.START_POSITION) - .invertX(state.buffer.width) - .removeXIf(tty_label_width, state.buffer.width > tty_label_width + state.edge_margin.x)); - state.clock_label.positionXY(state.edge_margin - .add(TerminalBuffer.START_POSITION) - .invertX(state.buffer.width) - .removeXIf(TerminalBuffer.strWidth(state.clock_label.text) + tty_label_width + tty_label_gap, state.buffer.width > TerminalBuffer.strWidth(state.clock_label.text) + tty_label_width + tty_label_gap + state.edge_margin.x)); + @memset(positioned.binds, false); + @memset(positioned.labels, false); - 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(TerminalBuffer.strWidth(state.lang.numlock), state.buffer.width > TerminalBuffer.strWidth(state.lang.numlock) + state.edge_margin.x)); - state.capslock_label.positionX(state.numlock_label - .childrenPosition() - .removeX(TerminalBuffer.strWidth(state.lang.numlock) + TerminalBuffer.strWidth(state.lang.capslock) + 1)); + try positionCorner(state, state.config.corner_top_left, .corner_top_left, &positioned); + try positionCorner(state, state.config.corner_top_right, .corner_top_right, &positioned); + try positionCorner(state, state.config.corner_bottom_left, .corner_bottom_left, &positioned); + try positionCorner(state, state.config.corner_bottom_right, .corner_bottom_right, &positioned); state.box.positionXY(TerminalBuffer.START_POSITION .addX((state.buffer.width - @min(state.buffer.width - 2, state.box.width)) / 2) @@ -2238,10 +2308,6 @@ fn positionWidgets(ptr: *anyopaque) !void { state.password.positionY(state.password_label .childrenPosition() .addX(state.labels_max_length - TerminalBuffer.strWidth(state.password_label.text) + 1)); - - state.version_label.positionXY(state.edge_margin - .add(TerminalBuffer.START_POSITION) - .invertY(state.buffer.height - 1)); } fn handleInactivity(ptr: *anyopaque) !void {