From d1b7e986982150e7f4f1096db09d63d335f39d4a Mon Sep 17 00:00:00 2001 From: Max Abramowitz Date: Fri, 9 Oct 2026 14:24:32 -0400 Subject: [PATCH] Pages panel: keep a short page current; context menu and drag to reorder (#573) * View: the middle button pans instead of drawing with markup tools egui's drag responses accept every pointer button, so on Windows and macOS a middle-button drag reached the page tools: with Draw (ink) selected it drew a stroke, and other tools cropped, selected or moved. Linux was unaffected because its middle-click auto-scroll already blocks page input. Outside Linux a middle-button drag that starts in the viewport (document or Organize Pages) now pans 1:1 while the button is held, with a Grabbing cursor, and page input is blocked from the press through the release. Linux keeps its latched auto-scroll. Co-Authored-By: Claude Opus 5.5 * M3: a short page gone to from the Pages panel stays current The current page was whichever page showed most of itself. Going to a short page (a cheque) above a tall one scrolls it to the top, where the tall page below shows more, so the current page and the Pages panel's highlight moved to the tall page instead of the one clicked. The page navigated to now stays current while it is wholly on screen. Co-Authored-By: Claude Opus 5.5 * M4: page operations from the Pages panel: context menu and drag to reorder Right-clicking a thumbnail offers Extract pages, Cut, Copy and Paste after, on the selection or on the page clicked (which becomes the selection), as the organize grid does. Dragging thumbnails moves the selection, or the page grabbed, to the gap it is dropped on: a bar marks the gap, the page count follows the pointer, and the panel scrolls near its edges. The move is one undoable step; the moved pages stay selected and the current page follows its page. Documents that forbid assembly keep click-only thumbnails. Parity: view.thumbnail-context-ops is shipped, with what it still lacks. Co-Authored-By: Claude Opus 5.5 * Pages panel: only the primary button drags thumbnails; a drag left over after the panel closed or the document changed is dropped --------- Co-authored-by: Cody Vogt Co-authored-by: Claude Opus 5.5 Co-authored-by: William Max & Co. Co-authored-by: Brandon Thomas --- crates/ui-egui/src/canvas.rs | 12 ++- crates/ui-egui/src/panels.rs | 142 +++++++++++++++++++++++++++++++- crates/ui-egui/tests/editing.rs | 37 +++++++++ crates/ui-egui/tests/viewer.rs | 59 +++++++++++++ parity/acrobat-features.toml | 6 +- 5 files changed, 248 insertions(+), 8 deletions(-) diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 3084f62383..948d153fbc 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -285,6 +285,8 @@ pub struct DocView { pub sign: crate::sign_ui::SignView, /// Organize: the pages being dragged to a new place. pub org_drag: Option>, + /// Pages panel: the pages being dragged to a new place. + pub panel_drag: Option>, /// Marquee Zoom / Snapshot: the rectangle being dragged (page, start), and a finished one. pub marquee: Option<(usize, Pos2)>, pub marquee_done: Option, @@ -417,6 +419,7 @@ impl DocView { crop_drag: None, sign: Default::default(), org_drag: None, + panel_drag: None, marquee: None, marquee_done: None, fill_text: None, @@ -446,6 +449,8 @@ impl DocView { self.goto_point = None; self.zoom_anchor = None; self.flash = None; + // A thumbnail drag holds page indexes from before the change. + self.panel_drag = None; } /// Pages an organize command acts on: the selection, or the current page. @@ -1544,6 +1549,7 @@ pub fn document_area(app: &mut PdfCraftApp, index: usize, ui: &mut egui::Ui) { let mut current = view.current; let mut best_overlap = -1.0f32; let mut current_overlap = -1.0f32; + let mut current_height = f32::INFINITY; let pointer = ui.input(|i| i.pointer.hover_pos()); for &i in &visible_pages { let r = snap_to_pixels(rects[i].translate(origin.to_vec2()), ppp); @@ -1559,6 +1565,7 @@ pub fn document_area(app: &mut PdfCraftApp, index: usize, ui: &mut egui::Ui) { let overlap = r.intersect(visible).height(); if i == view.current { current_overlap = overlap; + current_height = r.height(); } // Pages shown equally (two rows wholly on screen) differ only by rounding: the // topmost one counts. @@ -2006,7 +2013,10 @@ pub fn document_area(app: &mut PdfCraftApp, index: usize, ui: &mut egui::Ui) { } // The page navigated to stays current while no page shows more of itself, so the next // page step starts from it rather than from a page further down the screen (#188). - if current_overlap >= best_overlap - TIE { + // It also stays current while it is wholly on screen: a short page (a cheque) gone to + // from the Pages panel shows less of itself than the tall page below it, and must not + // hand the highlight to that page. + if current_overlap >= best_overlap - TIE || current_overlap >= current_height - TIE { current = view.current; } if view.layout != PageLayout::Single { diff --git a/crates/ui-egui/src/panels.rs b/crates/ui-egui/src/panels.rs index 377636f41c..738d61defa 100644 --- a/crates/ui-egui/src/panels.rs +++ b/crates/ui-egui/src/panels.rs @@ -546,7 +546,7 @@ pub fn right_panel(app: &mut PdfCraftApp, ui: &mut egui::Ui) { outline_item(ui, &t, info, item, &[i], info.outline.len(), &mut ctx); } } - RightPanel::Pages => pages(ui, &t, info, view, modal, &mut nav), + RightPanel::Pages => pages(ui, &t, info, view, modal, bm_editable, &mut nav), RightPanel::Fields => fields(ui, &t, info, &doc.form, preparing, &mut nav, &mut panel_edit), RightPanel::Layers => { if info.layers.is_empty() { @@ -895,8 +895,10 @@ fn outline_item(ui: &mut egui::Ui, t: &Tokens, info: &DocInfo, item: &OutlineIte } /// The page thumbnails. A click goes to the page; ⌘/Ctrl-click and ⇧-click pick several pages -/// (the selection page commands and Print act on) without moving the document. -fn pages(ui: &mut egui::Ui, t: &Tokens, info: &DocInfo, view: &mut crate::DocView, modal: bool, nav: &mut Option