summaryrefslogtreecommitdiff
path: root/src/draw.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/draw.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/draw.zig')
-rw-r--r--src/draw.zig32
1 files changed, 12 insertions, 20 deletions
diff --git a/src/draw.zig b/src/draw.zig
index 5db6da30..d84f2033 100644
--- a/src/draw.zig
+++ b/src/draw.zig
@@ -90,7 +90,7 @@ pub fn render(p: *Pardes, arena: std.mem.Allocator) !*Surface {
pane.context_row_limit = null;
s.body_layers[id].rows = 0;
Messages.collectNotices(p, pane, p.rects[id], id);
- try renderPane(p, arena, pane, p.rects[id], id, id == p.active);
+ try renderPane(p, s, arena, pane, p.rects[id], id, id == p.active);
try body_layer.renderBodyLayer(p, arena, pane, p.rects[id], id);
}
for (&p.panes, 0..) |*slot, id| {
@@ -156,7 +156,7 @@ pub fn render(p: *Pardes, arena: std.mem.Allocator) !*Surface {
}
}
- try paintHeaders(p);
+ try paintHeaders(p, s);
try renderTagLayers(p, arena);
switch (p.drag) {
@@ -397,11 +397,10 @@ fn hasGlyph(cell: *const Cell) bool {
return !cell.default and !(cell.len == 1 and cell.text[0] == ' ');
}
-fn renderPane(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rect, id: usize, active: bool) !void {
+fn renderPane(p: *Pardes, s: *Surface, arena: std.mem.Allocator, pane: *Pane, r: Rect, id: usize, active: bool) !void {
const tz = tracy.zone(@src(), "renderPane");
defer tz.end();
if (r.w <= config.GUTTER or r.h == 0) return;
- const s = &p.surface;
const th = p.theme();
const chrome = p.chromeTheme();
const tx = r.x + config.GUTTER; // text area (tag + body), right of the gutter
@@ -436,14 +435,14 @@ fn renderPane(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rect, id: us
.tty => config.tag_tty,
}, box_style);
- try paintPaneTag(p, arena, pane, r, id, active, p.tagCapacity(r.w -| TAG_TEXT_INSET));
+ try paintPaneTag(p, s, arena, pane, r, id, active, p.tagCapacity(r.w -| TAG_TEXT_INSET));
// Folded panes retain their body state, but neither body pixels nor
// its cursor belong outside the one-row tag rectangle.
if (pane.collapsed) return;
if (comptime pdf_enabled)
- if (pane.hasPdf() and panes.Pdf.draw(p, pane, r, id, tx, tw)) return;
+ if (pane.hasPdf() and panes.Pdf.draw(p, s, pane, r, id, tx, tw)) return;
if (pane.image) |*iv| {
// Below the notice chips: a picture is drawn after the cells (the
@@ -451,7 +450,7 @@ fn renderPane(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rect, id: us
// painted out. Text gets the overlay; a picture gives up the rows.
const shown = pane.notices.span();
const image_h = (r.h -| pane.tag_rows) -| shown;
- panes.Image.draw(p, iv, @intCast(id), pane.serial, tx, body_y + shown, tw, image_h);
+ panes.Image.draw(p, s, iv, @intCast(id), pane.serial, tx, body_y + shown, tw, image_h);
// thumbless, but the same one column as the real scrollbar below —
// that is the whole point of drawing it, and like that one it runs
// past the notice bands so the gutter has no notch in it
@@ -460,14 +459,13 @@ fn renderPane(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rect, id: us
return;
}
- try body_layer.renderBody(p, arena, pane, r, id, active, true);
+ try body_layer.renderBody(p, s, arena, pane, r, id, active, true);
}
/// The workspace and column tags as the grid shows them: their text, the
/// word under the pointer and, in the one being edited, its selection and
/// caret. Their tag layers are drawn by renderTagLayers.
-pub fn paintHeaders(p: *Pardes) !void {
- const s = &p.surface;
+pub fn paintHeaders(p: *Pardes, s: *Surface) !void {
const chrome = p.chromeTheme();
const th = p.theme();
// global tagbar: full width, top rows — unless a native shell has
@@ -623,12 +621,7 @@ pub fn renderTagLayers(p: *Pardes, arena: std.mem.Allocator) !void {
const storage = try arena.alloc(Cell, pitch * rows);
@memset(storage, .{});
var temporary: Surface = .{ .cols = columns + TAG_TEXT_INSET, .rows = rows, .cells = storage };
- std.mem.swap(Surface, &p.surface, &temporary);
- paintPaneTag(p, arena, pane, .{ .x = 0, .y = 0, .w = columns + TAG_TEXT_INSET, .h = rows }, id, id == p.active, p.tagCapacity(r.w - TAG_TEXT_INSET)) catch |err| {
- std.mem.swap(Surface, &p.surface, &temporary);
- return err;
- };
- std.mem.swap(Surface, &p.surface, &temporary);
+ try paintPaneTag(p, &temporary, arena, pane, .{ .x = 0, .y = 0, .w = columns + TAG_TEXT_INSET, .h = rows }, id, id == p.active, p.tagCapacity(r.w - TAG_TEXT_INSET));
// A layer to a line: the first in the pane's own slot, the rest
// in the slots kept for the lines of taller tags.
for (0..rows) |line| {
@@ -791,8 +784,7 @@ fn renderHeaderLayer(p: *Pardes, arena: std.mem.Allocator, index: usize, kind: T
}
}
-pub fn paintPaneTag(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rect, id: usize, active: bool, capacity: u16) !void {
- const s = &p.surface;
+pub fn paintPaneTag(p: *Pardes, s: *Surface, arena: std.mem.Allocator, pane: *Pane, r: Rect, id: usize, active: bool, capacity: u16) !void {
const th = p.theme();
const chrome = p.chromeTheme();
const tx = r.x + @min(TAG_TEXT_INSET, r.w);
@@ -867,7 +859,7 @@ pub fn paintPaneTag(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rect,
if (p.look_hover_preview) |preview| {
if (preview.pane == id and preview.serial == pane.serial) if (preview.sel) |sel| if (@min(sel.r0, sel.r1) < rows) {
const preview_bg = colors.mix(page_rgb, colors.mix(page_rgb, th.sel_bg));
- body_layer.paintPointerSelection(p, pane, r, tx, tw, tag_y, body_y, sel, preview_bg, null);
+ body_layer.paintPointerSelection(p, s, pane, r, tx, tw, tag_y, body_y, sel, preview_bg, null);
};
}
// The tag's own selections, while it has the keyboard, a line at a
@@ -908,6 +900,6 @@ pub fn paintPaneTag(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rect,
for (pane.sel, 0..) |selection, button| {
if (selection.state == .none or (selection.state == .done and button != 0)) continue;
if (@min(selection.r0, selection.r1) >= rows) continue;
- body_layer.paintPointerSelection(p, pane, r, tx, tw, tag_y, body_y, selection, th.sel_bg, th.sel_fg);
+ body_layer.paintPointerSelection(p, s, pane, r, tx, tw, tag_y, body_y, selection, th.sel_bg, th.sel_fg);
}
}