M5: reveal sidebar comments at the current zoom (#3418)

This commit is contained in:
LabDennis committed 2026-10-19 10:02:56 +02:00
1 parent 43550c448d
commit 62cde99aef
3 files changed
+67 -1

No files matched your search

+15
View File
@@ -1207,6 +1207,21 @@ impl DocView {
self.current = page;
}
/// Reveal a user-space rectangle without changing zoom, centred where the scroll bounds allow.
pub(crate) fn reveal_rect(&mut self, page: usize, rect: [f32; 4], info: &DocInfo) {
self.go_to_page(page);
self.goto_point = None;
let Some(p) = info.pages.get(self.current) else { return };
if !rect.iter().all(|v| v.is_finite()) {
return;
}
// Halve before adding so even large finite coordinates cannot overflow the midpoint.
let x = rect[0] * 0.5 + rect[2] * 0.5;
let y = rect[1] * 0.5 + rect[3] * 0.5;
let [fx, fy] = p.dest_fraction(Some(x), Some(y), self.rotation);
self.goto_point = Some((self.current, fx, fy, vec2(self.viewport_w, self.viewport_h) / 2.0));
}
/// Go to a destination, a bookmark's or a link's (ISO 32000-2 §12.3.2.2): its page, scrolled
/// and zoomed as `dest` asks, as Acrobat does. `/XYZ` puts (left, top) at the window's
/// top-left at its zoom (null or 0 keeps the zoom); `/Fit`, `/FitH` and `/FitV` switch to fit
+5 -1
View File
@@ -826,7 +826,11 @@ pub fn right_panel(app: &mut PdfCraftApp, ui: &mut egui::Ui) {
},
Some(Nav::Flash(p, r)) => {
let v = &mut app.views[index];
v.go_to_page(p);
if let Some(doc) = app.session.get(id) {
v.reveal_rect(p, r, &doc.info);
} else {
v.go_to_page(p);
}
v.flash = Some((p, r, 0.0));
}
None => {}
+47
View File
@@ -100,6 +100,53 @@ fn comments(h: &Harness<'static, PdfCraftApp>) -> Vec<Annotation> {
s.session.get(s.views[0].id).unwrap().info.annotations.clone()
}
/// #3418: choosing a sidebar comment must reveal the annotation, not just its page's top.
#[test]
fn sidebar_comment_centres_the_annotation_at_high_zoom() {
for (layout, rotation) in [("continuous", 0), ("single", 0), ("two-page", 0), ("continuous", 90), ("continuous", 180), ("continuous", 270)] {
let mut h = harness(move |app| {
app.set_option("panel", "comments").unwrap();
app.set_option("layout", layout).unwrap();
app.set_option("zoom", "800").unwrap();
app.views[0].rotation = rotation;
let shape = pdfcraft_engine::Shape::Note { at: [150.0, 90.0], icon: pdfcraft_engine::NoteIcon::Comment };
assert!(app.apply_edit(pdfcraft_engine::Edit::AddAnnotation(pdfcraft_engine::NewAnnotation {
page: 1,
style: pdfcraft_engine::Style::default_for(&shape),
shape,
contents: "Reveal this note".into(),
author: "Tester".into(),
})));
app.views[0].comments.selected = None;
app.set_option("page", "1").unwrap();
});
h.get_by_label("Reveal this note").click();
h.run_steps(8);
assert_eq!(h.state().views[0].comments.selected, Some((1, 0)), "the sidebar card was selected");
let a = &comments(&h)[0];
let view = &h.state().views[0];
let page = view.page_screen_rect(1).expect("the selected comment's page is drawn");
let (u, v) = ((a.rect[0] + a.rect[2]) / 600.0, 1.0 - (a.rect[1] + a.rect[3]) / 400.0);
let (u, v) = match rotation {
90 => (1.0 - v, u),
180 => (1.0 - u, 1.0 - v),
270 => (v, 1.0 - u),
_ => (u, v),
};
let centre = pos2(page.left() + u * page.width(), page.top() + v * page.height());
let viewport = view.viewport_rect();
assert!(centre.distance(viewport.center()) < 3.0, "{layout}, {rotation}°: comment at {centre:?}, viewport {viewport:?}");
assert!((view.zoom - 8.0).abs() < 0.01, "revealing a comment preserves zoom");
if layout == "continuous"
&& rotation == 0
&& let Ok(dir) = std::env::var("PDFCRAFT_SHOTS")
{
settle(&mut h);
h.render().unwrap().save(format!("{dir}/comment-centred.png")).unwrap();
}
}
}
#[test]
fn dragging_over_text_with_the_highlighter_highlights_it() {
let mut h = harness(|app| app.set_option("quick", "highlight").unwrap());