T: Type on a Path — tool, options, screen and PDF

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas
2026-10-02 20:34:20 -04:00
co-authored by Claude Opus 5.5
parent bfe4220840
commit eb4fa9c64c
15 changed files with 454 additions and 6 deletions
+1
View File
@@ -274,6 +274,7 @@ pub fn create(id: &str) -> Box<dyn Tool> {
"placeGun" => Box::new(nav::PlaceGun::default()),
"zoom" => Box::new(nav::ZoomTool::default()),
"page" => Box::new(nav::PageTool),
"typeOnPath" => Box::new(nav::PathTypeTool),
other => Box::new(NoopTool(tool_info(other).map(|t| t.id).unwrap_or("selection"))),
}
}
+24
View File
@@ -172,3 +172,27 @@ impl Tool for PageTool {
Cursor::Arrow
}
}
/// Type on a Path tool (Shift+T): click a path to put type on it; typing goes there.
#[derive(Default)]
pub struct PathTypeTool;
impl Tool for PathTypeTool {
fn id(&self) -> &'static str {
"typeOnPath"
}
fn pointer(&mut self, cx: &ToolContext, ev: &PointerEvent) -> Vec<Action> {
if ev.kind != PointerKind::Up {
return vec![];
}
let Some((_, id)) = cx.hit(ev.pos) else { return vec![] };
let Some(it) = cx.doc.item(id) else { return vec![] };
match &it.content {
designcraft_doc::Content::Unassigned => vec![Action::Exec("type.onPath".into(), json!({"id": id.0})), Action::SwitchTool("type".into())],
_ => vec![],
}
}
fn cursor(&self, _cx: &ToolContext, _p: Point, _m: Mods) -> Cursor {
Cursor::Text
}
}
+21
View File
@@ -214,3 +214,24 @@ fn page_tool_opens_page_size() {
let a = t.pointer(&cx, &PointerEvent::new(PointerKind::Up, 200.0 + off.x + d.spreads[0].pages[0].x, 200.0 + off.y));
assert_eq!(a, vec![Action::Dialog("cmd:layout.pageSize".into(), serde_json::json!({"pages": [1], "width": 612.0, "height": 792.0}))]);
}
#[test]
fn type_on_path_tool_targets_paths() {
let mut d = Document::new(&NewDocument::default());
let lid = d.default_layer();
let id = designcraft_doc::ItemId(d.alloc());
let mut it = designcraft_doc::Item::new(
id,
lid,
designcraft_doc::Shape::GraphicLine,
designcraft_geom::shapes::line(designcraft_geom::Point::new(100.0, 100.0), designcraft_geom::Point::new(300.0, 100.0)),
);
it.stroke = designcraft_doc::Stroke::default();
d.insert_item(SpreadRef::Doc(0), it, None).unwrap();
let (s, c, l) = (Selection::default(), Cache::new(), CanvasLayout::new(&d, false));
let cx = ctx(&d, &s, &c, &l);
let off = l.offset(SpreadRef::Doc(0));
let mut t = create("typeOnPath");
let a = t.pointer(&cx, &PointerEvent::new(PointerKind::Up, 200.0 + off.x, 100.0 + off.y));
assert_eq!(a, vec![Action::Exec("type.onPath".into(), serde_json::json!({"id": id.0})), Action::SwitchTool("type".into())]);
}