Merge pull request #5634 from dotCooCoo/fix/same-layers-skips-unreachable

Select: All on Same Layers takes the layers of objects that can be selected
This commit is contained in:
BFlat
2026-11-04 22:54:47 +07:00
committed by GitHub
4 changed files with 60 additions and 2 deletions
+10 -2
View File
@@ -819,8 +819,16 @@ fn same_attribute(s: &mut Session, p: &Value) -> Result<Value> {
fn same_layers(s: &mut Session, _: &Value) -> Result<Value> {
let st = s.doc()?;
// The layers or sublayers the selected objects are on (art in their sublayers too).
let layers: BTreeSet<NodeId> = st.selection.objects.iter().filter_map(|id| st.doc.layer_containing(*id)).collect();
// The layers or sublayers the selected objects are on (art in their sublayers too). A selected
// object that `is_selectable` does not accept adds no layer.
let locks = st.doc.locked_artboards();
let layers: BTreeSet<NodeId> = st
.selection
.objects
.iter()
.filter(|id| is_selectable(&st.doc, st.isolation, &locks, **id))
.filter_map(|id| st.doc.layer_containing(*id))
.collect();
let mut on = BTreeSet::new();
for l in layers.iter().filter_map(|l| st.doc.node(*l)) {
l.walk(&mut |n| {
+44
View File
@@ -2801,6 +2801,50 @@ fn select_menu_commands_leave_out_an_isolated_group_hidden_or_locked_since() {
assert_eq!(selected(&s), vec![]);
}
/// All on Same Layers takes the layers only of the selected objects that can be selected: an
/// object that is hidden or locked, on a template layer, or outside the isolated group adds no
/// layer. When the selection holds no such object, the command selects nothing.
#[test]
fn select_all_on_same_layers_skips_objects_that_cannot_be_selected() {
let mut s = session();
let [hidden, a] = [0.0, 20.0].map(|x| rect(&mut s, x, 0.0, 10.0, 10.0));
s.execute("layer.new", &json!({"top": true})).unwrap();
let [b, c, d] = [0.0, 20.0, 40.0].map(|x| rect(&mut s, x, 40.0, 10.0, 10.0));
let same = |s: &mut Session, from: &[NodeId]| {
sel(s, from);
assert_eq!(selected(s), from, "select.set keeps {from:?}");
s.execute("select.object.allOnSameLayers", &json!({})).unwrap();
let mut v = selected(s);
v.sort();
v
};
assert_eq!(same(&mut s, &[a, b]), vec![hidden, a, b, c, d]);
// A hidden or locked object on the first layer adds no layer.
for (on, off) in [
(json!({"id": hidden.0, "visible": false}), json!({"id": hidden.0, "visible": true})),
(json!({"id": hidden.0, "locked": true}), json!({"id": hidden.0, "locked": false})),
] {
s.execute("layer.setProps", &on).unwrap();
assert_eq!(same(&mut s, &[hidden, b]), vec![b, c, d], "{on}");
s.execute("layer.setProps", &off).unwrap();
}
// An object on a template layer adds no layer.
let l3 = id_of(&s.execute("layer.new", &json!({"top": true})).unwrap());
let on_template = rect(&mut s, 0.0, 80.0, 10.0, 10.0);
s.execute("layer.setProps", &json!({"id": l3.0, "template": true})).unwrap();
assert_eq!(same(&mut s, &[on_template, b]), vec![b, c, d]);
// An object outside the isolated group adds no layer, even though its layer holds the group.
sel(&mut s, &[c, d]);
let g = id_of(&s.execute("object.group", &json!({})).unwrap());
s.execute("object.isolate", &json!({"id": g.0})).unwrap();
assert_eq!(same(&mut s, &[b]), vec![]);
assert_eq!(same(&mut s, &[c]), vec![c, d]);
s.execute("object.exitIsolation", &json!({})).unwrap();
// With no object that can be selected, nothing is selected.
s.execute("layer.setProps", &json!({"id": hidden.0, "visible": false})).unwrap();
assert_eq!(same(&mut s, &[hidden, on_template]), vec![]);
}
/// The selection after command `c` runs with nothing selected.
fn select_from_none(s: &mut Session, c: &str) -> Vec<NodeId> {
s.execute("select.none", &json!({})).unwrap();
+5
View File
@@ -1490,6 +1490,11 @@ template layer, on a locked artboard while nothing is isolated, or outside the i
such as a symbol instance just placed, is not passed over for being on a locked artboard while it stays selected. When
the selection holds no other object, they return the error "nothing selected".
`select.object.allOnSameLayers` selects the objects on the layers and sublayers of the selected objects that can be
selected. A selected object that is hidden or locked, inside a hidden or locked group or layer, on a template layer, on
a locked artboard while nothing is isolated, or outside the isolated group adds no layer. When the selection holds no
other object, the command clears the selection.
## Shaper Groups
`shaper.releaseSource {source, group?}` takes the original shape `source` out of its Shaper Group: the shape becomes a
+1
View File
@@ -940,6 +940,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
Select › Same › Text Fill & Stroke Color selects the type whose characters match both the fill and the stroke of the selected type (`select.same.textFillAndStrokeColor`). The menu entry is listed in [Select objects by characteristics](https://helpx.adobe.com/illustrator/desktop/manage-objects/select-objects/select-objects-by-characteristics.html); requiring one run of characters to match both is VectorCraft's own behavior.
The Select › Same font and text color commands take the type to match from the selected type that can be selected: type that is hidden or locked, on a template layer, or outside the isolated group is passed over. This is VectorCraft's own behavior.
Select › Same › Fill Color, Stroke Color, Fill & Stroke, Stroke Weight, Opacity, Blending Mode, Appearance, Shape, Graphic Style, Appearance Attribute and Symbol Instance compare the other objects with the first selected object that can be selected, of any kind: an object that is hidden or locked, on a template layer, on a locked artboard while nothing is isolated (except new art while it stays selected), or outside the isolated group is passed over. This is VectorCraft's own behavior.
Select › Object › All on Same Layers takes the layers only of the selected objects that can be selected: an object that is hidden or locked, on a template layer, on a locked artboard while nothing is isolated, or outside the isolated group adds no layer. This is VectorCraft's own behavior.
Object › Flatten Transparency takes a frame of threaded text out of the thread in the same way when Convert All Text to Outlines outlines it or when the frame becomes flat regions or an image, and the other frames keep the text they hold.
New Symbol and Redefine Symbol on a frame of threaded text take it out of the thread with the text it shows, which the symbol's art holds, and the other frames keep the text they hold. When the last frame goes into the symbol, the text it has no room for goes after the text of the last frame that stays. When every frame of a thread goes into the symbol, the copy of the last frame keeps all its text.
With Optical Margin Alignment on, or Roman Hanging Punctuation on in the first paragraph, the glyphs at the ends of left-to-right type on a path hang outside its brackets, measured along the path. The type hangs at the start bracket when its line is placed there, hangs at the end bracket when the line is placed there, and hangs at both when it is centered. Glyphs that hang past the end bracket do not make the type overflow, and a hang stops at an open path's end. SVG export writes such type as outlines.