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
+85
View File
@@ -3,6 +3,7 @@ const testing = std.testing;
const act = @import("action");
const input = @import("input");
const font = @import("font.zig");
const layout = @import("layout.zig");
const Box = layout.Box;
@@ -422,3 +423,87 @@ test "map_to_output merges like any other setting" {
const moved = merged.merge(.{ .map_to_output = "DP-2" });
try testing.expectEqualStrings("DP-2", moved.map_to_output.?);
}
// ─── tab titles ──────────────────────────────────────────────────────────────
test "a title that fits is drawn whole" {
const f = font.fit("foot", 10);
try testing.expectEqual(@as(usize, 4), f.end);
try testing.expectEqual(@as(usize, 4), f.cells);
try testing.expect(!f.elided);
// Exactly full is still whole: the ellipsis costs a cell, so only spend it
// when something is actually cut.
const exact = font.fit("foot", 4);
try testing.expectEqual(@as(usize, 4), exact.cells);
try testing.expect(!exact.elided);
}
test "a title too long for the tab loses its last cell to the ellipsis" {
const f = font.fit("firefox", 4);
try testing.expectEqualStrings("fir", "firefox"[0..f.end]);
try testing.expectEqual(@as(usize, 3), f.cells);
try testing.expect(f.elided);
// One cell of room leaves nothing but the ellipsis, and no room at all
// draws nothing rather than indexing past the end.
const one = font.fit("firefox", 1);
try testing.expectEqual(@as(usize, 0), one.end);
try testing.expect(one.elided);
const none = font.fit("firefox", 0);
try testing.expectEqual(@as(usize, 0), none.end);
try testing.expect(!none.elided);
}
test "titles are measured in characters, not bytes" {
// Three codepoints, six bytes: a tab four cells wide holds them all.
const f = font.fit("ünïx", 4);
try testing.expectEqual(@as(usize, 4), f.cells);
try testing.expect(!f.elided);
// And a cut lands on a codepoint boundary, never mid-sequence.
const cut = font.fit("ünïx", 3);
try testing.expect(cut.elided);
try testing.expectEqualStrings("ün", "ünïx"[0..cut.end]);
}
test "malformed utf-8 in a title yields one glyph per bad byte" {
var it: font.Iterator = .{ .bytes = "a\xffb\xc3" };
try testing.expectEqual(@as(?u21, 'a'), it.next());
try testing.expectEqual(@as(?u21, 0xfffd), it.next());
try testing.expectEqual(@as(?u21, 'b'), it.next());
// A truncated sequence at the end must terminate the walk, not loop.
try testing.expectEqual(@as(?u21, 0xfffd), it.next());
try testing.expectEqual(@as(?u21, null), it.next());
}
test "text measuring and cell fitting agree" {
const scale = 2;
// Whatever cellsForWidth says fits must actually fit.
var width: i32 = 0;
while (width < 200) : (width += 1) {
const cells = font.cellsForWidth(width, scale);
try testing.expect(font.textWidth(cells, scale) <= width);
try testing.expect(font.textWidth(cells + 1, scale) > width);
}
}
test "printable ascii all have glyphs, and unknown codepoints fall back" {
// The space is blank, and nothing else printable is.
var c: u8 = 0x21;
while (c <= 0x7e) : (c += 1) {
const rows = font.glyph(c);
var lit = false;
for (rows) |r| lit = lit or r != 0;
try testing.expect(lit);
// Nothing may spill outside the five column cell.
for (rows) |r| try testing.expectEqual(@as(u8, 0), r & ~@as(u8, 0b11111));
}
for (font.glyph(' ')) |r| try testing.expectEqual(@as(u8, 0), r);
// Control characters stay blank; other codepoints get the fallback box.
for (font.glyph('\n')) |r| try testing.expectEqual(@as(u8, 0), r);
try testing.expect(!std.mem.eql(u8, font.glyph('?'), font.glyph('あ')));
try testing.expectEqualSlices(u8, font.glyph(0xfffd), font.glyph('あ'));
}