From 5cbb7989f9e1fe43af5e6238feae6f5c8e26265f Mon Sep 17 00:00:00 2001 From: Fabio Lussi <198260595+itrycodinganditsbad@users.noreply.github.com> Date: Mon, 12 Oct 2026 01:38:00 +0200 Subject: [PATCH] Pen + Alt converts anchors of paths in a selected group Fixes #1273. A selected group or compound path shows its paths' anchors with the Pen, but Pen + Alt only looked at paths selected directly, so an Alt-click there started a new path instead of converting the anchor. Co-Authored-By: Claude Opus 5.5 --- crates/engine/src/tests_draw2.rs | 26 ++++++++++++++++++++++++++ crates/tools/src/draw2.rs | 23 +++++++++++++++++++++++ crates/tools/src/pen.rs | 9 +++++---- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/crates/engine/src/tests_draw2.rs b/crates/engine/src/tests_draw2.rs index b5a6aa2d..004c9d1d 100644 --- a/crates/engine/src/tests_draw2.rs +++ b/crates/engine/src/tests_draw2.rs @@ -1035,3 +1035,29 @@ fn join_takes_the_paths_and_the_ends_to_join() { assert!(s.execute("path.join", &bad).is_err(), "{bad}"); } } + +#[test] +fn pen_with_alt_converts_the_anchors_of_a_path_in_a_selected_group() { + // #1273: a selected group (or compound path) shows its paths' anchors, so Alt with the Pen + // converts them as it does a selected path's, rather than starting a new path there. + let alt = Mods { alt: true, ..Mods::default() }; + let v = view(); + for wrap in ["object.group", "object.compoundPath.make"] { + let mut s = session(); + let anchors = json!([{"x": 100, "y": 200}, {"x": 200, "y": 100, "in": [150, 100], "out": [250, 100]}, {"x": 300, "y": 200}]); + let id = NodeId(s.execute("path.create", &json!({"anchors": anchors})).unwrap()["id"].as_u64().unwrap()); + s.execute(wrap, &json!({})).unwrap(); + assert!(!s.doc().unwrap().selection.objects.contains(&id), "{wrap}: the wrapper is selected, not the path"); + s.select_tool("pen", v).unwrap(); + // Alt-click the smooth anchor: a corner, in one step, and no new path. + assert_eq!(gesture(&mut s, &[(200.0, 100.0)], alt), 1, "{wrap}"); + let a = path(&s, id).subpaths[0].anchors[1]; + assert!(!a.has_in() && !a.has_out(), "{wrap}: a corner: {a:?}"); + assert_eq!(paths(&s).len(), 1, "{wrap}: no new path"); + // Alt-drag it: smooth again. + assert_eq!(gesture(&mut s, &[(200.0, 100.0), (220.0, 100.0), (240.0, 110.0)], alt), 1, "{wrap}"); + let a = path(&s, id).subpaths[0].anchors[1]; + assert_eq!((a.h_in, a.h_out), (Point::new(160.0, 90.0), Point::new(240.0, 110.0)), "{wrap}"); + assert_eq!(paths(&s).len(), 1, "{wrap}: no new path"); + } +} diff --git a/crates/tools/src/draw2.rs b/crates/tools/src/draw2.rs index 69117508..77731260 100644 --- a/crates/tools/src/draw2.rs +++ b/crates/tools/src/draw2.rs @@ -103,6 +103,29 @@ pub(crate) fn editable_paths<'a>(cx: &'a ToolContext) -> impl Iterator