From 6dfdf643d9117113c63882e6f1ae856fc4c3f2ab Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Thu, 15 Oct 2026 23:29:50 -0700 Subject: [PATCH] Type: Cmd/Ctrl+Shift+X resets horizontal scale to 100% The reference app resets the horizontal scale of type to 100% with Cmd/Ctrl+Shift+X. VectorCraft had no command for it, and the chord cut the selection: egui reports it as a Cut with Shift held. type.resetHorizontalScale (Reset Horizontal Scale, default Cmd+Shift+X) sets the horizontal scale to 100% in the text the Type tool has selected, or else in all the text of every selected type object, in one undo step. The value is the one the Character panel shows, so a type object stretched to twice its width keeps 50% stored. The vertical scale does not change. At a caret the command leaves the text as it is. Type objects given in ids are reset in full, as type.resetTracking does. Docs: docs/mcp.md, docs/target-app-parity.md. --- crates/engine/src/cmd/textedit.rs | 29 +++++++++++++++++++ crates/engine/src/tests_prefs.rs | 44 +++++++++++++++++++++++++++++ crates/mcp/src/tests.rs | 20 +++++++++++++ crates/ui-egui/src/i18n/bg.tsv | 1 + crates/ui-egui/src/i18n/cs.tsv | 1 + crates/ui-egui/src/i18n/de.tsv | 1 + crates/ui-egui/src/i18n/el.tsv | 1 + crates/ui-egui/src/i18n/es.tsv | 1 + crates/ui-egui/src/i18n/fi.tsv | 1 + crates/ui-egui/src/i18n/fr.tsv | 1 + crates/ui-egui/src/i18n/hu.tsv | 1 + crates/ui-egui/src/i18n/it.tsv | 1 + crates/ui-egui/src/i18n/ja.tsv | 1 + crates/ui-egui/src/i18n/ko.tsv | 1 + crates/ui-egui/src/i18n/pl.tsv | 1 + crates/ui-egui/src/i18n/pt-br.tsv | 1 + crates/ui-egui/src/i18n/ru.tsv | 1 + crates/ui-egui/src/i18n/sv.tsv | 1 + crates/ui-egui/src/i18n/uk.tsv | 1 + crates/ui-egui/src/i18n/vi.tsv | 1 + crates/ui-egui/src/i18n/zh-hans.tsv | 1 + crates/ui-egui/src/i18n/zh-hant.tsv | 1 + crates/ui-egui/src/shortcuts.rs | 28 ++++++++++++++++++ docs/mcp.md | 4 ++- docs/target-app-parity.md | 1 + 25 files changed, 144 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/cmd/textedit.rs b/crates/engine/src/cmd/textedit.rs index 879e75e904..75fa760bb6 100644 --- a/crates/engine/src/cmd/textedit.rs +++ b/crates/engine/src/cmd/textedit.rs @@ -112,6 +112,15 @@ pub fn specs() -> Vec { has_selection, reset_tracking ), + cmd!( + "type.resetHorizontalScale", + "Reset Horizontal Scale", + [], + Some("Cmd+Shift+X"), + "{ids?: [id…]} set the horizontal scale to 100% (as the Character panel shows it, the object's own transform included) in all the text of the type objects in `ids`, else in the text the Type tool has selected (nothing at a caret), else in every selected type object, as one undo step → {ids}", + has_selection, + reset_horizontal_scale + ), cmd!( "text.discardEmpty", "Discard Empty Type", @@ -752,6 +761,26 @@ fn reset_tracking(s: &mut Session, p: &Value) -> Result { Ok(json!({ "ids": ranges.iter().map(|(id, ..)| id.0).collect::>() })) } +/// Cmd/Ctrl+Shift+X: horizontal scale 100% (the Character panel's value, which includes the +/// object's own transform) in the type objects in `ids`, else in the Type tool's selected text, +/// else in every selected type object. At a caret the command leaves the text as it is. +fn reset_horizontal_scale(s: &mut Session, p: &Value) -> Result { + const C: &str = "type.resetHorizontalScale"; + let ranges = type_ranges(s, p, C, |_, _| None)?; + if !ranges.is_empty() { + s.edit("Reset Horizontal Scale", |d, _| { + for &(id, a, b, _) in &ranges { + let t = text_mut(d, id).ok_or(EngineError::NoNode(id))?; + let h_scale = super::typecmd::local_type_value(t, 100.0, (1.0, 10000.0), true, C)?; + edit::style_range(&mut t.runs, a, b, |st| st.h_scale = h_scale); + refresh_bounds(t); + } + Ok(()) + })?; + } + Ok(json!({ "ids": ranges.iter().map(|(id, ..)| id.0).collect::>() })) +} + /// Tracking (1/1000 em) that makes the first paragraph of `t` exactly `target` wide, if it fits /// on one line at all. fn headline_tracking(t: &TextObject, target: f64) -> Option { diff --git a/crates/engine/src/tests_prefs.rs b/crates/engine/src/tests_prefs.rs index 0ed460cae2..1bcc2ec9bb 100644 --- a/crates/engine/src/tests_prefs.rs +++ b/crates/engine/src/tests_prefs.rs @@ -1096,6 +1096,50 @@ fn reset_tracking_sets_tracking_and_kerning_to_zero() { assert_eq!(style_at(&s, other, 0).tracking, 0.0, "the reset stays"); } +/// Cmd/Ctrl+Shift+X resets the horizontal scale to 100% in every selected type object and in the +/// Type tool's selected text, as one undo step, and leaves the vertical scale as it is. At a caret +/// it leaves the text as it is. On a stretched object the scale the Character panel shows, which includes the +/// object's own stretch, goes to 100%. Type objects given in `ids` are reset in full while the +/// Type tool edits. +#[test] +fn reset_horizontal_scale_sets_it_to_100_percent() { + use vectorcraft_tools::PointerKind::{Down, Up}; + let mut s = new_doc(); + let id = NodeId(s.execute("text.create", &json!({"x": 100, "y": 100, "text": "Hello world", "size": 20})).unwrap()["id"].as_u64().unwrap()); + let scales = |s: &Session, byte| { + let st = style_at(s, id, byte); + (st.h_scale, st.v_scale) + }; + s.execute("text.setRangeStyle", &json!({"id": id.0, "hScale": 150, "vScale": 80})).unwrap(); + s.execute("select.set", &json!({"ids": [id.0]})).unwrap(); + assert_eq!(find_command("type.resetHorizontalScale").unwrap().shortcut, Some("Cmd+Shift+X")); + assert_eq!(s.execute("type.resetHorizontalScale", &json!({})).unwrap()["ids"], json!([id.0])); + assert_eq!((scales(&s, 0), scales(&s, 10)), ((100.0, 80.0), (100.0, 80.0))); + s.execute("edit.undo", &json!({})).unwrap(); + assert_eq!(scales(&s, 0), (150.0, 80.0), "one undo step"); + // A type object stretched to twice its width shows 200% and keeps half of it stored. + let wide = NodeId(s.execute("text.create", &json!({"x": 100, "y": 300, "text": "Wide", "size": 20})).unwrap()["id"].as_u64().unwrap()); + s.execute("select.set", &json!({"ids": [wide.0]})).unwrap(); + s.execute("object.scale", &json!({"sx": 200, "sy": 100})).unwrap(); + s.execute("type.resetHorizontalScale", &json!({})).unwrap(); + assert_eq!(style_at(&s, wide, 0).h_scale, 50.0); + // With the Type tool, it resets only the selected text. + let v = ViewInfo::default(); + s.select_tool("type", v).unwrap(); + gesture(&mut s, v, &[(Down, 102.0, 95.0), (Up, 102.0, 95.0)]); + assert_eq!(s.tool_options()["editing"], json!(id.0), "editing the text"); + s.set_tool_option("select", &json!({"start": 0, "end": 5})); + s.execute("type.resetHorizontalScale", &json!({})).unwrap(); + assert_eq!((scales(&s, 4), scales(&s, 6)), ((100.0, 80.0), (150.0, 80.0))); + // At a caret it leaves the text as it is. + s.set_tool_option("select", &json!({"start": 8, "end": 8})); + assert_eq!(s.execute("type.resetHorizontalScale", &json!({})).unwrap()["ids"], json!([])); + assert_eq!((scales(&s, 7), scales(&s, 8)), ((150.0, 80.0), (150.0, 80.0))); + // While the Type tool edits, the type objects in `ids` are reset in full. + s.execute("type.resetHorizontalScale", &json!({"ids": [id.0]})).unwrap(); + assert_eq!((scales(&s, 6), scales(&s, 10)), ((100.0, 80.0), (100.0, 80.0))); +} + /// Type › Fill New Type Objects With Placeholder Text (#394, on by default): type the Type tool /// places starts with placeholder text, selected, so typing replaces it; off, it starts empty. #[test] diff --git a/crates/mcp/src/tests.rs b/crates/mcp/src/tests.rs index 552136fd49..cfb20ae019 100644 --- a/crates/mcp/src/tests.rs +++ b/crates/mcp/src/tests.rs @@ -1120,6 +1120,26 @@ fn cmd_alt_q_resets_tracking_and_kerning_for_agents() { assert_eq!((&runs[0]["style"]["tracking"], &runs[0]["style"]["kerning"]), (&json!(0.0), &Value::Null), "{runs}"); } +/// Cmd+Shift+X reaches agents: the placeholder text the Type tool selects goes back to a +/// horizontal scale of 100%. +#[test] +fn cmd_shift_x_resets_horizontal_scale_for_agents() { + let mut s = server(); + let events = json!([{"kind": "down", "x": 100, "y": 100}, {"kind": "up", "x": 100, "y": 100}]); + let r = call(&mut s, 1, "pointer_gesture", json!({"tool": "type", "events": events})); + assert_eq!(r["isError"], false, "{r}"); + let id = serde_json::from_str::(&text_of(&r)).unwrap()["selection"][0].as_u64().unwrap(); + let r = call(&mut s, 2, "run_command", json!({"command": "text.setRangeStyle", "params": {"id": id, "hScale": 150}})); + assert_eq!(r["isError"], false, "{r}"); + let r = call(&mut s, 3, "press_key", json!({"key": "X", "mods": {"cmd": true, "shift": true}})); + assert_eq!(r["isError"], false, "{r}"); + assert_eq!(serde_json::from_str::(&text_of(&r)).unwrap()["command"], "type.resetHorizontalScale", "{r}"); + let r = call(&mut s, 4, "run_command", json!({"command": "text.getRange", "params": {"id": id}})); + let runs = serde_json::from_str::(&text_of(&r)).unwrap()["runs"].clone(); + assert_eq!(runs.as_array().unwrap().len(), 1, "{runs}"); + assert_eq!(runs[0]["style"]["h_scale"], json!(100.0), "{runs}"); +} + /// The drawing tools snap to Smart Guides over MCP as with the mouse (#506): a Rectangle drawn /// from near another object's corner starts on it, and a Pen anchor placed beside that object's /// centre lines up with it. diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 2583ea959f..d18d3188df 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -4881,6 +4881,7 @@ theme Appearance Външен вид # Type › Reset Tracking and Kerning Reset Tracking and Kerning Нулиране на разредката и кернинга + Reset Horizontal Scale Нулиране на хоризонталния мащаб @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` diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 86794d7858..d0e2eadb67 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -3485,6 +3485,7 @@ axis Both Obě Increase Font Size Zvětšit velikost písma 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 Step Type Krokovat text Get Text Range Získat rozsah textu Punctuation Interpunkce diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 09dd23f523..ae91f85f2f 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -1055,6 +1055,7 @@ Increase Font Size Schriftgrad erhöhen 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 Spacing Abstand Step Type Text schrittweise ändern Fit in View In Ansicht einpassen diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 45fe554f22..55a324ff00 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -4833,6 +4833,7 @@ effect Points Σημεία Precision: Ακρίβεια: Preview (1 inch on the page) Προεπισκόπηση (1 ίντσα στη σελίδα) Registration: Σημείο αναφοράς: + Reset Horizontal Scale Επαναφορά οριζόντιας κλίμακας Reset Tracking and Kerning Επαναφορά απόστασης χαρακτήρων και διαγραμμάτωσης Sample Color from Screen Δειγματοληψία χρώματος από την οθόνη Set as Default Ορισμός ως προεπιλογή diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index a4126fab1b..3d708acac0 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -1072,6 +1072,7 @@ Increase Font Size Aumentar tamaño de fuente 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 Spacing Espaciado Step Type Ajustar texto por incrementos Fit in View Encajar en la vista diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index ab1738b334..003804b693 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -4845,6 +4845,7 @@ mark-style Japanese Japanilainen Precision: Tarkkuus: Preview (1 inch on the page) Esikatselu (1 tuuma sivulla) Registration: Kohdistuspiste: + Reset Horizontal Scale Palauta vaakaskaalaus Reset Tracking and Kerning Palauta merkkiväli ja parivälistys Sample Color from Screen Poimi väri näytöltä Set as Default Aseta oletukseksi diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 2353300135..000430e2e3 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -1071,6 +1071,7 @@ Increase Font Size Augmenter la taille de police 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 Spacing Espacement Step Type Texte par incréments Fit in View Ajuster à la vue diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 602d502d7f..88910feea5 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -2913,6 +2913,7 @@ Reset Envelope Shape Burkoló alakjának visszaállítása Reset Essentials Essentials visszaállítása Reset Essentials Classic Essentials Classic visszaállítása + Reset Horizontal Scale Vízszintes méretezés visszaállítása Reset Keyboard Shortcuts Billentyűparancsok visszaállítása Reset Layout Layout visszaállítása Reset OpenType Features OpenType-funkciók visszaállítása diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 0662b14f66..c6cbcd1452 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -1071,6 +1071,7 @@ Increase Font Size Aumenta dimensione font 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 Spacing Spaziatura Step Type Dimensione testo a incrementi Fit in View Adatta alla vista diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 84dfaece0c..9721f5ab9c 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -332,6 +332,7 @@ Increase Font Size フォントサイズを大きく Increase Font Size by Five Steps フォントサイズを5段階大きく Reset Tracking and Kerning トラッキングとカーニングをリセット + Reset Horizontal Scale 水平比率をリセット Step Type 文字を段階的に調整 Find Font… フォントの検索と置換… Change Case 大文字と小文字の変更 diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index 9eb89a47ef..50f3ca5e9a 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -328,6 +328,7 @@ Increase Font Size 글꼴 크기 키우기 Increase Font Size by Five Steps 글꼴 크기 5단계 키우기 Reset Tracking and Kerning 자간 및 커닝 재설정 + Reset Horizontal Scale 가로 비율 재설정 Step Type 단계별 문자 Find Font… 글꼴 찾기… Change Case 대소문자 변경 diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 4c4cb4f1d2..04ef38a441 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -3318,6 +3318,7 @@ axis Both Obie Increase Font Size Zwiększ rozmiar czcionki 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ą Step Type Krok tekstu Get Text Range Pobierz zakres tekstu Punctuation Interpunkcja diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index 79bc524dee..c183c3dd98 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -952,6 +952,7 @@ Increase Font Size Aumentar Tamanho da Fonte 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 Step Type Ajustar Texto em Incrementos Fit dashes to corners and ends Ajustar tracejados aos cantos e extremidades Fit in Window Ajustar na Janela diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index b6f14c9671..1888009a3c 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -1069,6 +1069,7 @@ Increase Font Size Увеличить размер шрифта Increase Font Size by Five Steps Увеличить размер шрифта на пять шагов Reset Tracking and Kerning Сбросить трекинг и кернинг + Reset Horizontal Scale Сбросить масштаб по горизонтали Spacing Интервал Step Type Подгонять текст по шагам Fit in View Вписать в вид diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index 228db52f4e..4ce1944209 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -4837,6 +4837,7 @@ theme Appearance Utseende Precision: Precision: Preview (1 inch on the page) Förhandsgranskning (1 tum på sidan) Registration: Registreringspunkt: + Reset Horizontal Scale Återställ horisontell skala Reset Tracking and Kerning Återställ spärrning och kerning Sample Color from Screen Hämta färg från skärmen Set as Default Ange som standard diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index b1f7a34b6a..0521a54049 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -1065,6 +1065,7 @@ Increase Font Size Збільшити розмір шрифту Increase Font Size by Five Steps Збільшити розмір шрифту на п’ять кроків Reset Tracking and Kerning Скинути трекінг і кернінг + Reset Horizontal Scale Скинути масштаб по горизонталі Spacing Інтервал Step Type Підганяти текст кроками Fit in View Вписати у вигляд diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index e4bec9dffd..a37856f21b 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -2662,6 +2662,7 @@ Reset Envelope Mesh Đặt lại lưới Envelope Reset Envelope Shape Đặt lại hình dạng Envelope Reset Essentials Đặt lại Essentials + Reset Horizontal Scale Đặt lại tỷ lệ ngang Reset Keyboard Shortcuts Đặt lại phím tắt Reset OpenType Features Đặt lại tính năng OpenType Reset Panel Đặt lại bảng diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index a39e1af810..654f8bfeae 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -1020,6 +1020,7 @@ Increase Font Size 增大字体大小 Increase Font Size by Five Steps 按五个增量增大字体大小 Reset Tracking and Kerning 重置字距和字偶距 + Reset Horizontal Scale 重置水平缩放 Spacing 间距 Step Type 按增量调整文字 Fit in View 适合视图 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 4ce18df1cb..d86a1440b6 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -1043,6 +1043,7 @@ Increase Font Size 放大字體大小 Increase Font Size by Five Steps 按五個增量放大字體大小 Reset Tracking and Kerning 重設字距與字距微調 + Reset Horizontal Scale 重設水平縮放 Spacing 間距 Step Type 依增量調整文字 Fit in View 符合檢視 diff --git a/crates/ui-egui/src/shortcuts.rs b/crates/ui-egui/src/shortcuts.rs index 0163eb7d70..2147936c3e 100644 --- a/crates/ui-egui/src/shortcuts.rs +++ b/crates/ui-egui/src/shortcuts.rs @@ -811,6 +811,34 @@ mod tests { assert_eq!(spacing(&mut app), [(json!("One "), json!(200.0), json!(-40.0)), (json!("Two"), json!(0.0), json!(null))]); } + /// 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. + #[test] + fn cmd_shift_x_resets_horizontal_scale() { + 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 stretched = |app: &mut VectorcraftApp| app.session.execute("text.setRangeStyle", &json!({"id": id, "hScale": 150})).unwrap(); + let scales = |app: &mut VectorcraftApp| { + let r = app.session.execute("text.getRange", &json!({"id": id})).unwrap(); + r["runs"].as_array().unwrap().iter().map(|run| (run["text"].clone(), run["style"]["h_scale"].clone())).collect::>() + }; + let chord = vec![egui::Event::ModifiersChanged(Modifiers::COMMAND | Modifiers::SHIFT), egui::Event::Cut]; + stretched(&mut app); + app.session.execute("select.set", &json!({"ids": [id]})).unwrap(); + frame(&mut app, chord.clone()); + assert_eq!(scales(&mut app), [(json!("One Two"), json!(100.0))]); + // While the Type tool edits: only its selection, and the text stays. + stretched(&mut app); + 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, chord); + assert_eq!(scales(&mut app), [(json!("One "), json!(150.0)), (json!("Two"), json!(100.0))]); + } + /// Tab shows and hides the panels, and in the Type tool it types a tab, without moving the /// keyboard focus on to a field: the keys after it (a tool letter, the next letter typed) still /// work. diff --git a/docs/mcp.md b/docs/mcp.md index 09254c861b..a433251d2f 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -2025,7 +2025,9 @@ Pen's second anchor at (150, 431), in line with the square's centre. a caret or tracking of the selection (←/→), leading (↑/↓) and baseline shift (Shift+↑/↓), five steps with Cmd/Ctrl too. `type.resetTracking` (Cmd+Alt+Q, `press_key {key: "Q", mods: {cmd: true, alt: true}}`) sets tracking to 0 and 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. + 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. - `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 diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index bd8f70d904..a32545b4f6 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -452,6 +452,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - Cmd/Ctrl+A follows the keyboard: in a panel, dialog or search field it selects the field's text, while the Type tool edits it selects all of that text (also `select.all` over MCP), and only otherwise every object. On macOS the menu bar's Select All, Cut, Copy and Paste (keys or clicks) act on a focused field too, and elsewhere the in-window menus' do: the field that had the keyboard when the menu opened gets them, with its selection, and the keyboard back (#516). Not yet: Select All across threaded frames (one frame's text is selected). - 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+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. - Shortcut sets other apps save as text (`.kys`) open in Import Shortcut Set from File › Open, from Edit › Keyboard Shortcuts › Import… in the desktop app, or from a drop on the window. It matches each entry to a tool, command or panel by name and decodes the file's key and modifier codes where VectorCraft's default shortcuts agree on them; the user answers the rest, and Apply fills the Keyboard Shortcuts dialog's working set. The answers are kept with the preferences for the next import (`shortcutSet.read`, `importMatches.*`). - On Windows and Linux the app bar is the window's title bar (its own minimize, maximize and close buttons, drag to move, edges to resize), with the dragon app icon as the brand mark; dialogs and menus size to their content. As on a native menu bar, once one menu is open, moving the pointer onto another title opens that one. - **Tools:**