From 8735170a34221a4a2c6c00dd561e91984c61e287 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 22 Oct 2026 07:57:15 +0000 Subject: [PATCH] Type tools: hit path outline only for area and path type ## Summary Adobe help documents Area Type and Type on a Path as starting from a click on the path or shape border, not inside a filled interior. Restore outline-only hit testing for those tools (no fill-interior hits that would steal point-type clicks). Strokeless filled paths still accept clicks on the outline. ## Tests - area_and_path_tools_hit_path_outline_not_fill_interior Part of storytold/vectorcraft#3505 (review feedback on PR #3534) Co-authored-by: nrq6993-a11y --- crates/tools/src/text.rs | 4 +--- crates/tools/src/text_tests.rs | 25 +++++++++++++++++-------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/crates/tools/src/text.rs b/crates/tools/src/text.rs index cf434afb3..2628385c1 100644 --- a/crates/tools/src/text.rs +++ b/crates/tools/src/text.rs @@ -250,9 +250,7 @@ impl TypeTool { /// Path under `p` for the Area Type / Type on a Path tools. fn path_at(cx: &ToolContext, p: Point, closed: bool) -> Option { - let mut opt = cx.hit_options(); - opt.tol = cx.tol(4.0); - let h = hit_test(cx.doc, p, opt)?; + let h = hit_test(cx.doc, p, vectorcraft_doc::hit::HitOptions { tol: cx.tol(4.0), outline: true, ..Default::default() })?; match &cx.doc.node(h.leaf)?.kind { NodeKind::Path { path, .. } if !closed || path.is_closed() => Some(h.leaf), _ => None, diff --git a/crates/tools/src/text_tests.rs b/crates/tools/src/text_tests.rs index 502f2294e..f5ebcad01 100644 --- a/crates/tools/src/text_tests.rs +++ b/crates/tools/src/text_tests.rs @@ -419,23 +419,32 @@ fn area_and_path_tools_convert_clicked_paths() { assert!(acts.iter().any(|a| matches!(a, Action::Exec(cmd, p) if cmd == "text.createInPath" && p["mode"] == "onPath")), "{acts:?}"); } -/// #3505: a filled path with no stroke is hittable in the interior, not only on its outline. +/// Area Type and Type on a Path follow the reference app: click the path outline, not the fill interior +/// (helpx: add-text-work-with-type-objects, creating-type-path). #[test] -fn type_on_path_and_area_type_hit_filled_paths_without_a_stroke() { +fn area_and_path_tools_hit_path_outline_not_fill_interior() { let (mut d, rect) = crate::testutil::doc_with_rect(); if let Some(n) = d.node_mut(rect) { n.appearance.set_stroke(vectorcraft_color::Paint::None); } let (sel, p) = (Selection::default(), paint()); let c = cx(&d, &sel, &p); - let center = (150.0, 150.0); - for (tool_id, mode) in [("areaType", "area"), ("typeOnPath", "onPath")] { + let edge = (100.0, 150.0); + let interior = (150.0, 150.0); + for tool_id in ["areaType", "typeOnPath"] { let mut tool = TypeTool::new(tool_id); - tool.pointer(&c, &PointerEvent::new(PointerKind::Down, center.0, center.1)); - let acts = tool.pointer(&c, &PointerEvent::new(PointerKind::Up, center.0, center.1)); + tool.pointer(&c, &PointerEvent::new(PointerKind::Down, edge.0, edge.1)); + let acts = tool.pointer(&c, &PointerEvent::new(PointerKind::Up, edge.0, edge.1)); assert!( - acts.iter().any(|a| matches!(a, Action::Exec(cmd, v) if cmd == "text.createInPath" && v["path"] == json!(rect.0) && v["mode"] == mode)), - "{tool_id}: {acts:?}" + acts.iter().any(|a| matches!(a, Action::Exec(cmd, v) if cmd == "text.createInPath" && v["path"] == json!(rect.0))), + "{tool_id} on outline: {acts:?}" + ); + let mut tool = TypeTool::new(tool_id); + tool.pointer(&c, &PointerEvent::new(PointerKind::Down, interior.0, interior.1)); + let acts = tool.pointer(&c, &PointerEvent::new(PointerKind::Up, interior.0, interior.1)); + assert!( + !acts.iter().any(|a| matches!(a, Action::Exec(cmd, _) if cmd == "text.createInPath")), + "{tool_id} in fill interior should not attach to path: {acts:?}" ); } }