diff --git a/core/src/clipboard.rs b/core/src/clipboard.rs index c97fa6e09f..206e6e1e78 100644 --- a/core/src/clipboard.rs +++ b/core/src/clipboard.rs @@ -1,6 +1,6 @@ //! Access the clipboard. -use std::{any::Any, borrow::Cow, sync::Arc}; +use std::{any::Any, sync::Arc}; use dnd::{DndAction, DndDestinationRectangle, DndSurface}; use mime::{self, AllowedMimeTypes, AsMimeTypes, ClipboardStoreData}; @@ -165,3 +165,49 @@ pub enum DndSource { /// A surface is the source of the DnD operation. Surface(window::Id), } + +/// A list of DnD destination rectangles. +#[derive(Debug, Clone)] +pub struct DndDestinationRectangles { + /// The rectangle of the DnD destination. + rectangles: Vec, +} + +impl DndDestinationRectangles { + /// Creates a new [`DndDestinationRectangles`]. + pub fn new() -> Self { + Self { + rectangles: Vec::new(), + } + } + + /// Creates a new [`DndDestinationRectangles`] with the given capacity. + pub fn with_capacity(capacity: usize) -> Self { + Self { + rectangles: Vec::with_capacity(capacity), + } + } + + /// Pushes a new rectangle to the list of DnD destination rectangles. + pub fn push(&mut self, rectangle: DndDestinationRectangle) { + self.rectangles.push(rectangle); + } + + /// Appends the list of DnD destination rectangles to the current list. + pub fn append(&mut self, other: &mut Vec) { + self.rectangles.append(other); + } + + /// Returns the list of DnD destination rectangles. + /// This consumes the [`DndDestinationRectangles`]. + pub fn into_rectangles(mut self) -> Vec { + self.rectangles.reverse(); + self.rectangles + } +} + +impl AsRef<[DndDestinationRectangle]> for DndDestinationRectangles { + fn as_ref(&self) -> &[DndDestinationRectangle] { + &self.rectangles + } +} diff --git a/core/src/widget.rs b/core/src/widget.rs index 136eeae471..fdbadb97e2 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -167,4 +167,14 @@ where /// Sets the id of the widget /// This may be called while diffing the widget tree fn set_id(&mut self, _id: Id) {} + + /// Adds the drag destination rectangles of the widget. + /// Runs after the layout phase for each widget in the tree. + fn drag_destinations( + &self, + _state: &Tree, + _layout: Layout<'_>, + _dnd_rectangles: &mut crate::clipboard::DndDestinationRectangles, + ) { + } } diff --git a/runtime/src/user_interface.rs b/runtime/src/user_interface.rs index 5999c94db0..8bfb1e1dd5 100644 --- a/runtime/src/user_interface.rs +++ b/runtime/src/user_interface.rs @@ -1,4 +1,5 @@ //! Implement your own event loop to drive a user interface. +use iced_core::clipboard::DndDestinationRectangles; use iced_core::widget::{Operation, OperationOutputWrapper}; use crate::core::event::{self, Event}; @@ -625,6 +626,20 @@ where pub fn find(&self, id: &widget::Id) -> Option<&widget::Tree> { self.state.find(id) } + + /// Get the destination rectangles for the user interface. + pub fn dnd_rectangles( + &self, + prev_capacity: usize, + ) -> DndDestinationRectangles { + let ret = DndDestinationRectangles::with_capacity(prev_capacity); + self.root.as_widget().drag_destinations( + &self.state, + Layout::new(&self.base), + &mut ret.clone(), + ); + ret + } } /// Reusable data of a specific [`UserInterface`]. diff --git a/widget/src/column.rs b/widget/src/column.rs index f16caac998..560a04eb85 100644 --- a/widget/src/column.rs +++ b/widget/src/column.rs @@ -284,6 +284,23 @@ where }), ) } + + fn drag_destinations( + &self, + state: &Tree, + layout: Layout<'_>, + dnd_rectangles: &mut iced_style::core::clipboard::DndDestinationRectangles, + ) { + for ((e, layout), state) in self + .children + .iter() + .zip(layout.children()) + .zip(state.children.iter()) + { + e.as_widget() + .drag_destinations(state, layout, dnd_rectangles); + } + } } impl<'a, Message, Theme, Renderer> From> diff --git a/widget/src/container.rs b/widget/src/container.rs index fdd0389567..24e25396aa 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -309,6 +309,19 @@ where cursor, ) } + + fn drag_destinations( + &self, + state: &Tree, + layout: Layout<'_>, + dnd_rectangles: &mut iced_style::core::clipboard::DndDestinationRectangles, + ) { + self.content.as_widget().drag_destinations( + state, + layout, + dnd_rectangles, + ); + } } impl<'a, Message, Theme, Renderer> From> diff --git a/widget/src/mouse_area.rs b/widget/src/mouse_area.rs index 5b93d88eb1..1f040b740b 100644 --- a/widget/src/mouse_area.rs +++ b/widget/src/mouse_area.rs @@ -244,6 +244,21 @@ where renderer, ) } + + fn drag_destinations( + &self, + state: &Tree, + layout: Layout<'_>, + dnd_rectangles: &mut iced_style::core::clipboard::DndDestinationRectangles, + ) { + if let Some(state) = state.children.iter().next() { + self.content.as_widget().drag_destinations( + state, + layout, + dnd_rectangles, + ); + } + } } impl<'a, Message, Theme, Renderer> From> diff --git a/widget/src/row.rs b/widget/src/row.rs index 9970c047ac..0be2734580 100644 --- a/widget/src/row.rs +++ b/widget/src/row.rs @@ -273,6 +273,23 @@ where }), ) } + + fn drag_destinations( + &self, + state: &Tree, + layout: Layout<'_>, + dnd_rectangles: &mut iced_style::core::clipboard::DndDestinationRectangles, + ) { + for ((e, layout), state) in self + .children + .iter() + .zip(layout.children()) + .zip(state.children.iter()) + { + e.as_widget() + .drag_destinations(state, layout, dnd_rectangles); + } + } } impl<'a, Message, Theme, Renderer> From> diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index 5916e40c75..e20c95ba22 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -1,5 +1,6 @@ //! Navigate an endless amount of content with a scrollbar. use iced_runtime::core::widget::Id; +use iced_style::core::clipboard::DndDestinationRectangles; #[cfg(feature = "a11y")] use std::borrow::Cow; @@ -588,6 +589,36 @@ where } } } + + fn drag_destinations( + &self, + state: &Tree, + layout: Layout<'_>, + dnd_rectangles: &mut iced_style::core::clipboard::DndDestinationRectangles, + ) { + if let Some((c_layout, c_state)) = + layout.children().zip(state.children.iter()).next() + { + let mut my_dnd_rectangles = DndDestinationRectangles::new(); + self.content.as_widget().drag_destinations( + c_state, + layout, + &mut my_dnd_rectangles, + ); + let mut my_dnd_rectangles = my_dnd_rectangles.into_rectangles(); + + let bounds = layout.bounds(); + let content_bounds = c_layout.bounds(); + let state = state.state.downcast_ref::(); + for r in &mut my_dnd_rectangles { + let translation = + state.translation(self.direction, bounds, content_bounds); + r.rectangle.x -= translation.x as f64; + r.rectangle.y -= translation.y as f64; + } + dnd_rectangles.append(&mut my_dnd_rectangles); + } + } } impl<'a, Message, Theme, Renderer> diff --git a/winit/src/application.rs b/winit/src/application.rs index 8c2c0a61c5..583685a1e1 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -23,7 +23,6 @@ use crate::conversion; use crate::core; use crate::core::mouse; use crate::core::renderer; -use crate::core::renderer::Renderer; use crate::core::time::Instant; use crate::core::widget::operation; use crate::core::window; @@ -411,6 +410,8 @@ async fn run_instance( &mut debug, )); + let mut prev_dnd_rectangles_count = 0; + // Creates closure for handling the window drag resize state with winit. let mut drag_resize_window_func = drag_resize::event_func( &window, @@ -893,6 +894,22 @@ async fn run_instance( &mut debug, )); + let dnd_rectangles = user_interface + .dnd_rectangles(prev_dnd_rectangles_count); + let new_dnd_rectangles_count = + dnd_rectangles.as_ref().len(); + + if new_dnd_rectangles_count > 0 + || prev_dnd_rectangles_count > 0 + { + clipboard.register_dnd_destination( + DndSurface(Arc::new(Box::new(window.clone()))), + dnd_rectangles.into_rectangles(), + ); + } + + prev_dnd_rectangles_count = new_dnd_rectangles_count; + if should_exit { break; } diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 88ec01618f..306526696b 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -18,7 +18,6 @@ use crate::futures::futures::channel::mpsc; use crate::futures::futures::{task, Future, StreamExt}; use crate::futures::{Executor, Runtime, Subscription}; use crate::graphics::{compositor, Compositor}; -use crate::multi_window::operation::focusable::focus; use crate::multi_window::operation::OperationWrapper; use crate::multi_window::window_manager::WindowManager; use crate::runtime::command::{self, Command}; @@ -32,7 +31,6 @@ use dnd::Icon; use iced_graphics::Viewport; use iced_runtime::futures::futures::FutureExt; use iced_style::core::Length; -use iced_style::Theme; pub use state::State; use window_clipboard::mime::ClipboardStoreData; use winit::raw_window_handle::HasWindowHandle; @@ -429,6 +427,7 @@ async fn run_instance( window::Id::MAIN, user_interface::Cache::default(), )]), + &mut clipboard, )); run_command( @@ -861,6 +860,7 @@ async fn run_instance( &mut debug, &mut window_manager, cached_interfaces, + &mut clipboard, )); } @@ -1604,6 +1604,7 @@ fn run_command( debug, window_manager, std::mem::take(ui_caches), + clipboard, ); while let Some(mut operation) = current_operation.take() { @@ -1702,6 +1703,7 @@ pub fn build_user_interfaces<'a, A: Application, C: Compositor>( debug: &mut Debug, window_manager: &mut WindowManager, mut cached_user_interfaces: HashMap, + clipboard: &mut Clipboard, ) -> HashMap> where A::Theme: StyleSheet, @@ -1711,18 +1713,32 @@ where .drain() .filter_map(|(id, cache)| { let window = window_manager.get_mut(id)?; - - Some(( + let interface = build_user_interface( + application, + cache, + &mut window.renderer, + window.state.logical_size(), + debug, id, - build_user_interface( - application, - cache, - &mut window.renderer, - window.state.logical_size(), - debug, - id, - ), - )) + ); + + let dnd_rectangles = interface + .dnd_rectangles(window.prev_dnd_destination_rectangles_count); + let new_dnd_rectangles_count = dnd_rectangles.as_ref().len(); + + if new_dnd_rectangles_count > 0 + || window.prev_dnd_destination_rectangles_count > 0 + { + clipboard.register_dnd_destination( + DndSurface(Arc::new(Box::new(window.raw.clone()))), + dnd_rectangles.into_rectangles(), + ); + } + + window.prev_dnd_destination_rectangles_count = + new_dnd_rectangles_count; + + Some((id, interface)) }) .collect() } diff --git a/winit/src/multi_window/window_manager.rs b/winit/src/multi_window/window_manager.rs index 8b070da174..0a67a01580 100644 --- a/winit/src/multi_window/window_manager.rs +++ b/winit/src/multi_window/window_manager.rs @@ -69,6 +69,7 @@ where surface, renderer, mouse_interaction: mouse::Interaction::Idle, + prev_dnd_destination_rectangles_count: 0, }, ); @@ -142,6 +143,7 @@ where ) -> bool, >, >, + pub prev_dnd_destination_rectangles_count: usize, pub mouse_interaction: mouse::Interaction, pub surface: C::Surface, pub renderer: A::Renderer,