summaryrefslogtreecommitdiff
path: root/src/macos.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/macos.zig')
-rw-r--r--src/macos.zig58
1 files changed, 45 insertions, 13 deletions
diff --git a/src/macos.zig b/src/macos.zig
index 79036c3e..d263cfc6 100644
--- a/src/macos.zig
+++ b/src/macos.zig
@@ -106,6 +106,7 @@ const color_default: u32 = 0x01000000;
const color_indexed: u32 = 0x02000000;
const cell_flag_default: u8 = 1;
const cell_flag_tagline: u8 = 2;
+const cell_flag_hover: u8 = 4;
const scene_flag_crt: u32 = 1 << 0;
const scene_flag_ripple: u32 = 1 << 1;
const scene_flag_glitch: u32 = 1 << 2;
@@ -730,6 +731,16 @@ fn initCore(runtime: ?*const Runtime, cols_arg: u16, rows_arg: u16) !void {
const core = try pardes.Pardes.init(allocs.pardes, opts);
errdefer core.deinit();
+ // The workspace tag row is the macOS menu bar's business here (see
+ // docs/macos.md): the build option says whether this shell keeps the
+ // acme topbar or hands its commands to native chrome. The e2e harness
+ // opts back in with PARDES_WORKSPACE_TAG=1 — its scripts speak to a
+ // grid that has the row. Applied before the first resize so the layout
+ // has never seen it the other way.
+ core.settings.workspace_tag = if (std.c.getenv("PARDES_WORKSPACE_TAG")) |v|
+ std.mem.eql(u8, std.mem.span(v), "1")
+ else
+ @import("pardes_config").workspace_tag;
core.native_images = true;
var prompt_rcs = host_io.Shell.prepare();
@@ -917,7 +928,7 @@ export fn pardes_gui_tagline_font_percent() u8 {
}
export fn pardes_tagline_band_offset(row: u16, canvas_h: f32, cell_h: u32, tagline_h: u32) u32 {
- return pardes.taglineBandOffset(row, canvas_h, cell_h, tagline_h);
+ return pardes.taglineBandOffset(row, canvas_h, cell_h, tagline_h, if (state) |*st| st.core.settings.workspace_tag else true);
}
export fn pardes_topbar_pane_border_px(cell_h: u32, tagline_h: u32) u32 {
@@ -942,6 +953,11 @@ export fn pardes_topbar_pane_border_rgb() u32 {
return @as(u32, rgb[0]) << 16 | @as(u32, rgb[1]) << 8 | rgb[2];
}
+export fn pardes_tag_active_bg() u32 {
+ const st = state orelse return color_default;
+ const rgb = st.core.chromeTheme().tag_active_bg;
+ return @as(u32, rgb[0]) << 16 | @as(u32, rgb[1]) << 8 | rgb[2];
+}
export fn pardes_tagline_bg() u32 {
const st = state orelse return color_default;
const rgb = st.core.chromeTheme().tag_bg;
@@ -1756,12 +1772,18 @@ fn clearFrame(st: *State) void {
st.panel_diff_len = 0;
}
-fn blankTagCells(cells: []Cell, cols: u16, rows: u16, layers: []const pardes.TagLayer) void {
+fn blankTagCells(cells: []Cell, cols: u16, rows: u16, source: []const pardes.Cell, layers: []const pardes.TagLayer) void {
for (layers) |layer| {
if (layer.cols == 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]) |*cell| cell.* = encodeCell(.{});
+ 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;
+ }
}
}
@@ -1800,7 +1822,7 @@ fn presentFrame(ctx: ?*anyopaque, surface: *const pardes.Surface) void {
defer tz_cells.end();
for (surface.cells, st.cells[0..count]) |cell, *out| out.* = encodeCell(cell);
}
- blankTagCells(st.cells[0..count], surface.cols, surface.rows, &surface.tag_layers);
+ blankTagCells(st.cells[0..count], surface.cols, surface.rows, surface.cells, &surface.tag_layers);
collectPanelDiff(st, surface, count);
collectImages(st, surface);
collectPanelTracks(st, surface);
@@ -1831,7 +1853,7 @@ fn collectPanelDiff(st: *State, surface: *const pardes.Surface, count: usize) vo
}
for (surface.previous_cells, st.previous_cells[0..count]) |cell, *out|
out.* = encodeCell(cell);
- blankTagCells(st.previous_cells[0..count], surface.cols, surface.rows, surface.previous_tag_layers);
+ blankTagCells(st.previous_cells[0..count], surface.cols, surface.rows, surface.previous_cells, surface.previous_tag_layers);
for (surface.cell_diffs, st.changed_cells[0..count]) |diff, *out|
out.* = encodeChanged(diff);
st.panel_diff_len = count;
@@ -2395,8 +2417,8 @@ 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),
.len = if (cell.default) 1 else cell.len,
- .flags = encodeCellFlags(cell.default, cell.style.font_role),
};
if (cell.default)
out.text[0] = ' '
@@ -2418,9 +2440,10 @@ fn encodeAttrs(style: pardes.CellStyle) u16 {
return attrs;
}
-fn encodeCellFlags(default: bool, role: pardes.FontRole) u8 {
+fn encodeCellFlags(default: bool, role: pardes.FontRole, hover: bool) u8 {
return @as(u8, @intFromBool(default)) * cell_flag_default |
- @as(u8, @intFromBool(role == .tagline)) * cell_flag_tagline;
+ @as(u8, @intFromBool(role == .tagline)) * cell_flag_tagline |
+ @as(u8, @intFromBool(hover)) * cell_flag_hover;
}
fn noteScrollVelocity(st: *State, delta_rows: f32, delta_cols: f32) void {
@@ -2566,7 +2589,8 @@ test "pardes.h declares every export the way it is defined" {
try expectSameAbi(@TypeOf(c.pardes_animating), @TypeOf(pardes_animating));
try expectSameAbi(@TypeOf(c.pardes_gui_tagline_font_percent), @TypeOf(pardes_gui_tagline_font_percent));
try expectSameAbi(@TypeOf(c.pardes_tagline_band_offset), @TypeOf(pardes_tagline_band_offset));
- try expectSameAbi(@TypeOf(c.pardes_topbar_pane_border_px), @TypeOf(pardes_topbar_pane_border_px));
+ try expectSameAbi(@TypeOf(c.pardes_topbar_pane_border_rgb), @TypeOf(pardes_topbar_pane_border_rgb));
+ try expectSameAbi(@TypeOf(c.pardes_tag_active_bg), @TypeOf(pardes_tag_active_bg));
try expectSameAbi(@TypeOf(c.pardes_tagline_origin_col), @TypeOf(pardes_tagline_origin_col));
try expectSameAbi(@TypeOf(c.pardes_grid_col_at), @TypeOf(pardes_grid_col_at));
try expectSameAbi(@TypeOf(c.pardes_topbar_pane_border_rgb), @TypeOf(pardes_topbar_pane_border_rgb));
@@ -2658,6 +2682,7 @@ test "pardes.h matches the Zig boundary" {
try expectEqual(@as(u32, c.PARDES_COLOR_INDEXED), color_indexed);
try expectEqual(@as(u8, c.PARDES_CELL_DEFAULT), cell_flag_default);
try expectEqual(@as(u8, c.PARDES_CELL_TAGLINE), cell_flag_tagline);
+ try expectEqual(@as(u8, c.PARDES_CELL_HOVER), cell_flag_hover);
try expectEqual(@as(u16, c.PARDES_TOPBAR_H), pardes.TOPBAR_H);
try expectEqual(@as(u32, c.PARDES_SCENE_CRT), scene_flag_crt);
try expectEqual(@as(u32, c.PARDES_SCENE_RIPPLE), scene_flag_ripple);
@@ -2723,9 +2748,10 @@ test "pardes.h matches the Zig boundary" {
encodeAttrs(.{ .ul = .curly }),
);
- try expectEqual(@as(u8, 0), encodeCellFlags(false, .body));
- try expectEqual(cell_flag_tagline, encodeCellFlags(false, .tagline));
- try expectEqual(cell_flag_default | cell_flag_tagline, encodeCellFlags(true, .tagline));
+ try expectEqual(@as(u8, 0), encodeCellFlags(false, .body, false));
+ try expectEqual(cell_flag_hover, encodeCellFlags(false, .body, true));
+ try expectEqual(cell_flag_tagline, encodeCellFlags(false, .tagline, false));
+ try expectEqual(cell_flag_default | cell_flag_tagline, encodeCellFlags(true, .tagline, false));
try expectEqual(pardes.config.gui_tagline_font_percent, pardes_gui_tagline_font_percent());
}
@@ -2947,10 +2973,16 @@ 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 }));
- blankTagCells(&canonical, 8, 3, &core.surface.tag_layers);
+ // 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" {