From 625ffa14ee308938f9a0b04c47bc234e60976f7a Mon Sep 17 00:00:00 2001 From: jR4dh3y Date: Fri, 10 Jul 2026 21:59:14 +0530 Subject: [PATCH] Add optional bigclock digit outline (bigclock_outline_fg) When set, each big-clock glyph gets a 1-cell outline drawn under the digit so it stays readable over busy animations. Default is null (no outline, previous behavior). Also leave a bit more space under the clock when the outline is enabled so the stroke is not glued to the login box. --- ly-ui/src/components/BigLabel.zig | 71 +++++++++++++++++++++++++++++++ res/config.ini | 5 +++ src/config/Config.zig | 1 + src/main.zig | 5 ++- 4 files changed, 81 insertions(+), 1 deletion(-) diff --git a/ly-ui/src/components/BigLabel.zig b/ly-ui/src/components/BigLabel.zig index cb2f6b5..3420c71 100644 --- a/ly-ui/src/components/BigLabel.zig +++ b/ly-ui/src/components/BigLabel.zig @@ -51,6 +51,8 @@ text: []const u8, max_width: ?usize, fg: u32, bg: u32, +/// When set, draw a 1-cell outline around each digit glyph. +outline_fg: ?u32 = null, locale: BigLabelLocale, update_fn: ?*const fn (*BigLabel, *anyopaque) anyerror!void, calculate_timeout_fn: ?*const fn (*BigLabel, *anyopaque) anyerror!?usize, @@ -63,6 +65,7 @@ pub fn init( max_width: ?usize, fg: u32, bg: u32, + outline_fg: ?u32, locale: BigLabelLocale, update_fn: ?*const fn (*BigLabel, *anyopaque) anyerror!void, calculate_timeout_fn: ?*const fn (*BigLabel, *anyopaque) anyerror!?usize, @@ -75,6 +78,7 @@ pub fn init( .max_width = max_width, .fg = fg, .bg = bg, + .outline_fg = outline_fg, .locale = locale, .update_fn = update_fn, .calculate_timeout_fn = calculate_timeout_fn, @@ -151,6 +155,21 @@ pub fn childrenPosition(self: BigLabel) Position { } fn draw(self: *BigLabel) void { + if (self.text.len == 0) return; + + // Optional outline under the glyphs (bigclock_outline_fg). + if (self.outline_fg) |outline_fg| { + for (self.text, 0..) |c, i| { + drawDigitOutline( + self, + c, + self.component_pos.x + i * (CHAR_WIDTH + 1), + self.component_pos.y, + outline_fg, + ); + } + } + for (self.text, 0..) |c, i| { const clock_cell = clockCell( c, @@ -169,6 +188,58 @@ fn draw(self: *BigLabel) void { } } +/// 1-cell outline around each solid cell of a big-clock digit. +fn drawDigitOutline( + self: *BigLabel, + char: u8, + base_x: usize, + base_y: usize, + outline_fg: u32, +) void { + const pattern = toBigNumber(char, self.locale); + const stroke = Cell.init(X, outline_fg, 0x00000000); + + var yy: usize = 0; + while (yy < CHAR_HEIGHT) : (yy += 1) { + var xx: usize = 0; + while (xx < CHAR_WIDTH) : (xx += 1) { + if (pattern[yy * CHAR_WIDTH + xx] == O) continue; + + // 8-neighbor outline. Use i8 (not i2) — i2 overflows past 1. + var dy: i8 = -1; + while (dy <= 1) : (dy += 1) { + var dx: i8 = -1; + while (dx <= 1) : (dx += 1) { + if (dx == 0 and dy == 0) continue; + const nx: i32 = @as(i32, @intCast(xx)) + dx; + const ny: i32 = @as(i32, @intCast(yy)) + dy; + if (nx < -1 or ny < -1) continue; + if (nx > CHAR_WIDTH or ny > CHAR_HEIGHT) continue; + + if (nx >= 0 and ny >= 0 and + nx < CHAR_WIDTH and ny < CHAR_HEIGHT and + pattern[ + @as(usize, @intCast(ny)) * CHAR_WIDTH + + @as(usize, @intCast(nx)) + ] != O) + { + continue; + } + + const sx: i32 = @as(i32, @intCast(base_x)) + nx; + const sy: i32 = @as(i32, @intCast(base_y)) + ny; + if (sx < 0 or sy < 0) continue; + const ux: usize = @intCast(sx); + const uy: usize = @intCast(sy); + if (ux >= self.buffer.width or uy >= self.buffer.height) + continue; + stroke.put(ux, uy) catch {}; + } + } + } + } +} + fn update(self: *BigLabel, context: *anyopaque) !void { if (self.update_fn) |update_fn| { return @call( diff --git a/res/config.ini b/res/config.ini index 2bd2624..7b214e8 100644 --- a/res/config.ini +++ b/res/config.ini @@ -92,6 +92,11 @@ bigclock_12hr = false # Set bigclock to show the seconds. bigclock_seconds = false +# Optional 1-cell outline around each big-clock digit (null = none). +# Useful over busy animations so the glyphs stay readable. +# Example black: 0x20000000 (TB_HI_BLACK) +bigclock_outline_fg = null + # Blank main box background # Setting to false will make it transparent blank_box = true diff --git a/src/config/Config.zig b/src/config/Config.zig index 2ba8444..2f90a8b 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -21,6 +21,7 @@ bg: u32 = 0x00000000, bigclock: Bigclock = .none, bigclock_12hr: bool = false, bigclock_seconds: bool = false, +bigclock_outline_fg: ?u32 = null, blank_box: bool = true, border_fg: u32 = 0x00FFFFFF, box_position_h: f32 = 0.5, diff --git a/src/main.zig b/src/main.zig index 31a8519..b747236 100644 --- a/src/main.zig +++ b/src/main.zig @@ -556,6 +556,7 @@ pub fn main(init: std.process.Init) !void { null, state.buffer.fg, state.buffer.bg, + state.config.bigclock_outline_fg, switch (state.config.bigclock) { .none, .en => .en, .fa => .fa, @@ -2255,7 +2256,9 @@ fn positionWidgets(ptr: *anyopaque) !void { const clock_text_len = TerminalBuffer.strWidth(state.bigclock_label.text) * (BigLabel.CHAR_WIDTH + 1); if (state.config.bigclock != .none) { - bb_height += BigLabel.CHAR_HEIGHT + 2; + // Extra row when outline is on: stroke can extend 1 cell below glyphs. + const gap: usize = if (state.config.bigclock_outline_fg != null) 3 else 2; + bb_height += BigLabel.CHAR_HEIGHT + gap; bb_width = @max(bb_width, clock_text_len); }