From 6883235bad4614b0c7d041fa8b9c9e05effe7fb4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 22 Oct 2026 06:19:15 +0000 Subject: [PATCH] G11: Type on a Path hits filled paths without a stroke (#3505) ## Summary Area Type and Type on a Path locate the path under the click with the same hit rules as the other tools, so a filled shape with no stroke can be clicked in its interior. Illustrator accepts those clicks for area type and for type on a path (the start follows the click on the path). VectorCraft had forced outline-only hit testing here, so interior clicks missed and the tools fell back to ordinary point or area text with placeholder copy. ## Tests - type_on_path_and_area_type_hit_filled_paths_without_a_stroke in vectorcraft-tools Closes storytold/vectorcraft#3505 Co-authored-by: nrq6993-a11y --- crates/tools/src/text.rs | 4 +++- crates/tools/src/text_tests.rs | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/crates/tools/src/text.rs b/crates/tools/src/text.rs index 2628385c1..cf434afb3 100644 --- a/crates/tools/src/text.rs +++ b/crates/tools/src/text.rs @@ -250,7 +250,9 @@ 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 h = hit_test(cx.doc, p, vectorcraft_doc::hit::HitOptions { tol: cx.tol(4.0), outline: true, ..Default::default() })?; + let mut opt = cx.hit_options(); + opt.tol = cx.tol(4.0); + let h = hit_test(cx.doc, p, opt)?; 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 7554851cd..502f2294e 100644 --- a/crates/tools/src/text_tests.rs +++ b/crates/tools/src/text_tests.rs @@ -419,6 +419,27 @@ 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. +#[test] +fn type_on_path_and_area_type_hit_filled_paths_without_a_stroke() { + 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 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)); + 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:?}" + ); + } +} + #[test] fn vertical_tools_preserve_their_ids_and_create_vertical_text() { for id in ["verticalType", "verticalAreaType", "verticalTypeOnPath"] {