summaryrefslogtreecommitdiff
path: root/src/pdf_pane.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-08-24 22:18:24 -0300
committerGabriel Schneider <[email protected]>2026-08-25 09:42:07 -0300
commit08eadb3b46dc70a297ff89308dd1cd26237785d6 (patch)
tree66152ea18a3639dd216b6e658d9cd5503201afdc /src/pdf_pane.zig
parentc3d0b84b7961ae26d2d654e7120821cc2d83d20d (diff)
downloadpardes-08eadb3b46dc70a297ff89308dd1cd26237785d6.tar.gz
pardes-08eadb3b46dc70a297ff89308dd1cd26237785d6.zip
syntax + pdf_pane + file_pane: prose grammars paint, pdf Esc cancels, gj/gk walk wrapped rows
Diffstat (limited to 'src/pdf_pane.zig')
-rw-r--r--src/pdf_pane.zig35
1 files changed, 30 insertions, 5 deletions
diff --git a/src/pdf_pane.zig b/src/pdf_pane.zig
index f34a457d..8f163488 100644
--- a/src/pdf_pane.zig
+++ b/src/pdf_pane.zig
@@ -424,6 +424,18 @@ pub const State = if (enabled) struct {
if (changed) state.invalidateRaster(state.page);
}
+ /// Escape's cancel: everything transient a reader can SEE — the mouse
+ /// selection and the search overlay — and nothing that says WHERE in the
+ /// document they are. Page, scroll, fit and tint are what the pane is, not
+ /// chrome. Allocation-free, so it cannot half-cancel.
+ pub fn cancelChrome(state: *@This(), gpa: std.mem.Allocator) void {
+ state.clearDrag();
+ state.clearSelection(gpa);
+ if (state.search_query.len == 0) return;
+ state.dropSearchQuery(gpa);
+ state.invalidateRaster(state.page);
+ }
+
fn invalidatePage(state: *@This(), gpa: std.mem.Allocator) void {
state.dropSearchResults(gpa);
state.dropSelection(gpa);
@@ -438,13 +450,23 @@ pub const State = if (enabled) struct {
state.search_hit = 0;
}
+ /// Forget the query, its hits, and every flag derived from them. Shared by
+ /// the cancel above and by the replacement below, which owns new bytes the
+ /// caller allocated before anything here was dropped.
+ fn dropSearchQuery(state: *@This(), gpa: std.mem.Allocator) void {
+ if (state.search_query.len > 0) gpa.free(state.search_query);
+ state.search_query = &.{};
+ state.search_hit = 0;
+ state.dropSearchResults(gpa);
+ state.highlights_dirty = false;
+ state.search_reveal_pending = false;
+ }
+
pub fn setSearchQuery(state: *@This(), gpa: std.mem.Allocator, query: []const u8) !void {
if (std.mem.eql(u8, state.search_query, query)) return;
const owned = try gpa.dupe(u8, query);
- if (state.search_query.len > 0) gpa.free(state.search_query);
+ state.dropSearchQuery(gpa);
state.search_query = owned;
- state.search_hit = 0;
- state.dropSearchResults(gpa);
state.highlights_dirty = query.len > 0;
state.search_reveal_pending = query.len > 0;
state.invalidateRaster(state.page);
@@ -709,7 +731,6 @@ pub const ScrollResult = struct { active_page: usize };
pub const CellPixels = struct { w: u16, h: u16 };
pub const NormalHost = enum {
none,
- last,
leader,
command_line,
search,
@@ -1384,7 +1405,11 @@ pub fn applyNormal(
if (comptime !enabled) return .{};
var result: NormalResult = .{};
switch (semantic) {
- .escape => result.host = .last,
+ // Escape stays in the document and cancels what is drawn over it. The
+ // way OUT of a PDF is Shift-Escape, which the host takes before this
+ // parse ever runs — a reader who pressed Escape to drop a selection
+ // was not asking to be moved to another pane.
+ .escape => state.cancelChrome(gpa),
.move => |move| switch (move.motion) {
.down => result.page_changed = moveRows(
state,