Character: Cmd/Ctrl+Alt+K highlights the kerning and Cmd/Ctrl+Alt+Shift+F highlights the font name

Squashed from #4390 onto main.
This commit is contained in:
dotCooCoo
2026-10-29 20:48:21 +07:00
committed by bflatastic
parent 5fa538ee1c
commit 41dda1029f
38 changed files with 325 additions and 5 deletions
+1
View File
@@ -79,6 +79,7 @@ impl VectorcraftApp {
titlebar_inset: 0.0,
last_effect: None,
last_warp: None,
character_focus: None,
run_count: 0,
clipboard_out: None,
clipboard_published: None,
+3
View File
@@ -5292,6 +5292,9 @@ theme Appearance Външен вид
# Type › Reset Tracking and Kerning
Reset Tracking and Kerning Нулиране на разредката и кернинга
Reset Horizontal Scale Нулиране на хоризонталния мащаб
Highlight Font Name Маркиране на името на шрифта
Highlight Kerning Маркиране на кернинга
Highlight Tracking Маркиране на разредката
@msg span must be "object" or "selection", got {v} span трябва да е "object" или "selection", а не {v}
@msg span: "selection" places a `gradient`, not a swatch span: "selection" поставя `gradient`, а не мостра
@msg span: "selection" places a gradient: give `gradient` span: "selection" поставя градиент: задайте `gradient`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -3781,6 +3781,9 @@ axis Both Obě
Increase Font Size by Five Steps Zvětšit velikost písma o pět kroků
Reset Tracking and Kerning Obnovit prostrkání a vyrovnání párů
Reset Horizontal Scale Obnovit vodorovnou změnu velikosti
Highlight Font Name Zvýraznit název písma
Highlight Kerning Zvýraznit vyrovnání párů
Highlight Tracking Zvýraznit prostrkání
Step Type Krokovat text
Get Text Range Získat rozsah textu
Punctuation Interpunkce
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -5062,6 +5062,9 @@ mark-style Japanese Japansk
Panels Except Tools and Control Paneler undtagen værktøjer og kontrol
Reset Tracking and Kerning Nulstil sporing og kerning
Reset Horizontal Scale Nulstil vandret skalering
Highlight Font Name Fremhæv skrifttypenavn
Highlight Kerning Fremhæv kerning
Highlight Tracking Fremhæv sporing
@msg span must be "object" or "selection", got {v} span skal være "object" eller "selection", fik {v}
@msg span: "selection" places a `gradient`, not a swatch span: "selection" placerer en `gradient`, ikke en farveprøve
@msg span: "selection" places a gradient: give `gradient` span: "selection" placerer en overgang: angiv `gradient`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -1126,6 +1126,9 @@ font-technology Variable Variabel
Increase Font Size by Five Steps Schriftgrad um fünf Schritte erhöhen
Reset Tracking and Kerning Laufweite und Kerning zurücksetzen
Reset Horizontal Scale Horizontale Skalierung zurücksetzen
Highlight Font Name Schriftname hervorheben
Highlight Kerning Kerning hervorheben
Highlight Tracking Laufweite hervorheben
Spacing Abstand
Step Type Text schrittweise ändern
Fit in View In Ansicht einpassen
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -5281,6 +5281,9 @@ effect Points Σημεία
Registration: Σημείο αναφοράς:
Reset Horizontal Scale Επαναφορά οριζόντιας κλίμακας
Reset Tracking and Kerning Επαναφορά απόστασης χαρακτήρων και διαγραμμάτωσης
Highlight Font Name Επισήμανση ονόματος γραμματοσειράς
Highlight Kerning Επισήμανση διαγραμμάτωσης
Highlight Tracking Επισήμανση απόστασης χαρακτήρων
Sample Color from Screen Δειγματοληψία χρώματος από την οθόνη
Set as Default Ορισμός ως προεπιλογή
Show Template Εμφάνιση προτύπου
Can't render this file because it contains an unexpected character in line 2090 and column 19.
+3
View File
@@ -1149,6 +1149,9 @@ font-technology Variable Variables
Increase Font Size by Five Steps Aumentar tamaño de fuente en cinco incrementos
Reset Tracking and Kerning Restablecer espaciado e interletraje
Reset Horizontal Scale Restablecer escala horizontal
Highlight Font Name Resaltar nombre de fuente
Highlight Kerning Resaltar interletraje
Highlight Tracking Resaltar espaciado
Spacing Espaciado
Step Type Ajustar texto por incrementos
Fit in View Encajar en la vista
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -5293,6 +5293,9 @@ mark-style Japanese Japanilainen
Registration: Kohdistuspiste:
Reset Horizontal Scale Palauta vaakaskaalaus
Reset Tracking and Kerning Palauta merkkiväli ja parivälistys
Highlight Font Name Korosta fontin nimi
Highlight Kerning Korosta parivälistys
Highlight Tracking Korosta merkkiväli
Sample Color from Screen Poimi väri näytöltä
Set as Default Aseta oletukseksi
Show Template Näytä mallipohja
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -1148,6 +1148,9 @@ font-technology Variable Variables
Increase Font Size by Five Steps Augmenter la taille de police de cinq incréments
Reset Tracking and Kerning Réinitialiser l’approche et le crénage
Reset Horizontal Scale Réinitialiser la mise à l’échelle horizontale
Highlight Font Name Surligner le nom de la police
Highlight Kerning Surligner le crénage
Highlight Tracking Surligner l’approche
Spacing Espacement
Step Type Texte par incréments
Fit in View Ajuster à la vue
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -3201,6 +3201,9 @@ mark-style Japanese Japán
Reset to White, Black Visszaállítás fehér–fekete átmenetre
Reset Tracing Tracing visszaállítása
Reset Tracking and Kerning Betűköz és alávágás visszaállítása
Highlight Font Name Betűtípusnév kiemelése
Highlight Kerning Alávágás kiemelése
Highlight Tracking Betűköz kiemelése
Reset Typography Typography visszaállítása
Reset Web Web visszaállítása
Reset with Mesh Visszaállítás hálóval
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -5062,6 +5062,9 @@ mark-style Japanese Jepang
Panels Except Tools and Control Semua panel kecuali Alat dan Kontrol
Reset Tracking and Kerning Atur ulang tracking dan kerning
Reset Horizontal Scale Atur ulang skala horizontal
Highlight Font Name Sorot nama font
Highlight Kerning Sorot kerning
Highlight Tracking Sorot tracking
@msg span must be "object" or "selection", got {v} span harus "object" atau "selection", diterima {v}
@msg span: "selection" places a `gradient`, not a swatch span: "selection" menempatkan `gradient`, bukan swatch
@msg span: "selection" places a gradient: give `gradient` span: "selection" menempatkan gradien: berikan `gradient`
Can't render this file because it contains an unexpected character in line 3875 and column 19.
+3
View File
@@ -1149,6 +1149,9 @@ font-technology Variable Variabili
Increase Font Size by Five Steps Aumenta dimensione font di cinque incrementi
Reset Tracking and Kerning Reimposta spaziatura caratteri e crenatura
Reset Horizontal Scale Reimposta scala orizzontale
Highlight Font Name Evidenzia nome del font
Highlight Kerning Evidenzia crenatura
Highlight Tracking Evidenzia spaziatura caratteri
Spacing Spaziatura
Step Type Dimensione testo a incrementi
Fit in View Adatta alla vista
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -380,6 +380,9 @@ font-technology Variable バリアブル
Increase Font Size by Five Steps フォントサイズを5段階大きく
Reset Tracking and Kerning トラッキングとカーニングをリセット
Reset Horizontal Scale 水平比率をリセット
Highlight Font Name フォント名をハイライト
Highlight Kerning カーニングをハイライト
Highlight Tracking トラッキングをハイライト
Step Type 文字を段階的に調整
Find Font… フォントの検索と置換…
Change Case 大文字と小文字の変更
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -4924,6 +4924,9 @@ imageTrace Outline კონტური
The active document changed while the screen color sampler was open აქტიური დოკუმენტი შეიცვალა, სანამ ეკრანიდან ფერის აღება ღია იყო
Keep selected მონიშნულად დატოვება
Reset Tracking and Kerning ტრეკინგისა და კერნინგის ჩამოყრა
Highlight Font Name შრიფტის სახელის გამოკვეთა
Highlight Kerning კერნინგის გამოკვეთა
Highlight Tracking ტრეკინგის გამოკვეთა
Legacy (version 7) ძველი (ვერსია 7)
Transparent Images: გამჭვირვალე გამოსახულებები:
Flatten გაბრტყელება
Can't render this file because it is too large.
+3
View File
@@ -376,6 +376,9 @@ font-technology Variable 가변
Increase Font Size by Five Steps 글꼴 크기 5단계 키우기
Reset Tracking and Kerning 자간 및 커닝 재설정
Reset Horizontal Scale 가로 비율 재설정
Highlight Font Name 글꼴 이름 강조 표시
Highlight Kerning 커닝 강조 표시
Highlight Tracking 자간 강조 표시
Step Type 단계별 문자
Find Font… 글꼴 찾기…
Change Case 대소문자 변경
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -5074,6 +5074,9 @@ mark-style Japanese Japans
Panels Except Tools and Control Alle panelen behalve Gereedschap en Bediening
Reset Tracking and Kerning Tracking en kerning herstellen
Reset Horizontal Scale Horizontale schaal herstellen
Highlight Font Name Lettertypenaam markeren
Highlight Kerning Kerning markeren
Highlight Tracking Tracking markeren
@msg span must be "object" or "selection", got {v} span moet "object" of "selection" zijn, maar is {v}
@msg span: "selection" places a `gradient`, not a swatch span: "selection" plaatst een `gradient`, geen staal
@msg span: "selection" places a gradient: give `gradient` span: "selection" plaatst een verloop: geef `gradient`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -3623,6 +3623,9 @@ axis Both Obie
Increase Font Size by Five Steps Zwiększ rozmiar czcionki o pięć kroków
Reset Tracking and Kerning Resetuj światło i kerning
Reset Horizontal Scale Resetuj skalę poziomą
Highlight Font Name Wyróżnij nazwę czcionki
Highlight Kerning Wyróżnij kerning
Highlight Tracking Wyróżnij światło
Step Type Krok tekstu
Get Text Range Pobierz zakres tekstu
Punctuation Interpunkcja
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -1020,6 +1020,9 @@
Increase Font Size by Five Steps Aumentar Tamanho da Fonte em Cinco Incrementos
Reset Tracking and Kerning Redefinir Rastreamento e Kerning
Reset Horizontal Scale Redefinir Escala Horizontal
Highlight Font Name Destacar Nome da Fonte
Highlight Kerning Destacar Kerning
Highlight Tracking Destacar Rastreamento
Step Type Ajustar Texto em Incrementos
Fit dashes to corners and ends Ajustar tracejados aos cantos e extremidades
Fit in Window Ajustar na Janela
Can't render this file because it contains an unexpected character in line 6 and column 47.
+3
View File
@@ -1147,6 +1147,9 @@ font-technology Variable Вариативные
Increase Font Size by Five Steps Увеличить размер шрифта на пять шагов
Reset Tracking and Kerning Сбросить трекинг и кернинг
Reset Horizontal Scale Сбросить масштаб по горизонтали
Highlight Font Name Выделить имя шрифта
Highlight Kerning Выделить кернинг
Highlight Tracking Выделить трекинг
Spacing Интервал
Step Type Подгонять текст по шагам
Fit in View Вписать в вид
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -5285,6 +5285,9 @@ theme Appearance Utseende
Registration: Registreringspunkt:
Reset Horizontal Scale Återställ horisontell skala
Reset Tracking and Kerning Återställ spärrning och kerning
Highlight Font Name Framhäv typsnittsnamn
Highlight Kerning Framhäv kerning
Highlight Tracking Framhäv spärrning
Sample Color from Screen Hämta färg från skärmen
Set as Default Ange som standard
Show Template Visa mall
Can't render this file because it contains an unexpected character in line 5 and column 25.
+3
View File
@@ -1108,6 +1108,9 @@ font-technology Variable Değişken
Increase Font Size by Five Steps Yazı Tipi Boyutunu Beş Adım Artırın
Reset Tracking and Kerning Takip ve Karakter Aralığını Sıfırla
Reset Horizontal Scale Yatay Ölçeği Sıfırla
Highlight Font Name Yazı Tipi Adını Vurgula
Highlight Kerning Karakter Aralığını Vurgula
Highlight Tracking Takibi Vurgula
Spacing Aralık
Step Type Adım Türü
Fit in View Görünüme Sığdır
Can't render this file because it contains an unexpected character in line 5 and column 25.
+3
View File
@@ -1143,6 +1143,9 @@ font-technology Variable Варіативні
Increase Font Size by Five Steps Збільшити розмір шрифту на п’ять кроків
Reset Tracking and Kerning Скинути трекінг і кернінг
Reset Horizontal Scale Скинути масштаб по горизонталі
Highlight Font Name Виділити назву шрифту
Highlight Kerning Виділити кернінг
Highlight Tracking Виділити трекінг
Spacing Інтервал
Step Type Підганяти текст кроками
Fit in View Вписати у вигляд
Can't render this file because it contains an unexpected character in line 749 and column 40.
+3
View File
@@ -2962,6 +2962,9 @@ mark-style Japanese Kiểu Nhật
Reset to default Đặt lại về mặc định
Reset to White, Black Đặt lại về Trắng, Đen
Reset Tracking and Kerning Đặt lại giãn chữ và kerning
Highlight Font Name Đánh dấu tên phông
Highlight Kerning Đánh dấu kerning
Highlight Tracking Đánh dấu giãn chữ
Reset with Mesh Đặt lại với lưới
Reset with Mesh… Đặt lại với lưới…
Reset with Warp Đặt lại với biến dạng
1 # Vietnamese (vi) catalog. Same format as zh-hant.tsv: context <TAB> source <TAB> translation
2962
2963
2964
2965
2966
2967
2968
2969
2970
+3
View File
@@ -1089,6 +1089,9 @@
Increase Font Size by Five Steps 按五个增量增大字体大小
Reset Tracking and Kerning 重置字距和字偶距
Reset Horizontal Scale 重置水平缩放
Highlight Font Name 突出显示字体名称
Highlight Kerning 突出显示字偶距
Highlight Tracking 突出显示字距
Spacing 间距
Step Type 按增量调整文字
Fit in View 适合视图
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -1114,6 +1114,9 @@ font-technology Variable 可變
Increase Font Size by Five Steps 按五個增量放大字體大小
Reset Tracking and Kerning 重設字距與字距微調
Reset Horizontal Scale 重設水平縮放
Highlight Font Name 反白顯示字體名稱
Highlight Kerning 反白顯示字距微調
Highlight Tracking 反白顯示字距
Spacing 間距
Step Type 依增量調整文字
Fit in View 符合檢視
Can't render this file because it contains an unexpected character in line 6 and column 47.
+3
View File
@@ -125,6 +125,9 @@ pub struct VectorcraftApp {
pub last_effect: Option<(String, serde_json::Value)>,
/// Last confirmed Warp Options (bend, distortion, orientation) for the next new warp dialog.
pub last_warp: Option<serde_json::Value>,
/// The Character panel field that takes the keyboard focus when the panel next draws it
/// ([`panels::character::highlight`]).
pub(crate) character_focus: Option<panels::character::Field>,
/// Commands run through [`Self::run`] so far: the native menu bar reads its rows again when it
/// moves ([`native_menu::sync`]).
run_count: u64,
+3
View File
@@ -236,6 +236,9 @@ fn run_ui_part3(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option<Result<
"type.hiddenCharacters" => flag(&mut app.ui.view.hidden_chars),
"type.bold" => crate::panels::character::toggle_face(app, Face::Bold),
"type.italic" => crate::panels::character::toggle_face(app, Face::Italic),
"type.highlightFontName" => crate::panels::character::highlight(app, crate::panels::character::Field::FontName),
"type.highlightKerning" => crate::panels::character::highlight(app, crate::panels::character::Field::Kerning),
"type.highlightTracking" => crate::panels::character::highlight(app, crate::panels::character::Field::Tracking),
id if let Some((_, justify)) = crate::panels::paragraph::ALIGN_COMMANDS.iter().find(|c| c.0 == id) => {
crate::panels::paragraph::align(app, justify)
}
+18
View File
@@ -318,6 +318,24 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[
"",
"{} the selected type (or the Type tool's selection) in its family's Italic (or Oblique) face, or back upright when it is italic; keeps the weight. A family without that face is left as it is, with a message. While the Type tool edits text, Cmd+Shift+I",
),
(
"type.highlightFontName",
"Highlight Font Name",
"Cmd+Alt+Shift+F",
"{} show the Character panel if it is hidden and open its font family menu, whose search field takes the keyboard focus: type to search, Enter applies the highlighted font → {field: \"fontName\"}. Without selected type (or text the Type tool edits) the panel shows, and the error says to select type first",
),
(
"type.highlightKerning",
"Highlight Kerning",
"Cmd+Alt+K",
"{} show the Character panel if it is hidden and give its Kerning field the keyboard focus with its value selected: type a value and press Enter → {field: \"kerning\"}. Without selected type (or text the Type tool edits) the panel shows, and the error says to select type first",
),
(
"type.highlightTracking",
"Highlight Tracking",
"",
"{} show the Character panel if it is hidden and give its Tracking field the keyboard focus with its value selected: type a value and press Enter → {field: \"tracking\"}. Without selected type (or text the Type tool edits) the panel shows, and the error says to select type first",
),
("effect.last", "Last Effect…", "Cmd+Alt+Shift+E", "{} open the dialog of the last effect applied"),
("view.zoomIn", "Zoom In", "Cmd+=", "{}"),
("view.zoomOut", "Zoom Out", "Cmd+-", "{}"),
+19
View File
@@ -156,6 +156,25 @@ fn a_tab_dragged_out_floats_and_comes_back_where_it_was_after_its_window_closes(
assert!(h.tab("layers").is_some());
}
/// A Character field shortcut with the Character panel in front of its floating window brings
/// that window forward and leaves the panel showing.
#[test]
fn a_character_field_shortcut_brings_the_floating_character_panel_forward() {
let mut h = Harness::new();
let id = h.app.run("text.create", json!({"x": 20, "y": 50, "text": "AVAX"})).unwrap()["id"].clone();
h.app.run("select.set", json!({"ids": [id]})).unwrap();
h.app.run("window.panel.float", json!({"panel": "character"})).unwrap();
h.frame(vec![]);
assert!(showing(&h.app.ui, "character"));
assert_eq!(h.app.dock_rt.raise, None);
for command in ["type.highlightKerning", "type.highlightTracking", "type.highlightFontName"] {
h.app.run(command, json!({})).unwrap();
assert_eq!(h.app.dock_rt.raise.as_deref(), Some("character"), "{command}");
h.frame(vec![]);
assert!(showing(&h.app.ui, "character"), "{command} leaves the panel showing");
}
}
#[test]
fn the_dock_collapses_to_icons_and_back() {
let mut h = Harness::new();
+58
View File
@@ -328,6 +328,55 @@ pub(crate) fn toggle_face(app: &mut VectorcraftApp, face: Face) -> Result<Value,
Ok(json!({ "style": name }))
}
/// The Character panel fields that the Highlight Font Name, Highlight Kerning and Highlight
/// Tracking commands give the keyboard focus ([`highlight`]).
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Field {
/// The font family menu: it opens, and its search field takes the focus.
FontName,
Kerning,
Tracking,
}
impl Field {
/// The id the panel draws the field with ([`show`], [`metrics_grid`]).
fn salt(self) -> &'static str {
match self {
Field::FontName => "ch-font",
Field::Kerning => "ch-kern",
Field::Tracking => "ch-track",
}
}
fn name(self) -> &'static str {
match self {
Field::FontName => "fontName",
Field::Kerning => "kerning",
Field::Tracking => "tracking",
}
}
}
/// `type.highlightFontName`, `type.highlightKerning` and `type.highlightTracking`: show the
/// Character panel if it is hidden (a shown one is activated, so its floating window comes
/// forward), and give `field` the keyboard focus when the panel next draws it, a number field with
/// its value selected (typing replaces it, Enter applies it). Without selected type or text the
/// Type tool edits, the panel shows, nothing takes the focus and the command fails. → `{field}`.
pub(crate) fn highlight(app: &mut VectorcraftApp, field: Field) -> Result<Value, String> {
// `window.panel` would hide a floating panel in front of its window, or an open flyout.
if crate::panel_docking::showing(&app.ui, "character") {
app.run("window.panel.activate", json!({"panel": "character"}))?;
} else {
app.run("window.panel", json!({"panel": "character"}))?;
}
if text_style(app).is_none() {
app.character_focus = None;
return Err("select some type first".into());
}
app.character_focus = Some(field);
Ok(json!({ "field": field.name() }))
}
/// `text.setRangeStyle` with `p` on the range the Type tool has selected (the whole text when
/// nothing is selected). Does nothing unless the Type tool edits text.
pub(crate) fn range_style(app: &mut VectorcraftApp, p: Value) {
@@ -713,15 +762,24 @@ pub(crate) fn control_bar(app: &mut VectorcraftApp, ui: &mut Ui) {
pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
let Some((s, _)) = text_style(app) else {
app.character_focus = None;
super::empty_state(ui, "type", tl!("No text selected"), tl!("Select a text object to edit its character attributes."));
return;
};
let w = ui.available_width();
// The field a Highlight command asked for takes the focus as it is drawn. If no field took it
// (a sizing pass), the next frame tries again.
if let Some(f) = app.character_focus {
widgets::arm_field_focus(ui.ctx(), f.salt());
}
font_pickers(app, ui, &s, ("ch-font", "ch-style"), (w - 4.0, w - 4.0));
variable_font(app, ui, &s, w - 4.0);
ui.add_space(2.0);
let hidden = pstate::<bool>(ui.ctx(), "ch-hide-options");
metrics_grid(app, ui, "ch-grid", &s, w, !hidden);
if app.character_focus.is_some() && !widgets::disarm_field_focus(ui.ctx()) {
app.character_focus = None;
}
if hidden {
return;
}
@@ -130,6 +130,56 @@ fn kerning_at_a_caret_sets_the_pair_not_the_whole_text() {
assert_eq!((kerning(&app, 1), kerning(&app, 2), kerning(&app, 3)), (Some(-50.0), Some(30.0), Some(30.0)));
}
/// Highlight Kerning and Highlight Tracking show the Character panel and give its field the
/// keyboard focus with the value selected: what is typed replaces it, and Enter applies it.
/// Highlight Font Name opens the font family menu with its search field focused. Without
/// selected type the panel shows and nothing takes the focus.
#[test]
fn highlight_commands_focus_the_font_kerning_and_tracking_fields() {
let mut app = VectorcraftApp::new(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": "AVAX", "size": 12})).unwrap()["id"].clone();
let ctx = egui::Context::default();
let frame = |app: &mut VectorcraftApp, events: Vec<egui::Event>| {
let mut out = ctx.run_ui(egui::RawInput { events, ..Default::default() }, |ui| show(app, ui));
out.textures_delta.clear();
};
let typed = |text: &str| {
let enter = egui::Event::Key { key: egui::Key::Enter, physical_key: None, pressed: true, repeat: false, modifiers: Default::default() };
vec![egui::Event::Text(text.into()), enter]
};
let spacing = |app: &VectorcraftApp| text_style(app).map(|(s, _)| (s.kerning, s.tracking)).unwrap();
app.run("select.set", json!({"ids": [id]})).unwrap();
assert_eq!(app.run("type.highlightKerning", json!({})).unwrap(), json!({"field": "kerning"}));
assert!(crate::panel_docking::showing(&app.ui, "character"), "the panel shows");
frame(&mut app, vec![]);
assert_eq!(app.character_focus, None, "the Kerning field took the focus");
frame(&mut app, typed("-40"));
assert_eq!(spacing(&app), (Some(-40.0), 0.0), "typing replaced the kerning");
app.run("type.highlightTracking", json!({})).unwrap();
frame(&mut app, vec![]);
frame(&mut app, typed("75"));
assert_eq!(spacing(&app), (Some(-40.0), 75.0), "typing replaced the tracking");
// Highlighting a field again while it holds typed text that is not applied selects all of it.
app.run("type.highlightKerning", json!({})).unwrap();
frame(&mut app, vec![]);
frame(&mut app, vec![egui::Event::Text("12345".into())]);
app.run("type.highlightKerning", json!({})).unwrap();
frame(&mut app, vec![]);
frame(&mut app, typed("-20"));
assert_eq!(spacing(&app), (Some(-20.0), 75.0), "typing replaced the text typed before");
ctx.memory_mut(|m| m.stop_text_input());
app.run("type.highlightFontName", json!({})).unwrap();
frame(&mut app, vec![]);
frame(&mut app, vec![]);
assert!(egui::Popup::is_any_open(&ctx), "the font family menu is open");
assert!(ctx.memory(|m| m.focused()).is_some(), "its search field has the focus");
// No type selected: the panel shows, the command fails and nothing is armed.
app.run("select.none", json!({})).unwrap();
assert!(app.run("type.highlightKerning", json!({})).is_err());
assert_eq!(app.character_focus, None);
}
/// A variable font gets the Variable Font button, which opens one slider per axis named as the
/// font names it; a drag along one sets that axis of the selected type in one undo step.
#[test]
+33
View File
@@ -323,6 +323,39 @@ fn cmd_alt_q_resets_tracking_and_kerning() {
assert_eq!(spacing(&mut app), [(json!("One "), json!(200.0), json!(-40.0)), (json!("Two"), json!(0.0), json!(null))]);
}
/// Cmd+Alt+K highlights the Character panel's Kerning field and Cmd+Alt+Shift+F highlights its
/// font name field for the selected type, and Cmd+Alt+K also does so while the Type tool edits
/// text. Cmd+Alt+K does not open Preferences (Cmd+K).
#[test]
fn cmd_alt_k_and_cmd_alt_shift_f_highlight_the_character_fields() {
use crate::panels::character::Field;
let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default());
app.session.execute("file.new", &json!({"width": 200, "height": 200})).unwrap();
let id = app.session.execute("text.create", &json!({"x": 20, "y": 50, "text": "One Two"})).unwrap()["id"].as_u64().unwrap();
let chord = |key, m| egui::Event::Key { key, physical_key: None, pressed: true, repeat: false, modifiers: m };
app.session.execute("select.set", &json!({"ids": [id]})).unwrap();
frame(&mut app, vec![chord(Key::K, Modifiers::COMMAND | Modifiers::ALT)]);
assert_eq!(app.character_focus, Some(Field::Kerning));
assert!(app.ui.dialog.is_none(), "not Preferences");
assert!(crate::panel_docking::showing(&app.ui, "character"));
frame(&mut app, vec![chord(Key::F, Modifiers::COMMAND | Modifiers::ALT | Modifiers::SHIFT)]);
assert_eq!(app.character_focus, Some(Field::FontName));
// While the Type tool edits text.
app.character_focus = None;
app.select_tool("type");
app.session.apply_actions(vec![vectorcraft_tools::Action::Notify("text.editNew".into())]).unwrap();
app.session.set_tool_option("select", &json!({"start": 4, "end": 7}));
assert!(app.session.tool_wants_text());
frame(&mut app, vec![chord(Key::K, Modifiers::COMMAND | Modifiers::ALT)]);
assert_eq!(app.character_focus, Some(Field::Kerning));
assert!(app.ui.dialog.is_none(), "not Preferences");
// Keyboard Shortcuts lists all three under Type; Highlight Tracking has no default key.
for id in ["type.highlightFontName", "type.highlightKerning", "type.highlightTracking"] {
assert_eq!(crate::shortcut_editor::entry(id).map(|e| e.group.as_str()), Some("Type"), "{id}");
}
assert_eq!(crate::menus::shortcut_of("type.highlightTracking"), None);
}
/// Cmd+Shift+X resets the horizontal scale of the selected type to 100%, and of the Type
/// tool's selected text while it edits. egui reports the chord as a Cut with Shift held, and
/// the type object is not cut.
+3 -1
View File
@@ -56,7 +56,9 @@ pub use numeric::{
use numeric::{decimal_point, share_of, spinner_step, step_through, step_to_multiple, within_a_millionth};
pub(crate) use numeric::{round_to, set_bare_numbers_are_points, typed_unit};
pub use panel_chrome::{bottom_bar, layout_button, list_box, orientation_button, tab_bar};
pub(crate) use text::{dialog_focus_flag, enter_modifiers, focus_field, own_field_shift_enter, set_dialog_open, take_dialog_focus};
pub(crate) use text::{
arm_field_focus, dialog_focus_flag, disarm_field_focus, enter_modifiers, focus_field, own_field_shift_enter, set_dialog_open, take_dialog_focus,
};
pub use text::{
exact_text_field, exact_text_field_named, plain_field, plain_field_named, search_field, text_field, text_field_named, text_presets,
text_presets_named,
+9 -1
View File
@@ -1,5 +1,6 @@
use super::drag::PanelDrag;
use super::labels::{PreviousLabel, label_field, name_field, take_previous_label};
use super::text::take_armed_focus;
use super::{CornerRadius, HashOnly, Rect, Response, Sense, Stroke, StrokeKind, Tokens, Ui, Vec2, id_with, vec2};
/// A compact dropdown of interface labels (shown in the UI language). Returns the chosen index.
@@ -124,7 +125,9 @@ pub(crate) fn combo_named<R>(
/// filters in place over a list that scrolls (the font menu and the shortcut import's target
/// picker, #555). `menu` shows the menu with one of [`craft_ui::combo::Menu`]'s header forms and
/// returns what it returned (`None` while the menu is closed). A click inside the menu keeps it
/// open: it closes itself when an option is chosen. A click outside closes it.
/// open: it closes itself when an option is chosen. A click outside closes it. Armed with id salt
/// `id` ([`arm_field_focus`]), the menu opens and no widget keeps the keyboard focus, so the
/// menu's search field can take it.
pub(crate) fn combo_menu<R>(
ui: &mut Ui,
id: impl std::hash::Hash,
@@ -139,7 +142,12 @@ pub(crate) fn combo_menu<R>(
.stroke(Stroke::new(1.0, t.input_border))
.corner_radius(CornerRadius::same(3))
.show(ui, |ui| {
let armed = take_armed_focus(ui, &id);
let resp = combo_field(ui, id_with(ui.scope_id(), id), label, None, current, width - 4.0);
if armed {
egui::Popup::open_id(ui.ctx(), egui::Popup::default_response_id(&resp));
ui.memory_mut(|m| m.stop_text_input());
}
menu(craft_ui::combo::Menu::new(ui, &resp).close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside))
})
.inner
+13 -2
View File
@@ -1,6 +1,8 @@
use super::labels::{PreviousLabel, label_field, name_field, take_previous_label};
use super::panel_chrome::chevron_cell;
use super::text::{after_shift_enter, recessed_text, select_all, select_all_on_focus, shift_enter_keeps_focus, take_dialog_focus};
use super::text::{
after_shift_enter, focus_field, recessed_text, select_all, select_all_on_focus, shift_enter_keeps_focus, take_armed_focus, take_dialog_focus,
};
use super::{CornerRadius, RangeInclusive, Rect, Response, Sense, Stroke, StrokeKind, Tokens, Ui, Unit, id_with, pos2, scrub, vec2};
/// Preferences › Units › Numbers Without Units Are Points (#394; on until set): the app sets it
@@ -356,7 +358,8 @@ pub fn mixed_field_named(
}
/// [`mixed_field`] whose ↑/↓, wheel and scrub stop at `bounds`; ↑/↓ and the wheel take `step`'s steps
/// when it is given (see [`SliderValue::step`]).
/// when it is given (see [`SliderValue::step`]). Armed with id salt `id` ([`arm_field_focus`]), it
/// takes the keyboard focus with its value selected.
#[allow(clippy::too_many_arguments)]
fn bounded_field(
ui: &mut Ui,
@@ -370,6 +373,7 @@ fn bounded_field(
bounds: &RangeInclusive<f64>,
step: Option<fn(f64, f64) -> f64>,
) -> Option<f64> {
let armed = take_armed_focus(ui, &id);
let id = id_with(ui.scope_id(), id);
let show = |value: f64| field_text(value, suffix, decimals);
// The suffix (and %, °) may follow any operand: `45*2°`, `50% / 2`.
@@ -381,7 +385,14 @@ fn bounded_field(
};
let shown = value.map(show).unwrap_or_default();
take_dialog_focus(ui, id, &shown);
if armed {
focus_field(ui, id, &shown);
}
let (mut buf, commit, resp, rect) = recessed_text(ui, id, &shown, width, 1, label, name);
if armed {
// A field armed while it has focus keeps the text typed into it: select all of that text.
select_all(ui, id, &buf);
}
select_all_on_focus(ui, &resp, &buf);
// ↑/↓ and the wheel step at the field's precision (a count ignores Ctrl/Cmd's tenth); a drag on
// its label scrubs it.
+37
View File
@@ -26,6 +26,43 @@ pub(crate) fn focus_field(ui: &Ui, id: egui::Id, text: &str) {
ui.memory_mut(|m| m.request_focus(id));
}
/// The key under which [`arm_field_focus`] keeps the armed field's id salt.
fn armed_field_flag() -> egui::Id {
egui::Id::unique("armed-field-focus")
}
/// Arm the field drawn with id salt `salt`: a numeric field's `id`, or a searchable menu's (the
/// font family menu's). The next one drawn, enabled and outside a sizing pass, takes the keyboard
/// focus ([`take_armed_focus`]) until [`disarm_field_focus`].
pub(crate) fn arm_field_focus(ctx: &egui::Context, salt: impl std::hash::Hash) {
ctx.data_mut(|d| d.insert_temp(armed_field_flag(), id_with(egui::Id::NULL, salt)));
}
/// Disarm the field [`arm_field_focus`] armed. → Whether it was still armed: no field took it.
pub(crate) fn disarm_field_focus(ctx: &egui::Context) -> bool {
ctx.data_mut(|d| {
let armed = d.get_temp::<egui::Id>(armed_field_flag()).is_some();
d.remove::<egui::Id>(armed_field_flag());
armed
})
}
/// Whether the field drawn with id salt `salt` is the one armed and can take the keyboard focus
/// now (it is enabled, and this is not a sizing pass). It is then disarmed.
pub(crate) fn take_armed_focus(ui: &Ui, salt: impl std::hash::Hash) -> bool {
if ui.is_sizing_pass() || !ui.is_enabled() {
return false;
}
let salt = id_with(egui::Id::NULL, salt);
ui.data_mut(|d| {
let armed = d.get_temp::<egui::Id>(armed_field_flag()) == Some(salt);
if armed {
d.remove::<egui::Id>(armed_field_flag());
}
armed
})
}
/// The modifiers held with an Enter press among this frame's events, `None` without one, as when a
/// field's editing ends with a click elsewhere.
pub(crate) fn enter_modifiers(ui: &Ui) -> Option<egui::Modifiers> {
+5 -1
View File
@@ -3095,7 +3095,11 @@ Pen's second anchor at (150, 431), in line with the square's centre.
kerning to Auto in the type objects in `ids`, the Type tool's selected text or the selected type objects; at a caret
it resets only the kerning of the character before it. `type.resetHorizontalScale` (Cmd+Shift+X,
`press_key {key: "X", mods: {cmd: true, shift: true}}`) sets the horizontal scale to 100%, as the Character panel
shows it, in the same text; at a caret it leaves the text as it is.
shows it, in the same text; at a caret it leaves the text as it is. In the app, `type.highlightKerning` (Cmd+Alt+K),
`type.highlightTracking` (no default key) and `type.highlightFontName` (Cmd+Alt+Shift+F) show the Character panel and
give its Kerning or Tracking field the keyboard focus with the value selected, or open its font family menu with the
search field focused; each replies `{field: "kerning" | "tracking" | "fontName"}`. Without selected type or text the
Type tool edits, the panel shows and the command fails.
- `missingGlyphProtection` (on by default): `text.setStyle` and `text.setRangeStyle` changing the font leave the
characters the new font has no glyph for in the font that had one.
- `typeSelectionByPathOnly` (off by default): on, the Selection tools (`pointer_gesture` with `selection`, and the
+1
View File
@@ -617,6 +617,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- Cmd/Ctrl+/ (`appearance.addFill`) and Cmd/Ctrl+Alt+/ (`appearance.addStroke`) add a new fill and a new stroke to the selected objects, as the Appearance panel's Add New Fill and Add New Stroke buttons do, on the keys the reference app's [Default keyboard shortcuts](https://web.archive.org/web/20260726060015/https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html) page lists for adding a new fill and a new stroke. Keyboard Shortcuts lists both under Window › Appearance.
- Cmd/Ctrl+Alt+Shift+. and , (`type.size.increaseByFive`, `type.size.decreaseByFive`) step the font size of the selected type, or of the text the Type tool has selected, by five times the Size/Leading increment (Preferences › Type); Cmd/Ctrl+Shift+. and , still step it by one increment.
- Cmd/Ctrl+Alt+Q (`type.resetTracking`) sets the tracking of the selected type, or of the text the Type tool has selected, to 0 and its kerning to Auto (0 in the Character panel), in one undo step; at a caret it resets the kerning of the character before it.
- Cmd/Ctrl+Alt+K (`type.highlightKerning`) and Cmd/Ctrl+Alt+Shift+F (`type.highlightFontName`) show the Character panel and give its Kerning field the keyboard focus with the value selected, or open its font family menu with the search field focused, on the keys the reference app's [Default keyboard shortcuts](https://web.archive.org/web/20260726060015/https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html) page lists for highlighting kerning and the font name field. The page lists the same key for selecting the tracking value; `type.highlightTracking` focuses the Tracking field the same way and has no default key in Keyboard Shortcuts.
- Cmd/Ctrl+Alt+L (`ui.newLayer`) opens Layer Options for a new layer, as an Alt-click on the Layers panel's New Layer button does, on the key the reference app's [Default keyboard shortcuts](https://web.archive.org/web/20260726060015/https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html) page lists for adding a layer through the New Layer dialog box. OK adds the layer above the current layer, and Cancel adds none. Cmd/Ctrl+L still adds a layer without the dialog.
- F8 (`ui.symbolOptions`) opens Symbol Options for a new symbol made from the selection, as the Symbols panel's New Symbol button does, on the key the reference app's [Default keyboard shortcuts](https://web.archive.org/web/20260726060015/https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html) page lists for making a new symbol. OK makes the symbol and puts an instance of it in place of the art, and Cancel makes none. With nothing selected, F8 opens no dialog, and the status bar says that nothing is selected. Shift+F8 and Cmd/Ctrl+F8 still show the Transform and Info panels.
- Cmd/Ctrl+Shift+X (`type.resetHorizontalScale`) sets the horizontal scale of the selected type, or of the text the Type tool has selected, to 100% as the Character panel shows it (the object's own stretch included), in one undo step; at a caret it leaves the text as it is.