mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-07 15:51:56 +00:00
Type tool: Alt-click on a closed path makes area type too; type along a closed path stays with Type on a Path
This commit is contained in:
@@ -278,21 +278,20 @@ fn direct_selection_drags_an_anchor_of_the_path_and_the_type_reflows() {
|
||||
assert_eq!(path(&s).subpaths[0].anchors[1].p, middle);
|
||||
}
|
||||
|
||||
/// Type on a path made by clicking a path with the plain Type tool (an open path, or a closed one
|
||||
/// with Alt held) shows its brackets as soon as the Selection or Direct Selection tool is chosen,
|
||||
/// and its centre bracket, dragged across the path, flips it.
|
||||
/// Type on a path made by clicking a path with the plain Type tool (an open path) or Type on a
|
||||
/// Path (a closed one) shows its brackets as soon as the Selection or Direct Selection tool is
|
||||
/// chosen, and its centre bracket, dragged across the path, flips it.
|
||||
#[test]
|
||||
fn type_on_a_path_made_with_the_type_tool_has_brackets_at_once() {
|
||||
let v = ViewInfo::default();
|
||||
let alt = Mods { alt: true, ..Mods::default() };
|
||||
for (tool, d, mods) in [
|
||||
("selection", "M100 300 L500 300", Mods::default()),
|
||||
("directSelection", "M100 300 L500 300", Mods::default()),
|
||||
("selection", "M100 300 L500 300 L500 500 L100 500 Z", alt),
|
||||
("selection", "M100 300 L500 300 L500 500 L100 500 Z", Mods::default()),
|
||||
] {
|
||||
let mut s = session();
|
||||
s.execute("path.create", &json!({ "d": d })).unwrap();
|
||||
s.select_tool("type", v).unwrap();
|
||||
s.select_tool(if d.ends_with('Z') { "typeOnPath" } else { "type" }, v).unwrap();
|
||||
for kind in [PointerKind::Down, PointerKind::Up] {
|
||||
s.pointer(&PointerEvent::new(kind, 300.0, 300.0).with_mods(mods), v).unwrap();
|
||||
}
|
||||
|
||||
@@ -257,21 +257,21 @@ look = "The Text I-beam turned a quarter turn."
|
||||
[[cursor]]
|
||||
id = "TypeArea"
|
||||
group = "Type"
|
||||
shown_when = ["Type and Area Type over a closed path's outline, Alt up for Type: a click sets area type inside the path."]
|
||||
shown_when = ["Type over a closed path's outline, and Area Type over one with Alt up: a click sets area type inside the path."]
|
||||
hotspot = "The I-beam's centre."
|
||||
look = "The Text I-beam with a small badge below right: a square frame with three short lines of type in it."
|
||||
|
||||
[[cursor]]
|
||||
id = "TypeAreaVertical"
|
||||
group = "Type"
|
||||
shown_when = ["Vertical Type and Vertical Area Type over a closed path's outline, Alt up for Vertical Type: a click sets vertical area type inside the path."]
|
||||
shown_when = ["Vertical Type over a closed path's outline, and Vertical Area Type over one with Alt up: a click sets vertical area type inside the path."]
|
||||
hotspot = "The I-beam's centre."
|
||||
look = "TypeArea turned a quarter turn."
|
||||
|
||||
[[cursor]]
|
||||
id = "TypeOnPath"
|
||||
group = "Type"
|
||||
shown_when = ["Type over an open path's outline, or a closed one with Alt held; Type on a Path over any path's outline; Area Type over an open path with Alt held: a click sets type along the path."]
|
||||
shown_when = ["Type over an open path's outline; Type on a Path over any path's outline (Alt up); Area Type over an open path with Alt held: a click sets type along the path."]
|
||||
hotspot = "The I-beam's centre."
|
||||
look = "The Text I-beam with a small badge below right: a short smooth curve with a dot on it."
|
||||
|
||||
|
||||
+10
-13
@@ -6,9 +6,8 @@
|
||||
//! the Type tool […] and click anywhere on the object's path", [Create text](https://web.archive.org/web/20220127090642/https://helpx.adobe.com/illustrator/using/creating-text.html);
|
||||
//! "To create horizontal text along a path, select the Type tool or the Type On A Path tool […] If
|
||||
//! the path is closed, rather than open, you must use the Type On A Path tool", [Create type on a
|
||||
//! path](https://web.archive.org/web/20220127000000/https://helpx.adobe.com/illustrator/using/creating-type-path.html)).
|
||||
//! Alt (Option) held over a closed path sets type along it instead: our own shortcut, so the Type
|
||||
//! tool reaches both without switching tools.
|
||||
//! path](https://web.archive.org/web/20220127000000/https://helpx.adobe.com/illustrator/using/creating-type-path.html)),
|
||||
//! Alt or not: type along a closed path stays with Type on a Path.
|
||||
//! Area Type / Type on a Path: click a path to turn it into a text frame or a baseline
|
||||
//! (`text.createInPath`). While type is being edited, a click that lands on no type only
|
||||
//! ends the editing, and the next click makes new type. Point type placed by a click and left
|
||||
@@ -186,8 +185,6 @@ pub struct TypeTool {
|
||||
drag: Option<Point>,
|
||||
/// The Type tool the press acts as, with the keys held at the press ([`Self::acting`]).
|
||||
held: Option<(Mode, bool)>,
|
||||
/// Alt (Option) was held at the press: the Type tool sets type along a closed path, not in it.
|
||||
held_alt: bool,
|
||||
/// Smart Guides for where new type goes.
|
||||
snap: DrawSnap,
|
||||
/// The press landed in the edited text: dragging selects.
|
||||
@@ -619,9 +616,10 @@ impl TypeTool {
|
||||
self.caret = r.end;
|
||||
}
|
||||
|
||||
/// The path whose outline is under `p`, and whether it is closed.
|
||||
/// The path whose outline is under `p`, and whether it is closed: within the isolated group
|
||||
/// being edited, and no guide while guides are hidden or locked ([`ToolContext::hit_options`]).
|
||||
fn path_at(cx: &ToolContext, p: Point) -> Option<(NodeId, bool)> {
|
||||
let h = hit_test(cx.doc, p, vectorcraft_doc::hit::HitOptions { tol: cx.tol(4.0), outline: true, ..Default::default() })?;
|
||||
let h = hit_test(cx.doc, p, vectorcraft_doc::hit::HitOptions { tol: cx.tol(4.0), outline: true, ..cx.hit_options() })?;
|
||||
match &cx.doc.node(h.leaf)?.kind {
|
||||
NodeKind::Path { path, .. } => Some((h.leaf, path.is_closed())),
|
||||
_ => None,
|
||||
@@ -630,12 +628,12 @@ impl TypeTool {
|
||||
|
||||
/// What a click at `p` with the tool acting as `mode` makes of the path there: the path, and
|
||||
/// whether the type goes along it (else inside it); None when it makes no type in a path. The
|
||||
/// Type tools set type inside a closed path and along an open one, or along a closed one with
|
||||
/// `alt`; Area Type needs a closed path and Type on a Path takes either.
|
||||
fn in_path(cx: &ToolContext, p: Point, mode: Mode, alt: bool) -> Option<(NodeId, bool)> {
|
||||
/// Type tools set type inside a closed path and along an open one; Area Type needs a closed
|
||||
/// path and Type on a Path takes either.
|
||||
fn in_path(cx: &ToolContext, p: Point, mode: Mode) -> Option<(NodeId, bool)> {
|
||||
let (id, closed) = Self::path_at(cx, p)?;
|
||||
match mode {
|
||||
Mode::Type => Some((id, alt || !closed)),
|
||||
Mode::Type => Some((id, !closed)),
|
||||
Mode::Area => closed.then_some((id, false)),
|
||||
Mode::OnPath => Some((id, true)),
|
||||
}
|
||||
@@ -662,7 +660,6 @@ impl TypeTool {
|
||||
/// (#2871). A drag that draws an area-type frame makes it either way.
|
||||
fn on_up(&mut self, cx: &ToolContext, start: Point, at: Point) -> Vec<Action> {
|
||||
let (mode, vertical) = self.held.take().unwrap_or((self.mode, self.vertical));
|
||||
let alt = std::mem::take(&mut self.held_alt);
|
||||
// Click into existing text: place the caret.
|
||||
if let Some(out) = self.edit_at(cx, start) {
|
||||
return out;
|
||||
@@ -686,7 +683,7 @@ impl TypeTool {
|
||||
}
|
||||
// A click on a path: type in it or along it.
|
||||
if drag.is_none()
|
||||
&& let Some((pid, on_path)) = Self::in_path(cx, start, mode, alt)
|
||||
&& let Some((pid, on_path)) = Self::in_path(cx, start, mode)
|
||||
{
|
||||
let mode = if on_path { "onPath" } else { "area" };
|
||||
out.push(Action::Exec(
|
||||
|
||||
@@ -817,9 +817,9 @@ fn the_pointer_shows_the_type_tool_shift_or_alt_switches_to() {
|
||||
}
|
||||
|
||||
/// The plain Type tools make type in or along a path clicked on its outline: area type inside a
|
||||
/// closed path, type on a path along an open one or, with Alt held, along a closed one. A click away
|
||||
/// from paths still makes point type, a drag still draws a frame (even from a path's outline), and
|
||||
/// a click on existing type edits it. The pointer says which.
|
||||
/// closed path (Alt held or not: type along a closed path is Type on a Path's) and type on a path
|
||||
/// along an open one. A click away from paths still makes point type, a drag still draws a frame
|
||||
/// (even from a path's outline), and a click on existing type edits it. The pointer says which.
|
||||
#[test]
|
||||
fn the_type_tool_makes_type_in_or_along_a_clicked_path() {
|
||||
let (mut d, rect, line) = doc_with_rect_and_line();
|
||||
@@ -831,7 +831,7 @@ fn the_type_tool_makes_type_in_or_along_a_clicked_path() {
|
||||
for (at, mods, path, mode, cursor) in [
|
||||
(on_rect, Mods::default(), rect, "area", if vertical { Cursor::TypeAreaVertical } else { Cursor::TypeArea }),
|
||||
(on_line, Mods::default(), line, "onPath", if vertical { Cursor::TypeOnPathVertical } else { Cursor::TypeOnPath }),
|
||||
(on_rect, ALT, rect, "onPath", if vertical { Cursor::TypeOnPathVertical } else { Cursor::TypeOnPath }),
|
||||
(on_rect, ALT, rect, "area", if vertical { Cursor::TypeAreaVertical } else { Cursor::TypeArea }),
|
||||
(on_line, ALT, line, "onPath", if vertical { Cursor::TypeOnPathVertical } else { Cursor::TypeOnPath }),
|
||||
] {
|
||||
assert_eq!(TypeTool::new(id).cursor(&c, Point::new(at.0, at.1), mods), cursor, "{id} {at:?} {mods:?}");
|
||||
@@ -874,6 +874,25 @@ fn the_type_tool_makes_type_in_or_along_a_clicked_path() {
|
||||
assert_eq!(tool.editing, Some(tid));
|
||||
}
|
||||
|
||||
/// A guide is no path to set type in or along while guides are hidden or locked: the click makes
|
||||
/// point type, as it does on the selection tools' terms.
|
||||
#[test]
|
||||
fn the_type_tool_leaves_hidden_or_locked_guides_alone() {
|
||||
let (mut d, rect, _) = doc_with_rect_and_line();
|
||||
if let Some(NodeKind::Path { guide, .. }) = d.node_mut(rect).map(|n| &mut n.kind) {
|
||||
*guide = true;
|
||||
}
|
||||
let (sel, p) = (Selection::default(), paint());
|
||||
let mut c = cx(&d, &sel, &p);
|
||||
c.guides = false;
|
||||
assert_eq!(TypeTool::new("type").cursor(&c, Point::new(100.0, 150.0), Mods::default()), Cursor::Text);
|
||||
let acts = gesture(&mut TypeTool::new("type"), &c, (100.0, 150.0), None, Mods::default(), Mods::default());
|
||||
assert_eq!(made(&acts).map(|(cmd, _)| cmd), Some("text.create"), "{acts:?}");
|
||||
c.guides = true;
|
||||
let acts = gesture(&mut TypeTool::new("type"), &c, (100.0, 150.0), None, Mods::default(), Mods::default());
|
||||
assert_eq!(made(&acts).map(|(cmd, _)| cmd), Some("text.createInPath"), "{acts:?}");
|
||||
}
|
||||
|
||||
/// While type is being edited, Shift and Alt switch no tool: a frame dragged out is the tool
|
||||
/// chosen's, and the pointer stays its own.
|
||||
#[test]
|
||||
|
||||
@@ -55,7 +55,6 @@ impl Tool for TypeTool {
|
||||
}
|
||||
self.press = Some((ev.pos, self.snap.press(cx, ev.pos, self.editing.as_slice(), None)));
|
||||
self.held = Some(self.acting(ev.mods));
|
||||
self.held_alt = ev.mods.alt && self.editing.is_none();
|
||||
self.drag = None;
|
||||
vec![]
|
||||
}
|
||||
@@ -633,12 +632,12 @@ impl Tool for TypeTool {
|
||||
/// The pointer of the Type tool that a press would act as with the keys held, or, while a
|
||||
/// press is held, of the one it acts as ([`TypeTool::acting`]).
|
||||
fn cursor(&self, cx: &ToolContext, p: Point, m: Mods) -> Cursor {
|
||||
let ((mode, vertical), alt) = match (self.press, self.held) {
|
||||
(Some(_), Some(held)) => (held, self.held_alt),
|
||||
_ => (self.acting(m), m.alt && self.editing.is_none()),
|
||||
let (mode, vertical) = match (self.press, self.held) {
|
||||
(Some(_), Some(held)) => held,
|
||||
_ => self.acting(m),
|
||||
};
|
||||
// While type is being edited a click on a path only ends the editing.
|
||||
let in_path = if self.editing.is_some() { None } else { Self::in_path(cx, p, mode, alt) };
|
||||
let in_path = if self.editing.is_some() { None } else { Self::in_path(cx, p, mode) };
|
||||
match (in_path, vertical) {
|
||||
(Some((_, false)), false) => Cursor::TypeArea,
|
||||
(Some((_, false)), true) => Cursor::TypeAreaVertical,
|
||||
|
||||
Reference in New Issue
Block a user