Merge pull request #5570 from dotCooCoo/fix/hide-others-locked-layer-and-guides

Object: Unselected Artwork keeps groups that hold a selected layer or a locked guide
This commit is contained in:
BFlat
2026-11-04 18:51:19 +07:00
committed by GitHub
2 changed files with 65 additions and 2 deletions
+20 -2
View File
@@ -347,7 +347,7 @@ fn other_layers(s: &mut Session, lock: bool) -> Result<Value> {
}
/// Returns the objects [`super::select::walk_selectable`] visits that are not among the selected objects `roots` and
/// hold none of them. A selected layer's objects count as selected. The walk goes into a plain group, but not a Shaper
/// hold none of them. A selected layer and its objects count as selected. The walk goes into a plain group, but not a Shaper
/// group, that holds a selected object. With `lock`, it also goes into a clip group or compound path that holds one,
/// and includes guide paths while `guides_locked`. Without `lock`, guide paths and the objects that hold one are left
/// out while `guides_locked`; a clip group, Shaper group, compound path and every other object that holds a selected
@@ -360,14 +360,32 @@ fn unselected(
guides_locked: bool,
lock: bool,
) -> Vec<NodeId> {
/// Pushes the visible guide paths in `n`, `n` included, locked ones too.
fn shown_guides(n: &vectorcraft_doc::Node, out: &mut Vec<NodeId>) {
if !n.visible {
return;
}
if matches!(n.kind, NodeKind::Path { guide: true, .. }) {
out.push(n.id);
}
for c in n.children().into_iter().flatten() {
shown_guides(c, out);
}
}
let mut roots: HashSet<NodeId> = roots.iter().copied().collect();
roots.extend(selection.iter().filter_map(|id| d.node(*id)).filter(|n| n.is_layer()).flat_map(|l| l.layer_art(true)));
let layers = || selection.iter().filter_map(|id| d.node(*id)).filter(|n| n.is_layer());
roots.extend(layers().map(|l| l.id));
roots.extend(layers().flat_map(|l| l.layer_art(true)));
let mut kept: Vec<NodeId> = roots.iter().copied().collect();
if guides_locked && !lock {
super::select::walk_selectable(d, iso, |n| {
if matches!(n.kind, NodeKind::Path { guide: true, .. }) {
kept.push(n.id);
}
// The walk skips locked objects, so the guide paths in them are looked for here.
for c in n.children().into_iter().flatten().filter(|c| c.locked) {
shown_guides(c, &mut kept);
}
true
});
}
+45
View File
@@ -865,6 +865,51 @@ fn hide_unselected_artwork_keeps_groups_with_locked_guides_and_the_selected_laye
assert_eq!(selected(&s), [other]);
}
/// While Lock Guides is on, Hide › Unselected Artwork keeps a group visible when it holds a guide path that is locked
/// itself, and hides the group's other members one by one.
#[test]
fn hide_unselected_artwork_keeps_groups_with_a_locked_guide_path() {
let mut s = session();
let a = rect(&mut s, 0.0, 0.0, 10.0, 10.0);
let guide = rect(&mut s, 40.0, 0.0, 10.0, 10.0);
let x = rect(&mut s, 60.0, 0.0, 10.0, 10.0);
sel(&mut s, &[guide, x]);
let g = id_of(&s.execute("object.group", &json!({})).unwrap());
sel(&mut s, &[guide]);
s.execute("view.guides.make", &json!({})).unwrap();
s.execute("layer.setProps", &json!({"id": guide.0, "locked": true})).unwrap();
s.execute("view.guides.lock", &json!({"locked": true})).unwrap();
sel(&mut s, &[a]);
assert_eq!(s.execute("object.hide.unselected", &json!({})).unwrap(), json!({"count": 1}));
assert_eq!([a, g, guide, x].map(|id| node(&s, id).visible), [true, true, true, false]);
}
/// Hide › Unselected Artwork keeps a group visible when it holds a selected layer whose art is all locked, and hides the
/// group's other members one by one.
#[test]
fn hide_unselected_artwork_keeps_a_group_that_holds_a_selected_layer() {
let mut s = session();
let b = rect(&mut s, 0.0, 0.0, 10.0, 10.0);
let y = rect(&mut s, 20.0, 0.0, 10.0, 10.0);
let z = rect(&mut s, 40.0, 0.0, 10.0, 10.0);
sel(&mut s, &[y]);
let g = id_of(&s.execute("object.group", &json!({})).unwrap());
let sub = s
.edit("nest", |d, _| {
let id = d.alloc_id();
d.insert(Some(g), usize::MAX, Node::layer(id, "Sub", vectorcraft_doc::LayerColor::Preset(1)))
.map_err(|e| EngineError::Other(e.to_string()))?;
let art = d.remove(z).map_err(|e| EngineError::Other(e.to_string()))?;
d.insert(Some(id), 0, (*art).clone()).map_err(|e| EngineError::Other(e.to_string()))?;
Ok(id)
})
.unwrap();
s.execute("layer.setProps", &json!({"id": z.0, "locked": true})).unwrap();
sel(&mut s, &[sub]);
assert_eq!(s.execute("object.hide.unselected", &json!({})).unwrap(), json!({"count": 2}));
assert_eq!([b, g, y, sub, z].map(|id| node(&s, id).visible), [false, true, false, true, true]);
}
/// Lock Unselected Artwork locks `b`, `n` in the group that holds selected `m`, `p` on another layer, and the clip
/// group's unselected members `c1` and clipping path `c3` in one undo step labeled "Lock". The clip group container,
/// `c2`, the selected objects, group `g`, hidden object `h` and art `q` on a locked layer stay unlocked. `k` was locked