mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-14 19:26:56 -04:00
feat: marquee selection selects unlocked ruler guides
This commit is contained in:
1 parent
e54fe9c31e
commit
8796aa9d85
4 files changed
+127
-4
No files matched your search
@@ -730,7 +730,22 @@ impl Tool for DirectSelectionTool {
|
||||
}
|
||||
sel.retain(|(id, _)| cx.doc.is_editable(*id));
|
||||
let items: Vec<Value> = sel.iter().map(|(id, v)| json!({"id": id.0, "anchors": anchors_json(v)})).collect();
|
||||
vec![Action::Exec("select.anchorsMany".into(), json!({"items": items, "mode": if toggle { "toggle" } else { "set" }}))]
|
||||
let guides = crate::rulerguide::guides_in_rect(cx, r);
|
||||
let mut out = vec![];
|
||||
if toggle {
|
||||
if !items.is_empty() {
|
||||
out.push(Action::Exec("select.anchorsMany".into(), json!({"items": items, "mode": "toggle"})));
|
||||
}
|
||||
if !guides.is_empty() {
|
||||
out.push(Action::Exec("guide.select".into(), json!({ "indexes": guides, "toggle": true })));
|
||||
}
|
||||
} else {
|
||||
out.push(Action::Exec("select.anchorsMany".into(), json!({"items": items, "mode": "set"})));
|
||||
if !guides.is_empty() {
|
||||
out.push(Action::Exec("guide.select".into(), json!({ "indexes": guides, "toggle": true })));
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
_ => vec![],
|
||||
}
|
||||
@@ -1100,6 +1115,26 @@ mod tests {
|
||||
assert_eq!(a, vec![Action::Exec("select.anchorsMany".into(), json!({"items": [{"id": id.0, "anchors": [[0, 1]]}], "mode": "set"}))]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marquee_selects_ruler_guides() {
|
||||
let (mut d, id) = doc_with_rect();
|
||||
d.guides.push(vectorcraft_doc::Guide::new(false, 80.0));
|
||||
let s = Selection::default();
|
||||
let p = paint();
|
||||
let cx = cx(&d, &s, &p);
|
||||
let mut t = DirectSelectionTool::new(false);
|
||||
t.pointer(&cx, &PointerEvent::new(PointerKind::Down, 150.0, 50.0));
|
||||
t.pointer(&cx, &PointerEvent::new(PointerKind::Drag, 250.0, 150.0));
|
||||
let a = t.pointer(&cx, &PointerEvent::new(PointerKind::Up, 250.0, 150.0));
|
||||
assert_eq!(
|
||||
a,
|
||||
vec![
|
||||
Action::Exec("select.anchorsMany".into(), json!({"items": [{"id": id.0, "anchors": [[0, 1]]}], "mode": "set"})),
|
||||
Action::Exec("guide.select".into(), json!({"indexes": [0], "toggle": true})),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// A path inside an inner group, a sibling path outside it, and the groups' ids.
|
||||
/// The inside path is the rectangle at (100, 100); the outside one at (300, 300).
|
||||
fn nested_group_doc() -> (vectorcraft_doc::Document, NodeId, NodeId, NodeId, NodeId) {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
//! or locked layer, can't be picked.
|
||||
|
||||
use serde_json::json;
|
||||
use vectorcraft_geom::Point;
|
||||
use vectorcraft_geom::{Point, Rect};
|
||||
|
||||
use crate::guides::Targets;
|
||||
use crate::{Action, Cursor, Overlay, PointerEvent, PointerKind, ToolContext};
|
||||
@@ -32,6 +32,27 @@ pub fn guide_at(cx: &ToolContext, p: Point) -> Option<usize> {
|
||||
.map(|(i, _)| i)
|
||||
}
|
||||
|
||||
/// The editable ruler guides intersecting marquee rectangle `r`.
|
||||
pub fn guides_in_rect(cx: &ToolContext, r: Rect) -> Vec<usize> {
|
||||
if !cx.guides {
|
||||
return vec![];
|
||||
}
|
||||
cx.doc
|
||||
.guides
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, g)| cx.doc.guide_editable(g))
|
||||
.filter(|(_, g)| {
|
||||
if g.vertical {
|
||||
r.min_x() <= g.pos && g.pos <= r.max_x() && cx.doc.guide_span(g).is_none_or(|(y0, y1)| r.max_y() >= y0 && r.min_y() <= y1)
|
||||
} else {
|
||||
r.min_y() <= g.pos && g.pos <= r.max_y() && cx.doc.guide_span(g).is_none_or(|(x0, x1)| r.max_x() >= x0 && r.min_x() <= x1)
|
||||
}
|
||||
})
|
||||
.map(|(i, _)| i)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// The pointer for a guide: it moves sideways if vertical, up and down if horizontal.
|
||||
fn guide_cursor(vertical: bool) -> Cursor {
|
||||
if vertical { Cursor::ResizeH } else { Cursor::ResizeV }
|
||||
|
||||
@@ -459,7 +459,22 @@ impl Tool for SelectionTool {
|
||||
return if toggle { vec![] } else { vec![Action::Exec("select.none".into(), json!({}))] };
|
||||
}
|
||||
let ids: Vec<NodeId> = marquee(cx.doc, r, cx.isolation, false);
|
||||
vec![Action::Exec(if toggle { "select.toggle" } else { "select.set" }.into(), json!({ "ids": json_ids(&ids) }))]
|
||||
let guides = crate::rulerguide::guides_in_rect(cx, r);
|
||||
let mut out = vec![];
|
||||
if toggle {
|
||||
if !ids.is_empty() {
|
||||
out.push(Action::Exec("select.toggle".into(), json!({ "ids": json_ids(&ids) })));
|
||||
}
|
||||
if !guides.is_empty() {
|
||||
out.push(Action::Exec("guide.select".into(), json!({ "indexes": guides, "toggle": true })));
|
||||
}
|
||||
} else {
|
||||
out.push(Action::Exec("select.set".into(), json!({ "ids": json_ids(&ids) })));
|
||||
if !guides.is_empty() {
|
||||
out.push(Action::Exec("guide.select".into(), json!({ "indexes": guides, "toggle": true })));
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
_ => vec![],
|
||||
}
|
||||
@@ -993,6 +1008,58 @@ mod tests {
|
||||
assert!(t.pointer(&cx, &ev(PointerKind::Up, 50.0, 50.0).with_mods(shift)).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marquee_selects_ruler_guides() {
|
||||
let (mut d, id) = doc_with_rect();
|
||||
d.guides.push(vectorcraft_doc::Guide::new(false, 80.0));
|
||||
let s = Selection::default();
|
||||
let p = paint();
|
||||
let cx = cx(&d, &s, &p);
|
||||
let mut t = SelectionTool::default();
|
||||
t.pointer(&cx, &ev(PointerKind::Down, 50.0, 50.0));
|
||||
t.pointer(&cx, &ev(PointerKind::Drag, 120.0, 120.0));
|
||||
let a = t.pointer(&cx, &ev(PointerKind::Up, 120.0, 120.0));
|
||||
assert_eq!(
|
||||
a,
|
||||
vec![
|
||||
Action::Exec("select.set".into(), json!({"ids": [id.0]})),
|
||||
Action::Exec("guide.select".into(), json!({"indexes": [0], "toggle": true})),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marquee_selects_only_guide() {
|
||||
let (mut d, _) = doc_with_rect();
|
||||
d.guides.push(vectorcraft_doc::Guide::new(true, 20.0));
|
||||
let s = Selection::default();
|
||||
let p = paint();
|
||||
let cx = cx(&d, &s, &p);
|
||||
let mut t = SelectionTool::default();
|
||||
t.pointer(&cx, &ev(PointerKind::Down, 10.0, 10.0));
|
||||
t.pointer(&cx, &ev(PointerKind::Drag, 30.0, 30.0));
|
||||
let a = t.pointer(&cx, &ev(PointerKind::Up, 30.0, 30.0));
|
||||
assert_eq!(
|
||||
a,
|
||||
vec![Action::Exec("select.set".into(), json!({"ids": []})), Action::Exec("guide.select".into(), json!({"indexes": [0], "toggle": true})),]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marquee_ignores_locked_guides() {
|
||||
let (mut d, id) = doc_with_rect();
|
||||
d.guides.push(vectorcraft_doc::Guide::new(false, 80.0));
|
||||
let s = Selection::default();
|
||||
let p = paint();
|
||||
let mut cx_locked = cx(&d, &s, &p);
|
||||
cx_locked.guides = false;
|
||||
let mut t = SelectionTool::default();
|
||||
t.pointer(&cx_locked, &ev(PointerKind::Down, 50.0, 50.0));
|
||||
t.pointer(&cx_locked, &ev(PointerKind::Drag, 120.0, 120.0));
|
||||
let a = t.pointer(&cx_locked, &ev(PointerKind::Up, 120.0, 120.0));
|
||||
assert_eq!(a, vec![Action::Exec("select.set".into(), json!({"ids": [id.0]}))]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn handle_drag_scales() {
|
||||
let (d, id) = doc_with_rect();
|
||||
|
||||
Reference in new issue
Block a user