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.
This commit is contained in:
dotCooCoo
2026-10-16 14:58:09 +07:00
committed by bflatastic
parent ad1e8d1112
commit 6dfdf643d9
25 changed files with 144 additions and 1 deletions
+29
View File
@@ -112,6 +112,15 @@ pub fn specs() -> Vec<CommandSpec> {
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<Value> {
Ok(json!({ "ids": ranges.iter().map(|(id, ..)| id.0).collect::<Vec<_>>() }))
}
/// 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<Value> {
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::<Vec<_>>() }))
}
/// 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<f64> {