From 461200e74a1583427fe8bcb46ba03343b0165178 Mon Sep 17 00:00:00 2001 From: RiceDee <345114586+RiceDee@users.noreply.github.com> Date: Sun, 1 Nov 2026 18:37:23 +1100 Subject: [PATCH] fix(ui): bookmark_position keeps a sliver page when nothing else shows; the rail test checks the rail's real edges (#5636) A page showing under a pixel at the window's top is ignored in favour of the next page, but still chosen when no other page shows. The rail test no longer tolerates 2 pt of overflow: it compares the page and zoom fields with the rail's own edges. Co-Authored-By: Claude Sonnet 5.5 --- crates/ui-egui/src/canvas/navigation.rs | 16 ++++++++++------ crates/ui-egui/tests/it/content.rs | 4 +++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/crates/ui-egui/src/canvas/navigation.rs b/crates/ui-egui/src/canvas/navigation.rs index 30732ad499..b8a53d7c8b 100644 --- a/crates/ui-egui/src/canvas/navigation.rs +++ b/crates/ui-egui/src/canvas/navigation.rs @@ -33,12 +33,16 @@ impl DocView { // nearest one below. let top = self.viewport_screen.top(); // A page showing less than a pixel is rounding, not what the reader is looking at: a jump - // to the next page leaves the previous one's bottom edge within a pixel of the top. - let showing = || self.screen_xforms.iter().filter(|(_, xf)| xf.rect.bottom() > top + 1.0); - let (page, xf) = showing() - .filter(|(_, xf)| xf.rect.top() <= top) - .min_by_key(|(p, _)| *p) - .or_else(|| showing().min_by(|a, b| a.1.rect.top().total_cmp(&b.1.rect.top()).then(a.0.cmp(&b.0))))?; + // to the next page leaves the previous one's bottom edge within a pixel of the top. Such a + // sliver still counts when nothing else shows (the last page at the very end). + let pick = |slack: f32| { + let showing = || self.screen_xforms.iter().filter(move |(_, xf)| xf.rect.bottom() > top + slack); + showing() + .filter(|(_, xf)| xf.rect.top() <= top) + .min_by_key(|(p, _)| *p) + .or_else(|| showing().min_by(|a, b| a.1.rect.top().total_cmp(&b.1.rect.top()).then(a.0.cmp(&b.0)))) + }; + let (page, xf) = pick(1.0).or_else(|| pick(0.0))?; let p = info.pages.get(*page)?; // Above the page (in the gap before it) is the page's own top. The window's vertical is // the view's y, or its x when the view is turned a quarter. diff --git a/crates/ui-egui/tests/it/content.rs b/crates/ui-egui/tests/it/content.rs index 61c86644e4..55a78dfbab 100644 --- a/crates/ui-egui/tests/it/content.rs +++ b/crates/ui-egui/tests/it/content.rs @@ -1433,11 +1433,13 @@ fn typing_in_one_tab_never_reaches_another_tabs_added_text() { fn rail_page_and_zoom_fields_match_and_fit_the_rail() { let mut h = harness(); h.run_steps(3); + // The rail is 49 pt wide at the window's right edge (1400 pt wide), inside a 1 pt border. + let (rail_left, rail_right) = (1400.0 - 49.0 + 1.0, 1400.0 - 1.0); let rail = h.get_by_label("Bookmarks").rect(); let (page, zoom) = (h.get_by_label("Current page").rect(), h.get_by_label("Zoom").rect()); assert_eq!((page.width(), page.height()), (zoom.width(), zoom.height()), "{page:?} vs {zoom:?}"); for r in [page, zoom] { - assert!(r.left() >= rail.left() - 2.0 && r.right() <= rail.right() + 2.0, "{r:?} is cut off by the rail ({rail:?})"); + assert!(r.left() >= rail_left && r.right() <= rail_right, "{r:?} is cut off by the rail ({rail_left}..{rail_right})"); assert!((r.center().x - rail.center().x).abs() < 1.0, "{r:?} is off the rail's centre ({rail:?})"); } }