diff --git a/crates/engine/src/cmd/object.rs b/crates/engine/src/cmd/object.rs index 22ef28d13..c4cf27198 100644 --- a/crates/engine/src/cmd/object.rs +++ b/crates/engine/src/cmd/object.rs @@ -1804,7 +1804,7 @@ fn distribute_spacing(s: &mut Session, p: &Value) -> Result { } fn set_bounds(s: &mut Session, p: &Value) -> Result { - match bounds_transform(s, p, selected_roots(s)?, 0.0)? { + match bounds_transform(s, p, selected_roots_to_transform(s)?, 0.0)? { Bounds::Anchors(ids, b) => set_anchor_bounds(s, p, ids, b), Bounds::Objects(ids, xf) => apply_transform(s, "Transform", ids, xf, p, false), } diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index a96733cb4..a203ed6d0 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -2451,6 +2451,34 @@ fn an_isolated_compound_paths_member_moves_alone() { assert_eq!((x0(&s, outer), x0(&s, hole)), (5.0, 55.0), "the compound path selected: the whole ring"); } +/// The X, Y, W and H of the Properties and Transform panels run `object.setBounds`, which takes the +/// same objects as the other transforms: a path of a compound path selected alone sets its own +/// box and leaves the ring around it (#4046). The hole is off-centre, and a plain rectangle +/// outside the ring keeps "everything moved" from passing. +#[test] +fn set_bounds_of_a_compound_paths_member_sets_only_that_path() { + let x = |s: &Session, id: NodeId| { + let b = s.doc().unwrap().doc.node(id).unwrap().geometric_bounds().unwrap(); + (b.x0, b.x1) + }; + let mut s = session(); + let (c, outer, hole) = isolated_ring(&mut s); + let other = rect(&mut s, 100.0, 0.0, 10.0, 10.0); + s.execute("select.set", &json!({"ids": [hole.0]})).unwrap(); + s.execute("object.setBounds", &json!({"width": 20, "reference": 0})).unwrap(); + assert_eq!((x(&s, outer), x(&s, hole)), ((0.0, 40.0), (10.0, 30.0)), "isolated: only the hole is wider"); + s.execute("object.setBounds", &json!({"x": 25, "reference": 0})).unwrap(); + assert_eq!((x(&s, outer), x(&s, hole)), ((0.0, 40.0), (25.0, 45.0)), "isolated: only the hole moved"); + s.execute("object.exitIsolation", &json!({})).unwrap(); + s.execute("select.set", &json!({"ids": [hole.0]})).unwrap(); + s.execute("object.setBounds", &json!({"x": 12, "reference": 0})).unwrap(); + assert_eq!((x(&s, outer), x(&s, hole)), ((0.0, 40.0), (12.0, 32.0)), "not isolated: still only the hole"); + s.execute("select.set", &json!({"ids": [c.0]})).unwrap(); + s.execute("object.setBounds", &json!({"x": 5, "reference": 0})).unwrap(); + assert_eq!((x(&s, outer), x(&s, hole)), ((5.0, 45.0), (17.0, 37.0)), "the compound path selected: the whole ring"); + assert_eq!(x(&s, other), (100.0, 110.0), "the rectangle outside never moves"); +} + /// `depth` and `childLimit` slice a summary read; a level that shows fewer children /// than it has reports `childCount`, and invalid values are rejected, not reinterpreted. #[test]