From e51019086795570ce0923b1dbd4157c83767fbc4 Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Fri, 16 Oct 2026 06:32:18 +0200 Subject: [PATCH] Keep Layers panel selection inside the isolated group Fixes #2286 Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Opus 5.5 --- crates/engine/src/cmd/layer.rs | 10 +++++++++- crates/engine/src/tests_menucmds.rs | 29 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/cmd/layer.rs b/crates/engine/src/cmd/layer.rs index 116fd9e0a..7da86b806 100644 --- a/crates/engine/src/cmd/layer.rs +++ b/crates/engine/src/cmd/layer.rs @@ -412,6 +412,9 @@ fn target(s: &mut Session, p: &Value) -> Result { if !st.doc.is_editable(id) { return Err(bad(C, "the object is hidden or locked")); } + if !in_isolation(&st.doc, st.isolation, id) { + return Err(bad(C, "the object is outside the isolated group")); + } let layer = n.is_layer(); let rows = st.layer_rows.clone(); if p.get("add").and_then(Value::as_bool).unwrap_or(false) { @@ -645,6 +648,11 @@ fn collect(s: &mut Session, p: &Value) -> Result { Ok(json!({ "id": lid.0 })) } +/// Whether `id` lies inside the isolated group or layer (always, outside isolation mode). +fn in_isolation(doc: &Document, isolation: Option, id: NodeId) -> bool { + isolation.is_none_or(|iso| iso != id && doc.ancestry(id).is_some_and(|a| a.contains(&iso))) +} + /// The selection column: select a row's art. fn select_art(s: &mut Session, p: &Value) -> Result { const C: &str = "layer.selectAll"; @@ -652,7 +660,7 @@ fn select_art(s: &mut Session, p: &Value) -> Result { let add = bool_or(p, "add", false); let st = s.doc()?; let n = st.doc.node(id).ok_or(EngineError::NoNode(id))?; - let ids: Vec = if !st.doc.is_editable(id) { + let ids: Vec = if !st.doc.is_editable(id) || !in_isolation(&st.doc, st.isolation, id) { vec![] } else if n.is_layer() { n.layer_art(true) diff --git a/crates/engine/src/tests_menucmds.rs b/crates/engine/src/tests_menucmds.rs index 7855d884f..cbf09ead1 100644 --- a/crates/engine/src/tests_menucmds.rs +++ b/crates/engine/src/tests_menucmds.rs @@ -1014,6 +1014,35 @@ fn select_menu_commands_stay_inside_the_isolated_group() { assert_eq!(selected(&s), vec![a, b, text_inside]); } +/// In isolation mode the Layers panel's target circle and selection column can't select an object +/// outside the isolated group, so it can't be moved either (#2286). +#[test] +fn layers_panel_selects_nothing_outside_the_isolated_group() { + let mut s = session(); + let a = rect(&mut s, 0.0, 0.0, 50.0, 50.0); + let b = rect(&mut s, 100.0, 0.0, 50.0, 50.0); + let outside = rect(&mut s, 300.0, 0.0, 50.0, 50.0); + sel(&mut s, &[a, b]); + let g = id_of(&s.execute("object.group", &json!({})).unwrap()); + s.execute("object.isolate", &json!({"id": g.0})).unwrap(); + let before = bounds(&s, outside); + let _ = s.execute("layer.target", &json!({"id": outside.0})); + assert!(!selected(&s).contains(&outside)); + let _ = s.execute("layer.target", &json!({"id": outside.0, "add": true})); + assert!(!selected(&s).contains(&outside)); + let _ = s.execute("layer.selectAll", &json!({"id": outside.0})); + assert!(!selected(&s).contains(&outside)); + let _ = s.execute("layer.selectAll", &json!({"id": outside.0, "add": true})); + assert!(!selected(&s).contains(&outside)); + let _ = s.execute("object.move", &json!({"dx": 10, "dy": 0})); + assert_eq!(bounds(&s, outside), before); + // Objects inside the group can still be targeted and selected. + s.execute("layer.target", &json!({"id": a.0})).unwrap(); + assert_eq!(selected(&s), vec![a]); + s.execute("layer.selectAll", &json!({"id": b.0})).unwrap(); + assert_eq!(selected(&s), vec![b]); +} + /// In isolation mode a recalled selection leaves out the objects outside the isolated group, and /// Next Object Above selects nothing outside it. #[test]