Object: Hide Unselected Artwork keeps a selected layer's art and groups that hold locked guides

With a layer selected, Object > Hide > Unselected Artwork hid every object
on that layer, since the selected layer was dropped from the objects to
keep. The objects of a selected layer now count as selected.

While Lock Guides is on, a group that held a guide path was hidden whole,
which hid the guide with it. Guide paths now count as selected, so the
command keeps such a group visible and hides its other members one by one.
This commit is contained in:
dotCooCoo
2026-10-29 18:46:47 +07:00
committed by bflatastic
parent 9c01829cbb
commit d93657c631
3 changed files with 49 additions and 12 deletions
+19 -10
View File
@@ -79,7 +79,7 @@ pub fn specs() -> Vec<CommandSpec> {
"Unselected Artwork",
["Object", "Hide"],
Some("Cmd+Alt+Shift+3"),
"{} hide, in one undo step, every object select.all takes that is not selected and holds no selected object; in a group that holds one it hides the other members one by one, and a clip group, compound path or other object that holds one stays as it is; guide paths stay as they are while Lock Guides is on; object.showAll shows them again; disabled in isolation mode → {count}",
"{} hide, in one undo step, every object select.all takes that is not selected and holds no selected object; in a group that holds one it hides the other members one by one, and a clip group, compound path or other object that holds one stays as it is; the objects of a selected layer count as selected; while Lock Guides is on, guide paths count as selected and stay as they are; object.showAll shows them again; disabled in isolation mode → {count}",
has_selection_outside_isolation,
hide_unselected
),
@@ -322,17 +322,26 @@ fn other_layers(s: &mut Session, lock: bool) -> Result<Value> {
}
/// The objects [`super::select::walk_selectable`] visits that are not among the selected objects `roots` and hold none of
/// them. The walk goes into a plain group that holds one of `roots`; a clip group, a Shaper group, a compound path and
/// every other object that holds one is left out with its members. Guide paths are left out while `guides_locked`.
fn unselected(d: &vectorcraft_doc::Document, iso: Option<NodeId>, roots: &[NodeId], guides_locked: bool) -> Vec<NodeId> {
let holds: HashSet<NodeId> = d.index_paths(roots.iter().copied()).values().filter_map(|p| d.ancestry_at(p)).flatten().collect();
let roots: HashSet<NodeId> = roots.iter().copied().collect();
/// them. The objects of a selected layer count as selected. Guide paths are left out while `guides_locked`, and so are
/// the objects that hold one. The walk goes into a plain group that holds a selected object or such a guide path; a
/// clip group, a Shaper group, a compound path and every other object that holds one is left out with its members.
fn unselected(d: &vectorcraft_doc::Document, iso: Option<NodeId>, selection: &[NodeId], roots: &[NodeId], guides_locked: bool) -> Vec<NodeId> {
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 mut kept: Vec<NodeId> = roots.iter().copied().collect();
if guides_locked {
super::select::walk_selectable(d, iso, |n| {
if matches!(n.kind, NodeKind::Path { guide: true, .. }) {
kept.push(n.id);
}
true
});
}
let holds: HashSet<NodeId> = d.index_paths(kept).values().filter_map(|p| d.ancestry_at(p)).flatten().collect();
let mut out = vec![];
super::select::walk_selectable(d, iso, |n| {
if !holds.contains(&n.id) {
if !(guides_locked && matches!(n.kind, NodeKind::Path { guide: true, .. })) {
out.push(n.id);
}
out.push(n.id);
return false;
}
!roots.contains(&n.id) && matches!(n.kind, NodeKind::Group { clip: false, .. }) && n.extras().shaper.is_none()
@@ -344,7 +353,7 @@ fn hide_unselected(s: &mut Session, _: &Value) -> Result<Value> {
let roots = selected_roots_to_transform(s)?;
let guides_locked = s.menu.guides_locked;
let st = s.doc()?;
let ids = unselected(&st.doc, st.isolation, &roots, guides_locked);
let ids = unselected(&st.doc, st.isolation, &st.selection.objects, &roots, guides_locked);
// With nothing to hide, the document and its undo history stay as they are.
if !ids.is_empty() {
s.edit("Hide", |d, _| {
+28
View File
@@ -598,6 +598,34 @@ fn hide_unselected_artwork_keeps_locked_guides_and_is_disabled_in_isolation_mode
assert_eq!(shown(&s, &[g, c, outer, hole]), [false, true, true, true]);
}
/// While Lock Guides is on, Hide › Unselected Artwork keeps a group that holds a guide path visible and hides its other
/// members one by one. With a layer selected, the objects on that layer stay visible and the rest are hidden.
#[test]
fn hide_unselected_artwork_keeps_groups_with_locked_guides_and_the_selected_layers_art() {
let mut s = session();
let a = rect(&mut s, 0.0, 0.0, 10.0, 10.0);
let b = rect(&mut s, 20.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("view.guides.lock", &json!({"locked": true})).unwrap();
let other = id_of(&s.execute("layer.new", &json!({})).unwrap());
let p = rect(&mut s, 0.0, 100.0, 10.0, 10.0);
let hide = |s: &mut Session| s.execute("object.hide.unselected", &json!({})).unwrap();
let shown = |s: &Session, ids: &[NodeId]| ids.iter().map(|id| node(s, *id).visible).collect::<Vec<_>>();
sel(&mut s, &[a]);
assert_eq!(hide(&mut s), json!({"count": 3}));
assert_eq!(shown(&s, &[a, b, g, guide, x, p]), [true, false, true, true, false, false]);
s.execute("object.showAll", &json!({})).unwrap();
sel(&mut s, &[other]);
assert_eq!(hide(&mut s), json!({"count": 3}));
assert_eq!(shown(&s, &[a, b, g, guide, x, p]), [false, false, true, true, false, true]);
assert_eq!(selected(&s), [other]);
}
// ---------- Transform Each ----------
#[test]