mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-29 12:17:14 -06:00
Shaper: a press checks every selected original shape and keeps objects in front of its group
While an opacity mask or a pattern is edited, a press on a selected Shaper original shape now moves it when it is not the first selected object or shares the selection with shapes of another Shaper Group. An object in front of the Shaper Group at the pointer is selected instead.
This commit is contained in:
@@ -465,15 +465,28 @@ pub(crate) fn hold_point(cx: &ToolContext, h: &Hit, p: Point) -> Point {
|
||||
/// While an opacity mask or a pattern is edited, a Shaper double-click selects a Shaper Group's
|
||||
/// original shape without isolating the group's original shapes. A press at `p` on a selected
|
||||
/// original shape holds it for a move: the hit inside its Shaper Group's original shapes, and that
|
||||
/// container. None otherwise.
|
||||
/// container. Each selected original shape's Shaper Group is checked, front to back, and an object
|
||||
/// in front of that group at `p` keeps the press. None otherwise.
|
||||
fn selected_source_hit(cx: &ToolContext, p: Point) -> Option<(Hit, NodeId)> {
|
||||
if cx.doc.mask_edit.is_none() && cx.doc.pattern_edit.is_none() {
|
||||
return None;
|
||||
}
|
||||
let first = *cx.selection.objects.first()?;
|
||||
let sources = cx.doc.parent_of(first).filter(|g| cx.doc.node(*g).is_some_and(|n| n.name.as_deref() == Some(vectorcraft_doc::shaper::SOURCES)))?;
|
||||
let h = hit_test(cx.doc, p, vectorcraft_doc::hit::HitOptions { scope: Some(sources), ..cx.selection_hit_options() })?;
|
||||
cx.selection.contains(h.top_object(Some(sources))).then_some((h, sources))
|
||||
let is_sources = |g: &NodeId| cx.doc.node(*g).is_some_and(|n| n.name.as_deref() == Some(vectorcraft_doc::shaper::SOURCES));
|
||||
let mut containers = cx.doc.paint_order(cx.selection.objects.iter().filter_map(|id| cx.doc.parent_of(*id)).filter(is_sources));
|
||||
containers.dedup();
|
||||
let front = hit_test(cx.doc, p, cx.selection_hit_options());
|
||||
containers.into_iter().rev().find_map(|sources| {
|
||||
let group = cx.doc.parent_of(sources)?;
|
||||
if let Some(f) = &front
|
||||
&& f.leaf != group
|
||||
&& !f.ancestry.contains(&group)
|
||||
&& cx.doc.paint_order([group, f.leaf]).last() == Some(&f.leaf)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let h = hit_test(cx.doc, p, vectorcraft_doc::hit::HitOptions { scope: Some(sources), ..cx.selection_hit_options() })?;
|
||||
cx.selection.contains(h.top_object(Some(sources))).then_some((h, sources))
|
||||
})
|
||||
}
|
||||
|
||||
impl SelectionTool {
|
||||
|
||||
@@ -31,6 +31,35 @@ fn a_press_on_a_selected_shaper_source_moves_it_while_a_mask_is_edited() {
|
||||
assert!(down.iter().any(|a| matches!(a, Action::Exec(c, v) if c == "select.set" && v["ids"] == json!([g.0]))), "{down:?}");
|
||||
}
|
||||
|
||||
/// While an opacity mask is edited, a selected Shaper original shape that is not the first selected
|
||||
/// object still moves on a press, and an object in front of the Shaper Group keeps the press.
|
||||
#[test]
|
||||
fn a_selected_shaper_source_moves_after_other_objects_and_behind_front_art() {
|
||||
use vectorcraft_doc::{Appearance, Node};
|
||||
let (mut d, _, sources, _) = doc_with_shaper_group();
|
||||
let rect = d.layers[0].children().unwrap()[0].id;
|
||||
let layer = d.layers[0].id;
|
||||
d.mask_edit = Some(vectorcraft_doc::MaskEdit { object: rect, layer });
|
||||
let ids: Vec<NodeId> = d.node(sources).unwrap().children().unwrap().iter().map(|n| n.id).collect();
|
||||
let (mut s, p) = (Selection::default(), paint());
|
||||
s.set([rect, ids[1]]);
|
||||
let c = ToolContext { isolation: Some(layer), ..cx(&d, &s, &p) };
|
||||
let mut t = SelectionTool::default();
|
||||
assert_eq!(t.cursor(&c, Point::new(125.0, 350.0), Mods::default()), Cursor::Move);
|
||||
let down = t.pointer(&c, &ev(PointerKind::Down, 125.0, 350.0));
|
||||
assert!(!down.iter().any(|a| matches!(a, Action::Exec(c, _) if c == "select.set")), "{down:?}");
|
||||
let drag = t.pointer(&c, &ev(PointerKind::Drag, 145.0, 350.0));
|
||||
assert!(drag.iter().any(|a| matches!(a, Action::Preview(c, _) if c == "object.transform")), "{drag:?}");
|
||||
let front = d.alloc_id();
|
||||
let square = vectorcraft_geom::shapes::rectangle(Rect::new(110.0, 340.0, 140.0, 360.0));
|
||||
d.insert(Some(layer), 2, Node::path(front, square, Appearance::default_art())).unwrap();
|
||||
s.set([ids[1]]);
|
||||
let c = ToolContext { isolation: Some(layer), ..cx(&d, &s, &p) };
|
||||
assert_ne!(SelectionTool::default().cursor(&c, Point::new(125.0, 350.0), Mods::default()), Cursor::Move);
|
||||
let down = SelectionTool::default().pointer(&c, &ev(PointerKind::Down, 125.0, 350.0));
|
||||
assert!(down.iter().any(|a| matches!(a, Action::Exec(c, v) if c == "select.set" && v["ids"] == json!([front.0]))), "{down:?}");
|
||||
}
|
||||
|
||||
fn ev(kind: PointerKind, x: f64, y: f64) -> PointerEvent {
|
||||
PointerEvent::new(kind, x, y)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user