summaryrefslogtreecommitdiff
path: root/src/body_layer.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-28 14:37:29 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:15 -0300
commitd88e381793c6505bf3f2af43705bd2fa32f55ef6 (patch)
treed4f3966c4eacf7fee43b6f18fc83f136676f835d /src/body_layer.zig
parent7284b72396398213356577345151bd79e497d27c (diff)
downloadpardes-d88e381793c6505bf3f2af43705bd2fa32f55ef6.tar.gz
pardes-d88e381793c6505bf3f2af43705bd2fa32f55ef6.zip
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.
Diffstat (limited to 'src/body_layer.zig')
-rw-r--r--src/body_layer.zig117
1 files changed, 7 insertions, 110 deletions
diff --git a/src/body_layer.zig b/src/body_layer.zig
index ac87a07b..583a2e98 100644
--- a/src/body_layer.zig
+++ b/src/body_layer.zig
@@ -1,9 +1,6 @@
-//! A pane body as the surface shows it. A file body's logical rows can
-//! outnumber its physical grid rows: hosts place the compact declaration rows
-//! first and clip the remaining body rows. After the layer types come the
-//! functions that draw a body into the surface: its text, colours, selections
-//! and cursor; then the tags drawn beside it, a pane's and the column and
-//! workspace ones, each line a tag layer of its own.
+//! Drawing a pane body into a surface: its text, colours, selections and
+//! cursor, and joining a body with compact context rows through its layer
+//! (Layer.zig).
const std = @import("std");
const pardes = @import("pardes.zig");
const normal = @import("normal.zig");
@@ -29,7 +26,7 @@ const tagline = @import("tagline.zig");
const Messages = @import("Messages.zig");
const Text = panes.Text;
const MAX_PANES = pardes.MAX_PANES;
-const TagKind = @import("tag_layer.zig").Kind;
+const TagKind = @import("Layer.zig").Kind;
const MAX_TAG_ROWS = pardes.MAX_TAG_ROWS;
const NOTICE_LAYER_BASE = pardes.NOTICE_LAYER_BASE;
const TAG_LINE_LAYER_BASE = pardes.TAG_LINE_LAYER_BASE;
@@ -40,108 +37,7 @@ const COLBAR_H = pardes.COLBAR_H;
const TAG_GAP = pardes.TAG_GAP;
const wordBoundsAtCol = pardes.wordBoundsAtCol;
-pub const Metrics = struct {
- body_w: u16 = 1,
- body_h: u16 = 1,
- tagline_w: u16 = 1,
- tagline_h: u16 = 1,
-};
-
-pub const Layer = struct {
- pane: u16 = 0,
- serial: u32 = 0,
- viewport: pardes.Rect = .{ .x = 0, .y = 0, .w = 0, .h = 0 },
- cols: u16 = 0,
- rows: u16 = 0,
- context_rows: u16 = 0,
- context_separators: std.bit_set.StaticBitSet(limits.wrap_rows) = .initEmpty(),
- cells: []pardes.Cell = &.{},
- cursor: ?struct { x: u16, y: u16, bar: bool = false } = null,
-
- pub fn setContextLines(layer: *Layer, lines: []const i32) void {
- layer.context_separators = .initEmpty();
- const count = @min(lines.len, layer.context_rows);
- var row: usize = 0;
- while (row + 1 < count and row < limits.wrap_rows) : (row += 1) {
- if (lines[row + 1] != lines[row] +| 1) layer.context_separators.set(row);
- }
- }
-
- pub fn hasContextBorderAfter(layer: *const Layer, row: usize) bool {
- if (row >= layer.context_rows or row >= limits.wrap_rows) return false;
- return row + 1 == layer.context_rows or layer.context_separators.isSet(row);
- }
-
- pub fn rowTop(layer: *const Layer, row: u16, body_h: f32, tagline_h: f32) f32 {
- const body = @max(1, body_h);
- const tag = std.math.clamp(tagline_h, 1, body);
- return @as(f32, @floatFromInt(layer.viewport.y)) * body +
- @as(f32, @floatFromInt(@min(row, layer.context_rows))) * tag +
- @as(f32, @floatFromInt(row -| layer.context_rows)) * body;
- }
-
- pub fn rowHeight(layer: *const Layer, row: u16, body_h: f32, tagline_h: f32) f32 {
- return if (row < layer.context_rows) std.math.clamp(tagline_h, 1, @max(1, body_h)) else @max(1, body_h);
- }
-
- pub fn hitAt(layer: *const Layer, x: f32, y: f32, body_w: f32, body_h: f32, tagline_w: f32, tagline_h: f32) ?pardes.Mouse.BodyHit {
- if (layer.rows == 0 or !std.math.isFinite(x) or !std.math.isFinite(y)) return null;
- const bw = @max(1, body_w);
- const bh = @max(1, body_h);
- const tw = std.math.clamp(tagline_w, 1, bw);
- const th = std.math.clamp(tagline_h, 1, bh);
- const dx = x - @as(f32, @floatFromInt(layer.viewport.x)) * bw;
- const dy = y - @as(f32, @floatFromInt(layer.viewport.y)) * bh;
- if (dx < 0 or dy < 0 or dx >= @as(f32, @floatFromInt(layer.viewport.w)) * bw or dy >= @as(f32, @floatFromInt(layer.viewport.h)) * bh) return null;
- const headers = @as(f32, @floatFromInt(layer.context_rows)) * th;
- const row: u16 = @intFromFloat(if (dy < headers) @floor(dy / th) else @as(f32, @floatFromInt(layer.context_rows)) + @floor((dy - headers) / bh));
- if (row >= layer.rows) return null;
- const col: u16 = @intFromFloat(@min(@as(f32, @floatFromInt(layer.cols -| 1)), @floor(dx / (if (row < layer.context_rows) tw else bw))));
- return .{
- .serial = layer.serial,
- .row = row,
- .col = col + config.GUTTER,
- .pixel_x = x,
- .pixel_y = y,
- .metrics = .{ .body_w = @intFromFloat(bw), .body_h = @intFromFloat(bh), .tagline_w = @intFromFloat(tw), .tagline_h = @intFromFloat(th) },
- };
- }
-};
-
-test "compact body layer rows fill reclaimed height and invert exact boundaries" {
- const layer: Layer = .{ .serial = 7, .viewport = .{ .x = 4, .y = 3, .w = 40, .h = 10 }, .cols = 40, .rows = 15, .context_rows = 5 };
- try std.testing.expectEqual(@as(f32, 120), layer.rowTop(5, 20, 12));
- try std.testing.expectEqual(@as(f32, 240), layer.rowTop(11, 20, 12));
- try std.testing.expectEqual(@as(f32, 12), layer.rowHeight(4, 20, 12));
- try std.testing.expectEqual(@as(f32, 20), layer.rowHeight(5, 20, 12));
- for (0..12) |index| {
- const row: u16 = @intCast(index);
- const y = layer.rowTop(row, 20, 12) + layer.rowHeight(row, 20, 12) / 2;
- const width: f32 = if (row < 5) 6 else 10;
- const hit = layer.hitAt(40 + 3.5 * width, y, 10, 20, 6, 12).?;
- try std.testing.expectEqual(row, hit.row);
- try std.testing.expectEqual(@as(u16, 3 + config.GUTTER), hit.col);
- }
- try std.testing.expectEqual(@as(u16, 4), layer.hitAt(45, 119.99, 10, 20, 6, 12).?.row);
- try std.testing.expectEqual(@as(u16, 5), layer.hitAt(45, 120, 10, 20, 6, 12).?.row);
- try std.testing.expectEqual(@as(u16, 11), layer.hitAt(45, 259.99, 10, 20, 6, 12).?.row);
- try std.testing.expect(layer.hitAt(45, 260, 10, 20, 6, 12) == null);
- try std.testing.expect(layer.hitAt(39.99, 80, 10, 20, 6, 12) == null);
- try std.testing.expect(layer.hitAt(std.math.nan(f32), 80, 10, 20, 6, 12) == null);
-}
-
-test "compact context borders separate source gaps and preserve multiline headers" {
- var layer: Layer = .{ .context_rows = 6 };
- layer.setContextLines(&.{ 3, 4, 8, 9, 10, 17 });
- const expected = [_]bool{ false, true, false, false, true, true, false };
- for (expected, 0..) |border, row| try std.testing.expectEqual(border, layer.hasContextBorderAfter(row));
- layer.context_rows = 2;
- layer.setContextLines(&.{ 9, 10 });
- try std.testing.expect(!layer.hasContextBorderAfter(0));
- try std.testing.expect(layer.hasContextBorderAfter(1));
- try std.testing.expect(!layer.hasContextBorderAfter(4));
- try std.testing.expect(!layer.context_separators.isSet(4));
-}
+const Layer = @import("Layer.zig");
// ---- drawing a pane body into the surface ----
@@ -305,7 +201,8 @@ pub fn joinBody(p: *Pardes, s: *Surface, arena: std.mem.Allocator, pane: *Pane,
const start = (row + first_row) * r.w + config.GUTTER;
@memcpy(layer.cells[row * width ..][0..width], temporary_cells[start..][0..width]);
}
- layer.pane = @intCast(id);
+ layer.kind = .body;
+ layer.id = @intCast(id);
layer.serial = pane.serial;
const body = s.region(.body, id).?;
layer.viewport = .{ .x = body.rect.x, .y = body.rect.y, .w = body.rect.w - body.clip, .h = body.rect.h };