Keep Layers panel selection inside the isolated group

Fixes #2286

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
NetterSilver
2026-10-16 14:57:21 +07:00
committed by bflatastic
co-authored by Claude Haiku 5.5
parent ee6cd6de55
commit e510190867
2 changed files with 38 additions and 1 deletions
+9 -1
View File
@@ -412,6 +412,9 @@ fn target(s: &mut Session, p: &Value) -> Result<Value> {
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<Value> {
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<NodeId>, 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<Value> {
const C: &str = "layer.selectAll";
@@ -652,7 +660,7 @@ fn select_art(s: &mut Session, p: &Value) -> Result<Value> {
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<NodeId> = if !st.doc.is_editable(id) {
let ids: Vec<NodeId> = if !st.doc.is_editable(id) || !in_isolation(&st.doc, st.isolation, id) {
vec![]
} else if n.is_layer() {
n.layer_art(true)
+29
View File
@@ -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]