diff --git a/crates/ui-egui/src/menus/run.rs b/crates/ui-egui/src/menus/run.rs index 7a777117e..5ba39f237 100644 --- a/crates/ui-egui/src/menus/run.rs +++ b/crates/ui-egui/src/menus/run.rs @@ -375,6 +375,12 @@ fn run_ui_part5(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option go_to_artboard(app, p), + // The navigator's four buttons as commands of their own, so Edit › Keyboard Shortcuts lists + // them (view.goToArtboard needs an index, so a key alone can't run it). + "view.firstArtboard" => go_to_artboard(app, &json!({ "index": "first" })), + "view.previousArtboard" => go_to_artboard(app, &json!({ "index": "previous" })), + "view.nextArtboard" => go_to_artboard(app, &json!({ "index": "next" })), + "view.lastArtboard" => go_to_artboard(app, &json!({ "index": "last" })), "view.presentation" => { app.ui.screen_mode = if app.ui.screen_mode == 3 { 0 } else { 3 }; Ok(json!(app.ui.screen_mode)) diff --git a/crates/ui-egui/src/menus/ui_commands.rs b/crates/ui-egui/src/menus/ui_commands.rs index ba15ba83f..5f7dc201e 100644 --- a/crates/ui-egui/src/menus/ui_commands.rs +++ b/crates/ui-egui/src/menus/ui_commands.rs @@ -390,6 +390,20 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[ "", "{index: 0-based number | \"first\" | \"previous\" | \"next\" | \"last\"} make that artboard the status bar navigator's (the one Fit Artboard in Window and Actual Size show) and fit it in the window → {index}", ), + ( + "view.firstArtboard", + "First Artboard", + "", + "{} the status bar navigator's First Artboard button: view.goToArtboard {index: \"first\"} → {index}", + ), + ( + "view.previousArtboard", + "Previous Artboard", + "", + "{} the navigator's Previous Artboard button: view.goToArtboard {index: \"previous\"} → {index}", + ), + ("view.nextArtboard", "Next Artboard", "", "{} the navigator's Next Artboard button: view.goToArtboard {index: \"next\"} → {index}"), + ("view.lastArtboard", "Last Artboard", "", "{} the navigator's Last Artboard button: view.goToArtboard {index: \"last\"} → {index}"), ("view.edges", "Hide Edges", "Cmd+H", "{}"), ("view.artboards", "Hide Artboards", "Cmd+Shift+H", "{}"), ( diff --git a/crates/ui-egui/src/shortcuts/tests.rs b/crates/ui-egui/src/shortcuts/tests.rs index 1ba304e61..2693965e5 100644 --- a/crates/ui-egui/src/shortcuts/tests.rs +++ b/crates/ui-egui/src/shortcuts/tests.rs @@ -2093,3 +2093,31 @@ fn a_plain_up_nudges_unless_a_corner_widget_is_pressed() { assert_eq!((x2, y2), (x1, y1), "no nudge while the widget is pressed"); assert_eq!(kind2, CornerKind::InvertedRound, "Up stepped the kind"); } + +/// #5218: the status bar navigator's four buttons are commands of their own, listed in Edit › +/// Keyboard Shortcuts without a default key, so a user can give them one (Page Up and Page Down +/// here) and step through the artboards from the keyboard. +#[test] +fn artboard_navigation_commands_can_be_given_keys() { + let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default()); + app.run("file.new", json!({"width": 200, "height": 100, "artboards": 3})).unwrap(); + for id in ["view.firstArtboard", "view.previousArtboard", "view.nextArtboard", "view.lastArtboard"] { + let list = app.run("shortcuts.list", json!({ "query": id })).unwrap(); + assert!(list.as_array().unwrap().iter().any(|e| e["id"] == id), "{id} is listed: {list}"); + assert_eq!(crate::shortcut_editor::default_command_shortcut(id), None, "{id} has no default key"); + } + let at = |app: &VectorcraftApp| app.view().unwrap().artboard; + assert_eq!(app.run("view.lastArtboard", json!({})), Ok(json!({"index": 2}))); + assert_eq!(app.run("view.nextArtboard", json!({})), Ok(json!({"index": 2})), "the last artboard stays"); + assert_eq!(app.run("view.firstArtboard", json!({})), Ok(json!({"index": 0}))); + assert_eq!(app.run("view.previousArtboard", json!({})), Ok(json!({"index": 0})), "the first artboard stays"); + crate::keymap::set_override(&mut app.ui.shortcut_overrides, "view.nextArtboard", "PageDown"); + crate::keymap::set_override(&mut app.ui.shortcut_overrides, "view.previousArtboard", "PageUp"); + let press = |key| vec![egui::Event::Key { key, physical_key: None, pressed: true, repeat: false, modifiers: Modifiers::NONE }]; + frame(&mut app, press(Key::PageDown)); + assert_eq!(at(&app), 1, "Page Down goes to the next artboard"); + frame(&mut app, press(Key::PageDown)); + assert_eq!(at(&app), 2); + frame(&mut app, press(Key::PageUp)); + assert_eq!(at(&app), 1, "Page Up goes to the previous artboard"); +} diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 9087b33cd..dd083fdb1 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -808,7 +808,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur Snap to Glyph options (#4232, [Snap to Glyph options](https://helpx.adobe.com/illustrator/desktop/design-with-text/special-characters-glyphs/snap-to-glyph-options.html)): the Character panel menu's Show Snap to Glyph Options (Hide Snap to Glyph Options) shows a Snap to Glyph section in the panel, with or without type selected, with a checkbox for each kind of guide: Baseline, x-height, Glyph Bounds, Proximity Guides (the edges of each character's width), Angular Guides and Anchor Point. Each off takes its guides away and leaves the others (`view.snapToGlyph.options`, the `glyphSnap` preference group, all on by default). Angular Guides covers a glyph's slanted strokes and every guide of rotated type, which runs at its angle; the boxes are dimmed while View › Snap to Glyph is off (VectorCraft choices; the page settles neither). Tested in `crates/tools/src/guides/tests.rs`, `crates/ui-egui/src/panels/character/tests.rs` and `crates/engine/src/tests/prefs.rs`. - **Artboard click-to-fit (#1227):** a plain click fits the topmost editable object or group to a new artboard, including its stroke like Fit to Selected Art, and makes the new artboard active. Art and its selection stay unchanged; creation is one undo step. Handles, modified clicks, drags, Escape and tool changes retain their meaning. Tested inside an artboard and on the pasteboard (`crates/engine/tests/it/artboard_click.rs`); the public [CS6 Printing Guide](https://helpx.adobe.com/content/dam/help/en/indesign/pdf/CS6_PrintGuide.pdf) documents the object-click gesture. This legacy gesture adds evidence without regrading the 2026 canvas controls. - **Rotated views:** with the view rotated, Fit Artboard in Window, Fit All in Window and the status bar's artboard navigator fit the artboard or the art as the view turns it, and a Zoom tool marquee (Animated Zoom or GPU Performance off) zooms to the area dragged across. Ruler guides on no artboard turn with the view. The canvas draws the selection marquee where the tools select, Show Hidden Characters draws the marks of type partly inside the canvas, the Contextual Task Bar stays under the selection, and the Navigator's red box outlines what the window shows. -- **Views:** the status bar's artboard navigator (first, previous, next, last; `view.goToArtboard`) goes to an artboard, fits it and synchronizes the Artboard tool selection when that tool is active; ordinary object selection is preserved. Actual Next-button regressions cover keyboard/Properties deletion and Undo in `chrome.rs`. Fit Artboard in Window and Actual Size show the navigator's artboard. The Artboards panel highlights that active artboard: clicking a row makes it active (the Artboard tool's too, while it is chosen) as does clicking an artboard with the Artboard tool, double-clicking its number also fits it in the window and double-clicking its name renames it. Duplicate Artboards, or a row dragged onto New Artboard, copies the artboard with its art (#446). Escape leaves Presentation Mode, `view.screenMode` from it goes back to Normal Screen Mode, and the screen mode isn't saved with the preferences: the app always starts in Normal Screen Mode with its menus and panels (#472). View › Pixel Preview shows the art as the document pixels it rasterizes to, one per point and hard-edged when magnified, turning with a rotated view (it used to draw the smooth vectors, with only the pixel grid over them). In Outline view a template layer keeps drawing in Preview, dimmed, so a picture placed to be traced stays visible under the outlines (#1505, fixes #1486). +- **Views:** the status bar's artboard navigator (first, previous, next, last; `view.goToArtboard`) goes to an artboard, fits it and synchronizes the Artboard tool selection when that tool is active; ordinary object selection is preserved. Its four buttons are also commands of their own (`view.firstArtboard`, `view.previousArtboard`, `view.nextArtboard`, `view.lastArtboard`), listed in Edit › Keyboard Shortcuts without a default key, so a user can give them keys such as Page Up and Page Down (#5218). Actual Next-button regressions cover keyboard/Properties deletion and Undo in `chrome.rs`. Fit Artboard in Window and Actual Size show the navigator's artboard. The Artboards panel highlights that active artboard: clicking a row makes it active (the Artboard tool's too, while it is chosen) as does clicking an artboard with the Artboard tool, double-clicking its number also fits it in the window and double-clicking its name renames it. Duplicate Artboards, or a row dragged onto New Artboard, copies the artboard with its art (#446). Escape leaves Presentation Mode, `view.screenMode` from it goes back to Normal Screen Mode, and the screen mode isn't saved with the preferences: the app always starts in Normal Screen Mode with its menus and panels (#472). View › Pixel Preview shows the art as the document pixels it rasterizes to, one per point and hard-edged when magnified, turning with a rotated view (it used to draw the smooth vectors, with only the pixel grid over them). In Outline view a template layer keeps drawing in Preview, dimmed, so a picture placed to be traced stays visible under the outlines (#1505, fixes #1486). In Presentation Mode, → and ← go to the next and the previous artboard, and a click anywhere goes to the next one, as the status bar navigator's Next and Previous do (`view.goToArtboard`): on the last artboard → and a click leave the view as it is, and so does ← on the first. The arrow keys do not move the selection there, and the tool does not receive a click, so a click selects and draws nothing (behavior reference: [View artwork](https://helpx.adobe.com/illustrator/using/viewing-artwork.html)). Full Screen Mode and Presentation Mode put the window full screen, covering the taskbar and the window's frame, and Normal Screen Mode or Full Screen Mode with Menu Bar puts it back (#4365); a window the user made full screen stays so. An unmodified Escape in either Full Screen Mode exits to Normal Screen Mode after any open popup, focused field, tool gesture, ruler-origin drag, floating Tools panel move, edit mode, drawer, flyout or view rotation has taken its Escape; a later Escape exits when that action is finished. This is a VectorCraft choice for both full screen variants, based on the public [shortcut list](https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html); the list does not specify that priority or the return mode.