diff --git a/crates/ui-egui/src/menus.rs b/crates/ui-egui/src/menus.rs index aaf3cd171..4ae42442b 100644 --- a/crates/ui-egui/src/menus.rs +++ b/crates/ui-egui/src/menus.rs @@ -553,7 +553,7 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[ ( "ui.newLayer", "New Layer Options…", - "", + "Cmd+Alt+L", "{sublayer?: bool} open Layer Options for a new layer (or a sublayer of the current layer), named and coloured as it would be: dialog `layerOptions` with mode new|newSublayer; OK runs layer.new or layer.newSublayer with the fields", ), ( diff --git a/crates/ui-egui/src/panels/layers.rs b/crates/ui-egui/src/panels/layers.rs index 3871b7df7..c670d110c 100644 --- a/crates/ui-egui/src/panels/layers.rs +++ b/crates/ui-egui/src/panels/layers.rs @@ -2685,6 +2685,73 @@ mod tests { assert_eq!(layers(&app), vec![first, second]); } + /// Cmd+Alt+L is New Layer Options… by default. On Windows and Linux the key is Ctrl+Alt+L, and on + /// macOS it is Option+Command+L. Cmd+L is still New Layer. No other command, tool or panel has + /// Cmd+Alt+L by default. + #[test] + fn cmd_alt_l_is_new_layer_options() { + use crate::shortcut_editor::{command_for_key, default_command_shortcut}; + assert_eq!(default_command_shortcut("ui.newLayer"), Some("Cmd+Alt+L")); + assert_eq!(command_for_key("Cmd+Alt+L"), Some("ui.newLayer")); + assert_eq!(command_for_key("Cmd+L"), Some("layer.new")); + let chord = crate::shortcuts::parse("Cmd+Alt+L").expect("parses"); + assert_eq!((chord.modifiers, chord.logical_key), (egui::Modifiers::COMMAND | egui::Modifiers::ALT, egui::Key::L)); + let shown = crate::menus::pretty_shortcut("Cmd+Alt+L"); + assert_eq!(shown, if cfg!(target_os = "macos") { "⌥⌘L" } else { "Ctrl+Alt+L" }); + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + let listed = app.run("shortcuts.list", json!({"query": "ui.newLayer"})).unwrap(); + let rows: Vec<_> = listed.as_array().expect("rows").iter().map(|r| (r["label"].clone(), r["default"].clone())).collect(); + assert_eq!(rows, [(json!("New Layer Options…"), json!("Cmd+Alt+L"))], "Keyboard Shortcuts lists it"); + assert_eq!(app.run("shortcuts.conflicts", json!({})).unwrap(), json!([]), "no other default has the key"); + } + + /// Cmd+Alt+L, pressed through the shortcut handler, opens Layer Options for a new layer as an + /// Alt-click on the panel's New Layer button does, and no layer is added until OK. OK adds the + /// layer above the current layer, as after the Alt-click, and Cancel adds none. Cmd+L still adds + /// a layer without the dialog. + #[test] + fn cmd_alt_l_opens_new_layer_options_as_alt_clicking_new_layer_does() { + fn press(app: &mut VectorcraftApp, modifiers: egui::Modifiers) { + let ctx = egui::Context::default(); + let ev = egui::Event::Key { key: egui::Key::L, physical_key: None, pressed: true, repeat: false, modifiers }; + let mut out = ctx.run_ui(egui::RawInput { events: vec![ev], ..Default::default() }, |ui| crate::shortcuts::handle(app, ui.ctx())); + out.textures_delta.clear(); + } + /// Layer 1 with two rectangles under a layer named Top, with Layer 1 current. + fn two_layers() -> VectorcraftApp { + let (mut app, first, _) = two_rects(); + app.session.execute("layer.new", &json!({"name": "Top"})).unwrap(); + app.session.execute("layer.setCurrent", &json!({"id": first.0})).unwrap(); + app + } + let names = |app: &VectorcraftApp| app.session.active().unwrap().doc.layers.iter().map(|l| l.display_name()).collect::>(); + let dialog = |app: &VectorcraftApp| app.ui.dialog.as_ref().map(|d| (d.kind.clone(), d.fields.clone())); + let mut keyed = two_layers(); + press(&mut keyed, egui::Modifiers::COMMAND | egui::Modifiers::ALT); + let mut clicked = two_layers(); + let ctx = egui::Context::default(); + let new_layer = bar_buttons(&mut clicked, &ctx)[1]; + click_with(&mut clicked, &ctx, new_layer, egui::Modifiers::ALT); + let opened = dialog(&keyed).expect("the key opens a dialog"); + assert_eq!((opened.0.as_str(), opened.1.get("mode")), ("layerOptions", Some(&json!("new")))); + assert_eq!(dialog(&clicked), Some(opened), "the key opens what the Alt-click opens"); + assert_eq!(names(&keyed), ["Layer 1", "Top"], "no layer is added until OK"); + for app in [&mut keyed, &mut clicked] { + crate::dialogs::confirm(app).unwrap(); + } + assert_eq!(names(&keyed), ["Layer 1", "Layer 3", "Top"]); + assert_eq!(names(&clicked), names(&keyed)); + // Cancel adds no layer. + press(&mut keyed, egui::Modifiers::COMMAND | egui::Modifiers::ALT); + assert!(keyed.ui.dialog.is_some()); + crate::dialogs::cancel(&mut keyed); + assert!(keyed.ui.dialog.is_none()); + assert_eq!(names(&keyed), ["Layer 1", "Layer 3", "Top"]); + press(&mut keyed, egui::Modifiers::COMMAND); + assert!(keyed.ui.dialog.is_none(), "Cmd+L opens no dialog"); + assert_eq!(names(&keyed).len(), 4); + } + #[test] fn the_selected_art_square_drags_the_selection_to_another_layer() { let (mut app, [_, sub, g, _, a, c]) = nested(); diff --git a/docs/mcp.md b/docs/mcp.md index 3b904e828..6627e2359 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -1760,7 +1760,7 @@ and `layerRows`, the rows highlighted in the panel. `object.isolate {id}` (a group, a layer or a single object; not type) / `object.exitIsolation`. - **Dialogs and panel state** (control channel and the app's MCP): `ui.layerOptions {ids?}` and `ui.newLayer {sublayer?}` open Layer Options (dialog `layerOptions`: name, color, template, locked, visible, printable, preview, - dimImages, dimPercent; OK is one undo step), `ui.layersPanelOptions` opens Panel Options (`layersPanelOptions`: + dimImages, dimPercent; OK is one undo step; Cmd/Ctrl+Alt+L runs `ui.newLayer` for a new layer), `ui.layersPanelOptions` opens Panel Options (`layersPanelOptions`: layersOnly, locateSelected, rowSize small|medium|large|other, otherSize, thumbLayers, thumbGroups, thumbObjects), and `ui.layersExpand {ids?, open?}` opens or closes rows as their triangles do (Alt-click: everything inside). With `locateSelected` on (Locate Selected Objects, off by default), when the last selected object changes on the canvas diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 6ef1e11ac..83286c37b 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -520,6 +520,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - Cmd/Ctrl+/ (`appearance.addFill`) and Cmd/Ctrl+Alt+/ (`appearance.addStroke`) add a new fill and a new stroke to the selected objects, as the Appearance panel's Add New Fill and Add New Stroke buttons do, on the keys 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 for "Add new fill" and "Add new stroke". Keyboard Shortcuts lists both under Window › Appearance. - Cmd/Ctrl+Alt+Shift+. and , (`type.size.increaseByFive`, `type.size.decreaseByFive`) step the font size of the selected type, or of the text the Type tool has selected, by five times the Size/Leading increment (Preferences › Type); Cmd/Ctrl+Shift+. and , still step it by one increment. - Cmd/Ctrl+Alt+Q (`type.resetTracking`) sets the tracking of the selected type, or of the text the Type tool has selected, to 0 and its kerning to Auto (0 in the Character panel), in one undo step; at a caret it resets the kerning of the character before it. + - Cmd/Ctrl+Alt+L (`ui.newLayer`) opens Layer Options for a new layer, as an Alt-click on the Layers panel's New Layer button does, on the key 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 for "Add a layer while opening the New Layer dialog box". OK adds the layer above the current layer, and Cancel adds none. Cmd/Ctrl+L still adds a layer without the dialog. - Cmd/Ctrl+Shift+X (`type.resetHorizontalScale`) sets the horizontal scale of the selected type, or of the text the Type tool has selected, to 100% as the Character panel shows it (the object's own stretch included), in one undo step; at a caret it leaves the text as it is. - Cmd/Ctrl+Alt+N (`file.newWithoutDialog`) makes a document without opening the New Document dialog, on the key 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 for "Create a document without opening the New Document dialog box". The page does not say which settings the document gets. VectorCraft gives it the settings the dialog opens on (the first Recent entry, or the Print category's Letter while Recent is empty) and the next untitled name. - Shortcut sets other apps save as text (`.kys`) open in Import Shortcut Set from File › Open, from Edit › Keyboard Shortcuts › Import… in the desktop app, or from a drop on the window. It matches each entry to a tool, command or panel by name and decodes the file's key and modifier codes where VectorCraft's default shortcuts agree on them; the user answers the rest, and Apply fills the Keyboard Shortcuts dialog's working set. The answers are kept with the preferences for the next import (`shortcutSet.read`, `importMatches.*`). diff --git a/docs/ui-parity.md b/docs/ui-parity.md index a148caa73..cad1e229d 100644 --- a/docs/ui-parity.md +++ b/docs/ui-parity.md @@ -197,7 +197,7 @@ Cmd/Ctrl+/ (`appearance.addFill`) and Cmd/Ctrl+Alt+/ (`appearance.addStroke`) ad selected objects, as the Appearance panel's Add New Fill and Add New Stroke buttons do. ~110 distinct modified shortcuts on menu commands plus single-key tool shortcuts for the default toolbar -(`crates/tools/src/catalog.rs`), a shortcut editor with chords, and saved sets. Not yet diffed item by item +(`crates/tools/src/catalog.rs`), a shortcut editor with chords, and saved sets. Cmd/Ctrl+Alt+L (`ui.newLayer`) opens Layer Options for a new layer, as an Alt-click on the Layers panel's New Layer button does, and Cmd/Ctrl+L still adds a layer without the dialog. Not yet diffed item by item against Illustrator's published default shortcut list; estimated ~70% of it. Remaining: the panel shortcuts, the type shortcuts (tracking/kerning/leading/baseline steps with Alt/Shift), and selection shortcuts in the Layers panel. The shortcut editor also lists the commands that are in no menu but run without parameters (Edit › Duplicate,