Brushes: Delete Brush asks first, and Alt-click deletes without asking

The Brushes panel's Delete Brush button and its panel menu item now ask
before deleting the selected brushes, as Swatches and Graphic Styles do.
OK deletes them in one undo step and strokes that used them become plain
strokes; Cancel leaves the document unchanged. Alt-click (Option-click on
macOS) on the button deletes without asking. The brush.delete command
still deletes at once.
This commit is contained in:
dotCooCoo
2026-11-01 01:21:54 +07:00
committed by bflatastic
parent 19b4fc8d43
commit 49290fa8e6
28 changed files with 171 additions and 9 deletions
+3
View File
@@ -742,6 +742,9 @@ font-technology Variable Променливи
Yes Да
No Не
Delete Tab Изтриване на табулация
Delete the brush “{name}”? Да се изтрие четката „{name}“?
Delete these {count} brushes? Да се изтрият тези {count} четки?
Strokes using deleted brushes become plain strokes. Щрихите, които използват изтритите четки, стават обикновени щрихи.
Delete the graphic style “{name}”? Изтриване на графичния стил „{name}“?
Delete Theme Изтриване на тема
Delete theme Изтриване на тема
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -1059,6 +1059,9 @@
Yes Sí
No No
Delete Tab Suprimeix la tabulació
Delete the brush “{name}”? Voleu suprimir el pinzell “{name}”?
Delete these {count} brushes? Voleu suprimir aquests {count} pinzells?
Strokes using deleted brushes become plain strokes. Els traços que utilitzen pinzells suprimits esdevenen traços normals.
Delete the graphic style “{name}”? Voleu suprimir l’estil gràfic “{name}”?
Delete the library “{name}”? Voleu suprimir la biblioteca “{name}”?
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
@@ -1558,6 +1558,9 @@ lighting Light Světlo
Reverse Gradient Obrátit přechod
Save to Swatches Uložit do vzorníku
Delete Graphic Style (Alt-click: without asking) Odstranit grafický styl (Alt+kliknutí: bez dotazu)
Delete the brush “{name}”? Odstranit štětec „{name}“?
Delete these {count} brushes? Odstranit těchto {count} štětců?
Strokes using deleted brushes become plain strokes. Tahy používající odstraněné štětce se změní na běžné tahy.
Delete the graphic style “{name}”? Odstranit grafický styl „{name}“?
Delete these {n} graphic styles? Odstranit tyto grafické styly ({n})?
Delete this {n} graphic style? Odstranit tento grafický styl ({n})?
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -1472,6 +1472,9 @@ lighting Light Lys
Reverse Gradient Omvend gradient
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 brush “{name}”? Slet penslen »{name}«?
Delete these {count} brushes? Slet disse {count} pensler?
Strokes using deleted brushes become plain strokes. Streger, der bruger slettede pensler, bliver almindelige streger.
Delete the graphic style “{name}”? Slet den grafiske typografi »{name}«?
Delete these {n} graphic styles? Slet disse {n} grafiske typografier?
Delete this {n} graphic style? Slet denne {n} grafiske typografi?
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -736,6 +736,9 @@ font-technology Variable Variabel
Yes Ja
No Nein
Delete Tab Tabulator löschen
Delete the brush “{name}”? Pinsel „{name}“ löschen?
Delete these {count} brushes? Diese {count} Pinsel löschen?
Strokes using deleted brushes become plain strokes. Konturen mit gelöschten Pinseln werden zu einfachen Konturen.
Delete the graphic style “{name}”? Grafikstil „{name}“ löschen?
Delete Theme Thema löschen
Delete theme Thema löschen
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -716,6 +716,9 @@ font-technology Variable Μεταβλητές
Yes Ναι
No Όχι
Delete Tab Διαγραφή στηλοθέτη
Delete the brush “{name}”? Να διαγραφεί το πινέλο «{name}»;
Delete these {count} brushes? Να διαγραφούν αυτά τα {count} πινέλα;
Strokes using deleted brushes become plain strokes. Οι πινελιές που χρησιμοποιούν διαγραμμένα πινέλα γίνονται απλές πινελιές.
Delete the graphic style “{name}”? Να διαγραφεί το στυλ γραφικών «{name}»;
Delete Theme Διαγραφή θέματος
Delete theme Διαγραφή θέματος
Can't render this file because it contains an unexpected character in line 164 and column 5.
+3
View File
@@ -757,6 +757,9 @@ font-technology Variable Variables
Yes Sí
No No
Delete Tab Eliminar tabulación
Delete the brush “{name}”? ¿Eliminar el pincel “{name}”?
Delete these {count} brushes? ¿Eliminar estos {count} pinceles?
Strokes using deleted brushes become plain strokes. Los trazos que usan pinceles eliminados se convierten en trazos simples.
Delete the graphic style “{name}”? ¿Eliminar el estilo gráfico “{name}”?
Delete Theme Eliminar tema
Delete theme Eliminar tema
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -1532,6 +1532,9 @@ lighting Light Valo
Reverse Gradient Käännä liukuväri
Save to Swatches Tallenna värimalleihin
Delete Graphic Style (Alt-click: without asking) Poista grafiikkatyyli (Alt-napsautus: kysymättä)
Delete the brush “{name}”? Poistetaanko sivellin ”{name}”?
Delete these {count} brushes? Poistetaanko nämä {count} sivellintä?
Strokes using deleted brushes become plain strokes. Poistettuja siveltimiä käyttävät viivat muuttuvat tavallisiksi viivoiksi.
Delete the graphic style “{name}”? Poistetaanko grafiikkatyyli ”{name}”?
Delete these {n} graphic styles? Poistetaanko nämä {n} grafiikkatyyliä?
Delete this {n} graphic style? Poistetaanko tämä {n} grafiikkatyyli?
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -759,6 +759,9 @@ font-technology Variable Variables
Yes Oui
No Non
Delete Tab Supprimer la tabulation
Delete the brush “{name}”? Supprimer la forme « {name} » ?
Delete these {count} brushes? Supprimer ces {count} formes ?
Strokes using deleted brushes become plain strokes. Les contours utilisant des formes supprimées deviennent des contours simples.
Delete the graphic style “{name}”? Supprimer le style graphique « {name} » ?
Delete Theme Supprimer le thème
Delete theme Supprimer le thème
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -981,6 +981,9 @@ font-technology Variable Változó
Yes Igen
No Nem
Delete Tab Tabulátor törlése
Delete the brush “{name}”? Törlöd ezt az ecsetet: „{name}”?
Delete these {count} brushes? Törlöd ezt a {count} ecsetet?
Strokes using deleted brushes become plain strokes. A törölt ecseteket használó körvonalak egyszerű körvonalakká válnak.
Delete the graphic style “{name}”? Törlöd ezt a grafikai stílust: „{name}”?
Delete the library “{name}”? Törlöd ezt a könyvtárat: „{name}”?
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
@@ -1472,6 +1472,9 @@ lighting Light Cahaya
Reverse Gradient Balikkan gradien
Save to Swatches Simpan ke swatch
Delete Graphic Style (Alt-click: without asking) Hapus gaya grafis (Alt+klik: tanpa konfirmasi)
Delete the brush “{name}”? Hapus kuas “{name}”?
Delete these {count} brushes? Hapus {count} kuas ini?
Strokes using deleted brushes become plain strokes. Goresan yang menggunakan kuas terhapus menjadi goresan biasa.
Delete the graphic style “{name}”? Hapus gaya grafis “{name}”?
Delete these {n} graphic styles? Hapus {n} gaya grafis ini?
Delete this {n} graphic style? Hapus {n} gaya grafis ini?
1 # Indonesian (id) catalog. Same format as zh-hant.tsv: context <TAB> source <TAB> translation
1472
1473
1474
1475
1476
1477
1478
1479
1480
+3
View File
@@ -757,6 +757,9 @@ font-technology Variable Variabili
Yes Sì
No No
Delete Tab Elimina tabulazione
Delete the brush “{name}”? Eliminare il pennello “{name}”?
Delete these {count} brushes? Eliminare questi {count} pennelli?
Strokes using deleted brushes become plain strokes. I tracciati che usano pennelli eliminati diventano tracciati semplici.
Delete the graphic style “{name}”? Eliminare lo stile grafico “{name}”?
Delete Theme Elimina tema
Delete theme Elimina tema
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -1308,6 +1308,9 @@ font-technology Variable バリアブル
Delete Transparency Flattener Preset 透明の分割・統合プリセットを削除
Delete Width Point 幅ポイントを削除
Delete Workspace ワークスペースを削除
Delete the brush “{name}”? ブラシ「{name}」を削除しますか?
Delete these {count} brushes? これら {count} 個のブラシを削除しますか?
Strokes using deleted brushes become plain strokes. 削除したブラシを使用する線は通常の線になります。
Delete the graphic style “{name}”? グラフィックスタイル「{name}」を削除しますか?
Delete theme テーマを削除
Delete this saved preset この保存済みプリセットを削除
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -1389,6 +1389,9 @@ lighting Light სინათლე
Reverse Gradient გრადიენტის შებრუნება
Save to Swatches ფერის ნიმუშებში შენახვა
Delete Graphic Style (Alt-click: without asking) გრაფიკული სტილის წაშლა (Alt-დაწკაპუნებით: დაუკითხავად)
Delete the brush “{name}”? წაიშალოს ფუნჯი „{name}“?
Delete these {count} brushes? წაიშალოს ეს {count} ფუნჯი?
Strokes using deleted brushes become plain strokes. წაშლილი ფუნჯებით დახატული შტრიხები ჩვეულებრივ შტრიხებად იქცევა.
Delete the graphic style “{name}”? წაიშალოს გრაფიკული სტილი „{name}“?
Delete these {n} graphic styles? წაიშალოს ეს გრაფიკული სტილები ({n})?
Delete this {n} graphic style? წაიშალოს ეს გრაფიკული სტილი ({n})?
Can't render this file because it is too large.
+3
View File
@@ -1299,6 +1299,9 @@ font-technology Variable 가변
Delete Transparency Flattener Preset 투명도 병합 프리셋 삭제
Delete Width Point 너비 포인트 삭제
Delete Workspace 작업 영역 삭제
Delete the brush “{name}”? 브러시 “{name}”을(를) 삭제하시겠습니까?
Delete these {count} brushes? 이 브러시 {count}개를 삭제하시겠습니까?
Strokes using deleted brushes become plain strokes. 삭제된 브러시를 사용한 선은 일반 선이 됩니다.
Delete the graphic style “{name}”? 그래픽 스타일 “{name}”을(를) 삭제하시겠습니까?
Delete theme 테마 삭제
Delete this saved preset 저장된 이 프리셋 삭제
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -1486,6 +1486,9 @@ lighting Light Licht
Reverse Gradient Verloop omkeren
Save to Swatches Opslaan in stalen
Delete Graphic Style (Alt-click: without asking) Grafische stijl verwijderen (Alt-klik: zonder vragen)
Delete the brush “{name}”? Kwast „{name}” verwijderen?
Delete these {count} brushes? Deze {count} kwasten verwijderen?
Strokes using deleted brushes become plain strokes. Lijnen die verwijderde kwasten gebruiken, worden gewone lijnen.
Delete the graphic style “{name}”? De grafische stijl „{name}” verwijderen?
Delete these {n} graphic styles? Deze {n} grafische stijlen verwijderen?
Delete this {n} graphic style? Deze {n} grafische stijl verwijderen?
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -1472,6 +1472,9 @@ lighting Light Światło
Reverse Gradient Odwróć gradient
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 brush “{name}”? Usunąć pędzel „{name}”?
Delete these {count} brushes? Usunąć te {count} pędzle?
Strokes using deleted brushes become plain strokes. Obrysy używające usuniętych pędzli stają się zwykłymi obrysami.
Delete the graphic style “{name}”? Usunąć styl graficzny „{name}”?
Delete these {n} graphic styles? Usunąć te style graficzne ({n})?
Delete this {n} graphic style? Usunąć ten styl graficzny ({n})?
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -674,6 +674,9 @@
Delete Transparency Flattener Preset Excluir Predefinido de Achatamento de Transparência
Delete Width Point Excluir Ponto de Largura
Delete Workspace Excluir Espaço de Trabalho
Delete the brush “{name}”? Excluir o pincel “{name}”?
Delete these {count} brushes? Excluir estes {count} pincéis?
Strokes using deleted brushes become plain strokes. Traços que usam pincéis excluídos tornam-se traços simples.
Delete the graphic style “{name}”? Excluir o estilo gráfico “{name}”?
Delete theme Excluir tema
Delete this saved preset Excluir este predefinido salvo
Can't render this file because it contains an unexpected character in line 6 and column 47.
+3
View File
@@ -755,6 +755,9 @@ font-technology Variable Вариативные
Yes Да
No Нет
Delete Tab Удалить табуляцию
Delete the brush “{name}”? Удалить кисть «{name}»?
Delete these {count} brushes? Удалить эти кисти ({count})?
Strokes using deleted brushes become plain strokes. Обводки, использующие удалённые кисти, становятся обычными обводками.
Delete the graphic style “{name}”? Удалить графический стиль «{name}»?
Delete Theme Удалить тему
Delete theme Удалить тему
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -744,6 +744,9 @@ font-technology Variable Variabla
Yes Ja
No Nej
Delete Tab Radera tabb
Delete the brush “{name}”? Radera penseln ”{name}”?
Delete these {count} brushes? Radera dessa {count} penslar?
Strokes using deleted brushes become plain strokes. Linjer som använder borttagna penslar blir vanliga linjer.
Delete the graphic style “{name}”? Radera grafikstilen ”{name}”?
Delete Theme Radera tema
Delete theme Radera tema
Can't render this file because it contains an unexpected character in line 5 and column 25.
+3
View File
@@ -736,6 +736,9 @@ font-technology Variable Değişken
Yes Evet
No Hayır
Delete Tab Sekmeyi Sil
Delete the brush “{name}”? “{name}” fırçası silinsin mi?
Delete these {count} brushes? Bu {count} fırça silinsin mi?
Strokes using deleted brushes become plain strokes. Silinen fırçaları kullanan konturlar düz konturlara dönüşür.
Delete the graphic style “{name}”? “{name}” grafik stili silinsin mi?
Delete Theme Temayı Sil
Delete theme Temayı sil
Can't render this file because it contains an unexpected character in line 5 and column 25.
+3
View File
@@ -751,6 +751,9 @@ font-technology Variable Варіативні
Yes Так
No Ні
Delete Tab Видалити табуляцію
Delete the brush “{name}”? Видалити пензель «{name}»?
Delete these {count} brushes? Видалити ці пензлі ({count})?
Strokes using deleted brushes become plain strokes. Обведення, що використовують видалені пензлі, стають звичайними обведеннями.
Delete the graphic style “{name}”? Видалити графічний стиль «{name}»?
Delete Theme Видалити тему
Delete theme Видалити тему
Can't render this file because it contains an unexpected character in line 178 and column 5.
+3
View File
@@ -877,6 +877,9 @@ font-technology Variable Biến thiên
Yes Có
No Không
Delete Tab Xóa tab
Delete the brush “{name}”? Xóa cọ “{name}”?
Delete these {count} brushes? Xóa {count} cọ này?
Strokes using deleted brushes become plain strokes. Các nét dùng cọ đã xóa trở thành nét thông thường.
Delete the graphic style “{name}”? Xóa kiểu đồ họa “{name}”?
Delete the library “{name}”? Xóa thư viện “{name}”?
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
@@ -700,6 +700,9 @@
Yes 是
No 否
Delete Tab 删除制表位
Delete the brush “{name}”? 要删除画笔“{name}”吗?
Delete these {count} brushes? 要删除这 {count} 个画笔吗?
Strokes using deleted brushes become plain strokes. 使用已删除画笔的描边会变成普通描边。
Delete the graphic style “{name}”? 要删除图形样式“{name}”吗?
Delete Theme 删除主题
Delete theme 删除主题
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -719,6 +719,9 @@ font-technology Variable 可變
Yes 是
No 否
Delete Tab 刪除定位點
Delete the brush “{name}”? 要刪除筆刷「{name}」嗎?
Delete these {count} brushes? 要刪除這 {count} 個筆刷嗎?
Strokes using deleted brushes become plain strokes. 使用已刪除筆刷的筆畫會變成一般筆畫。
Delete the graphic style “{name}”? 要刪除繪圖樣式「{name}」嗎?
Delete Theme 刪除主題
Delete theme 刪除主題
Can't render this file because it contains an unexpected character in line 6 and column 47.
+19 -2
View File
@@ -204,6 +204,23 @@ fn run_on(app: &mut VectorcraftApp, id: &str, names: &[String]) {
app.run(id, p).ok();
}
/// Delete `names` after asking, or immediately when the bottom button was Alt-clicked.
fn delete(app: &mut VectorcraftApp, names: &[String], now: bool) {
let params = match names {
[name] => json!({ "name": name }),
_ => json!({ "names": names }),
};
if now {
app.run("brush.delete", params).ok();
return;
}
let message = match names {
[name] => crate::i18n::fmt(tl!("Delete the brush “{name}”?"), &[("name", name)]),
_ => crate::i18n::fmt(tl!("Delete these {count} brushes?"), &[("count", &names.len().to_string())]),
};
crate::dialogs::confirm::ask(app, &message, tl!("Strokes using deleted brushes become plain strokes."), "brush.delete", params);
}
/// The names in `list` in the order the panel shows them: by type in [`KINDS`] order, without the
/// hidden types.
fn shown<'a>(list: &'a [(String, String)], hidden: &[String]) -> Vec<&'a str> {
@@ -417,7 +434,7 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
app.status(e);
}
if widgets::icon_button_enabled(ui, "trash-2", tl!("Delete Brush"), false, !targets.is_empty(), 24.0).clicked() {
run_on(app, "brush.delete", &targets);
delete(app, &targets, ui.input(|i| i.modifiers.alt));
}
});
}
@@ -452,7 +469,7 @@ pub fn menu(app: &mut VectorcraftApp, ui: &mut Ui) {
run_on(app, "brush.duplicate", &targets);
}
if menu_item(ui, tl!("Delete Brush"), !targets.is_empty(), false) {
run_on(app, "brush.delete", &targets);
delete(app, &targets, false);
}
if menu_item(ui, tl!("Remove Brush Stroke"), sel.is_some(), false) {
app.run("brush.remove", json!({})).ok();
+76 -7
View File
@@ -146,6 +146,23 @@ fn names(app: &mut VectorcraftApp) -> Vec<String> {
brushes(app).0.into_iter().map(|b| b.0).collect()
}
fn undo_count(app: &VectorcraftApp) -> usize {
app.session.active().unwrap().history.undo.len()
}
fn delete_button(ctx: &egui::Context, app: &mut VectorcraftApp, modifiers: egui::Modifiers, time: f64) {
let out = frame_with(ctx, app, vec![], egui::Modifiers::NONE, time, show);
let at = clickable(&out).into_iter().find(|(label, _)| label.as_deref() == Some("Delete Brush")).expect("Delete Brush button").1.center();
click(ctx, app, at, modifiers, time + 0.5, show);
}
fn assert_delete_dialog(app: &VectorcraftApp, message: &str) {
let d = app.ui.dialog.as_ref().expect("Delete Brush asks first");
assert_eq!(d.kind, crate::dialogs::confirm::KIND);
assert_eq!(d.str("message"), message);
assert_eq!(d.str("detail"), "Strokes using deleted brushes become plain strokes.");
}
/// Click the panel menu's item `label` (frames at `time`, `time + 0.5`).
fn menu_click(ctx: &egui::Context, app: &mut VectorcraftApp, label: &str, time: f64) {
let out = frame_with(ctx, app, vec![], egui::Modifiers::NONE, time, menu);
@@ -186,21 +203,23 @@ fn shift_and_command_clicks_select_brushes_for_delete_and_duplicate() {
click(&ctx, &mut app, tiles[1].center(), egui::Modifiers::COMMAND, 3.0, show);
let out = frame_with(&ctx, &mut app, vec![], egui::Modifiers::NONE, 3.5, show);
assert_eq!(lit_tiles(&out), [0, 2]);
let undo = |app: &VectorcraftApp| app.session.active().unwrap().history.undo.len();
let before = (names(&mut app), undo(&app));
let before = (names(&mut app), undo_count(&app));
menu_click(&ctx, &mut app, "Duplicate Brush", 4.0);
let copies = [format!("{} copy", three[0]), format!("{} copy", three[2])];
assert_eq!(names(&mut app)[before.0.len()..], copies);
assert_eq!(undo(&app), before.1 + 1);
assert_eq!(undo_count(&app), before.1 + 1);
// The bottom bar's last button, Delete Brush.
let out = frame_with(&ctx, &mut app, vec![], egui::Modifiers::NONE, 5.0, show);
let trash = clickable(&out).last().unwrap().1.center();
click(&ctx, &mut app, trash, egui::Modifiers::NONE, 6.0, show);
delete_button(&ctx, &mut app, egui::Modifiers::NONE, 5.0);
assert_delete_dialog(&app, "Delete these 2 brushes?");
assert_eq!(names(&mut app), [before.0.clone(), copies.to_vec()].concat());
assert_eq!(brush_of(&app, rect).as_deref(), Some(three[0].as_str()));
assert_eq!(undo_count(&app), before.1 + 1);
crate::dialogs::confirm(&mut app).unwrap();
let left = names(&mut app);
assert!(!left.contains(&three[0]) && !left.contains(&three[2]) && left.contains(&three[1]), "{left:?}");
assert!(copies.iter().all(|c| left.contains(c)), "{left:?}");
assert_eq!(brush_of(&app, rect), None);
assert_eq!(undo(&app), before.1 + 2);
assert_eq!(undo_count(&app), before.1 + 2);
}
/// Selecting other art ends the multi-selection: selecting the first art again does not bring
@@ -231,6 +250,9 @@ fn selecting_other_art_ends_the_brush_multi_selection() {
let out = frame_with(&ctx, &mut app, vec![], egui::Modifiers::NONE, 4.0, show);
assert_eq!(lit_tiles(&out), [1]);
menu_click(&ctx, &mut app, "Delete Brush", 5.0);
assert_delete_dialog(&app, &format!("Delete the brush “{}”?", three[1]));
assert!(names(&mut app).contains(&three[1]));
crate::dialogs::confirm(&mut app).unwrap();
let left = names(&mut app);
assert!(left.contains(&three[0]) && !left.contains(&three[1]) && left.contains(&three[2]), "{left:?}");
assert_eq!((brush_of(&app, a).as_deref(), brush_of(&app, c)), (Some(three[0].as_str()), None));
@@ -297,10 +319,57 @@ fn select_all_unused_selects_the_brushes_no_stroke_paints_with() {
let on: Vec<usize> = (0..tiles.len()).filter(|i| lit(&ctx, &out).contains(&tiles[*i])).collect();
assert_eq!(on, (0..tiles.len()).filter(|i| *i != 1).collect::<Vec<_>>());
menu_click(&ctx, &mut app, "Delete Brush", 3.0);
assert_delete_dialog(&app, &format!("Delete these {} brushes?", list.len() - 1));
assert_eq!(names(&mut app).len(), list.len());
crate::dialogs::confirm(&mut app).unwrap();
assert_eq!(names(&mut app), [list[1].0.clone()]);
assert_eq!(brush_of(&app, a), Some(list[1].0.clone()));
}
#[test]
fn delete_button_cancel_keeps_selected_brushes_strokes_and_history() {
let mut app = VectorcraftApp::new(Session::new(), Default::default());
app.run("file.new", json!({"width": 200, "height": 100})).unwrap();
let rect = NodeId(app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 50, "height": 40})).unwrap()["id"].as_u64().unwrap());
let ctx = egui::Context::default();
ctx.enable_accesskit();
let (three, _) = select_three(&ctx, &mut app, rect);
let before = (names(&mut app), brush_of(&app, rect), undo_count(&app));
delete_button(&ctx, &mut app, egui::Modifiers::NONE, 3.0);
assert_delete_dialog(&app, "Delete these 3 brushes?");
assert_eq!((names(&mut app), brush_of(&app, rect), undo_count(&app)), before);
crate::dialogs::cancel(&mut app);
assert!(app.ui.dialog.is_none());
assert_eq!((names(&mut app), brush_of(&app, rect), undo_count(&app)), before);
assert_eq!(before.1.as_deref(), Some(three[0].as_str()));
app.run("brush.delete", json!({"name": three[0]})).unwrap();
assert!(app.ui.dialog.is_none());
assert_eq!(undo_count(&app), before.2 + 1);
app.run("brush.delete", json!({"names": [three[1], three[2]]})).unwrap();
assert!(app.ui.dialog.is_none());
assert_eq!(undo_count(&app), before.2 + 2);
}
#[test]
fn alt_delete_button_removes_selected_brushes_without_dialog() {
let mut app = VectorcraftApp::new(Session::new(), Default::default());
app.run("file.new", json!({"width": 200, "height": 100})).unwrap();
let rect = NodeId(app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 50, "height": 40})).unwrap()["id"].as_u64().unwrap());
let ctx = egui::Context::default();
ctx.enable_accesskit();
let (three, _) = select_three(&ctx, &mut app, rect);
let before = undo_count(&app);
delete_button(&ctx, &mut app, egui::Modifiers::NONE, 3.0);
assert_delete_dialog(&app, "Delete these 3 brushes?");
crate::dialogs::cancel(&mut app);
delete_button(&ctx, &mut app, egui::Modifiers::ALT, 4.0);
assert!(app.ui.dialog.is_none());
let left = names(&mut app);
assert!(three.iter().all(|name| !left.contains(name)));
assert_eq!(brush_of(&app, rect), None);
assert_eq!(undo_count(&app), before + 1);
}
#[test]
fn art_dropped_on_the_panel_becomes_an_art_brush_and_a_brush_drags_out() {
let mut app = VectorcraftApp::new(Session::new(), Default::default());
+1
View File
@@ -962,6 +962,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
Hue Shift set by `brush.new` or `brush.options` without a key color takes the color the art's fills and strokes use most, as Brush Options already offers it, following the Brushes page, where the key color defaults to the color that stands out most in the art (<https://web.archive.org/web/20241230205225/https://helpx.adobe.com/illustrator/using/brushes.html>). The page does not state how prominence is measured. Counting the fills and strokes that use each solid color, with the first one found winning a tie and black for art with no solid color, is VectorCraft's own rule. A `brush.options` edit that gives only the method keeps the brush's key color, and one that gives only an art brush's start guide keeps its end guide. Evidence: `tests_brushsym::hue_shift_without_a_key_takes_the_most_used_art_color`, `tests_brushsym::art_brush_options_keep_the_scale_fields_an_edit_leaves_out`.
Bristle Brush Options and `brush.new` take a bristle brush name of at most 31 characters, as the Brushes page states for the Bristle Brush Options. The Name field stops taking characters at 31, and `brush.new` and `brush.options` refuse a longer new name with an error that names the limit. The page does not say how the limit counts characters or what happens to longer names already saved; counting characters and the number added to a taken name, keeping longer saved names, and leaving `brush.duplicate` unlimited are VectorCraft's own rules. A bristle brush that a document or a saved file already holds under a longer name keeps it, and the other brush types take a name of any length. Test: `bristle_brush_names_have_at_most_31_characters` in `tests/brushsym.rs`. Public reference: [Brushes](https://web.archive.org/web/20241230205225/https://helpx.adobe.com/illustrator/using/brushes.html) (archived copy).
In the Brushes panel, Shift-click selects a range of brushes and Ctrl/Cmd-click adds or removes one. Delete Brush and Duplicate Brush then act on all of them (`brush.delete {names}`, `brush.duplicate {names}`).
The bottom Delete Brush button and the panel menu ask before deleting; Alt-click (Option-click on macOS) on the button deletes without asking.
Document Info has a Brushes category after Graphic Styles. It lists the Brushes panel's brushes, or with Selection Only it lists the brushes that the selected objects' strokes use. The Document Info report and Package's report list them too (`document.info {category: "brushes"}`, `brushNames`).
Document Info has a Repeat Objects category after Embedded Images. It lists each radial, grid and mirror Repeat object by name with its kind and the number of instances it makes. For a grid it gives the rows and columns instead, followed by the number of instances it draws when that is fewer. With Selection Only it lists the Repeat objects in the selection. The Document Info report and Package's report list them too (`document.info {category: "repeats"}`, `repeatObjects`).
The Brushes panel menu's Select All Unused selects the brushes that are not used in the document, as the [Brushes](https://web.archive.org/web/20241230205225/https://helpx.adobe.com/illustrator/using/brushes.html) page (archived copy) describes. A brush counts as used when a stroke paints with it in the art (opacity masks included), in symbols, graph designs, pattern tiles or graphic styles, or in the art of a used brush (`brush.unused`).