Merge pull request #4402 from dotCooCoo/feat/styles-alt-click-new-asks-name

Character and Paragraph Styles: Alt-click on New asks for the new style's name
This commit is contained in:
BFlat
2026-10-30 00:43:02 +07:00
committed by GitHub
36 changed files with 294 additions and 6 deletions
+17 -2
View File
@@ -366,11 +366,27 @@ pub(crate) fn selected_style_attrs(s: &Session, para: bool) -> Result<Option<Map
selection_attrs(s, &json!({}), if para { Kind::Para } else { Kind::Char })
}
/// The attributes `charStyle.new` (`para`: `paraStyle.new`) would give a style made now from
/// `target` (its `id`, `start` and `end`; empty for the selected objects). `None` without text
/// there.
pub fn target_style_attrs(s: &Session, target: &Value, para: bool) -> Option<Map<String, Value>> {
selection_attrs(s, target, if para { Kind::Para } else { Kind::Char }).ok().flatten()
}
/// Whether the document has a character (`para`: paragraph) style `name`, and its attributes.
pub(crate) fn style_attrs(d: &Document, para: bool, name: &str) -> Option<Map<String, Value>> {
if para { Kind::Para } else { Kind::Char }.attrs(d, name)
}
/// The name `charStyle.new` (`para`: `paraStyle.new`) gives a style when it is given none: the
/// first "Character Style N" ("Paragraph Style N") that no character style (paragraph style) of the
/// document has.
pub fn new_style_name(d: &Document, para: bool) -> String {
let kind = if para { Kind::Para } else { Kind::Char };
let base = if para { "Paragraph Style" } else { "Character Style" };
(1..).map(|i| format!("{base} {i}")).find(|n| kind.attrs(d, n).is_none()).unwrap_or_else(|| base.to_string())
}
fn name_param<'a>(p: &'a Value, kind: Kind, verb: &str) -> Result<&'a str> {
str_param(p, "name").ok_or_else(|| bad(&kind.cmd(verb), "missing `name`"))
}
@@ -436,10 +452,9 @@ fn new(s: &mut Session, p: &Value, kind: Kind) -> Result<Value> {
},
};
let d = &s.doc()?.doc;
let base = if kind == Kind::Char { "Character Style" } else { "Paragraph Style" };
let name = match str_param(p, "name") {
Some(n) => n.trim().to_string(),
None => (1..).map(|i| format!("{base} {i}")).find(|n| kind.attrs(d, n).is_none()).unwrap_or_else(|| base.to_string()),
None => new_style_name(d, kind == Kind::Para),
};
if name.is_empty() {
return Err(bad(&c, "give the style a name"));
+2
View File
@@ -62,6 +62,7 @@ pub mod new_brush;
pub mod new_color_group;
mod new_document;
pub mod new_swatch;
pub mod new_text_style;
pub mod office_export;
pub mod package;
pub(crate) mod path_ops;
@@ -298,6 +299,7 @@ registry! {
GradientStop: ["gradientStop"] => gradient_stop::SPEC,
ColorPicker: [color_picker::KIND] => color_picker::SPEC,
GraphicStyleOptions: [graphic_style_options::KIND] => graphic_style_options::SPEC,
NewTextStyle: [new_text_style::KIND] => new_text_style::SPEC,
EffectExists: [effect::EXISTS] => effect::EXISTS_SPEC,
ColorGuideOptions: [color_guide_options::KIND] => color_guide_options::SPEC,
ColorBalance: [color_balance::KIND] => color_balance::SPEC,
@@ -0,0 +1,60 @@
//! New Character Style and New Paragraph Style: the new style's name. OK makes the style from the
//! text that was selected when the dialog opened (`charStyle.new`, `paraStyle.new`).
//!
//! Fields: `name`, `__para` (true for a paragraph style) and `__target` (the `id`, `start` and
//! `end` of the Type tool's selection, empty for the selected objects, and the `attrs` that text had
//! when the dialog opened).
use serde_json::{Value, json};
use super::swatch_options::{grid, label};
use super::{DialogSpec, form};
use crate::VectorcraftApp;
use crate::state::Dialog;
/// The dialog kind of New Character Style and New Paragraph Style.
pub const KIND: &str = "newTextStyle";
pub(super) const SPEC: DialogSpec = DialogSpec { heading, body, confirm, min_width: 320.0, ..DialogSpec::FORM };
fn para(d: &Dialog) -> bool {
d.fields.get("__para").and_then(Value::as_bool).unwrap_or(false)
}
fn heading(d: &Dialog) -> String {
if para(d) { tl!("New Paragraph Style") } else { tl!("New Character Style") }.into()
}
/// Open New Character Style (`para`: New Paragraph Style) with the name `charStyle.new` would give
/// the style, to make it from the text `target` names.
pub fn open(app: &mut VectorcraftApp, para: bool, target: Value) -> Result<Value, String> {
let d = &app.session.active().ok_or("no document open")?.doc;
let name = vectorcraft_engine::cmd::textstyles::new_style_name(d, para);
let mut target = target;
if let Some(attrs) = vectorcraft_engine::cmd::textstyles::target_style_attrs(&app.session, &target, para) {
target["attrs"] = Value::Object(attrs);
}
app.ui.dialog = Some(Dialog::new(KIND, json!({ "name": name, "__para": para, "__target": target })));
Ok(json!({ "dialog": KIND }))
}
fn body(_: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> bool {
grid(ui, |ui| {
label(ui, tl!("Style Name:"));
form::text(ui, d, "name", 190.0);
ui.end_row();
});
false
}
/// OK: make the style with the name. A name another style has, or an empty one, keeps the dialog
/// open.
fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result<Value, String> {
let mut params = d.fields.get("__target").filter(|t| t.is_object()).cloned().unwrap_or_else(|| json!({}));
params["name"] = json!(d.str("name"));
let r = app.run(if para(d) { "paraStyle.new" } else { "charStyle.new" }, params);
if r.is_ok() {
app.ui.dialog = None;
}
r
}
+1
View File
@@ -393,6 +393,7 @@ fn dialogs_do_not_stretch_to_the_screen() {
("ui.swatchOptions", json!({"name": "White"})),
("ui.colorPicker", json!({})),
("ui.graphicStyleOptions", json!({})),
("ui.newCharStyle", json!({})),
("ui.newArtboard", json!({})),
("ui.brushOptions", json!({"name": "3 pt. Round"})),
("ui.brushOptions", json!({"name": "Bristle Round"})),
+2
View File
@@ -1963,6 +1963,7 @@ font-technology Variable Променливи
Brush Art Графика на четка
New Calligraphic Brush Нова калиграфска четка
New Character Style Нов стил на знак
New Character Style… Нов стил на знак…
New Color Group Нова цветова група
New Color Group… Нова цветова група…
New Document Presets Предварителни настройки за нов документ
@@ -1978,6 +1979,7 @@ font-technology Variable Променливи
New Opacity Masks Are Clipping Новите маски за непрозрачност са изрязващи
New Opacity Masks Are Inverted Новите маски за непрозрачност са инвертирани
New Paragraph Style Нов стил на абзац
New Paragraph Style… Нов стил на абзац…
New Pattern Brush from Selection Нова щампова четка от селекцията
New PDF Preset Нова предварителна настройка за PDF
New Print Preset Нова предварителна настройка за печат
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4038,7 +4038,9 @@ font-technology Variable Variabilní
Mojikumi Mojikumi
N pt per N pt na
New Character Style Nový styl znaků
New Character Style… Nový styl znaků…
New Paragraph Style Nový styl odstavce
New Paragraph Style… Nový styl odstavce…
Paragraph Style Options Možnosti stylu odstavce
Redefine Character Style Předefinovat styl znaků
Redefine Paragraph Style Předefinovat styl odstavce
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -3773,7 +3773,9 @@ font-technology Variable Variable
Mojikumi Mojikumi
N pt per N pt per
New Character Style Ny tegnstil
New Character Style… Ny tegnstil…
New Paragraph Style Ny afsnitsstil
New Paragraph Style… Ny afsnitsstil…
Paragraph Style Options Indstillinger for afsnitsstil
Redefine Character Style Omdefinér tegnstil
Redefine Paragraph Style Omdefinér afsnitsstil
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -1976,6 +1976,7 @@ file-filter Templates Vorlagen
Brush Art Pinselgrafik
New Calligraphic Brush Neuer Kalligrafiepinsel
New Character Style Neues Zeichenformat
New Character Style… Neues Zeichenformat…
New Color Group Neue Farbgruppe
New Color Group… Neue Farbgruppe…
New Document Presets Vorgaben für neue Dokumente
@@ -1991,6 +1992,7 @@ file-filter Templates Vorlagen
New Opacity Masks Are Clipping Neue Deckkraftmasken sind Schnittmasken
New Opacity Masks Are Inverted Neue Deckkraftmasken sind invertiert
New Paragraph Style Neues Absatzformat
New Paragraph Style… Neues Absatzformat…
New Pattern Brush from Selection Neuer Musterpinsel aus Auswahl
New PDF Preset Neue PDF-Vorgabe
New Print Preset Neue Druckvorgabe
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -2749,6 +2749,7 @@ theme Appearance Εμφάνιση
Flat Fan Επίπεδη βεντάλια
New Calligraphic Brush Νέο καλλιγραφικό πινέλο
New Character Style Νέο στυλ χαρακτήρων
New Character Style… Νέο στυλ χαρακτήρων…
New Color Group Νέα ομάδα χρωμάτων
New Color Group… Νέα ομάδα χρωμάτων…
New Document Presets Νέες προεπιλογές εγγράφου
@@ -2763,6 +2764,7 @@ theme Appearance Εμφάνιση
New Opacity Masks Are Clipping Οι νέες μάσκες αδιαφάνειας εφαρμόζουν αποκοπή
New Opacity Masks Are Inverted Οι νέες μάσκες αδιαφάνειας είναι ανεστραμμένες
New Paragraph Style Νέο στυλ παραγράφου
New Paragraph Style… Νέο στυλ παραγράφου…
New Pattern Brush from Selection Νέο πινέλο μοτίβου από επιλογή
New PDF Preset Νέα προεπιλογή PDF
New Print Preset Νέα προεπιλογή εκτύπωσης
Can't render this file because it contains an unexpected character in line 164 and column 5.
+2
View File
@@ -2018,6 +2018,7 @@ font-technology Variable Variables
Brush Art Ilustración del pincel
New Calligraphic Brush Nuevo pincel caligráfico
New Character Style Nuevo estilo de carácter
New Character Style… Nuevo estilo de carácter…
New Color Group Nuevo grupo de colores
New Color Group… Nuevo grupo de colores…
New Document Presets Preajustes de documento nuevo
@@ -2033,6 +2034,7 @@ font-technology Variable Variables
New Opacity Masks Are Clipping Las nuevas máscaras de opacidad recortan
New Opacity Masks Are Inverted Las nuevas máscaras de opacidad se invierten
New Paragraph Style Nuevo estilo de párrafo
New Paragraph Style… Nuevo estilo de párrafo…
New Pattern Brush from Selection Nuevo pincel de motivo a partir de la selección
New PDF Preset Nuevo preajuste de PDF
New Print Preset Nuevo preajuste de impresión
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -3958,7 +3958,9 @@ font-technology Variable Muuttuvat
Mojikumi Mojikumi
N pt per N pt kohden
New Character Style Uusi merkkityyli
New Character Style… Uusi merkkityyli…
New Paragraph Style Uusi kappaletyyli
New Paragraph Style… Uusi kappaletyyli…
Paragraph Style Options Kappaletyylin asetukset
Redefine Character Style Määritä merkkityyli uudelleen
Redefine Paragraph Style Määritä kappaletyyli uudelleen
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -2015,6 +2015,7 @@ font-technology Variable Variables
Brush Art Illustration de la forme
New Calligraphic Brush Nouvelle forme calligraphique
New Character Style Nouveau style de caractère
New Character Style… Nouveau style de caractère…
New Color Group Nouveau groupe de couleurs
New Color Group… Nouveau groupe de couleurs…
New Document Presets Paramètres prédéfinis de nouveau document
@@ -2030,6 +2031,7 @@ font-technology Variable Variables
New Opacity Masks Are Clipping Les nouveaux masques d’opacité sont des masques d’écrêtage
New Opacity Masks Are Inverted Les nouveaux masques d’opacité sont inversés
New Paragraph Style Nouveau style de paragraphe
New Paragraph Style… Nouveau style de paragraphe…
New Pattern Brush from Selection Nouvelle forme de motif à partir de la sélection
New PDF Preset Nouveau paramètre prédéfini PDF
New Print Preset Nouveau paramètre prédéfini d’impression
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -2456,6 +2456,7 @@ file-filter Templates Sablonok
New Brush… Új ecset…
New Calligraphic Brush Új kalligrafikus ecset
New Character Style Új karakterstílus
New Character Style… Új karakterstílus…
New Color Group Új színcsoport
New Color Group… Új színcsoport…
New Data Set Új adatkészlet
@@ -2477,6 +2478,7 @@ file-filter Templates Sablonok
New Opacity Masks Are Clipping Új átlátszósági maszkok vágnak
New Opacity Masks Are Inverted Új átlátszósági maszkok invertáltak
New Paragraph Style Új bekezdésstílus
New Paragraph Style… Új bekezdésstílus…
New Pattern Brush from Selection Új mintázatecset a kijelölésből
New PDF Preset Új PDF-előbeállítás
New Print Preset Új nyomtatási előbeállítás
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -3773,7 +3773,9 @@ font-technology Variable Variabel
Mojikumi Mojikumi
N pt per N pt per
New Character Style Gaya karakter baru
New Character Style… Gaya karakter baru…
New Paragraph Style Gaya paragraf baru
New Paragraph Style… Gaya paragraf baru…
Paragraph Style Options Opsi gaya paragraf
Redefine Character Style Definisikan ulang gaya karakter
Redefine Paragraph Style Definisikan ulang gaya paragraf
Can't render this file because it contains an unexpected character in line 2169 and column 5.
+2
View File
@@ -2045,6 +2045,7 @@ file-filter Templates Modelli
Brush Art Grafica del pennello
New Calligraphic Brush Nuovo pennello calligrafico
New Character Style Nuovo stile carattere
New Character Style… Nuovo stile carattere…
New Color Group Nuovo gruppo colori
New Color Group… Nuovo gruppo colori…
New Document Presets Preset nuovo documento
@@ -2060,6 +2061,7 @@ file-filter Templates Modelli
New Opacity Masks Are Clipping Le nuove maschere di opacità ritagliano
New Opacity Masks Are Inverted Le nuove maschere di opacità sono invertite
New Paragraph Style Nuovo stile paragrafo
New Paragraph Style… Nuovo stile paragrafo…
New Pattern Brush from Selection Nuovo pennello motivo dalla selezione
New PDF Preset Nuovo preset PDF
New Print Preset Nuovo preset di stampa
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -2179,6 +2179,7 @@ font-technology Variable バリアブル
Brush Art ブラシのアート
New Calligraphic Brush 新規絵筆ブラシ
New Character Style 新規文字スタイル
New Character Style… 新規文字スタイル…
New Color Group 新規カラーグループ
New Color Group… 新規カラーグループ…
New Document Presets 新規ドキュメントプリセット
@@ -2192,6 +2193,7 @@ font-technology Variable バリアブル
New Opacity Masks Are Inverted 新規不透明マスクを反転に設定
New PDF Preset 新規 PDF プリセット
New Paragraph Style 新規段落スタイル
New Paragraph Style… 新規段落スタイル…
New Pattern Brush from Selection 選択範囲から新規パターンブラシ
New Print Preset 新規プリントプリセット
New Scatter Brush from Selection 選択範囲から新規散布ブラシ
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -3554,7 +3554,9 @@ font-technology Variable ცვლადი
Mojikumi Mojikumi
N pt per N pt თითოზე
New Character Style სიმბოლოს ახალი სტილი
New Character Style… სიმბოლოს ახალი სტილი…
New Paragraph Style აბზაცის ახალი სტილი
New Paragraph Style… აბზაცის ახალი სტილი…
Paragraph Style Options აბზაცის სტილის პარამეტრები
Redefine Character Style სიმბოლოს სტილის ხელახლა განსაზღვრა
Redefine Paragraph Style აბზაცის სტილის ხელახლა განსაზღვრა
Can't render this file because it is too large.
+2
View File
@@ -2174,6 +2174,7 @@ font-technology Variable 가변
Brush Art 브러시 아트
New Calligraphic Brush 새 캘리그래피 브러시
New Character Style 새 문자 스타일
New Character Style… 새 문자 스타일…
New Color Group 새 색상 그룹
New Color Group… 새 색상 그룹…
New Document Presets 새 문서 사전 설정
@@ -2187,6 +2188,7 @@ font-technology Variable 가변
New Opacity Masks Are Inverted 새 불투명 마스크를 반전으로 설정
New PDF Preset 새 PDF 사전 설정
New Paragraph Style 새 단락 스타일
New Paragraph Style… 새 단락 스타일…
New Pattern Brush from Selection 선택 항목으로 새 패턴 브러시 만들기
New Print Preset 새 인쇄 사전 설정
New Scatter Brush from Selection 선택 항목으로 새 흩뿌리기 브러시 만들기
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -3787,7 +3787,9 @@ font-technology Variable Variabel
Mojikumi Mojikumi
N pt per N pt per
New Character Style Nieuwe tekenstijl
New Character Style… Nieuwe tekenstijl…
New Paragraph Style Nieuwe alineastijl
New Paragraph Style… Nieuwe alineastijl…
Paragraph Style Options Alineastijlopties
Redefine Character Style Tekenstijl opnieuw definiëren
Redefine Paragraph Style Alineastijl opnieuw definiëren
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -3879,7 +3879,9 @@ font-technology Variable Zmienne
Mojikumi Mojikumi
N pt per N pt na
New Character Style Nowy styl znakowy
New Character Style… Nowy styl znakowy…
New Paragraph Style Nowy styl akapitowy
New Paragraph Style… Nowy styl akapitowy…
Paragraph Style Options Opcje stylu akapitowego
Redefine Character Style Zdefiniuj ponownie styl znakowy
Redefine Paragraph Style Zdefiniuj ponownie styl akapitowy
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -1632,6 +1632,7 @@
Brush Art Arte do Pincel
New Calligraphic Brush Novo Pincel Caligráfico
New Character Style Novo Estilo de Caractere
New Character Style… Novo Estilo de Caractere…
New Color Group Novo Grupo de Cores
New Color Group… Novo Grupo de Cores…
New Document Presets Novos Predefinidos de Documento
@@ -1643,6 +1644,7 @@
New Opacity Masks Are Inverted Novas Máscaras de Opacidade São Invertidas
New PDF Preset Novo Predefinido de PDF
New Paragraph Style Novo Estilo de Parágrafo
New Paragraph Style… Novo Estilo de Parágrafo…
New Pattern Brush from Selection Novo Pincel de Padrão a partir da Seleção
New Print Preset Novo Predefinido de Impressão
New Scatter Brush from Selection Novo Pincel de Dispersão a partir da Seleção
Can't render this file because it contains an unexpected character in line 6 and column 47.
+2
View File
@@ -2041,6 +2041,7 @@ file-filter Templates Шаблоны
Brush Art Рисунок кисти
New Calligraphic Brush Новая каллиграфическая кисть
New Character Style Новый стиль символа
New Character Style… Новый стиль символа…
New Color Group Новая цветовая группа
New Color Group… Новая цветовая группа…
New Document Presets Наборы новых документов
@@ -2056,6 +2057,7 @@ file-filter Templates Шаблоны
New Opacity Masks Are Clipping Новые непрозрачные маски обрезают
New Opacity Masks Are Inverted Новые непрозрачные маски инвертируются
New Paragraph Style Новый стиль абзаца
New Paragraph Style… Новый стиль абзаца…
New Pattern Brush from Selection Новая узорная кисть из выделенного
New PDF Preset Новый набор PDF
New Print Preset Новый набор печати
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -1963,6 +1963,7 @@ font-technology Variable Variabla
Brush Art Penselgrafik
New Calligraphic Brush Ny kalligrafisk pensel
New Character Style Ny teckenstil
New Character Style… Ny teckenstil…
New Color Group Ny färggrupp
New Color Group… Ny färggrupp…
New Document Presets Förinställningar för nytt dokument
@@ -1978,6 +1979,7 @@ font-technology Variable Variabla
New Opacity Masks Are Clipping Nya opacitetsmasker är klippande
New Opacity Masks Are Inverted Nya opacitetsmasker är inverterade
New Paragraph Style Ny styckestil
New Paragraph Style… Ny styckestil…
New Pattern Brush from Selection Ny mönsterpensel från markeringen
New PDF Preset Ny PDF-förinställning
New Print Preset Ny utskriftsförinställning
Can't render this file because it contains an unexpected character in line 5 and column 25.
+2
View File
@@ -1844,6 +1844,7 @@ font-technology Variable Değişken
New Brush… Yeni Fırça…
New Calligraphic Brush Yeni Kaligrafi Fırçası
New Character Style Yeni Karakter Stili
New Character Style… Yeni Karakter Stili…
New Color Group Yeni Renk Grubu
New Color Group… Yeni Renk Grubu…
New Document Presets Yeni Belge Ön Ayarları
@@ -1858,6 +1859,7 @@ font-technology Variable Değişken
New Opacity Masks Are Clipping Yeni Opaklık Maskeleri Kırpılıyor
New Opacity Masks Are Inverted Yeni Opaklık Maskeleri Ters Çevrildi
New Paragraph Style Yeni Paragraf Stili
New Paragraph Style… Yeni Paragraf Stili…
New Pattern Brush from Selection Seçimden Yeni Desen Fırçası
New PDF Preset Yeni PDF Hazır Ayarı
New Print Preset Yeni Yazdırma Ön Ayarı
Can't render this file because it contains an unexpected character in line 5 and column 25.
+2
View File
@@ -2037,6 +2037,7 @@ file-filter Templates Шаблони
Brush Art Малюнок пензля
New Calligraphic Brush Новий каліграфічний пензель
New Character Style Новий стиль символу
New Character Style… Новий стиль символу…
New Color Group Нова колірна група
New Color Group… Нова колірна група…
New Document Presets Набір нових документів
@@ -2052,6 +2053,7 @@ file-filter Templates Шаблони
New Opacity Masks Are Clipping Нові маски непрозорості відсікають
New Opacity Masks Are Inverted Нові маски непрозорості інвертуються
New Paragraph Style Новий стиль абзацу
New Paragraph Style… Новий стиль абзацу…
New Pattern Brush from Selection Новий візерунковий пензель з виділеного
New PDF Preset Новий набір PDF
New Print Preset Новий набір друку
Can't render this file because it contains an unexpected character in line 178 and column 5.
+2
View File
@@ -2263,6 +2263,7 @@ file-filter Templates Mẫu
New Brush… Cọ mới…
New Calligraphic Brush Cọ thư pháp mới
New Character Style Kiểu ký tự mới
New Character Style… Kiểu ký tự mới…
New Color Group Nhóm màu mới
New Color Group… Nhóm màu mới…
New Data Set Tập dữ liệu mới
@@ -2285,6 +2286,7 @@ file-filter Templates Mẫu
New Opacity Masks Are Clipping Mặt nạ độ mờ mới là cắt
New Opacity Masks Are Inverted Mặt nạ độ mờ mới bị đảo ngược
New Paragraph Style Kiểu đoạn văn mới
New Paragraph Style… Kiểu đoạn văn mới…
New Pattern Brush from Selection Cọ họa tiết mới từ vùng chọn
New PDF Preset Thiết lập sẵn PDF mới
New Print Preset Thiết lập sẵn in mới
Can't render this file because it contains an unexpected character in line 200 and column 5.
+2
View File
@@ -1918,6 +1918,7 @@ file-filter Templates 模板
Brush Art 画笔图稿
New Calligraphic Brush 新建书法画笔
New Character Style 新建字符样式
New Character Style… 新建字符样式…
New Color Group 新建颜色组
New Color Group… 新建颜色组…
New Document Presets 新建文档预设
@@ -1931,6 +1932,7 @@ file-filter Templates 模板
New Opacity Masks Are Clipping 新建不透明度蒙版为剪切
New Opacity Masks Are Inverted 新建不透明度蒙版为反相
New Paragraph Style 新建段落样式
New Paragraph Style… 新建段落样式…
New Pattern Brush from Selection 从选区新建图案画笔
New PDF Preset 新建 PDF 预设
New Print Preset 新建打印预设
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -2841,6 +2841,7 @@ file-filter Templates 範本
Brush Art 筆刷圖稿
New Calligraphic Brush 新增書法筆刷
New Character Style 新增字元樣式
New Character Style… 新增字元樣式…
New Color Group 新增顏色群組
New Color Group… 新增顏色群組…
New Document Presets 新增文件預設集
@@ -2856,6 +2857,7 @@ file-filter Templates 範本
New Opacity Masks Are Clipping 新的不透明度遮色片為剪裁
New Opacity Masks Are Inverted 新的不透明度遮色片為反轉
New Paragraph Style 新增段落樣式
New Paragraph Style… 新增段落樣式…
New Pattern Brush from Selection 從選取範圍新增圖樣筆刷
New PDF Preset 新增 PDF 預設集
New Print Preset 新增列印預設集
Can't render this file because it contains an unexpected character in line 6 and column 47.
+2
View File
@@ -587,6 +587,8 @@ fn run_ui_part7(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option<Result<
"ui.colorPicker" => crate::dialogs::open_color_picker(app, p),
"ui.sampleScreenColor" => crate::screen_color::start(app, p),
"ui.graphicStyleOptions" => crate::dialogs::graphic_style_options::open(app, s("name").as_deref()),
"ui.newCharStyle" => crate::panels::text_styles::open_new(app, crate::panels::text_styles::Kind::Char),
"ui.newParaStyle" => crate::panels::text_styles::open_new(app, crate::panels::text_styles::Kind::Para),
"ui.symbolOptions" => {
let name = s("name");
let edit = p.get("edit").and_then(Value::as_bool).unwrap_or(false);
+1 -1
View File
@@ -221,7 +221,7 @@ pub fn enabled(app: &VectorcraftApp, id: &str) -> bool {
"ui.missingFontsDialog" => !cfg!(target_arch = "wasm32") && app.session.active().is_some(),
"ui.findFontsInFolder" => crate::picks::can(app, &crate::picks::PickRequest::Folder) && app.session.active().is_some(),
"ui.swatchOptions" | "ui.newSwatch" | "ui.newColorGroup" => app.session.active().is_some(),
"ui.graphicStyleOptions" => app.session.active().is_some(),
"ui.graphicStyleOptions" | "ui.newCharStyle" | "ui.newParaStyle" => app.session.active().is_some(),
"ui.newArtboard" => app.session.active().is_some(),
"ui.sampleScreenColor" => crate::screen_color::available(app),
"ui.newBrush" => app.session.active().is_some(),
+12
View File
@@ -633,6 +633,18 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[
"",
"{name?} open Graphic Style Options (dialog `graphicStyleOptions`, field `name`): for style `name` OK renames it (graphicStyle.rename); without, OK makes a new style of that name from the selection (graphicStyle.new)",
),
(
"ui.newCharStyle",
"New Character Style…",
"",
"{} open New Character Style (dialog `newTextStyle`, field `name`, filled in with the name charStyle.new {} would give the style), what Alt-clicking the Character Styles panel's New button opens; OK runs charStyle.new with the name and the Type tool's selected characters (else the selected type) as they were when the dialog opened; Cancel adds no style",
),
(
"ui.newParaStyle",
"New Paragraph Style…",
"",
"{} open New Paragraph Style (dialog `newTextStyle`, field `name`, filled in with the name paraStyle.new {} would give the style), what Alt-clicking the Paragraph Styles panel's New button opens; OK runs paraStyle.new with the name and the paragraph of the Type tool's selection (else the selected type) as it was when the dialog opened; Cancel adds no style",
),
(
"ui.symbolOptions",
"Symbol Options…",
+15 -3
View File
@@ -2,14 +2,15 @@
//!
//! Click a style to apply it to the selected text (or the Type tool's selected characters);
//! Alt-click applies it and clears overrides. The style of the selection is highlighted, with a
//! "+" when the text overrides some of its attributes.
//! "+" when the text overrides some of its attributes. New makes a style from the selected text;
//! Alt-click on New first asks for its name (`ui.newCharStyle`, `ui.newParaStyle`).
use egui::{Sense, Ui, vec2};
use serde_json::{Map, Value, json};
use vectorcraft_engine::cmd::textstyles;
use super::character::{end_typing, text_editing, text_style};
use super::{pstate, selection_len, set_pstate};
use super::{alt_held, pstate, selection_len, set_pstate};
use crate::VectorcraftApp;
use crate::theme::Tokens;
use crate::widgets::{self, menu_item};
@@ -106,6 +107,15 @@ fn target(app: &VectorcraftApp, kind: Kind, mut p: Value) -> Value {
p
}
/// Open New Character Style (`Kind::Para`: New Paragraph Style) to name a style made from the
/// selected text: `ui.newCharStyle` and `ui.newParaStyle`.
pub(crate) fn open_new(app: &mut VectorcraftApp, kind: Kind) -> Result<Value, String> {
// A later typing preview must not restore the document from before this capture.
end_typing(app);
let t = target(app, kind, json!({}));
crate::dialogs::new_text_style::open(app, kind == Kind::Para, t)
}
pub fn show(app: &mut VectorcraftApp, ui: &mut Ui, kind: Kind) {
let t = Tokens::get(ui.ctx());
let all = styles(app, kind);
@@ -176,7 +186,9 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui, kind: Kind) {
}
} else if new.clicked() {
end_typing(app);
if let Ok(r) = app.run(&format!("{}.new", kind.prefix()), target(app, kind, json!({}))) {
if alt_held(ui) {
app.run(if kind == Kind::Char { "ui.newCharStyle" } else { "ui.newParaStyle" }, json!({})).ok();
} else if let Ok(r) = app.run(&format!("{}.new", kind.prefix()), target(app, kind, json!({}))) {
highlight_made(ui.ctx(), kind, r["name"].as_str().map(str::to_string));
}
}
@@ -537,6 +537,129 @@ fn click_new_style_footer_range(ctx: &egui::Context, app: &mut VectorcraftApp, i
assert_eq!(text_editing(app), Some((id, start, end)), "footer capture preserves the Type range");
}
/// Alt-click the production bottom bar's New button of the `kind` panel, then draw the dialog
/// it opened: every text the last frame drew.
fn alt_click_new(ctx: &egui::Context, app: &mut VectorcraftApp, kind: Kind) -> Vec<String> {
click_new_with(ctx, app, kind, egui::Modifiers::ALT)
}
fn click_new(ctx: &egui::Context, app: &mut VectorcraftApp, kind: Kind) {
click_new_with(ctx, app, kind, egui::Modifiers::NONE);
}
fn click_new_with(ctx: &egui::Context, app: &mut VectorcraftApp, kind: Kind, modifiers: egui::Modifiers) -> Vec<String> {
let pos = std::cell::Cell::new(egui::Pos2::ZERO);
let mut draw = |app: &mut VectorcraftApp, ui: &mut egui::Ui| {
ui.set_width(280.0);
let right = ui.max_rect().right();
show(app, ui, kind);
pos.set(egui::pos2(right - 40.0, ui.min_rect().bottom() - 12.0));
};
frame_events(ctx, app, vec![], &mut draw);
frame_events(ctx, app, vec![], &mut draw);
let at = pos.get();
let button = |pressed| egui::Event::PointerButton { pos: at, button: egui::PointerButton::Primary, pressed, modifiers };
for e in [egui::Event::PointerMoved(at), button(true), button(false)] {
frame_events(ctx, app, vec![egui::Event::ModifiersChanged(modifiers), e], &mut draw);
}
// A window draws its content from its second frame.
frame_events(ctx, app, vec![], |app, ui| crate::dialogs::show(app, ui.ctx()));
frame_events(ctx, app, vec![], |app, ui| crate::dialogs::show(app, ui.ctx())).into_iter().map(|(s, _)| s).collect()
}
fn set_name(app: &mut VectorcraftApp, name: &str) {
app.ui.dialog.as_mut().expect("the dialog is open").fields.insert("name".into(), json!(name));
}
/// Alt-click on New Character Style opens New Character Style with the name a plain click's
/// style gets, and adds no style yet. Cancel adds none. OK makes the style with the typed name
/// from the Type tool's selected characters, in one undo step, and a name another style has keeps
/// the dialog open.
#[test]
fn alt_clicking_new_character_style_asks_for_its_name() {
let (mut app, id, ctx) = capture_app();
select_type_range(&mut app, id, 3, 5);
let names = |app: &VectorcraftApp| app.session.doc().unwrap().doc.char_styles.iter().map(|s| s.name.clone()).collect::<Vec<_>>();
let before = names(&app);
let shown = alt_click_new(&ctx, &mut app, Kind::Char);
let d = app.ui.dialog.as_ref().expect("Alt-click on New opens a dialog");
assert_eq!((d.kind.as_str(), d.str("name").as_str()), (crate::dialogs::new_text_style::KIND, "Character Style 1"));
assert!(shown.iter().any(|s| s == "New Character Style"), "the dialog's heading: {shown:?}");
assert_eq!(names(&app), before, "no style is added before OK");
crate::dialogs::cancel(&mut app);
assert_eq!(names(&app), before, "Cancel adds no style");
alt_click_new(&ctx, &mut app, Kind::Char);
set_name(&mut app, "Untouched");
assert!(crate::dialogs::confirm(&mut app).is_err(), "names are unique");
assert!(app.ui.dialog.is_some(), "a taken name keeps the dialog open");
set_name(&mut app, "Heading");
let undo = app.session.doc().unwrap().history.undo.len();
crate::dialogs::confirm(&mut app).unwrap();
assert!(app.ui.dialog.is_none());
let doc = &app.session.doc().unwrap().doc;
let heading = doc.char_styles.iter().find(|s| s.name == "Heading").expect("OK makes the style");
assert_eq!(heading.attrs.get("size").and_then(Value::as_f64), Some(34.0), "made from the selected characters");
assert_eq!(doc.char_styles.len(), before.len() + 1);
assert_eq!(app.session.doc().unwrap().history.undo.len(), undo + 1);
assert_eq!(text_editing(&app), Some((id, 3, 5)), "the Type selection stays");
}
/// OK makes the style from the text's attributes as they were when the dialog opened: a later
/// format change of the Type selection, or a change of the selected objects, does not reach it.
#[test]
fn new_style_dialog_keeps_the_attributes_from_when_it_opened() {
let (mut app, id, ctx) = capture_app();
select_type_range(&mut app, id, 3, 5);
alt_click_new(&ctx, &mut app, Kind::Char);
app.run("text.setRangeStyle", json!({"id": id.0, "start": 2, "end": 6, "size": 50})).unwrap();
set_name(&mut app, "Range");
crate::dialogs::confirm(&mut app).unwrap();
app.session.select_tool("selection", Default::default()).unwrap();
app.run("select.set", json!({"ids": [id.0]})).unwrap();
assert_eq!(text_editing(&app), None);
alt_click_new(&ctx, &mut app, Kind::Char);
let count = app.session.doc().unwrap().doc.char_styles.len();
app.run("text.setRangeStyle", json!({"id": id.0, "start": 0, "end": 2, "size": 60})).unwrap();
set_name(&mut app, "Object");
crate::dialogs::confirm(&mut app).unwrap();
let doc = &app.session.doc().unwrap().doc;
assert_eq!(doc.char_styles.len(), count + 1);
let size = |n: &str| doc.char_styles.iter().find(|s| s.name == n).and_then(|s| s.attrs.get("size")).and_then(Value::as_f64);
assert_eq!((size("Range"), size("Object")), (Some(34.0), Some(18.0)));
}
/// Alt-click on New Paragraph Style opens New Paragraph Style for the selected type's paragraph;
/// a plain click still makes the style at once.
#[test]
fn alt_clicking_new_paragraph_style_asks_for_its_name() {
let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default());
app.run("file.new", json!({"width": 300, "height": 200})).unwrap();
let id = app.run("text.create", json!({"x": 20, "y": 50, "text": "Body"})).unwrap()["id"].clone();
app.run("select.set", json!({"ids": [id]})).unwrap();
app.run("text.setFormat", json!({"composer": "singleLine"})).unwrap();
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
let shown = alt_click_new(&ctx, &mut app, Kind::Para);
let d = app.ui.dialog.as_ref().expect("Alt-click on New opens a dialog");
assert_eq!((d.kind.as_str(), d.str("name").as_str()), (crate::dialogs::new_text_style::KIND, "Paragraph Style 1"));
assert!(shown.iter().any(|s| s == "New Paragraph Style"), "the dialog's heading: {shown:?}");
assert!(app.session.doc().unwrap().doc.para_styles.is_empty(), "no style is added before OK");
set_name(&mut app, "Body Copy");
crate::dialogs::confirm(&mut app).unwrap();
let (_, attrs) = current(&app, Kind::Para).unwrap();
let doc = &app.session.doc().unwrap().doc;
assert_eq!(doc.para_styles.iter().map(|s| (s.name.as_str(), &s.attrs)).collect::<Vec<_>>(), [("Body Copy", &attrs)]);
// A plain click still makes "Paragraph Style 1" at once, with no dialog.
click_new(&ctx, &mut app, Kind::Para);
assert!(app.ui.dialog.is_none());
let names: Vec<_> = app.session.doc().unwrap().doc.para_styles.iter().map(|s| s.name.clone()).collect();
assert_eq!(names, ["Body Copy", "Paragraph Style 1"]);
}
fn create_redefined_texts(app: &mut VectorcraftApp, ctx: &egui::Context, id: NodeId) -> (Option<NodeId>, Option<NodeId>) {
let mut follower = None;
let mut local_override = None;
+5
View File
@@ -156,6 +156,11 @@ dialog open); without `name` it names a new style made from the selection (`grap
`ui.mergeGraphicStyles {names}` opens the same dialog to name the style `ui.dialog.confirm` merges from them
(`graphicStyle.merge`). Deleting styles from the panel asks first with a `confirm` dialog (`graphicStyle.delete`).
Text styles: `ui.newCharStyle` and `ui.newParaStyle`, what Alt-click on New in the Character Styles and Paragraph
Styles panels runs, open the `newTextStyle` dialog (field `name`) for a style made from the text selected when it
opened; `ui.dialog.confirm` makes it with `charStyle.new` / `paraStyle.new` (a name another style has returns an error
and keeps the dialog open).
Symbols: `ui.symbolOptions` opens the `symbolOptions` dialog (field `name`) that the Symbols panel's New Symbol asks
first; `ui.dialog.confirm` makes the symbol from the selection with that name (`symbol.new`). F8 runs
`ui.symbolOptions` without params.
+7
View File
@@ -4955,6 +4955,13 @@ inserting `\n`) continues the style of the paragraph it splits; deleting a break
`text.setText` gives every paragraph the first one's attributes. Threaded text keeps each paragraph's attributes as
the story re-flows (a paragraph split between frames has them in both).
`ui.newCharStyle` and `ui.newParaStyle` (Alt-click on New in the Character Styles or Paragraph Styles panel) open
New Character Style or New Paragraph Style (dialog `newTextStyle`, field `name`) with the name `charStyle.new {}` or
`paraStyle.new {}` would give the style, and add no style yet. OK runs `charStyle.new` or `paraStyle.new` with the
name and the Type tool's selection as it was when the dialog opened (else the selected type); a name another style
has, or an empty one, keeps the dialog open, and Cancel adds no style. A plain click on New still makes the style at
once.
In the native file a text object's `para` holds the first paragraph's attributes and `paras` (left out when every
paragraph is alike) one entry per paragraph, so older readers keep the first paragraph's. SVG export anchors each
centred or right-aligned line on its own (`<tspan text-anchor>`), and SVG import gives each line's paragraph its
+1
View File
@@ -498,6 +498,7 @@ since 2026-10-01.
- **Auto Leading outside the layout:** a paragraph's Justification… › Auto Leading (<https://helpx.adobe.com/illustrator/desktop/design-with-text/edit-format-text/adjust-word-and-letterspacing-in-justified-text.html>) also sets the Leading the Character panel shows for Auto type (blank where the selected paragraphs differ), the leading the Alt+↑/↓ nudge (`type.step`) steps from, each paragraph's own, and the lines Fill with Placeholder Text counts in area type. Evidence: `ParaStyle::effective_leading` (`crates/doc/src/text.rs` test `paragraph_effective_leading_follows_auto_leading`), `tests_prefs::auto_leading_steps_and_fills_from_the_paragraph_auto_leading`, `panels::character` test `leading_field_shows_the_paragraph_auto_leading`.
- **The text style panel menus' Duplicate and Delete:** in the Character Styles and Paragraph Styles panel menus, Duplicate highlights the copy, as Create New Style highlights the new style, and Delete leaves no style highlighted, as the Delete button does, so Duplicate, Clear Overrides, Redefine and Style Options are disabled until another style is clicked. A highlighted style that is renamed in Style Options or deleted by a command is no longer highlighted either. The tests `panels::text_styles::tests::panel_menu_duplicate_and_delete_update_the_highlighted_style` and `a_highlighted_style_that_is_no_longer_listed_is_not_highlighted` cover this. Public reference: [Character and paragraph styles](https://web.archive.org/web/20261015081534/https://helpx.adobe.com/illustrator/using/character-paragraph-styles.html).
- **Names of new styles and symbols:** `charStyle.new` and `paraStyle.new` trim the white space around the name they are given, as `charStyle.rename` and `paraStyle.rename` do, and refuse a name that is then empty; Duplicate names its copy from the trimmed name. `symbol.new` and `symbol.duplicate` trim the name as Symbol Options does, and `library.use` adds a library's text style under its trimmed name. Public reference: [Character and paragraph styles](https://web.archive.org/web/20260825113735/https://helpx.adobe.com/illustrator/using/character-paragraph-styles.html).
- **Naming a new text style first:** Alt-click on New in the Character Styles or Paragraph Styles panel opens New Character Style or New Paragraph Style (`ui.newCharStyle`, `ui.newParaStyle`) with the name a plain click's style gets; OK makes the style from the text selected when the dialog opened, and Cancel adds none. Tests `panels::text_styles::tests::alt_clicking_new_character_style_asks_for_its_name` and `alt_clicking_new_paragraph_style_asks_for_its_name`. Public reference: [Default keyboard shortcuts](https://web.archive.org/web/20260726060015/https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html), "Set options (except for Actions, Brushes, Swatches, and Symbols panels)".
- **Width Point Edit:** Shift-double-click enables Adjust Adjoining Width Points; plain double-click and direct opening retain the default. Tool dispatch and headless dialog tests cover neighboring widths, Undo/Redo and Cancel. Public reference: [Variable-width strokes](https://helpx.adobe.com/illustrator/using/stroke-object.html).
Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a feature lands.