mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 17:56:50 -06:00
Type: Cmd/Ctrl+Alt+Q resets tracking to 0 and kerning to Auto
The reference app resets tracking and kerning to 0 with Cmd/Ctrl+Alt+Q. VectorCraft had no command for it. type.resetTracking (Reset Tracking and Kerning, default Cmd+Alt+Q) sets tracking to 0 and kerning to Auto, which the Character panel shows as 0, in the text the Type tool has selected, or else in all the text of every selected type object. At a caret it resets only the kerning of the character before it, where Alt+Left/Right kerns. It is one undo step. The range logic of type.step moves into a shared type_ranges. When a call gives ids, type.step and type.resetTracking change all the text of those type objects even while the Type tool edits text, as paint.setFill does. Before, type.step changed the Type tool's selected text instead. Docs: docs/mcp.md, docs/target-app-parity.md.
This commit is contained in:
@@ -63,7 +63,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Step Type",
|
||||
[],
|
||||
None,
|
||||
"{attribute: \"size\"|\"leading\"|\"tracking\"|\"kerning\"|\"baselineShift\", by?: steps (1; negative steps down), id?, start?: byte, end?: byte, ids?: [id…] (instead of id)} step type by the Preferences › Type increments (size and leading: typeSizeIncrement, pt; tracking and kerning: trackingIncrement, 1/1000 em; baseline shift: baselineShiftIncrement, pt), what the Type tool's Alt+arrows do (Cmd/Ctrl too: five steps; ←/→ kerning at the caret or tracking of the selection, ↑/↓ leading, Shift+↑/↓ baseline shift). The range of `id`, else the text the Type tool has selected (at a caret: kerning steps the character before it, leading its paragraph), else every selected type object → {ids}",
|
||||
"{attribute: \"size\"|\"leading\"|\"tracking\"|\"kerning\"|\"baselineShift\", by?: steps (1; negative steps down), id?, start?: byte, end?: byte, ids?: [id…] (instead of id)} step type by the Preferences › Type increments (size and leading: typeSizeIncrement, pt; tracking and kerning: trackingIncrement, 1/1000 em; baseline shift: baselineShiftIncrement, pt), what the Type tool's Alt+arrows do (Cmd/Ctrl too: five steps; ←/→ kerning at the caret or tracking of the selection, ↑/↓ leading, Shift+↑/↓ baseline shift). The range of `id`, else the type objects in `ids`, else the text the Type tool has selected (at a caret: kerning steps the character before it, leading its paragraph), else every selected type object → {ids}",
|
||||
has_doc,
|
||||
type_step
|
||||
),
|
||||
@@ -85,6 +85,15 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
has_selection,
|
||||
|s, p| type_step(s, &json!({"attribute": "size", "by": -f64_or(p, "by", 1.0)}))
|
||||
),
|
||||
cmd!(
|
||||
"type.resetTracking",
|
||||
"Reset Tracking and Kerning",
|
||||
[],
|
||||
Some("Cmd+Alt+Q"),
|
||||
"{ids?: [id…]} set tracking to 0 and kerning to Auto (the font's own kerning, which the Character panel shows as 0) in all the text of the type objects in `ids`, else in the text the Type tool has selected (at a caret: only the kerning of the character before it), else in every selected type object, as one undo step → {ids}",
|
||||
has_selection,
|
||||
reset_tracking
|
||||
),
|
||||
cmd!(
|
||||
"text.discardEmpty",
|
||||
"Discard Empty Type",
|
||||
@@ -598,6 +607,46 @@ pub(crate) fn editing_range(s: &mut Session) -> Result<Option<(NodeId, usize, us
|
||||
Ok(Some((id, at("start"), at("end"))))
|
||||
}
|
||||
|
||||
/// The text ranges a type shortcut changes: the range of `id`, else all the text of the type
|
||||
/// objects in `ids`, else the text the Type tool has selected, else all the text of every selected
|
||||
/// type object. At a caret, `at_caret` gives the range from the plain text and the caret's byte
|
||||
/// (None: nothing changes there). Each range comes with whether it came from a caret.
|
||||
fn type_ranges(
|
||||
s: &mut Session,
|
||||
p: &Value,
|
||||
c: &str,
|
||||
at_caret: impl Fn(&str, usize) -> Option<(usize, usize)>,
|
||||
) -> Result<Vec<(NodeId, usize, usize, bool)>> {
|
||||
// (text, its range; None: all of it).
|
||||
let targets: Vec<(NodeId, Option<(usize, usize)>)> = match id_param(p, "id") {
|
||||
Some(id) => vec![(id, Some(range_of(p, edit::runs_len(&text_ref(s, id)?.runs))))],
|
||||
None => {
|
||||
let editing = editing_range(s)?.filter(|_| p.get("ids").filter(|v| !v.is_null()).is_none());
|
||||
match editing {
|
||||
Some((id, a, b)) => vec![(id, Some((a.min(b), a.max(b))))],
|
||||
None => super::typecmd::text_targets(s, p, c)?.into_iter().map(|id| (id, None)).collect(),
|
||||
}
|
||||
}
|
||||
};
|
||||
let mut ranges = vec![];
|
||||
for (id, range) in targets {
|
||||
let text = text_ref(s, id)?.plain_text();
|
||||
let (a, b) = range.map_or((0, text.len()), |(a, b)| (a.min(text.len()), b.min(text.len())));
|
||||
let (a, b, caret) = if a < b {
|
||||
(a, b, false)
|
||||
} else {
|
||||
match at_caret(&text, a) {
|
||||
Some((a, b)) => (a, b, true),
|
||||
None => continue,
|
||||
}
|
||||
};
|
||||
if a < b {
|
||||
ranges.push((id, a, b, caret));
|
||||
}
|
||||
}
|
||||
Ok(ranges)
|
||||
}
|
||||
|
||||
fn type_step(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
const C: &str = "type.step";
|
||||
let step = match str_param(p, "attribute") {
|
||||
@@ -619,36 +668,19 @@ fn type_step(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
Step::Tracking | Step::Kerning => prefs.tracking_increment,
|
||||
Step::BaselineShift => prefs.baseline_shift_increment,
|
||||
};
|
||||
// (text, its range; None: all of it).
|
||||
let targets: Vec<(NodeId, Option<(usize, usize)>)> = match id_param(p, "id") {
|
||||
Some(id) => vec![(id, Some(range_of(p, edit::runs_len(&text_ref(s, id)?.runs))))],
|
||||
None => match editing_range(s)? {
|
||||
Some((id, a, b)) => vec![(id, Some((a.min(b), a.max(b))))],
|
||||
None => super::typecmd::text_targets(s, p, C)?.into_iter().map(|id| (id, None)).collect(),
|
||||
},
|
||||
};
|
||||
// The ranges stepped. At a caret: kerning is the space after the character before it, leading
|
||||
// the paragraph's; the rest wait for a selection.
|
||||
let mut ranges = vec![];
|
||||
for (id, range) in targets {
|
||||
let text = text_ref(s, id)?.plain_text();
|
||||
let (a, b) = range.map_or((0, text.len()), |(a, b)| (a.min(text.len()), b.min(text.len())));
|
||||
let (a, b) = match step {
|
||||
_ if a < b => (a, b),
|
||||
Step::Kerning => (edit::prev_char(&text, a), a),
|
||||
Step::Leading => {
|
||||
let para = edit::paragraph_at(&text, a);
|
||||
(para.start, para.end)
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
if a < b {
|
||||
ranges.push((id, a, b));
|
||||
let ranges = type_ranges(s, p, C, |text, a| match step {
|
||||
Step::Kerning => Some((edit::prev_char(text, a), a)),
|
||||
Step::Leading => {
|
||||
let para = edit::paragraph_at(text, a);
|
||||
Some((para.start, para.end))
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
})?;
|
||||
if !ranges.is_empty() {
|
||||
s.edit("Character", |d, _| {
|
||||
for &(id, a, b) in &ranges {
|
||||
for &(id, a, b, _) in &ranges {
|
||||
let t = text_mut(d, id).ok_or(EngineError::NoNode(id))?;
|
||||
let points = if matches!(step, Step::Size | Step::Leading | Step::BaselineShift) {
|
||||
t.style_scale().ok_or_else(|| bad(C, "text transform is collapsed or unrepresentable"))?.points
|
||||
@@ -679,6 +711,29 @@ fn type_step(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
Ok(json!({ "ids": ranges.iter().map(|(id, ..)| id.0).collect::<Vec<_>>() }))
|
||||
}
|
||||
|
||||
/// Cmd/Ctrl+Alt+Q: tracking 0 and kerning Auto (0 in the Character panel) in the type objects in
|
||||
/// `ids`, else in the Type tool's selected text, else in every selected type object. At a caret,
|
||||
/// only the kerning of the character before it changes, as Alt+←/→ kerns there.
|
||||
fn reset_tracking(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let ranges = type_ranges(s, p, "type.resetTracking", |text, a| Some((edit::prev_char(text, a), a)))?;
|
||||
if !ranges.is_empty() {
|
||||
s.edit("Reset Tracking and Kerning", |d, _| {
|
||||
for &(id, a, b, caret) in &ranges {
|
||||
let t = text_mut(d, id).ok_or(EngineError::NoNode(id))?;
|
||||
edit::style_range(&mut t.runs, a, b, |st| {
|
||||
st.kerning = None;
|
||||
if !caret {
|
||||
st.tracking = 0.0;
|
||||
}
|
||||
});
|
||||
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> {
|
||||
|
||||
@@ -1019,6 +1019,54 @@ fn type_increments_step_the_type() {
|
||||
// The font size shortcut steps the selected text while the tool edits.
|
||||
s.execute("type.size.increase", &json!({})).unwrap();
|
||||
assert_eq!((style_at(&s, id, 0).size, style_at(&s, id, 6).size), (19.0, 22.0));
|
||||
// The type objects given in `ids` step in full while the tool edits.
|
||||
s.execute("type.step", &json!({"attribute": "size", "ids": [id.0]})).unwrap();
|
||||
assert_eq!((style_at(&s, id, 0).size, style_at(&s, id, 6).size), (22.0, 25.0));
|
||||
}
|
||||
|
||||
/// Cmd/Ctrl+Alt+Q resets tracking to 0 and kerning to Auto (0 in the Character panel) in every
|
||||
/// selected type object and in the Type tool's selected text. At a caret it resets only the
|
||||
/// kerning of the character before it. It is one undo step. Type objects given in `ids` are reset
|
||||
/// in full while the Type tool edits, and the typing in progress ends first.
|
||||
#[test]
|
||||
fn reset_tracking_sets_tracking_and_kerning_to_zero() {
|
||||
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 spaced = |s: &mut Session| s.execute("text.setRangeStyle", &json!({"id": id.0, "tracking": 200, "kerning": -40})).unwrap();
|
||||
let spacing = |s: &Session, byte| {
|
||||
let st = style_at(s, id, byte);
|
||||
(st.tracking, st.kerning)
|
||||
};
|
||||
spaced(&mut s);
|
||||
let other = NodeId(s.execute("text.create", &json!({"x": 100, "y": 300, "text": "Other", "size": 20})).unwrap()["id"].as_u64().unwrap());
|
||||
s.execute("text.setRangeStyle", &json!({"id": other.0, "tracking": 200})).unwrap();
|
||||
s.execute("select.set", &json!({"ids": [id.0]})).unwrap();
|
||||
assert_eq!(find_command("type.resetTracking").unwrap().shortcut, Some("Cmd+Alt+Q"));
|
||||
assert_eq!(s.execute("type.resetTracking", &json!({})).unwrap()["ids"], json!([id.0]));
|
||||
assert_eq!((spacing(&s, 0), spacing(&s, 10)), ((0.0, None), (0.0, None)));
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
assert_eq!(spacing(&s, 0), (200.0, Some(-40.0)), "one undo step");
|
||||
// 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.resetTracking", &json!({})).unwrap();
|
||||
assert_eq!((spacing(&s, 4), spacing(&s, 6)), ((0.0, None), (200.0, Some(-40.0))));
|
||||
// At a caret it resets only the kerning of the character before it, and the tracking does not change.
|
||||
s.set_tool_option("select", &json!({"start": 8, "end": 8}));
|
||||
s.execute("type.resetTracking", &json!({})).unwrap();
|
||||
assert_eq!((spacing(&s, 6), spacing(&s, 7), spacing(&s, 8)), ((200.0, Some(-40.0)), (200.0, None), (200.0, Some(-40.0))));
|
||||
// While the Type tool edits, the type objects in `ids` are reset in full.
|
||||
s.execute("type.resetTracking", &json!({"ids": [id.0]})).unwrap();
|
||||
assert_eq!((spacing(&s, 6), spacing(&s, 10)), ((0.0, None), (0.0, None)));
|
||||
// Typing in progress ends first, so the reset of another object stays when typing goes on.
|
||||
s.tool_text("!", v).unwrap();
|
||||
s.execute("type.resetTracking", &json!({"ids": [other.0]})).unwrap();
|
||||
s.tool_text("?", v).unwrap();
|
||||
assert_eq!(style_at(&s, other, 0).tracking, 0.0, "the reset stays");
|
||||
}
|
||||
|
||||
/// Type › Fill New Type Objects With Placeholder Text (#394, on by default): type the Type tool
|
||||
|
||||
@@ -1038,6 +1038,26 @@ fn type_preferences_apply_to_agents() {
|
||||
assert_eq!(style(&mut s, 6), (size + 4.0, tracking + 50.0));
|
||||
}
|
||||
|
||||
/// Cmd+Alt+Q reaches agents: the placeholder text the Type tool selects goes back to tracking 0
|
||||
/// and Auto kerning.
|
||||
#[test]
|
||||
fn cmd_alt_q_resets_tracking_and_kerning_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::<Value>(&text_of(&r)).unwrap()["selection"][0].as_u64().unwrap();
|
||||
let r = call(&mut s, 2, "run_command", json!({"command": "text.setRangeStyle", "params": {"id": id, "tracking": 200, "kerning": -40}}));
|
||||
assert_eq!(r["isError"], false, "{r}");
|
||||
let r = call(&mut s, 3, "press_key", json!({"key": "Q", "mods": {"cmd": true, "alt": true}}));
|
||||
assert_eq!(r["isError"], false, "{r}");
|
||||
assert_eq!(serde_json::from_str::<Value>(&text_of(&r)).unwrap()["command"], "type.resetTracking", "{r}");
|
||||
let r = call(&mut s, 4, "run_command", json!({"command": "text.getRange", "params": {"id": id}}));
|
||||
let runs = serde_json::from_str::<Value>(&text_of(&r)).unwrap()["runs"].clone();
|
||||
assert_eq!(runs.as_array().unwrap().len(), 1, "{runs}");
|
||||
assert_eq!((&runs[0]["style"]["tracking"], &runs[0]["style"]["kerning"]), (&json!(0.0), &Value::Null), "{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.
|
||||
|
||||
@@ -3456,6 +3456,7 @@ axis Both Obě
|
||||
Area / Path Type Text v oblasti / na cestě
|
||||
Decrease Font Size Zmenšit velikost písma
|
||||
Increase Font Size Zvětšit velikost písma
|
||||
Reset Tracking and Kerning Obnovit prostrkání a vyrovnání párů
|
||||
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.
|
@@ -1049,6 +1049,7 @@
|
||||
Fit Headline Überschrift einpassen
|
||||
Decrease Font Size Schriftgrad verringern
|
||||
Increase Font Size Schriftgrad erhöhen
|
||||
Reset Tracking and Kerning Laufweite und Kerning zurücksetzen
|
||||
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.
|
@@ -1066,6 +1066,7 @@
|
||||
Fit Headline Ajustar titular
|
||||
Decrease Font Size Reducir tamaño de fuente
|
||||
Increase Font Size Aumentar tamaño de fuente
|
||||
Reset Tracking and Kerning Restablecer espaciado e interletraje
|
||||
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.
|
@@ -1065,6 +1065,7 @@
|
||||
Fit Headline Ajuster le titre
|
||||
Decrease Font Size Réduire la taille de police
|
||||
Increase Font Size Augmenter la taille de police
|
||||
Reset Tracking and Kerning Réinitialiser l’approche et le crénage
|
||||
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.
|
@@ -2915,6 +2915,7 @@
|
||||
Reset to Defaults Alapértékek visszaállítása
|
||||
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
|
||||
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.
|
@@ -1065,6 +1065,7 @@
|
||||
Fit Headline Adatta titolo
|
||||
Decrease Font Size Riduci dimensione font
|
||||
Increase Font Size Aumenta dimensione font
|
||||
Reset Tracking and Kerning Reimposta spaziatura caratteri e crenatura
|
||||
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.
|
@@ -329,6 +329,7 @@
|
||||
Fit Headline ヘッドラインを合わせる
|
||||
Decrease Font Size フォントサイズを小さく
|
||||
Increase Font Size フォントサイズを大きく
|
||||
Reset Tracking and Kerning トラッキングとカーニングをリセット
|
||||
Step Type 文字を段階的に調整
|
||||
Find Font… フォントの検索と置換…
|
||||
Change Case 大文字と小文字の変更
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -325,6 +325,7 @@
|
||||
Fit Headline 제목에 맞추기
|
||||
Decrease Font Size 글꼴 크기 줄이기
|
||||
Increase Font Size 글꼴 크기 키우기
|
||||
Reset Tracking and Kerning 자간 및 커닝 재설정
|
||||
Step Type 단계별 문자
|
||||
Find Font… 글꼴 찾기…
|
||||
Change Case 대소문자 변경
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -3288,6 +3288,7 @@ axis Both Obie
|
||||
Area / Path Type Tekst akapitowy / na ścieżce
|
||||
Decrease Font Size Zmniejsz rozmiar czcionki
|
||||
Increase Font Size Zwiększ rozmiar czcionki
|
||||
Reset Tracking and Kerning Resetuj światło i kerning
|
||||
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.
|
@@ -949,6 +949,7 @@
|
||||
Fit Headline Ajustar Manchete
|
||||
Decrease Font Size Diminuir Tamanho da Fonte
|
||||
Increase Font Size Aumentar Tamanho da Fonte
|
||||
Reset Tracking and Kerning Redefinir Rastreamento e Kerning
|
||||
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.
|
@@ -1063,6 +1063,7 @@
|
||||
Fit Headline Подогнать заголовок
|
||||
Decrease Font Size Уменьшить размер шрифта
|
||||
Increase Font Size Увеличить размер шрифта
|
||||
Reset Tracking and Kerning Сбросить трекинг и кернинг
|
||||
Spacing Интервал
|
||||
Step Type Подгонять текст по шагам
|
||||
Fit in View Вписать в вид
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1059,6 +1059,7 @@
|
||||
Fit Headline Підігнати заголовок
|
||||
Decrease Font Size Зменшити розмір шрифту
|
||||
Increase Font Size Збільшити розмір шрифту
|
||||
Reset Tracking and Kerning Скинути трекінг і кернінг
|
||||
Spacing Інтервал
|
||||
Step Type Підганяти текст кроками
|
||||
Fit in View Вписати у вигляд
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 715 and column 40.
|
@@ -2660,6 +2660,7 @@
|
||||
Reset to Default Đặt lại về mặc định
|
||||
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
|
||||
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
|
||||
|
||||
|
@@ -1018,6 +1018,7 @@
|
||||
Fit Headline 适合标题
|
||||
Decrease Font Size 减小字体大小
|
||||
Increase Font Size 增大字体大小
|
||||
Reset Tracking and Kerning 重置字距和字偶距
|
||||
Spacing 间距
|
||||
Step Type 按增量调整文字
|
||||
Fit in View 适合视图
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1041,6 +1041,7 @@
|
||||
Fit Headline 符合標題
|
||||
Decrease Font Size 縮小字體大小
|
||||
Increase Font Size 放大字體大小
|
||||
Reset Tracking and Kerning 重設字距與字距微調
|
||||
Spacing 間距
|
||||
Step Type 依增量調整文字
|
||||
Fit in View 符合檢視
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -662,6 +662,40 @@ mod tests {
|
||||
assert!(!crate::menus::enabled(&app, "type.alignCenter"));
|
||||
}
|
||||
|
||||
/// Cmd+Alt+Q resets the tracking and kerning of the selected type (tracking 0, kerning Auto),
|
||||
/// and of the Type tool's selected text while it edits.
|
||||
#[test]
|
||||
fn cmd_alt_q_resets_tracking_and_kerning() {
|
||||
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 spaced =
|
||||
|app: &mut VectorcraftApp| app.session.execute("text.setRangeStyle", &json!({"id": id, "tracking": 200, "kerning": -40})).unwrap();
|
||||
let spacing = |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"]["tracking"].clone(), run["style"]["kerning"].clone()))
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
let chord =
|
||||
egui::Event::Key { key: Key::Q, physical_key: None, pressed: true, repeat: false, modifiers: Modifiers::COMMAND | Modifiers::ALT };
|
||||
spaced(&mut app);
|
||||
app.session.execute("select.set", &json!({"ids": [id]})).unwrap();
|
||||
frame(&mut app, vec![chord.clone()]);
|
||||
assert_eq!(spacing(&mut app), [(json!("One Two"), json!(0.0), json!(null))]);
|
||||
// While the Type tool edits: only its selection.
|
||||
spaced(&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, vec![chord]);
|
||||
assert_eq!(spacing(&mut app), [(json!("One "), json!(200.0), json!(-40.0)), (json!("Two"), json!(0.0), json!(null))]);
|
||||
}
|
||||
|
||||
/// 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.
|
||||
|
||||
+7
-4
@@ -1917,10 +1917,13 @@ Pen's second anchor at (150, 431), in line with the square's centre.
|
||||
- `placeholderText` (on by default): type the Type tools place (`pointer_gesture` with `type`, `areaType`…) starts with
|
||||
placeholder text, selected, so `type_text` replaces it; `text.create` and `text.createInPath` take `placeholder: true`.
|
||||
- `typeSizeIncrement`, `trackingIncrement`, `baselineShiftIncrement`: what `type.step {attribute: "size" | "leading" |
|
||||
"tracking" | "kerning" | "baselineShift", by?}` steps by, on the text range given, the Type tool's selected text or the
|
||||
selected type objects. `type.size.increase` / `type.size.decrease` (Cmd+Shift+. and Cmd+Shift+,) step the size; the
|
||||
Type tool's Alt+arrows (`press_key {key: "Right", mods: {alt: true}}`) step kerning at a caret or tracking of the
|
||||
selection (←/→), leading (↑/↓) and baseline shift (Shift+↑/↓), five steps with Cmd/Ctrl too.
|
||||
"tracking" | "kerning" | "baselineShift", by?}` steps by, on the text range given, the type objects in `ids`, the
|
||||
Type tool's selected text or the selected type objects. `type.size.increase` / `type.size.decrease` (Cmd+Shift+. and
|
||||
Cmd+Shift+,) step the size; the Type tool's Alt+arrows (`press_key {key: "Right", mods: {alt: true}}`) step kerning at
|
||||
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.
|
||||
- `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
|
||||
|
||||
@@ -438,6 +438,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
- Tab shows and hides the panels (in the Type tool it types a tab) without moving the keyboard focus onto a field, so the next shortcut or letter still works; in dialogs and panel fields it moves between fields.
|
||||
- Shift+Tab (`window.panels`) shows and hides the dock and the floating panels and leaves the toolbar and the Control bar as they are; hiding the panels with Tab or Shift+Tab also closes a panel popped out of the icon column.
|
||||
- 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+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.
|
||||
- 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:**
|
||||
|
||||
Reference in New Issue
Block a user