diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index e6914ada9..aed900147 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -1060,6 +1060,8 @@ font-technology Variable Променливи Export for Screens Settings Настройки за експортиране за екрани Export for Screens… Експортиране за екрани… Export Keyboard Shortcuts… Експортиране на клавишни комбинации… + Export Keyboard Shortcuts as Text… Експортиране на клавишни комбинации като текст… + Export Text… Експортиране на текст… Export Option Опция за експортиране Export PDF Експортиране в PDF Export PDF Presets Предварителни настройки за PDF diff --git a/crates/ui-egui/src/i18n/bs.tsv b/crates/ui-egui/src/i18n/bs.tsv index 9bdeb8492..eef02974f 100644 --- a/crates/ui-egui/src/i18n/bs.tsv +++ b/crates/ui-egui/src/i18n/bs.tsv @@ -938,6 +938,8 @@ lighting Light Svjetlo Expand Layers Panel Rows Proširi redove panela slojeva Export CSS… Izvezi CSS… Export Keyboard Shortcuts… Izvezi prečice na tastaturi… + Export Keyboard Shortcuts as Text… Izvezi prečice na tastaturi kao tekst… + Export Text… Izvezi tekst… File Info Dialog Dijalog informacija o datoteci Go to Artboard Idi na radnu ploču First Artboard Prva radna ploča diff --git a/crates/ui-egui/src/i18n/ca.tsv b/crates/ui-egui/src/i18n/ca.tsv index f28e1c8fd..dbf6d691f 100644 --- a/crates/ui-egui/src/i18n/ca.tsv +++ b/crates/ui-egui/src/i18n/ca.tsv @@ -1466,6 +1466,8 @@ Export for Screens Settings Configuració d’exportació per a pantalles Export for Screens… Exporta per a pantalles… Export Keyboard Shortcuts… Exporta les dreceres de teclat… + Export Keyboard Shortcuts as Text… Exporta les dreceres de teclat com a text… + Export Text… Exporta el text… Export Layer Exporta la capa Export Library Exporta la biblioteca Export Library… Exporta la biblioteca… diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 18dba1eec..6711c58ef 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -957,6 +957,8 @@ lighting Light Světlo Expand Layers Panel Rows Rozbalit řádky panelu Vrstvy Export CSS… Exportovat CSS… Export Keyboard Shortcuts… Exportovat klávesové zkratky… + Export Keyboard Shortcuts as Text… Exportovat klávesové zkratky jako text… + Export Text… Exportovat text… File Info Dialog Dialogové okno Informace o souboru Go to Artboard Přejít na pracovní plochu First Artboard První pracovní plocha diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index c506c7106..de8fa2ed6 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -878,6 +878,8 @@ lighting Light Lys Expand Layers Panel Rows Udvid rækker i lagpanelet Export CSS… Eksportér CSS… Export Keyboard Shortcuts… Eksportér tastaturgenveje… + Export Keyboard Shortcuts as Text… Eksportér tastaturgenveje som tekst… + Export Text… Eksportér tekst… File Info Dialog Dialogboks til filoplysninger Go to Artboard Gå til tegnebræt Graphic Style Options… Indstillinger for grafisk typografi… diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index b2588ed89..67c5e629f 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -1060,6 +1060,8 @@ font-technology Variable Variabel Export for Screens Settings Einstellungen für „Für Bildschirme exportieren“ Export for Screens… Für Bildschirme exportieren… Export Keyboard Shortcuts… Tastaturbefehle exportieren… + Export Keyboard Shortcuts as Text… Tastaturbefehle als Text exportieren… + Export Text… Text exportieren… Export Option Exportoption Export PDF PDF exportieren Export PDF Presets PDF-Vorgaben exportieren diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 497682bf9..d9d5614a2 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -1033,6 +1033,8 @@ font-technology Variable Μεταβλητές Export for Screens Settings Ρυθμίσεις εξαγωγής για οθόνες Export for Screens… Εξαγωγή για οθόνες… Export Keyboard Shortcuts… Εξαγωγή συντομεύσεων πληκτρολογίου… + Export Keyboard Shortcuts as Text… Εξαγωγή συντομεύσεων πληκτρολογίου ως κείμενο… + Export Text… Εξαγωγή κειμένου… Export Option Επιλογή εξαγωγής Export PDF Εξαγωγή PDF Export PDF Presets Εξαγωγή προεπιλογών PDF diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index ee8dd703f..678f97bb5 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -1089,6 +1089,8 @@ font-technology Variable Variables Export for Screens Settings Ajustes de Exportar para pantallas Export for Screens… Exportar para pantallas… Export Keyboard Shortcuts… Exportar métodos abreviados de teclado… + Export Keyboard Shortcuts as Text… Exportar métodos abreviados de teclado como texto… + Export Text… Exportar texto… Export Option Opción de exportación Export PDF Exportar PDF Export PDF Presets Exportar preajustes de PDF diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index 8bc4d5163..ef68bedd1 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -936,6 +936,8 @@ lighting Light Valo Expand Layers Panel Rows Laajenna tasopaneelin rivit Export CSS… Vie CSS… Export Keyboard Shortcuts… Vie pikanäppäimet… + Export Keyboard Shortcuts as Text… Vie pikanäppäimet tekstinä… + Export Text… Vie teksti… File Info Dialog Tiedoston tiedot -valintaikkuna Go to Artboard Siirry piirtoalustaan First Artboard Ensimmäinen piirtoalusta diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 4ffec6fcb..80344c35d 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -1084,6 +1084,8 @@ font-technology Variable Variables Export for Screens Settings Paramètres d’Exporter pour les écrans Export for Screens… Exporter pour les écrans… Export Keyboard Shortcuts… Exporter les raccourcis clavier… + Export Keyboard Shortcuts as Text… Exporter les raccourcis clavier en texte… + Export Text… Exporter le texte… Export Option Option d’exportation Export PDF Exporter en PDF Export PDF Presets Exporter les paramètres prédéfinis PDF diff --git a/crates/ui-egui/src/i18n/hi.tsv b/crates/ui-egui/src/i18n/hi.tsv index f14b40c0c..d181ae8dd 100644 --- a/crates/ui-egui/src/i18n/hi.tsv +++ b/crates/ui-egui/src/i18n/hi.tsv @@ -1631,6 +1631,8 @@ Export for Screens Settings स्क्रीन सेटिंग के लिए निर्यात Export for Screens… स्क्रीन के लिए निर्यात… Export Keyboard Shortcuts… कीबोर्ड शॉर्टकट निर्यात करें… + Export Keyboard Shortcuts as Text… कीबोर्ड शॉर्टकट टेक्स्ट के रूप में निर्यात करें… + Export Text… टेक्स्ट निर्यात करें… Export Layer निर्यात लेयर Export Library लाइब्रेरी Export Library… लाइब्रेरी निर्यात करें… diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 966e52a74..dea4dff15 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -1367,6 +1367,8 @@ font-technology Variable Változó Export for Screens Settings Exportálás képernyőkre – beállítások Export for Screens… Exportálás képernyőkre… Export Keyboard Shortcuts… Billentyűparancsok exportálása… + Export Keyboard Shortcuts as Text… Billentyűparancsok exportálása szövegként… + Export Text… Szöveg exportálása… Export Library Könyvtár exportálása Export Library… Könyvtár exportálása… Export Option Exportálási beállítás diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index 0dc1c7974..837d84f32 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -878,6 +878,8 @@ lighting Light Cahaya Expand Layers Panel Rows Perluas baris panel Lapisan Export CSS… Ekspor CSS… Export Keyboard Shortcuts… Ekspor pintasan papan ketik… + Export Keyboard Shortcuts as Text… Ekspor pintasan papan ketik sebagai teks… + Export Text… Ekspor teks… File Info Dialog Dialog info file Go to Artboard Buka artboard Graphic Style Options… Opsi gaya grafis… diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 97426df98..dfa7856d4 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -1090,6 +1090,8 @@ font-technology Variable Variabili Export for Screens Settings Impostazioni Esporta per schermi Export for Screens… Esporta per schermi… Export Keyboard Shortcuts… Esporta scelte rapide da tastiera… + Export Keyboard Shortcuts as Text… Esporta scelte rapide da tastiera come testo… + Export Text… Esporta testo… Export Option Opzione di esportazione Export PDF Esporta PDF Export PDF Presets Esporta preset PDF diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 53bf5ed0f..a2bbe2630 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -1581,6 +1581,8 @@ digits Malayalam マラヤーラム数字 Export Document ドキュメントを書き出し Export EPS EPS を書き出し Export Keyboard Shortcuts… キーボードショートカットを書き出し… + Export Keyboard Shortcuts as Text… キーボードショートカットをテキストで書き出し… + Export Text… テキストを書き出し… Export Option 書き出しオプション Export PDF PDF を書き出し Export PDF Presets PDF プリセットを書き出し diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index c3c0e35b7..66d7e438a 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -831,6 +831,8 @@ lighting Light სინათლე Expand Layers Panel Rows შრეების პანელის სტრიქონების გაშლა Export CSS… CSS-ის ექსპორტირება… Export Keyboard Shortcuts… კლავიატურის მალსახმობების ექსპორტირება… + Export Keyboard Shortcuts as Text… კლავიატურის მალსახმობების ტექსტად ექსპორტირება… + Export Text… ტექსტის ექსპორტი… File Info Dialog ფაილის ინფორმაციის დიალოგი Go to Artboard სამუშაო დაფაზე გადასვლა Graphic Style Options… გრაფიკული სტილის პარამეტრები… diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index 1d8bcd543..2f8a0d7c4 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -1571,6 +1571,8 @@ digits Malayalam 말라얄람어 Export Document 문서 내보내기 Export EPS EPS 내보내기 Export Keyboard Shortcuts… 단축키 내보내기… + Export Keyboard Shortcuts as Text… 단축키를 텍스트로 내보내기… + Export Text… 텍스트 내보내기… Export Option 내보내기 옵션 Export PDF PDF 내보내기 Export PDF Presets PDF 프리셋 내보내기 diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index b1d22ac50..cb4562bb5 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -892,6 +892,8 @@ lighting Light Licht Expand Layers Panel Rows Rijen in het Lagen-paneel uitvouwen Export CSS… CSS exporteren… Export Keyboard Shortcuts… Sneltoetsen exporteren… + Export Keyboard Shortcuts as Text… Sneltoetsen als tekst exporteren… + Export Text… Tekst exporteren… File Info Dialog Dialoogvenster bestandsinfo Go to Artboard Ga naar tekengebied Graphic Style Options… Opties voor grafische stijl… diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 9261962a8..eb5a3aebd 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -894,6 +894,8 @@ lighting Light Światło Expand Layers Panel Rows Rozwiń wiersze panelu Warstwy Export CSS… Eksportuj CSS… Export Keyboard Shortcuts… Eksportuj skróty klawiaturowe… + Export Keyboard Shortcuts as Text… Eksportuj skróty klawiaturowe jako tekst… + Export Text… Eksportuj tekst… File Info Dialog Okno informacji o pliku Go to Artboard Przejdź do obszaru kompozycji First Artboard Pierwszy obszar kompozycji diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index 2a05465fa..d3e4a7c6d 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -981,6 +981,8 @@ Export Document Exportar Documento Export EPS Exportar EPS Export Keyboard Shortcuts… Exportar Atalhos de Teclado… + Export Keyboard Shortcuts as Text… Exportar Atalhos de Teclado como Texto… + Export Text… Exportar Texto… Export Option Opção de Exportação Export PDF Exportar PDF Export PDF Presets Exportar Predefinidos de PDF diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index 45bebb11c..b3fa57493 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -1087,6 +1087,8 @@ font-technology Variable Вариативные Export for Screens Settings Параметры экспорта для экранов Export for Screens… Экспортировать для экранов… Export Keyboard Shortcuts… Экспортировать сочетания клавиш… + Export Keyboard Shortcuts as Text… Экспортировать сочетания клавиш как текст… + Export Text… Экспорт текста… Export Option Параметр экспорта Export PDF Экспортировать PDF Export PDF Presets Экспортировать наборы PDF diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index 03f98e9fa..5e48f7f81 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -1068,6 +1068,8 @@ font-technology Variable Variabla Export for Screens Settings Inställningar för Exportera för skärmar Export for Screens… Exportera för skärmar… Export Keyboard Shortcuts… Exportera kortkommandon… + Export Keyboard Shortcuts as Text… Exportera kortkommandon som text… + Export Text… Exportera text… Export Option Exportalternativ Export PDF Exportera PDF Export PDF Presets Exportera PDF-förinställningar diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index abaee4003..c4c515e56 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -1060,6 +1060,8 @@ font-technology Variable Değişken Export for Screens Settings Ekran Ayarları için Dışa Aktarma Export for Screens… Ekranlar için Dışa Aktarma… Export Keyboard Shortcuts… Klavye Kısayollarını Dışa Aktar… + Export Keyboard Shortcuts as Text… Klavye Kısayollarını Metin Olarak Dışa Aktar… + Export Text… Metni Dışa Aktar… Export Option Dışa Aktarma Seçeneği Export PDF PDF'yi dışa aktar Export PDF Presets PDF Hazır Ayarlarını Dışa Aktar diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 140970789..c6f793067 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -1084,6 +1084,8 @@ font-technology Variable Варіативні Export for Screens Settings Параметри експорту для екранів Export for Screens… Експортувати для екранів… Export Keyboard Shortcuts… Експортувати сполучення клавіш… + Export Keyboard Shortcuts as Text… Експортувати сполучення клавіш як текст… + Export Text… Експорт тексту… Export Option Параметр експорту Export PDF Експортувати PDF Export PDF Presets Експортувати набори PDF diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index f3a323200..29f222849 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -1244,6 +1244,8 @@ font-technology Variable Biến thiên Export for Screens Settings Cài đặt xuất cho màn hình Export for Screens… Xuất cho màn hình… Export Keyboard Shortcuts… Xuất phím tắt… + Export Keyboard Shortcuts as Text… Xuất phím tắt dưới dạng văn bản… + Export Text… Xuất văn bản… Export Library Xuất thư viện Export Library… Xuất thư viện… Export Option Tùy chọn xuất diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index 2ed9a0c3d..4f3d2dcd9 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -1030,6 +1030,8 @@ Export for Screens Settings 导出为屏幕所用格式设置 Export for Screens… 导出为屏幕所用格式… Export Keyboard Shortcuts… 导出键盘快捷键… + Export Keyboard Shortcuts as Text… 将键盘快捷键导出为文本… + Export Text… 导出文本… Export Option 导出选项 Export PDF 导出 PDF Export PDF Presets 导出 PDF 预设 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 63d2e0f24..ba3a80ef0 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -1054,6 +1054,8 @@ font-technology Variable 可變 Export for Screens Settings 匯出為螢幕適用設定 Export for Screens… 匯出為螢幕適用… Export Keyboard Shortcuts… 匯出鍵盤快速鍵… + Export Keyboard Shortcuts as Text… 將鍵盤快速鍵匯出為文字… + Export Text… 匯出文字… Export Option 匯出選項 Export PDF 匯出 PDF Export PDF Presets 匯出 PDF 預設集 diff --git a/crates/ui-egui/src/menus/ui_commands.rs b/crates/ui-egui/src/menus/ui_commands.rs index 41504f5e2..bb2b4d83b 100644 --- a/crates/ui-egui/src/menus/ui_commands.rs +++ b/crates/ui-egui/src/menus/ui_commands.rs @@ -167,6 +167,12 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[ "{name: \"VectorCraft Defaults\" | \"Classic Defaults\"} (names of earlier versions are accepted)", ), ("shortcuts.export", "Export Keyboard Shortcuts…", "", "{path?}"), + ( + "shortcuts.exportText", + "Export Keyboard Shortcuts as Text…", + "", + "{path?} save the current set's tools and menu commands with their shortcuts as a UTF-8 text file (Keyboard Shortcuts › Export Text…): the set's name, then a Tools and a Menu Commands section in the dialog's order, one `nameshortcut` line per row, empty after the tab when it has none; without `path`, pick a file", + ), ("shortcuts.import", "Import Keyboard Shortcuts…", "", "{path? | data?}"), ( "ui.shortcutImportDialog", diff --git a/crates/ui-egui/src/picks.rs b/crates/ui-egui/src/picks.rs index 81253e83c..92314bfba 100644 --- a/crates/ui-egui/src/picks.rs +++ b/crates/ui-egui/src/picks.rs @@ -76,6 +76,7 @@ const DOCUMENT_FREE: &[&str] = &[ "ui.libraryPanel.locate", "shortcuts.import", "shortcuts.export", + "shortcuts.exportText", "ui.shortcutImportDialog", crate::panels::actions::SAVE_SET, crate::panels::actions::LOAD_SET, diff --git a/crates/ui-egui/src/shortcut_editor.rs b/crates/ui-egui/src/shortcut_editor.rs index ee2b6ea83..3836f3f18 100644 --- a/crates/ui-egui/src/shortcut_editor.rs +++ b/crates/ui-egui/src/shortcut_editor.rs @@ -700,6 +700,25 @@ pub fn import_json(v: &Value) -> Result<(String, Overrides), String> { Ok((v.get("set").and_then(Value::as_str).map_or(CUSTOM, set_name).to_string(), out)) } +/// Export Text: the set's name, then the dialog's Tools and Menu Commands lists in its order, one +/// row per line with the name and the shortcuts as the dialog shows them, separated by a tab. A +/// row with no shortcut ends after the tab. +pub fn export_text(set: &str, overrides: &Overrides) -> String { + let km = keymap_with(overrides); + let backtick = backtick_text(); + let shown_set = if PRESETS.contains(&set) || set == CUSTOM { tl!(set) } else { set }; + let mut out = format!("{}\n{} {shown_set}\n", tl!("Keyboard Shortcuts"), tl!("Set:")); + for (tools, heading) in [(true, "Tools"), (false, "Menu Commands")] { + out += &format!("\n{}\n", tl!(heading)); + for e in entries().iter().filter(|e| e.is_tool == tools && listed(km.overrides(), e)) { + let name = if e.is_tool || e.group.is_empty() { tl!(&e.label).to_string() } else { format!("{} › {}", tl!(&e.group), tl!(&e.label)) }; + let sc = dialog_shortcuts_with(&km, &e.key, backtick.as_deref()).unwrap_or_default(); + out += &format!("{name}\t{sc}\n"); + } + } + out +} + // ---------- UI commands ---------- pub fn open(app: &mut VectorcraftApp) { @@ -800,6 +819,31 @@ pub fn run_command(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option { + let path = match p.get("path") { + None | Some(Value::Null) => None, + Some(Value::String(path)) => Some(path.clone()), + Some(_) => { + let key = "path"; + return Some(Err(format!("`{key}` must be a string"))); + } + }; + let text = export_text(&app.ui.shortcut_set, &app.ui.shortcut_overrides); + let pick = + crate::FilePick { filters: vec![crate::FilePick::filter("Text", &["txt"])], ..crate::FilePick::named("VectorCraft Shortcuts.txt") }; + match path.or_else(|| crate::picks::save(app, &pick)) { + Some(path) => match app.services.write.as_mut() { + Some(w) => w(&path, text.as_bytes()).map(|_| json!({"path": path})), + None => Err("no file writer".into()), + }, + // Its save dialog is open: the file is written when it answers. + None if app.file_dialog_open() => Err("cancelled".into()), + None => match app.services.download.as_mut() { + Some(dl) => dl("VectorCraft Shortcuts.txt", text.as_bytes()).map(|()| Value::Null), + None => Ok(json!({"text": text})), + }, + } + } "shortcuts.import" => { let data = if let Some(v) = p.get("data") { Ok(v.clone()) @@ -947,6 +991,9 @@ pub fn show(app: &mut VectorcraftApp, ctx: &egui::Context) { if ui.button(tl!("Export…")).clicked() { d.fields.insert("__export".into(), json!(true)); } + if ui.button(tl!("Export Text…")).clicked() { + d.fields.insert("__exportText".into(), json!(true)); + } if ui.button(tl!("Reset to Defaults")).clicked() { km.to_mut().reset_all(); d.fields.insert("set".into(), json!(PRESETS[0])); @@ -1090,6 +1137,7 @@ pub fn show(app: &mut VectorcraftApp, ctx: &egui::Context) { } let import = d.fields.remove("__import").is_some(); let export = d.fields.remove("__export").is_some(); + let export_text = d.fields.remove("__exportText").is_some(); app.ui.dialog = Some(d); // Their file dialogs, shown off the UI thread, import into or export from the dialog as it is // when they answer. @@ -1099,6 +1147,9 @@ pub fn show(app: &mut VectorcraftApp, ctx: &egui::Context) { if export { crate::picks::button(app, export_from_dialog); } + if export_text { + crate::picks::button(app, export_text_from_dialog); + } if cancel { app.ui.dialog = None; } else if ok && let Err(e) = confirm(app) { @@ -1137,6 +1188,16 @@ fn import_into_dialog(app: &mut VectorcraftApp) { /// The dialog's Export…: its working set, as edited so far. fn export_from_dialog(app: &mut VectorcraftApp) { + export_dialog_set(app, "shortcuts.export"); +} + +/// The dialog's Export Text…: its working set, as edited so far, as a text file. +fn export_text_from_dialog(app: &mut VectorcraftApp) { + export_dialog_set(app, "shortcuts.exportText"); +} + +/// Runs `command` on the dialog's working set and set name. +fn export_dialog_set(app: &mut VectorcraftApp, command: &str) { if !shortcuts_dialog(app) { app.status(crate::picks::dialog_gone()); return; @@ -1144,7 +1205,7 @@ fn export_from_dialog(app: &mut VectorcraftApp) { let (ov, set) = app.ui.dialog.as_ref().map(|d| (dialog_overrides(d), d.str("set"))).unwrap_or_default(); let saved = std::mem::replace(&mut app.ui.shortcut_overrides, ov); let saved_set = std::mem::replace(&mut app.ui.shortcut_set, set); - if let Some(Err(e)) = run_command(app, "shortcuts.export", &json!({})) { + if let Some(Err(e)) = run_command(app, command, &json!({})) { app.status(e); } app.ui.shortcut_overrides = saved; @@ -1453,6 +1514,82 @@ mod tests { assert!(import_json(&json!({})).is_err()); } + /// An Export Text line: the row's name as the dialog shows it, a tab and `shortcut`. + fn text_row(key: &str, shortcut: &str) -> String { + let e = entry(key).unwrap_or_else(|| panic!("no entry {key}")); + if e.is_tool || e.group.is_empty() { format!("{}\t{shortcut}", e.label) } else { format!("{} › {}\t{shortcut}", e.group, e.label) } + } + + /// Export Text writes the set's name, then the Tools and the Menu Commands rows in the dialog's + /// order, each with its shortcut as the dialog shows it. + #[test] + fn export_text_lists_the_dialogs_rows_with_their_shortcuts() { + let mut ov = Overrides::new(); + crate::keymap::set_override(&mut ov, "edit.preferences", "Ctrl+Alt+Shift+9"); + let text = export_text("VectorCraft Defaults", &ov); + let lines: Vec<&str> = text.lines().collect(); + assert_eq!(lines[..4], ["Keyboard Shortcuts", "Set: VectorCraft Defaults", "", "Tools"]); + let menu_at = lines.iter().position(|l| *l == "Menu Commands").expect("a Menu Commands section"); + assert_eq!(lines[menu_at - 1], "", "a blank line before Menu Commands"); + let km = keymap_with(&ov); + let names = |tools: bool| -> Vec { + entries().iter().filter(|e| e.is_tool == tools && listed(km.overrides(), e)).map(|e| text_row(&e.key, "")).collect() + }; + let names_of = |rows: &[&str]| -> Vec { rows.iter().map(|l| format!("{}\t", l.split('\t').next().unwrap_or_default())).collect() }; + assert_eq!(names_of(&lines[4..menu_at - 1]), names(true), "the Tools rows, in the dialog's order"); + assert_eq!(names_of(&lines[menu_at + 1..]), names(false), "the Menu Commands rows, in the dialog's order"); + let pen = menus::pretty_shortcut(default_of("tool:pen").expect("the Pen tool has a default key")); + assert!(lines[4..menu_at].contains(&text_row("tool:pen", &pen).as_str()), "{text}"); + let prefs = menus::pretty_shortcut(&effective_with(&km, "edit.preferences").expect("the shortcut set")); + assert!(prefs.ends_with('9'), "{prefs}"); + assert!(lines[menu_at..].contains(&text_row("edit.preferences", &prefs).as_str()), "{text}"); + assert!(text.ends_with('\n')); + } + + /// A command without a shortcut is listed with nothing after the tab: one without a default key + /// and one whose shortcut the set clears. + #[test] + fn export_text_lists_a_command_without_a_shortcut_with_an_empty_shortcut() { + let mut ov = Overrides::new(); + crate::keymap::set_override(&mut ov, "edit.preferences", ""); + assert!(default_of("align.horizontalLeft").is_none()); + let text = export_text(CUSTOM, &ov); + let lines: Vec<&str> = text.lines().collect(); + assert_eq!(lines[1], "Set: Custom"); + for key in ["align.horizontalLeft", "edit.preferences"] { + assert!(lines.contains(&text_row(key, "").as_str()), "{key}: {text}"); + } + } + + /// `shortcuts.exportText` writes the set's name as the set was named, and a path that can't be + /// written or isn't a string gives an error and writes nothing. + #[test] + fn export_text_command_writes_the_sets_name_and_rejects_a_bad_path() { + type Files = std::rc::Rc>>; + let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default()); + let written: Files = Default::default(); + let w = written.clone(); + app.services.write = Some(Box::new(move |path: &str, b: &[u8]| { + if path.starts_with("/missing/") { + return Err(format!("can't write {path}")); + } + w.borrow_mut().push((path.to_string(), String::from_utf8(b.to_vec()).map_err(|e| e.to_string())?)); + Ok(()) + })); + app.run("shortcuts.import", json!({"data": export_json("Studio", &Overrides::new())})).unwrap(); + assert_eq!(app.run("shortcuts.exportText", json!({"path": "/sets/Studio.txt"})), Ok(json!({"path": "/sets/Studio.txt"}))); + assert_eq!( + app.run("shortcuts.exportText", json!({"path": "/missing/dir/Studio.txt"})), + Err("can't write /missing/dir/Studio.txt".to_string()) + ); + assert_eq!(app.run("shortcuts.exportText", json!({"path": 5})), Err("`path` must be a string".to_string())); + let files = written.borrow(); + let [(path, text)] = files.as_slice() else { panic!("{files:?}") }; + assert_eq!(path, "/sets/Studio.txt"); + assert!(text.starts_with("Keyboard Shortcuts\nSet: Studio\n\nTools\n"), "{text}"); + assert_eq!(*text, export_text("Studio", &Overrides::new())); + } + /// The dialog's Import… leaves an action its function key when the imported set gives the key /// to a command, through both Cancel and OK. #[test] diff --git a/crates/ui-egui/src/tests/picks.rs b/crates/ui-egui/src/tests/picks.rs index 65e7f5762..326b2b2c0 100644 --- a/crates/ui-egui/src/tests/picks.rs +++ b/crates/ui-egui/src/tests/picks.rs @@ -405,6 +405,38 @@ fn keyboard_shortcuts_import_and_export_ask_off_the_ui_thread() { assert_eq!(*written.borrow(), ["/keys/out.json", "/keys/command.json"]); } +/// The Keyboard Shortcuts dialog's Export Text… asks off the UI thread for a `.txt` file and writes +/// the dialog's working set, as edited so far, as text where the dialog answers. +#[test] +fn keyboard_shortcuts_export_text_writes_the_working_set_as_text() { + let (mut app, shown, _) = app(); + let texts: Rc>> = Default::default(); + let t = texts.clone(); + app.services.write = Some(Box::new(move |path: &str, b: &[u8]| { + t.borrow_mut().push((path.to_string(), String::from_utf8_lossy(b).into_owned())); + Ok(()) + })); + let ov = crate::keymap::overrides_from(&[("view.grid", "Ctrl+Alt+G")]); + app.run("edit.keyboardShortcuts", json!({})).unwrap(); + let d = app.ui.dialog.as_mut().expect("Keyboard Shortcuts"); + d.fields.insert("overrides".into(), crate::keymap::stored::to_value(&ov)); + d.fields.insert("set".into(), json!("Custom")); + click(&mut app, "Export Text…", dialogs); + match shown.requests().as_slice() { + [PickRequest::Save(pick)] => assert_eq!(pick.filters, FilePick::filters([("Text", &["txt"][..])])), + other => panic!("{other:?}"), + } + shown.answer(&["/keys/list.txt"]); + poll(&mut app); + let texts = texts.borrow(); + let [(path, text)] = texts.as_slice() else { panic!("{texts:?}") }; + assert_eq!(path, "/keys/list.txt"); + assert_eq!(*text, crate::shortcut_editor::export_text("Custom", &ov)); + assert!(text.contains(&format!("\t{}\n", crate::menus::pretty_shortcut("Ctrl+Alt+G"))), "{text}"); + assert!(app.ui.shortcut_overrides.is_empty(), "the working set is kept only on OK"); + assert_eq!(app.ui.dialog.as_ref().map(|d| d.kind.as_str()), Some("shortcuts"), "the dialog stays open"); +} + /// Keyboard Shortcuts' Import… opens a shortcut set another app saved as text (`.kys`) in Import /// Shortcut Set over the dialog when its file dialog answers, and the dialog's working set stays in /// Keyboard Shortcuts, which Cancel shows again. diff --git a/crates/ui-egui/src/tests/saveext.rs b/crates/ui-egui/src/tests/saveext.rs index 7a2fa718b..497ac0c74 100644 --- a/crates/ui-egui/src/tests/saveext.rs +++ b/crates/ui-egui/src/tests/saveext.rs @@ -88,6 +88,8 @@ fn save_panels_show_the_file_type_they_write() { assert_eq!(last_shown(&shown), FilePick::filters([("PostScript", &["ps"][..])]), "Print to a PostScript file"); app.run("shortcuts.export", json!({})).unwrap(); assert_eq!(last_shown(&shown), FilePick::filters([("Shortcut sets", &["json"][..])]), "Keyboard Shortcuts › Export…"); + app.run("shortcuts.exportText", json!({})).unwrap(); + assert_eq!(last_shown(&shown), FilePick::filters([("Text", &["txt"][..])]), "Keyboard Shortcuts › Export Text…"); let presets = [ ("pdf.preset.save", json!({"name": "Mine"}), "pdf.preset.export", "PDF presets", pdfcmds::PRESET_EXTS), ("print.presets.save", json!({"name": "Mine"}), "print.presets.export", "Print presets", printpresets::PRESET_EXTS), diff --git a/docs/mcp.md b/docs/mcp.md index c15e2f6ff..bd36412db 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -3401,6 +3401,20 @@ They list such a command while the user has changed its shortcut (`overridden: t shortcut for that command runs what a click on the item runs: Object › Path › Clean Up…, Print Ink Fixes… and Edit › Find and Replace… open their dialogs. +`shortcuts.exportText {path?}` saves the current set's shortcut list as a UTF-8 text file, as Edit › Keyboard +Shortcuts › Export Text… does with the dialog's set as edited so far. The file starts with a "Keyboard Shortcuts" line +and a "Set:" line with the set's name, followed by a Tools section and a Menu Commands section. Each section lists the +rows Edit › Keyboard Shortcuts lists, in its order and with names as it shows them: one line per row, with the name, a +tab and the shortcuts as the dialog shows them on the current platform. A row with no shortcut ends after the tab. +Without `path` a save dialog asks for the file. A `path` that is not a string, or a file that cannot be written, +returns an error, and nothing is written. The layout is VectorCraft's own. The reference app's [Export keyboard +shortcuts list as a text document](https://helpx.adobe.com/illustrator/desktop/get-started/preferences-and-settings/export-keyboard-shortcuts-list.html) +page describes saving the list from the Keyboard Shortcuts dialog as a text file, and does not describe its layout. + +```json +{"name":"run_command","arguments":{"command":"shortcuts.exportText","params":{"path":"/Users/me/Shortcuts.txt"}}} +``` + The desktop app adds its active artboard, the one the Artboards panel highlights, to the `object.align`, `object.distribute`, `object.distributeSpacing` and `select.allOnArtboard` calls it runs without `artboard`, as it does for `edit.pasteInPlace`, `edit.pasteInFront` and `edit.pasteInBack`, and to each `command.batch`. A batch passes its diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 2e6431761..0ff5c0e3d 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -765,6 +765,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - 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.*`). + - Edit › Keyboard Shortcuts › Export Text… saves the set shown in the dialog as a UTF-8 text file, as the reference app's [Export keyboard shortcuts list as a text document](https://helpx.adobe.com/illustrator/desktop/get-started/preferences-and-settings/export-keyboard-shortcuts-list.html) page describes (`shortcuts.exportText {path?}`). The layout is VectorCraft's own: the set's name, then the Tools and Menu Commands rows in the dialog's order, each with its name, a tab and its shortcuts as the dialog shows them, and nothing after the tab for a row with no shortcut. Tests `shortcut_editor::tests::export_text_*` and `tests::picks::keyboard_shortcuts_export_text_writes_the_working_set_as_text`. - 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. - Edit › Keyboard Shortcuts lists items of the menu bar and panel menus, as the reference app's [Customize keyboard shortcuts](https://web.archive.org/web/20221205131209id_/https://helpx.adobe.com/illustrator/using/customizing-keyboard-shortcuts.html) page describes for its Menu Commands. A menu command with no default shortcut that needs a value a key press does not give, such as File › Document Setup › Units (`document.setUnits {units}`), is listed only while the user has changed its shortcut. A command stays listed when a menu bar item runs it without a value or when it opens a dialog run without one, as the Variables panel's commands do. - A shortcut on the key left of 1 runs whatever character that key types, and Edit › Keyboard Shortcuts, the menus in the window, the command palette and tool tips show it as that character once the key has been pressed: `"` on a Turkish Q layout, `~` on a US layout as before. Shortcuts saved as `~` keep working (#4559).