Actions: Delete Selection deletes the selected action set

Delete Selection in the Actions panel (the bottom bar's trash and the
panel menu's new Delete) now deletes the selected action set with its
actions and keeps the other sets, after asking. A selected action is
still deleted at once. Shift-click on a set's name selects the sets from
the current one to it, and one question deletes them together. Default
Actions and the last set can be deleted; New Set still works on the
empty panel.

ui.actionSet.delete {sets?: [index]} does the deletion: an index past
the last set is an error before anything is deleted, a recording into a
deleted set stops, and a recording into a later set stays in its set.
Undo does not restore a deleted set.
This commit is contained in:
dotCooCoo
2026-11-01 04:55:10 +07:00
committed by bflatastic
parent 7211dc789f
commit 1f5cb914fd
33 changed files with 358 additions and 10 deletions
+3
View File
@@ -743,6 +743,9 @@ font-technology Variable Променливи
No Не
Delete Tab Изтриване на табулация
Delete the graphic style “{name}”? Изтриване на графичния стил „{name}“?
Delete Action Sets Изтриване на набори от действия
Delete the action set “{name}” and its actions? Изтриване на набора от действия „{name}“ и неговите действия?
Delete the selected action sets and their actions? Изтриване на избраните набори от действия и техните действия?
Delete Theme Изтриване на тема
Delete theme Изтриване на тема
Delete these {n} graphic styles? Изтриване на тези {n} графични стила?
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -1544,6 +1544,9 @@ digits Malayalam Malajalam
Save to Swatches Sačuvaj u uzorke boja
Delete Graphic Style (Alt-click: without asking) Izbriši grafički stil (Alt-klik: bez pitanja)
Delete the graphic style “{name}”? Izbrisati grafički stil „{name}”?
Delete Action Sets Izbriši skupove akcija
Delete the action set “{name}” and its actions? Izbrisati skup akcija „{name}” i njegove akcije?
Delete the selected action sets and their actions? Izbrisati odabrane skupove akcija i njihove akcije?
Delete these {n} graphic styles? Izbrisati ove grafičke stilove ({n})?
Delete this {n} graphic style? Izbrisati ovaj grafički stil ({n})?
Graphic Style Libraries Menu Meni biblioteka grafičkih stilova
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -1060,6 +1060,9 @@
No No
Delete Tab Suprimeix la tabulació
Delete the graphic style “{name}”? Voleu suprimir l’estil gràfic “{name}”?
Delete Action Sets Suprimeix els conjunts d’accions
Delete the action set “{name}” and its actions? Voleu suprimir el conjunt d’accions “{name}” i les seves accions?
Delete the selected action sets and their actions? Voleu suprimir els conjunts d’accions seleccionats i les seves accions?
Delete the library “{name}”? Voleu suprimir la biblioteca “{name}”?
Delete Theme Suprimeix el tema
Delete theme Suprimeix el tema
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -1568,6 +1568,9 @@ digits Malayalam Malajálamské
Save to Swatches Uložit do vzorníku
Delete Graphic Style (Alt-click: without asking) Odstranit grafický styl (Alt+kliknutí: bez dotazu)
Delete the graphic style “{name}”? Odstranit grafický styl „{name}“?
Delete Action Sets Odstranit sady akcí
Delete the action set “{name}” and its actions? Odstranit sadu akcí „{name}“ a její akce?
Delete the selected action sets and their actions? Odstranit vybrané sady akcí a jejich akce?
Delete these {n} graphic styles? Odstranit tyto grafické styly ({n})?
Delete this {n} graphic style? Odstranit tento grafický styl ({n})?
Graphic Style Libraries Menu Nabídka knihoven grafických stylů
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -1481,6 +1481,9 @@ digits Malayalam Malayalam
Save to Swatches Gem i farveprøver
Delete Graphic Style (Alt-click: without asking) Slet grafisk typografi (Alt-klik: uden at spørge)
Delete the graphic style “{name}”? Slet den grafiske typografi »{name}«?
Delete Action Sets Slet handlingssæt
Delete the action set “{name}” and its actions? Slet handlingssættet »{name}« og dets handlinger?
Delete the selected action sets and their actions? Slet de markerede handlingssæt og deres handlinger?
Delete these {n} graphic styles? Slet disse {n} grafiske typografier?
Delete this {n} graphic style? Slet denne {n} grafiske typografi?
Graphic Style Libraries Menu Menu med biblioteker af grafiske typografier
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -737,6 +737,9 @@ font-technology Variable Variabel
No Nein
Delete Tab Tabulator löschen
Delete the graphic style “{name}”? Grafikstil „{name}“ löschen?
Delete Action Sets Aktionssätze löschen
Delete the action set “{name}” and its actions? Aktionssatz „{name}“ und seine Aktionen löschen?
Delete the selected action sets and their actions? Ausgewählte Aktionssätze und ihre Aktionen löschen?
Delete Theme Thema löschen
Delete theme Thema löschen
Delete these {n} graphic styles? Diese {n} Grafikstile löschen?
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -717,6 +717,9 @@ font-technology Variable Μεταβλητές
No Όχι
Delete Tab Διαγραφή στηλοθέτη
Delete the graphic style “{name}”? Να διαγραφεί το στυλ γραφικών «{name}»;
Delete Action Sets Διαγραφή συνόλων ενεργειών
Delete the action set “{name}” and its actions? Να διαγραφεί το σύνολο ενεργειών «{name}» και οι ενέργειές του;
Delete the selected action sets and their actions? Να διαγραφούν τα επιλεγμένα σύνολα ενεργειών και οι ενέργειές τους;
Delete Theme Διαγραφή θέματος
Delete theme Διαγραφή θέματος
Delete these {n} graphic styles? Να διαγραφούν αυτά τα {n} στυλ γραφικών;
Can't render this file because it contains an unexpected character in line 164 and column 5.
+3
View File
@@ -758,6 +758,9 @@ font-technology Variable Variables
No No
Delete Tab Eliminar tabulación
Delete the graphic style “{name}”? ¿Eliminar el estilo gráfico “{name}”?
Delete Action Sets Eliminar conjuntos de acciones
Delete the action set “{name}” and its actions? ¿Eliminar el conjunto de acciones “{name}” y sus acciones?
Delete the selected action sets and their actions? ¿Eliminar los conjuntos de acciones seleccionados y sus acciones?
Delete Theme Eliminar tema
Delete theme Eliminar tema
Delete these {n} graphic styles? ¿Eliminar estos {n} estilos gráficos?
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -1541,6 +1541,9 @@ digits Malayalam Malajalam
Save to Swatches Tallenna värimalleihin
Delete Graphic Style (Alt-click: without asking) Poista grafiikkatyyli (Alt-napsautus: kysymättä)
Delete the graphic style “{name}”? Poistetaanko grafiikkatyyli ”{name}”?
Delete Action Sets Poista toimintosarjat
Delete the action set “{name}” and its actions? Poistetaanko toimintosarja ”{name}” ja sen toiminnot?
Delete the selected action sets and their actions? Poistetaanko valitut toimintosarjat ja niiden toiminnot?
Delete these {n} graphic styles? Poistetaanko nämä {n} grafiikkatyyliä?
Delete this {n} graphic style? Poistetaanko tämä {n} grafiikkatyyli?
Graphic Style Libraries Menu Grafiikkatyylikirjastojen valikko
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -747,6 +747,9 @@ font-technology Variable Variables
Delete Save for Web Preset Supprimer le paramètre prédéfini Enregistrer pour le Web
Delete Selected Item Supprimer l’élément sélectionné
Delete Selection Supprimer la sélection
Delete Action Sets Supprimer les jeux de scripts
Delete the action set “{name}” and its actions? Supprimer le jeu de scripts « {name} » et ses scripts ?
Delete the selected action sets and their actions? Supprimer les jeux de scripts sélectionnés et leurs scripts ?
Delete Slices Supprimer les tranches
Delete Spine Point Supprimer le point de la dorsale
Delete Stop Supprimer l’étape de dégradé
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -982,6 +982,9 @@ font-technology Variable Változó
No Nem
Delete Tab Tabulátor törlése
Delete the graphic style “{name}”? Törlöd ezt a grafikai stílust: „{name}”?
Delete Action Sets Műveletkészletek törlése
Delete the action set “{name}” and its actions? Törlöd ezt a műveletkészletet és a műveleteit: „{name}”?
Delete the selected action sets and their actions? Törlöd a kijelölt műveletkészleteket és a műveleteiket?
Delete the library “{name}”? Törlöd ezt a könyvtárat: „{name}”?
Delete Theme Téma törlése
Delete theme Téma törlése
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -1481,6 +1481,9 @@ digits Malayalam Malayalam
Save to Swatches Simpan ke swatch
Delete Graphic Style (Alt-click: without asking) Hapus gaya grafis (Alt+klik: tanpa konfirmasi)
Delete the graphic style “{name}”? Hapus gaya grafis “{name}”?
Delete Action Sets Hapus set tindakan
Delete the action set “{name}” and its actions? Hapus set tindakan “{name}” beserta tindakannya?
Delete the selected action sets and their actions? Hapus set tindakan yang dipilih beserta tindakannya?
Delete these {n} graphic styles? Hapus {n} gaya grafis ini?
Delete this {n} graphic style? Hapus {n} gaya grafis ini?
Graphic Style Libraries Menu Menu pustaka gaya grafis
1 # Indonesian (id) catalog. Same format as zh-hant.tsv: context <TAB> source <TAB> translation
1481
1482
1483
1484
1485
1486
1487
1488
1489
+3
View File
@@ -758,6 +758,9 @@ font-technology Variable Variabili
No No
Delete Tab Elimina tabulazione
Delete the graphic style “{name}”? Eliminare lo stile grafico “{name}”?
Delete Action Sets Elimina set di azioni
Delete the action set “{name}” and its actions? Eliminare il set di azioni “{name}” e le sue azioni?
Delete the selected action sets and their actions? Eliminare i set di azioni selezionati e le loro azioni?
Delete Theme Elimina tema
Delete theme Elimina tema
Delete these {n} graphic styles? Eliminare questi {n} stili grafici?
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -1319,6 +1319,9 @@ digits Malayalam マラヤーラム数字
Delete Width Point 幅ポイントを削除
Delete Workspace ワークスペースを削除
Delete the graphic style “{name}”? グラフィックスタイル「{name}」を削除しますか?
Delete Action Sets アクションセットを削除
Delete the action set “{name}” and its actions? アクションセット「{name}」とそのアクションを削除しますか?
Delete the selected action sets and their actions? 選択したアクションセットとそのアクションを削除しますか?
Delete theme テーマを削除
Delete this saved preset この保存済みプリセットを削除
Delete {kind} Style {kind}スタイルを削除
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -1398,6 +1398,9 @@ digits Malayalam მალაიალამური
Save to Swatches ფერის ნიმუშებში შენახვა
Delete Graphic Style (Alt-click: without asking) გრაფიკული სტილის წაშლა (Alt-დაწკაპუნებით: დაუკითხავად)
Delete the graphic style “{name}”? წაიშალოს გრაფიკული სტილი „{name}“?
Delete Action Sets მოქმედებების ნაკრებების წაშლა
Delete the action set “{name}” and its actions? წაიშალოს მოქმედებების ნაკრები „{name}“ და მისი მოქმედებები?
Delete the selected action sets and their actions? წაიშალოს მონიშნული მოქმედებების ნაკრებები და მათი მოქმედებები?
Delete these {n} graphic styles? წაიშალოს ეს გრაფიკული სტილები ({n})?
Delete this {n} graphic style? წაიშალოს ეს გრაფიკული სტილი ({n})?
Graphic Style Libraries Menu გრაფიკული სტილების ბიბლიოთეკების მენიუ
Can't render this file because it is too large.
+3
View File
@@ -1309,6 +1309,9 @@ digits Malayalam 말라얄람어
Delete Width Point 너비 포인트 삭제
Delete Workspace 작업 영역 삭제
Delete the graphic style “{name}”? 그래픽 스타일 “{name}”을(를) 삭제하시겠습니까?
Delete Action Sets 액션 세트 삭제
Delete the action set “{name}” and its actions? 액션 세트 “{name}”과(와) 해당 액션을 삭제하시겠습니까?
Delete the selected action sets and their actions? 선택한 액션 세트와 해당 액션을 삭제하시겠습니까?
Delete theme 테마 삭제
Delete this saved preset 저장된 이 프리셋 삭제
Delete {kind} Style {kind} 스타일 삭제
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -1495,6 +1495,9 @@ digits Malayalam Malayalam
Save to Swatches Opslaan in stalen
Delete Graphic Style (Alt-click: without asking) Grafische stijl verwijderen (Alt-klik: zonder vragen)
Delete the graphic style “{name}”? De grafische stijl „{name}” verwijderen?
Delete Action Sets Actiesets verwijderen
Delete the action set “{name}” and its actions? De actieset „{name}” en de acties erin verwijderen?
Delete the selected action sets and their actions? De geselecteerde actiesets en de acties erin verwijderen?
Delete these {n} graphic styles? Deze {n} grafische stijlen verwijderen?
Delete this {n} graphic style? Deze {n} grafische stijl verwijderen?
Graphic Style Libraries Menu Menu grafische stijlbibliotheken
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -1481,6 +1481,9 @@ digits Malayalam Malajalam
Save to Swatches Zapisz w próbkach
Delete Graphic Style (Alt-click: without asking) Usuń styl graficzny (kliknięcie z Alt: bez pytania)
Delete the graphic style “{name}”? Usunąć styl graficzny „{name}”?
Delete Action Sets Usuń zestawy operacji
Delete the action set “{name}” and its actions? Usunąć zestaw operacji „{name}” i jego operacje?
Delete the selected action sets and their actions? Usunąć zaznaczone zestawy operacji i ich operacje?
Delete these {n} graphic styles? Usunąć te style graficzne ({n})?
Delete this {n} graphic style? Usunąć ten styl graficzny ({n})?
Graphic Style Libraries Menu Menu bibliotek stylów graficznych
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -675,6 +675,9 @@
Delete Width Point Excluir Ponto de Largura
Delete Workspace Excluir Espaço de Trabalho
Delete the graphic style “{name}”? Excluir o estilo gráfico “{name}”?
Delete Action Sets Excluir Conjuntos de Ações
Delete the action set “{name}” and its actions? Excluir o conjunto de ações “{name}” e suas ações?
Delete the selected action sets and their actions? Excluir os conjuntos de ações selecionados e suas ações?
Delete theme Excluir tema
Delete this saved preset Excluir este predefinido salvo
Delete {kind} Style Excluir Estilo de {kind}
Can't render this file because it contains an unexpected character in line 6 and column 47.
+3
View File
@@ -756,6 +756,9 @@ font-technology Variable Вариативные
No Нет
Delete Tab Удалить табуляцию
Delete the graphic style “{name}”? Удалить графический стиль «{name}»?
Delete Action Sets Удалить наборы действий
Delete the action set “{name}” and its actions? Удалить набор действий «{name}» и его действия?
Delete the selected action sets and their actions? Удалить выбранные наборы действий и их действия?
Delete Theme Удалить тему
Delete theme Удалить тему
Delete these {n} graphic styles? Удалить эти графические стили ({n})?
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -745,6 +745,9 @@ font-technology Variable Variabla
No Nej
Delete Tab Radera tabb
Delete the graphic style “{name}”? Radera grafikstilen ”{name}”?
Delete Action Sets Radera åtgärdsuppsättningar
Delete the action set “{name}” and its actions? Radera åtgärdsuppsättningen ”{name}” och dess åtgärder?
Delete the selected action sets and their actions? Radera de markerade åtgärdsuppsättningarna och deras åtgärder?
Delete Theme Radera tema
Delete theme Radera tema
Delete these {n} graphic styles? Radera dessa {n} grafikstilar?
Can't render this file because it contains an unexpected character in line 5 and column 25.
+3
View File
@@ -737,6 +737,9 @@ font-technology Variable Değişken
No Hayır
Delete Tab Sekmeyi Sil
Delete the graphic style “{name}”? “{name}” grafik stili silinsin mi?
Delete Action Sets Eylem Setlerini Sil
Delete the action set “{name}” and its actions? “{name}” eylem seti ve eylemleri silinsin mi?
Delete the selected action sets and their actions? Seçili eylem setleri ve eylemleri silinsin mi?
Delete Theme Temayı Sil
Delete theme Temayı sil
Delete these {n} graphic styles? Bu {n} grafik stilleri silinsin mi?
Can't render this file because it contains an unexpected character in line 5 and column 25.
+3
View File
@@ -752,6 +752,9 @@ font-technology Variable Варіативні
No Ні
Delete Tab Видалити табуляцію
Delete the graphic style “{name}”? Видалити графічний стиль «{name}»?
Delete Action Sets Видалити набори дій
Delete the action set “{name}” and its actions? Видалити набір дій «{name}» і його дії?
Delete the selected action sets and their actions? Видалити вибрані набори дій і їхні дії?
Delete Theme Видалити тему
Delete theme Видалити тему
Delete these {n} graphic styles? Видалити ці графічні стилі ({n})?
Can't render this file because it contains an unexpected character in line 178 and column 5.
+3
View File
@@ -878,6 +878,9 @@ font-technology Variable Biến thiên
No Không
Delete Tab Xóa tab
Delete the graphic style “{name}”? Xóa kiểu đồ họa “{name}”?
Delete Action Sets Xóa các bộ hành động
Delete the action set “{name}” and its actions? Xóa bộ hành động “{name}” và các hành động của nó?
Delete the selected action sets and their actions? Xóa các bộ hành động đã chọn và các hành động của chúng?
Delete the library “{name}”? Xóa thư viện “{name}”?
Delete Theme Xóa bộ màu
Delete theme Xóa bộ màu
Can't render this file because it contains an unexpected character in line 200 and column 5.
+3
View File
@@ -701,6 +701,9 @@
No 否
Delete Tab 删除制表位
Delete the graphic style “{name}”? 要删除图形样式“{name}”吗?
Delete Action Sets 删除动作组
Delete the action set “{name}” and its actions? 要删除动作组“{name}”及其中的动作吗?
Delete the selected action sets and their actions? 要删除所选动作组及其中的动作吗?
Delete Theme 删除主题
Delete theme 删除主题
Delete these {n} graphic styles? 要删除这 {n} 个图形样式吗?
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -720,6 +720,9 @@ font-technology Variable 可變
No 否
Delete Tab 刪除定位點
Delete the graphic style “{name}”? 要刪除繪圖樣式「{name}」嗎?
Delete Action Sets 刪除動作組
Delete the action set “{name}” and its actions? 要刪除動作組「{name}」及其中的動作嗎?
Delete the selected action sets and their actions? 要刪除所選動作組及其中的動作嗎?
Delete Theme 刪除主題
Delete theme 刪除主題
Delete these {n} graphic styles? 要刪除這 {n} 個繪圖樣式嗎?
Can't render this file because it contains an unexpected character in line 6 and column 47.
+1
View File
@@ -783,6 +783,7 @@ fn run_ui_part11(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option<Result
let action = p.get("action").and_then(Value::as_u64).unwrap_or(0) as usize;
crate::dialogs::action_batch::open(app, set, action)
}
crate::panels::actions::DELETE_SETS => crate::panels::actions::delete_sets(app, p),
"ui.shortcutImportDialog" => crate::dialogs::shortcut_import::command(app, p),
_ => return None,
};
+6
View File
@@ -1119,6 +1119,12 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[
"",
"{set?: index, action?: index} open Batch (dialog `actionBatch`) for the action at set/action (default: the first action of the first set); OK runs action.batch with the dialog's source, destination and overrides",
),
(
crate::panels::actions::DELETE_SETS,
"Delete Action Sets",
"",
"{sets?: [index]} delete the Actions panel's sets at these indexes (0 is the top set), with their actions (default: the current set). The panel's Delete Selection on selected sets (Shift-click adds a range) asks first, and OK runs this. A recording into a deleted set stops and keeps nothing. An index past the last set is an error → {deleted: n}",
),
("file.showPackage", "Show Package", "", "{folder} show a package folder (file.package's folder) in the file manager → {folder}"),
(
"docInfo.save",
+259 -9
View File
@@ -51,6 +51,79 @@ pub fn play(app: &mut VectorcraftApp, set: usize, idx: usize) -> Result<Value, S
app.run("command.batch", json!({"label": a.name, "commands": commands}))
}
/// The command that deletes action sets ([`delete_sets`]).
pub const DELETE_SETS: &str = "ui.actionSet.delete";
/// The sets Delete Selection deletes: the selected sets ([`crate::state::UiState::action_sets_selected`])
/// when the current set is one of them, else the current set alone.
fn selected_sets(app: &VectorcraftApp) -> Vec<usize> {
let len = app.ui.action_sets.len();
let current = app.ui.action_set.filter(|&s| s < len);
let sets: Vec<usize> = app.ui.action_sets_selected.iter().copied().filter(|&s| s < len).collect();
if current.is_some_and(|c| sets.contains(&c)) { sets } else { current.into_iter().collect() }
}
/// Delete Selection, from the bottom bar and the panel menu: deletes the selected action, else asks
/// whether to delete the selected sets ([`DELETE_SETS`]).
fn delete_selection(app: &mut VectorcraftApp, ctx: &egui::Context) {
const SELECTED: &str = "actions-selected";
// A selected action counts only while its set is the current one, as the panel shows it.
if let Some((si, ai)) = pstate::<Option<(usize, usize)>>(ctx, SELECTED).filter(|&(s, _)| app.ui.action_set == Some(s)) {
if let Some(s) = app.ui.action_sets.get_mut(si)
&& ai < s.actions.len()
{
s.actions.remove(ai);
set_pstate::<Option<(usize, usize)>>(ctx, SELECTED, None);
}
return;
}
let sets = selected_sets(app);
let message = match sets[..] {
[] => return,
[s] => crate::i18n::fmt(
tl!("Delete the action set “{name}” and its actions?"),
&[("name", app.ui.action_sets.get(s).map_or("", |s| s.name.as_str()))],
),
_ => tl!("Delete the selected action sets and their actions?").to_string(),
};
crate::dialogs::confirm::ask(app, &message, "", DELETE_SETS, json!({ "sets": sets }));
}
/// `ui.actionSet.delete {sets?: [index]}`: deletes the action sets at these indexes, with their
/// actions (default: the current set). A recording into a deleted set stops, and its steps are
/// dropped; a recording into another set goes on. When the current set is deleted, no set is
/// current afterwards.
pub fn delete_sets(app: &mut VectorcraftApp, p: &Value) -> Result<Value, String> {
let len = app.ui.action_sets.len();
let mut sets: Vec<usize> = match p.get("sets") {
None | Some(Value::Null) => app.ui.action_set.filter(|&s| s < len).into_iter().collect(),
Some(v) => v
.as_array()
.ok_or("no such action set")?
.iter()
.map(|i| i.as_u64().map(|i| i as usize).filter(|&i| i < len).ok_or("no such action set"))
.collect::<Result<_, _>>()?,
};
sets.sort_unstable();
sets.dedup();
if let Some((rec, ..)) = app.ui.recording.as_mut() {
if sets.contains(rec) {
app.ui.recording = None;
app.session.journal.hold(None);
} else {
*rec -= sets.iter().filter(|&&s| s < *rec).count();
}
}
for &s in sets.iter().rev() {
app.ui.action_sets.remove(s);
}
if !sets.is_empty() {
app.ui.action_set = app.ui.action_set.filter(|c| !sets.contains(c)).map(|c| c - sets.iter().filter(|&&s| s < c).count());
app.ui.action_sets_selected.clear();
}
Ok(json!({ "deleted": sets.len() }))
}
/// The panel's (≡) menu.
pub fn menu(app: &mut VectorcraftApp, ui: &mut egui::Ui) {
let selected: Option<(usize, usize)> = pstate(ui.ctx(), "actions-selected");
@@ -58,6 +131,9 @@ pub fn menu(app: &mut VectorcraftApp, ui: &mut egui::Ui) {
if widgets::menu_item(ui, tl!("New Set…"), true, false) {
crate::dialogs::action_set_options::open_new(app);
}
if widgets::menu_item(ui, tl!("Delete"), has || !selected_sets(app).is_empty(), false) {
delete_selection(app, ui.ctx());
}
let current = app.ui.action_set.filter(|&s| s < app.ui.action_sets.len());
if widgets::menu_item(ui, tl!("Set Options…"), current.is_some(), false)
&& let Some(s) = current
@@ -90,18 +166,28 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut egui::Ui) {
selected = None;
}
let mut current = app.ui.action_set;
let mut sets_selected = selected_sets(app);
let shift = ui.input(|i| i.modifiers.shift);
let mut play_req = None;
egui::ScrollArea::vertical().max_height(260.0).show(ui, |ui| {
for (si, set) in app.ui.action_sets.iter().enumerate() {
let (r, resp) = ui.allocate_exact_size(egui::vec2(ui.available_width(), 20.0), egui::Sense::click());
if current == Some(si) && selected.is_none() {
if sets_selected.contains(&si) && selected.is_none() {
ui.painter().rect_filled(r, 0.0, t.row_selected);
} else if resp.hovered() {
ui.painter().rect_filled(r, 0.0, t.hover);
}
ui.painter().text(r.left_center(), egui::Align2::LEFT_CENTER, format!("▾ {}", set.name), egui::FontId::proportional(12.5), t.text);
if resp.clicked() {
current = Some(si);
// Shift-click selects the sets from the current one to the one clicked; the current
// set stays as it was.
match current {
Some(c) if shift && selected.is_none() && sets_selected.contains(&c) => sets_selected = (c.min(si)..=c.max(si)).collect(),
_ => {
current = Some(si);
sets_selected = vec![si];
}
}
selected = None;
}
for (ai, a) in set.actions.iter().enumerate() {
@@ -129,6 +215,7 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut egui::Ui) {
if resp.clicked() {
selected = Some((si, ai));
current = Some(si);
sets_selected = vec![si];
}
if resp.double_clicked() {
play_req = Some((si, ai));
@@ -138,6 +225,7 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut egui::Ui) {
});
set_pstate(ui.ctx(), SELECTED, selected);
app.ui.action_set = current;
app.ui.action_sets_selected = sets_selected;
widgets::divider(ui);
// Bottom bar: stop, record, play, new set, delete.
ui.horizontal(|ui| {
@@ -174,13 +262,8 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut egui::Ui) {
if widgets::icon_button(ui, "dc-folder", tl!("Create New Set"), false, 24.0).clicked() {
crate::dialogs::action_set_options::open_new(app);
}
if widgets::icon_button(ui, "trash-2", tl!("Delete Selection"), false, 24.0).clicked()
&& let Some((si, ai)) = selected
&& let Some(s) = app.ui.action_sets.get_mut(si)
&& ai < s.actions.len()
{
s.actions.remove(ai);
set_pstate::<Option<(usize, usize)>>(ui.ctx(), SELECTED, None);
if widgets::icon_button(ui, "trash-2", tl!("Delete Selection"), false, 24.0).clicked() {
delete_selection(app, ui.ctx());
}
if recording {
ui.label(egui::RichText::new(tl!("● Recording")).color(egui::Color32::from_rgb(0xe0, 0x30, 0x30)));
@@ -309,6 +392,173 @@ mod tests {
assert_eq!(names, [("Default Actions", 6), ("Web", 0)], "the clicked set is renamed, the other kept");
}
/// 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());
let action = |name: &str| Action { name: name.into(), steps: vec![("object.move".into(), json!({"dx": 1, "dy": 0}))] };
app.ui.action_sets.push(ActionSet { name: "Mine".into(), actions: vec![action("Nudge"), action("Nudge Again")] });
app.ui.action_sets.push(ActionSet { name: "Other".into(), actions: vec![] });
app
}
fn set_names(app: &VectorcraftApp) -> Vec<String> {
app.ui.action_sets.iter().map(|s| s.name.clone()).collect()
}
/// The bottom bar's Delete Selection button (stop, record, play, new set, delete).
fn trash(ctx: &egui::Context, app: &mut VectorcraftApp) -> egui::Pos2 {
super::super::tests_appearance::frame_events(ctx, app, vec![], show);
let mut bar = crate::toolbar::tests::widget_rects(ctx, egui::vec2(24.0, 24.0));
bar.sort_by(|a, b| a.left().total_cmp(&b.left()));
let [_, _, _, _, delete] = bar[..] else { panic!("the bottom bar: {bar:?}") };
delete.center()
}
/// A click on the name of set `name` with `modifiers` held.
fn click_set(ctx: &egui::Context, app: &mut VectorcraftApp, name: &str, modifiers: egui::Modifiers) {
use super::super::tests_appearance::{frame_events, text_rect};
let pos = text_rect(&frame_events(ctx, app, vec![], show), &format!("▾ {name}")).center();
let button = |pressed| egui::Event::PointerButton { pos, button: egui::PointerButton::Primary, pressed, modifiers };
for e in [egui::Event::PointerMoved(pos), button(true), button(false)] {
frame_events(ctx, app, vec![egui::Event::ModifiersChanged(modifiers), e], show);
}
}
/// The question Delete Selection asks about sets, with the sets OK deletes.
fn asked(app: &VectorcraftApp) -> Option<(String, Value)> {
let d = app.ui.dialog.as_ref()?;
(d.kind == crate::dialogs::confirm::KIND && d.str("__command") == DELETE_SETS)
.then(|| (d.str("message"), d.fields["__params"]["sets"].clone()))
}
/// Delete Selection with a set selected asks first: Cancel keeps every set, OK deletes the
/// selected set with its actions and keeps the others.
#[test]
fn delete_selection_asks_and_then_deletes_the_selected_set() {
use super::super::tests_appearance::click;
let mut app = app_with_sets();
let ctx = egui::Context::default();
click_set(&ctx, &mut app, "Mine", egui::Modifiers::NONE);
let delete = trash(&ctx, &mut app);
click(&ctx, &mut app, delete, show);
let (message, sets) = asked(&app).expect("Delete Selection asks before deleting a set");
assert_eq!((message.as_str(), sets), ("Delete the action set “Mine” and its actions?", json!([1])));
crate::dialogs::cancel(&mut app);
assert_eq!(set_names(&app), ["Default Actions", "Mine", "Other"], "Cancel deletes nothing");
click(&ctx, &mut app, delete, show);
crate::dialogs::confirm(&mut app).unwrap();
assert_eq!(set_names(&app), ["Default Actions", "Other"], "OK deletes the selected set and its actions");
assert_eq!(app.ui.action_set, None, "no set is current after the current one is deleted");
let delete = trash(&ctx, &mut app);
click(&ctx, &mut app, delete, show);
assert!(app.ui.dialog.is_none(), "nothing selected: nothing to ask about");
}
/// The panel menu's Delete asks the same question about the selected set, and deletes a
/// selected action at once, as the bottom bar's button does.
#[test]
fn the_panel_menus_delete_deletes_the_selected_set_or_action() {
use super::super::tests_appearance::{click, frame_events, text_rect};
let mut app = app_with_sets();
let ctx = egui::Context::default();
let delete = |ctx: &egui::Context, app: &mut VectorcraftApp| {
let texts = frame_events(ctx, app, vec![], menu);
let item = texts.iter().find(|(s, _)| s.trim() == "Delete").map(|(_, r)| *r).expect("Delete in the menu");
click(ctx, app, item.center(), menu);
};
click_set(&ctx, &mut app, "Other", egui::Modifiers::NONE);
delete(&ctx, &mut app);
assert_eq!(asked(&app).map(|(_, sets)| sets), Some(json!([2])));
crate::dialogs::confirm(&mut app).unwrap();
assert_eq!(set_names(&app), ["Default Actions", "Mine"]);
let texts = frame_events(&ctx, &mut app, vec![], show);
click(&ctx, &mut app, text_rect(&texts, "Nudge").center(), show);
frame_events(&ctx, &mut app, vec![], show);
delete(&ctx, &mut app);
assert!(app.ui.dialog.is_none(), "an action is deleted without a question");
let actions = app.ui.action_sets[1].actions.iter().map(|a| a.name.as_str()).collect::<Vec<_>>();
assert_eq!((set_names(&app).len(), actions), (2, vec!["Nudge Again"]), "the action goes, its set stays");
}
/// After New Set makes another set current, the panel menu's Delete asks about that set and
/// leaves the action selected before it.
#[test]
fn delete_after_new_set_targets_the_new_set() {
use super::super::tests_appearance::{click, frame_events, text_rect};
let mut app = app_with_sets();
let ctx = egui::Context::default();
let texts = frame_events(&ctx, &mut app, vec![], show);
click(&ctx, &mut app, text_rect(&texts, "Nudge").center(), show);
crate::dialogs::action_set_options::open_new(&mut app);
crate::dialogs::confirm(&mut app).unwrap();
let texts = frame_events(&ctx, &mut app, vec![], menu);
let item = texts.iter().find(|(s, _)| s.trim() == "Delete").map(|(_, r)| *r).expect("Delete in the menu");
click(&ctx, &mut app, item.center(), menu);
assert_eq!(asked(&app).map(|(_, sets)| sets), Some(json!([3])), "the new set, not the action");
assert_eq!(app.ui.action_sets[1].actions.len(), 2);
}
/// Shift-click on a set's name selects the sets from the current one to it, and one question
/// deletes them all; the set left out stays.
#[test]
fn shift_click_selects_sets_that_one_question_deletes() {
use super::super::tests_appearance::click;
let mut app = app_with_sets();
let ctx = egui::Context::default();
click_set(&ctx, &mut app, "Mine", egui::Modifiers::NONE);
click_set(&ctx, &mut app, "Other", egui::Modifiers::SHIFT);
assert_eq!((app.ui.action_set, app.ui.action_sets_selected.clone()), (Some(1), vec![1, 2]), "the current set stays");
let delete = trash(&ctx, &mut app);
click(&ctx, &mut app, delete, show);
assert_eq!(asked(&app), Some(("Delete the selected action sets and their actions?".to_string(), json!([1, 2]))));
crate::dialogs::confirm(&mut app).unwrap();
assert_eq!(set_names(&app), ["Default Actions"]);
// A plain click selects one set again.
app.ui.action_sets.extend([ActionSet { name: "A".into(), actions: vec![] }, ActionSet { name: "B".into(), actions: vec![] }]);
click_set(&ctx, &mut app, "A", egui::Modifiers::NONE);
click_set(&ctx, &mut app, "Default Actions", egui::Modifiers::SHIFT);
click_set(&ctx, &mut app, "B", egui::Modifiers::NONE);
assert_eq!((app.ui.action_set, app.ui.action_sets_selected.clone()), (Some(2), vec![2]));
}
/// The default set can be deleted, and so can the last set: the panel is then empty, no set is
/// current, and New Set makes a set again.
#[test]
fn deleting_the_default_and_the_last_set_leaves_an_empty_panel() {
use super::super::tests_appearance::click;
let mut app = VectorcraftApp::new(Session::new(), Default::default());
let ctx = egui::Context::default();
click_set(&ctx, &mut app, "Default Actions", egui::Modifiers::NONE);
let delete = trash(&ctx, &mut app);
click(&ctx, &mut app, delete, show);
crate::dialogs::confirm(&mut app).unwrap();
assert!(app.ui.action_sets.is_empty());
assert_eq!(app.ui.action_set, None);
crate::dialogs::action_set_options::open_new(&mut app);
crate::dialogs::confirm(&mut app).unwrap();
assert_eq!((set_names(&app), app.ui.action_set), (vec!["Set 1".to_string()], Some(0)), "New Set works on an empty panel");
}
/// `ui.actionSet.delete` keeps a recording into a later set going in the same set, stops one
/// into a deleted set, and refuses an index past the last set.
#[test]
fn deleting_sets_keeps_a_recording_in_its_set() {
let mut app = app_with_sets();
app.run("file.new", json!({"width": 200, "height": 100})).unwrap();
app.ui.action_set = Some(2);
let start = app.session.journal.len();
app.session.journal.hold(Some(start));
app.ui.recording = Some((2, "Action 1".into(), start));
assert_eq!(app.run(DELETE_SETS, json!({"sets": [1]})).unwrap(), json!({"deleted": 1}));
assert_eq!(set_names(&app), ["Default Actions", "Other"]);
assert_eq!((app.ui.recording.as_ref().map(|r| r.0), app.ui.action_set), (Some(1), Some(1)), "still recording into Other");
assert_eq!(app.run(DELETE_SETS, json!({"sets": [5]})).unwrap_err(), "no such action set");
assert_eq!(app.run(DELETE_SETS, json!({})).unwrap(), json!({"deleted": 1}), "the current set by default");
assert_eq!(set_names(&app), ["Default Actions"]);
assert!(app.ui.recording.is_none(), "the recording into the deleted set stops");
assert_eq!(app.session.journal.held(), None);
}
/// A recording keeps its steps when a canceled undo group drops journal entries made before the
/// recording began.
#[test]
+5
View File
@@ -448,6 +448,10 @@ pub struct UiState {
/// with New Set): a recording goes into it and Set Options renames it. Not saved.
#[serde(skip)]
pub action_set: Option<usize>,
/// The sets selected in the Actions panel, in index order: the current set and the sets a
/// Shift-click on a set's name added. Delete Selection deletes them. Not saved.
#[serde(skip)]
pub action_sets_selected: Vec<usize>,
/// Recording in progress: (set index, action name, journal length when recording started).
#[serde(skip)]
pub recording: Option<(usize, String, usize)>,
@@ -876,6 +880,7 @@ impl Default for UiState {
about: false,
action_sets: crate::panels::actions::default_sets(),
action_set: None,
action_sets_selected: vec![],
recording: None,
shortcut_overrides: Default::default(),
shortcut_set: default_shortcut_set(),
+6
View File
@@ -1650,6 +1650,12 @@ 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).
- Action sets: `ui.actionSet.delete {sets?: [index]}` 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. A recording into a deleted set stops and keeps no steps; a recording into another set
goes on in that set. The panel's Delete Selection (the trash button and the panel menu's Delete) asks before it runs
this for the selected sets, and Shift-click on a set's name selects the sets from the current one to it. Undo does
not restore a deleted set.
- Image Trace: `imageTrace.presets`, `imageTrace.make {id?, preset?, params?, view?}` (or `imageTrace.makeAndExpand`),
`imageTrace.setView`, `imageTrace.expand`, `imageTrace.release`. A key inside `params` that is not one of the trace
settings, such as `colours`, is an error that names it and the nearest setting (`colors`), as for a command's own keys.
+1
View File
@@ -547,6 +547,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- 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 (record/playback, persisted), generic parameter dialogs for every "…" command.
- 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.
- Batch… over data sets names each file after the document and the data set (`action.batch`). It writes `/`, `\`, `:`, `<`, `>`, `"`, `|`, `?`, `*` and control characters in the data set's name as `-`, and adds `-2`, `-3`… to a name an earlier data set used.
- Batch… offers File + Number, File + Data Set Name, and Data Set Name for a Data Sets source. The dialog defaults to File + Number; `action.batch` without `datasetFileNaming` keeps File + Data Set Name. An output already in a destination folder the batch may read and write gets the next free `-2`, `-3` suffix. [Batch options](https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/batch-options.html).
- Export for Screens: the dialog's Example line shows the path of the file the export writes first, sub-folder included, named as the export names it, for the first chosen artboard, asset, layer or object (`document.exportForScreens`).
+2 -1
View File
@@ -294,7 +294,8 @@ next unit in the order a right-click on a ruler lists them, from Feet back to Po
| 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 (<https://web.archive.org/web/20250831090623/https://helpx.adobe.com/illustrator/using/image-trace.html>). 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: 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)) | partial | #3712; the New Action dialog (name, set menu, shortcut, colour), dragging actions between sets and deleting a set 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)) | 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 |
| Properties panel per context (every selection type's Quick Actions) | partial | one clip group: Edit Clipping Path and Edit Contents, at the top of the panel rather than in Quick Actions (G1); the overview's additional controls ([Properties panel overview](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/properties-panel-overview.html)): Blend Options (one blend selected, with any tool, #4817) and Merge Live Paint (a Live Paint group with more) in Quick Actions, the Width Profile list (Width tool or a variable-width stroke) and an applied effect's Delete icon in Appearance, Corner and Corner Radius for corners selected with Direct Selection; Variable Font was already there |
| Select › Start Global Edit / Stop Global Edit; Properties › Quick Actions with the Global Edit Options popover (Match: Appearance, Size; On Artboards: All Artboards or a range, Include Objects On Canvas; Result(s)); Escape stops it; the matched objects are highlighted in their layer colour while it runs | partial | #3521, `select.globalEdit` / `select.globalEditOptions`, `crates/engine/src/tests/globaledit.rs`; paths, compound paths, symbol instances and groups of them (similar groups, #4939: edits inside one group reach the same object in the others; [public source](https://helpx.adobe.com/illustrator/desktop/manage-objects/edit-objects/edit-groups-with-similar-objects.html)); the Control panel's copy of the options, Shift-click to leave a match out, rotated copies and highlighting matches before the start remain ([public source](https://helpx.adobe.com/illustrator/desktop/manage-objects/edit-objects/edit-similar-objects.html)) |
| Contextual task bar | done | |