feature: add corner customization

This commit is contained in:
Luna 2026-04-16 22:32:20 +02:00 committed by AnErrupTion
commit 031815b3cc
No known key found for this signature in database
3 changed files with 265 additions and 0 deletions

View file

@ -153,6 +153,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.

View file

@ -39,6 +39,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,

View file

@ -2062,6 +2062,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, &current_x, current_y, is_left, is_top, positioned)) {
y_offset += 1;
}
}
}
fn positionWidgets(ptr: *anyopaque) !void {
var state: *UiState = @ptrCast(@alignCast(ptr));