Brushes: Delete Brush asks Yes or No for unused brushes, and Remove Strokes or Cancel for brushes in use

The question for brushes no stroke uses reads "Delete the brush selection?" with Yes and No, from the bottom button and the panel menu, and Alt-click (Option-click on macOS) on the button still deletes them without asking. When a stroke uses one of the brushes, the button, Alt-click and the menu item ask with Remove Strokes and Cancel; Remove Strokes deletes the brushes and the strokes that used them become plain strokes.

brush.unused also counts the tile that pattern editing's Cancel puts back, which brush.delete already clears, so the panel asks the in-use question for a brush only that tile uses.
This commit is contained in:
dotCooCoo
2026-11-01 01:21:54 +07:00
committed by bflatastic
parent 79188b3593
commit b1aa576764
36 changed files with 367 additions and 81 deletions
+1 -1
View File
@@ -125,7 +125,7 @@ pub fn specs() -> Vec<CommandSpec> {
"Select All Unused",
[],
None,
"{} → {names: [the brushes no stroke paints with, in library order]} (the Brushes panel selects them): strokes in the art (opacity masks included), symbols, graph designs, pattern tiles and graphic styles count, and so do strokes in the art of a brush that is used",
"{} → {names: [the brushes no stroke paints with, in library order]} (the Brushes panel selects them): strokes in the art (opacity masks included), symbols, graph designs, pattern tiles (with the tiles that pattern editing's Cancel puts back) and graphic styles count, and so do strokes in the art of a brush that is used",
has_doc,
brush_unused
),
+4 -3
View File
@@ -362,14 +362,15 @@ fn art_brushes(n: &Node, out: &mut Vec<String>) {
}
/// The brushes no stroke paints with. Strokes in the art (opacity masks included), symbols, graph
/// designs, pattern tiles and graphic styles count, and so do the strokes in the art of every brush
/// that a counted stroke paints with.
/// designs, pattern tiles and the tiles that pattern editing's Cancel puts back, and graphic styles
/// count, and so do the strokes in the art of every brush that a counted stroke paints with.
pub(super) fn brush_unused(s: &mut Session, _: &Value) -> Result<Value> {
let d = &s.doc()?.doc;
let lib = brush::library(d);
let mut todo = vec![];
let trees = d.layers.iter().chain(d.symbols.iter().map(|s| &s.art)).chain(d.graph_designs.iter().map(|g| &g.art));
trees.chain(d.patterns.iter().flat_map(|p| &p.art)).for_each(|n| art_brushes(n, &mut todo));
let patterns = d.patterns.iter().chain(d.pattern_edit.iter().filter_map(|pe| pe.original.as_ref()));
trees.chain(patterns.flat_map(|p| &p.art)).for_each(|n| art_brushes(n, &mut todo));
d.graphic_styles.iter().for_each(|g| stroke_brushes(&g.appearance.items, &mut todo));
let mut used = BTreeSet::new();
while let Some(name) = todo.pop() {
+25
View File
@@ -457,6 +457,31 @@ fn brush_unused_lists_the_brushes_no_stroke_paints_with() {
assert_eq!(unused(&mut s), ["3 pt. Round", "6 pt. Flat", "Charcoal"]);
}
/// A stroke in the tile that pattern editing's Cancel puts back counts as a use, as `brush.delete`
/// clears it there.
#[test]
fn brush_unused_counts_pattern_edit_originals() {
let mut s = session();
let id = rect(&mut s, 10.0, 10.0, 40.0, 20.0);
s.execute("brush.apply", &json!({"name": "6 pt. Flat", "ids": [id.0]})).unwrap();
let name = s.execute("object.pattern.make", &json!({"ids": [id.0], "edit": false})).unwrap()["name"].clone();
s.execute("brush.remove", &json!({"ids": [id.0]})).unwrap();
s.execute("object.pattern.edit", &json!({ "name": name })).unwrap();
// The tile's copies in the editing layer are selected; only the tile kept for Cancel paints with the brush.
s.execute("brush.remove", &json!({})).unwrap();
let d = std::sync::Arc::make_mut(&mut s.doc_mut().unwrap().doc);
let original = d.pattern_edit.as_ref().and_then(|pe| pe.original.as_ref()).expect("pattern edit original");
assert_eq!(original.art.first().and_then(|n| n.appearance.stroke()).and_then(|k| k.brush.as_deref()), Some("6 pt. Flat"));
for pattern in &mut d.patterns {
for art in &mut pattern.art {
if let Some(stroke) = std::sync::Arc::make_mut(art).appearance.stroke_mut() {
stroke.brush = None;
}
}
}
assert!(!unused(&mut s).contains(&"6 pt. Flat".to_string()));
}
/// A brush counts as used when a stroke paints with it in an opacity mask, a symbol, a graph
/// design, a pattern tile, a graphic style, or the art of a brush that is used. The art of a brush
/// that is not used, its own strokes included, does not count.
+2
View File
@@ -12,6 +12,7 @@ pub mod action_set_options;
pub mod artboard_options;
pub mod blend_options;
mod brush_art_options;
pub mod brush_delete;
pub mod brush_options;
pub mod color_balance;
pub mod color_guide_options;
@@ -293,6 +294,7 @@ registry! {
FindFont: ["findFont"] => DialogSpec::window(crate::find_font::show, |app, _| crate::find_font::confirm(app)),
SwatchOptions: [swatch_options::KIND] => swatch_options::SPEC,
Confirm: [confirm::KIND] => confirm::SPEC,
BrushDelete: [brush_delete::KIND] => brush_delete::SPEC,
Message: [confirm::MESSAGE] => confirm::MESSAGE_SPEC,
Replace: [confirm::REPLACE] => confirm::REPLACE_SPEC,
NewSwatch: [new_swatch::KIND] => new_swatch::SPEC,
@@ -0,0 +1,61 @@
//! The Brushes panel's Delete Brush question ([`ask`]): Yes or No when none of the brushes is in
//! use, else Remove Strokes or Cancel. Yes and Remove Strokes run `brush.delete`, which leaves the
//! strokes that used the brushes as plain strokes.
//!
//! Fields: `message` (the question, shown as the heading), `used` (whether a brush is in use) and
//! `params` (the `brush.delete` params).
use serde_json::{Value, json};
use super::{DialogAction, DialogSpec};
use crate::VectorcraftApp;
use crate::state::Dialog;
use crate::widgets;
/// The dialog kind of the Delete Brush question.
pub const KIND: &str = "brushDelete";
pub(super) const SPEC: DialogSpec =
DialogSpec { heading: |d| d.str("message"), body_with_actions: Some(body), confirm, ok: None, max_width: Some(540.0), ..DialogSpec::FORM };
/// Ask whether to delete the brushes `params` names (`used`: one of them is in use).
pub fn ask(app: &mut VectorcraftApp, params: Value, used: bool) {
let message = if used {
tl!("One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed.")
} else {
tl!("Delete the brush selection?")
};
app.ui.dialog = Some(Dialog::new(KIND, json!({"params": params, "used": used, "message": message})));
}
fn body(_: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> DialogAction {
let mut action = DialogAction::None;
ui.horizontal(|ui| {
if d.bool("used") {
if widgets::primary_button_enabled(ui, tl!("Remove Strokes"), true).clicked() {
action = DialogAction::Confirm;
}
if widgets::secondary_button(ui, tl!("Cancel")).clicked() {
action = DialogAction::Cancel;
}
} else {
if widgets::primary_button_enabled(ui, tl!("Yes"), true).clicked() {
action = DialogAction::Confirm;
}
if widgets::secondary_button(ui, tl!("No")).clicked() {
action = DialogAction::Cancel;
}
}
});
action
}
/// Closes before running, so a dialog the command opens stays open.
fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result<Value, String> {
let params = d.fields.get("params").cloned().unwrap_or_else(|| json!({}));
app.ui.dialog = None;
app.run("brush.delete", params)
}
#[cfg(test)]
mod tests;
@@ -0,0 +1,71 @@
use egui::{Pos2, Rect, pos2, vec2};
use serde_json::json;
use vectorcraft_doc::NodeId;
use vectorcraft_engine::Session;
use super::*;
fn frame(ctx: &egui::Context, app: &mut VectorcraftApp, events: Vec<egui::Event>, time: f64) -> egui::FullOutput {
let screen_rect = Some(Rect::from_min_size(Pos2::ZERO, vec2(800.0, 600.0)));
let input = egui::RawInput { screen_rect, time: Some(time), events, ..Default::default() };
let mut out = ctx.run_ui(input, |ui| crate::dialogs::show(app, ui.ctx()));
out.textures_delta.clear();
out
}
fn press(ctx: &egui::Context, app: &mut VectorcraftApp, label: &str, time: f64) {
frame(ctx, app, vec![], time);
let out = frame(ctx, app, vec![], time + 0.1);
let nodes = &out.platform_output.accesskit_update.as_ref().expect("dialog accessibility tree").nodes;
let button = nodes.iter().find(|(_, node)| node.label().map(str::trim) == Some(label));
let bounds = button.and_then(|(_, node)| node.bounds()).expect("visible button");
let at = pos2(((bounds.x0 + bounds.x1) / 2.0) as f32, ((bounds.y0 + bounds.y1) / 2.0) as f32);
let button = |pressed| egui::Event::PointerButton { pos: at, button: egui::PointerButton::Primary, pressed, modifiers: egui::Modifiers::NONE };
frame(ctx, app, vec![egui::Event::PointerMoved(at), button(true), button(false)], time + 0.2);
}
fn context() -> egui::Context {
let ctx = egui::Context::default();
ctx.enable_accesskit();
crate::theme::install_fonts(&ctx);
crate::theme::apply(&ctx, Default::default());
ctx
}
#[test]
fn visible_yes_and_no_buttons_control_unused_deletion() {
let mut app = VectorcraftApp::new(Session::new(), Default::default());
app.run("file.new", json!({"width": 200, "height": 100})).unwrap();
let name = app.run("brush.list", json!({})).unwrap()["brushes"][0]["name"].as_str().unwrap().to_string();
let ctx = context();
let before = app.session.active().unwrap().history.undo.len();
ask(&mut app, json!({"name": name}), false);
press(&ctx, &mut app, "No", 0.0);
assert!(app.ui.dialog.is_none());
assert_eq!(app.session.active().unwrap().history.undo.len(), before);
assert!(app.run("brush.get", json!({"name": name})).is_ok());
ask(&mut app, json!({"name": name}), false);
press(&ctx, &mut app, "Yes", 1.0);
assert!(app.run("brush.get", json!({"name": name})).is_err());
assert_eq!(app.session.active().unwrap().history.undo.len(), before + 1);
}
#[test]
fn visible_remove_strokes_and_cancel_buttons_control_used_deletion() {
for choice in ["Cancel", "Remove Strokes"] {
let mut app = VectorcraftApp::new(Session::new(), Default::default());
app.run("file.new", json!({"width": 200, "height": 100})).unwrap();
let path = NodeId(app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 60, "height": 40})).unwrap()["id"].as_u64().unwrap());
let name = "10 pt. Oval";
app.run("brush.apply", json!({"name": name})).unwrap();
let before = app.session.active().unwrap().history.undo.len();
let ctx = context();
ask(&mut app, json!({"name": name}), true);
press(&ctx, &mut app, choice, 0.0);
assert!(app.ui.dialog.is_none());
assert_eq!(app.session.active().unwrap().history.undo.len(), before + usize::from(choice != "Cancel"));
let stroke = app.session.active().unwrap().doc.node(path).and_then(|n| n.appearance.stroke()).unwrap();
assert_eq!(stroke.brush.as_deref(), (choice == "Cancel").then_some(name));
assert!(stroke.width > 0.0);
}
}
+5 -2
View File
@@ -742,8 +742,6 @@ font-technology Variable Променливи
Yes Да
No Не
Delete Tab Изтриване на табулация
Delete the brush “{name}”? Да се изтрие четката „{name}“?
Strokes using deleted brushes become plain strokes. Щрихите, които използват изтритите четки, стават обикновени щрихи.
Delete the graphic style “{name}”? Изтриване на графичния стил „{name}“?
Delete Theme Изтриване на тема
Delete theme Изтриване на тема
@@ -5901,6 +5899,11 @@ breakText Glyphs Глифи
Gap 4 Разстояние 4
Dash 5 Тире 5
Gap 5 Разстояние 5
Delete the brush selection? Да се изтрият избраните четки?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. Една или повече от четките се използват и не могат да бъдат изтрити, докато щрихите им не бъдат разширени или премахнати.
Remove Strokes Премахване на щрихи
Yes Да
No Не
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools Инструменти за подреждане
Arrange in Grid… Подреждане в мрежа…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5
View File
@@ -5931,6 +5931,11 @@ shape Open Otvoren
@msg `follow` must be "average", "a" or "b" `follow` mora biti "average", "a" ili "b"
@msg the paths have nothing to merge there putanje tu nemaju šta spojiti
@msg the path is locked or hidden putanja je zaključana ili skrivena
Delete the brush selection? Izbrisati odabrane četkice?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. Jedna ili više četkica se koristi i ne mogu se izbrisati dok se njihovi potezi ne prošire ili uklone.
Remove Strokes Ukloni poteze
Yes Da
No Ne
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools Alati za raspoređivanje
Arrange in Grid… Rasporedi u mrežu…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5 -2
View File
@@ -1059,8 +1059,6 @@
Yes Sí
No No
Delete Tab Suprimeix la tabulació
Delete the brush “{name}”? Voleu suprimir el pinzell “{name}”?
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
@@ -6001,6 +5999,11 @@ breakText Glyphs Glifs
Gap 4 Separació 4
Dash 5 Guió 5
Gap 5 Separació 5
Delete the brush selection? Voleu suprimir la selecció de pinzells?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. Un o més pinzells estan en ús i no es poden suprimir fins que els traços s’expandeixin o s’eliminin.
Remove Strokes Elimina els traços
Yes Sí
No No
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools Eines d’organització
Arrange in Grid… Organitza en quadrícula…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5 -2
View File
@@ -1558,8 +1558,6 @@ 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}“?
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})?
@@ -6010,6 +6008,11 @@ breakText Glyphs Glyfy
Gap 4 Mezera 4
Dash 5 Čárka 5
Gap 5 Mezera 5
Delete the brush selection? Odstranit vybrané štětce?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. Jeden nebo více štětců se používá a nelze je odstranit, dokud jejich tahy nebudou rozloženy nebo odebrány.
Remove Strokes Odebrat tahy
Yes Ano
No Ne
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools Nástroje uspořádání
Arrange in Grid… Uspořádat do mřížky…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5 -2
View File
@@ -1472,8 +1472,6 @@ 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}«?
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?
@@ -5915,6 +5913,11 @@ breakText Glyphs Glyffer
Gap 4 Mellemrum 4
Dash 5 Streg 5
Gap 5 Mellemrum 5
Delete the brush selection? Slet de valgte pensler?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. En eller flere pensler er i brug og kan ikke slettes, før deres streger er udvidet eller fjernet.
Remove Strokes Fjern streger
Yes Ja
No Nej
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools Arrangeringsværktøjer
Arrange in Grid… Arrangér i gitter…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5 -2
View File
@@ -736,8 +736,6 @@ font-technology Variable Variabel
Yes Ja
No Nein
Delete Tab Tabulator löschen
Delete the brush “{name}”? Pinsel „{name}“ 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
@@ -5596,6 +5594,11 @@ breakText Glyphs Glyphen
Gap 4 Lücke 4
Dash 5 Strich 5
Gap 5 Lücke 5
Delete the brush selection? Ausgewählte Pinsel löschen?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. Ein oder mehrere Pinsel werden verwendet und können erst gelöscht werden, wenn ihre Konturen umgewandelt oder entfernt wurden.
Remove Strokes Konturen entfernen
Yes Ja
No Nein
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools Anordnungswerkzeuge
Arrange in Grid… In Raster anordnen…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5 -2
View File
@@ -716,8 +716,6 @@ font-technology Variable Μεταβλητές
Yes Ναι
No Όχι
Delete Tab Διαγραφή στηλοθέτη
Delete the brush “{name}”? Να διαγραφεί το πινέλο «{name}»;
Strokes using deleted brushes become plain strokes. Οι πινελιές που χρησιμοποιούν διαγραμμένα πινέλα γίνονται απλές πινελιές.
Delete the graphic style “{name}”? Να διαγραφεί το στυλ γραφικών «{name}»;
Delete Theme Διαγραφή θέματος
Delete theme Διαγραφή θέματος
@@ -5963,6 +5961,11 @@ breakText Glyphs Γλύφοι
Gap 4 Διάκενο 4
Dash 5 Παύλα 5
Gap 5 Διάκενο 5
Delete the brush selection? Να διαγραφούν τα επιλεγμένα πινέλα;
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. Ένα ή περισσότερα πινέλα χρησιμοποιούνται και δεν μπορούν να διαγραφούν μέχρι να επεκταθούν ή να αφαιρεθούν τα περιγράμματά τους.
Remove Strokes Αφαίρεση περιγραμμάτων
Yes Ναι
No Όχι
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools Εργαλεία διάταξης
Arrange in Grid… Διάταξη σε πλέγμα…
Can't render this file because it contains an unexpected character in line 164 and column 5.
+5 -2
View File
@@ -757,8 +757,6 @@ font-technology Variable Variables
Yes Sí
No No
Delete Tab Eliminar tabulación
Delete the brush “{name}”? ¿Eliminar el pincel “{name}”?
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
@@ -5953,6 +5951,11 @@ breakText Glyphs Glifos
Gap 4 Hueco 4
Dash 5 Guion 5
Gap 5 Hueco 5
Delete the brush selection? ¿Eliminar los pinceles seleccionados?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. Uno o más pinceles están en uso y no se pueden eliminar hasta expandir o quitar sus trazos.
Remove Strokes Quitar trazos
Yes Sí
No No
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools Herramientas de organización
Arrange in Grid… Organizar en cuadrícula…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5 -2
View File
@@ -1532,8 +1532,6 @@ 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}”?
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?
@@ -6041,6 +6039,11 @@ breakText Glyphs Merkit
Gap 4 Väli 4
Dash 5 Viiva 5
Gap 5 Väli 5
Delete the brush selection? Poistetaanko valitut siveltimet?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. Yksi tai useampi sivellin on käytössä eikä niitä voi poistaa ennen kuin niiden viivat on laajennettu tai poistettu.
Remove Strokes Poista viivat
Yes Kyllä
No Ei
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools Järjestelytyökalut
Arrange in Grid… Järjestä ruudukkoon…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5 -3
View File
@@ -759,8 +759,6 @@ font-technology Variable Variables
Yes Oui
No Non
Delete Tab Supprimer la tabulation
Delete the brush “{name}”? Supprimer la forme « {name} » ?
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
@@ -3703,7 +3701,6 @@ imageTrace Outline Contour
@plural {n} item|{n} items {n} élément|{n} éléments
@plural Color Table ({n} color)|Color Table ({n} colors) Table des couleurs ({n} couleur)|Table des couleurs ({n} couleurs)
@plural Delete this {n} graphic style?|Delete these {n} graphic styles? Supprimer {n} style graphique ?|Supprimer ces {n} styles graphiques ?
@plural Delete this {n} brush?|Delete these {n} brushes? Supprimer {n} forme ?|Supprimer ces {n} formes ?
@plural {n} Layer|{n} Layers {n} calque|{n} calques
@plural {n} Object|{n} Objects {n} objet|{n} objets
@plural {n} area highlighted|{n} areas highlighted {n} zone mise en évidence|{n} zones mises en évidence
@@ -6009,6 +6006,11 @@ breakText Glyphs Glyphes
Gap 4 Écart 4
Dash 5 Tiret 5
Gap 5 Écart 5
Delete the brush selection? Supprimer les formes sélectionnées ?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. Une ou plusieurs formes sont utilisées et ne peuvent pas être supprimées tant que leurs contours ne sont pas décomposés ou supprimés.
Remove Strokes Supprimer les contours
Yes Oui
No Non
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools Outils de disposition
Arrange in Grid… Disposer en grille…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5 -2
View File
@@ -981,8 +981,6 @@ 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}”?
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
@@ -5997,6 +5995,11 @@ breakText Glyphs Glifák
Gap 4 Hézag 4
Dash 5 Vonal 5
Gap 5 Hézag 5
Delete the brush selection? Törlöd a kijelölt ecseteket?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. Egy vagy több ecset használatban van, és nem törölhető, amíg a körvonalait ki nem bontod vagy el nem távolítod.
Remove Strokes Körvonalak eltávolítása
Yes Igen
No Nem
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools Elrendezőeszközök
Arrange in Grid… Elrendezés rácsba…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5 -2
View File
@@ -1472,8 +1472,6 @@ 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}”?
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?
@@ -5915,6 +5913,11 @@ breakText Glyphs Glif
Gap 4 Jarak 4
Dash 5 Garis 5
Gap 5 Jarak 5
Delete the brush selection? Hapus kuas yang dipilih?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. Satu atau beberapa kuas sedang digunakan dan tidak dapat dihapus sampai stroke-nya diperluas atau dihapus.
Remove Strokes Hapus Stroke
Yes Ya
No Tidak
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools Alat penyusunan
Arrange in Grid… Susun dalam grid…
Can't render this file because it contains an unexpected character in line 2176 and column 5.
+5 -2
View File
@@ -757,8 +757,6 @@ font-technology Variable Variabili
Yes Sì
No No
Delete Tab Elimina tabulazione
Delete the brush “{name}”? Eliminare il pennello “{name}”?
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
@@ -5630,6 +5628,11 @@ breakText Glyphs Glifi
Gap 4 Spazio 4
Dash 5 Tratto 5
Gap 5 Spazio 5
Delete the brush selection? Eliminare i pennelli selezionati?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. Uno o più pennelli sono in uso e non possono essere eliminati finché le tracce non vengono espanse o rimosse.
Remove Strokes Rimuovi tracce
Yes Sì
No No
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools Strumenti di disposizione
Arrange in Grid… Disponi in griglia…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5 -2
View File
@@ -1308,8 +1308,6 @@ font-technology Variable バリアブル
Delete Transparency Flattener Preset 透明の分割・統合プリセットを削除
Delete Width Point 幅ポイントを削除
Delete Workspace ワークスペースを削除
Delete the brush “{name}”? ブラシ「{name}」を削除しますか?
Strokes using deleted brushes become plain strokes. 削除したブラシを使用する線は通常の線になります。
Delete the graphic style “{name}”? グラフィックスタイル「{name}」を削除しますか?
Delete theme テーマを削除
Delete this saved preset この保存済みプリセットを削除
@@ -5696,6 +5694,11 @@ breakText Glyphs 字形
Gap 4 間隔 4
Dash 5 線分 5
Gap 5 間隔 5
Delete the brush selection? 選択したブラシを削除しますか?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. 使用中のブラシが含まれています。線を拡張または削除するまでブラシを削除できません。
Remove Strokes 線を削除
Yes はい
No いいえ
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools 配置ツール
Arrange in Grid… グリッドに配置…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5 -2
View File
@@ -1389,8 +1389,6 @@ lighting Light სინათლე
Reverse Gradient გრადიენტის შებრუნება
Save to Swatches ფერის ნიმუშებში შენახვა
Delete Graphic Style (Alt-click: without asking) გრაფიკული სტილის წაშლა (Alt-დაწკაპუნებით: დაუკითხავად)
Delete the brush “{name}”? წაიშალოს ფუნჯი „{name}“?
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})?
@@ -6000,6 +5998,11 @@ breakText Glyphs გლიფები
Gap 4 შუალედი 4
Dash 5 ტირე 5
Gap 5 შუალედი 5
Delete the brush selection? წაიშალოს არჩეული ფუნჯები?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. ერთი ან მეტი ფუნჯი გამოიყენება და მათი წაშლა შეუძლებელია, სანამ კონტურები არ გაიშლება ან არ მოიხსნება.
Remove Strokes კონტურების მოხსნა
Yes დიახ
No არა
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools განლაგების ხელსაწყოები
Arrange in Grid… ბადეში განლაგება…
Can't render this file because it is too large.
+5 -2
View File
@@ -1299,8 +1299,6 @@ font-technology Variable 가변
Delete Transparency Flattener Preset 투명도 병합 프리셋 삭제
Delete Width Point 너비 포인트 삭제
Delete Workspace 작업 영역 삭제
Delete the brush “{name}”? 브러시 “{name}”을(를) 삭제하시겠습니까?
Strokes using deleted brushes become plain strokes. 삭제된 브러시를 사용한 선은 일반 선이 됩니다.
Delete the graphic style “{name}”? 그래픽 스타일 “{name}”을(를) 삭제하시겠습니까?
Delete theme 테마 삭제
Delete this saved preset 저장된 이 프리셋 삭제
@@ -5593,6 +5591,11 @@ breakText Glyphs 글리프
Gap 4 간격 4
Dash 5 대시 5
Gap 5 간격 5
Delete the brush selection? 선택한 브러시를 삭제하시겠습니까?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. 하나 이상의 브러시가 사용 중이며 선을 확장하거나 제거할 때까지 삭제할 수 없습니다.
Remove Strokes 선 제거
Yes 예
No 아니요
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools 배치 도구
Arrange in Grid… 격자로 배치…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5 -2
View File
@@ -1486,8 +1486,6 @@ 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?
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?
@@ -5959,6 +5957,11 @@ breakText Glyphs Glyphs
Gap 4 Tussenruimte 4
Dash 5 Streepje 5
Gap 5 Tussenruimte 5
Delete the brush selection? Geselecteerde kwasten verwijderen?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. Een of meer kwasten zijn in gebruik en kunnen pas worden verwijderd als hun contouren zijn uitgebreid of verwijderd.
Remove Strokes Contouren verwijderen
Yes Ja
No Nee
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools Schikgereedschappen
Arrange in Grid… In raster schikken…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5 -2
View File
@@ -1472,8 +1472,6 @@ 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}”?
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})?
@@ -5866,6 +5864,11 @@ breakText Glyphs Glify
Gap 4 Odstęp 4
Dash 5 Kreska 5
Gap 5 Odstęp 5
Delete the brush selection? Usunąć zaznaczone pędzle?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. Co najmniej jeden pędzel jest używany i nie można go usunąć, dopóki jego obrysy nie zostaną rozszerzone lub usunięte.
Remove Strokes Usuń obrysy
Yes Tak
No Nie
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools Narzędzia układania
Arrange in Grid… Ułóż w siatce…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5 -2
View File
@@ -674,8 +674,6 @@
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}”?
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
@@ -5930,6 +5928,11 @@ breakText Glyphs Glifos
Gap 4 Lacuna 4
Dash 5 Traço 5
Gap 5 Lacuna 5
Delete the brush selection? Excluir os pincéis selecionados?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. Um ou mais pincéis estão em uso e não podem ser excluídos até que seus traçados sejam expandidos ou removidos.
Remove Strokes Remover Traçados
Yes Sim
No Não
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools Ferramentas de Organização
Arrange in Grid… Organizar em Grade…
Can't render this file because it contains an unexpected character in line 6 and column 47.
+5 -2
View File
@@ -755,8 +755,6 @@ font-technology Variable Вариативные
Yes Да
No Нет
Delete Tab Удалить табуляцию
Delete the brush “{name}”? Удалить кисть «{name}»?
Strokes using deleted brushes become plain strokes. Обводки, использующие удалённые кисти, становятся обычными обводками.
Delete the graphic style “{name}”? Удалить графический стиль «{name}»?
Delete Theme Удалить тему
Delete theme Удалить тему
@@ -5988,6 +5986,11 @@ breakText Glyphs Глифы
Gap 4 Зазор 4
Dash 5 Штрих 5
Gap 5 Зазор 5
Delete the brush selection? Удалить выбранные кисти?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. Одна или несколько кистей используются и не могут быть удалены, пока их обводки не будут разобраны или убраны.
Remove Strokes Убрать обводки
Yes Да
No Нет
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools Инструменты размещения
Arrange in Grid… Разместить по сетке…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5 -2
View File
@@ -744,8 +744,6 @@ font-technology Variable Variabla
Yes Ja
No Nej
Delete Tab Radera tabb
Delete the brush “{name}”? Radera penseln ”{name}”?
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
@@ -6063,6 +6061,11 @@ breakText Glyphs Glyfer
Gap 4 Mellanrum 4
Dash 5 Streck 5
Gap 5 Mellanrum 5
Delete the brush selection? Radera de valda penslarna?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. En eller flera penslar används och kan inte raderas förrän deras konturer har expanderats eller tagits bort.
Remove Strokes Ta bort konturer
Yes Ja
No Nej
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools Ordningsverktyg
Arrange in Grid… Ordna i rutnät…
Can't render this file because it contains an unexpected character in line 5 and column 25.
+5 -2
View File
@@ -736,8 +736,6 @@ 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?
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
@@ -5968,6 +5966,11 @@ breakText Glyphs Glifler
Gap 4 Boşluk 4
Dash 5 Çizgi 5
Gap 5 Boşluk 5
Delete the brush selection? Seçili fırçalar silinsin mi?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. Bir veya daha fazla fırça kullanılıyor ve konturları genişletilmeden veya kaldırılmadan silinemez.
Remove Strokes Konturları Kaldır
Yes Evet
No Hayır
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools Düzenleme Araçları
Arrange in Grid… Izgarada Düzenle…
Can't render this file because it contains an unexpected character in line 5 and column 25.
+5 -2
View File
@@ -751,8 +751,6 @@ font-technology Variable Варіативні
Yes Так
No Ні
Delete Tab Видалити табуляцію
Delete the brush “{name}”? Видалити пензель «{name}»?
Strokes using deleted brushes become plain strokes. Обведення, що використовують видалені пензлі, стають звичайними обведеннями.
Delete the graphic style “{name}”? Видалити графічний стиль «{name}»?
Delete Theme Видалити тему
Delete theme Видалити тему
@@ -5610,6 +5608,11 @@ breakText Glyphs Гліфи
Gap 4 Проміжок 4
Dash 5 Штрих 5
Gap 5 Проміжок 5
Delete the brush selection? Видалити вибрані пензлі?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. Один або кілька пензлів використовуються і не можуть бути видалені, доки їхні обведення не буде розібрано або забрано.
Remove Strokes Забрати обведення
Yes Так
No Ні
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools Інструменти розміщення
Arrange in Grid… Розмістити сіткою…
Can't render this file because it contains an unexpected character in line 178 and column 5.
+5 -2
View File
@@ -877,8 +877,6 @@ 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}”?
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
@@ -5612,6 +5610,11 @@ breakText Glyphs Glyph
Gap 4 Khoảng hở 4
Dash 5 Nét 5
Gap 5 Khoảng hở 5
Delete the brush selection? Xóa các cọ đã chọn?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. Một hoặc nhiều cọ đang được sử dụng và không thể xóa cho đến khi nét được mở rộng hoặc gỡ bỏ.
Remove Strokes Gỡ nét
Yes Có
No Không
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools Công cụ sắp xếp
Arrange in Grid… Sắp xếp theo lưới…
Can't render this file because it contains an unexpected character in line 200 and column 5.
+5 -2
View File
@@ -700,8 +700,6 @@
Yes 是
No 否
Delete Tab 删除制表位
Delete the brush “{name}”? 要删除画笔“{name}”吗?
Strokes using deleted brushes become plain strokes. 使用已删除画笔的描边会变成普通描边。
Delete the graphic style “{name}”? 要删除图形样式“{name}”吗?
Delete Theme 删除主题
Delete theme 删除主题
@@ -5852,6 +5850,11 @@ breakText Glyphs 字形
Gap 4 间隔 4
Dash 5 虚线 5
Gap 5 间隔 5
Delete the brush selection? 删除选中的画笔吗?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. 一个或多个画笔正在使用中,必须先扩展或移除其描边才能删除。
Remove Strokes 移除描边
Yes 是
No 否
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools 排列工具
Arrange in Grid… 按网格排列…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5 -2
View File
@@ -719,8 +719,6 @@ font-technology Variable 可變
Yes 是
No 否
Delete Tab 刪除定位點
Delete the brush “{name}”? 要刪除筆刷「{name}」嗎?
Strokes using deleted brushes become plain strokes. 使用已刪除筆刷的筆畫會變成一般筆畫。
Delete the graphic style “{name}”? 要刪除繪圖樣式「{name}」嗎?
Delete Theme 刪除主題
Delete theme 刪除主題
@@ -5649,6 +5647,11 @@ breakText Glyphs 字符
Gap 4 間隙 4
Dash 5 虛線 5
Gap 5 間隙 5
Delete the brush selection? 要刪除選取的筆刷嗎?
One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed. 一個或多個筆刷正在使用中,必須先擴展或移除其筆畫才能刪除。
Remove Strokes 移除筆畫
Yes 是
No 否
# Arrange Tools add-on (Object › Plug-ins › Arrange Tools).
Arrange Tools 排列工具
Arrange in Grid… 依格狀排列…
Can't render this file because it contains an unexpected character in line 6 and column 47.
+9 -7
View File
@@ -204,9 +204,15 @@ 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.
/// Delete `names` after asking: Yes or No when none of them is in use, else Remove Strokes or
/// Cancel. With `now` (Alt-click on the bottom button), brushes that are not in use go at once.
fn delete(app: &mut VectorcraftApp, names: &[String], now: bool) {
if now {
let Ok(unused) = app.run("brush.unused", json!({})) else {
return;
};
let unused = unused.get("names").and_then(Value::as_array).cloned().unwrap_or_default();
let used = names.iter().any(|name| !unused.iter().any(|n| n.as_str() == Some(name)));
if now && !used {
run_on(app, "brush.delete", names);
return;
}
@@ -214,11 +220,7 @@ fn delete(app: &mut VectorcraftApp, names: &[String], now: bool) {
[name] => json!({ "name": name }),
_ => json!({ "names": names }),
};
let message = match names {
[name] => crate::i18n::fmt(tl!("Delete the brush “{name}”?"), &[("name", name)]),
_ => crate::i18n::tn(names.len() as u64, "Delete this {n} brush?", "Delete these {n} brushes?"),
};
crate::dialogs::confirm::ask(app, &message, tl!("Strokes using deleted brushes become plain strokes."), "brush.delete", params);
crate::dialogs::brush_delete::ask(app, params, used);
}
/// The names in `list` in the order the panel shows them: by type in [`KINDS`] order, without the
+59 -16
View File
@@ -158,22 +158,25 @@ fn delete_button(ctx: &egui::Context, app: &mut VectorcraftApp, modifiers: egui:
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.kind, crate::dialogs::brush_delete::KIND);
assert_eq!(d.str("message"), message);
assert_eq!(d.str("detail"), "Strokes using deleted brushes become plain strokes.");
assert_eq!(d.bool("used"), message.starts_with("One or more"));
}
/// Deleting several unused brushes asks one question about the selection, with no count in it.
#[test]
fn deleting_five_brushes_uses_polish_many_form() {
fn deleting_five_brushes_uses_one_selection_question() {
let mut app = VectorcraftApp::new(Session::new(), Default::default());
app.run("file.new", json!({"width": 200, "height": 100})).unwrap();
let five: Vec<String> = names(&mut app).into_iter().take(5).collect();
assert_eq!(five.len(), 5);
let previous = crate::i18n::current();
let polish = crate::i18n::Lang::from_code("pl").unwrap();
crate::i18n::set_current(polish);
let mut app = VectorcraftApp::new(Session::new(), Default::default());
let names: Vec<String> = (1..=5).map(|n| format!("Brush {n}")).collect();
delete(&mut app, &names, false);
delete(&mut app, &five, false);
let message = app.ui.dialog.as_ref().map(|d| d.str("message"));
crate::i18n::set_current(previous);
assert_eq!(message, Some("Usunąć 5 pędzli?".to_string()));
assert_eq!(message, Some("Usunąć zaznaczone pędzle?".to_string()));
}
/// Click the panel menu's item `label` (frames at `time`, `time + 0.5`).
@@ -223,7 +226,7 @@ fn shift_and_command_clicks_select_brushes_for_delete_and_duplicate() {
assert_eq!(undo_count(&app), before.1 + 1);
// The bottom bar's last button, Delete Brush.
delete_button(&ctx, &mut app, egui::Modifiers::NONE, 5.0);
assert_delete_dialog(&app, "Delete these 2 brushes?");
assert_delete_dialog(&app, "One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed.");
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);
@@ -263,7 +266,7 @@ 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_delete_dialog(&app, "One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed.");
assert!(names(&mut app).contains(&three[1]));
crate::dialogs::confirm(&mut app).unwrap();
let left = names(&mut app);
@@ -332,7 +335,7 @@ 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_delete_dialog(&app, "Delete the brush selection?");
assert_eq!(names(&mut app).len(), list.len());
crate::dialogs::confirm(&mut app).unwrap();
assert_eq!(names(&mut app), [list[1].0.clone()]);
@@ -349,7 +352,7 @@ fn delete_button_cancel_keeps_selected_brushes_strokes_and_history() {
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_delete_dialog(&app, "One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed.");
assert_eq!((names(&mut app), brush_of(&app, rect), undo_count(&app)), before);
crate::dialogs::cancel(&mut app);
assert!(app.ui.dialog.is_none());
@@ -364,7 +367,7 @@ fn delete_button_cancel_keeps_selected_brushes_strokes_and_history() {
}
#[test]
fn alt_delete_button_removes_selected_brushes_without_dialog() {
fn alt_delete_button_asks_when_a_selected_brush_is_used() {
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());
@@ -373,16 +376,56 @@ fn alt_delete_button_removes_selected_brushes_without_dialog() {
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?");
assert_delete_dialog(&app, "One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed.");
crate::dialogs::cancel(&mut app);
delete_button(&ctx, &mut app, egui::Modifiers::ALT, 4.0);
assert!(app.ui.dialog.is_none());
assert_delete_dialog(&app, "One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed.");
crate::dialogs::cancel(&mut app);
let left = names(&mut app);
assert!(three.iter().all(|name| !left.contains(name)));
assert_eq!(brush_of(&app, rect), None);
assert!(three.iter().all(|name| left.contains(name)));
assert_eq!(brush_of(&app, rect).as_deref(), Some(three[0].as_str()));
assert_eq!(undo_count(&app), before);
}
#[test]
fn alt_delete_button_deletes_unused_brush_without_a_question() {
let mut app = VectorcraftApp::new(Session::new(), Default::default());
app.run("file.new", json!({"width": 200, "height": 100})).unwrap();
let ctx = egui::Context::default();
ctx.enable_accesskit();
let first = names(&mut app)[0].clone();
let out = frame_with(&ctx, &mut app, vec![], egui::Modifiers::NONE, 0.0, show);
let tile = clickable(&out).into_iter().find(|(label, _)| label.as_deref() == Some(first.as_str())).expect("brush tile").1.center();
click(&ctx, &mut app, tile, egui::Modifiers::NONE, 1.0, show);
let before = undo_count(&app);
delete_button(&ctx, &mut app, egui::Modifiers::ALT, 2.0);
assert!(app.ui.dialog.is_none());
assert!(!names(&mut app).contains(&first));
assert_eq!(undo_count(&app), before + 1);
}
#[test]
fn menu_delete_used_brush_requires_remove_strokes_or_cancel() {
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);
menu_click(&ctx, &mut app, "Delete Brush", 3.0);
assert_delete_dialog(&app, "One or more of the brushes are in use and cannot be deleted until their strokes are expanded or removed.");
crate::dialogs::cancel(&mut app);
assert_eq!(undo_count(&app), before);
assert_eq!(brush_of(&app, rect).as_deref(), Some(three[0].as_str()));
menu_click(&ctx, &mut app, "Delete Brush", 4.0);
crate::dialogs::confirm(&mut app).unwrap();
assert_eq!(undo_count(&app), before + 1);
assert_eq!(brush_of(&app, rect), None);
assert!(app.session.active().unwrap().doc.node(rect).unwrap().appearance.stroke().is_some());
assert!(three.iter().all(|name| !names(&mut app).contains(name)));
}
#[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());
+4 -2
View File
@@ -1557,8 +1557,10 @@ already walks Image Trace. Short agent entry points:
name they are given, as `symbol.options` trims `newName`; a blank one gives "New Symbol" or "<name> copy".
- The Brushes panel menu's Select All Unused: `brush.unused` (a query) lists the brushes no stroke paints with, in
library order, and the panel selects them. Strokes in the art (opacity masks included), symbols, graph designs,
pattern tiles and graphic styles count, and so do the strokes in the art of a brush that is used. The art of a brush
that is not used does not count, so a brush whose own art is the only art that paints with it is listed.
pattern tiles (with the tiles that pattern editing's Cancel puts back) and graphic styles count, and so do the
strokes in the art of a brush that is used. The art of a brush that is not used does not count, so a brush whose own
art is the only art that paints with it is listed. The panel's Delete Brush asks Yes/No when every brush it deletes
is unused, and offers Remove Strokes or Cancel when one of them is in use.
- Brushes: `brush.list`, `brush.get {name}`, `brush.apply {name, ids?}`, `brush.new {type, name?, params?, ids?}`,
`brush.setCurrent {name}`, `brush.options {name, params?, newName?}`, `brush.delete {name} | {names}`,
`brush.duplicate {name, newName?} | {names}`, `brush.freehand`, `object.expandBrush {ids?}`. `brush.new`,
+1 -1
View File
@@ -962,7 +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.
The Brushes panel's Delete Brush button and panel menu item ask "Delete the brush selection?" with Yes and No when none of the brushes is in use. Alt-click (Option-click on macOS) on the button deletes them without asking, as the [Default keyboard shortcuts](https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html) page lists ("Delete without confirmation (except for Layers panel)"). When a stroke uses one of the brushes, the button, Alt-click and the menu item all ask with Remove Strokes and Cancel; Remove Strokes deletes the brushes and the strokes that used them become plain strokes. Tests: `panels::brushes::tests`, `dialogs::brush_delete::tests`.
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`).