summaryrefslogtreecommitdiff
path: root/src/tag_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/tag_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/tag_layer.zig')
-rw-r--r--src/tag_layer.zig87
1 files changed, 7 insertions, 80 deletions
diff --git a/src/tag_layer.zig b/src/tag_layer.zig
index 7cfe5bbf..213ebc07 100644
--- a/src/tag_layer.zig
+++ b/src/tag_layer.zig
@@ -1,83 +1,10 @@
-//! Tag text has its own horizontal pitch. Its viewport stays in physical
-//! body-grid coordinates so compact text never changes pane hit targets.
+//! Pointer hits on tag bands (Layer.TagHit): comparing two, and bringing one
+//! that left its band back onto it.
const std = @import("std");
const pardes = @import("pardes.zig");
-
-/// `notice` is a pane's transient line -- a builtin's name, a pending leader
-/// chord -- carried as a tagline-shaped band so it gets the tagline's height,
-/// small-font metrics, band offset and border instead of being a body-grid row
-/// wearing a tagline font.
-pub const Kind = enum(u8) { workspace, column, pane, notice };
-pub const Hit = struct {
- kind: Kind,
- id: u16,
- serial: u32,
- col: u16,
- /// Which line of a taller tag the layer draws.
- line: u16 = 0,
- pixel_x: f32,
- pixel_y: f32,
- metrics: pardes.RowMetrics,
-};
-pub const Layer = struct {
- kind: Kind = .workspace,
- id: u16 = 0,
- serial: u32 = 0,
- /// A pane's tag taller than a line is drawn a layer to a line; this is
- /// which one, counted from its first.
- line: u16 = 0,
- viewport: pardes.Rect = .{ .x = 0, .y = 0, .w = 0, .h = 0 },
- cols: u16 = 0,
- bg: [3]u8 = .{ 0, 0, 0 },
- cells: []pardes.Cell = &.{},
- cursor: ?struct { col: u16, bar: bool = false } = null,
- /// Vertical offset in rows (negative: above its viewport), for a band
- /// that is moving into place. A pixel shell draws it offset and clipped
- /// to the rows below the viewport's top; the grid has no such thing.
- slide: f32 = 0,
- /// How present a notice band is (1 = fully) while it fades up or
- /// dissolves. `bg` and the cells are already blended by it; a pixel
- /// shell uses it for what it draws around the band, like its rule.
- fade: f32 = 1,
-
- pub fn capacity(layer: *const Layer, metrics: pardes.RowMetrics) u16 {
- return @intCast(@min(std.math.maxInt(u16), @max(1, @as(u32, layer.viewport.w) * @max(1, metrics.body_w) / @max(1, metrics.tagline_w))));
- }
-
- pub fn hitAt(layer: *const Layer, x: f32, y: f32, body_w: f32, body_h: f32, tagline_w: f32, tagline_h: f32) ?Hit {
- if (layer.cols == 0 or !std.math.isFinite(x) or !std.math.isFinite(y)) return null;
- for ([_]f32{ body_w, body_h, tagline_w, tagline_h }) |metric|
- if (!std.math.isFinite(metric) or metric < 1 or metric > std.math.maxInt(u16)) 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;
- return .{
- .kind = layer.kind,
- .id = layer.id,
- .serial = layer.serial,
- .line = layer.line,
- .col = @intFromFloat(@min(@as(f32, @floatFromInt(layer.cols - 1)), @floor(dx / tw))),
- .pixel_x = x,
- .pixel_y = y,
- .metrics = .{ .body_w = @intFromFloat(bw), .body_h = @intFromFloat(bh), .tagline_w = @intFromFloat(tw), .tagline_h = @intFromFloat(th) },
- };
- }
-};
-
-test "tag text capacity uses its own pitch and excludes the physical grip" {
- const layer: Layer = .{ .kind = .pane, .id = 2, .serial = 7, .viewport = .{ .x = 12, .y = 4, .w = 38, .h = 1 }, .cols = 90 };
- try std.testing.expectEqual(@as(u16, 63), layer.capacity(.{ .body_w = 10, .tagline_w = 6 }));
- try std.testing.expect(layer.hitAt(119.99, 85, 10, 20, 6, 12) == null);
- try std.testing.expectEqual(@as(u16, 0), layer.hitAt(120, 85, 10, 20, 6, 12).?.col);
- try std.testing.expectEqual(@as(u16, 50), layer.hitAt(423, 85, 10, 20, 6, 12).?.col);
- try std.testing.expect(layer.hitAt(500, 85, 10, 20, 6, 12) == null);
- try std.testing.expect(layer.hitAt(120, 85, std.math.nan(f32), 20, 6, 12) == null);
- try std.testing.expect(layer.hitAt(120, 85, 10, 20, 0, 12) == null);
-}
+const Layer = @import("Layer.zig");
+const Hit = Layer.TagHit;
+const Kind = Layer.Kind;
/// Whether two pointer hits name the same cell of the same tag line.
pub fn sameCell(a: ?Hit, b: ?Hit) bool {
@@ -92,13 +19,13 @@ pub fn columnAt(p: *const pardes.Pardes, kind: Kind, id: usize, supplied: ?Hit,
var point = supplied orelse return null;
if (point.kind != kind or point.id != id) return null;
if (clamp) for (p.surface.tagLayers()) |*layer| {
- if (layer.cols == 0 or layer.kind != kind or layer.id != id or layer.serial != point.serial or layer.line != line) continue;
+ if (layer.rows == 0 or layer.kind != kind or layer.id != id or layer.serial != point.serial or line >= layer.rows) continue;
const bw: f32 = @floatFromInt(point.metrics.body_w);
const bh: f32 = @floatFromInt(point.metrics.body_h);
const left = @as(f32, @floatFromInt(layer.viewport.x)) * bw;
const right = @as(f32, @floatFromInt(layer.viewport.x + layer.viewport.w)) * bw;
point.pixel_x = std.math.clamp(point.pixel_x, left, @max(left, right - 0.001));
- point.pixel_y = (@as(f32, @floatFromInt(layer.viewport.y)) + 0.5) * bh;
+ point.pixel_y = (@as(f32, @floatFromInt(layer.viewport.y + line)) + 0.5) * bh;
break;
};
return if (p.reprojectTagHit(point)) |mapped| mapped.col else null;