summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/body_layer.zig4
-rw-r--r--src/draw.zig47
-rw-r--r--src/macos.zig18
-rw-r--r--src/surface.zig16
4 files changed, 46 insertions, 39 deletions
diff --git a/src/body_layer.zig b/src/body_layer.zig
index 583a2e98..e9407719 100644
--- a/src/body_layer.zig
+++ b/src/body_layer.zig
@@ -294,9 +294,7 @@ pub fn renderBody(p: *Pardes, s: *Surface, arena: std.mem.Allocator, pane: *Pane
if (p.look_hover_preview) |preview| {
if (preview.pane == id and preview.serial == pane.serial) {
// Preserve body ink, then let every real user selection below
- // win over this quiet affordance. mark_hover carries the
- // cells this paints out to the hosts as the affordance rect —
- // macOS composes its glass overlay over exactly these.
+ // win over this quiet affordance.
const preview_bg = colors.mix(page_rgb, colors.mix(page_rgb, th.sel_bg));
if (preview.file_word) |word|
panes.File.paintWordSelection(p, s, pane, r, word.row, word.lo, word.hi, preview_bg)
diff --git a/src/draw.zig b/src/draw.zig
index 4242a6e7..3798bf59 100644
--- a/src/draw.zig
+++ b/src/draw.zig
@@ -163,10 +163,6 @@ pub fn render(p: *Pardes, arena: std.mem.Allocator) !*Surface {
// the pointer per-text (the macOS I-beam) read this as "target" and
// afford it the arrow instead.
if (p.look_hover_preview != null) s.pointer_shape = .target;
- // Stale hover bits must not survive into a frame whose pointer has
- // moved on: the ordinary content paint does not clear fields it
- // never set.
- for (s.cells) |*c| c.hover = false;
for (&s.body_layers) |*layer| layer.rows = 0;
for (&s.tag_layers) |*layer| layer.rows = 0;
s.nimages = 0;
@@ -661,10 +657,11 @@ fn renderHeaderLayer(p: *Pardes, arena: std.mem.Allocator, layer: *pardes.Layer,
var surface: Surface = .{ .cols = columns, .rows = 1, .cells = layer.cells[index * columns ..][0..columns] };
surface.fill(0, 0, columns, 1, .{ .bg = .{ .rgb = bg }, .font_role = .tagline });
_ = surface.print(0, 0, columns, shown[panes.File.rawAtDisplay(shown, scroll)..], .{ .fg = .{ .rgb = fg }, .bg = .{ .rgb = bg }, .font_role = .tagline });
- // The word under the pointer: on a header even while it is edited,
- // but not on a notice (a message is not a command line), nor on a
- // column a carried column's pointer is aiming at a place over.
- const hovered = p.pointer_inside and kind != .notice and !(kind == .column and p.drag == .column_move);
+ // The word under the pointer, a Look or Exec target: on a header even
+ // while it is edited, on a notice, and on the grid's copy of either
+ // (joined from this); not on a column a carried column's pointer is
+ // aiming at a place over.
+ const hovered = p.pointer_inside and !(kind == .column and p.drag == .column_move);
if (hovered) {
const tag_hit = p.pointer_tag_hit;
const col: ?usize = if (tag_hit) |pointer|
@@ -1033,6 +1030,40 @@ test "place puts every part where the painters and layers put it, in every layou
}
}
+test "the word under the pointer on a notice is lit in its layer and in the grid's copy" {
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 41, .rows = 12 });
+ defer p.deinit();
+ _ = try p.setTestFile("text\n");
+ var arena: std.heap.ArenaAllocator = .init(std.testing.allocator);
+ defer arena.deinit();
+ try std.testing.expect(p.executeBuiltinLine(p.active, "Msg Look here"));
+ p.sync();
+ var s = try p.render(arena.allocator());
+ const notice = for (s.regionList()) |region| {
+ if (region.kind == .notice) break region;
+ } else return error.TestExpectedNotice;
+ const row = s.cells[@as(usize, notice.rect.y) * s.cols ..][0..s.cols];
+ // "here", the chip's last word, before its blank edge.
+ const here = notice.rect.x + notice.rect.w - 5;
+ try std.testing.expectEqualStrings("h", row[here].grapheme());
+ p.pointer_inside = true;
+ p.hover_col = here + 1;
+ p.hover_row = notice.rect.y;
+ _ = arena.reset(.retain_capacity);
+ s = try p.render(arena.allocator());
+ const lit = s.cells[@as(usize, notice.rect.y) * s.cols ..][0..s.cols];
+ const sel: Color = .{ .rgb = p.theme().sel_bg };
+ for (lit[here..][0..4]) |cell| try std.testing.expectEqual(sel, cell.style.bg);
+ try std.testing.expect(!std.meta.eql(sel, lit[here - 2].style.bg));
+ const layer = for (s.tagLayers()) |*layer| {
+ if (layer.rows != 0 and layer.kind == .notice) break layer;
+ } else return error.TestExpectedNotice;
+ const lit_in_layer = for (layer.cells) |cell| {
+ if (std.meta.eql(sel, cell.style.bg)) break true;
+ } else false;
+ try std.testing.expect(lit_in_layer);
+}
+
test "a wide glyph cut by a tag's or a notice's edge is joined into the grid as blank, never half drawn" {
const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 41, .rows = 12 });
defer p.deinit();
diff --git a/src/macos.zig b/src/macos.zig
index f7e03cd4..4d8f2883 100644
--- a/src/macos.zig
+++ b/src/macos.zig
@@ -1866,13 +1866,10 @@ fn blankTagCells(cells: []Cell, cols: u16, rows: u16, source: []const pardes.Cel
if (layer.rows == 0 or layer.viewport.y >= rows or layer.viewport.x >= cols) continue;
const start = @as(usize, layer.viewport.y) * cols + layer.viewport.x;
const count = @min(layer.viewport.w, cols - layer.viewport.x);
- for (cells[start..][0..count], source[start..][0..count]) |*cell, was| {
- // The tag text is the layer's to draw, but a look-hover on a tag
- // word is a canonical fact: carry the hover bit through so the
- // glass overlay still finds the affordance rect.
- cell.* = encodeCell(.{});
- cell.flags |= @as(u8, @intFromBool(was.hover)) * cell_flag_hover;
- }
+ // The tag text is the layer's to draw. (The look-hover's glass
+ // rect went with the core's hover bit, stage 8's follow-up.)
+ _ = source;
+ for (cells[start..][0..count]) |*cell| cell.* = encodeCell(.{});
}
}
@@ -2574,7 +2571,7 @@ fn encodeCell(cell: pardes.Cell) Cell {
.fg = encodeColor(cell.style.fg),
.bg = encodeColor(cell.style.bg),
.attrs = encodeAttrs(cell.style),
- .flags = encodeCellFlags(cell.default, cell.style.font_role, cell.hover),
+ .flags = encodeCellFlags(cell.default, cell.style.font_role, false),
.len = if (cell.default) 1 else cell.len,
};
if (cell.default)
@@ -3131,16 +3128,11 @@ test "mac tag layer ABI preserves logical capacity and physical grip" {
try std.testing.expectEqual(@as(u32, 11), pardes_tag_layer_value(0, 8));
try std.testing.expectEqual(@as(u8, 'Z'), pardes_tag_layer_cells(0).?[11].text[0]);
var canonical: [24]Cell = @splat(encodeCell(.{ .text = .{'G'} ++ .{0} ** 6, .len = 1, .default = false }));
- // A look-hover under the layer is a canonical fact and survives the blank;
- // the glyph it marks does not, because the layer redraws that text itself.
var source: [24]pardes.Cell = @splat(.{});
- source[11].hover = true;
blankTagCells(&canonical, 8, 3, &source, &core.surface.tag_layers);
try std.testing.expectEqual(@as(u8, 'G'), canonical[9].text[0]);
try std.testing.expectEqual(@as(u8, 'G'), canonical[18].text[0]);
try std.testing.expectEqual(encodeCell(.{}).len, canonical[10].len);
- try std.testing.expectEqual(@as(u8, 0), canonical[10].flags & cell_flag_hover);
- try std.testing.expectEqual(cell_flag_hover, canonical[11].flags & cell_flag_hover);
}
test "mac window blur builtin reaches the ABI without changing opacity" {
diff --git a/src/surface.zig b/src/surface.zig
index 07b593b1..b6f41853 100644
--- a/src/surface.zig
+++ b/src/surface.zig
@@ -38,12 +38,6 @@ pub const Cell = struct {
len: u8 = 1,
style: CellStyle = .{},
default: bool = true,
- /// The look-hover affordance is on this cell: the word under the pointer
- /// is a real Look or Exec operand, and hosts that draw the quiet tint as
- /// more than a flat fill — macOS renders it as a glass overlay — read
- /// this instead of guessing from the background colour, which a real
- /// selection shares.
- hover: bool = false,
pub fn grapheme(c: *const Cell) []const u8 {
return c.text[0..c.len];
@@ -405,10 +399,6 @@ pub const Chrome = struct {
pub const Surface = struct {
pub const PointerShape = enum(u8) { arrow, link, target };
pointer_shape: PointerShape = .arrow,
- /// Set only for the duration of the look-hover paint, so the cells that
- /// paint the quiet affordance can carry the hover bit through the ABI
- /// without every selection painter learning a new parameter.
- mark_hover: bool = false,
body_layers: [MAX_PANES]Layer = @splat(.{}),
tag_layers: [MAX_TAG_LAYERS]Layer = @splat(.{}),
cols: u16 = 0,
@@ -474,11 +464,7 @@ pub const Surface = struct {
pub fn at(s: *Surface, x: u16, y: u16) *Cell {
std.debug.assert(x < s.cols and y < s.rows);
- const cell = &s.cells[@as(usize, y) * s.cols + x];
- // The look-hover paint runs with mark_hover set, and its cells are
- // exactly the ones this marks — see render's preview block.
- if (s.mark_hover) cell.hover = true;
- return cell;
+ return &s.cells[@as(usize, y) * s.cols + x];
}
pub fn set(s: *Surface, x: u16, y: u16, text: []const u8, style: CellStyle) void {