From c2c92b7d491b57d25d5fc12ab32833d78f4282fc Mon Sep 17 00:00:00 2001 From: Sven Lafebre Date: Sat, 7 Nov 2026 14:34:06 +0100 Subject: [PATCH] Fix #5188: selected anchors that are no corner keep Shift-click adding --- crates/tools/src/corners.rs | 6 ++++-- crates/tools/src/corners/tests.rs | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/crates/tools/src/corners.rs b/crates/tools/src/corners.rs index 0629c53c8..c60c6a748 100644 --- a/crates/tools/src/corners.rs +++ b/crates/tools/src/corners.rs @@ -284,8 +284,10 @@ impl CornerDrag { let lc = cx.doc.node(path.id).and_then(LiveCorners::of)?; // While every corner is selected (the path selected whole), a Shift-click picks // this corner alone, as the first click of "click one widget, then Shift-click the - // others" does: toggling it would deselect it and leave the rest (#5188). - let every = lc.all().is_subset(&lc.picked(cx.selection.partial(path.id))); + // others" does: toggling it would deselect it and leave the rest (#5188). Selected + // anchors that are no corner show every widget too, but select no corner: there a + // Shift-click adds the corner. + let every = cx.selection.partial(path.id).is_none_or(|anchors| lc.all().is_subset(&lc.corners_of(anchors))); Some(CornerClick { id: path.id, anchors: lc.anchors_of(&[corner].into()).into_iter().collect(), shift: ev.mods.shift && !every }) }) .flatten(); diff --git a/crates/tools/src/corners/tests.rs b/crates/tools/src/corners/tests.rs index 5cd783679..e555782d1 100644 --- a/crates/tools/src/corners/tests.rs +++ b/crates/tools/src/corners/tests.rs @@ -264,6 +264,29 @@ fn a_shift_click_on_a_widget_of_a_whole_path_picks_its_corner() { assert_eq!(click(&two, w[0]), pick(0, "toggle")); } +/// #5188: an anchor selected that is no corner (here half way along a side) shows every widget +/// but selects no corner, so a Shift-click on a widget adds its corner to that selection. +#[test] +fn a_shift_click_on_a_widget_adds_to_anchors_that_are_no_corner() { + let corners = [(100.0, 100.0), (150.0, 100.0), (200.0, 100.0), (200.0, 200.0), (100.0, 200.0)].map(|(x, y)| Point::new(x, y)); + let (d, id) = doc_with(PathData::single(vectorcraft_geom::SubPath::polyline(&corners, true)), None); + let p = paint(); + let mut s = selected(id); + s.anchors.insert(id, [(0, 1)].into()); + let cx = cx(&d, &s, &p); + let widgets = CornerWidgets::for_tool(&cx, true).unwrap(); + assert_eq!(widgets.corners().len(), 4, "no corner selected: every widget shows"); + let at = pts(&widgets)[0]; + let lc = LiveCorners::of(d.node(id).unwrap()).unwrap(); + let corner = *lc.all().iter().next().unwrap(); + let anchors: Vec<(usize, usize)> = lc.anchors_of(&[corner].into()).into_iter().collect(); + let shift = Mods { shift: true, ..Mods::default() }; + let mut t = crate::create("directSelection"); + t.pointer(&cx, &PointerEvent::new(PointerKind::Down, at.x, at.y).with_mods(shift)); + let a = t.pointer(&cx, &PointerEvent::new(PointerKind::Up, at.x, at.y).with_mods(shift)); + assert_eq!(a, vec![Action::Exec("select.anchors".into(), json!({"id": id.0, "anchors": anchors, "mode": "toggle"}))]); +} + #[test] fn direct_selected_corners_show_their_widgets_alone_and_round_alone() { let (d, id) = live_rect(0.0, Affine::translate((100.0, 100.0)));