diff --git a/crates/engine/src/cmd/edit/props.rs b/crates/engine/src/cmd/edit/props.rs index fb8a1c17f..e7f04d7a1 100644 --- a/crates/engine/src/cmd/edit/props.rs +++ b/crates/engine/src/cmd/edit/props.rs @@ -245,7 +245,7 @@ pub(super) fn stroke_brush(s: &mut Session, p: &Value) -> Result { pub(super) fn reparent(s: &mut Session, p: &Value) -> Result { let ids = req_targets("node.reparent", s, p)?; let parent = req_id("node.reparent", p, "parent")?; - let index = num(p, "index").map(|i| i.max(0.0) as usize); + let index = opt_index("node.reparent", p, "index")?; let doc = s.doc_mut()?; let ids = z_sorted(doc, &roots(doc, &ids)); // `index` is where the first moved layer ends up among the parent's children (bottom first), diff --git a/crates/engine/src/panels_behaviour_tests.rs b/crates/engine/src/panels_behaviour_tests.rs index 585fc3e08..60450dcbe 100644 --- a/crates/engine/src/panels_behaviour_tests.rs +++ b/crates/engine/src/panels_behaviour_tests.rs @@ -200,6 +200,22 @@ fn reparent_index_is_the_final_position_in_auto_layout_and_across_parents() { assert!(kids(&s, row).is_empty()); } +#[test] +fn reparent_rejects_a_negative_or_fractional_index() { + let mut s = Session::new(); + let frame = id(&ex(&mut s, "node.create", json!({"type": "frame", "name": "F"}))); + let rect = |s: &mut Session, n: &str, parent: NodeId| id(&ex(s, "node.create", json!({"type": "rectangle", "name": n, "parent": parent}))); + let (a, b) = (rect(&mut s, "a", frame), rect(&mut s, "b", frame)); + let free = id(&ex(&mut s, "node.create", json!({"type": "rectangle", "name": "free"}))); + for bad in [json!(-1), json!(1.5), json!("x")] { + let r = s.execute("node.reparent", &json!({"ids": [free], "parent": frame, "index": bad})); + assert!(r.is_err(), "index {bad} accepted"); + assert_eq!(kids(&s, frame), [a, b], "the tree is unchanged for index {bad}"); + } + ex(&mut s, "node.reparent", json!({"ids": [free], "parent": frame, "index": 0})); + assert_eq!(kids(&s, frame), [free, a, b]); +} + #[test] fn renaming_several_layers_by_the_documented_codes() { // Three layers named `names`, bottom to top; (names, params, new names, derivation).