From d88e381793c6505bf3f2af43705bd2fa32f55ef6 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Mon, 28 Sep 2026 14:37:29 -0300 Subject: One Layer for tags, notices, headers and bodies; a taller tag is one layer of rows; wire v8 src/Layer.zig merges TagLayer and BodyLayer. `rows` (0 = no layer) and a cursor at {x, y}. A tag of N rows is ONE layer of N grid rows: tagHit answers the row as `line`, bodyHit keeps its meaning, and the per-line layer bases (TAG_LINE_LAYER_BASE, HEADER_LINE_LAYER_BASE) are gone, not aliased. Wire v8, the one bump: tag layers carry rows and cursor y, and the frame carries the placed region list. v7 and v9 peers are refused in both directions (server test over both, a new frontend test over both). web: tag_layer_value 11 = rows, 12 = cursor y; app.mjs lays every row. macOS: the Zig side compiles against Layer; pardes.h still sees one row per tag layer (accepted, the macOS shell is ignored for now). No visual change: snapshot goldens and the 17 GUI goldens byte-identical. Shared files touched: pardes.zig, Messages.zig, gui.zig, macos.zig, detached/client.zig, detached/server.zig, detached/wire.zig (plus web.zig, app.mjs, edit.zig, look.zig). Not touched: mouse.zig, tagline.zig, colors.zig, tty.zig, dump.zig, exec.zig, host_io.zig, panes.zig. --- test/column_drag.zig | 10 +++++----- test/panes.zig | 30 ++++++++++++++++-------------- 2 files changed, 21 insertions(+), 19 deletions(-) (limited to 'test') diff --git a/test/column_drag.zig b/test/column_drag.zig index feea1bf7..3aea4286 100644 --- a/test/column_drag.zig +++ b/test/column_drag.zig @@ -8,17 +8,17 @@ fn mouse(p: *Pardes, kind: pardes.Mouse.Kind, col: u16) void { p.update(.{ .mouse = .{ .button = .left, .kind = kind, .col = col, .row = pardes.TOPBAR_H } }); } -fn tagLayer(p: *Pardes, column: usize) !*pardes.TagLayer { +fn tagLayer(p: *Pardes, column: usize) !*pardes.Layer { for (&p.surface.tag_layers) |*layer| { - if (layer.cols > 0 and layer.kind == .column and layer.id == column) return layer; + if (layer.rows > 0 and layer.kind == .column and layer.id == column) return layer; } return error.MissingColumnTagLayer; } -fn headerMouse(layer: *const pardes.TagLayer, display_col: u16, kind: pardes.Mouse.Kind) pardes.Mouse { +fn headerMouse(layer: *const pardes.Layer, display_col: u16, kind: pardes.Mouse.Kind) pardes.Mouse { const x = @as(f32, @floatFromInt(layer.viewport.x)) * 10 + (@as(f32, @floatFromInt(display_col)) + 0.5) * 5; 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) }; + return .{ .button = .left, .kind = kind, .col = @intFromFloat(x / 10), .row = @intFromFloat(y / 20), .tag_hit = layer.tagHit(x, y, 10, 20, 5, 10) }; } const Snapshot = struct { @@ -144,7 +144,7 @@ test "column grip is separate from compact text and the resize seam" { try std.testing.expectEqual(p.col_x[column], rect.x); try std.testing.expectEqual(rect.x + pardes.TAG_TEXT_INSET, (try tagLayer(p, column)).viewport.x); for (p.surface.tagLayers()) |pane_layer| { - if (pane_layer.cols > 0 and pane_layer.kind == .pane and pane_layer.id == pane_id) + if (pane_layer.rows > 0 and pane_layer.kind == .pane and pane_layer.id == pane_id) try std.testing.expectEqual((try tagLayer(p, column)).viewport.x, pane_layer.viewport.x); } const pane_tag_y = if (p.settings.tag_bottom) rect.y + rect.h - pardes.BOX_H else rect.y; diff --git a/test/panes.zig b/test/panes.zig index 11fb750a..5e26cdd0 100644 --- a/test/panes.zig +++ b/test/panes.zig @@ -19,17 +19,17 @@ fn setTag(pane: *Pane, text: []const u8) !void { } const TagLayerTests = struct { - const Kind = @FieldType(pardes.TagLayer, "kind"); - fn findLayer(p: *Pardes, kind: Kind, id: usize) !*pardes.TagLayer { + const Kind = @FieldType(pardes.Layer, "kind"); + fn findLayer(p: *Pardes, kind: Kind, id: usize) !*pardes.Layer { for (&p.surface.tag_layers) |*layer| { - if (layer.cols > 0 and layer.kind == kind and layer.id == id) return layer; + if (layer.rows > 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 { + fn mouse(layer: *const pardes.Layer, 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) }; + return .{ .button = .left, .kind = kind, .col = @intFromFloat(x / 10), .row = @intFromFloat(y / 20), .tag_hit = layer.tagHit(x, y, 10, 20, 5, 10) }; } test "compact tag layers render and edit Unicode text beyond body column capacity" { @@ -200,14 +200,16 @@ test "a tag with a newline in it is as tall as its lines, and each line is typed const body_y = p.bodyTop(pane, r); try std.testing.expectEqual(if (bottom) r.y else r.y + 2, body_y); try std.testing.expectEqualStrings("b", surface.at(r.x + config.GUTTER + panes.File.gutterWidth(pane), body_y).grapheme()); - // a layer to a line, for a pixel shell - var lines: usize = 0; + // one layer, a row to each line, for a pixel shell + var layers: usize = 0; for (p.surface.tagLayers()) |*layer| { - if (layer.cols == 0 or layer.kind != .pane or layer.id != 0) continue; - try std.testing.expectEqual(tag_y + layer.line, layer.viewport.y); - lines += 1; + if (layer.rows == 0 or layer.kind != .pane or layer.id != 0) continue; + try std.testing.expectEqual(tag_y, layer.viewport.y); + try std.testing.expectEqual(@as(u16, 2), layer.rows); + try std.testing.expectEqual(@as(u16, 2), layer.viewport.h); + layers += 1; } - try std.testing.expectEqual(@as(usize, 2), lines); + try std.testing.expectEqual(@as(usize, 1), layers); // a click on the second line puts the tag's cursor on it p.update(.{ .key = .{ .cp = Key.escape } }); p.update(.{ .key = .{ .cp = ':' } }); @@ -541,11 +543,11 @@ const TagNavigationTests = struct { }; const BodyHitTests = struct { - fn mouse(layer: *const pardes.BodyLayer, row: u16, col: u16, kind: pardes.Mouse.Kind) pardes.Mouse { + fn mouse(layer: *const pardes.Layer, row: u16, col: u16, kind: pardes.Mouse.Kind) pardes.Mouse { const width: f32 = if (row < layer.context_rows) 8 else 10; const x = @as(f32, @floatFromInt(layer.viewport.x)) * 10 + (@as(f32, @floatFromInt(col)) + 0.5) * width; const y = layer.rowTop(row, 20, 10) + layer.rowHeight(row, 20, 10) / 2; - return .{ .button = .left, .kind = kind, .col = @intFromFloat(x / 10), .row = @intFromFloat(y / 20), .body_hit = layer.hitAt(x, y, 10, 20, 8, 10).? }; + return .{ .button = .left, .kind = kind, .col = @intFromFloat(x / 10), .row = @intFromFloat(y / 20), .body_hit = layer.bodyHit(x, y, 10, 20, 8, 10).? }; } test "compact TreeContext mouse follows source rows in reclaimed space" { @@ -673,7 +675,7 @@ const TreeContextTests = struct { const y = layer.rowTop(row, 20, 10) + 9.5; const x = @as(f32, @floatFromInt(layer.viewport.x)) * 10 + 5; // A one-pixel separator remains inside its existing header row. - try std.testing.expectEqual(row, layer.hitAt(x, y, 10, 20, 8, 10).?.row); + try std.testing.expectEqual(row, layer.bodyHit(x, y, 10, 20, 8, 10).?.row); } pane.scrollBy(-5); _ = try p.render(frame.allocator()); -- cgit v1.3