diff --git a/crates/ui-egui/src/canvas/drag.rs b/crates/ui-egui/src/canvas/drag.rs index b59be46a15..930daf8fd4 100644 --- a/crates/ui-egui/src/canvas/drag.rs +++ b/crates/ui-egui/src/canvas/drag.rs @@ -413,6 +413,10 @@ pub fn apply_requests(app: &mut VectorcraftApp, r: vectorcraft_engine::Result { let index = doc.and_then(|uid| app.session.documents().iter().position(|d| d.uid == uid)); pressed_with(app, index, pos, tool); + // The selection tool Cmd lent has the press now, so its drag can carry art to another tab. + if let Some(ctx) = app.ui_ctx.clone() { + crate::document_drag::begin(app, &ctx, pos); + } } } } diff --git a/crates/ui-egui/src/tests/document_drag.rs b/crates/ui-egui/src/tests/document_drag.rs index b5727eaada..f01314a667 100644 --- a/crates/ui-egui/src/tests/document_drag.rs +++ b/crates/ui-egui/src/tests/document_drag.rs @@ -4,6 +4,7 @@ use egui::{Event, PointerButton, Pos2}; use serde_json::json; use vectorcraft_engine::Session; use vectorcraft_geom::Point; +use vectorcraft_tools::{PointerEvent, PointerKind}; use crate::{VectorcraftApp, canvas, chrome}; @@ -142,6 +143,57 @@ fn drag_fixture_modifiers(modifiers: egui::Modifiers) -> (VectorcraftApp, egui:: (app, ctx, grab, tabs) } +/// A Cmd-press held while a Shape Builder merge is computed reaches the lent Selection tool once +/// the merge is applied, and its drag carries the artwork over another tab. +#[test] +fn a_cmd_drag_held_during_a_merge_activates_another_tab() { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.run("file.new", json!({"width":800,"height":200,"name":"Source"})).unwrap(); + for k in 0..40 { + app.run("shape.rectangle", json!({"x": k * 10, "y": 0, "width": 20, "height": 20})).unwrap(); + } + app.run("select.all", json!({})).unwrap(); + app.run("file.new", json!({"width":800,"height":200,"name":"Target"})).unwrap(); + app.session.set_active(0); + app.select_tool("shapeBuilder"); + app.ui.task_bar = false; + app.ui.view.rulers = false; + let ctx = egui::Context::default(); + crate::theme::install_fonts(&ctx); + app.ui_ctx = Some(ctx.clone()); + frame(&mut app, &ctx, 0.0, vec![]); + let tabs = frame(&mut app, &ctx, 0.01, vec![]); + let view = app.view_info(); + app.session.pointer(&PointerEvent::new(PointerKind::Move, 15.0, 10.0), view).unwrap(); + app.session.tool_poll(view, true).unwrap(); + app.session.pointer(&PointerEvent::new(PointerKind::Down, 15.0, 10.0), view).unwrap(); + app.session.pointer(&PointerEvent::new(PointerKind::Up, 15.0, 10.0), view).unwrap(); + assert!(app.session.tool_editing(), "the click starts a merge on another thread"); + let grab = canvas::Xf::new(app.canvas_rect.unwrap(), app.view().unwrap()).to_screen(Point::new(355.0, 10.0)); + let modifiers = egui::Modifiers::COMMAND; + frame( + &mut app, + &ctx, + 0.05, + vec![ + Event::ModifiersChanged(modifiers), + Event::PointerMoved(grab), + Event::PointerButton { pos: grab, button: PointerButton::Primary, pressed: true, modifiers }, + ], + ); + assert_eq!(app.session.tool_id(), "shapeBuilder", "the press waits for the merge"); + let r = app.session.tool_poll(view, true); + canvas::apply_requests(&mut app, r); + frame(&mut app, &ctx, 0.08, vec![]); + assert_eq!(app.session.tool_id(), "selection", "the held press borrowed the Selection tool"); + frame(&mut app, &ctx, 0.1, vec![Event::PointerMoved(grab + egui::vec2(20.0, 10.0))]); + let tab = tabs[1].center(); + frame(&mut app, &ctx, 0.2, vec![Event::PointerMoved(tab)]); + frame(&mut app, &ctx, 0.25, vec![]); + frame(&mut app, &ctx, 0.8, vec![]); + assert_eq!(app.session.active_index(), Some(1), "holding the artwork over the tab activates it"); +} + /// Verify that leaving a tab restarts its dwell timer and hovering its close button never activates it. #[test] fn tab_hover_delay_restarts_after_leaving_and_close_button_does_not_activate() {