Type: Touch Type moves an upright character in vertical type down and across its column

An upright glyph in vertical type has its frame's up axis along its column, parallel to the
line, so Touch Type split no drag or arrow-key nudge into kerning and baseline shift and sent
no movement. Such a glyph now takes the line's cross axis, right of its column, as the way
baseline shift moves it.
This commit is contained in:
dotCooCoo
2026-11-04 21:58:52 +07:00
committed by bflatastic
parent ebbbf29f1e
commit a883874a73
2 changed files with 37 additions and 2 deletions
+8 -2
View File
@@ -58,7 +58,8 @@ struct CharBox {
pivot: Point,
/// A unit vector along its line in reading order.
along: Vec2,
/// A unit vector up from its line, without its rotation: the way baseline shift moves it.
/// A unit vector up from its line, without its rotation: the way baseline shift moves it. For
/// a glyph standing upright in vertical type, the line's cross axis (right of its column).
up: Vec2,
/// The glyph's own x axis and its up axis (with its rotation), unit vectors.
ex: Vec2,
@@ -174,6 +175,11 @@ fn char_box(t: &TextObject, lay: &TextLayout, gi: usize) -> Option<CharBox> {
}
let quad = [m * Point::new(x0, y0), m * Point::new(x1, y0), m * Point::new(x1, y1), m * Point::new(x0, y1)];
let d = Vec2::new(g.angle.cos(), g.angle.sin());
// Up from the glyph's frame without its rotation. For a glyph standing upright in vertical
// type, that up runs along the column, so the glyph takes the line's cross axis instead: the
// way baseline shift moves the column's sideways glyphs.
let up = unit(linear(m * g.unturned, Vec2::new(0.0, -1.0)));
let up = if d.cross(up).abs() > 1e-6 { up } else { Vec2::new(d.y, -d.x) };
let style = edit::slice_runs(&t.runs, g.byte, g.byte + g.len).first().map(|r| r.style.clone())?;
Some(CharBox {
start: g.byte,
@@ -181,7 +187,7 @@ fn char_box(t: &TextObject, lay: &TextLayout, gi: usize) -> Option<CharBox> {
pen,
pivot: m * g.pivot,
along: if g.rtl { -d } else { d },
up: unit(linear(m * g.unturned, Vec2::new(0.0, -1.0))),
up,
ex,
ey,
kerned: lay.glyphs.iter().any(|h| h.line == g.line && h.len > 0 && h.byte + h.len == g.byte && h.rtl == g.rtl),
+29
View File
@@ -176,6 +176,35 @@ fn a_character_rotated_near_a_quarter_turn_moves_across_its_line() {
}
}
/// In vertical type, a character standing upright (§) still splits a drag into kerning down its
/// column and baseline shift across it: down 10 pt and right 4 pt is `dx` 10 and `dy` 4. Every
/// arrow key nudges it.
#[test]
fn an_upright_character_in_vertical_type_moves_down_and_across_its_column() {
let (mut d, id, s) = doc_with_type();
if let Some(NodeKind::Text(t)) = d.node_mut(id).map(|n| &mut n.kind) {
**t = TextObject::point(Point::new(100.0, 200.0), "l§", CharStyle { size: 40.0, ..Default::default() });
t.vertical = true;
}
let p = paint();
let cx = cx(&d, &s, &p);
let mut t = TouchTypeTool { target: Some((id, 1)), ..Default::default() };
let q = quad(&t, &cx);
let at = q[0].midpoint(q[2]);
let acts = drag(&mut t, &cx, at, at + Vec2::new(4.0, 10.0));
let p1 = preview(&acts[1]);
assert!(close(p1["dx"].as_f64().unwrap_or(0.0), 10.0) && close(p1["dy"].as_f64().unwrap_or(0.0), 4.0), "{p1}");
for key in [ToolKey::Up, ToolKey::Down, ToolKey::Left, ToolKey::Right] {
let acts = t.key(&cx, key, Mods::default());
let moved = match acts.first() {
Some(Action::Exec(c, v)) if c == "text.touchType" => v,
other => panic!("{key:?}: {other:?}"),
};
let parts = [&moved["dx"], &moved["dy"]].map(|v| v.as_f64().unwrap_or(0.0).abs());
assert!(close(parts[0] + parts[1], 1.0) && (close(parts[0], 0.0) || close(parts[1], 0.0)), "{key:?}: {moved}");
}
}
/// In type scaled unevenly, a drag's parts are measured in document points: right 10 pt and up 4
/// pt of a quarter-turned character is `dx` 10 and `dy` 4.
#[test]