Merge pull request #4377 from dotCooCoo/feat/next-document-shortcut

Window: Cmd+F6 and Cmd+Shift+F6 go to the next and previous document
This commit is contained in:
BFlat
2026-10-30 00:42:17 +07:00
committed by GitHub
32 changed files with 229 additions and 13 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ pub use status_bar::status_bar;
use status_bar::{StatusReadoutWidths, status_readouts};
#[cfg(test)]
use tabs::{CLOSE_BEFORE_TITLE, first_tab_shown, tab_title};
pub use tabs::{doc_tabs, isolation_bar};
pub use tabs::{doc_tabs, isolation_bar, step_document};
/// The application bar's height, in points (compact, as in PhotoCraft).
pub const APP_BAR_HEIGHT: f32 = 32.0;
+38 -12
View File
@@ -114,21 +114,47 @@ pub fn doc_tabs(app: &mut VectorcraftApp, ui: &mut Ui) {
app.status(e);
}
} else if let Some(i) = activate {
app.ui.home = None;
let changed = app.session.active_index() != Some(i);
if app.session.set_active(i)
&& changed
&& app.session.tool_id() == "artboard"
&& let Some(n) = app.session.active().map(|d| d.doc.artboards.len())
{
// A document switch recreates the tool; restore the displayed board as its target.
// Clicking the current tab keeps its existing multiselection and tool options.
let selected = crate::panels::artboards::selected(app, n);
crate::panels::artboards::select(app, selected);
}
show_document(app, i);
}
}
/// Makes document `i` the current one, as a click on its tab does: the Home screen closes, and
/// with the Artboard tool the artboard the document shows becomes the tool's target.
fn show_document(app: &mut VectorcraftApp, i: usize) {
app.ui.home = None;
let changed = app.session.active_index() != Some(i);
if app.session.set_active(i)
&& changed
&& app.session.tool_id() == "artboard"
&& let Some(n) = app.session.active().map(|d| d.doc.artboards.len())
{
// A document switch recreates the tool; restore the displayed board as its target.
// Clicking the current tab keeps its existing multiselection and tool options.
let selected = crate::panels::artboards::selected(app, n);
crate::panels::artboards::select(app, selected);
}
}
/// `window.nextDocument` (`forward`) and `window.previousDocument`: makes the open document after
/// the current one in tab order (before it) the current one, as a click on its tab does. After the
/// last document comes the first, and before the first document comes the last. On the Home
/// screen, where no tab is the current one, the next document is the first and the previous
/// document is the last. Returns `{index}`.
pub fn step_document(app: &mut VectorcraftApp, forward: bool) -> Result<serde_json::Value, String> {
let n = app.session.documents().len();
if n == 0 {
return Err("no document open".into());
}
let i = match (app.session.active_index().filter(|_| app.ui.home.is_none()), forward) {
(Some(a), true) => (a + 1) % n,
(Some(a), false) => (a + n - 1) % n,
(None, true) => 0,
(None, false) => n - 1,
};
show_document(app, i);
Ok(json!({ "index": i }))
}
/// The isolation mode breadcrumb bar. It is drawn over the top of `area` (the canvas), after the
/// canvas, so entering isolation mode does not change the canvas's size (#3126).
pub fn isolation_bar(app: &mut VectorcraftApp, ui: &mut Ui, area: egui::Rect) {
+121
View File
@@ -1294,6 +1294,127 @@ fn artboard_document_tab_roundtrip_preserves_other_tools() {
}
}
/// Press F6 with `modifiers` through the shortcut handler.
fn press_f6(app: &mut crate::VectorcraftApp, modifiers: egui::Modifiers) {
let ctx = egui::Context::default();
let ev = egui::Event::Key { key: egui::Key::F6, 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();
}
/// Cmd+F6 is Next Document and Cmd+Shift+F6 is Previous Document by default: Ctrl+F6 and
/// Ctrl+Shift+F6 on Windows and Linux, Command+F6 and Shift+Command+F6 on macOS. Keyboard
/// Shortcuts lists both under Window. F6 and Shift+F6 are still the Color and Appearance panels'
/// keys, and no other command, tool or panel has either key by default.
#[test]
fn cmd_f6_and_cmd_shift_f6_are_next_and_previous_document() {
use crate::shortcut_editor::{command_for_key, default_command_shortcut, panel_shortcut};
assert_eq!(default_command_shortcut("window.nextDocument"), Some("Cmd+F6"));
assert_eq!(default_command_shortcut("window.previousDocument"), Some("Cmd+Shift+F6"));
assert_eq!(command_for_key("Cmd+F6"), Some("window.nextDocument"));
assert_eq!(command_for_key("Cmd+Shift+F6"), Some("window.previousDocument"));
assert_eq!((panel_shortcut("color"), panel_shortcut("appearance")), (Some("F6"), Some("Shift+F6")));
let chord = crate::shortcuts::parse("Cmd+Shift+F6").expect("parses");
assert_eq!((chord.modifiers, chord.logical_key), (egui::Modifiers::COMMAND | egui::Modifiers::SHIFT, egui::Key::F6));
let shown = (crate::menus::pretty_shortcut("Cmd+F6"), crate::menus::pretty_shortcut("Cmd+Shift+F6"));
let want = if cfg!(target_os = "macos") { ("⌘F6", "⇧⌘F6") } else { ("Ctrl+F6", "Ctrl+Shift+F6") };
assert_eq!((shown.0.as_str(), shown.1.as_str()), want);
let mut app = crate::VectorcraftApp::new(Session::new(), Default::default());
let listed = app.run("shortcuts.list", json!({"query": "Document"})).unwrap();
let rows: Vec<_> = listed
.as_array()
.expect("rows")
.iter()
.filter(|r| r["id"].as_str().is_some_and(|id| id.ends_with("Document") && id.starts_with("window.")))
.map(|r| (r["label"].clone(), r["group"].clone(), r["default"].clone()))
.collect();
assert_eq!(
rows,
[(json!("Next Document"), json!("Window"), json!("Cmd+F6")), (json!("Previous Document"), json!("Window"), json!("Cmd+Shift+F6"))],
"Keyboard Shortcuts lists both"
);
assert_eq!(app.run("shortcuts.conflicts", json!({})).unwrap(), json!([]), "no other default has the keys");
}
/// Cmd+F6 makes the next open document in tab order the current one, and Cmd+Shift+F6 makes the
/// previous one the current one. After the last document comes the first, and before the first document comes the
/// last. From the Home screen the keys go to the first and the last document and close the screen.
/// With no document open the commands are disabled and fail with "no document open".
#[test]
fn cmd_f6_goes_to_the_next_document_and_cmd_shift_f6_to_the_previous_one() {
let mut app = crate::VectorcraftApp::new(Session::new(), Default::default());
assert!(!crate::menus::enabled(&app, "window.nextDocument") && !crate::menus::enabled(&app, "window.previousDocument"));
assert_eq!(app.run("window.nextDocument", json!({})).unwrap_err(), "no document open");
assert_eq!(app.run("window.previousDocument", json!({})).unwrap_err(), "no document open");
for name in ["A", "B", "C"] {
app.run("file.new", json!({"width": 100, "height": 100, "name": name})).unwrap();
}
assert!(crate::menus::enabled(&app, "window.nextDocument") && crate::menus::enabled(&app, "window.previousDocument"));
assert_eq!(app.session.active_index(), Some(2));
let mut seen = vec![];
for modifiers in [egui::Modifiers::COMMAND; 4] {
press_f6(&mut app, modifiers);
seen.push(app.session.active_index());
}
assert_eq!(seen, [Some(0), Some(1), Some(2), Some(0)], "Cmd+F6 goes right and wraps from the last to the first");
seen.clear();
for modifiers in [egui::Modifiers::COMMAND | egui::Modifiers::SHIFT; 4] {
press_f6(&mut app, modifiers);
seen.push(app.session.active_index());
}
assert_eq!(seen, [Some(2), Some(1), Some(0), Some(2)], "Cmd+Shift+F6 goes left and wraps from the first to the last");
assert_eq!(app.run("window.nextDocument", json!({})).unwrap(), json!({"index": 0}));
assert_eq!(app.run("window.previousDocument", json!({})).unwrap(), json!({"index": 2}));
app.session.set_active(1);
app.run("app.home", json!({})).unwrap();
press_f6(&mut app, egui::Modifiers::COMMAND);
assert_eq!((app.session.active_index(), app.ui.home), (Some(0), None), "from the Home screen Cmd+F6 shows the first");
app.session.set_active(1);
app.run("app.home", json!({})).unwrap();
press_f6(&mut app, egui::Modifiers::COMMAND | egui::Modifiers::SHIFT);
assert_eq!((app.session.active_index(), app.ui.home), (Some(2), None), "and Cmd+Shift+F6 the last");
// One document: the keys keep it current.
let mut one = crate::VectorcraftApp::new(Session::new(), Default::default());
one.run("file.new", json!({"width": 100, "height": 100})).unwrap();
press_f6(&mut one, egui::Modifiers::COMMAND);
press_f6(&mut one, egui::Modifiers::COMMAND | egui::Modifiers::SHIFT);
assert_eq!(one.session.active_index(), Some(0));
}
/// With the Artboard tool, Cmd+F6 and Cmd+Shift+F6 switch documents as a click on a tab does: the
/// document switched back to keeps the artboard it shows as the tool's target, so the Properties
/// panel's Delete Artboard then removes that artboard. Switching is not an edit.
#[test]
fn cmd_f6_switches_documents_as_a_tab_click_does_with_the_artboard_tool() {
let (mut app, ctx) = artboard_document_app();
app.select_tool("artboard");
artboard_navigation_next(&mut app, &ctx);
assert_eq!(app.session.tool_options()["selected"], json!([1]));
let before = app.session.active().unwrap().doc.clone();
let undo = app.session.active().unwrap().history.undo.len();
press_f6(&mut app, egui::Modifiers::COMMAND);
assert_eq!(app.session.active_index(), Some(1));
// The app draws frames while the other document is the current one.
artboard_navigation_properties_frame(&mut app, &ctx, vec![]);
artboard_document_tabs_frame(&mut app, &ctx, vec![]);
press_f6(&mut app, egui::Modifiers::COMMAND | egui::Modifiers::SHIFT);
assert_eq!(app.session.active_index(), Some(0));
app.sync_views();
assert_eq!(app.session.tool_options()["selected"], json!([1]), "the tool targets the displayed artboard");
assert_eq!(app.view().unwrap().artboard, 1, "the document keeps its displayed artboard");
assert_eq!(app.session.active().unwrap().doc, before, "switching documents is not a document edit");
assert_eq!(app.session.active().unwrap().history.undo.len(), undo);
artboard_navigation_properties_frame(&mut app, &ctx, vec![]);
let texts = artboard_navigation_properties_frame(&mut app, &ctx, vec![]);
let at = texts.iter().find(|(label, _)| label == "Delete Artboard").map(|(_, r)| r.center()).expect("Properties Delete button");
let button = |pressed| egui::Event::PointerButton { pos: at, button: egui::PointerButton::Primary, pressed, modifiers: Default::default() };
for event in [egui::Event::PointerMoved(at), button(true), button(false)] {
artboard_navigation_properties_frame(&mut app, &ctx, vec![event]);
}
let names: Vec<_> = app.session.active().unwrap().doc.artboards.iter().map(|a| a.name.clone()).collect();
assert_eq!(names, ["Keep"], "Delete Artboard after the keys removes the artboard the document shows");
}
/// One history entry: its label, document and selection.
type HistoryRow = (String, Arc<vectorcraft_doc::Document>, Arc<vectorcraft_doc::Selection>);
+2
View File
@@ -1991,6 +1991,8 @@ font-technology Variable Променливи
New View Нов изглед
New View… Нов изглед…
New Window Нов прозорец
Next Document Следващ документ
Previous Document Предишен документ
New Without Dialog Нов без диалогов прозорец
New Workspace Ново работно пространство
New Workspace… Ново работно пространство…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -625,6 +625,8 @@
Saved View 25 Uložené zobrazení 25
Window Okno
New Window Nové okno
Next Document Další dokument
Previous Document Předchozí dokument
Cascade Do kaskády
Tile Vedle sebe
Float in Window Uvolnit do samostatného okna
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -578,6 +578,8 @@
Saved View 25 Gemt visning 25
Window Vindue
New Window Nyt vindue
Next Document Næste dokument
Previous Document Forrige dokument
Cascade Kaskade
Tile Fliselæg
Float in Window Flyd i vindue
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -2004,6 +2004,8 @@ file-filter Templates Vorlagen
New View Neue Ansicht
New View… Neue Ansicht…
New Window Neues Fenster
Next Document Nächstes Dokument
Previous Document Vorheriges Dokument
New Without Dialog Neu ohne Dialogfeld
New Workspace Neuer Arbeitsbereich
New Workspace… Neuer Arbeitsbereich…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -1674,6 +1674,8 @@ font-technology Variable Μεταβλητές
New Layer… Νέο επίπεδο…
New Swatch… Νέο δείγμα χρώματος…
New Window Νέο παράθυρο
Next Document Επόμενο έγγραφο
Previous Document Προηγούμενο έγγραφο
New Without Dialog Νέο χωρίς παράθυρο διαλόγου
New Workspace Νέος χώρος εργασίας
New Workspace… Νέος χώρος εργασίας…
Can't render this file because it contains an unexpected character in line 164 and column 5.
+2
View File
@@ -2046,6 +2046,8 @@ font-technology Variable Variables
New View Nueva vista
New View… Nueva vista…
New Window Nueva ventana
Next Document Documento siguiente
Previous Document Documento anterior
New Without Dialog Nuevo sin cuadro de diálogo
New Workspace Nuevo espacio de trabajo
New Workspace… Nuevo espacio de trabajo…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -616,6 +616,8 @@
Saved View 25 Tallennettu näkymä 25
Window Ikkuna
New Window Uusi ikkuna
Next Document Seuraava asiakirja
Previous Document Edellinen asiakirja
Cascade Porrasta
Tile Vierekkäin
Float in Window Kelluta ikkunassa
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -2043,6 +2043,8 @@ font-technology Variable Variables
New View Nouvelle vue
New View… Nouvelle vue…
New Window Nouvelle fenêtre
Next Document Document suivant
Previous Document Document précédent
New Without Dialog Nouveau sans boîte de dialogue
New Workspace Nouvel espace de travail
New Workspace… Nouvel espace de travail…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -2492,6 +2492,8 @@ file-filter Templates Sablonok
New View Új nézet
New View… Új nézet…
New Window Új ablak
Next Document Következő dokumentum
Previous Document Előző dokumentum
New Without Dialog Új párbeszédablak nélkül
New Workspace Új munkaterület
New Workspace… Új munkaterület…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -578,6 +578,8 @@
Saved View 25 Tampilan tersimpan 25
Window Jendela
New Window Jendela baru
Next Document Dokumen berikutnya
Previous Document Dokumen sebelumnya
Cascade Bertingkat
Tile Ubin
Float in Window Mengambang di jendela
1 # Indonesian (id) catalog. Same format as zh-hant.tsv: context <TAB> source <TAB> translation
578
579
580
581
582
583
584
585
+2
View File
@@ -2073,6 +2073,8 @@ file-filter Templates Modelli
New View Nuova vista
New View… Nuova vista…
New Window Nuova finestra
Next Document Documento successivo
Previous Document Documento precedente
New Without Dialog Nuovo senza finestra di dialogo
New Workspace Nuova area di lavoro
New Workspace… Nuova area di lavoro…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -616,6 +616,8 @@ font-technology Variable バリアブル
Saved View 25 保存した表示 25
Window ウィンドウ
New Window 新規ウィンドウ
Next Document 次のドキュメント
Previous Document 前のドキュメント
Cascade 重ねて表示
Tile 並べて表示
Float in Window ウィンドウを分離
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -530,6 +530,8 @@
Saved View 25 შენახული ხედი 25
Window ფანჯარა
New Window ახალი ფანჯარა
Next Document შემდეგი დოკუმენტი
Previous Document წინა დოკუმენტი
Cascade კასკადად
Tile ფილებად
Float in Window ცალკე ფანჯარაში გამოტანა
Can't render this file because it is too large.
+2
View File
@@ -614,6 +614,8 @@ font-technology Variable 가변
Saved View 25 저장된 보기 25
Window 윈도우
New Window 새 창
Next Document 다음 문서
Previous Document 이전 문서
Cascade 계단식 배열
Tile 바둑판식 배열
Float in Window 창에 플로팅
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -592,6 +592,8 @@
Saved View 25 Opgeslagen weergave 25
Window Venster
New Window Nieuw venster
Next Document Volgend document
Previous Document Vorig document
Cascade Trapsgewijs schikken
Tile Naast elkaar schikken
Float in Window In venster zweven
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -583,6 +583,8 @@
Saved View 25 Zapisany widok 25
Window Okno
New Window Nowe okno
Next Document Następny dokument
Previous Document Poprzedni dokument
Cascade Kaskadowo
Tile Sąsiadująco
Float in Window Pływające w oknie
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -1653,6 +1653,8 @@
New Symbol… Novo Símbolo…
New View… Nova Visualização…
New Window Nova Janela
Next Document Próximo Documento
Previous Document Documento Anterior
New Without Dialog Novo sem Diálogo
New Workspace Novo Espaço de Trabalho
New Workspace… Novo Espaço de Trabalho…
Can't render this file because it contains an unexpected character in line 6 and column 47.
+2
View File
@@ -2069,6 +2069,8 @@ file-filter Templates Шаблоны
New View Новый вид
New View… Новый вид…
New Window Новое окно
Next Document Следующий документ
Previous Document Предыдущий документ
New Without Dialog Создать без диалогового окна
New Workspace Новая рабочая среда
New Workspace… Новая рабочая среда…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -1991,6 +1991,8 @@ font-technology Variable Variabla
New View Ny vy
New View… Ny vy…
New Window Nytt fönster
Next Document Nästa dokument
Previous Document Föregående dokument
New Without Dialog Ny utan dialogruta
New Workspace Ny arbetslayout
New Workspace… Ny arbetslayout…
Can't render this file because it contains an unexpected character in line 5 and column 25.
+2
View File
@@ -1871,6 +1871,8 @@ font-technology Variable Değişken
New View Yeni Görünüm
New View… Yeni Görünüm…
New Window Yeni Pencere
Next Document Sonraki Belge
Previous Document Önceki Belge
New Workspace Yeni Çalışma Alanı
New Workspace… Yeni Çalışma Alanı…
New {kind} Style… Yeni {kind} Stili…
Can't render this file because it contains an unexpected character in line 5 and column 25.
+2
View File
@@ -2065,6 +2065,8 @@ file-filter Templates Шаблони
New View Новий вигляд
New View… Новий вигляд…
New Window Нове вікно
Next Document Наступний документ
Previous Document Попередній документ
New Without Dialog Створити без діалогового вікна
New Workspace Нове робоче середовище
New Workspace… Нове робоче середовище…
Can't render this file because it contains an unexpected character in line 178 and column 5.
+2
View File
@@ -2300,6 +2300,8 @@ file-filter Templates Mẫu
New View Chế độ xem mới
New View… Chế độ xem mới…
New Window Cửa sổ mới
Next Document Tài liệu tiếp theo
Previous Document Tài liệu trước
New Without Dialog Mới không qua hộp thoại
New Workspace Không gian làm việc mới
New Workspace… Không gian làm việc mới…
Can't render this file because it contains an unexpected character in line 200 and column 5.
+2
View File
@@ -1944,6 +1944,8 @@ file-filter Templates 模板
New View 新建视图
New View… 新建视图…
New Window 新建窗口
Next Document 下一个文档
Previous Document 上一个文档
New Without Dialog 无对话框新建
New Workspace 新建工作区
New Workspace… 新建工作区…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -2869,6 +2869,8 @@ file-filter Templates 範本
New View 新增檢視
New View… 新增檢視…
New Window 新增視窗
Next Document 下一個文件
Previous Document 上一個文件
New Without Dialog 新增但不開啟對話方塊
New Workspace 新增工作區
New Workspace… 新增工作區…
Can't render this file because it contains an unexpected character in line 6 and column 47.
+2
View File
@@ -511,6 +511,8 @@ fn run_ui_part6(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option<Result<
None => Err("brightness must be dark|mediumDark|mediumLight|light".into()),
},
"window.newWindow" => Err("New Window isn't available yet: a document opens in one window".into()),
"window.nextDocument" => crate::chrome::step_document(app, true),
"window.previousDocument" => crate::chrome::step_document(app, false),
"tool.select" => match s("tool") {
Some(t) if vectorcraft_tools::tool_info(&t).is_some() => {
app.select_tool(&t);
+1
View File
@@ -263,6 +263,7 @@ pub fn enabled(app: &VectorcraftApp, id: &str) -> bool {
"ui.perspectivePlane" => app.session.active().is_some(),
// Only one window per document so far: greyed out, as Window › Arrange's items are (#3724).
"window.newWindow" => false,
"window.nextDocument" | "window.previousDocument" => !app.session.documents().is_empty(),
_ => true,
}
}
+12
View File
@@ -518,6 +518,18 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[
("window.workspace.rename", "Rename Workspace", "", "{name, to}"),
("window.workspace.list", "List Workspaces", "", "{}"),
("window.newWindow", "New Window", "", "{}"),
(
"window.nextDocument",
"Next Document",
"Cmd+F6",
"{} make the open document after the current one in tab order the current one, as a click on its tab does (after the last document it goes to the first, and from the Home screen to the first); returns {index}",
),
(
"window.previousDocument",
"Previous Document",
"Cmd+Shift+F6",
"{} make the open document before the current one in tab order the current one, as a click on its tab does (before the first document it goes to the last, and from the Home screen to the last); returns {index}",
),
("tool.select", "Select Tool", "", "{tool: id} (see tools)"),
(
"tool.setOption",
+5
View File
@@ -3270,6 +3270,11 @@ each range, and a size of 0 or less is read as 1 pt.
The desktop app's `file.newDialog` (Cmd/Ctrl+N) opens the New Document dialog on the first Recent entry, or on the
Print category's Letter while Recent is empty. `file.newWithoutDialog` (Cmd/Ctrl+Alt+N) runs `file.new` with those
settings and the next untitled name, without opening the dialog, and returns what `file.new` returns.
`window.nextDocument` (Cmd/Ctrl+F6) and `window.previousDocument` (Cmd/Ctrl+Shift+F6) make the next and the previous
open document in tab order the current one, as a click on its tab does, and return `{index}`. After the last document
comes the first, and before the first document comes the last. From the Home screen they go to the first and the last
document. With no document open they fail with "no document open". `document.activate {index}` makes a given document
the current one.
```json
{"name":"run_command","arguments":{"command":"file.new","params":{"preset":"A4","orientation":"landscape","artboards":4,"artboardLayout":{"columns":2},"bleed":9}}}
+1
View File
@@ -623,6 +623,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- F8 (`ui.symbolOptions`) opens Symbol Options for a new symbol made from the selection, as the Symbols panel's New Symbol 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 making a new symbol. OK makes the symbol and puts an instance of it in place of the art, and Cancel makes none. With nothing selected, F8 opens no dialog, and the status bar says that nothing is selected. Shift+F8 and Cmd/Ctrl+F8 still show the Transform and Info panels.
- 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 making a new document with no 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.
- Cmd/Ctrl+F6 (`window.nextDocument`) and Cmd/Ctrl+Shift+F6 (`window.previousDocument`) make the next and the previous open document the current one, in tab order and as a click on its tab does, 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 navigating to the next and the previous document. After the last document comes the first, and before the first document comes the last. From the Home screen the keys go to the first and the last document. Keyboard Shortcuts lists both under Window.
- Cmd/Ctrl+Alt+Shift+U (`document.switchUnits`) switches the document to the next unit of measurement, in one undo step, 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 switching units of measurement. The page does not give an order. VectorCraft steps through the units in the order a right-click on a ruler lists them (Points, Picas, Inches, Millimeters, Centimeters, Pixels, Feet & Inches, Meters, Yards, Feet) and goes from Feet back to Points.
- 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.*`).
- On Windows and Linux the app bar is the window's title bar (its own minimize, maximize and close buttons, drag to move, edges to resize), with the dragon app icon as the brand mark; dialogs and menus size to their content. As on a native menu bar, once one menu is open, moving the pointer onto another title opens that one.