From 5ab84500fed60deb67a3ef451d20fb765ad86a18 Mon Sep 17 00:00:00 2001 From: MarekSpodBiedry <73122968+MarekSpodBiedry@users.noreply.github.com> Date: Sun, 11 Oct 2026 11:39:36 +0200 Subject: [PATCH] Show the first parent page behind single-sided pages (#429) --- crates/doc/src/build.rs | 17 ++++++++++++++++- crates/tools/src/snap.rs | 15 +++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/crates/doc/src/build.rs b/crates/doc/src/build.rs index c274f213..e31dc3c7 100644 --- a/crates/doc/src/build.rs +++ b/crates/doc/src/build.rs @@ -549,7 +549,9 @@ impl Document { let pi = self.parent_index(page.parent?)?; let ps = &self.parents[pi]; // A parent spread can come without pages from a file. - let idx = if ps.pages.len() >= 2 && page.side == PageSide::Left { 0 } else { ps.pages.len().checked_sub(1)? }; + // A single-sided page takes the parent's first page, like a left-hand page. + let first = ps.pages.len() >= 2 && matches!(page.side, PageSide::Left | PageSide::Single); + let idx = if first { 0 } else { ps.pages.len().checked_sub(1)? }; Some((pi, idx)) } } @@ -584,6 +586,19 @@ mod tests { assert_eq!(d.parent_page_for(1), None); } + /// A single-sided page applying a two-page parent shows the parent's first page. + #[test] + fn single_sided_page_shows_first_parent_page() { + let mut d = Document::new(&NewDocument { pages: 2, facing_pages: false, ..Default::default() }); + let extra = d.parents[0].pages[0].clone(); + Arc::make_mut(&mut d.parents[0]).pages.push(extra); + assert_eq!(d.parents[0].pages.len(), 2); + assert_eq!(d.parent_page_for(0), Some((0, 0))); + assert_eq!(d.parent_page_for(1), Some((0, 0))); + Arc::make_mut(&mut d.parents[0]).pages.clear(); + assert_eq!(d.parent_page_for(0), None); + } + #[test] fn non_facing_one_page_per_spread() { let d = Document::new(&NewDocument { pages: 3, facing_pages: false, ..Default::default() }); diff --git a/crates/tools/src/snap.rs b/crates/tools/src/snap.rs index 6d5c8f6a..50c95f66 100644 --- a/crates/tools/src/snap.rs +++ b/crates/tools/src/snap.rs @@ -1291,9 +1291,10 @@ fn offer_parent_chain( offer_parent_chain(best, cx, req, flags, axis, tol, doc_x, doc_side, overridden, next_index, seen); } -/// Left page of a facing parent, otherwise the last page. Same choice as `Document::parent_page_for`. +/// First page of a multi-page parent for a left-hand or single-sided page, otherwise the last page. +/// Same choice as `Document::parent_page_for`. fn shown_parent_page_index(page_count: usize, side: PageSide) -> Option { - if page_count >= 2 && side == PageSide::Left { Some(0) } else { page_count.checked_sub(1) } + if page_count >= 2 && matches!(side, PageSide::Left | PageSide::Single) { Some(0) } else { page_count.checked_sub(1) } } fn shift_x(r: Rect, dx: f64) -> Rect { @@ -1444,6 +1445,16 @@ mod tests { use super::*; + /// Snapping offers the parent page the canvas shows: a single-sided page uses a two-page parent's first page. + #[test] + fn shown_parent_page_matches_page_side() { + assert_eq!(shown_parent_page_index(2, PageSide::Single), Some(0)); + assert_eq!(shown_parent_page_index(2, PageSide::Left), Some(0)); + assert_eq!(shown_parent_page_index(2, PageSide::Right), Some(1)); + assert_eq!(shown_parent_page_index(1, PageSide::Single), Some(0)); + assert_eq!(shown_parent_page_index(0, PageSide::Single), None); + } + // The factory flags are const. Pin them anyway. #[test] #[allow(clippy::assertions_on_constants)]