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.
This commit is contained in:
jR4dh3y 2026-07-10 21:59:14 +05:30
commit 625ffa14ee
4 changed files with 81 additions and 1 deletions

View file

@ -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(

View file

@ -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

View file

@ -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,

View file

@ -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);
}