diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index d2d32e2b4..d03af76b7 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -1320,12 +1320,7 @@ impl DesignApp { #[cfg(not(target_arch = "wasm32"))] { let (dropped, pointer) = ctx.input(|i| (i.raw.dropped_files.clone(), i.pointer.hover_pos())); - for f in dropped { - let p = f.path().to_string_lossy().to_string(); - if !p.is_empty() { - let _ = self.open_dropped_at(&p, pointer); - } - } + self.open_dropped_files(&dropped, pointer); } self.handle_quit(ctx); } @@ -1482,6 +1477,18 @@ impl DesignApp { self.open_dropped_at(path, None) } + /// Open or place each file dropped on the main window or on a floating panel's own window. + /// `pointer` is in the main window's coordinates. + #[cfg(not(target_arch = "wasm32"))] + pub(crate) fn open_dropped_files(&mut self, files: &[egui::DroppedFileHandle], pointer: Option) { + for f in files { + let p = f.path().to_string_lossy().to_string(); + if !p.is_empty() { + let _ = self.open_dropped_at(&p, pointer); + } + } + } + /// [`Self::open_dropped`] at the pointer: a graphic's top left lands where the file was /// dropped on the canvas, text goes to a new frame on the document page there. pub fn open_dropped_at(&mut self, path: &str, pointer: Option) -> Result { diff --git a/crates/ui-egui/src/panel_docking.rs b/crates/ui-egui/src/panel_docking.rs index 8b69821c0..52dd988ff 100644 --- a/crates/ui-egui/src/panel_docking.rs +++ b/crates/ui-egui/src/panel_docking.rs @@ -1086,10 +1086,21 @@ pub(crate) fn show(app: &mut DesignApp, ui: &mut egui::Ui) { if let Some(panel) = focused.filter(|panel| panel == CANVAS || layout_of(app).contains(panel)) { app.ui.docking_focused = Some(panel); } + #[cfg(not(target_arch = "wasm32"))] + open_window_drops(app, &output.window_files); // Keys pressed in a floating panel's own OS window run the main window's shortcuts. crate::menus::forwarded_shortcuts(app, ui.ctx(), output.forwarded_events, output.forwarded_modifiers.unwrap_or_default()); } +/// Files dropped on a floating panel's own OS window open or place as drops on the main window +/// do. The pointer there is in that window's coordinates, so a placed file gets no canvas point. +#[cfg(not(target_arch = "wasm32"))] +fn open_window_drops(app: &mut DesignApp, windows: &[craft_ui::docking::WindowFiles]) { + for window in windows { + app.open_dropped_files(&window.dropped, None); + } +} + /// Whether a modal (a dialog, About or Quick Apply) is open. Floating panels in their own OS /// windows take no input then, as the main window behind the modal doesn't. pub(crate) fn modal_open(app: &DesignApp) -> bool { diff --git a/crates/ui-egui/src/panel_docking_tests.rs b/crates/ui-egui/src/panel_docking_tests.rs index bd7ba82fd..b3a1ee11b 100644 --- a/crates/ui-egui/src/panel_docking_tests.rs +++ b/crates/ui-egui/src/panel_docking_tests.rs @@ -1242,3 +1242,36 @@ fn dialogs_about_and_quick_apply_block_native_floating_windows() { app.ui.palette = None; assert!(!modal_open(&app)); } + +#[cfg(not(target_arch = "wasm32"))] +#[test] +fn a_document_dropped_on_a_floating_panel_window_opens() { + #[derive(Debug)] + struct Dropped(std::path::PathBuf); + impl egui::DroppedFile for Dropped { + fn path(&self) -> &std::path::Path { + &self.0 + } + fn bytes(&self) -> Result, String> { + std::fs::read(&self.0).map_err(|e| e.to_string()) + } + } + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("dropped.designcraft"); + let mut saved = app(); + saved.run("file.new", json!({})).unwrap(); + saved.run("file.save", json!({"path": path})).unwrap(); + + let mut app = app(); + assert!(app.session.documents().is_empty()); + let files = craft_ui::docking::WindowFiles { + window: 1, + viewport: egui::ViewportId::from_hash_of("float"), + panel: "layers".to_owned(), + pos: None, + hovered: Vec::new(), + dropped: vec![std::sync::Arc::new(Dropped(path.clone())) as egui::DroppedFileHandle], + }; + open_window_drops(&mut app, &[files]); + assert_eq!(app.session.documents().len(), 1, "the dropped document opens"); +}