diff --git a/crates/engine/src/tests/prefs.rs b/crates/engine/src/tests/prefs.rs index 5727e9db1..b590e3c28 100644 --- a/crates/engine/src/tests/prefs.rs +++ b/crates/engine/src/tests/prefs.rs @@ -1658,6 +1658,30 @@ fn real_time_drawing_off_still_shows_a_dragged_guide() { assert_eq!((s.doc().unwrap().doc.guides[0].pos, s.doc().unwrap().history.undo.last().map(|e| e.label.as_str())), (150.0, Some("Move Guide"))); } +/// With Real-time Drawing off, a 9-slice guide of the edited symbol moved with the Selection tool +/// still moves while it is dragged, and the release leaves one Symbol Options undo step. +#[test] +fn real_time_drawing_off_still_shows_a_dragged_nine_slice_guide() { + let mut s = Session::new(); + s.execute("file.new", &json!({"width": 400, "height": 400})).unwrap(); + s.execute("shape.rectangle", &json!({"x": 100, "y": 100, "width": 100, "height": 100})).unwrap(); + s.execute("symbol.new", &json!({"name": "Box", "nineSlice": true})).unwrap(); + s.execute("symbol.edit", &json!({})).unwrap(); + let v = ViewInfo::default(); + s.select_tool("selection", v).unwrap(); + set_pref(&mut s, "realTimeDrawing", json!(false)); + let left = |s: &Session| s.doc().unwrap().doc.symbol_nine_slice("Box").unwrap().left; + assert_eq!(left(&s), 0.25); + let steps = s.doc().unwrap().history.undo.len(); + s.pointer(&PointerEvent::new(PointerKind::Down, 125.0, 150.0), v).unwrap(); + s.pointer(&PointerEvent::new(PointerKind::Drag, 110.0, 150.0), v).unwrap(); + assert!((left(&s) - 0.1).abs() < 1e-9, "the guide moves while dragged: {}", left(&s)); + s.pointer(&PointerEvent::new(PointerKind::Up, 110.0, 150.0), v).unwrap(); + let d = s.doc().unwrap(); + assert!((left(&s) - 0.1).abs() < 1e-9); + assert_eq!((d.history.undo.len(), d.history.undo.last().map(|e| e.label.as_str())), (steps + 1, Some("Symbol Options"))); +} + /// Real-time Drawing turned on mid-drag still applies the deferred preview on commit. #[test] fn real_time_drawing_turned_on_mid_drag_still_commits_deferred_preview() { diff --git a/crates/engine/src/tooling.rs b/crates/engine/src/tooling.rs index efaef7d64..c53383ddd 100644 --- a/crates/engine/src/tooling.rs +++ b/crates/engine/src/tooling.rs @@ -65,6 +65,13 @@ fn rgb(hex: &str, fallback: [u8; 3]) -> [u8; 3] { }) } +/// Whether a tool preview of `cmd` with params `p` sets only a symbol's 9-slice guides: a +/// `symbol.options` whose params hold `nineSliceGuides` and nothing besides `name`. +fn nine_slice_guide_preview(cmd: &str, p: &Value) -> bool { + cmd == "symbol.options" + && p.as_object().is_some_and(|m| m.contains_key("nineSliceGuides") && m.keys().all(|k| k == "name" || k == "nineSliceGuides")) +} + /// Requests from tools that only the frontend can fulfil. #[derive(Clone, Debug, PartialEq)] pub enum UiRequest { @@ -784,8 +791,9 @@ impl Session { Action::Preview(cmd, p) => { // Performance › Real-time Drawing and Editing (#394): off, remember the latest // params and apply them once on Commit instead of every move. A ruler guide - // being dragged still shows: the guide is all there is to see (#4646). - if !self.prefs.real_time_drawing && !matches!(cmd.as_str(), "guide.add" | "guide.move") { + // being dragged still shows: the guide is all there is to see (#4646). So does a + // symbol's 9-slice guide being dragged. + if !self.prefs.real_time_drawing && !matches!(cmd.as_str(), "guide.add" | "guide.move") && !nine_slice_guide_preview(&cmd, &p) { self.clear_deferred_overlay_cache(); let st = self.doc_mut()?; if let Some(it) = &mut st.interaction {