From a5961f264b7a96ce5608db8c9d1052056754204d Mon Sep 17 00:00:00 2001 From: HassanKhan Date: Tue, 27 Oct 2026 15:55:12 +0500 Subject: [PATCH] Fix #4237: Add Anchor Point selects the path it adds to An anchor added to a path that wasn't selected now selects that path, the new anchor direct-selected, so the point shows and can be edited at once instead of only after a Direct Selection click. Undo restores the old selection; a path already selected (the Pen's Auto Add) keeps it. Co-Authored-By: Claude Opus 5.5 --- crates/engine/src/cmd/path.rs | 12 ++++++++++-- crates/engine/src/tests/draw2.rs | 24 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/crates/engine/src/cmd/path.rs b/crates/engine/src/cmd/path.rs index 9230cfd3f..af57b06ec 100644 --- a/crates/engine/src/cmd/path.rs +++ b/crates/engine/src/cmd/path.rs @@ -1251,13 +1251,21 @@ fn insert_anchor(s: &mut Session, p: &Value) -> Result { let si = usize_opt(p, "subpath", "path.insertAnchor")?.unwrap_or(0); let seg = p.get("segment").and_then(Value::as_u64).map(index).ok_or_else(|| bad("path.insertAnchor", "missing segment"))?; let t = number_or(p, "t", 0.5, "path.insertAnchor")?.clamp(0.0, 1.0); - let idx = s.edit("Add Anchor Point", |d, _| { + let idx = s.edit("Add Anchor Point", |d, sel| { let path = path_mut(d, id)?; let sp = path.subpaths.get_mut(si).ok_or_else(|| EngineError::Other("no such subpath".into()))?; if seg >= sp.segment_count() { return Err(EngineError::Other("no such segment".into())); } - Ok(sp.insert_anchor(seg, t)) + let idx = sp.insert_anchor(seg, t); + // A path that wasn't selected becomes the selection, its new anchor direct-selected, so + // the point added shows and can be edited at once (#4237). + if !sel.contains(id) { + sel.clear(); + sel.add(id); + sel.anchors.entry(id).or_default().insert((si, idx)); + } + Ok(idx) })?; Ok(json!({ "anchor": idx })) } diff --git a/crates/engine/src/tests/draw2.rs b/crates/engine/src/tests/draw2.rs index 6a4537666..d40aaff8b 100644 --- a/crates/engine/src/tests/draw2.rs +++ b/crates/engine/src/tests/draw2.rs @@ -1799,6 +1799,30 @@ fn remove_anchor_points_keeps_the_paths_closed() { assert_eq!(spec.menu, &["Object", "Path"][..]); } +#[test] +fn add_anchor_point_selects_the_path_it_was_added_to() { + // #4237: an anchor added to a path that wasn't selected selects that path, the new anchor + // direct-selected, so it shows at once; undo puts the old selection back. A path already + // selected (the Pen's Auto Add) keeps its selection. + let mut s = session(); + let other = rect(&mut s, 300.0, 0.0, 50.0, 50.0); + let line = |s: &mut Session| { + NodeId(s.execute("path.create", &json!({"anchors": [{"x": 0, "y": 0}, {"x": 100, "y": 0}]})).unwrap()["id"].as_u64().unwrap()) + }; + let id = line(&mut s); + s.execute("select.set", &json!({"ids": [other.0]})).unwrap(); + s.execute("path.insertAnchor", &json!({"id": id.0, "segment": 0, "t": 0.5})).unwrap(); + assert_eq!(s.doc().unwrap().selection.objects, [id]); + assert_eq!(selected_anchors(&s, id), [(0, 1)]); + s.execute("edit.undo", &json!({})).unwrap(); + assert_eq!(s.doc().unwrap().selection.objects, [other]); + let kept = line(&mut s); + s.execute("select.set", &json!({"ids": [kept.0]})).unwrap(); + s.execute("path.insertAnchor", &json!({"id": kept.0, "segment": 0, "t": 0.5})).unwrap(); + assert_eq!(s.doc().unwrap().selection.objects, [kept]); + assert!(selected_anchors(&s, kept).is_empty(), "a path selected as a whole stays so"); +} + fn selected_anchors(s: &Session, id: NodeId) -> Vec<(usize, usize)> { s.doc().unwrap().selection.anchors.get(&id).map(|set| set.iter().copied().collect()).unwrap_or_default() }