From 1199f690adbc18aaafffa0e0af35141677022fe3 Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Thu, 15 Oct 2026 06:12:21 +0200 Subject: [PATCH] Fix Send to back leaving layers above siblings across frames Found while testing crates/engine/src/cmd/arrange.rs:327-330 Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Opus 5.5 --- crates/engine/src/cmd/arrange.rs | 8 ++++++-- crates/engine/src/tests.rs | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/crates/engine/src/cmd/arrange.rs b/crates/engine/src/cmd/arrange.rs index 2c088cfa..ca691bfa 100644 --- a/crates/engine/src/cmd/arrange.rs +++ b/crates/engine/src/cmd/arrange.rs @@ -325,8 +325,12 @@ fn zorder(s: &mut Session, p: &Value, z: Z) -> Result { } } if matches!(z, Z::Back) { - for (k, i) in ids.iter().enumerate() { - doc.reorder(*i, k)?; + let mut next: std::collections::HashMap = std::collections::HashMap::new(); + for i in &ids { + let Some(pp) = doc.get(*i)?.parent else { continue }; + let k = next.entry(pp).or_insert(0); + doc.reorder(*i, *k)?; + *k += 1; } } ok() diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index 40da2f1d..14ec4cfc 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -253,6 +253,28 @@ fn align_distribute_zorder() { assert_eq!(s.doc().unwrap().node(page).unwrap().children[0], c); } +#[test] +fn send_to_back_across_frames_counts_per_parent() { + let mut s = sess(); + let make = |s: &mut Session, p: Value| id(&s.execute("node.create", &p).unwrap()); + let fa = make(&mut s, json!({"type": "frame", "x": 0, "y": 0, "width": 200, "height": 200})); + let fb = make(&mut s, json!({"type": "frame", "x": 300, "y": 0, "width": 200, "height": 200})); + let rect = |parent: NodeId| json!({"type": "rectangle", "parent": parent, "x": 10, "y": 10, "width": 20, "height": 20}); + let a: Vec = (0..2).map(|_| make(&mut s, rect(fa))).collect(); + let b: Vec = (0..3).map(|_| make(&mut s, rect(fb))).collect(); + let idx = |s: &Session, n: NodeId| s.doc().unwrap().index_in_parent(n); + assert_eq!(idx(&s, a[1]), Some(1)); + assert_eq!(idx(&s, b[2]), Some(2)); + s.execute("arrange.back", &json!({"ids": [a[1], b[2]]})).unwrap(); + assert_eq!(idx(&s, a[1]), Some(0), "top of frame A at the back"); + assert_eq!(idx(&s, b[2]), Some(0), "top of frame B at the back"); + // Two from B plus one from A: each parent's selection ends at 0..n in its original order. + s.execute("arrange.back", &json!({"ids": [b[1], a[0], b[0]]})).unwrap(); + let kids = |s: &Session, f: NodeId| s.doc().unwrap().node(f).unwrap().children.clone(); + assert_eq!(kids(&s, fa), vec![a[0], a[1]]); + assert_eq!(kids(&s, fb), vec![b[0], b[1], b[2]]); +} + #[test] fn copy_paste_and_duplicate() { let mut s = sess();