diff --git a/core/src/clipboard.rs b/core/src/clipboard.rs index 0197354f67..4dc64c5842 100644 --- a/core/src/clipboard.rs +++ b/core/src/clipboard.rs @@ -1,11 +1,47 @@ //! Access the clipboard. -use std::{any::Any, sync::Arc}; +use std::any::Any; use dnd::{DndAction, DndDestinationRectangle, DndSurface}; use mime::{self, AllowedMimeTypes, AsMimeTypes, ClipboardStoreData}; -use crate::{widget::tree::State, window, Element}; +use crate::{widget::tree::State, window, Element, Vector}; + +#[derive(Debug)] +pub struct IconSurface { + pub element: E, + pub state: State, + pub offset: Vector, +} + +pub type DynIconSurface = IconSurface>; + +impl IconSurface> { + pub fn new(element: Element<'static, (), T, R>, state: State, offset: Vector) -> Self { + Self { element, state, offset } + } + + fn upcast(self) -> DynIconSurface { + IconSurface { + element: Box::new(self.element), + state: self.state, + offset: self.offset, + } + } +} + +impl DynIconSurface { + /// Downcast `element` to concrete type `Element<(), T, R>` + /// + /// Panics if type doesn't match + pub fn downcast(self) -> IconSurface> { + IconSurface { + element: *self.element.downcast().expect("drag-and-drop icon surface has invalid element type"), + state: self.state, + offset: self.offset, + } + } +} /// A buffer for short-term storage and transfer within and between /// applications. @@ -53,7 +89,7 @@ pub trait Clipboard { &mut self, _internal: bool, _source_surface: Option, - _icon_surface: Option>, + _icon_surface: Option, _content: Box, _actions: DndAction, ) { @@ -86,21 +122,18 @@ pub enum Kind { /// Starts a DnD operation. /// icon surface is a tuple of the icon element and optionally the icon element state. -pub fn start_dnd( +pub fn start_dnd( clipboard: &mut dyn Clipboard, internal: bool, source_surface: Option, - icon_surface: Option<(Element<'static, M, T, R>, State)>, + icon_surface: Option>>, content: Box, actions: DndAction, ) { clipboard.start_dnd( internal, source_surface, - icon_surface.map(|i| { - let i: Box = Box::new(Arc::new(i)); - i - }), + icon_surface.map(IconSurface::upcast), content, actions, ); diff --git a/runtime/src/dnd.rs b/runtime/src/dnd.rs index 69c956f686..e6f2b2678a 100644 --- a/runtime/src/dnd.rs +++ b/runtime/src/dnd.rs @@ -17,19 +17,6 @@ pub enum DndAction { /// The rectangles to register. rectangles: Vec, }, - /// Start a Dnd operation. - StartDnd { - /// Whether the Dnd operation is internal. - internal: bool, - /// The source surface of the Dnd operation. - source_surface: Option, - /// The icon surface of the Dnd operation. - icon_surface: Option>, - /// The content of the Dnd operation. - content: Box, - /// The actions of the Dnd operation. - actions: dnd::DndAction, - }, /// End a Dnd operation. EndDnd, /// Peek the current Dnd operation. @@ -53,19 +40,6 @@ impl std::fmt::Debug for DndAction { .field("surface", surface) .field("rectangles", rectangles) .finish(), - Self::StartDnd { - internal, - source_surface, - icon_surface, - content: _, - actions, - } => f - .debug_struct("StartDnd") - .field("internal", internal) - .field("source_surface", source_surface) - .field("icon_surface", icon_surface) - .field("actions", actions) - .finish(), Self::EndDnd => f.write_str("EndDnd"), Self::PeekDnd(mime, _) => { f.debug_struct("PeekDnd").field("mime", mime).finish() @@ -99,23 +73,6 @@ pub fn register_dnd_destination( })) } -/// Start a Dnd operation. -pub fn start_dnd( - internal: bool, - source_surface: Option, - icon_surface: Option>, - content: Box, - actions: dnd::DndAction, -) -> Task { - task::effect(Action::Dnd(DndAction::StartDnd { - internal, - source_surface, - icon_surface, - content, - actions, - })) -} - /// End a Dnd operation. pub fn end_dnd() -> Task { task::effect(Action::Dnd(DndAction::EndDnd)) diff --git a/winit/src/clipboard.rs b/winit/src/clipboard.rs index 26381d2554..097e65ca1e 100644 --- a/winit/src/clipboard.rs +++ b/winit/src/clipboard.rs @@ -3,7 +3,7 @@ use std::sync::Mutex; use std::{any::Any, borrow::Cow}; -use crate::core::clipboard::DndSource; +use crate::core::clipboard::{DndSource, DynIconSurface}; use crate::core::clipboard::Kind; use std::sync::Arc; use winit::dpi::LogicalSize; @@ -26,7 +26,7 @@ pub struct Clipboard { pub(crate) struct StartDnd { pub(crate) internal: bool, pub(crate) source_surface: Option, - pub(crate) icon_surface: Option>, + pub(crate) icon_surface: Option, pub(crate) content: Box, pub(crate) actions: DndAction, } @@ -261,7 +261,7 @@ impl crate::core::Clipboard for Clipboard { &mut self, internal: bool, source_surface: Option, - icon_surface: Option>, + icon_surface: Option, content: Box, actions: DndAction, ) { diff --git a/winit/src/platform_specific/mod.rs b/winit/src/platform_specific/mod.rs index 5490bfac77..77e851802c 100644 --- a/winit/src/platform_specific/mod.rs +++ b/winit/src/platform_specific/mod.rs @@ -4,7 +4,7 @@ use std::collections::HashMap; use iced_graphics::Compositor; -use iced_runtime::{core::window, user_interface, Debug}; +use iced_runtime::{core::{window, Vector}, user_interface, Debug}; use raw_window_handle::HasWindowHandle; #[cfg(all(feature = "wayland", target_os = "linux"))] @@ -128,10 +128,10 @@ impl PlatformSpecific { None } - pub(crate) fn update_surface_shm(&mut self, surface: &dyn HasWindowHandle, width: u32, height: u32, data: &[u8]) { + pub(crate) fn update_surface_shm(&mut self, surface: &dyn HasWindowHandle, width: u32, height: u32, scale: f64, data: &[u8], offset: Vector) { #[cfg(all(feature = "wayland", target_os = "linux"))] { - return self.wayland.update_surface_shm(surface, width, height, data); + return self.wayland.update_surface_shm(surface, width, height, scale, data, offset); } } } diff --git a/winit/src/platform_specific/wayland/mod.rs b/winit/src/platform_specific/wayland/mod.rs index 1b3b8cf5b8..16c316c0a7 100644 --- a/winit/src/platform_specific/wayland/mod.rs +++ b/winit/src/platform_specific/wayland/mod.rs @@ -15,7 +15,7 @@ use cctk::sctk::reexports::client::protocol::wl_surface::WlSurface; use cctk::sctk::seat::keyboard::Modifiers; use iced_futures::futures::channel::mpsc; use iced_graphics::Compositor; -use iced_runtime::core::window; +use iced_runtime::core::{window, Vector}; use iced_runtime::Debug; use raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle}; use raw_window_handle::{HasRawDisplayHandle, RawWindowHandle}; @@ -239,12 +239,12 @@ impl WaylandSpecific { } } - pub(crate) fn update_surface_shm(&mut self, window: &dyn HasWindowHandle, width: u32, height: u32, data: &[u8]) { + pub(crate) fn update_surface_shm(&mut self, window: &dyn HasWindowHandle, width: u32, height: u32, scale: f64, data: &[u8], offset: Vector) { if let Some(subsurface_state) = self.subsurface_state.as_mut() { if let RawWindowHandle::Wayland(window) = window.window_handle().unwrap().as_raw() { let id = unsafe { ObjectId::from_ptr(WlSurface::interface(), window.surface.as_ptr().cast()).unwrap() }; let surface = WlSurface::from_id(self.conn.as_ref().unwrap(), id).unwrap(); - subsurface_state.update_surface_shm(&surface, width, height, data); + subsurface_state.update_surface_shm(&surface, width, height, scale, data, offset); } } } diff --git a/winit/src/platform_specific/wayland/subsurface_widget.rs b/winit/src/platform_specific/wayland/subsurface_widget.rs index 5cde0e96bb..288dbb6106 100644 --- a/winit/src/platform_specific/wayland/subsurface_widget.rs +++ b/winit/src/platform_specific/wayland/subsurface_widget.rs @@ -4,7 +4,7 @@ use crate::core::{ layout::{self, Layout}, mouse, renderer, widget::{self, Widget}, - ContentFit, Element, Length, Rectangle, Size, + ContentFit, Element, Length, Rectangle, Size, Vector, }; use std::{ cell::RefCell, @@ -377,14 +377,22 @@ impl SubsurfaceState { .create_surface(&self.qh, SurfaceData::new(None, 1)) } - pub fn update_surface_shm(&self, surface: &WlSurface, width: u32, height: u32, data: &[u8]) { + pub fn update_surface_shm(&self, surface: &WlSurface, width: u32, height: u32, scale: f64, data: &[u8], offset: Vector) { + let wp_viewport = self.wp_viewporter.get_viewport( + &surface, + &self.qh, + cctk::sctk::globals::GlobalData, + ); let shm = ShmGlobal(&self.wl_shm); let mut pool = SlotPool::new(width as usize * height as usize * 4, &shm).unwrap(); let (buffer, canvas) = pool.create_buffer(width as i32, height as i32, width as i32 * 4, wl_shm::Format::Argb8888).unwrap(); canvas[0..width as usize * height as usize * 4].copy_from_slice(data); surface.damage_buffer(0, 0, width as i32, height as i32); buffer.attach_to(&surface); + surface.offset(offset.x as i32, offset.y as i32); + wp_viewport.set_destination((width as f64 / scale) as i32, (height as f64 / scale) as i32); surface.commit(); + wp_viewport.destroy(); } fn create_subsurface(&self, parent: &WlSurface) -> SubsurfaceInstance { diff --git a/winit/src/program.rs b/winit/src/program.rs index f3b1ae9db7..1640702925 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -886,119 +886,92 @@ async fn run_instance<'a, P, C>( }; let state = &window.state; + let mut dnd_buffer = None; let icon_surface = icon_surface .map(|i| { - let i: Box = i; - i - }) - .map(|i| { - i.downcast::, - core::widget::tree::State, - )>>() - .unwrap() - }) - .map( - |e: Box< - Arc<( - core::Element< - 'static, - (), - P::Theme, - P::Renderer, - >, - core::widget::tree::State, - )>, - >| { - let mut renderer = compositor.create_renderer(); - - let e = Arc::into_inner(*e).unwrap(); - let (mut e, widget_state) = e; - let lim = core::layout::Limits::new( - Size::new(1., 1.), - Size::new( - state.viewport().physical_width() - as f32, - state.viewport().physical_height() - as f32, - ), - ); - - let mut tree = core::widget::Tree { - id: e.as_widget().id(), - tag: e.as_widget().tag(), - state: widget_state, - children: e.as_widget().children(), - }; - - let size = e - .as_widget() - .layout(&mut tree, &renderer, &lim); - e.as_widget_mut().diff(&mut tree); + let mut icon_surface = i.downcast::(); + + let mut renderer = compositor.create_renderer(); + + let lim = core::layout::Limits::new( + Size::new(1., 1.), + Size::new( + state.viewport().physical_width() + as f32, + state.viewport().physical_height() + as f32, + ), + ); - let size = lim.resolve( - Length::Shrink, - Length::Shrink, - size.size(), - ); - let viewport = Viewport::with_logical_size( - size, - state.viewport().scale_factor(), - ); + let mut tree = core::widget::Tree { + id: icon_surface.element.as_widget().id(), + tag: icon_surface.element.as_widget().tag(), + state: icon_surface.state, + children: icon_surface.element.as_widget().children(), + }; + + let size = icon_surface.element + .as_widget() + .layout(&mut tree, &renderer, &lim); + icon_surface.element.as_widget_mut().diff(&mut tree); + + let size = lim.resolve( + Length::Shrink, + Length::Shrink, + size.size(), + ); + let viewport = Viewport::with_logical_size( + size, + state.viewport().scale_factor(), + ); - let mut ui = UserInterface::build( - e, - size, - user_interface::Cache::default(), - &mut renderer, - ); - _ = ui.draw( - &mut renderer, - state.theme(), - &renderer::Style { - icon_color: state.icon_color(), - text_color: state.text_color(), - scale_factor: state.scale_factor(), - }, - Default::default(), - );; - let mut bytes = compositor.screenshot( - &mut renderer, - &viewport, - core::Color::TRANSPARENT, - &debug.overlay(), - ); - for pix in bytes.chunks_exact_mut(4) { - // rgba -> argb little endian - pix.swap(0, 2); - } - // update subsurfaces - if let Some(surface) = platform_specific_handler.create_surface() { - // TODO Remove id - let id = window::Id::unique(); - platform_specific_handler - .update_subsurfaces(id, &surface); - platform_specific_handler.update_surface_shm(&surface, viewport.physical_width(), viewport.physical_height(), &bytes); - let surface = Arc::new(surface); - dnd_surface = Some(surface.clone()); - Icon::Surface(dnd::DndSurface(surface)) - } else { - platform_specific_handler - .clear_subsurface_list(); - Icon::Buffer { - data: Arc::new(bytes), - width: viewport.physical_width(), - height: viewport.physical_height(), - transparent: true, - } + let mut ui = UserInterface::build( + icon_surface.element, + size, + user_interface::Cache::default(), + &mut renderer, + ); + _ = ui.draw( + &mut renderer, + state.theme(), + &renderer::Style { + icon_color: state.icon_color(), + text_color: state.text_color(), + scale_factor: state.scale_factor(), + }, + Default::default(), + );; + let mut bytes = compositor.screenshot( + &mut renderer, + &viewport, + core::Color::TRANSPARENT, + &debug.overlay(), + ); + for pix in bytes.chunks_exact_mut(4) { + // rgba -> argb little endian + pix.swap(0, 2); + } + // update subsurfaces + if let Some(surface) = platform_specific_handler.create_surface() { + // TODO Remove id + let id = window::Id::unique(); + platform_specific_handler + .update_subsurfaces(id, &surface); + let surface = Arc::new(surface); + dnd_surface = Some(surface.clone()); + dnd_buffer = Some((viewport.physical_size(), state.scale_factor(), bytes, icon_surface.offset)); + Icon::Surface(dnd::DndSurface(surface)) + } else { + platform_specific_handler + .clear_subsurface_list(); + Icon::Buffer { + data: Arc::new(bytes), + width: viewport.physical_width(), + height: viewport.physical_height(), + transparent: true, } - }, - ); + } + }); clipboard.start_dnd_winit( internal, @@ -1007,6 +980,11 @@ async fn run_instance<'a, P, C>( content, actions, ); + + // This needs to be after `wl_data_device::start_drag` for the offset to have an effect + if let (Some(surface), Some((size, scale, bytes, offset))) = (dnd_surface.as_ref(), dnd_buffer) { + platform_specific_handler.update_surface_shm(&surface, size.width, size.height, scale, &bytes, offset); + } } } Event::WindowCreated { @@ -2297,21 +2275,6 @@ where } => { clipboard.register_dnd_destination(surface, rectangles); } - iced_runtime::dnd::DndAction::StartDnd { - internal, - source_surface, - icon_surface, - content, - actions, - } => { - clipboard.start_dnd( - internal, - source_surface, - icon_surface.map(|d| d as Box), - content, - actions, - ); - } iced_runtime::dnd::DndAction::EndDnd => { clipboard.end_dnd(); }