diff --git a/crates/ui-egui/src/panels/layers.rs b/crates/ui-egui/src/panels/layers.rs index 9c118a329..955eb04b7 100644 --- a/crates/ui-egui/src/panels/layers.rs +++ b/crates/ui-egui/src/panels/layers.rs @@ -5,7 +5,9 @@ //! //! - Clicking a row highlights it and makes its layer current (`layer.setCurrent`); Shift-click //! highlights the range of rows down to it, Ctrl/Cmd-click toggles one (`layer.highlight`). Panel -//! commands (Duplicate, Delete, Merge, Options…) act on the highlighted rows. +//! commands (Duplicate, Delete, Merge, Options…) act on the highlighted rows. Alt-click on a +//! layer's or sublayer's name also selects its art (`layer.selectAll`), as its selection square +//! does. //! - The selection column selects a row's art (`layer.selectAll`, Shift adds or removes); a row //! whose art is selected shows a square in its layer's colour, and dragging that square onto //! another row moves the selected art there (Alt copies it). diff --git a/crates/ui-egui/src/panels/layers/rows.rs b/crates/ui-egui/src/panels/layers/rows.rs index 5ed4c44ba..76f780dcb 100644 --- a/crates/ui-egui/src/panels/layers/rows.rs +++ b/crates/ui-egui/src/panels/layers/rows.rs @@ -310,15 +310,19 @@ pub(super) fn row(ui: &mut Ui, view: &View, item: &Row, out: &mut Out) { egui::DragAndDrop::set_payload(ui.ctx(), LayersDrag::Art); } let m = ui.input(|i| i.modifiers); + let on_name = ui.input(|i| i.pointer.interact_pos()).is_some_and(|p| p.x <= name_end.max(name_rect.left() + 20.0) && p.x >= name_rect.left()); if tresp.clicked() { out.actions.push(("layer.target".into(), json!({"id": n.id.0, "add": m.shift}))); } else if sq_resp.clicked() { out.actions.push(("layer.selectAll".into(), json!({"id": n.id.0, "add": m.shift}))); } else if resp.clicked() { out.click = Some((n.id, m)); + // Alt-click on a layer's or sublayer's name also selects its art, as its selection square does. + if m.alt && !m.shift && !m.command && n.is_layer() && on_name { + out.actions.push(("layer.selectAll".into(), json!({"id": n.id.0}))); + } } if resp.double_clicked() { - let on_name = ui.input(|i| i.pointer.interact_pos()).is_some_and(|p| p.x <= name_end.max(name_rect.left() + 20.0) && p.x >= name_rect.left()); let on_thumb = thumb_rect.is_some_and(|th| ui.input(|i| i.pointer.interact_pos()).is_some_and(|p| th.contains(p))); if on_name && !on_thumb { ui.data_mut(|d| d.insert_temp(rename_id(), (n.id.0, n.identify_label(view.identify_xml_id)))); diff --git a/crates/ui-egui/src/panels/layers/tests.rs b/crates/ui-egui/src/panels/layers/tests.rs index e779e328c..ec94adc44 100644 --- a/crates/ui-egui/src/panels/layers/tests.rs +++ b/crates/ui-egui/src/panels/layers/tests.rs @@ -544,6 +544,36 @@ fn selection_squares_show_and_select_a_rows_art() { let _ = (layer, sub, b); } +/// Alt-click on a layer's or sublayer's name makes it current, as a plain click does, and selects +/// its art, as its selection square does. On another row, beside the name or with Shift or Ctrl/Cmd +/// held too, it is the click without Alt. +#[test] +fn alt_clicking_a_layer_name_selects_its_art() { + let (mut app, [layer, sub, g, _, a, c]) = nested(); + let ctx = egui::Context::default(); + let (circles, _) = frame(&mut app, &ctx, vec![], false); + // The rows' names start right of the thumbnail; the sublayer's row is one level deeper. + let name = |i: usize, depth: f32| egui::pos2(2.0 * COLUMN + 2.0 + 6.0 + depth * INDENT + 16.0 + 30.0, circles[i].y); + let state = |app: &VectorcraftApp| { + let st = app.session.active().unwrap(); + (st.selection.objects.clone(), st.highlighted_rows(), st.current_layer()) + }; + click_with(&mut app, &ctx, name(1, 1.0), egui::Modifiers::ALT); + assert_eq!(state(&app), (vec![a, g], vec![sub], Some(sub)), "the sublayer's art"); + click_with(&mut app, &ctx, name(0, 0.0), egui::Modifiers::ALT); + assert_eq!(state(&app), (vec![c, a, g], vec![layer], Some(layer)), "the layer's, its sublayer's too"); + app.session.execute("select.none", &json!({})).unwrap(); + // A group's row, the space right of a layer's name, Alt+Shift and Ctrl/Cmd+Alt select nothing. + click_with(&mut app, &ctx, name(2, 2.0), egui::Modifiers::ALT); + assert_eq!(state(&app), (vec![], vec![g], Some(sub)), "a group's row"); + click_with(&mut app, &ctx, circles[0] - vec2(40.0, 0.0), egui::Modifiers::ALT); + assert_eq!(state(&app), (vec![], vec![layer], Some(layer)), "beside the name"); + click_with(&mut app, &ctx, name(1, 1.0), egui::Modifiers::ALT | egui::Modifiers::SHIFT); + assert_eq!(state(&app).0, vec![], "Alt+Shift"); + click_with(&mut app, &ctx, name(0, 0.0), egui::Modifiers::ALT | egui::Modifiers::COMMAND); + assert_eq!(state(&app).0, vec![], "Ctrl/Cmd+Alt"); +} + #[test] fn eye_and_lock_toggle_nested_rows_and_drag_down_the_column() { let (mut app, [_, sub, g, b, a, _]) = nested(); diff --git a/docs/mcp.md b/docs/mcp.md index 89efe1917..67b08c895 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -2117,7 +2117,8 @@ and `layerRows`, the rows highlighted in the panel. set|add|toggle}` is Shift-click (a range) and Ctrl/Cmd-click (toggle). Selecting art makes its layer current. Without `ids`, the panel commands below act on the highlighted rows, else on the current layer. - **Selection column**: `layer.selectAll {id, add?}` selects a row's visible, unlocked art (a layer's sublayers' - too); `add` (Shift) adds it, or removes it when it's all selected. + too); `add` (Shift) adds it, or removes it when it's all selected. Alt-click on a layer's or sublayer's name runs + `layer.selectAll {id}` and then `layer.setCurrent {id}`. - **Eye and lock**: `layer.setProps {ids?, visible?, locked?, name?}` for any rows, plus Layer Options for layers: `template` (also locks the layer and dims its images to 50% unless given), `printable`, `preview` (off: the layer draws and is clicked in outline, Ctrl/Cmd-click its eye), `dimImages` (0–100, or false) and `color` (an index 0–26, diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 884f49b01..9b53e3f84 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -709,6 +709,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur The panel menu's Save Selection submenu has Save Selection…, Update Selection and Edit Selection…, enabled and run as in the Select menu, and lists the document's saved selections: choosing one selects its objects again (`select.recall`). `object.setProps` with `visible: false` or `locked: true` deselects every selected object that is then hidden or locked, the key object included, as the eye and lock columns do. Ctrl/Cmd+Alt-clicking an eye switches the other layers to Outline and the row's layer to Preview, or switches all layers back to Preview when the others are in Outline (`layer.outlineOthers`, `layer.previewAll`). + Alt-clicking a layer's or sublayer's name makes it the current layer, as a click does, and selects its art, as its selection square does (`layer.selectAll`, then `layer.setCurrent`). The reference app's [Default keyboard shortcuts](https://web.archive.org/web/20260726060015/https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html) page lists Alt-click on the layer name for selecting all objects on the layer. Object › Arrange › Send to Current Layer returns the error "the target layer is locked or hidden" and leaves the selected objects where they are when the current layer is hidden or locked, or is inside a hidden or locked layer. Selected objects are deselected, the key object included, when a row drag moves them into a hidden layer or group, when `node.move` moves them into a hidden or locked layer or group, when Merge Selected moves them into a hidden or locked layer, when Collect in New Layer moves them into a new layer inside a hidden or locked layer, when Flatten Artwork moves them into a locked layer, and when applying a data set hides them. Panel Options › Locate Selected Objects (beyond Illustrator, off by default): when the last selected object changes on the canvas or by a command, the panel opens the layers and groups around it and scrolls to its row, as Locate Object does, without highlighting the row; when Show Layers Only, a search or the filter leaves the object without a row, the panel opens no rows for it and scrolls as with the option off.