mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-30 04:37:29 -06:00
Merge pull request #4645 from mockydevs/fix/direct-select-drag-to-panels-4233
Fix #4233: Direct Selection drags art onto Asset Export and the other panels
This commit is contained in:
@@ -172,12 +172,18 @@ pub(super) fn drop_press(app: &mut VectorcraftApp, ui: &Ui, view: vectorcraft_en
|
||||
/// the selected art ([`widgets::PanelDrag::Art`]): the art waits where it last moved on the
|
||||
/// canvas, and a panel it is released on takes it (the Graphic Styles panel makes a style of it).
|
||||
/// Back on the canvas, [`Drag::Art`] takes the move up again where the pointer is (#2826).
|
||||
/// The Direct and Group Selection tools hand over art they move whole too (#4233); a move of
|
||||
/// anchor points or segments stays a reshape.
|
||||
pub(super) fn drag_art_out(app: &VectorcraftApp, ui: &Ui, resp: &egui::Response, p: Pos2) -> bool {
|
||||
let Some(st) = app.session.active().filter(|_| app.session.tool_id() == "selection") else { return false };
|
||||
// The Selection tool's interaction while it moves (or Alt-copies) the selection.
|
||||
let tool = app.session.tool_id();
|
||||
let Some(st) = app.session.active().filter(|_| matches!(tool, "selection" | "directSelection" | "groupSelection")) else { return false };
|
||||
// The tool's interaction while it moves (or Alt-copies) the selection.
|
||||
if !st.interaction.as_ref().is_some_and(|i| i.label == "Move" || i.label == "Copy") {
|
||||
return false;
|
||||
}
|
||||
if tool != "selection" && st.selection.objects.iter().any(|id| st.selection.partial(*id).is_some() || st.selection.segments.contains_key(id)) {
|
||||
return false;
|
||||
}
|
||||
// A floating panel in an OS window of its own takes the art too: craft-ui hands that window
|
||||
// the pointer while it is over it, and this window sees the pointer go.
|
||||
craft_ui::docking::route_app_drag(ui.ctx(), crate::panel_docking::area_id());
|
||||
|
||||
@@ -5298,6 +5298,71 @@ fn art_moved_off_the_canvas_waits_and_drops_on_the_panel() {
|
||||
assert_eq!(st.history.undo.len(), undo);
|
||||
}
|
||||
|
||||
/// #4233: the Direct and Group Selection tools hand art they move whole to the panels too (Asset
|
||||
/// Export, Graphic Styles…), as the Selection tool does; the move waits and nothing is recorded.
|
||||
#[test]
|
||||
fn art_moved_whole_off_the_canvas_with_direct_selection_drops_on_the_panel() {
|
||||
for tool in ["directSelection", "groupSelection"] {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.session.execute("file.new", &json!({"width": 400, "height": 300})).unwrap();
|
||||
let id = app.session.execute("shape.rectangle", &json!({"x": 50, "y": 50, "width": 100, "height": 100})).unwrap()["id"].as_u64().unwrap();
|
||||
let id = vectorcraft_doc::NodeId(id);
|
||||
app.session.execute("select.none", &json!({})).unwrap();
|
||||
app.select_tool(tool);
|
||||
let ctx = egui::Context::default();
|
||||
frame(&mut app, &ctx, vec![]);
|
||||
let xf = Xf::new(app.canvas_rect.unwrap(), app.view().unwrap());
|
||||
let before = app.session.active().unwrap().doc.clone();
|
||||
let undo = app.session.active().unwrap().history.undo.len();
|
||||
let button = |pos: Pos2, pressed: bool| egui::Event::PointerButton {
|
||||
pos,
|
||||
button: egui::PointerButton::Primary,
|
||||
pressed,
|
||||
modifiers: Default::default(),
|
||||
};
|
||||
// Pressed on the fill: the rectangle is selected whole.
|
||||
let start = xf.to_screen(Point::new(100.0, 100.0));
|
||||
frame(&mut app, &ctx, vec![egui::Event::PointerMoved(start), button(start, true)]);
|
||||
assert_eq!(app.session.active().unwrap().selection.whole(), [id], "{tool}: selected whole");
|
||||
frame(&mut app, &ctx, vec![egui::Event::PointerMoved(start + vec2(40.0, 0.0))]);
|
||||
assert!(app.session.active().unwrap().interaction.is_some(), "{tool}: the move is live");
|
||||
frame(&mut app, &ctx, vec![egui::Event::PointerMoved(pos2(850.0, 100.0))]);
|
||||
assert!(app.session.active().unwrap().interaction.is_some(), "{tool}: the move waits, not cancels");
|
||||
assert_eq!(egui::DragAndDrop::payload::<widgets::PanelDrag>(&ctx).as_deref(), Some(&widgets::PanelDrag::Art(vec![id])), "{tool}");
|
||||
frame(&mut app, &ctx, vec![button(pos2(850.0, 100.0), false)]);
|
||||
let st = app.session.active().unwrap();
|
||||
assert!(st.interaction.is_none(), "{tool}");
|
||||
assert_eq!(*st.doc, *before, "{tool}: the move goes back");
|
||||
assert_eq!(st.history.undo.len(), undo, "{tool}: nothing recorded");
|
||||
}
|
||||
}
|
||||
|
||||
/// An anchor point the Direct Selection tool drags off the canvas isn't art for the panels: the
|
||||
/// reshape goes on and no art drag starts (#4233).
|
||||
#[test]
|
||||
fn an_anchor_dragged_off_the_canvas_is_not_handed_to_the_panels() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.session.execute("file.new", &json!({"width": 400, "height": 300})).unwrap();
|
||||
let id = app.session.execute("shape.rectangle", &json!({"x": 50, "y": 50, "width": 100, "height": 100})).unwrap()["id"].as_u64().unwrap();
|
||||
app.select_tool("directSelection");
|
||||
app.session.execute("select.anchors", &json!({"id": id, "anchors": [[0, 0]]})).unwrap();
|
||||
let ctx = egui::Context::default();
|
||||
frame(&mut app, &ctx, vec![]);
|
||||
let xf = Xf::new(app.canvas_rect.unwrap(), app.view().unwrap());
|
||||
let button =
|
||||
|pos: Pos2, pressed: bool| egui::Event::PointerButton { pos, button: egui::PointerButton::Primary, pressed, modifiers: Default::default() };
|
||||
let corner = xf.to_screen(Point::new(50.0, 50.0));
|
||||
frame(&mut app, &ctx, vec![egui::Event::PointerMoved(corner), button(corner, true)]);
|
||||
frame(&mut app, &ctx, vec![egui::Event::PointerMoved(corner + vec2(20.0, 0.0))]);
|
||||
assert!(app.session.active().unwrap().selection.partial(vectorcraft_doc::NodeId(id)).is_some(), "an anchor is picked");
|
||||
frame(&mut app, &ctx, vec![egui::Event::PointerMoved(pos2(850.0, 100.0))]);
|
||||
assert!(
|
||||
!matches!(egui::DragAndDrop::payload::<widgets::PanelDrag>(&ctx).as_deref(), Some(widgets::PanelDrag::Art(_))),
|
||||
"no art drag for an anchor"
|
||||
);
|
||||
frame(&mut app, &ctx, vec![button(pos2(850.0, 100.0), false)]);
|
||||
}
|
||||
|
||||
/// #2826: a move taken over a panel waits where it left the canvas, and goes on with the
|
||||
/// pointer when it comes back — it isn't dropped or put back.
|
||||
#[test]
|
||||
|
||||
@@ -363,6 +363,7 @@ separate. Scores and hours unchanged. [Behavior and command details](development
|
||||
|---|---|---|
|
||||
| 2026-10-30 | minor | Character panel menu › Show Snap to Glyph Options shows the Snap to Glyph section (Baseline, x-height, Glyph Bounds, Proximity Guides, Angular Guides, Anchor Point; `view.snapToGlyph.options`) (#4232); scores and estimates unchanged |
|
||||
| 2026-10-30 | minor | Align panel menu › Align to Glyph Bounds › Point Text and Area Text align and distribute type by the ink of its glyphs (#4231); the Character and Properties panels' controls for it are not done; scores and estimates unchanged |
|
||||
| 2026-10-30 | minor | Art moved whole with the Direct Selection or Group Selection tool and dragged off the canvas goes to the panels (Asset Export, Graphic Styles, Swatches, Symbols) as with the Selection tool; anchor points and segments dragged off stay a reshape (#4233). Tests in `crates/ui-egui/src/canvas/tests.rs`; scores and estimates unchanged |
|
||||
| 2026-10-30 | minor | Blob Brush and Eraser Tool Options: Size follows the pen pressure, Fixed or Pressure with a Variation (#4590, #3961); scores and estimates unchanged |
|
||||
| 2026-10-30 | minor | Blob Brush Tool Options: Size follows the pen pressure, Fixed or Pressure with a Variation (#4590); scores and estimates unchanged |
|
||||
| 2026-10-29 | minor | Pathfinder: live compound shapes (#4522): the panel menu's Make, Release and Expand Compound Shape, Alt-clicking a Shape Mode, a selected member's Shape Mode and the Expand button act; scores and estimates unchanged |
|
||||
|
||||
Reference in New Issue
Block a user