From 642df312b35331c0570decbdf81f80e633ece9a0 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sun, 1 Nov 2026 14:21:39 -0800 Subject: [PATCH 1/4] =?UTF-8?q?Actions:=20the=20panel=20menu's=20Action=20?= =?UTF-8?q?Options=E2=80=A6=20renames=20the=20selected=20action?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Action Options… opens the New Set and Set Options dialog, headed Action Options, with the selected action's name; OK renames the action and keeps its steps. The new command ui.action.rename {set, action, name} does the rename and refuses an index past the last set or action or an empty name. Part of #1629. --- .../ui-egui/src/dialogs/action_set_options.rs | 49 +++++++++--- crates/ui-egui/src/i18n/bg.tsv | 3 + crates/ui-egui/src/i18n/bs.tsv | 3 + crates/ui-egui/src/i18n/ca.tsv | 3 + crates/ui-egui/src/i18n/cs.tsv | 3 + crates/ui-egui/src/i18n/da.tsv | 3 + crates/ui-egui/src/i18n/de.tsv | 3 + crates/ui-egui/src/i18n/el.tsv | 3 + crates/ui-egui/src/i18n/es.tsv | 3 + crates/ui-egui/src/i18n/fi.tsv | 3 + crates/ui-egui/src/i18n/fr.tsv | 3 + crates/ui-egui/src/i18n/hu.tsv | 3 + crates/ui-egui/src/i18n/id.tsv | 3 + crates/ui-egui/src/i18n/it.tsv | 3 + crates/ui-egui/src/i18n/ja.tsv | 3 + crates/ui-egui/src/i18n/ka.tsv | 3 + crates/ui-egui/src/i18n/ko.tsv | 3 + crates/ui-egui/src/i18n/nl.tsv | 3 + crates/ui-egui/src/i18n/pl.tsv | 3 + crates/ui-egui/src/i18n/pt-br.tsv | 3 + crates/ui-egui/src/i18n/ru.tsv | 3 + crates/ui-egui/src/i18n/sv.tsv | 3 + crates/ui-egui/src/i18n/tr.tsv | 3 + crates/ui-egui/src/i18n/uk.tsv | 3 + crates/ui-egui/src/i18n/vi.tsv | 3 + crates/ui-egui/src/i18n/zh-hans.tsv | 3 + crates/ui-egui/src/i18n/zh-hant.tsv | 3 + crates/ui-egui/src/menus/run.rs | 1 + crates/ui-egui/src/menus/ui_commands.rs | 6 ++ crates/ui-egui/src/panels/actions.rs | 78 +++++++++++++++++++ docs/mcp.md | 4 + docs/target-app-parity.md | 1 + docs/ui-parity.md | 1 + 33 files changed, 209 insertions(+), 9 deletions(-) diff --git a/crates/ui-egui/src/dialogs/action_set_options.rs b/crates/ui-egui/src/dialogs/action_set_options.rs index 9e9cf8b3c..ae10b18b4 100644 --- a/crates/ui-egui/src/dialogs/action_set_options.rs +++ b/crates/ui-egui/src/dialogs/action_set_options.rs @@ -1,8 +1,9 @@ -//! Actions panel › Create New Set (and ≡ › New Set…) and ≡ › Set Options…: the name of a new action -//! set, or of the current one. +//! Actions panel › Create New Set (and ≡ › New Set…), ≡ › Set Options… and ≡ › Action Options…: +//! the name of a new action set, of the current one, or of the selected action. //! -//! Fields: `set` (the index of the set renamed; none for a new set) and `name`. OK adds the set and -//! makes it the current one, or renames the set. +//! Fields: `set` (the index of the set renamed, or of the action's set; none for a new set), +//! `action` (the index of the action renamed, in `set`; none for a set) and `name`. OK adds the set +//! and makes it the current one, renames the set, or renames the action (`ui.action.rename`). use serde_json::{Value, json}; @@ -10,14 +11,20 @@ use super::DialogSpec; use crate::state::Dialog; use crate::{VectorcraftApp, widgets}; -/// The dialog kind of New Set and Set Options. +/// The dialog kind of New Set, Set Options and Action Options. pub const KIND: &str = "actionSetOptions"; pub(super) const SPEC: DialogSpec = DialogSpec { heading, body, confirm, ok_enabled: Some(|_, d| !d.str("name").trim().is_empty()), min_width: 300.0, ..DialogSpec::FORM }; fn heading(d: &Dialog) -> String { - if d.fields.contains_key("set") { tl!("Set Options").into() } else { tl!("New Set").into() } + if d.fields.contains_key("action") { + tl!("Action Options").into() + } else if d.fields.contains_key("set") { + tl!("Set Options").into() + } else { + tl!("New Set").into() + } } /// Open New Set, named "Set N" with the first number no set uses. @@ -34,6 +41,13 @@ pub fn open(app: &mut VectorcraftApp, set: usize) { } } +/// Open Action Options for the action at `action` in set `set`, if there is one. +pub fn open_action(app: &mut VectorcraftApp, set: usize, action: usize) { + if let Some(a) = app.ui.action_sets.get(set).and_then(|s| s.actions.get(action)) { + app.ui.dialog = Some(Dialog::new(KIND, json!({"set": set, "action": action, "name": a.name}))); + } +} + fn body(_: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> bool { widgets::label_row(ui, "Name:", 50.0, |ui| { super::form::text(ui, d, "name", 200.0); @@ -47,13 +61,16 @@ fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result { if name.is_empty() { return Ok(Value::Null); } - match d.fields.get("set").and_then(Value::as_u64) { - Some(set) => { + match (d.fields.get("set").and_then(Value::as_u64), d.fields.get("action").and_then(Value::as_u64)) { + (Some(set), Some(action)) => { + app.run(crate::panels::actions::RENAME_ACTION, json!({"set": set, "action": action, "name": name}))?; + } + (Some(set), None) => { if let Some(s) = app.ui.action_sets.get_mut(set as usize) { s.name = name; } } - None => { + (None, _) => { app.ui.action_sets.push(crate::panels::actions::ActionSet::new(name, vec![])); app.ui.action_set = Some(app.ui.action_sets.len() - 1); } @@ -61,3 +78,17 @@ fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result { app.ui.dialog = None; Ok(Value::Null) } + +#[cfg(test)] +mod tests { + use super::*; + + /// The heading names the dialog by what it holds: an action, a set, or neither. + #[test] + fn the_heading_names_action_options_set_options_or_new_set() { + let heading_of = |fields: Value| heading(&Dialog::new(KIND, fields)); + assert_eq!(heading_of(json!({"set": 1, "action": 0, "name": "Nudge"})), "Action Options"); + assert_eq!(heading_of(json!({"set": 1, "name": "Mine"})), "Set Options"); + assert_eq!(heading_of(json!({"name": "Set 1"})), "New Set"); + } +} diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 4d57fb852..f6389ea22 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -5561,6 +5561,9 @@ axis Y Axis Y ос New Set Нов набор Set Options… Опции на набора… Set Options Опции на набора + Action Options… Опции на действието… + Action Options Опции на действието + Rename Action Преименуване на действие Create New Set Създаване на нов набор Stop Global Edit Край на глобалното редактиране Global Edit Options Опции за глобално редактиране diff --git a/crates/ui-egui/src/i18n/bs.tsv b/crates/ui-egui/src/i18n/bs.tsv index 8cbdcbcec..2d0718693 100644 --- a/crates/ui-egui/src/i18n/bs.tsv +++ b/crates/ui-egui/src/i18n/bs.tsv @@ -5584,6 +5584,9 @@ axis Y Axis Y osa New Set Novi skup Set Options… Opcije skupa… Set Options Opcije skupa + Action Options… Opcije akcije… + Action Options Opcije akcije + Rename Action Preimenuj akciju Create New Set Napravi novi skup Stop Global Edit Zaustavi globalno uređivanje Global Edit Options Opcije globalnog uređivanja diff --git a/crates/ui-egui/src/i18n/ca.tsv b/crates/ui-egui/src/i18n/ca.tsv index 61fcad348..ea7002ef5 100644 --- a/crates/ui-egui/src/i18n/ca.tsv +++ b/crates/ui-egui/src/i18n/ca.tsv @@ -3744,6 +3744,9 @@ digits Malayalam Malaiàlam Set Keyboard Shortcut Defineix la drecera de teclat Set Mesh Point Color Defineix el color del punt de malla Set Options Defineix les opcions + Action Options… Opcions de l’acció… + Action Options Opcions de l’acció + Rename Action Canvia el nom de l’acció Set Options… Defineix les opcions… Set Preference Defineix la preferència Set Slice Rectangle Defineix el rectangle del tall diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 57468f490..45f2cb1ef 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -5673,6 +5673,9 @@ axis Y Axis osa Y New Set Nová sada Set Options… Možnosti sady… Set Options Možnosti sady + Action Options… Možnosti akce… + Action Options Možnosti akce + Rename Action Přejmenovat akci Create New Set Vytvořit novou sadu Stop Global Edit Ukončit globální úpravy Global Edit Options Možnosti globálních úprav diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index 75db7463b..0ada885a9 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -5577,6 +5577,9 @@ axis Y Axis Y-akse New Set Nyt sæt Set Options… Indstillinger for sæt… Set Options Indstillinger for sæt + Action Options… Indstillinger for handling… + Action Options Indstillinger for handling + Rename Action Omdøb handling Create New Set Opret nyt sæt Stop Global Edit Stop global redigering Global Edit Options Indstillinger for global redigering diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 42acad4c6..c6a8d9136 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -5255,6 +5255,9 @@ axis Y Axis Y-Achse New Set Neuer Satz Set Options… Satzoptionen… Set Options Satzoptionen + Action Options… Aktionsoptionen… + Action Options Aktionsoptionen + Rename Action Aktion umbenennen Create New Set Neuen Satz erstellen Stop Global Edit Globale Bearbeitung beenden Global Edit Options Optionen für globale Bearbeitung diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index d6ff8a732..c6245d792 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -5624,6 +5624,9 @@ axis Y Axis Άξονας Y New Set Νέο σύνολο Set Options… Επιλογές συνόλου… Set Options Επιλογές συνόλου + Action Options… Επιλογές ενέργειας… + Action Options Επιλογές ενέργειας + Rename Action Μετονομασία ενέργειας Create New Set Δημιουργία νέου συνόλου Stop Global Edit Τερματισμός καθολικής επεξεργασίας Global Edit Options Επιλογές καθολικής επεξεργασίας diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index f614f311d..ed4f728a8 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -5614,6 +5614,9 @@ axis Y Axis Eje Y New Set Nuevo conjunto Set Options… Opciones de conjunto… Set Options Opciones de conjunto + Action Options… Opciones de acción… + Action Options Opciones de acción + Rename Action Cambiar nombre de acción Create New Set Crear nuevo conjunto Stop Global Edit Detener edición global Global Edit Options Opciones de edición global diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index aafa736d8..97a9a1c00 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -5703,6 +5703,9 @@ axis Y Axis Y-akseli New Set Uusi sarja Set Options… Sarjan asetukset… Set Options Sarjan asetukset + Action Options… Toiminnon asetukset… + Action Options Toiminnon asetukset + Rename Action Nimeä toiminto uudelleen Create New Set Luo uusi sarja Stop Global Edit Lopeta yleismuokkaus Global Edit Options Yleismuokkauksen asetukset diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 2942e478c..71492222a 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -5670,6 +5670,9 @@ axis Y Axis Axe Y New Set Nouveau jeu Set Options… Options du jeu… Set Options Options du jeu + Action Options… Options de script… + Action Options Options de script + Rename Action Renommer le script Create New Set Créer un jeu Stop Global Edit Arrêter la modification globale Global Edit Options Options de modification globale diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 0e4098876..c9dc2ccdd 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -5658,6 +5658,9 @@ axis Y Axis Y tengely New Set Új készlet Set Options… Készlet beállításai… Set Options Készlet beállításai + Action Options… Művelet beállításai… + Action Options Művelet beállításai + Rename Action Művelet átnevezése Create New Set Új készlet létrehozása Stop Global Edit Globális szerkesztés befejezése Global Edit Options Globális szerkesztés beállításai diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index e8730c9b4..f61266994 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -5577,6 +5577,9 @@ axis Y Axis Sumbu Y New Set Set baru Set Options… Opsi set… Set Options Opsi set + Action Options… Opsi tindakan… + Action Options Opsi tindakan + Rename Action Ganti nama tindakan Create New Set Buat set baru Stop Global Edit Hentikan edit global Global Edit Options Opsi edit global diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 7cd3366b2..47802ca7f 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -5289,6 +5289,9 @@ axis Y Axis Asse Y New Set Nuovo set Set Options… Opzioni set… Set Options Opzioni set + Action Options… Opzioni azione… + Action Options Opzioni azione + Rename Action Rinomina azione Create New Set Crea nuovo set Stop Global Edit Interrompi modifica globale Global Edit Options Opzioni modifica globale diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 4fa0b1bbe..e685841c9 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -5356,6 +5356,9 @@ axis Y Axis Y 軸 New Set 新規セット Set Options… セットオプション… Set Options セットオプション + Action Options… アクションオプション… + Action Options アクションオプション + Rename Action アクション名を変更 Create New Set 新規セットを作成 Stop Global Edit グローバル編集を終了 Global Edit Options グローバル編集オプション diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index 04f41065c..eafdc314e 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -5587,6 +5587,9 @@ axis Y Axis Y ღერძი New Set ახალი ნაკრები Set Options… ნაკრების პარამეტრები… Set Options ნაკრების პარამეტრები + Action Options… მოქმედების პარამეტრები… + Action Options მოქმედების პარამეტრები + Rename Action მოქმედების სახელის შეცვლა Create New Set ახალი ნაკრების შექმნა Stop Global Edit გლობალური რედაქტირების დასრულება Global Edit Options გლობალური რედაქტირების პარამეტრები diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index e73f4310e..3482f3d37 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -5252,6 +5252,9 @@ axis Y Axis Y축 New Set 새 세트 Set Options… 세트 옵션… Set Options 세트 옵션 + Action Options… 액션 옵션… + Action Options 액션 옵션 + Rename Action 액션 이름 바꾸기 Create New Set 새 세트 만들기 Stop Global Edit 전체 편집 중지 Global Edit Options 전체 편집 옵션 diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index cde6c4cf3..8f8b538df 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -5621,6 +5621,9 @@ axis Y Axis Y-as New Set Nieuwe set Set Options… Setopties… Set Options Setopties + Action Options… Actieopties… + Action Options Actieopties + Rename Action Actie hernoemen Create New Set Nieuwe set maken Stop Global Edit Globale bewerking stoppen Global Edit Options Opties voor globale bewerking diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 6d561afc9..d609be067 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -5526,6 +5526,9 @@ axis Y Axis Oś Y New Set Nowy zestaw Set Options… Opcje zestawu… Set Options Opcje zestawu + Action Options… Opcje operacji… + Action Options Opcje operacji + Rename Action Zmień nazwę operacji Create New Set Utwórz nowy zestaw Stop Global Edit Zakończ edycję globalną Global Edit Options Opcje edycji globalnej diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index 31105aedd..658823a2a 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -5588,6 +5588,9 @@ axis Y Axis Eixo Y New Set Novo Conjunto Set Options… Opções do Conjunto… Set Options Opções do Conjunto + Action Options… Opções da Ação… + Action Options Opções da Ação + Rename Action Renomear Ação Create New Set Criar Novo Conjunto Stop Global Edit Parar Edição Global Global Edit Options Opções de Edição Global diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index 8a1478e1e..bb35a427e 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -5284,6 +5284,9 @@ axis Y Axis Ось Y New Set Новый набор Set Options… Параметры набора… Set Options Параметры набора + Action Options… Параметры действия… + Action Options Параметры действия + Rename Action Переименовать действие Create New Set Создать новый набор Stop Global Edit Завершить глобальное редактирование Global Edit Options Параметры глобального редактирования diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index 587a56644..0e2091a05 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -5696,6 +5696,9 @@ axis Y Axis Y-axel New Set Ny uppsättning Set Options… Alternativ för uppsättning… Set Options Alternativ för uppsättning + Action Options… Alternativ för åtgärd… + Action Options Alternativ för åtgärd + Rename Action Byt namn på åtgärd Create New Set Skapa ny uppsättning Stop Global Edit Avsluta global redigering Global Edit Options Alternativ för global redigering diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index b4d154879..b21829143 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -5630,6 +5630,9 @@ axis Y Axis Y ekseni New Set Yeni Set Set Options… Set Seçenekleri… Set Options Set Seçenekleri + Action Options… Eylem Seçenekleri… + Action Options Eylem Seçenekleri + Rename Action Eylemi Yeniden Adlandır Create New Set Yeni Set Oluştur Stop Global Edit Genel Düzenlemeyi Durdur Global Edit Options Genel Düzenleme Seçenekleri diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index fdbb550b9..437cef11e 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -5269,6 +5269,9 @@ axis Y Axis Вісь Y New Set Новий набір Set Options… Параметри набору… Set Options Параметри набору + Action Options… Параметри дії… + Action Options Параметри дії + Rename Action Перейменувати дію Create New Set Створити новий набір Stop Global Edit Завершити глобальне редагування Global Edit Options Параметри глобального редагування diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index ad847a6ce..c63447073 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -5272,6 +5272,9 @@ axis Y Axis Trục Y New Set Bộ mới Set Options… Tùy chọn bộ… Set Options Tùy chọn bộ + Action Options… Tùy chọn hành động… + Action Options Tùy chọn hành động + Rename Action Đổi tên hành động Create New Set Tạo bộ mới Stop Global Edit Dừng chỉnh sửa toàn cục Global Edit Options Tùy chọn chỉnh sửa toàn cục diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index 19eb3227e..ecc3a3543 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -5513,6 +5513,9 @@ axis Y Axis Y 轴 New Set 新建组 Set Options… 组选项… Set Options 组选项 + Action Options… 动作选项… + Action Options 动作选项 + Rename Action 重命名动作 Create New Set 创建新组 Stop Global Edit 停止全局编辑 Global Edit Options 全局编辑选项 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 9d5cb36c2..9b9cd1b1c 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -5309,6 +5309,9 @@ axis Y Axis Y 軸 New Set 新增組 Set Options… 組選項… Set Options 組選項 + Action Options… 動作選項… + Action Options 動作選項 + Rename Action 重新命名動作 Create New Set 建立新組 Stop Global Edit 停止整體編輯 Global Edit Options 整體編輯選項 diff --git a/crates/ui-egui/src/menus/run.rs b/crates/ui-egui/src/menus/run.rs index 3da268eec..ea446081f 100644 --- a/crates/ui-egui/src/menus/run.rs +++ b/crates/ui-egui/src/menus/run.rs @@ -783,6 +783,7 @@ fn run_ui_part11(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option crate::dialogs::package::open(app), + crate::panels::actions::RENAME_ACTION => crate::panels::actions::rename_action(app, p), "ui.batchDialog" => { let set = p.get("set").and_then(Value::as_u64).unwrap_or(0) as usize; let action = p.get("action").and_then(Value::as_u64).unwrap_or(0) as usize; diff --git a/crates/ui-egui/src/menus/ui_commands.rs b/crates/ui-egui/src/menus/ui_commands.rs index 7c5ec134a..381fa5436 100644 --- a/crates/ui-egui/src/menus/ui_commands.rs +++ b/crates/ui-egui/src/menus/ui_commands.rs @@ -1113,6 +1113,12 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[ "Cmd+Alt+Shift+P", "{} open Package for the saved document (dialog `package`: folder, name, copyLinks, linksFolder, relink, copyFonts, report; a document never saved asks to Save As first); OK saves unsaved changes, runs file.package (the web downloads the zip) and offers file.showPackage", ), + ( + crate::panels::actions::RENAME_ACTION, + "Rename Action", + "", + "{set: index, action: index, name} rename the Actions panel's action at action in set (0 is the top set) to name, with spaces trimmed from both ends; its steps stay. The panel menu's Action Options… (dialog `actionSetOptions` with set, action and name) runs this for the selected action. An index past the last set or action, or an empty name, is an error and renames nothing → {name}", + ), ( "ui.batchDialog", "Batch…", diff --git a/crates/ui-egui/src/panels/actions.rs b/crates/ui-egui/src/panels/actions.rs index b7f7fb77e..61f86a99e 100644 --- a/crates/ui-egui/src/panels/actions.rs +++ b/crates/ui-egui/src/panels/actions.rs @@ -76,6 +76,24 @@ pub fn play(app: &mut VectorcraftApp, set: usize, idx: usize) -> Result Result { + let index = |k: &str| p.get(k).and_then(Value::as_u64).and_then(|i| usize::try_from(i).ok()); + let name = p.get("name").and_then(Value::as_str).map(str::trim).unwrap_or_default(); + let action = index("set").zip(index("action")).and_then(|(s, a)| app.ui.action_sets.get_mut(s)?.actions.get_mut(a)).ok_or("no such action")?; + if name.is_empty() { + return Err("name is empty".into()); + } + action.name = name.to_string(); + Ok(json!({ "name": name })) +} + /// The command that deletes action sets ([`delete_sets`]). pub const DELETE_SETS: &str = "ui.actionSet.delete"; @@ -286,6 +304,11 @@ pub fn menu(app: &mut VectorcraftApp, ui: &mut egui::Ui) { { app.status(e); } + if widgets::menu_item(ui, tl!("Action Options…"), has, false) + && let Some((s, a)) = selected + { + crate::dialogs::action_set_options::open_action(app, s, a); + } } pub fn show(app: &mut VectorcraftApp, ui: &mut egui::Ui) { @@ -532,6 +555,61 @@ mod tests { assert_eq!(names, [("Default Actions", 6), ("Web", 0)], "the clicked set is renamed, the other kept"); } + /// #1629: the panel menu's Action Options… is off with no action selected, and with an action + /// selected it asks for a new name: OK renames that action and keeps its steps, Cancel keeps + /// the name. + #[test] + fn action_options_renames_the_selected_action() { + use super::super::tests_appearance::{click, frame_events, text_rect}; + let mut app = app_with_sets(); + let ctx = egui::Context::default(); + let action_options = |ctx: &egui::Context, app: &mut VectorcraftApp| { + let texts = frame_events(ctx, app, vec![], menu); + let item = texts.iter().find(|(s, _)| s.trim() == "Action Options…").map(|(_, r)| *r).expect("Action Options… in the menu"); + click(ctx, app, item.center(), menu); + }; + action_options(&ctx, &mut app); + assert!(app.ui.dialog.is_none(), "no action selected: Action Options… is off"); + let texts = frame_events(&ctx, &mut app, vec![], show); + click(&ctx, &mut app, text_rect(&texts, "Nudge Again").center(), show); + frame_events(&ctx, &mut app, vec![], show); + action_options(&ctx, &mut app); + let d = app.ui.dialog.as_ref().expect("Action Options opens"); + assert_eq!((d.kind.as_str(), d.str("name")), (crate::dialogs::action_set_options::KIND, "Nudge Again".to_string())); + crate::dialogs::cancel(&mut app); + assert_eq!(app.ui.action_sets[1].actions[1].name, "Nudge Again", "Cancel keeps the name"); + action_options(&ctx, &mut app); + app.ui.dialog.as_mut().unwrap().fields.insert("name".into(), json!(" Nudge Right ")); + crate::dialogs::confirm(&mut app).unwrap(); + assert!(app.ui.dialog.is_none()); + let names = app.ui.action_sets[1].actions.iter().map(|a| a.name.as_str()).collect::>(); + assert_eq!(names, ["Nudge", "Nudge Right"], "the selected action is renamed, trimmed; the other is kept"); + assert_eq!(app.ui.action_sets[1].actions[1].steps, [("object.move".to_string(), json!({"dx": 1, "dy": 0}))], "the steps stay"); + assert_eq!(set_names(&app), ["Default Actions", "Mine", "Other"], "no set is renamed"); + } + + /// `ui.action.rename` renames the action at its set and action indexes and refuses a missing + /// index or a name with nothing but spaces, renaming nothing. + #[test] + fn rename_action_renames_one_action_and_refuses_bad_params() { + let mut app = app_with_sets(); + let names = |app: &VectorcraftApp| app.ui.action_sets[1].actions.iter().map(|a| a.name.clone()).collect::>(); + assert_eq!(app.run(RENAME_ACTION, json!({"set": 1, "action": 0, "name": "Push"})).unwrap(), json!({"name": "Push"})); + assert_eq!(names(&app), ["Push", "Nudge Again"]); + for p in [ + json!({"set": 1, "action": 2, "name": "X"}), + json!({"set": 3, "action": 0, "name": "X"}), + json!({"set": 1, "name": "X"}), + json!({"action": 0, "name": "X"}), + json!({"set": 4_294_967_297u64, "action": 0, "name": "X"}), + ] { + assert_eq!(app.run(RENAME_ACTION, p.clone()).unwrap_err(), "no such action", "{p}"); + } + assert_eq!(app.run(RENAME_ACTION, json!({"set": 1, "action": 0, "name": " "})).unwrap_err(), "name is empty"); + assert_eq!(app.run(RENAME_ACTION, json!({"set": 1, "action": 0})).unwrap_err(), "name is empty"); + assert_eq!(names(&app), ["Push", "Nudge Again"], "a refused rename renames nothing"); + } + /// An app with the default set and the sets Mine (two actions) and Other. fn app_with_sets() -> VectorcraftApp { let mut app = VectorcraftApp::new(Session::new(), Default::default()); diff --git a/docs/mcp.md b/docs/mcp.md index 60292a3ab..430246c89 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -1686,6 +1686,10 @@ already walks Image Trace. Short agent entry points: is one of the format's extensions (`.svg` and `SVG` give `.svg`, `.JPEG` gives `.jpeg`), and otherwise in the format's first extension (`png8` gives `.png`, `template` gives `.vctemplate`). A step without `format` keeps the source file's extension (`.vectorcraft` over data sets). +- Actions: `ui.action.rename {set, action, name}` renames the Actions panel's action at `action` in set `set` (0 is + the top set) to `name` with spaces trimmed from both ends, keeps its steps, and returns `{name}`. An index past the + last set or action, or an empty name, is an error, and then nothing is renamed. The panel menu's Action Options… + runs this for the selected action. - Action sets: `ui.actionSet.delete {sets?: [index], ids?: [id]}` deletes the Actions panel's sets at these indexes (0 is the top set) with their actions, by default the current set, and returns `{deleted}`. An index past the last set is an error, and then no set is deleted. `ids` holds the panel's own set ids, which its question passes: the diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 765b910c4..de1311a17 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -549,6 +549,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - Variables: when no object is bound to a variable that the data set has a value for, for example after Import Data Sets… defines new variables without binding them, choosing the data set in the Variables panel or stepping to it with the panel's arrows shows a status message, and `dataset.select`, `dataset.next` and `dataset.prev` add the message to the reply as `warning`. - Variables: Lock Variables (`variable.lock`, in the Variables panel's bottom bar and menu) stops variables from being defined, deleted or renamed until they are unlocked, while objects can still be bound and unbound and data sets still change and apply; the lock is saved with the document. - Batch…: Override Save and Override Export name each file with the extension of the format the step writes, however the step spells the format (`.svg` and `SVG` give `.svg`, `png8` gives `.png`), in `action.batch`. + - Actions panel: the panel menu's Action Options… renames the selected action and keeps its steps ([Actions panel overview](https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/actions-panel-overview.html): "Opens a dialog to rename or assign function keys/colors to the action"; `ui.action.rename`). The dialog has the name only; the function key and color are not there yet. - Actions panel (record/playback, persisted), generic parameter dialogs for every "…" command. - Actions panel: the current set is kept with the UI preferences, so after a restart a recording goes into the set that was current, not into the first set. With no set current, Begin Recording records into the first set and makes it current. - Actions panel: Delete Selection deletes the selected action set with its actions and keeps the other sets ([Actions panel overview](https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/actions-panel-overview.html): "Removes the selected action, command, or action set from the panel"; `ui.actionSet.delete`). VectorCraft's own rules, which the overview does not cover: deleting a set asks first, Shift-click on a set's name selects a range of sets to delete together, the panel menu's Delete does the same, and Default Actions and the last set can be deleted. Undo does not bring a deleted set back. diff --git a/docs/ui-parity.md b/docs/ui-parity.md index 4e48a437f..6bcd5ac1a 100644 --- a/docs/ui-parity.md +++ b/docs/ui-parity.md @@ -296,6 +296,7 @@ next unit in the order a right-click on a ruler lists them, from Feet back to Po | Live trace result bounds and selection highlights exclude the retained bitmap; source-image views retain it | done | All TraceView modes and rotated result bounds regression-covered; ordinary hidden group behavior retained | | Image Trace panel: the Preview checkbox before Trace chooses whether a setting change traces the selected Image Trace object again at once | done | Per the public documentation, Preview shows the result of the current settings and Trace traces with them (). Preview is on by default and kept with the UI preferences; Reset to Default leaves it. Off, changes wait in the panel until Trace applies them as one undo step, or until Preview is turned on again. Changes stay with the object they were made for while another object is selected, and come back when it is selected again. `imageTrace.preview {on?}`, `ui.set {imageTracePreview}`; `panels/image_trace.rs` headless tests (`with_preview_off_a_slider_change_waits_for_trace`, `turning_preview_on_traces_the_changes_that_waited`, `preview_turned_on_with_nothing_selected_traces_when_the_object_is_selected_again`, `settings_that_wait_stay_with_their_object`, `settings_that_wait_come_back_when_their_object_is_selected_again`, `settings_that_wait_are_dropped_when_their_object_changed_since`, `preview_is_kept_with_the_preferences_and_reset_to_default_leaves_it`) | | Toolbar without group labels by default, one or two columns; All Tools drawer by flyout group with shortcuts, its menu (Reset, Basic, Advanced) and Show section | done | #1751; a preference brings the labels back; `toolbar.show` sets the Fill/Stroke, Coloring, Draw Mode and Screen Mode controls | +| Actions panel: ≡ › Action Options… renames the selected action ([Actions panel overview](https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/actions-panel-overview.html): "Opens a dialog to rename or assign function keys/colors to the action") | partial | `ui.action.rename`; `panels/actions.rs` tests (part of #1629). The dialog has the name only: the function key and color remain | | Actions panel: a clicked action stays selected for Play Current Selection and Delete Selection | partial | #1853 (part of #1629); editing actions and action shortcuts remain | | Actions panel: Create New Set (bottom bar, ≡ › New Set…) names a new set and Set Options… renames the current one; a click on a set's name makes it current and a recording goes into the current set ([helpx: Automation with actions](https://helpx.adobe.com/illustrator/using/automation-actions.html)); the current set is kept with the UI preferences, and with no set current a recording goes into the first set, which becomes current | partial | #3712; the New Action dialog (name, set menu, shortcut, colour) and dragging actions between sets remain | | Actions panel: Delete Selection (bottom bar) deletes the selected set with its actions and keeps the other sets ([Actions panel overview](https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/actions-panel-overview.html): "Removes the selected action, command, or action set from the panel") | partial | `ui.actionSet.delete`; `panels/actions.rs` tests. VectorCraft's own rules, which the overview does not cover: deleting a set asks first (a selected action is still deleted at once), Shift-click on a set's name selects a range of sets, ≡ › Delete does the same as the button, and Default Actions and the last set can be deleted. Undo does not restore a deleted set, and recorded commands cannot be selected or deleted | From 0212c2b00e2640b2039f6592e6467f68d0703246 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sun, 1 Nov 2026 17:30:12 -0800 Subject: [PATCH 2/4] Actions: Action Options and Set Options refuse a field that is not an index The dialog's fields choose what OK does, as they choose its heading. A `set` or `action` field that `ui.dialog.set` changed to null or to an index past the last set or action is now an error: OK renames no set or action, adds no set, and the dialog stays open. Before, an Action Options dialog whose `action` was null renamed the set instead. --- .../ui-egui/src/dialogs/action_set_options.rs | 26 +++++++++---------- crates/ui-egui/src/panels/actions.rs | 23 ++++++++++++++++ 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/crates/ui-egui/src/dialogs/action_set_options.rs b/crates/ui-egui/src/dialogs/action_set_options.rs index ae10b18b4..568ea41c0 100644 --- a/crates/ui-egui/src/dialogs/action_set_options.rs +++ b/crates/ui-egui/src/dialogs/action_set_options.rs @@ -3,7 +3,9 @@ //! //! Fields: `set` (the index of the set renamed, or of the action's set; none for a new set), //! `action` (the index of the action renamed, in `set`; none for a set) and `name`. OK adds the set -//! and makes it the current one, renames the set, or renames the action (`ui.action.rename`). +//! and makes it the current one, renames the set, or renames the action (`ui.action.rename`). A +//! `set` or `action` field that is not the index of a set or action is an error, OK then changes +//! no set or action, and the dialog stays open. use serde_json::{Value, json}; @@ -61,19 +63,15 @@ fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result { if name.is_empty() { return Ok(Value::Null); } - match (d.fields.get("set").and_then(Value::as_u64), d.fields.get("action").and_then(Value::as_u64)) { - (Some(set), Some(action)) => { - app.run(crate::panels::actions::RENAME_ACTION, json!({"set": set, "action": action, "name": name}))?; - } - (Some(set), None) => { - if let Some(s) = app.ui.action_sets.get_mut(set as usize) { - s.name = name; - } - } - (None, _) => { - app.ui.action_sets.push(crate::panels::actions::ActionSet::new(name, vec![])); - app.ui.action_set = Some(app.ui.action_sets.len() - 1); - } + // The fields the dialog holds choose what OK does, as they choose its heading. + if d.fields.contains_key("action") { + app.run(crate::panels::actions::RENAME_ACTION, json!({"set": d.fields.get("set"), "action": d.fields.get("action"), "name": name}))?; + } else if let Some(set) = d.fields.get("set") { + let s = set.as_u64().and_then(|i| usize::try_from(i).ok()).and_then(|i| app.ui.action_sets.get_mut(i)).ok_or("no such action set")?; + s.name = name; + } else { + app.ui.action_sets.push(crate::panels::actions::ActionSet::new(name, vec![])); + app.ui.action_set = Some(app.ui.action_sets.len() - 1); } app.ui.dialog = None; Ok(Value::Null) diff --git a/crates/ui-egui/src/panels/actions.rs b/crates/ui-egui/src/panels/actions.rs index 61f86a99e..109d4d824 100644 --- a/crates/ui-egui/src/panels/actions.rs +++ b/crates/ui-egui/src/panels/actions.rs @@ -610,6 +610,29 @@ mod tests { assert_eq!(names(&app), ["Push", "Nudge Again"], "a refused rename renames nothing"); } + /// OK in Action Options or Set Options whose `set` or `action` field `ui.dialog.set` changed to + /// something that is not an index is an error: it renames no set or action, adds no set, and + /// keeps the dialog open. + #[test] + fn options_with_a_field_that_is_not_an_index_change_nothing() { + use crate::dialogs::action_set_options::KIND; + let mut app = app_with_sets(); + for fields in [ + json!({"set": 1, "action": null, "name": "X"}), + json!({"set": null, "action": 0, "name": "X"}), + json!({"set": 1, "action": 9, "name": "X"}), + json!({"set": null, "name": "X"}), + json!({"set": 9, "name": "X"}), + ] { + app.ui.dialog = Some(crate::state::Dialog::new(KIND, fields.clone())); + assert!(crate::dialogs::confirm(&mut app).is_err(), "{fields}"); + assert!(app.ui.dialog.is_some(), "the dialog stays open: {fields}"); + assert_eq!(set_names(&app), ["Default Actions", "Mine", "Other"], "{fields}"); + let names = app.ui.action_sets[1].actions.iter().map(|a| a.name.as_str()).collect::>(); + assert_eq!(names, ["Nudge", "Nudge Again"], "{fields}"); + } + } + /// An app with the default set and the sets Mine (two actions) and Other. fn app_with_sets() -> VectorcraftApp { let mut app = VectorcraftApp::new(Session::new(), Default::default()); From b2c62c398ed3a188153aa9feccca931a273ec317 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sun, 1 Nov 2026 17:38:53 -0800 Subject: [PATCH 3/4] Actions: the panel menu reads the selected action only in the current set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The panel menu read the stored (set, action) pair without checking that its set is still the current one, as the panel and Delete Selection do. After `ui.actionSet.delete` removed a set above it, the pair pointed at another set's action, and Action Options…, Batch… and Play Current Selection acted on that action. They are now off until an action of the current set is selected. --- crates/ui-egui/src/panels/actions.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/src/panels/actions.rs b/crates/ui-egui/src/panels/actions.rs index 109d4d824..fa8ed65bc 100644 --- a/crates/ui-egui/src/panels/actions.rs +++ b/crates/ui-egui/src/panels/actions.rs @@ -275,7 +275,9 @@ pub fn duplicate(app: &mut VectorcraftApp, p: &Value) -> Result { /// The panel's (≡) menu. pub fn menu(app: &mut VectorcraftApp, ui: &mut egui::Ui) { - let selected: Option<(usize, usize)> = pstate(ui.ctx(), "actions-selected"); + // A selected action counts only while its set is the current one, as the panel shows it: a set + // deleted since leaves the stored pair pointing at another set's action. + let selected = pstate::>(ui.ctx(), "actions-selected").filter(|&(s, _)| app.ui.action_set == Some(s)); let has = selected.and_then(|(s, a)| app.ui.action_sets.get(s).and_then(|set| set.actions.get(a))).is_some(); if widgets::menu_item(ui, tl!("New Set…"), true, false) { crate::dialogs::action_set_options::open_new(app); @@ -633,6 +635,26 @@ mod tests { } } + /// After `ui.actionSet.delete` removes a set above the selected action's set, the panel menu's + /// Action Options… is off: it does not open for the action that moved to the stored position. + #[test] + fn action_options_is_off_after_a_set_above_the_selection_is_deleted() { + use super::super::tests_appearance::{click, frame_events}; + let mut app = app_with_sets(); + let action = |name: &str| Action { name: name.into(), steps: vec![] }; + app.ui.action_sets[2].actions.push(action("Spin")); + app.ui.action_sets.push(ActionSet::new("Last", vec![action("Wrong")])); + let ctx = egui::Context::default(); + app.ui.action_set = Some(2); + set_pstate::>(&ctx, "actions-selected", Some((2, 0))); + app.run(DELETE_SETS, json!({"sets": [1]})).unwrap(); + assert_eq!(app.ui.action_set, Some(1), "Other is still current"); + let texts = frame_events(&ctx, &mut app, vec![], menu); + let item = texts.iter().find(|(s, _)| s.trim() == "Action Options…").map(|(_, r)| *r).expect("Action Options… in the menu"); + click(&ctx, &mut app, item.center(), menu); + assert!(app.ui.dialog.is_none(), "Action Options… does not open for Last's action"); + } + /// An app with the default set and the sets Mine (two actions) and Other. fn app_with_sets() -> VectorcraftApp { let mut app = VectorcraftApp::new(Session::new(), Default::default()); From 9031906abde07eab836fd710fc3f6636d2997f9b Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sun, 1 Nov 2026 19:00:37 -0800 Subject: [PATCH 4/4] Actions: deleting sets moves the selected action with its set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `ui.actionSet.delete` now moves the panel's selected action down with its set, and clears it when its set is deleted. Before, the stored set and action indexes stayed as they were, so a set made later at the deleted set's index, and an action recorded into it, showed as selected, and Action Options… renamed that action. --- crates/ui-egui/src/panels/actions.rs | 38 ++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/crates/ui-egui/src/panels/actions.rs b/crates/ui-egui/src/panels/actions.rs index fa8ed65bc..49a818f14 100644 --- a/crates/ui-egui/src/panels/actions.rs +++ b/crates/ui-egui/src/panels/actions.rs @@ -181,6 +181,14 @@ pub fn delete_sets(app: &mut VectorcraftApp, p: &Value) -> Result d.fields.insert("set".into(), json!(set - sets.iter().filter(|&&s| s < set).count())); } } + // The panel's selected action follows its set down too, and is no longer selected when its set + // is deleted, so a set made later at its old index does not find it selected. + if let Some(ctx) = app.ui_ctx.clone() + && let Some((set, action)) = pstate::>(&ctx, "actions-selected") + { + let moved = (!sets.contains(&set)).then(|| (set - sets.iter().filter(|&&s| s < set).count(), action)); + set_pstate::>(&ctx, "actions-selected", moved); + } for &s in sets.iter().rev() { app.ui.action_sets.remove(s); } @@ -655,6 +663,36 @@ mod tests { assert!(app.ui.dialog.is_none(), "Action Options… does not open for Last's action"); } + /// `ui.actionSet.delete` moves the panel's selected action down with its set, and clears it + /// when its set is deleted: a new set made at that index, and the action recorded into it, are + /// not selected, so the panel menu's Action Options… stays off. + #[test] + fn deleting_sets_moves_or_clears_the_selected_action() { + use super::super::tests_appearance::{click, frame_events}; + let selected = |ctx: &egui::Context| pstate::>(ctx, "actions-selected"); + let mut app = app_with_sets(); + let ctx = egui::Context::default(); + app.ui_ctx = Some(ctx.clone()); + app.ui.action_set = Some(1); + set_pstate::>(&ctx, "actions-selected", Some((1, 1))); + app.run(DELETE_SETS, json!({"sets": [0]})).unwrap(); + assert_eq!(selected(&ctx), Some((0, 1)), "Nudge Again is still selected, at its set's new index"); + app.ui.action_sets.push(ActionSet::new("Last", vec![])); + app.ui.action_set = Some(2); + set_pstate::>(&ctx, "actions-selected", Some((2, 0))); + app.ui.action_sets[2].actions.push(Action { name: "Spin".into(), steps: vec![] }); + app.run(DELETE_SETS, json!({"sets": [2]})).unwrap(); + assert_eq!(selected(&ctx), None, "the deleted set's action is no longer selected"); + crate::dialogs::action_set_options::open_new(&mut app); + crate::dialogs::confirm(&mut app).unwrap(); + assert_eq!(app.ui.action_set, Some(2), "the new set is current, at the deleted set's index"); + app.ui.action_sets[2].actions.push(Action { name: "Recorded".into(), steps: vec![] }); + let texts = frame_events(&ctx, &mut app, vec![], menu); + let item = texts.iter().find(|(s, _)| s.trim() == "Action Options…").map(|(_, r)| *r).expect("Action Options… in the menu"); + click(&ctx, &mut app, item.center(), menu); + assert!(app.ui.dialog.is_none(), "Action Options… does not open for the new set's action"); + } + /// An app with the default set and the sets Mine (two actions) and Other. fn app_with_sets() -> VectorcraftApp { let mut app = VectorcraftApp::new(Session::new(), Default::default());