summaryrefslogtreecommitdiff
path: root/test/panes.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-15 16:28:57 -0300
committerGabriel Schneider <[email protected]>2026-09-15 17:24:42 -0300
commit1ab4c071be58033d5452f69797ca8e2c32240cff (patch)
treeafa108a1c372276da1d2c62b6c05d8f167cc81b8 /test/panes.zig
parentc6ab9db0fdbcb8d16f666749194bb111218b96a8 (diff)
downloadpardes-1ab4c071be58033d5452f69797ca8e2c32240cff.tar.gz
pardes-1ab4c071be58033d5452f69797ca8e2c32240cff.zip
Separate tag text geometry from physical pane grips
Diffstat (limited to 'test/panes.zig')
-rw-r--r--test/panes.zig145
1 files changed, 145 insertions, 0 deletions
diff --git a/test/panes.zig b/test/panes.zig
index bf8a98a2..71127ea3 100644
--- a/test/panes.zig
+++ b/test/panes.zig
@@ -12,6 +12,150 @@ const image = pardes.image;
const syntax = pardes.syntax;
const filesystem = pardes.filesystem;
+const TagLayerTests = struct {
+ const Kind = @FieldType(pardes.TagLayer, "kind");
+ fn findLayer(p: *Pardes, kind: Kind, id: usize) !*pardes.TagLayer {
+ for (&p.surface.tag_layers) |*layer| {
+ if (layer.cols > 0 and layer.kind == kind and layer.id == id) return layer;
+ }
+ return error.MissingTagLayer;
+ }
+
+ fn mouse(layer: *const pardes.TagLayer, x: f32, kind: pardes.Mouse.Kind) pardes.Mouse {
+ const y = (@as(f32, @floatFromInt(layer.viewport.y)) + 0.5) * 20;
+ return .{ .button = .left, .kind = kind, .col = @intFromFloat(x / 10), .row = @intFromFloat(y / 20), .tag_hit = layer.hitAt(x, y, 10, 20, 5, 10) };
+ }
+
+ test "compact tag layers render and edit Unicode text beyond body column capacity" {
+ for ([_]bool{ false, true }) |bottom| {
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 40, .rows = 16 });
+ defer p.deinit();
+ const pane = try p.setTestFile("body\n");
+ p.presentation.enabled = false;
+ p.settings.tag_bottom = bottom;
+ p.row_metrics = .{ .body_w = 10, .body_h = 20, .tagline_w = 5, .tagline_h = 10 };
+ p.gpa.free(pane.file.?.path);
+ pane.file.?.path = try p.gpa.dupe(u8, "/x");
+ const tail = " " ++ "a" ** 38 ++ "\t界 END";
+ @memcpy(pane.tag_tail[0..tail.len], tail);
+ pane.tag_tail_len = tail.len;
+ pane.tag_init = true;
+ var frame = std.heap.ArenaAllocator.init(p.gpa);
+ defer frame.deinit();
+ _ = try p.render(frame.allocator());
+ const layer = try findLayer(p, .pane, 0);
+ const text = try p.tagText(frame.allocator(), pane);
+ const at = std.mem.indexOf(u8, text, "END").?;
+ const col = panes.File.displayWidth(text[0..at]);
+ try std.testing.expect(col > p.rects[0].w);
+ try std.testing.expect(col + 3 < layer.cols);
+ try std.testing.expectEqualStrings("E", layer.cells[col].grapheme());
+ try std.testing.expectEqual(@as(u16, 0), pane.tag_scroll);
+ const x = @as(f32, @floatFromInt(layer.viewport.x)) * 10 + (@as(f32, @floatFromInt(col)) + 0.5) * 5;
+ var stale = mouse(layer, x, .press);
+ stale.tag_hit.?.serial +%= 1;
+ p.update(.{ .mouse = stale });
+ try std.testing.expect(!pane.tag_edit);
+ p.update(.{ .mouse = mouse(layer, x, .press) });
+ p.update(.{ .mouse = mouse(layer, x, .release) });
+ try std.testing.expect(pane.tag_edit);
+ try std.testing.expectEqual(@as(u16, @intCast(at)), pane.tag_col);
+ p.update(.{ .key = .{ .cp = 'X', .text = "X" } });
+ _ = try p.render(frame.allocator());
+ const edited = try p.tagText(frame.allocator(), pane);
+ try std.testing.expect(std.mem.endsWith(u8, edited, "界 XEND"));
+ try std.testing.expectEqual(@as(u16, 0), pane.tag_scroll);
+ try std.testing.expectEqualStrings("body\n", pane.file.?.content);
+ p.update(.{ .key = .{ .cp = Key.escape } });
+ const rect = p.rects[0];
+ const track: layout.Track = .{
+ .serial = pane.serial,
+ .pane = 0,
+ .effect = .zoom,
+ .frame = 4,
+ .frame_count = 10,
+ .from = .{ .x = @as(f32, @floatFromInt(rect.x)) + 2, .y = @floatFromInt(rect.y), .w = @floatFromInt(rect.w - 4), .h = @floatFromInt(rect.h) },
+ .to = .{ .x = @floatFromInt(rect.x), .y = @floatFromInt(rect.y), .w = @floatFromInt(rect.w), .h = @floatFromInt(rect.h) },
+ };
+ p.presentation.shown_tracks[0] = track;
+ p.presentation.pending = false;
+ const shown = track.presented();
+ const source_y = @as(f32, @floatFromInt(layer.viewport.y)) + 0.5;
+ const painted_x = shown.x + (x / 10 - track.to.x) / track.to.w * shown.w;
+ const painted_y = shown.y + (source_y - track.to.y) / track.to.h * shown.h;
+ for ([_]pardes.Mouse.Kind{ .press, .release }) |kind| {
+ var event = mouse(layer, x, kind);
+ // Hosts inverse the exact painted pixel into the tag envelope;
+ // physical routing still receives the original body-grid cell.
+ event.col = @intFromFloat(painted_x);
+ event.row = @intFromFloat(painted_y);
+ p.update(.{ .mouse = event });
+ }
+ try std.testing.expectEqual(@as(u16, @intCast(at)), pane.tag_col);
+ }
+ }
+
+ test "compact tag pointer keeps physical grips and adjacent header ownership" {
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 100, .rows = 24 });
+ defer p.deinit();
+ _ = try p.setTestFile("body\n");
+ p.presentation.enabled = false;
+ p.settings.column_tags = true;
+ p.row_metrics = .{ .body_w = 10, .body_h = 20, .tagline_w = 5, .tagline_h = 10 };
+ try std.testing.expect(p.executeBuiltinLine(0, "New"));
+ const second = p.active;
+ try std.testing.expect(layout.splitColumn(p, second, second, false));
+ p.update(.tick);
+ var frame = std.heap.ArenaAllocator.init(p.gpa);
+ defer frame.deinit();
+ _ = try p.render(frame.allocator());
+ const layer = try findLayer(p, .pane, second);
+ const edge = @as(f32, @floatFromInt(layer.viewport.x)) * 10;
+ p.update(.{ .mouse = mouse(layer, edge - 0.1, .press) });
+ try std.testing.expect(p.drag == .move);
+ try std.testing.expectEqual(second, p.drag.move.id);
+ p.update(.{ .mouse = mouse(layer, edge - 0.1, .release) });
+ p.update(.{ .mouse = mouse(layer, edge + 0.1, .press) });
+ p.update(.{ .mouse = mouse(layer, edge + 0.1, .release) });
+ try std.testing.expectEqual(second, p.active);
+ try std.testing.expect(p.panes[second].?.tag_edit);
+ try std.testing.expectEqual(@as(u16, 0), p.panes[second].?.tag_col);
+ p.update(.{ .key = .{ .cp = Key.escape } });
+ const left = p.panes[0].?;
+ const tail = " " ++ "a" ** 110;
+ @memcpy(left.tag_tail[0..tail.len], tail);
+ left.tag_tail_len = tail.len;
+ left.tag_init = true;
+ _ = try p.render(frame.allocator());
+ const left_layer = try findLayer(p, .pane, 0);
+ const left_text = try p.tagText(frame.allocator(), left);
+ const start_x = @as(f32, @floatFromInt(left_layer.viewport.x)) * 10 + 12.5;
+ const beyond_x = edge + 12.5;
+ p.update(.{ .mouse = mouse(left_layer, start_x, .press) });
+ p.update(.{ .mouse = mouse(layer, beyond_x, .drag) });
+ p.update(.{ .mouse = mouse(layer, beyond_x, .release) });
+ try std.testing.expectEqual(@as(usize, 0), p.active);
+ const end_col = left_layer.capacity(p.row_metrics) - 1;
+ try std.testing.expectEqual(@as(u16, @intCast(panes.File.rawAtDisplay(left_text, end_col))), left.tag_col);
+ try std.testing.expect(left.tag_sel);
+ p.update(.{ .key = .{ .cp = Key.escape } });
+ const long = "a" ** 60 ++ "界END";
+ try std.testing.expect(p.global_tag.set(long));
+ for (p.col_tags[0..p.ncol]) |*tag| try std.testing.expect(tag.set(long));
+ _ = try p.render(frame.allocator());
+ for ([_]Kind{ .workspace, .column }) |kind| {
+ const id: usize = if (kind == .column) 1 else 0;
+ const header = try findLayer(p, kind, id);
+ const x = @as(f32, @floatFromInt(header.viewport.x)) * 10 + 62.5 * 5;
+ p.update(.{ .mouse = mouse(header, x, .press) });
+ p.update(.{ .mouse = mouse(header, x, .release) });
+ try std.testing.expectEqual(@as(?u16, 63), p.topbar_col);
+ if (kind == .column) try std.testing.expectEqual(@as(?usize, 1), p.header_column) else try std.testing.expect(p.header_column == null);
+ p.update(.{ .key = .{ .cp = Key.escape } });
+ }
+ }
+};
+
const TagNavigationTests = struct {
fn key(p: *Pardes, cp: u21) void {
p.update(.{ .key = .{ .cp = cp } });
@@ -3776,6 +3920,7 @@ const TerminalTests = struct {
test {
_ = TagNavigationTests;
+ _ = TagLayerTests;
_ = TagNameTintTests;
_ = TreeContextTests;
_ = BodyHitTests;