Brushes: built-in library panels show brush names in the UI language

A built-in brush library's panel now shows each brush's name in the UI
language in its list rows and tooltips, and Find matches either the
shown name or the English name. User Defined and Other Library...
libraries, and the document's Brushes panel, keep names as stored. A
click, a drag, Add to Brushes and brush.addFromLibrary still use the
English name, so the brush comes into the document under that name.

The shared library panel gets a label hook for this, and its rows take
the library id. The 16 built-in brush names that had no catalog row
(Notched Ribbon, Double Line, Wave, Teardrop, Striped Ribbon, Fine Nib,
Broad Nib, Chisel, Round Pen, Pressure Pen, Beads, Dash Dot, Scallops,
Stars, Triangles, Sparkles) get a row in each of the 27 catalogs, and a
test requires a row for every built-in brush name.
This commit is contained in:
dotCooCoo
2026-11-13 05:27:22 -08:00
parent 1dbdbce91f
commit 4c93c04213
38 changed files with 528 additions and 17 deletions
+16
View File
@@ -6148,6 +6148,22 @@ axis Y Axis Y ос
Graphite Pencil Графитен молив
Wax Crayon Восъчен пастел
Charcoal Dust Въглен прах
Notched Ribbon Назъбена лента
Double Line Двойна линия
Wave Вълна
Teardrop Сълза
Striped Ribbon Раирана лента
Fine Nib Тънък писец
Broad Nib Широк писец
Chisel Длето
Round Pen Кръгло перо
Pressure Pen Перо с натиск
Beads Мъниста
Dash Dot Тире-точка
Scallops Фестони
Stars Звезди
Triangles Триъгълници
Sparkles Искри
Save Brush Library… Запази библиотека с четки…
@msg no empty artboards to delete няма празни работни области за изтриване
Scale Presets Пресети за мащаб
Can't render this file because it contains an unexpected character in line 2 and column 73.
+16
View File
@@ -6201,6 +6201,22 @@ axis Y Axis Y osa
Graphite Pencil Grafitna olovka
Wax Crayon Voštana bojica
Charcoal Dust Ugljena prašina
Notched Ribbon Zarezana traka
Double Line Dvostruka linija
Wave Talas
Teardrop Suza
Striped Ribbon Prugasta traka
Fine Nib Tanki vrh
Broad Nib Široki vrh
Chisel Dlijeto
Round Pen Okruglo pero
Pressure Pen Pero na pritisak
Beads Perle
Dash Dot Crta-tačka
Scallops Festoni
Stars Zvijezde
Triangles Trouglovi
Sparkles Iskrice
Save Brush Library… Sačuvaj biblioteku četkica…
@msg `{_1}` has no brushes to apply `{_1}` nema četkica za primjenu
@msg missing `library` (see brush.libraries) nedostaje `library` (pogledajte brush.libraries)
Can't render this file because it contains an unexpected character in line 2 and column 73.
+16
View File
@@ -3917,6 +3917,22 @@ digits Malayalam Malaiàlam
Graphite Pencil Llapis de grafit
Wax Crayon Cera de colors
Charcoal Dust Pols de carbó
Notched Ribbon Cinta dentada
Double Line Línia doble
Wave Ona
Teardrop Llàgrima
Striped Ribbon Cinta ratllada
Fine Nib Plomí fi
Broad Nib Plomí ample
Chisel Escarpra
Round Pen Ploma rodona
Pressure Pen Ploma de pressió
Beads Perles
Dash Dot Ratlla i punt
Scallops Festons
Stars Estrelles
Triangles Triangles
Sparkles Espurnes
Scatter: Dispersió:
Scissors Tisores
Scissors Tool Eina de tisores
Can't render this file because it contains an unexpected character in line 2 and column 73.
+16
View File
@@ -6694,6 +6694,22 @@ axis Y Axis osa Y
Graphite Pencil Grafitová tužka
Wax Crayon Vosková pastelka
Charcoal Dust Uhlový prach
Notched Ribbon Vroubkovaná stuha
Double Line Dvojitá čára
Wave Vlna
Teardrop Slza
Striped Ribbon Pruhovaná stuha
Fine Nib Jemný hrot
Broad Nib Široký hrot
Chisel Dláto
Round Pen Kulaté pero
Pressure Pen Tlakové pero
Beads Korálky
Dash Dot Čárka-tečka
Scallops Feston
Stars Hvězdy
Triangles Trojúhelníky
Sparkles Jiskry
Save Brush Library… Uložit knihovnu štětců…
@msg `{_1}` has no brushes to apply `{_1}` nemá žádné štětce k použití
@msg missing `library` (see brush.libraries) chybí `library` (viz brush.libraries)
Can't render this file because it contains an unexpected character in line 2 and column 73.
+16
View File
@@ -6165,6 +6165,22 @@ axis Y Axis Y-akse
Graphite Pencil Grafitblyant
Wax Crayon Vokskridt
Charcoal Dust Kulstøv
Notched Ribbon Takket bånd
Double Line Dobbelt linje
Wave Bølge
Teardrop Dråbe
Striped Ribbon Stribet bånd
Fine Nib Fin pen
Broad Nib Bred pen
Chisel Mejsel
Round Pen Rund pen
Pressure Pen Trykpen
Beads Perler
Dash Dot Streg-prik
Scallops Bølgekant
Stars Stjerner
Triangles Trekanter
Sparkles Gnister
Save Brush Library… Gem penselbibliotek…
@msg no empty artboards to delete ingen tomme tegneflader at slette
Scale Presets Skaleringsforudindstillinger
Can't render this file because it contains an unexpected character in line 2 and column 73.
+16
View File
@@ -6285,6 +6285,22 @@ axis Y Axis Y-Achse
Graphite Pencil Graphitstift
Wax Crayon Wachsmalstift
Charcoal Dust Kohlestaub
Notched Ribbon Gekerbtes Band
Double Line Doppellinie
Wave Welle
Teardrop Tropfen
Striped Ribbon Gestreiftes Band
Fine Nib Feine Feder
Broad Nib Breite Feder
Chisel Meißel
Round Pen Runde Feder
Pressure Pen Druckfeder
Beads Perlen
Dash Dot Strich-Punkt
Scallops Bogenkante
Stars Sterne
Triangles Dreiecke
Sparkles Funkeln
Save Brush Library… Pinsel-Bibliothek speichern…
@msg `{_1}` has no brushes to apply `{_1}` hat keine Pinsel zum Anwenden
@msg missing `library` (see brush.libraries) `library` fehlt (siehe brush.libraries)
Can't render this file because it contains an unexpected character in line 2 and column 73.
+16
View File
@@ -6645,6 +6645,22 @@ axis Y Axis Άξονας Y
Graphite Pencil Μολύβι γραφίτη
Wax Crayon Κηρομπογιά
Charcoal Dust Σκόνη κάρβουνου
Notched Ribbon Οδοντωτή κορδέλα
Double Line Διπλή γραμμή
Wave Κύμα
Teardrop Δάκρυ
Striped Ribbon Ριγέ κορδέλα
Fine Nib Λεπτή πένα
Broad Nib Φαρδιά πένα
Chisel Σμίλη
Round Pen Στρογγυλή πένα
Pressure Pen Πένα πίεσης
Beads Χάντρες
Dash Dot Παύλα-τελεία
Scallops Φεστόνια
Stars Αστέρια
Triangles Τρίγωνα
Sparkles Σπίθες
Save Brush Library… Αποθήκευση βιβλιοθήκης πινέλων…
@msg `{_1}` has no brushes to apply Η `{_1}` δεν έχει πινέλα για εφαρμογή
@msg missing `library` (see brush.libraries) λείπει το `library` (δείτε brush.libraries)
Can't render this file because it is too large.
+16
View File
@@ -6636,6 +6636,22 @@ axis Y Axis Eje Y
Graphite Pencil Lápiz de grafito
Wax Crayon Cera de colores
Charcoal Dust Polvo de carboncillo
Notched Ribbon Cinta dentada
Double Line Línea doble
Wave Onda
Teardrop Lágrima
Striped Ribbon Cinta a rayas
Fine Nib Plumilla fina
Broad Nib Plumilla ancha
Chisel Cincel
Round Pen Pluma redonda
Pressure Pen Pluma de presión
Beads Cuentas
Dash Dot Raya y punto
Scallops Festones
Stars Estrellas
Triangles Triángulos
Sparkles Destellos
Save Brush Library… Guardar biblioteca de pinceles…
@msg `{_1}` has no brushes to apply `{_1}` no tiene pinceles para aplicar
@msg missing `library` (see brush.libraries) falta `library` (consulte brush.libraries)
Can't render this file because it contains an unexpected character in line 2 and column 73.
+16
View File
@@ -6724,6 +6724,22 @@ axis Y Axis Y-akseli
Graphite Pencil Grafiittikynä
Wax Crayon Vahaliitu
Charcoal Dust Hiilipöly
Notched Ribbon Lovettu nauha
Double Line Kaksoisviiva
Wave Aalto
Teardrop Pisara
Striped Ribbon Raidallinen nauha
Fine Nib Ohut kärki
Broad Nib Leveä kärki
Chisel Taltta
Round Pen Pyöreä kynä
Pressure Pen Painekynä
Beads Helmet
Dash Dot Viiva-piste
Scallops Kaarireunus
Stars Tähdet
Triangles Kolmiot
Sparkles Kipinät
Save Brush Library… Tallenna sivellinkirjasto…
@msg `{_1}` has no brushes to apply Kohteessa `{_1}` ei ole käytettäviä siveltimiä
@msg missing `library` (see brush.libraries) `library` puuttuu (katso brush.libraries)
Can't render this file because it contains an unexpected character in line 2 and column 73.
+16
View File
@@ -6689,6 +6689,22 @@ axis Y Axis Axe Y
Graphite Pencil Crayon graphite
Wax Crayon Craie grasse
Charcoal Dust Poussière de fusain
Notched Ribbon Ruban cranté
Double Line Double ligne
Wave Vague
Teardrop Goutte
Striped Ribbon Ruban rayé
Fine Nib Plume fine
Broad Nib Plume large
Chisel Ciseau
Round Pen Plume ronde
Pressure Pen Plume à pression
Beads Perles
Dash Dot Tiret-point
Scallops Festons
Stars Étoiles
Triangles Triangles
Sparkles Étincelles
Save Brush Library… Enregistrer la bibliothèque de formes…
@msg `{_1}` has no brushes to apply `{_1}` n'a aucune forme à appliquer
@msg missing `library` (see brush.libraries) `library` manquant (voir brush.libraries)
Can't render this file because it contains an unexpected character in line 2 and column 73.
+16
View File
@@ -444,6 +444,22 @@ antiAlias Sharp शार्प
Graphite Pencil ग्रेफाइट पेंसिल
Wax Crayon मोम क्रेयॉन
Charcoal Dust चारकोल धूल
Notched Ribbon नोकदार रिबन
Double Line दोहरी लाइन
Wave लहर
Teardrop आंसू की बूंद
Striped Ribbon धारीदार रिबन
Fine Nib बारीक निब
Broad Nib चौड़ी निब
Chisel छेनी
Round Pen गोल पेन
Pressure Pen प्रेशर पेन
Beads मोती
Dash Dot डैश डॉट
Scallops स्कैलप
Stars तारे
Triangles त्रिभुज
Sparkles चमक
Art using a deleted global swatch keeps its colour. हटाए गए ग्लोबल स्वॉच वाला आर्टवर्क अपना रंग बनाए रखता है।
Artboard आर्टबोर्ड
Artboard {n} of {total} {total} में से आर्टबोर्ड {n}
Can't render this file because it is too large.
+16
View File
@@ -6677,6 +6677,22 @@ axis Y Axis Y tengely
Graphite Pencil Grafitceruza
Wax Crayon Zsírkréta
Charcoal Dust Szénpor
Notched Ribbon Csipkézett szalag
Double Line Dupla vonal
Wave Hullám
Teardrop Csepp
Striped Ribbon Csíkos szalag
Fine Nib Finom tollhegy
Broad Nib Széles tollhegy
Chisel Véső
Round Pen Kerek toll
Pressure Pen Nyomásérzékeny toll
Beads Gyöngyök
Dash Dot Pont-vonal
Scallops Hullámos szegély
Stars Csillagok
Triangles Háromszögek
Sparkles Szikrák
Save Brush Library… Ecsetkönyvtár mentése…
@msg `{_1}` has no brushes to apply A(z) `{_1}` nem tartalmaz alkalmazható ecsetet
@msg missing `library` (see brush.libraries) hiányzik a `library` (lásd: brush.libraries)
Can't render this file because it contains an unexpected character in line 2 and column 73.
+16
View File
@@ -6165,6 +6165,22 @@ axis Y Axis Sumbu Y
Graphite Pencil Pensil grafit
Wax Crayon Krayon lilin
Charcoal Dust Debu arang
Notched Ribbon Pita bertakik
Double Line Garis ganda
Wave Gelombang
Teardrop Tetesan
Striped Ribbon Pita bergaris
Fine Nib Mata pena halus
Broad Nib Mata pena lebar
Chisel Pahat
Round Pen Pena bulat
Pressure Pen Pena tekanan
Beads Manik-manik
Dash Dot Garis titik
Scallops Lekuk kerang
Stars Bintang
Triangles Segitiga
Sparkles Kilauan
Save Brush Library… Simpan pustaka kuas…
@msg no empty artboards to delete tidak ada artboard kosong untuk dihapus
Scale Presets Preset skala
Can't render this file because it contains an unexpected character in line 2418 and column 5.
+16
View File
@@ -6319,6 +6319,22 @@ axis Y Axis Asse Y
Graphite Pencil Matita di grafite
Wax Crayon Pastello a cera
Charcoal Dust Polvere di carboncino
Notched Ribbon Nastro dentellato
Double Line Doppia linea
Wave Onda
Teardrop Goccia
Striped Ribbon Nastro a righe
Fine Nib Pennino fine
Broad Nib Pennino largo
Chisel Scalpello
Round Pen Penna tonda
Pressure Pen Penna a pressione
Beads Perline
Dash Dot Tratto-punto
Scallops Smerlo
Stars Stelle
Triangles Triangoli
Sparkles Scintille
Save Brush Library… Salva libreria pennelli…
@msg `{_1}` has no brushes to apply `{_1}` non ha pennelli da applicare
@msg missing `library` (see brush.libraries) manca `library` (vedi brush.libraries)
Can't render this file because it contains an unexpected character in line 2 and column 73.
+16
View File
@@ -6384,6 +6384,22 @@ axis Y Axis Y 軸
Graphite Pencil 黒鉛鉛筆
Wax Crayon ワックスクレヨン
Charcoal Dust 木炭の粉
Notched Ribbon ノッチリボン
Double Line 二重線
Wave 波
Teardrop しずく
Striped Ribbon ストライプリボン
Fine Nib 細いペン先
Broad Nib 太いペン先
Chisel 平ノミ
Round Pen 丸ペン
Pressure Pen 筆圧ペン
Beads ビーズ
Dash Dot 一点鎖線
Scallops スカラップ
Stars 星
Triangles 三角形
Sparkles きらめき
Save Brush Library… ブラシライブラリを保存…
@msg `{_1}` has no brushes to apply `{_1}` に適用できるブラシがありません
@msg missing `library` (see brush.libraries) `library` がありません (brush.libraries を参照)
Can't render this file because it contains an unexpected character in line 2 and column 73.
+16
View File
@@ -6645,6 +6645,22 @@ axis Y Axis Y ღერძი
Graphite Pencil გრაფიტის ფანქარი
Wax Crayon ცვილის ფანქარი
Charcoal Dust ნახშირის მტვერი
Notched Ribbon დაკბილული ლენტი
Double Line ორმაგი ხაზი
Wave ტალღა
Teardrop წვეთი
Striped Ribbon ზოლიანი ლენტი
Fine Nib წვრილი წვერი
Broad Nib განიერი წვერი
Chisel საჭრეთელი
Round Pen მრგვალი კალამი
Pressure Pen დაწოლის კალამი
Beads მძივები
Dash Dot ტირე-წერტილი
Scallops ფესტონები
Stars ვარსკვლავები
Triangles სამკუთხედები
Sparkles ნაპერწკლები
Save Brush Library… ფუნჯების ბიბლიოთეკის შენახვა…
@msg `{_1}` has no brushes to apply `{_1}`-ში გამოსაყენებელი ფუნჯები არ არის
@msg missing `library` (see brush.libraries) `library` აკლია (იხ. brush.libraries)
Can't render this file because it is too large.
+16
View File
@@ -6280,6 +6280,22 @@ axis Y Axis Y축
Graphite Pencil 흑연 연필
Wax Crayon 왁스 크레용
Charcoal Dust 목탄 가루
Notched Ribbon 노치 리본
Double Line 이중선
Wave 물결
Teardrop 눈물방울
Striped Ribbon 줄무늬 리본
Fine Nib 가는 펜촉
Broad Nib 넓은 펜촉
Chisel 끌
Round Pen 둥근 펜
Pressure Pen 필압 펜
Beads 구슬
Dash Dot 일점쇄선
Scallops 스캘럽
Stars 별
Triangles 삼각형
Sparkles 반짝임
Save Brush Library… 브러시 라이브러리 저장…
@msg `{_1}` has no brushes to apply `{_1}`에 적용할 브러시가 없습니다
@msg missing `library` (see brush.libraries) `library`가 없습니다(brush.libraries 참조)
Can't render this file because it contains an unexpected character in line 2 and column 73.
+16
View File
@@ -6216,6 +6216,22 @@ axis Y Axis Y-as
Graphite Pencil Grafietpotlood
Wax Crayon Waskrijt
Charcoal Dust Houtskoolstof
Notched Ribbon Gekerfd lint
Double Line Dubbele lijn
Wave Golf
Teardrop Traan
Striped Ribbon Gestreept lint
Fine Nib Fijne penpunt
Broad Nib Brede penpunt
Chisel Beitel
Round Pen Ronde pen
Pressure Pen Drukpen
Beads Kralen
Dash Dot Streep-punt
Scallops Schulprand
Stars Sterren
Triangles Driehoeken
Sparkles Fonkelingen
Save Brush Library… Penseelbibliotheek opslaan…
@msg no empty artboards to delete geen lege tekengebieden om te verwijderen
Scale Presets Schaalvoorinstellingen
Can't render this file because it contains an unexpected character in line 2 and column 73.
+16
View File
@@ -6548,6 +6548,22 @@ axis Y Axis Oś Y
Graphite Pencil Ołówek grafitowy
Wax Crayon Kredka woskowa
Charcoal Dust Pył węglowy
Notched Ribbon Wstęga z nacięciami
Double Line Podwójna linia
Wave Fala
Teardrop Łza
Striped Ribbon Wstęga w paski
Fine Nib Cienka stalówka
Broad Nib Szeroka stalówka
Chisel Dłuto
Round Pen Okrągłe pióro
Pressure Pen Pióro z naciskiem
Beads Koraliki
Dash Dot Kreska-kropka
Scallops Feston
Stars Gwiazdy
Triangles Trójkąty
Sparkles Iskry
Save Brush Library… Zapisz bibliotekę pędzli…
@msg `{_1}` has no brushes to apply `{_1}` nie ma pędzli do zastosowania
@msg missing `library` (see brush.libraries) brak `library` (zobacz brush.libraries)
Can't render this file because it contains an unexpected character in line 2 and column 73.
+16
View File
@@ -6609,6 +6609,22 @@ axis Y Axis Eixo Y
Graphite Pencil Lápis de Grafite
Wax Crayon Giz de Cera
Charcoal Dust Pó de Carvão
Notched Ribbon Fita Entalhada
Double Line Linha Dupla
Wave Onda
Teardrop Lágrima
Striped Ribbon Fita Listrada
Fine Nib Pena Fina
Broad Nib Pena Larga
Chisel Cinzel
Round Pen Pena Redonda
Pressure Pen Pena de Pressão
Beads Contas
Dash Dot Traço-Ponto
Scallops Festões
Stars Estrelas
Triangles Triângulos
Sparkles Brilhos
Save Brush Library… Salvar Biblioteca de Pincéis…
@msg `{_1}` has no brushes to apply `{_1}` não tem pincéis para aplicar
@msg missing `library` (see brush.libraries) falta `library` (consulte brush.libraries)
Can't render this file because it contains an unexpected character in line 6 and column 47.
+16
View File
@@ -6305,6 +6305,22 @@ axis Y Axis Ось Y
Graphite Pencil Графитный карандаш
Wax Crayon Восковой мелок
Charcoal Dust Угольная пыль
Notched Ribbon Лента с насечками
Double Line Двойная линия
Wave Волна
Teardrop Капля
Striped Ribbon Полосатая лента
Fine Nib Тонкое перо
Broad Nib Широкое перо
Chisel Стамеска
Round Pen Круглое перо
Pressure Pen Перо с нажимом
Beads Бусины
Dash Dot Штрихпунктир
Scallops Фестоны
Stars Звёзды
Triangles Треугольники
Sparkles Искры
Save Brush Library… Сохранить библиотеку кистей…
@msg `{_1}` has no brushes to apply В `{_1}` нет кистей для применения
@msg missing `library` (see brush.libraries) не указан `library` (см. brush.libraries)
Can't render this file because it is too large.
+16
View File
@@ -6675,6 +6675,22 @@ axis Y Axis Y-axel
Graphite Pencil Grafitpenna
Wax Crayon Vaxkrita
Charcoal Dust Koldamm
Notched Ribbon Hackat band
Double Line Dubbellinje
Wave Våg
Teardrop Droppe
Striped Ribbon Randigt band
Fine Nib Fin spets
Broad Nib Bred spets
Chisel Mejsel
Round Pen Rund penna
Pressure Pen Tryckpenna
Beads Pärlor
Dash Dot Streck-punkt
Scallops Bågkant
Stars Stjärnor
Triangles Trianglar
Sparkles Gnistor
Save Brush Library… Spara penselbibliotek…
@msg `{_1}` has no brushes to apply `{_1}` har inga penslar att använda
@msg missing `library` (see brush.libraries) `library` saknas (se brush.libraries)
Can't render this file because it is too large.
+24
View File
@@ -198,6 +198,30 @@ fn built_in_brush_library_names_are_in_complete_catalogs() {
assert!(bad.is_empty(), "{}", bad.join("\n"));
}
/// A built-in brush library's panel lists its brushes in the UI language, so every brush of every
/// built-in library has a row in each complete catalog.
#[test]
fn built_in_library_brush_names_are_in_complete_catalogs() {
use vectorcraft_engine::cmd::brushlib;
let session = vectorcraft_engine::Session::new();
let mut names: Vec<String> = brushlib::BUILTIN
.iter()
.filter_map(|b| brushlib::library(&session, b.id))
.flat_map(|(_, lib)| lib.brushes.iter().map(|b| b.name.clone()).collect::<Vec<_>>())
.collect();
names.sort();
names.dedup();
assert_eq!(names.len(), 43, "{names:?}");
let mut bad = vec![];
for lang in LANGUAGES.iter().filter(|lang| lang.complete_menus) {
let missing: Vec<&String> = names.iter().filter(|n| lang.catalog().plain(n).is_none()).collect();
if !missing.is_empty() {
bad.push(format!("{} is missing {missing:?}", lang.code));
}
}
assert!(bad.is_empty(), "{}", bad.join("\n"));
}
/// Preference choices and the marquee's E hint share the same translated labels.
#[test]
fn marquee_selection_labels_are_in_complete_catalogs() {
+16
View File
@@ -6650,6 +6650,22 @@ axis Y Axis Y ekseni
Graphite Pencil Grafit Kalem
Wax Crayon Mum Boya
Charcoal Dust Kömür Tozu
Notched Ribbon Çentikli Şerit
Double Line Çift Çizgi
Wave Dalga
Teardrop Gözyaşı
Striped Ribbon Çizgili Şerit
Fine Nib İnce Uç
Broad Nib Geniş Uç
Chisel Keski
Round Pen Yuvarlak Kalem
Pressure Pen Basınçlı Kalem
Beads Boncuklar
Dash Dot Çizgi Nokta
Scallops Fistolar
Stars Yıldızlar
Triangles Üçgenler
Sparkles Parıltılar
Save Brush Library… Fırça Kitaplığını Kaydet…
@msg `{_1}` has no brushes to apply `{_1}` içinde uygulanacak fırça yok
@msg missing `library` (see brush.libraries) `library` eksik (bkz. brush.libraries)
Can't render this file because it contains an unexpected character in line 5 and column 25.
+16
View File
@@ -6299,6 +6299,22 @@ axis Y Axis Вісь Y
Graphite Pencil Графітний олівець
Wax Crayon Восковий олівець
Charcoal Dust Вугільний пил
Notched Ribbon Стрічка з насічками
Double Line Подвійна лінія
Wave Хвиля
Teardrop Крапля
Striped Ribbon Смугаста стрічка
Fine Nib Тонке перо
Broad Nib Широке перо
Chisel Стамеска
Round Pen Кругле перо
Pressure Pen Перо з натиском
Beads Намистини
Dash Dot Штрихпунктир
Scallops Фестони
Stars Зірки
Triangles Трикутники
Sparkles Іскри
Save Brush Library… Зберегти бібліотеку пензлів…
@msg `{_1}` has no brushes to apply У `{_1}` немає пензлів для застосування
@msg missing `library` (see brush.libraries) не вказано `library` (див. brush.libraries)
Can't render this file because it is too large.
+16
View File
@@ -6300,6 +6300,22 @@ axis Y Axis Trục Y
Graphite Pencil Bút chì than chì
Wax Crayon Bút sáp màu
Charcoal Dust Bụi than
Notched Ribbon Dải băng khía
Double Line Đường đôi
Wave Sóng
Teardrop Giọt lệ
Striped Ribbon Dải băng sọc
Fine Nib Ngòi mảnh
Broad Nib Ngòi rộng
Chisel Đục
Round Pen Bút tròn
Pressure Pen Bút áp lực
Beads Hạt cườm
Dash Dot Gạch chấm
Scallops Viền vỏ sò
Stars Ngôi sao
Triangles Tam giác
Sparkles Lấp lánh
Save Brush Library… Lưu thư viện cọ…
@msg `{_1}` has no brushes to apply `{_1}` không có cọ nào để áp dụng
@msg missing `library` (see brush.libraries) thiếu `library` (xem brush.libraries)
Can't render this file because it contains an unexpected character in line 204 and column 5.
+16
View File
@@ -6532,6 +6532,22 @@ axis Y Axis Y 轴
Graphite Pencil 石墨铅笔
Wax Crayon 蜡笔
Charcoal Dust 炭粉
Notched Ribbon 凹口丝带
Double Line 双线
Wave 波浪
Teardrop 泪滴
Striped Ribbon 条纹丝带
Fine Nib 细笔尖
Broad Nib 宽笔尖
Chisel 凿形
Round Pen 圆头笔
Pressure Pen 压感笔
Beads 珠串
Dash Dot 点划线
Scallops 扇贝边
Stars 星形
Triangles 三角形
Sparkles 闪光
Save Brush Library… 保存画笔库…
@msg `{_1}` has no brushes to apply `{_1}` 中没有可应用的画笔
@msg missing `library` (see brush.libraries) 缺少 `library`(请参阅 brush.libraries)
Can't render this file because it contains an unexpected character in line 2 and column 73.
+16
View File
@@ -6337,6 +6337,22 @@ axis Y Axis Y 軸
Graphite Pencil 石墨鉛筆
Wax Crayon 蠟筆
Charcoal Dust 炭粉
Notched Ribbon 凹口緞帶
Double Line 雙線
Wave 波浪
Teardrop 淚滴
Striped Ribbon 條紋緞帶
Fine Nib 細筆尖
Broad Nib 寬筆尖
Chisel 鑿形
Round Pen 圓頭筆
Pressure Pen 壓感筆
Beads 珠串
Dash Dot 點劃線
Scallops 扇貝邊
Stars 星形
Triangles 三角形
Sparkles 閃光
Save Brush Library… 儲存筆刷資料庫…
@msg `{_1}` has no brushes to apply `{_1}` 中沒有可套用的筆刷
@msg missing `library` (see brush.libraries) 未提供 `library`(請參閱 brush.libraries)
Can't render this file because it contains an unexpected character in line 6 and column 47.
+8 -2
View File
@@ -637,10 +637,16 @@ impl LibraryKind for BrushLibraries {
fn path(app: &VectorcraftApp, id: &str) -> Option<String> {
app.session.brush_libraries.path(id).map(str::to_string)
}
fn rows<'a>(lib: &'a Self::Lib, query: &str) -> Vec<Row<'a, LibBrush>> {
let found = |n: &str| query.is_empty() || n.to_lowercase().contains(query);
/// Find matches a brush's name as shown and as stored.
fn rows<'a>(lib: &'a Self::Lib, id: &str, query: &str) -> Vec<Row<'a, LibBrush>> {
let found = |n: &str| query.is_empty() || n.to_lowercase().contains(query) || Self::label(id, n).to_lowercase().contains(query);
lib.iter().filter(|b| found(b.name())).map(|b| Row { name: b.name(), item: Some(b) }).collect()
}
/// A built-in library's brush in the UI language; a User Defined or loaded library's brush as
/// it is named.
fn label<'a>(id: &str, name: &'a str) -> &'a str {
super::label_or_name(name, brushlib::BUILTIN.iter().any(|b| b.id == id))
}
/// The brush's stroke preview, drawn with the library's resources.
fn draw(_: &VectorcraftApp, ui: &Ui, r: egui::Rect, b: &LibBrush, selected: bool, hovered: bool) {
chip(ui, r, &b.lib.doc, &b.shown.def, b.shown.key);
+1 -1
View File
@@ -726,7 +726,7 @@ impl LibraryKind for GraphicStyleLibraries {
fn path(app: &VectorcraftApp, id: &str) -> Option<String> {
app.session.style_libraries.path(id).map(str::to_string)
}
fn rows<'a>(lib: &'a Self::Lib, query: &str) -> Vec<Row<'a, LibStyle>> {
fn rows<'a>(lib: &'a Self::Lib, _: &str, query: &str) -> Vec<Row<'a, LibStyle>> {
let found = |n: &str| query.is_empty() || n.to_lowercase().contains(query);
lib.iter().filter(|s| found(&s.style().name)).map(|s| Row { name: &s.style().name, item: Some(s) }).collect()
}
+12 -6
View File
@@ -104,8 +104,13 @@ pub(crate) trait LibraryKind {
fn find(app: &VectorcraftApp, key: &str) -> Found;
/// The file library `id` was read from, if any.
fn path(app: &VectorcraftApp, id: &str) -> Option<String>;
/// The rows matching the find field's `query` (lower case; empty: all).
fn rows<'a>(lib: &'a Self::Lib, query: &str) -> Vec<Row<'a, Self::Item>>;
/// The rows of library `id` matching the find field's `query` (lower case; empty: all).
fn rows<'a>(lib: &'a Self::Lib, id: &str, query: &str) -> Vec<Row<'a, Self::Item>>;
/// Row `name` of library `id` as its list row and tooltips show it (by default as it is). The
/// row keeps `name` for its tile, selection, clicks, drags and the commands they run.
fn label<'a>(_id: &str, name: &'a str) -> &'a str {
name
}
/// An item's thumbnail in `r`.
fn draw(app: &VectorcraftApp, ui: &Ui, r: Rect, item: &Self::Item, selected: bool, hovered: bool);
/// The icons at the right of list row `r`.
@@ -626,7 +631,7 @@ fn body<K: LibraryKind>(app: &mut VectorcraftApp, ui: &mut Ui, id: &str) {
let (view, hide_find) = settings(&app.ui, K::KIND, id).map_or((K::VIEW, false), |s| (s.view.unwrap_or(K::VIEW), s.hide_find));
let query = if hide_find { String::new() } else { widgets::search_field(ui, egui::Id::unique(key(&panel, "find")), tl!("Find")) };
ui.add_space(4.0);
let rows = K::rows(&lib, &query.trim().to_lowercase());
let rows = K::rows(&lib, id, &query.trim().to_lowercase());
let selected = selection(ui.ctx(), &panel);
let is_sel = |n: &str| selected.iter().any(|s| s == n);
let mut clicked: Option<(String, bool, egui::Modifiers)> = None;
@@ -659,9 +664,10 @@ fn body<K: LibraryKind>(app: &mut VectorcraftApp, ui: &mut Ui, id: &str) {
}
None => super::swatches::draw_folder(ui, chip),
}
list_row_name(ui, r, chip, row.name);
let label = K::label(id, row.name);
list_row_name(ui, r, chip, label);
drag_out::<K>(ui, &resp, id, row);
let tip = || row.item.map_or_else(|| row.name.to_string(), |i| format!("{} ({})", row.name, K::describe(i)));
let tip = || row.item.map_or_else(|| label.to_string(), |i| format!("{label} ({})", K::describe(i)));
click(ui, resp, row, &tip);
}
} else {
@@ -672,7 +678,7 @@ fn body<K: LibraryKind>(app: &mut VectorcraftApp, ui: &mut Ui, id: &str) {
None => folder_tile(ui, cell, is_sel(row.name)),
}
drag_out::<K>(ui, &resp, id, row);
click(ui, resp, row, &|| row.name.to_string());
click(ui, resp, row, &|| K::label(id, row.name).to_string());
}
}
});
@@ -127,7 +127,7 @@ fn a_plain_click_sets_where_a_shift_click_range_starts() {
let ctx = context();
app.run("window.swatchLibrary", json!({"library": "pastels"})).unwrap();
let (_, lib) = SwatchLibraries::get(&app, "pastels").unwrap();
let order: Vec<String> = SwatchLibraries::rows(&lib, "").iter().map(|r| r.name.to_string()).collect();
let order: Vec<String> = SwatchLibraries::rows(&lib, "pastels", "").iter().map(|r| r.name.to_string()).collect();
click(&mut app, &ctx, &order[1], Modifiers::NONE, 0.0);
click(&mut app, &ctx, &order[4], Modifiers::SHIFT, 2.0);
assert_eq!(selection(&ctx, "library:swatches:pastels"), order[1..=4], "the range starts at the plain click");
@@ -83,7 +83,7 @@ impl LibraryKind for SwatchLibraries {
}
/// Ungrouped swatches, then each colour group's folder and swatches (a group whose name
/// matches shows all of them), as in the Swatches panel.
fn rows<'a>(lib: &'a Self::Lib, query: &str) -> Vec<Row<'a, Swatch>> {
fn rows<'a>(lib: &'a Self::Lib, _: &str, query: &str) -> Vec<Row<'a, Swatch>> {
let found = |n: &str| query.is_empty() || n.to_lowercase().contains(query);
let row = |w: &'a Swatch| Row { name: &w.name, item: Some(w) };
let mut out: Vec<Row<Swatch>> = lib.swatches.iter().filter(|w| found(&w.name)).map(row).collect();
+1 -1
View File
@@ -496,7 +496,7 @@ impl LibraryKind for SymbolLibraries {
fn path(app: &VectorcraftApp, id: &str) -> Option<String> {
app.session.symbol_libraries.path(id).map(str::to_string)
}
fn rows<'a>(lib: &'a Self::Lib, query: &str) -> Vec<Row<'a, LibSymbol>> {
fn rows<'a>(lib: &'a Self::Lib, _: &str, query: &str) -> Vec<Row<'a, LibSymbol>> {
let found = |n: &str| query.is_empty() || n.to_lowercase().contains(query);
lib.iter().filter(|s| found(s.name())).map(|s| Row { name: s.name(), item: Some(s) }).collect()
}
@@ -103,7 +103,7 @@ fn clicking_a_library_brush_adds_it_to_the_documents_brushes_in_one_step() {
assert!(has(&app, "Teardrop") && has(&app, "Double Line"));
// Find filters by name; list rows give the type.
let lib = BrushLibraries::get(&app, "art-ribbons").unwrap().1;
let rows = BrushLibraries::rows(&lib, "tear");
let rows = BrushLibraries::rows(&lib, "art-ribbons", "tear");
assert_eq!(rows.iter().map(|r| r.name).collect::<Vec<_>>(), ["Teardrop"]);
assert_eq!(BrushLibraries::describe(rows[0].item.unwrap()), "Art");
// Dragging a brush out of the panel carries it.
@@ -127,7 +127,7 @@ fn the_ink_and_wash_library_opens_and_a_click_adds_and_applies_its_brush() {
let r = app.run("window.brushLibrary", json!({"library": "ink-and-wash"})).unwrap();
assert_eq!((r["open"].as_str(), r["count"].as_u64()), (Some("ink-and-wash"), Some(4)));
let lib = BrushLibraries::get(&app, "ink-and-wash").unwrap().1;
let rows = BrushLibraries::rows(&lib, "");
let rows = BrushLibraries::rows(&lib, "ink-and-wash", "");
assert_eq!(rows.iter().map(|r| r.name).collect::<Vec<_>>(), ["Tapered Ink", "Brush Pen", "Soft Wash", "Pooling Wash"]);
let path = app.run("shape.line", json!({"x1": 10, "y1": 10, "x2": 150, "y2": 60})).unwrap()["id"].as_u64().unwrap();
let (undo, brushes) = (undo_len(&app), count(&app));
@@ -140,6 +140,48 @@ fn the_ink_and_wash_library_opens_and_a_click_adds_and_applies_its_brush() {
assert_eq!(app.run("brush.list", json!({})).unwrap()["current"], "Brush Pen");
}
/// A built-in library lists its brushes in the UI language, and Find matches the name shown and
/// the brush's own name. The brush keeps its own name for its tile, a click and the document's
/// Brushes. A library loaded from a document lists its brushes as they are named.
#[test]
fn built_in_library_brushes_are_listed_in_the_ui_language() {
let dir = std::env::temp_dir().join(format!("vc-brush-names-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let mut app = app();
let ctx = context();
crate::i18n::set_current(crate::i18n::Lang::from_code("fr").expect("fr registered"));
let list = |app: &mut VectorcraftApp, t: f64| {
let panel = last_open(&app.ui).map(LibraryPanel::panel).unwrap_or_default();
app.run("ui.libraryPanel", json!({"panel": panel, "view": "smallList"})).unwrap();
frame(&ctx, vec![], t, |ui| library_panel::show_window(app, ui.ctx()));
frame(&ctx, vec![], t + 0.25, |ui| library_panel::show_window(app, ui.ctx()))
};
app.run("window.brushLibrary", json!({"library": "ink-and-wash"})).unwrap();
let texts = list(&mut app, 0.0);
assert!(texts.iter().any(|t| t == "Encre effilée"), "{texts:?}");
assert!(!texts.iter().any(|t| t == "Tapered Ink"), "{texts:?}");
let lib = BrushLibraries::get(&app, "ink-and-wash").unwrap().1;
for query in ["effil", "tapered"] {
let rows = BrushLibraries::rows(&lib, "ink-and-wash", query);
assert_eq!(rows.iter().map(|r| r.name).collect::<Vec<_>>(), ["Tapered Ink"], "{query}");
}
assert!(BrushLibraries::rows(&lib, "loaded/1", "effil").is_empty(), "another library's brush is matched by its own name only");
click(&mut app, &ctx, "Tapered Ink", Modifiers::NONE, 1.0);
assert!(has(&app, "Tapered Ink"));
assert_eq!(app.run("brush.list", json!({})).unwrap()["current"], "Tapered Ink");
// The document's brushes, opened as a library, keep their names.
let doc = dir.join("Inks.vectorcraft");
let b64 = app.run("document.serialize", json!({})).unwrap()["dataBase64"].as_str().unwrap().to_string();
std::fs::write(&doc, vectorcraft_format::base64_decode(&b64).unwrap()).unwrap();
app.run("window.brushLibrary.other", json!({"path": doc.to_string_lossy()})).unwrap();
let texts = list(&mut app, 2.0);
assert!(texts.iter().any(|t| t == "Tapered Ink"), "{texts:?}");
assert!(!texts.iter().any(|t| t == "Encre effilée"), "{texts:?}");
crate::i18n::set_current(crate::i18n::Lang::EN);
let _ = std::fs::remove_dir_all(dir);
}
#[test]
fn the_window_menu_and_the_brushes_panel_open_brush_libraries() {
let mut app = app();
@@ -92,7 +92,7 @@ fn clicking_a_library_style_adds_and_applies_it_in_one_step() {
assert!(d.graphic_style("Feathered").is_some() && d.graphic_style("Haze").is_some());
// Find filters by name; list rows describe the styles; looks are computed once per library.
let lib = GraphicStyleLibraries::get(&app, "shadows-glows").unwrap().1;
let rows = GraphicStyleLibraries::rows(&lib, "ember");
let rows = GraphicStyleLibraries::rows(&lib, "shadows-glows", "ember");
assert_eq!(rows.iter().map(|r| r.name).collect::<Vec<_>>(), ["Ember"]);
assert_eq!(GraphicStyleLibraries::describe(rows[0].item.unwrap()), "1 fill, Inner Glow, Outer Glow");
assert!(std::sync::Arc::ptr_eq(&lib, &GraphicStyleLibraries::get(&app, "shadows-glows").unwrap().1));
@@ -121,7 +121,7 @@ fn painted_and_sketched_opens_from_the_window_menu_and_a_click_applies_its_style
let node = app.session.doc().unwrap().doc.node(vectorcraft_doc::NodeId(rect)).unwrap().appearance.effects.clone();
assert_eq!(node.iter().map(|e| e.id.as_str()).collect::<Vec<_>>(), ["sketch.charcoal"]);
let lib = GraphicStyleLibraries::get(&app, "painted-sketched").unwrap().1;
let rows = GraphicStyleLibraries::rows(&lib, "ink");
let rows = GraphicStyleLibraries::rows(&lib, "painted-sketched", "ink");
assert_eq!(rows.iter().map(|r| r.name).collect::<Vec<_>>(), ["Ink Contour", "Wet Ink"]);
}
@@ -89,7 +89,7 @@ fn clicking_a_library_symbol_adds_it_to_the_documents_symbols_in_one_step() {
assert_eq!(symbols(&app), ["Star", "Hexagon", "Arrow"]);
// Find filters by name; list rows give the natural size.
let lib = SymbolLibraries::get(&app, "basic-shapes").unwrap().1;
let rows = SymbolLibraries::rows(&lib, "arr");
let rows = SymbolLibraries::rows(&lib, "basic-shapes", "arr");
assert_eq!(rows.iter().map(|r| r.name).collect::<Vec<_>>(), ["Arrow"]);
assert_eq!(SymbolLibraries::describe(rows[0].item.unwrap()), "48 × 32 pt");
// Dragging a symbol out of the panel carries it to the canvas.
+1
View File
@@ -895,6 +895,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- **Brush libraries** ([public source](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/apply-and-edit-strokes/create-new-brush-libraries.html), [import](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/apply-and-edit-strokes/import-brushes-from-brush-libraries-or-other-files.html), [Brushes panel](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/apply-and-edit-strokes/brushes-panel-overview.html)): Window › Brush Libraries, the Brushes panel's Brush Libraries Menu and its menu's Open Brush Library open each library in a library panel of its own, as swatch, graphic style and symbol libraries do. The built-in libraries (Art Ribbons, Ink and Wash, Calligraphic Nibs, Bristle Brushes, Pattern Borders, Scatter Shapes, Game Trails, Energy Beams, Dry Media) are original brushes generated in code (`crates/engine/src/cmd/brushlib.rs`); Striped Ribbon's art is filled with the library's own pattern and Sparkles scatters the library's own symbol; Ink and Wash adds ink and wash brushes (the [help page](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/apply-and-edit-strokes/about-brushes.html) lists an Ink brush library of strokes with varied thickness and texture; the names, shapes and opacities are VectorCraft's own): Tapered Ink, an art brush broad near its start that thins to a fine tip; Brush Pen, a calligraphic brush whose size follows the pen pressure; and Soft Wash and Pooling Wash, art brushes of translucent gray layers that take the stroke color as tints; Game Trails adds motion art brushes (Speed Line, Dash Trail, Burst Ray, Zigzag); Energy Beams adds laser and plasma art brushes (Laser Bolt, Plasma Arc, Charge Wave, Spark Trail); Dry Media adds grainy art brushes (Chalk Grain, Graphite Pencil, Wax Crayon, Charcoal Dust). A click on a brush adds it to the document's Brushes as one undo step with every resource its art uses (patterns, symbols, swatches, graphic styles, images), makes it the current brush and, with paths selected, applies it to them in the same step; a brush the document already has is not added again and a different brush of the same name comes in numbered ("Wave 2"). Shift/Cmd-clicks select brushes for Add to Brushes; a brush dragged onto a path is added and applied to it, and one dragged onto the Brushes panel is added. Save Brushes… / the Brushes panel's Save Brush Library… write the document's brushes (or the selected ones) as a `.vectorcraft` library into the `Brushes` folder next to the preferences, listed under User Defined, or to a file; Other Library… reads the brushes of any document VectorCraft opens. Thumbnails, in the library panels, the Brushes panel and Brush Options, render brush art with the patterns and symbols it uses. Copying brushed art now carries the patterns and symbols its brush's art uses too. Tests: `crates/engine/src/tests/brushlib.rs`, `crates/ui-egui/src/panels/tests_brush_libraries.rs`, `panels::brushes::tests::library_brush_previews_show_the_patterns_their_art_uses`, `canvas::tests::a_library_brush_dropped_on_a_path_is_added_and_applied_in_one_step`.
- **Bristle Brushes library** ([What's new](https://helpx.adobe.com/illustrator/desktop/new-features/whats-new.html), [About brushes](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/apply-and-edit-strokes/about-brushes.html)): Window › Brush Libraries › Bristle Brushes holds ten bristle brushes, one for each tip shape (Fine Liner, Pointed Flat, Stubby Round, Dry Scrub, Soft Dome, Filbert, Angled Round, Angled Flat, Fan Blender, Grass Fan). The brushes, their names and their values are VectorCraft's own, and every value lies inside the range the bristle brush keeps (`brush.libraries`, `brush.addFromLibrary`; `the_bristle_brushes_library_covers_every_tip_shape_within_the_bristle_ranges`).
- **Dry Media library** ([help page](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/apply-and-edit-strokes/about-brushes.html)): Window › Brush Libraries › Dry Media holds four art brushes with textured strokes like dry drawing materials, each 100 pt long: Chalk Grain (about 8 pt high), Graphite Pencil (about 2.3 pt), Wax Crayon (about 5.5 pt) and Charcoal Dust (about 7 pt). The brushes, their names, sizes and art are VectorCraft's own: each is filled vector paths from a fixed seed, at most 80 paths and 500 anchors, stretched along the path with Tints colorization, and the chalk and crayon art has even-odd gaps where the paper shows (`brush.libraries`, `brush.addFromLibrary`; `the_dry_media_library_holds_grainy_art_brushes_with_bounded_art`).
- **Brush names in library panels**: a built-in brush library's panel shows its brushes' names in the UI language, in its list rows and tooltips, and its Find field matches the name shown or the English name. This is VectorCraft's own rule. A User Defined or Other Library… library's brushes, and the document's Brushes panel, show names as they are stored. A click, a drag, Add to Brushes and `brush.addFromLibrary` still use the English name, so the brush comes into the document under that name (`built_in_library_brushes_are_listed_in_the_ui_language`, `built_in_library_brush_names_are_in_complete_catalogs`).
- A canvas context menu: right-click selects the object under the pointer and lists what applies to the selection (Undo/Redo, clipboard, group, isolation, join, Remove Anchor Points when anchors are selected, masks, compound paths, guides, Transform, Arrange, Select, Export Selection, and on type Bullets and Numbering and Spelling › Auto Spell Check, checked while it is on), or the view commands on empty canvas.
- A right-click over locked objects adds an Unlock submenu to the canvas context menu. The submenu lists up to 20 locked objects under the pointer, topmost first, by the name the Layers panel shows, and choosing one unlocks it (`layer.setProps`). A locked group is listed as one item. Objects on hidden, locked or template layers are not listed.
- Native macOS save sheets offer a localised Format menu (#866), built from the request's existing format filters. It selects the suggested filename's format and updates the suffix when changed, including legacy `.drawcraft` names. `apps/vectorcraft/src/mac_save_format.rs` has filename regressions; an actual native SVG save, selection of PDF and cancellation without changing the saved document were verified.