Merge pull request #3863 from marcoarnulfo/feat/action-sets

Actions: Create New Set and Set Options… name action sets, and a recording goes into the current set (#3712)
This commit is contained in:
BFlat
2026-10-24 04:10:45 +07:00
committed by GitHub
29 changed files with 320 additions and 8 deletions
@@ -0,0 +1,63 @@
//! Actions panel › Create New Set (and ≡ › New Set…) and ≡ › Set Options…: the name of a new action
//! set, or of the current one.
//!
//! 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.
use serde_json::{Value, json};
use super::DialogSpec;
use crate::state::Dialog;
use crate::{VectorcraftApp, widgets};
/// The dialog kind of New Set and Set 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() }
}
/// Open New Set, named "Set N" with the first number no set uses.
pub fn open_new(app: &mut VectorcraftApp) {
let taken = |n: usize| app.ui.action_sets.iter().any(|s| s.name == format!("Set {n}"));
let n = (1..).find(|&n| !taken(n)).unwrap_or(1);
app.ui.dialog = Some(Dialog::new(KIND, json!({"name": format!("Set {n}")})));
}
/// Open Set Options for the set at `set`, if there is one.
pub fn open(app: &mut VectorcraftApp, set: usize) {
if let Some(s) = app.ui.action_sets.get(set) {
app.ui.dialog = Some(Dialog::new(KIND, json!({"set": set, "name": s.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);
});
false
}
fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result<Value, String> {
let name = d.str("name").trim().to_string();
// OK is disabled without a name; `ui.dialog.confirm` keeps the dialog open the same way.
if name.is_empty() {
return Ok(Value::Null);
}
match d.fields.get("set").and_then(Value::as_u64) {
Some(set) => {
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 { name, actions: vec![] });
app.ui.action_set = Some(app.ui.action_sets.len() - 1);
}
}
app.ui.dialog = None;
Ok(Value::Null)
}
+2
View File
@@ -8,6 +8,7 @@
mod about;
pub mod action_batch;
pub mod action_set_options;
pub mod artboard_options;
pub mod blend_options;
mod brush_art_options;
@@ -318,6 +319,7 @@ registry! {
PlacementOptions: [placement_options::KIND] => placement_options::SPEC,
Package: [package::KIND] => package::SPEC,
ActionBatch: [action_batch::KIND] => action_batch::SPEC,
ActionSetOptions: [action_set_options::KIND] => action_set_options::SPEC,
SliceOptions: [slices::OPTIONS] => slices::OPTIONS_SPEC,
DivideSlices: [slices::DIVIDE] => slices::DIVIDE_SPEC,
EpsOptions: [eps_options::KIND] => eps_options::SPEC,
+6
View File
@@ -5430,6 +5430,12 @@ axis Y Axis Y ос
The plug-in is computing… Приставката изчислява…
Inspect Plug-in Проверка на приставка
Cancel Plug-in Run Отказ от изпълнението на приставката
# Actions panel: action sets (#3712).
New Set… Нов набор…
New Set Нов набор
Set Options… Опции на набора…
Set Options Опции на набора
Create New Set Създаване на нов набор
Stop Global Edit Край на глобалното редактиране
Global Edit Options Опции за глобално редактиране
Match Съвпадение
Can't render this file because it contains an unexpected character in line 2 and column 73.
+6
View File
@@ -5481,6 +5481,12 @@ axis Y Axis osa Y
@msg a live effect runs while the document is drawn, so it can't be `heavy` živý efekt běží během vykreslování dokumentu, takže nemůže být `heavy`
@msg an exporter always gets an export's budget: leave `budget` out exportní zásuvný modul dostává vždy rozpočet exportu: klíč `budget` vynechte
@msg “{_1}” asks for a heavy budget (each run up to {minutes} minutes and {times} times the usual instructions): install it with `allowHeavy: true` once the user agrees „{_1}“ žádá o velký výpočetní rozpočet (každé spuštění až {minutes} minut a {times}krát víc instrukcí než obvykle): nainstalujte ho s `allowHeavy: true`, jakmile uživatel souhlasí
# Actions panel: action sets (#3712).
New Set… Nová sada…
New Set Nová sada
Set Options… Možnosti sady…
Set Options Možnosti sady
Create New Set Vytvořit novou sadu
Stop Global Edit Ukončit globální úpravy
Global Edit Options Možnosti globálních úprav
Match Shoda
Can't render this file because it contains an unexpected character in line 2 and column 73.
+6
View File
@@ -5446,6 +5446,12 @@ axis Y Axis Y-akse
The plug-in is computing… Plug-in'en regner…
Inspect Plug-in Undersøg plug-in
Cancel Plug-in Run Annullér plug-in-kørsel
# Actions panel: action sets (#3712).
New Set… Nyt sæt…
New Set Nyt sæt
Set Options… Indstillinger for sæt…
Set Options Indstillinger for sæt
Create New Set Opret nyt sæt
Stop Global Edit Stop global redigering
Global Edit Options Indstillinger for global redigering
Match Match
Can't render this file because it contains an unexpected character in line 2 and column 73.
+6
View File
@@ -5064,6 +5064,12 @@ axis Y Axis Y-Achse
@msg a live effect runs while the document is drawn, so it can't be `heavy` ein Live-Effekt läuft, während das Dokument gezeichnet wird, daher kann er nicht `heavy` sein
@msg an exporter always gets an export's budget: leave `budget` out ein Export-Plug-in erhält immer das Budget eines Exports: Lassen Sie `budget` weg
@msg “{_1}” asks for a heavy budget (each run up to {minutes} minutes and {times} times the usual instructions): install it with `allowHeavy: true` once the user agrees „{_1}“ verlangt ein großes Rechenbudget (jede Ausführung bis zu {minutes} Minuten und {times}-mal so viele Anweisungen wie üblich): Installieren Sie es mit `allowHeavy: true`, sobald der Benutzer zustimmt
# Actions panel: action sets (#3712).
New Set… Neuer Satz…
New Set Neuer Satz
Set Options… Satzoptionen…
Set Options Satzoptionen
Create New Set Neuen Satz erstellen
Stop Global Edit Globale Bearbeitung beenden
Global Edit Options Optionen für globale Bearbeitung
Match Übereinstimmung
Can't render this file because it contains an unexpected character in line 2 and column 73.
+6
View File
@@ -5433,6 +5433,12 @@ axis Y Axis Άξονας Y
@msg a live effect runs while the document is drawn, so it can't be `heavy` ένα δυναμικό εφέ εκτελείται κατά τη σχεδίαση του εγγράφου, οπότε δεν μπορεί να είναι `heavy`
@msg an exporter always gets an export's budget: leave `budget` out ένα πρόσθετο εξαγωγής παίρνει πάντα τον προϋπολογισμό μιας εξαγωγής: παραλείψτε το `budget`
@msg “{_1}” asks for a heavy budget (each run up to {minutes} minutes and {times} times the usual instructions): install it with `allowHeavy: true` once the user agrees το «{_1}» ζητά μεγάλο υπολογιστικό προϋπολογισμό (κάθε εκτέλεση έως {minutes} λεπτά και {times} φορές τις συνήθεις εντολές): εγκαταστήστε το με `allowHeavy: true` μόλις συμφωνήσει ο χρήστης
# Actions panel: action sets (#3712).
New Set… Νέο σύνολο…
New Set Νέο σύνολο
Set Options… Επιλογές συνόλου…
Set Options Επιλογές συνόλου
Create New Set Δημιουργία νέου συνόλου
Stop Global Edit Τερματισμός καθολικής επεξεργασίας
Global Edit Options Επιλογές καθολικής επεξεργασίας
Match Αντιστοίχιση
Can't render this file because it contains an unexpected character in line 2066 and column 19.
+6
View File
@@ -5423,6 +5423,12 @@ axis Y Axis Eje Y
@msg a live effect runs while the document is drawn, so it can't be `heavy` un efecto interactivo se ejecuta mientras se dibuja el documento, así que no puede ser `heavy`
@msg an exporter always gets an export's budget: leave `budget` out un plugin de exportación siempre recibe el presupuesto de una exportación: omite `budget`
@msg “{_1}” asks for a heavy budget (each run up to {minutes} minutes and {times} times the usual instructions): install it with `allowHeavy: true` once the user agrees “{_1}” pide un presupuesto de cálculo grande (cada ejecución hasta {minutes} minutos y {times} veces las instrucciones habituales): instálalo con `allowHeavy: true` cuando el usuario lo acepte
# Actions panel: action sets (#3712).
New Set… Nuevo conjunto…
New Set Nuevo conjunto
Set Options… Opciones de conjunto…
Set Options Opciones de conjunto
Create New Set Crear nuevo conjunto
Stop Global Edit Detener edición global
Global Edit Options Opciones de edición global
Match Coincidir
Can't render this file because it contains an unexpected character in line 2 and column 73.
+6
View File
@@ -5512,6 +5512,12 @@ axis Y Axis Y-akseli
@msg a live effect runs while the document is drawn, so it can't be `heavy` live-tehoste suoritetaan asiakirjaa piirrettäessä, joten se ei voi olla `heavy`
@msg an exporter always gets an export's budget: leave `budget` out vientilaajennus saa aina viennin budjetin: jätä `budget` pois
@msg “{_1}” asks for a heavy budget (each run up to {minutes} minutes and {times} times the usual instructions): install it with `allowHeavy: true` once the user agrees ”{_1}” pyytää suurta laskentabudjettia (jokainen ajo enintään {minutes} minuuttia ja {times} kertaa tavallinen määrä käskyjä): asenna se asetuksella `allowHeavy: true`, kun käyttäjä hyväksyy
# Actions panel: action sets (#3712).
New Set… Uusi sarja…
New Set Uusi sarja
Set Options… Sarjan asetukset…
Set Options Sarjan asetukset
Create New Set Luo uusi sarja
Stop Global Edit Lopeta yleismuokkaus
Global Edit Options Yleismuokkauksen asetukset
Match Vastaavuus
Can't render this file because it contains an unexpected character in line 2 and column 73.
+6
View File
@@ -5480,6 +5480,12 @@ axis Y Axis Axe Y
@msg a live effect runs while the document is drawn, so it can't be `heavy` un effet dynamique s’exécute pendant le tracé du document, il ne peut donc pas être `heavy`
@msg an exporter always gets an export's budget: leave `budget` out un module externe d’exportation reçoit toujours le budget d’une exportation : omettez `budget`
@msg “{_1}” asks for a heavy budget (each run up to {minutes} minutes and {times} times the usual instructions): install it with `allowHeavy: true` once the user agrees « {_1} » demande un budget de calcul important (chaque exécution jusqu’à {minutes} minutes et {times} fois les instructions habituelles) : installez-le avec `allowHeavy: true` une fois que l’utilisateur l’accepte
# Actions panel: action sets (#3712).
New Set… Nouveau jeu…
New Set Nouveau jeu
Set Options… Options du jeu…
Set Options Options du jeu
Create New Set Créer un jeu
Stop Global Edit Arrêter la modification globale
Global Edit Options Options de modification globale
Match Correspondance
Can't render this file because it contains an unexpected character in line 2 and column 73.
+6
View File
@@ -5467,6 +5467,12 @@ axis Y Axis Y tengely
@msg a live effect runs while the document is drawn, so it can't be `heavy` az élő effektus a dokumentum rajzolása közben fut, ezért nem lehet `heavy`
@msg an exporter always gets an export's budget: leave `budget` out az exportáló bővítmény mindig az exportálás keretét kapja: hagyd ki a `budget` kulcsot
@msg “{_1}” asks for a heavy budget (each run up to {minutes} minutes and {times} times the usual instructions): install it with `allowHeavy: true` once the user agrees a(z) „{_1}” nagy számítási keretet kér (futásonként legfeljebb {minutes} perc és a szokásos utasításszám {times}-szorosa): telepítsd az `allowHeavy: true` megadásával, miután a felhasználó beleegyezett
# Actions panel: action sets (#3712).
New Set… Új készlet…
New Set Új készlet
Set Options… Készlet beállításai…
Set Options Készlet beállításai
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
Match Egyezés
Can't render this file because it contains an unexpected character in line 2 and column 73.
+6
View File
@@ -5446,6 +5446,12 @@ axis Y Axis Sumbu Y
The plug-in is computing… Plug-in sedang menghitung…
Inspect Plug-in Periksa plug-in
Cancel Plug-in Run Batalkan jalannya plug-in
# Actions panel: action sets (#3712).
New Set… Set baru…
New Set Set baru
Set Options… Opsi set…
Set Options Opsi set
Create New Set Buat set baru
Stop Global Edit Hentikan edit global
Global Edit Options Opsi edit global
Match Cocokkan
Can't render this file because it contains an unexpected character in line 3837 and column 19.
+6
View File
@@ -5098,6 +5098,12 @@ axis Y Axis Asse Y
@msg a live effect runs while the document is drawn, so it can't be `heavy` un effetto dinamico viene eseguito mentre il documento viene disegnato, quindi non può essere `heavy`
@msg an exporter always gets an export's budget: leave `budget` out un plug-in di esportazione riceve sempre il budget di un’esportazione: ometti `budget`
@msg “{_1}” asks for a heavy budget (each run up to {minutes} minutes and {times} times the usual instructions): install it with `allowHeavy: true` once the user agrees “{_1}” chiede un budget di calcolo elevato (ogni esecuzione fino a {minutes} minuti e {times} volte le istruzioni consuete): installalo con `allowHeavy: true` quando l’utente acconsente
# Actions panel: action sets (#3712).
New Set… Nuovo set…
New Set Nuovo set
Set Options… Opzioni set…
Set Options Opzioni set
Create New Set Crea nuovo set
Stop Global Edit Interrompi modifica globale
Global Edit Options Opzioni modifica globale
Match Corrispondenza
Can't render this file because it contains an unexpected character in line 2 and column 73.
+6
View File
@@ -5160,6 +5160,12 @@ axis Y Axis Y 軸
@msg a live effect runs while the document is drawn, so it can't be `heavy` ライブ効果はドキュメントの描画中に実行されるため、`heavy` にはできません
@msg an exporter always gets an export's budget: leave `budget` out 書き出しプラグインには常に書き出しの予算が割り当てられます。`budget` は省略してください
@msg “{_1}” asks for a heavy budget (each run up to {minutes} minutes and {times} times the usual instructions): install it with `allowHeavy: true` once the user agrees 「{_1}」は大きな計算予算を求めています (1 回の実行につき最大 {minutes} 分、通常の {times} 倍の命令)。ユーザーが同意したら `allowHeavy: true` を指定してインストールしてください
# Actions panel: action sets (#3712).
New Set… 新規セット…
New Set 新規セット
Set Options… セットオプション…
Set Options セットオプション
Create New Set 新規セットを作成
Stop Global Edit グローバル編集を終了
Global Edit Options グローバル編集オプション
Match 一致
Can't render this file because it contains an unexpected character in line 2 and column 73.
+6
View File
@@ -5397,6 +5397,12 @@ axis Y Axis Y ღერძი
@msg a live effect runs while the document is drawn, so it can't be `heavy` ცოცხალი ეფექტი დოკუმენტის დახატვისას ეშვება, ამიტომ ის ვერ იქნება `heavy`
@msg an exporter always gets an export's budget: leave `budget` out ექსპორტის პლაგინი ყოველთვის ექსპორტის ბიუჯეტს იღებს: გამოტოვეთ `budget`
@msg “{_1}” asks for a heavy budget (each run up to {minutes} minutes and {times} times the usual instructions): install it with `allowHeavy: true` once the user agrees „{_1}“ დიდ გამოთვლით ბიუჯეტს ითხოვს (თითო გაშვება {minutes} წუთამდე და ჩვეულებრივზე {times}-ჯერ მეტი ინსტრუქცია): მომხმარებლის თანხმობის შემდეგ დააყენეთ ის `allowHeavy: true` პარამეტრით
# Actions panel: action sets (#3712).
New Set… ახალი ნაკრები…
New Set ახალი ნაკრები
Set Options… ნაკრების პარამეტრები…
Set Options ნაკრების პარამეტრები
Create New Set ახალი ნაკრების შექმნა
Stop Global Edit გლობალური რედაქტირების დასრულება
Global Edit Options გლობალური რედაქტირების პარამეტრები
Match დამთხვევა
Can't render this file because it contains an unexpected character in line 2 and column 73.
+6
View File
@@ -5061,6 +5061,12 @@ axis Y Axis Y축
@msg a live effect runs while the document is drawn, so it can't be `heavy` 라이브 효과는 문서를 그리는 동안 실행되므로 `heavy`일 수 없습니다
@msg an exporter always gets an export's budget: leave `budget` out 내보내기 플러그인은 항상 내보내기 예산을 받습니다. `budget`은 생략하세요
@msg “{_1}” asks for a heavy budget (each run up to {minutes} minutes and {times} times the usual instructions): install it with `allowHeavy: true` once the user agrees “{_1}”은(는) 큰 계산 예산을 요청합니다(실행당 최대 {minutes}분, 평소의 {times}배 명령어). 사용자가 동의하면 `allowHeavy: true`로 설치하세요
# Actions panel: action sets (#3712).
New Set… 새 세트…
New Set 새 세트
Set Options… 세트 옵션…
Set Options 세트 옵션
Create New Set 새 세트 만들기
Stop Global Edit 전체 편집 중지
Global Edit Options 전체 편집 옵션
Match 일치
Can't render this file because it contains an unexpected character in line 2 and column 73.
+6
View File
@@ -5489,6 +5489,12 @@ axis Y Axis Y-as
The plug-in is computing… De plug-in is aan het rekenen…
Inspect Plug-in Plug-in inspecteren
Cancel Plug-in Run Plug-in-run annuleren
# Actions panel: action sets (#3712).
New Set… Nieuwe set…
New Set Nieuwe set
Set Options… Setopties…
Set Options Setopties
Create New Set Nieuwe set maken
Stop Global Edit Globale bewerking stoppen
Global Edit Options Opties voor globale bewerking
Match Overeenkomst
Can't render this file because it contains an unexpected character in line 2 and column 73.
+6
View File
@@ -5333,6 +5333,12 @@ axis Y Axis Oś Y
@msg a live effect runs while the document is drawn, so it can't be `heavy` efekt na żywo działa podczas rysowania dokumentu, więc nie może być `heavy`
@msg an exporter always gets an export's budget: leave `budget` out wtyczka eksportu zawsze dostaje budżet eksportu: pomiń `budget`
@msg “{_1}” asks for a heavy budget (each run up to {minutes} minutes and {times} times the usual instructions): install it with `allowHeavy: true` once the user agrees „{_1}” prosi o duży budżet obliczeń (każde uruchomienie do {minutes} minut i {times} razy więcej instrukcji niż zwykle): zainstaluj wtyczkę z `allowHeavy: true`, gdy użytkownik się zgodzi
# Actions panel: action sets (#3712).
New Set… Nowy zestaw…
New Set Nowy zestaw
Set Options… Opcje zestawu…
Set Options Opcje zestawu
Create New Set Utwórz nowy zestaw
Stop Global Edit Zakończ edycję globalną
Global Edit Options Opcje edycji globalnej
Match Dopasowanie
Can't render this file because it contains an unexpected character in line 2 and column 73.
+6
View File
@@ -5398,6 +5398,12 @@ axis Y Axis Eixo Y
@msg a live effect runs while the document is drawn, so it can't be `heavy` um efeito dinâmico é executado enquanto o documento é desenhado, então não pode ser `heavy`
@msg an exporter always gets an export's budget: leave `budget` out um plug-in de exportação sempre recebe o orçamento de uma exportação: omita `budget`
@msg “{_1}” asks for a heavy budget (each run up to {minutes} minutes and {times} times the usual instructions): install it with `allowHeavy: true` once the user agrees “{_1}” pede um orçamento de processamento grande (cada execução até {minutes} minutos e {times} vezes as instruções habituais): instale-o com `allowHeavy: true` depois que o usuário concordar
# Actions panel: action sets (#3712).
New Set… Novo Conjunto…
New Set Novo Conjunto
Set Options… Opções do Conjunto…
Set Options Opções do Conjunto
Create New Set Criar Novo Conjunto
Stop Global Edit Parar Edição Global
Global Edit Options Opções de Edição Global
Match Corresponder
Can't render this file because it contains an unexpected character in line 6 and column 47.
+6
View File
@@ -5094,6 +5094,12 @@ axis Y Axis Ось Y
@msg a live effect runs while the document is drawn, so it can't be `heavy` динамический эффект выполняется во время отрисовки документа, поэтому он не может быть `heavy`
@msg an exporter always gets an export's budget: leave `budget` out модуль экспорта всегда получает бюджет экспорта: не указывайте `budget`
@msg “{_1}” asks for a heavy budget (each run up to {minutes} minutes and {times} times the usual instructions): install it with `allowHeavy: true` once the user agrees «{_1}» запрашивает большой вычислительный бюджет (каждый запуск до {minutes} минут и в {times} раз больше инструкций, чем обычно): установите его с `allowHeavy: true`, когда пользователь согласится
# Actions panel: action sets (#3712).
New Set… Новый набор…
New Set Новый набор
Set Options… Параметры набора…
Set Options Параметры набора
Create New Set Создать новый набор
Stop Global Edit Завершить глобальное редактирование
Global Edit Options Параметры глобального редактирования
Match Совпадение
Can't render this file because it contains an unexpected character in line 2 and column 73.
+6
View File
@@ -5437,6 +5437,12 @@ axis Y Axis Y-axel
@msg a live effect runs while the document is drawn, so it can't be `heavy` en live-effekt körs medan dokumentet ritas, så den kan inte vara `heavy`
@msg an exporter always gets an export's budget: leave `budget` out ett exportinsticksprogram får alltid en exports budget: utelämna `budget`
@msg “{_1}” asks for a heavy budget (each run up to {minutes} minutes and {times} times the usual instructions): install it with `allowHeavy: true` once the user agrees ”{_1}” begär en stor beräkningsbudget (varje körning upp till {minutes} minuter och {times} gånger de vanliga instruktionerna): installera det med `allowHeavy: true` när användaren har godkänt
# Actions panel: action sets (#3712).
New Set… Ny uppsättning…
New Set Ny uppsättning
Set Options… Alternativ för uppsättning…
Set Options Alternativ för uppsättning
Create New Set Skapa ny uppsättning
Stop Global Edit Avsluta global redigering
Global Edit Options Alternativ för global redigering
Match Matcha
Can't render this file because it contains an unexpected character in line 5 and column 25.
+6
View File
@@ -5444,6 +5444,12 @@ axis Y Axis Y ekseni
@msg a live effect runs while the document is drawn, so it can't be `heavy` canlı efekt belge çizilirken çalışır, bu yüzden `heavy` olamaz
@msg an exporter always gets an export's budget: leave `budget` out dışa aktarma eklentisi her zaman bir dışa aktarmanın bütçesini alır: `budget` anahtarını çıkarın
@msg “{_1}” asks for a heavy budget (each run up to {minutes} minutes and {times} times the usual instructions): install it with `allowHeavy: true` once the user agrees “{_1}” büyük bir hesaplama bütçesi istiyor (her çalıştırma en fazla {minutes} dakika ve olağan komutların {times} katı): kullanıcı onayladıktan sonra `allowHeavy: true` ile yükleyin
# Actions panel: action sets (#3712).
New Set… Yeni Set…
New Set Yeni Set
Set Options… Set Seçenekleri…
Set Options Set Seçenekleri
Create New Set Yeni Set Oluştur
Stop Global Edit Genel Düzenlemeyi Durdur
Global Edit Options Genel Düzenleme Seçenekleri
Match Eşleştir
Can't render this file because it contains an unexpected character in line 5 and column 25.
+6
View File
@@ -5078,6 +5078,12 @@ axis Y Axis Вісь Y
@msg a live effect runs while the document is drawn, so it can't be `heavy` динамічний ефект виконується під час відмальовування документа, тому він не може бути `heavy`
@msg an exporter always gets an export's budget: leave `budget` out модуль експорту завжди отримує бюджет експорту: не вказуйте `budget`
@msg “{_1}” asks for a heavy budget (each run up to {minutes} minutes and {times} times the usual instructions): install it with `allowHeavy: true` once the user agrees «{_1}» запитує великий обчислювальний бюджет (кожен запуск до {minutes} хвилин і в {times} разів більше інструкцій, ніж зазвичай): встановіть його з `allowHeavy: true`, коли користувач погодиться
# Actions panel: action sets (#3712).
New Set… Новий набір…
New Set Новий набір
Set Options… Параметри набору…
Set Options Параметри набору
Create New Set Створити новий набір
Stop Global Edit Завершити глобальне редагування
Global Edit Options Параметри глобального редагування
Match Збіг
Can't render this file because it contains an unexpected character in line 747 and column 40.
+6
View File
@@ -5078,6 +5078,12 @@ axis Y Axis Trục Y
@msg a live effect runs while the document is drawn, so it can't be `heavy` hiệu ứng trực tiếp chạy trong lúc tài liệu được vẽ nên không thể là `heavy`
@msg an exporter always gets an export's budget: leave `budget` out plug-in xuất luôn nhận ngân sách của một lần xuất: hãy bỏ `budget`
@msg “{_1}” asks for a heavy budget (each run up to {minutes} minutes and {times} times the usual instructions): install it with `allowHeavy: true` once the user agrees “{_1}” yêu cầu ngân sách tính toán lớn (mỗi lần chạy tới {minutes} phút và gấp {times} lần số lệnh thông thường): hãy cài đặt với `allowHeavy: true` khi người dùng đồng ý
# Actions panel: action sets (#3712).
New Set… Bộ mới…
New Set Bộ mới
Set Options… Tùy chọn bộ…
Set Options Tùy chọn bộ
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
Match Khớp
Can't render this file because it contains an unexpected character in line 4051 and column 10.
+6
View File
@@ -5321,6 +5321,12 @@ axis Y Axis Y 轴
@msg a live effect runs while the document is drawn, so it can't be `heavy` 实时效果在绘制文档时运行,因此不能为 `heavy`
@msg an exporter always gets an export's budget: leave `budget` out 导出增效工具始终使用导出的预算:请省略 `budget`
@msg “{_1}” asks for a heavy budget (each run up to {minutes} minutes and {times} times the usual instructions): install it with `allowHeavy: true` once the user agrees “{_1}”需要大量计算预算(每次运行最多 {minutes} 分钟,指令数为通常的 {times} 倍):请在用户同意后使用 `allowHeavy: true` 安装
# Actions panel: action sets (#3712).
New Set… 新建组…
New Set 新建组
Set Options… 组选项…
Set Options 组选项
Create New Set 创建新组
Stop Global Edit 停止全局编辑
Global Edit Options 全局编辑选项
Match 匹配
Can't render this file because it contains an unexpected character in line 2 and column 73.
+6
View File
@@ -5117,6 +5117,12 @@ axis Y Axis Y 軸
@msg a live effect runs while the document is drawn, so it can't be `heavy` 即時效果會在繪製文件時執行,因此不能是 `heavy`
@msg an exporter always gets an export's budget: leave `budget` out 匯出增效模組一律使用匯出的預算:請移除 `budget`
@msg “{_1}” asks for a heavy budget (each run up to {minutes} minutes and {times} times the usual instructions): install it with `allowHeavy: true` once the user agrees 「{_1}」需要大量運算預算(每次執行最多 {minutes} 分鐘,指令數為一般的 {times} 倍):請在使用者同意後以 `allowHeavy: true` 安裝
# Actions panel: action sets (#3712).
New Set… 新增組…
New Set 新增組
Set Options… 組選項…
Set Options 組選項
Create New Set 建立新組
Stop Global Edit 停止整體編輯
Global Edit Options 整體編輯選項
Match 符合
Can't render this file because it contains an unexpected character in line 6 and column 47.
+104 -8
View File
@@ -55,6 +55,15 @@ pub fn play(app: &mut VectorcraftApp, set: usize, idx: usize) -> Result<Value, S
pub fn menu(app: &mut VectorcraftApp, ui: &mut egui::Ui) {
let selected: Option<(usize, usize)> = pstate(ui.ctx(), "actions-selected");
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);
}
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
{
crate::dialogs::action_set_options::open(app, s);
}
if widgets::menu_item(ui, tl!("Batch…"), has, false)
&& let Some((s, a)) = selected
&& let Err(e) = crate::dialogs::action_batch::open(app, s, a)
@@ -75,10 +84,26 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut egui::Ui) {
// value per type, so reading the bare pair never found it and no action stayed selected (#1629).
const SELECTED: &str = "actions-selected";
let mut selected: Option<(usize, usize)> = pstate(ui.ctx(), SELECTED);
// A set made with New Set, or whose name was clicked, is current with none of its actions
// selected.
if selected.is_some_and(|(s, _)| app.ui.action_set != Some(s)) {
selected = None;
}
let mut current = app.ui.action_set;
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() {
ui.label(egui::RichText::new(format!("▾ {}", set.name)).color(t.text));
let (r, resp) = ui.allocate_exact_size(egui::vec2(ui.available_width(), 20.0), egui::Sense::click());
if current == Some(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);
selected = None;
}
for (ai, a) in set.actions.iter().enumerate() {
let sel = selected == Some((si, ai));
let (r, resp) = ui.allocate_exact_size(egui::vec2(ui.available_width(), 22.0), egui::Sense::click());
@@ -103,6 +128,7 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut egui::Ui) {
);
if resp.clicked() {
selected = Some((si, ai));
current = Some(si);
}
if resp.double_clicked() {
play_req = Some((si, ai));
@@ -111,8 +137,9 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut egui::Ui) {
}
});
set_pstate(ui.ctx(), SELECTED, selected);
app.ui.action_set = current;
widgets::divider(ui);
// Bottom bar: stop, record, play, new, delete.
// Bottom bar: stop, record, play, new set, delete.
ui.horizontal(|ui| {
let recording = app.ui.recording.is_some();
if widgets::icon_button(ui, "square", tl!("Stop Playing/Recording"), false, 24.0).clicked()
@@ -129,11 +156,11 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut egui::Ui) {
let (r, resp) = ui.allocate_exact_size(egui::vec2(24.0, 24.0), egui::Sense::click());
ui.painter().circle_filled(r.center(), 6.0, if recording { egui::Color32::from_rgb(0xe0, 0x30, 0x30) } else { t.icon });
if resp.on_hover_text(tl!("Begin Recording")).clicked() && !recording {
let n = app.ui.action_sets.first().map(|s| s.actions.len()).unwrap_or(0) + 1;
if app.ui.action_sets.is_empty() {
app.ui.action_sets.push(ActionSet { name: "Set 1".into(), actions: vec![] });
}
let set = selected.map(|s| s.0).unwrap_or(0);
let set = current.filter(|&s| s < app.ui.action_sets.len()).unwrap_or(0);
let n = app.ui.action_sets.get(set).map_or(0, |s| s.actions.len()) + 1;
let start = app.session.journal.len();
// Keep every step from here on until recording stops, however long it runs.
app.session.journal.hold(Some(start));
@@ -144,6 +171,9 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut egui::Ui) {
{
play_req = Some(s);
}
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)
@@ -197,10 +227,10 @@ mod tests {
click(&ctx, &mut app, text_rect(&texts, "Reflect Horizontal").center(), show);
frame_events(&ctx, &mut app, vec![], show);
assert_eq!(pstate::<Option<(usize, usize)>>(&ctx, "actions-selected"), Some((0, 5)), "selected on the frames after the click");
// The bottom bar's 24-point buttons, left to right: stop, record, play, delete.
// The bottom bar's 24-point buttons, left to right: stop, record, play, new set, delete.
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 [_, _, play, delete] = bar[..] else { panic!("the bottom bar: {bar:?}") };
let [_, _, play, _, delete] = bar[..] else { panic!("the bottom bar: {bar:?}") };
click(&ctx, &mut app, text_rect(&texts, "Duplicate and Offset 10 pt").center(), show);
click(&ctx, &mut app, play.center(), show);
assert_eq!(app.session.active().unwrap().selection.len(), 1);
@@ -213,6 +243,72 @@ mod tests {
assert_eq!(names(&app), before[1..], "nothing selected after a delete: nothing more goes");
}
/// #3712: Create New Set asks for a name (Set 1 when free), the new set is the current one, so a
/// recording goes into it, and a click on another set's name makes that one current. The sets
/// are saved with the UI preferences.
#[test]
fn create_new_set_names_a_set_that_recordings_go_into() {
use super::super::tests_appearance::{click, frame_events, text_rect};
let mut app = VectorcraftApp::new(Session::new(), Default::default());
app.run("file.new", json!({"width": 200, "height": 100})).unwrap();
let ctx = egui::Context::default();
// The bottom bar's 24-point buttons, left to right: stop, record, play, new set, delete. A new
// set or action moves the bar down.
let bar = |ctx: &egui::Context, app: &mut VectorcraftApp| {
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 [stop, record, _, new_set, _] = bar[..] else { panic!("the bottom bar: {bar:?}") };
(stop, record, new_set)
};
let (_, _, new_set) = bar(&ctx, &mut app);
click(&ctx, &mut app, new_set.center(), show);
let d = app.ui.dialog.as_ref().expect("New Set asks for a name");
assert_eq!((d.kind.as_str(), d.str("name")), ("actionSetOptions", "Set 1".to_string()));
app.ui.dialog.as_mut().unwrap().fields.insert("name".into(), json!("Print"));
crate::dialogs::confirm(&mut app).unwrap();
assert!(app.ui.dialog.is_none());
let sets = |app: &VectorcraftApp| app.ui.action_sets.iter().map(|s| (s.name.clone(), s.actions.len())).collect::<Vec<_>>();
assert_eq!(sets(&app), [("Default Actions".to_string(), 6), ("Print".into(), 0)]);
let rect = json!({"x": 10, "y": 10, "width": 20, "height": 20});
let (stop, record, _) = bar(&ctx, &mut app);
click(&ctx, &mut app, record.center(), show);
app.run("shape.rectangle", rect.clone()).unwrap();
click(&ctx, &mut app, stop.center(), show);
assert_eq!(sets(&app), [("Default Actions".to_string(), 6), ("Print".into(), 1)], "recorded into the new set");
assert_eq!(app.ui.action_sets[1].actions[0].name, "Action 1", "numbered within its set");
let texts = frame_events(&ctx, &mut app, vec![], show);
click(&ctx, &mut app, text_rect(&texts, "▾ Default Actions").center(), show);
let (stop, record, _) = bar(&ctx, &mut app);
click(&ctx, &mut app, record.center(), show);
app.run("shape.rectangle", rect).unwrap();
click(&ctx, &mut app, stop.center(), show);
assert_eq!(sets(&app), [("Default Actions".to_string(), 7), ("Print".into(), 1)], "recorded into the set clicked");
let saved = serde_json::to_value(&app.ui).unwrap();
let restored = serde_json::from_value::<crate::state::UiState>(saved).unwrap();
assert_eq!(restored.action_sets.iter().map(|s| s.name.as_str()).collect::<Vec<_>>(), ["Default Actions", "Print"]);
}
/// #3712: the panel menu's Set Options… renames the current set and keeps its actions.
#[test]
fn set_options_renames_the_current_set() {
use super::super::tests_appearance::{click, frame_events, text_rect};
let mut app = VectorcraftApp::new(Session::new(), Default::default());
app.ui.action_sets.push(ActionSet { name: "Mine".into(), actions: vec![] });
let ctx = egui::Context::default();
let texts = frame_events(&ctx, &mut app, vec![], show);
click(&ctx, &mut app, text_rect(&texts, "▾ Mine").center(), show);
let texts = frame_events(&ctx, &mut app, vec![], menu);
let item = texts.iter().find(|(s, _)| s.trim() == "Set Options…").map(|(_, r)| *r).expect("Set Options… in the menu");
click(&ctx, &mut app, item.center(), menu);
let d = app.ui.dialog.as_ref().expect("Set Options opens");
assert_eq!((d.kind.as_str(), d.str("name")), ("actionSetOptions", "Mine".to_string()));
app.ui.dialog.as_mut().unwrap().fields.insert("name".into(), json!("Web"));
crate::dialogs::confirm(&mut app).unwrap();
let names = app.ui.action_sets.iter().map(|s| (s.name.as_str(), s.actions.len())).collect::<Vec<_>>();
assert_eq!(names, [("Default Actions", 6), ("Web", 0)], "the clicked set is renamed, the other kept");
}
/// A recording keeps its steps when a canceled undo group drops journal entries made before the
/// recording began.
#[test]
@@ -231,10 +327,10 @@ mod tests {
}
let ctx = egui::Context::default();
frame_events(&ctx, &mut app, vec![], show);
// The bottom bar's 24-point buttons, left to right: stop, record, play, delete.
// The bottom bar's 24-point buttons, left to right: stop, record, play, new set, delete.
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 [stop, record, _, _] = bar[..] else { panic!("the bottom bar: {bar:?}") };
let [stop, record, _, _, _] = bar[..] else { panic!("the bottom bar: {bar:?}") };
click(&ctx, &mut app, record.center(), show);
for (id, p) in [("document.activate", json!({"index": 1})), ("shape.rectangle", rect(50)), ("edit.undoGroup.end", json!({"cancel": true}))] {
app.run(id, p).unwrap();
+5
View File
@@ -425,6 +425,10 @@ pub struct UiState {
/// Recorded actions (Actions panel), persisted with the UI preferences.
#[serde(default = "crate::panels::actions::default_sets")]
pub action_sets: Vec<crate::panels::actions::ActionSet>,
/// The current set of the Actions panel (its name clicked, one of its actions clicked, or made
/// with New Set): a recording goes into it and Set Options renames it. Not saved.
#[serde(skip)]
pub action_set: Option<usize>,
/// Recording in progress: (set index, action name, journal length when recording started).
#[serde(skip)]
pub recording: Option<(usize, String, usize)>,
@@ -860,6 +864,7 @@ impl Default for UiState {
draw_mode: 0,
about: false,
action_sets: crate::panels::actions::default_sets(),
action_set: None,
recording: None,
shortcut_overrides: Default::default(),
shortcut_set: default_shortcut_set(),
+2
View File
@@ -276,6 +276,7 @@ empty), without opening the dialog.
| 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 |
| 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 |
| 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) |
| 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 and symbol instances; the Control panel's copy of the options, Shift-click to leave a match out, highlighting matches before the start and similar groups remain ([public source](https://helpx.adobe.com/illustrator/desktop/manage-objects/edit-objects/edit-similar-objects.html)) |
| Contextual task bar | done | |
@@ -342,6 +343,7 @@ separate. Scores and hours unchanged. [Behavior and command details](development
| Date | Change | Summary |
|---|---|---|
| 2026-10-23 | minor | Actions panel: Create New Set and Set Options… name action sets, and a recording goes into the set clicked or just made (#3712). Scores and estimates unchanged. |
| 2026-10-23 | minor | Select › Start Global Edit / Stop Global Edit (menu label flips), 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 matches are highlighted on the canvas (#3521). Scores and estimates unchanged. |
| 2026-10-23 | minor | Symbol libraries: Window › Symbol Libraries and the Symbols panel's Symbol Libraries Menu open library panels (Basic Shapes, User Defined, Other Library…); click adds, drag places, Save Symbols / Save Symbol Library writes a library (`panels::tests_symbol_libraries`). |
| 2026-10-23 | minor | Selection tool: every selected area type and type on a path shows its in and out ports in their states (empty, arrow, red "+"); the out port of type that fits loads the text too, and the in port threads a new or clicked frame before the clicked one (#2572). Scores and estimates unchanged. |