mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-01 05:37:23 -06:00
Fix #4890: Alt with Direct Selection marquee-selects whole paths
This commit is contained in:
+20
-11
@@ -8,7 +8,9 @@
|
||||
//! Group Selection's clicks do), drag a direction handle to
|
||||
//! reshape (Shift keeps it at 45° steps round its anchor, Alt moves it alone; smart guides snap
|
||||
//! it), marquee to select anchors (Shift-drag toggles them: the selected ones inside are
|
||||
//! deselected, the others selected), drag a corner widget of any path (a star, a pen path) to round its corners
|
||||
//! deselected, the others selected; with Alt held at the press, as Group Selection for the drag,
|
||||
//! it selects every object it touches, whole), drag a corner widget of any path (a star, a pen
|
||||
//! path) to round its corners
|
||||
//! (the selected ones when anchors are selected; Alt-click cycles their kind, double-click opens
|
||||
//! the Corners dialog).
|
||||
//! Group Selection: click selects the leaf; each further click on it adds the next enclosing group;
|
||||
@@ -103,6 +105,9 @@ enum State {
|
||||
/// Group Selection, E pressed an odd number of times during the drag: the marquee selects
|
||||
/// only the objects that lie entirely inside it, whole.
|
||||
enclosed: bool,
|
||||
/// Direct Selection with Alt held at the press, which makes it the Group Selection tool
|
||||
/// for the drag: the marquee selects every object it touches, whole (#4890).
|
||||
whole: bool,
|
||||
},
|
||||
Corner(CornerDrag),
|
||||
/// Dragging a bracket of type on a path.
|
||||
@@ -756,7 +761,7 @@ impl Tool for DirectSelectionTool {
|
||||
if let Some(out) = self.press_hidden(cx, ev, p) {
|
||||
return out;
|
||||
}
|
||||
self.state = State::Marquee { start: p, cur: p, toggle: ev.mods.shift, enclosed: false };
|
||||
self.state = State::Marquee { start: p, cur: p, toggle: ev.mods.shift, enclosed: false, whole: ev.mods.alt };
|
||||
vec![]
|
||||
}
|
||||
(PointerKind::Drag, State::MoveAnchors { start, grab, began }) => self.drag_move_anchors(cx, ev, p, (start, grab, began)),
|
||||
@@ -839,8 +844,8 @@ impl Tool for DirectSelectionTool {
|
||||
self.state = State::Idle;
|
||||
self.mesh.release(cx).unwrap_or_default()
|
||||
}
|
||||
(PointerKind::Drag, State::Marquee { start, toggle, enclosed, .. }) => {
|
||||
self.state = State::Marquee { start, cur: p, toggle, enclosed };
|
||||
(PointerKind::Drag, State::Marquee { start, toggle, enclosed, whole, .. }) => {
|
||||
self.state = State::Marquee { start, cur: p, toggle, enclosed, whole };
|
||||
vec![]
|
||||
}
|
||||
(PointerKind::Drag, State::Corner(mut c)) => {
|
||||
@@ -875,7 +880,7 @@ impl Tool for DirectSelectionTool {
|
||||
self.guides.clear();
|
||||
vec![Action::Commit]
|
||||
}
|
||||
(PointerKind::Up, State::Marquee { start, toggle, enclosed, .. }) => self.release_marquee(cx, p, (start, toggle, enclosed)),
|
||||
(PointerKind::Up, State::Marquee { start, toggle, enclosed, whole, .. }) => self.release_marquee(cx, p, (start, toggle, enclosed, whole)),
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
@@ -913,6 +918,8 @@ impl Tool for DirectSelectionTool {
|
||||
match self.state {
|
||||
// Alt-dragging leaves a copy behind.
|
||||
State::MoveObject { began: true, .. } if m.alt => return Cursor::SelectionCopy,
|
||||
// An Alt-marquee is Group Selection's until it is released.
|
||||
State::Marquee { whole: true, .. } => return Cursor::GroupSelection,
|
||||
// Bending a segment keeps the cursor that offered it.
|
||||
State::Segment { began: true, .. } | State::SpineSegment { began: true, .. } => return Cursor::ReshapeSegment,
|
||||
// A drag shows the filled arrow, hollow while it snaps to a point or a guide.
|
||||
@@ -1102,7 +1109,7 @@ impl DirectSelectionTool {
|
||||
if let Some(out) = self.press_hidden(cx, ev, p) {
|
||||
return out;
|
||||
}
|
||||
self.state = State::Marquee { start: p, cur: p, toggle: ev.mods.shift, enclosed: false };
|
||||
self.state = State::Marquee { start: p, cur: p, toggle: ev.mods.shift, enclosed: false, whole: false };
|
||||
return vec![];
|
||||
};
|
||||
// A compound path's paths are picked one at a time, below the compound path (#2441).
|
||||
@@ -1381,7 +1388,7 @@ impl DirectSelectionTool {
|
||||
(false, None, None, None) => group.map(|g| Action::Exec("select.add".into(), json!({"ids": [g.0]}))).into_iter().collect(),
|
||||
}
|
||||
}
|
||||
fn release_marquee(&mut self, cx: &ToolContext, p: Point, (start, toggle, enclosed): (Point, bool, bool)) -> Vec<Action> {
|
||||
fn release_marquee(&mut self, cx: &ToolContext, p: Point, (start, toggle, enclosed, whole): (Point, bool, bool, bool)) -> Vec<Action> {
|
||||
self.state = State::Idle;
|
||||
let r = Rect::from_points(start, p);
|
||||
if r.width() < cx.tol(3.0) && r.height() < cx.tol(3.0) {
|
||||
@@ -1392,14 +1399,16 @@ impl DirectSelectionTool {
|
||||
return vec![Action::Exec("select.none".into(), json!({}))];
|
||||
}
|
||||
// With Group Selection's enclosed marquee, the tool selects the objects inside it
|
||||
// (members of groups too), each whole, as a click selects one.
|
||||
if enclosed {
|
||||
// (members of groups too), each whole, as a click selects one; with an Alt-marquee, every
|
||||
// object it touches, each whole.
|
||||
if enclosed || whole {
|
||||
let mut touched = vectorcraft_doc::hit::marquee(cx.doc, r, cx.isolation, true);
|
||||
let editable = cx.doc.editable();
|
||||
touched.retain(|id| editable(*id));
|
||||
let ids: Vec<Value> = crate::select::inside(cx, r, touched).into_iter().map(|id| json!(id.0)).collect();
|
||||
let picked = if enclosed { crate::select::inside(cx, r, touched) } else { touched };
|
||||
let ids: Vec<Value> = picked.into_iter().map(|id| json!(id.0)).collect();
|
||||
let art = Action::Exec(if toggle { "select.toggle" } else { "select.set" }.into(), json!({ "ids": ids }));
|
||||
return std::iter::once(art).chain(crate::rulerguide::marquee_select(cx, r, true)).collect();
|
||||
return std::iter::once(art).chain(crate::rulerguide::marquee_select(cx, r, enclosed)).collect();
|
||||
}
|
||||
// Anchors inside the rect, on editable paths inside isolation mode.
|
||||
let mut sel: Vec<(NodeId, Vec<AnchorRef>)> = vec![];
|
||||
|
||||
@@ -922,6 +922,27 @@ fn shift_marquee_toggles_anchors() {
|
||||
}
|
||||
}
|
||||
|
||||
/// #4890: Alt held at the press makes a Direct Selection marquee Group Selection's for the drag:
|
||||
/// it selects each path it touches whole (with Shift, toggles it), not the anchors inside it, and
|
||||
/// leaves the paths it misses; the Group Selection cursor stays until the release.
|
||||
#[test]
|
||||
fn an_alt_marquee_selects_the_paths_it_touches_whole() {
|
||||
let (d, a, _) = two_squares();
|
||||
let (s, p) = (Selection::default(), paint());
|
||||
let cx = cx(&d, &s, &p);
|
||||
let alt = Mods { alt: true, ..Default::default() };
|
||||
let alt_shift = Mods { shift: true, ..alt };
|
||||
for (mods, cmd) in [(alt, "select.set"), (alt_shift, "select.toggle")] {
|
||||
let mut t = DirectSelectionTool::new(false);
|
||||
// Round A's top-right corner only; B (300..400) is left out.
|
||||
t.pointer(&cx, &PointerEvent::new(PointerKind::Down, 150.0, 50.0).with_mods(mods));
|
||||
t.pointer(&cx, &PointerEvent::new(PointerKind::Drag, 250.0, 150.0).with_mods(mods));
|
||||
assert_eq!(t.cursor(&cx, Point::new(250.0, 150.0), mods), Cursor::GroupSelection, "{mods:?}");
|
||||
let up = t.pointer(&cx, &PointerEvent::new(PointerKind::Up, 250.0, 150.0).with_mods(mods));
|
||||
assert_eq!(up, vec![Action::Exec(cmd.into(), json!({"ids": [a.0]}))], "{mods:?}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Group Selection: E pressed during a marquee drag makes it select the objects entirely inside
|
||||
/// it whole, a group's member without its group. Direct Selection does not take E.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user