diff --git a/crates/engine/src/cmd/view.rs b/crates/engine/src/cmd/view.rs index 9729ae6a65..7ef7a36646 100644 --- a/crates/engine/src/cmd/view.rs +++ b/crates/engine/src/cmd/view.rs @@ -262,10 +262,14 @@ fn side_by_side(s: &mut Session, v: &Value) -> CmdResult { if let Some(old) = s.windows.side_by_side.filter(|old| *old != id) { s.ui_requests.push(json!({"windows": "sideBySide", "off": old})); } - s.windows.side_by_side = Some(id); - s.windows.sync_scroll = true; - s.ui_requests.push(json!({"windows": "sideBySide", "with": id})); - Ok(json!({"value": true, "window": id, "syncScroll": true})) + // The partner it already has keeps the user's Synchronous Scrolling choice and is not asked + // again; a new pairing starts with scrolling on. + if s.windows.side_by_side != Some(id) { + s.windows.side_by_side = Some(id); + s.windows.sync_scroll = true; + s.ui_requests.push(json!({"windows": "sideBySide", "with": id})); + } + Ok(json!({"value": true, "window": id, "syncScroll": s.windows.sync_scroll})) } /// The browser keeps full screen to itself (its own F11); the web app can't ask for it. diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index c9f7861d9b..cbf708daef 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -10490,6 +10490,10 @@ fn window_commands_need_the_desktop_and_name_a_window() { run(&mut s, "view.syncScroll", json!({})); assert!(!s.windows.sync_scroll); assert_eq!(s.ui_requests.pop(), Some(json!({"windows": "syncScroll", "with": 7, "value": false}))); + // Asking again for the partner it already has keeps scrolling off and pairs nothing anew. + let r = run(&mut s, "view.sideBySide", json!({"window": 7, "value": true})); + assert_eq!((r["syncScroll"].as_bool(), s.windows.sync_scroll), (Some(false), false)); + assert!(s.ui_requests.is_empty()); // Another partner replaces the first; the bare button turns it off. run(&mut s, "view.sideBySide", json!({"window": 9})); assert_eq!(std::mem::take(&mut s.ui_requests), [json!({"windows": "sideBySide", "off": 7}), json!({"windows": "sideBySide", "with": 9})]); diff --git a/crates/ui-egui/src/windows/arrange.rs b/crates/ui-egui/src/windows/arrange.rs index 76f70a94d7..9ee478dd41 100644 --- a/crates/ui-egui/src/windows/arrange.rs +++ b/crates/ui-egui/src/windows/arrange.rs @@ -74,7 +74,10 @@ pub(crate) fn poll(app: &mut WordApp, _ctx: &egui::Context) { w.others = others; w.side_by_side = partner; w.sync_scroll = sync; - if sync && waiting.is_finite() { + if !sync { + // Scrolling still queued for a partner that is gone, replaced or no longer synced. + app.canvas.sync_in = 0.0; + } else if waiting.is_finite() { app.canvas.sync_in += waiting.clamp(-MAX_SCROLL, MAX_SCROLL); } } @@ -97,6 +100,7 @@ pub(crate) fn request(app: &mut WordApp, req: &Value) { let id = |k: &str| req.get(k).and_then(Value::as_u64); match what { "sideBySide" => { + app.canvas.sync_in = 0.0; if id("off").is_some() { app.windows.arranged.clear(); } @@ -113,6 +117,7 @@ pub(crate) fn request(app: &mut WordApp, req: &Value) { } } "syncScroll" => { + app.canvas.sync_in = 0.0; if let Some(on) = req.get("value").and_then(Value::as_bool) { app.windows.arranged.sync = on; } @@ -275,7 +280,15 @@ mod tests { assert_eq!(app.canvas.sync_out, 0.0); assert_eq!(app.windows.arranged.waiting.get(&other), Some(&40.0)); assert!(!app.windows.arranged.waiting.contains_key(&shown)); - // Nothing is passed on once Synchronous Scrolling is off. + // Nothing is passed on once Synchronous Scrolling is off, and scrolling still queued for + // this window is dropped. + app.canvas.sync_in = 9.0; + app.windows.arranged.sync = false; + poll(&mut app, &ctx); + assert_eq!(app.canvas.sync_in, 0.0); + app.canvas.sync_in = 9.0; + request(&mut app, &json!({"windows": "syncScroll", "value": true})); + assert_eq!(app.canvas.sync_in, 0.0); app.windows.arranged.sync = false; app.canvas.sync_out = 5.0; forward_scroll(&mut app, &ctx); @@ -287,7 +300,9 @@ mod tests { assert_eq!(app.windows.arranged.waiting.get(&other), Some(&MAX_SCROLL)); // The partner closing ends the pairing. app.windows.open.retain(|w| w.key == shown); + app.canvas.sync_in = 3.0; poll(&mut app, &ctx); + assert_eq!(app.canvas.sync_in, 0.0); assert_eq!((app.session.windows.side_by_side, app.session.windows.sync_scroll), (None, false)); } }