show names on tabs in tabbed layout

This commit is contained in:
2026-08-01 14:19:24 +02:00
parent 8d99d9171c
commit 4a08d19365
7 changed files with 457 additions and 12 deletions
+51 -3
View File
@@ -21,6 +21,7 @@ const Wm = @import("Wm.zig");
const Window = @import("Window.zig");
const shm = @import("shm.zig");
const color = @import("color.zig");
const font = @import("font.zig");
const layout = @import("layout.zig");
const Box = layout.Box;
@@ -157,9 +158,10 @@ fn onLayerEvent(_: *river.LayerShellOutputV1, event: river.LayerShellOutputV1.Ev
}
}
/// The strip of solid colour blocks drawn above the windows in the tabbed
/// layout. One block per window, the focused one highlighted; titles are not
/// drawn here but are published over IPC for bars that want to render them.
/// The strip of tabs drawn above the windows in the tabbed layout. One tab per
/// window, labelled with its title and the focused one highlighted. The same
/// titles are published over IPC, for bars that would rather draw the tabs
/// themselves with a real font.
pub const TabBar = struct {
wm: *Wm,
@@ -240,6 +242,7 @@ pub const TabBar = struct {
const sep = color.toArgb8888(config.tab_separator);
buffer.fill(local, sep);
const scale = textScale(box.height);
for (self.rects.items, windows) |rect, win| {
const is_focused = focused != null and focused.? == win;
const argb = color.toArgb8888(if (is_focused) config.tab_focused else config.tab_normal);
@@ -252,6 +255,9 @@ pub const TabBar = struct {
.height = rect.height,
};
buffer.fill(inner, argb);
const ink = color.toArgb8888(if (is_focused) config.tab_text_focused else config.tab_text_normal);
drawLabel(buffer, inner, label(win), scale, ink);
}
// Translate the hit rectangles into global coordinates for click
@@ -286,6 +292,48 @@ pub const TabBar = struct {
self.windows.clearRetainingCapacity();
}
/// What to write on a window's tab: its title, or its app id while it has
/// not set one, so that a tab is never blank.
fn label(win: *const Window) []const u8 {
if (win.title) |t| {
if (t.len > 0) return t;
}
if (win.app_id) |a| {
if (a.len > 0) return a;
}
return "?";
}
/// Whole-pixel scale for the font: the largest that leaves a little air
/// above and below in a bar this tall. `config.tab_font_scale` overrides
/// it for anyone wanting big letters in a thin bar, or the reverse.
fn textScale(height: i32) i32 {
if (config.tab_font_scale > 0) return config.tab_font_scale;
var s: i32 = 1;
while (s < 4 and font.cell_height * (s + 1) + 4 <= height) s += 1;
return s;
}
/// Centre a title in one tab, eliding it with an ellipsis when the tab is
/// too narrow to hold it.
fn drawLabel(buffer: *shm.Buffer, tab: Box, text: []const u8, scale: i32, argb: u32) void {
const pad = 2 * scale;
const avail = tab.width - 2 * pad;
if (avail <= 0) return;
const f = font.fit(text, font.cellsForWidth(avail, scale));
const cells = f.cells + @intFromBool(f.elided);
if (cells == 0) return;
const x = tab.x + @divFloor(tab.width - font.textWidth(cells, scale), 2);
// Centre the part above the baseline; the descender row may hang into
// the padding, which is what it is for.
const y = tab.y + @divFloor(tab.height - (font.cell_height - 1) * scale, 2);
const pen = buffer.drawText(text[0..f.end], x, y, scale, argb, tab);
if (f.elided) buffer.drawGlyph(font.ellipsis, pen, y, scale, argb, tab);
}
/// Which window's tab covers this global coordinate, if any.
pub fn windowAt(self: *const TabBar, x: i32, y: i32) ?*Window {
if (!self.mapped) return null;