Layout: Next or Previous Spread at the last or first spread is no jump for Go Back

At the end of the document the spread command only centres the spread in view,
which can move the view from one facing page to the other. That move was
recorded as a jump, so Go Back went to the other page of the same spread
instead of the page viewed before.
This commit is contained in:
Marco Arnulfo
2026-10-23 15:39:47 +02:00
parent 5539e28647
commit 13f6739f49
2 changed files with 58 additions and 4 deletions
+17 -4
View File
@@ -326,16 +326,29 @@ fn present_fit(app: &mut DesignApp, rect: Rect) {
/// Centre the spread `delta` spreads after (or before) the one in view (keeps zoom); stops at the
/// first and last spread. Parent spreads while they are being edited.
pub fn step_spread(app: &mut DesignApp, delta: isize) {
note_jump(app, false);
// At the first or last spread only the view moves, within the spread: no jump for Go Back.
let jump = spread_step(app, delta).is_some_and(|(cur, to)| cur != to);
if jump {
note_jump(app, false);
}
centre_spread(app, delta);
note_jump(app, true);
if jump {
note_jump(app, true);
}
}
/// The slot in view and the one `delta` spreads after (or before) it, stopping at the ends.
fn spread_step(app: &DesignApp, delta: isize) -> Option<(usize, usize)> {
let st = app.session.active()?;
let layout = CanvasLayout::new(&st.doc, st.editing_parents);
let cur = current_slot(app, &layout)?;
Some((cur, cur.saturating_add_signed(delta).min(layout.slots.len().saturating_sub(1))))
}
fn centre_spread(app: &mut DesignApp, delta: isize) {
let Some((_, to)) = spread_step(app, delta) else { return };
let Some(st) = app.session.active() else { return };
let layout = CanvasLayout::new(&st.doc, st.editing_parents);
let Some(cur) = current_slot(app, &layout) else { return };
let to = cur.saturating_add_signed(delta).min(layout.slots.len().saturating_sub(1));
let Some(b) = layout.slots.get(to).map(|s| s.bounds) else { return };
let Some(rect) = app.canvas_rect else { return };
if let Some(v) = app.view_mut() {
+41
View File
@@ -4289,6 +4289,47 @@ mod tests {
assert_eq!(page(&app), 4, "Go Back returns to the page before Next Page");
}
#[test]
fn next_spread_at_the_last_spread_is_no_jump() {
let mut app = crate::DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
app.session.execute("file.new", &json!({"pages": 7, "facingPages": true})).unwrap();
let ctx = egui::Context::default();
let frame = |app: &mut crate::DesignApp, events: Vec<egui::Event>| {
let input = egui::RawInput {
screen_rect: Some(egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1400.0, 900.0))),
events,
..Default::default()
};
let mut out = ctx.run_ui(input, |ui| {
app.logic(&ui.ctx().clone());
app.ui(ui);
});
out.textures_delta.clear();
};
let key = |k: egui::Key, modifiers: egui::Modifiers| {
vec![
egui::Event::Key { key: k, physical_key: None, pressed: true, repeat: false, modifiers },
egui::Event::Key { key: k, physical_key: None, pressed: false, repeat: false, modifiers },
]
};
let page = |app: &crate::DesignApp| crate::canvas::current_page(app).unwrap();
let cmd = egui::Modifiers { command: true, ctrl: true, ..Default::default() };
let alt = egui::Modifiers::ALT;
frame(&mut app, vec![]);
frame(&mut app, vec![]);
// Pages 1 | 2-3 | 4-5 | 6-7: page 6 is the left page of the last spread.
app.run("view.goToPage", json!({"page": 6})).unwrap();
frame(&mut app, vec![]);
assert_eq!(page(&app), 5);
// Next Spread has no spread to go to; centring the last one moves the view to its fold.
frame(&mut app, key(egui::Key::PageDown, alt));
frame(&mut app, key(egui::Key::PageDown, alt));
assert_ne!(page(&app), 5, "the view moved to the right page");
frame(&mut app, key(egui::Key::PageUp, cmd));
assert_eq!(page(&app), 0, "Go Back returns to the page before Go to Page");
assert!(!menu_enabled(&app, "view.goBack"));
}
#[test]
fn go_back_returns_to_the_view_a_jump_left_and_scrolling_adds_no_entry() {
let mut app = crate::DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());