summaryrefslogtreecommitdiff
path: root/src/body_layer.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-28 13:24:06 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:14 -0300
commite11048c20a50b67841e648a4d5ddd7490bc9c9b7 (patch)
tree2f4737d83c5f894e8b7a68e5798bd2a2cebb70b9 /src/body_layer.zig
parente0b5564f04aa0ae6666f29ef29c768d800fbabce (diff)
downloadpardes-e11048c20a50b67841e648a4d5ddd7490bc9c9b7.tar.gz
pardes-e11048c20a50b67841e648a4d5ddd7490bc9c9b7.zip
Paint into a target Surface instead of swapping the frame's out
The painters of a pane's body and tag (renderBody and its selection, gutter, syntax, ANSI and wrap-marker passes; paintPaneTag; paintHeaders; renderPane; the image and PDF grid paths) take the Surface they paint as a parameter. The body layer and the tag layers paint straight into their own temporary surfaces, and the std.mem.swap of p.surface around those calls is gone. No behaviour change.
Diffstat (limited to 'src/body_layer.zig')
-rw-r--r--src/body_layer.zig38
1 files changed, 16 insertions, 22 deletions
diff --git a/src/body_layer.zig b/src/body_layer.zig
index aa52957c..db4d9524 100644
--- a/src/body_layer.zig
+++ b/src/body_layer.zig
@@ -145,7 +145,7 @@ test "compact context borders separate source gaps and preserve multiline header
// ---- drawing a pane body into the surface ----
-fn paintTerminalSelection(p: *Pardes, pane: *Pane, r: Rect, rows: []const Pane.PointerRow, bg: [3]u8, fg: ?[3]u8) void {
+fn paintTerminalSelection(p: *Pardes, s: *Surface, pane: *Pane, r: Rect, rows: []const Pane.PointerRow, bg: [3]u8, fg: ?[3]u8) void {
const body = bodyText(p, p.scratch.allocator(), pane) catch return;
const raw = pane.body.mode == .tty;
const tx = r.x + config.GUTTER;
@@ -178,7 +178,7 @@ fn paintTerminalSelection(p: *Pardes, pane: *Pane, r: Rect, rows: []const Pane.P
var col = panes.File.rawDisplayCol(line, lo);
const end = panes.File.rawDisplayCol(line, hi);
while (col < end and col < width) : (col += 1) {
- const cell = p.surface.at(tx + @as(u16, @intCast(col)), body_y + @as(u16, @intCast(visible)));
+ const cell = s.at(tx + @as(u16, @intCast(col)), body_y + @as(u16, @intCast(visible)));
cell.default = false;
cell.style.bg = .{ .rgb = bg };
if (fg) |ink| cell.style.fg = .{ .rgb = ink };
@@ -187,8 +187,8 @@ fn paintTerminalSelection(p: *Pardes, pane: *Pane, r: Rect, rows: []const Pane.P
}
}
-fn paintSourceSelection(p: *Pardes, pane: *Pane, r: Rect, rows: []const Pane.PointerRow, bg: [3]u8, fg: ?[3]u8) void {
- if (pane.isTerminal()) return paintTerminalSelection(p, pane, r, rows, bg, fg);
+fn paintSourceSelection(p: *Pardes, s: *Surface, pane: *Pane, r: Rect, rows: []const Pane.PointerRow, bg: [3]u8, fg: ?[3]u8) void {
+ if (pane.isTerminal()) return paintTerminalSelection(p, s, pane, r, rows, bg, fg);
const terminal_lines = if (pane.file == null) panes.Terminal.cursorLines(p, pane) catch return else &.{};
const tx = r.x + config.GUTTER;
const width = r.w -| config.GUTTER;
@@ -209,7 +209,7 @@ fn paintSourceSelection(p: *Pardes, pane: *Pane, r: Rect, rows: []const Pane.Poi
var col = prefix + @as(i32, @intCast(panes.File.rawDisplayCol(line[start..], lo - start)));
const last = prefix + @as(i32, @intCast(panes.File.rawDisplayCol(line[start..], hi - start)));
while (col < last and col < width) : (col += 1) {
- const cell = p.surface.at(tx + @as(u16, @intCast(col)), body_y + @as(u16, @intCast(visible)));
+ const cell = s.at(tx + @as(u16, @intCast(col)), body_y + @as(u16, @intCast(visible)));
cell.default = false;
cell.style.bg = .{ .rgb = bg };
if (fg) |ink| cell.style.fg = .{ .rgb = ink };
@@ -220,6 +220,7 @@ fn paintSourceSelection(p: *Pardes, pane: *Pane, r: Rect, rows: []const Pane.Poi
pub fn paintPointerSelection(
p: *Pardes,
+ s: *Surface,
pane: *Pane,
r: Rect,
tx: u16,
@@ -230,8 +231,7 @@ pub fn paintPointerSelection(
bg: [3]u8,
fg: ?[3]u8,
) void {
- if (pane.pointerSelection(sl)) |rows| return paintSourceSelection(p, pane, r, rows, bg, fg);
- const s = &p.surface;
+ if (pane.pointerSelection(sl)) |rows| return paintSourceSelection(p, s, pane, r, rows, bg, fg);
const r0 = @min(sl.r0, sl.r1);
const r1 = @max(sl.r0, sl.r1);
const c0 = @max(0, @min(sl.c0, sl.c1));
@@ -278,13 +278,8 @@ pub fn renderBodyLayer(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rec
const temporary_cells = try arena.alloc(Cell, @as(usize, r.w) * (logical_rows + pane.tag_rows));
@memset(temporary_cells, .{});
var temporary: Surface = .{ .cols = r.w, .rows = @intCast(logical_rows + pane.tag_rows), .cells = temporary_cells };
- std.mem.swap(Surface, &p.surface, &temporary);
const synthetic: Rect = .{ .x = 0, .y = 0, .w = r.w, .h = @intCast(logical_rows + pane.tag_rows) };
- renderBody(p, arena, pane, synthetic, id, id == p.active, false) catch |err| {
- std.mem.swap(Surface, &p.surface, &temporary);
- return err;
- };
- std.mem.swap(Surface, &p.surface, &temporary);
+ try renderBody(p, &temporary, arena, pane, synthetic, id, id == p.active, false);
const first_row: usize = if (p.settings.tag_bottom) 0 else pane.tag_rows;
for (0..logical_rows) |row| {
const start = (row + first_row) * r.w + config.GUTTER;
@@ -304,8 +299,7 @@ pub fn renderBodyLayer(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rec
}
}
-pub fn renderBody(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rect, id: usize, active: bool, scrollbar: bool) !void {
- const s = &p.surface;
+pub fn renderBody(p: *Pardes, s: *Surface, arena: std.mem.Allocator, pane: *Pane, r: Rect, id: usize, active: bool, scrollbar: bool) !void {
const th = p.theme();
const chrome = p.chromeTheme();
const tx = r.x + config.GUTTER;
@@ -343,12 +337,12 @@ pub fn renderBody(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rect, id
const tz_color = tracy.zone(@src(), "paneRecolor");
// File layout is independent of whether its producer requests colors.
if (pane.file) |*f| {
- panes.File.drawGutter(p, pane, r, tx, tw, body_h, active);
+ panes.File.drawGutter(p, s, pane, r, tx, tw, body_h, active);
if (p.settings.colors and pane.colorAlgo() != .none)
- panes.File.recolorSyntax(p, pane, f, r, tx, tw, body_h);
- panes.File.drawWrapMarkers(p, pane, r, tx, tw, body_h, pane_bg);
+ panes.File.recolorSyntax(p, s, pane, f, r, tx, tw, body_h);
+ panes.File.drawWrapMarkers(p, s, pane, r, tx, tw, body_h, pane_bg);
} else if (pane.isTerminal() and p.settings.colors) {
- panes.Terminal.recolorAnsi(p, pane, r, tx, tw, body_h, body);
+ panes.Terminal.recolorAnsi(p, s, pane, r, tx, tw, body_h, body);
}
tz_color.end();
@@ -366,9 +360,9 @@ pub fn renderBody(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rect, id
// macOS composes its glass overlay over exactly these.
const preview_bg = colors.mix(page_rgb, colors.mix(page_rgb, th.sel_bg));
if (preview.file_word) |word|
- panes.File.paintWordSelection(p, pane, r, word.row, word.lo, word.hi, preview_bg)
+ panes.File.paintWordSelection(p, s, pane, r, word.row, word.lo, word.hi, preview_bg)
else if (preview.sel) |sel|
- paintPointerSelection(p, pane, r, tx, tw, tag_y, body_y, sel, preview_bg, null);
+ paintPointerSelection(p, s, pane, r, tx, tw, tag_y, body_y, sel, preview_bg, null);
}
}
const modal_hover = if (p.look_hover_preview) |preview|
@@ -381,7 +375,7 @@ pub fn renderBody(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rect, id
// A select sweep over the tag is the tag's selection, painted there
// in the tag's own selection colours.
if (b == pardes.sel_slot and @min(sl.r0, sl.r1) < pane.tag_rows) continue;
- paintPointerSelection(p, pane, r, tx, tw, tag_y, body_y, sl, sel_btn[b], th.sel_fg);
+ paintPointerSelection(p, s, pane, r, tx, tw, tag_y, body_y, sl, sel_btn[b], th.sel_fg);
}
// modal line selection (helix `x`): whole body rows, normal mode only
if ((pane.body.mode == .normal or modal_hover) and pane.body.msel.active) {