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 879e75e90..75fa760bb 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 0ed460cae..1bcc2ec9b 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 552136fd4..cfb20ae01 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 2583ea959..d18d3188d 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 86794d785..d0e2eadb6 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 09dd23f52..ae91f85f2 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 45fe554f2..55a324ff0 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 a4126fab1..3d708acac 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 ab1738b33..003804b69 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 235330013..000430e2e 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 602d502d7..88910feea 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 0662b14f6..c6cbcd145 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 84dfaece0..9721f5ab9 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 9eb89a47e..50f3ca5e9 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 4c4cb4f1d..04ef38a44 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 79bc524de..c183c3dd9 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 b6f14c967..1888009a3 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 228db52f4..4ce194420 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 b1f7a34b6..0521a5404 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 e4bec9dff..a37856f21 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 a39e1af81..654f8bfea 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 4ce18df1c..d86a1440b 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 0163eb7d7..2147936c3 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 09254c861..a433251d2 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 bd8f70d90..a32545b4f 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:**