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]
+2 -2
View File
@@ -1372,8 +1372,8 @@ is one of them, and `edit.undo` selects them again with the key object.
`object.hide.unselected` (Object › Hide › Unselected Artwork, Cmd+Alt+Shift+3) hides, in one undo step, every
object that `select.all` takes and that is neither selected nor holds a selected object, and returns `{count}`. In a
group that holds a selected object it hides the group's other members one by one; a clip group, a compound path or
another object that holds a selected object stays as it is with its members. While Lock Guides is on it leaves the
guide paths as they are. The selection stays as it is, and `object.showAll` shows the objects again. With nothing to
another object that holds a selected object stays as it is with its members. The objects of a selected layer count as
selected. While Lock Guides is on, the guide paths count as selected and stay as they are. The selection stays as it is, and `object.showAll` shows the objects again. With nothing to
hide it records no undo step. In isolation mode the menu item is disabled, and the command returns the error "exit
isolation mode first" and changes nothing.