From 484a78f812c37f1f226aaba312825d3a661f05ae Mon Sep 17 00:00:00 2001 From: Don Omondi <282319807+mockydevs@users.noreply.github.com> Date: Sun, 1 Nov 2026 10:37:35 +0300 Subject: [PATCH] Copy and paste guides: Cut takes the selected guides with the objects Cut with objects and ruler guides selected now removes both, in one undo step: edit.clear takes guides: true beside ids to delete the selected guides with the objects it names. --- crates/engine/src/cmd/edit.rs | 10 ++++++---- crates/engine/src/tests/guide_clipboard.rs | 10 ++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/crates/engine/src/cmd/edit.rs b/crates/engine/src/cmd/edit.rs index 6c99d3350..08f765c83 100644 --- a/crates/engine/src/cmd/edit.rs +++ b/crates/engine/src/cmd/edit.rs @@ -193,7 +193,7 @@ pub fn specs() -> Vec { "Clear", ["Edit"], Some("Delete"), - "{ids?} delete the objects `ids`, else the selection (fully selected objects and direct-selected anchors together, else ruler guides)", + "{ids?, guides?: bool (with ids: also delete the selected ruler guides)} delete the objects `ids`, else the selection (fully selected objects and direct-selected anchors together, else ruler guides)", has_selection_or_guides, clear ), @@ -712,7 +712,8 @@ fn cut(s: &mut Session, p: &Value) -> Result { } let ids = selected_roots(s)?; copy(s, p)?; - if s.doc()?.selection.anchors.is_empty() { clear(s, &json!({ "ids": ids })) } else { clear(s, &json!({})) } + // The selected ruler guides it copied go with the objects, in the same undo step (#3226). + if s.doc()?.selection.anchors.is_empty() { clear(s, &json!({ "ids": ids, "guides": true })) } else { clear(s, &json!({})) } } fn clear(s: &mut Session, p: &Value) -> Result { @@ -726,8 +727,9 @@ fn clear(s: &mut Session, p: &Value) -> Result { return super::docmenu::guide_remove(s, &json!({})); } let st = s.doc()?; - // Guides selected with the art go with it (not when ids are given). - let with_guides = explicit.is_none(); + // Guides selected with the art go with it (not when ids are given, unless `guides` says so: + // Cut, which names the objects it copied). + let with_guides = explicit.is_none() || bool_or(p, "guides", false); let targets = explicit.unwrap_or_else(|| st.selection.in_paint_order(&st.doc)); // Keep the selected ids themselves, without promoting compound members. Delete a selected // ancestor just once; its selected descendants leave with it. Layers themselves are kept. diff --git a/crates/engine/src/tests/guide_clipboard.rs b/crates/engine/src/tests/guide_clipboard.rs index 6c8eec3c2..ca1f9fd79 100644 --- a/crates/engine/src/tests/guide_clipboard.rs +++ b/crates/engine/src/tests/guide_clipboard.rs @@ -64,6 +64,16 @@ fn guides_paste_in_place_onto_another_artboard_and_cut_removes_them() { let mut at: Vec = guides(&s).iter().map(|g| g.1).collect(); at.sort_by(f64::total_cmp); assert_eq!(at, [a.x0 + 30.0, b.x0 + 30.0, b.x0 + 30.0]); + // Cut with an object and a guide selected takes both away, in one undo step. + let rect = run(&mut s, "shape.rectangle", json!({"x": 10, "y": 10, "width": 20, "height": 20}))["id"].as_u64().unwrap(); + let before = guides(&s).len(); + run(&mut s, "select.set", json!({"ids": [rect]})); + run(&mut s, "guide.select", json!({"indexes": [0], "toggle": true})); + let undo = s.doc().unwrap().history.undo.len(); + run(&mut s, "edit.cut", json!({})); + assert!(s.doc().unwrap().doc.node(NodeId(rect)).is_none()); + assert_eq!(guides(&s).len(), before - 1, "the selected guide goes too"); + assert_eq!(s.doc().unwrap().history.undo.len(), undo + 1); // Nothing selected: Copy is off. run(&mut s, "select.none", json!({})); assert!(s.execute("edit.copy", &json!({})).is_err());