iced_core/
clipboard.rs

1//! Access the clipboard.
2
3use std::any::Any;
4
5use dnd::{DndAction, DndDestinationRectangle, DndSurface};
6use mime::{self, AllowedMimeTypes, AsMimeTypes, ClipboardStoreData};
7
8use crate::{widget::tree::State, window, Element, Vector};
9
10#[derive(Debug)]
11pub struct IconSurface<E> {
12    pub element: E,
13    pub state: State,
14    pub offset: Vector,
15}
16
17pub type DynIconSurface = IconSurface<Box<dyn Any>>;
18
19impl<T: 'static, R: 'static> IconSurface<Element<'static, (), T, R>> {
20    pub fn new(
21        element: Element<'static, (), T, R>,
22        state: State,
23        offset: Vector,
24    ) -> Self {
25        Self {
26            element,
27            state,
28            offset,
29        }
30    }
31
32    fn upcast(self) -> DynIconSurface {
33        IconSurface {
34            element: Box::new(self.element),
35            state: self.state,
36            offset: self.offset,
37        }
38    }
39}
40
41impl DynIconSurface {
42    /// Downcast `element` to concrete type `Element<(), T, R>`
43    ///
44    /// Panics if type doesn't match
45    pub fn downcast<T: 'static, R: 'static>(
46        self,
47    ) -> IconSurface<Element<'static, (), T, R>> {
48        IconSurface {
49            element: *self
50                .element
51                .downcast()
52                .expect("drag-and-drop icon surface has invalid element type"),
53            state: self.state,
54            offset: self.offset,
55        }
56    }
57}
58
59/// A buffer for short-term storage and transfer within and between
60/// applications.
61pub trait Clipboard {
62    /// Reads the current content of the [`Clipboard`] as text.
63    fn read(&self, kind: Kind) -> Option<String>;
64
65    /// Writes the given text contents to the [`Clipboard`].
66    fn write(&mut self, kind: Kind, contents: String);
67
68    /// Consider using [`read_data`] instead
69    /// Reads the current content of the [`Clipboard`] as text.
70    fn read_data(
71        &self,
72        _kind: Kind,
73        _mimes: Vec<String>,
74    ) -> Option<(Vec<u8>, String)> {
75        None
76    }
77
78    /// Writes the given contents to the [`Clipboard`].
79    fn write_data(
80        &mut self,
81        _kind: Kind,
82        _contents: ClipboardStoreData<
83            Box<dyn Send + Sync + 'static + mime::AsMimeTypes>,
84        >,
85    ) {
86    }
87
88    /// Starts a DnD operation.
89    fn register_dnd_destination(
90        &self,
91        _surface: DndSurface,
92        _rectangles: Vec<DndDestinationRectangle>,
93    ) {
94    }
95
96    /// Set the final action for the DnD operation.
97    /// Only should be done if it is requested.
98    fn set_action(&self, _action: DndAction) {}
99
100    /// Registers Dnd destinations
101    fn start_dnd(
102        &mut self,
103        _internal: bool,
104        _source_surface: Option<DndSource>,
105        _icon_surface: Option<DynIconSurface>,
106        _content: Box<dyn AsMimeTypes + Send + 'static>,
107        _actions: DndAction,
108    ) {
109    }
110
111    /// Ends a DnD operation.
112    fn end_dnd(&self) {}
113
114    /// Consider using [`peek_dnd`] instead
115    /// Peeks the data on the DnD with a specific mime type.
116    /// Will return an error if there is no ongoing DnD operation.
117    fn peek_dnd(&self, _mime: String) -> Option<(Vec<u8>, String)> {
118        None
119    }
120
121    /// Request window size
122    fn request_logical_window_size(&self, _width: f32, _height: f32) {}
123}
124
125/// The kind of [`Clipboard`].
126#[derive(Debug, Clone, Copy, PartialEq, Eq)]
127pub enum Kind {
128    /// The standard clipboard.
129    Standard,
130    /// The primary clipboard.
131    ///
132    /// Normally only present in X11 and Wayland.
133    Primary,
134}
135
136/// Starts a DnD operation.
137/// icon surface is a tuple of the icon element and optionally the icon element state.
138pub fn start_dnd<T: 'static, R: 'static>(
139    clipboard: &mut dyn Clipboard,
140    internal: bool,
141    source_surface: Option<DndSource>,
142    icon_surface: Option<IconSurface<Element<'static, (), T, R>>>,
143    content: Box<dyn AsMimeTypes + Send + 'static>,
144    actions: DndAction,
145) {
146    clipboard.start_dnd(
147        internal,
148        source_surface,
149        icon_surface.map(IconSurface::upcast),
150        content,
151        actions,
152    );
153}
154
155/// A null implementation of the [`Clipboard`] trait.
156#[derive(Debug, Clone, Copy)]
157pub struct Null;
158
159impl Clipboard for Null {
160    fn read(&self, _kind: Kind) -> Option<String> {
161        None
162    }
163
164    fn write(&mut self, _kind: Kind, _contents: String) {}
165}
166
167/// Reads the current content of the [`Clipboard`].
168pub fn read_data<T: AllowedMimeTypes>(
169    clipboard: &mut dyn Clipboard,
170) -> Option<T> {
171    clipboard
172        .read_data(Kind::Standard, T::allowed().into())
173        .and_then(|data| T::try_from(data).ok())
174}
175
176/// Reads the current content of the primary [`Clipboard`].
177pub fn read_primary_data<T: AllowedMimeTypes>(
178    clipboard: &mut dyn Clipboard,
179) -> Option<T> {
180    clipboard
181        .read_data(Kind::Primary, T::allowed().into())
182        .and_then(|data| T::try_from(data).ok())
183}
184
185/// Reads the current content of the primary [`Clipboard`].
186pub fn peek_dnd<T: AllowedMimeTypes>(
187    clipboard: &mut dyn Clipboard,
188    mime: Option<String>,
189) -> Option<T> {
190    let mime = mime.or_else(|| T::allowed().first().cloned())?;
191    clipboard
192        .peek_dnd(mime)
193        .and_then(|data| T::try_from(data).ok())
194}
195
196/// Source of a DnD operation.
197#[derive(Debug, Clone)]
198pub enum DndSource {
199    /// A widget is the source of the DnD operation.
200    Widget(crate::id::Id),
201    /// A surface is the source of the DnD operation.
202    Surface(window::Id),
203}
204
205/// A list of DnD destination rectangles.
206#[derive(Debug, Clone, Default)]
207pub struct DndDestinationRectangles {
208    /// The rectangle of the DnD destination.
209    rectangles: Vec<DndDestinationRectangle>,
210}
211
212impl DndDestinationRectangles {
213    /// Creates a new [`DndDestinationRectangles`].
214    pub fn new() -> Self {
215        Self::default()
216    }
217
218    /// Creates a new [`DndDestinationRectangles`] with the given capacity.
219    pub fn with_capacity(capacity: usize) -> Self {
220        Self {
221            rectangles: Vec::with_capacity(capacity),
222        }
223    }
224
225    /// Pushes a new rectangle to the list of DnD destination rectangles.
226    pub fn push(&mut self, rectangle: DndDestinationRectangle) {
227        self.rectangles.push(rectangle);
228    }
229
230    /// Appends the list of DnD destination rectangles to the current list.
231    pub fn append(&mut self, other: &mut Vec<DndDestinationRectangle>) {
232        self.rectangles.append(other);
233    }
234
235    /// Returns the list of DnD destination rectangles.
236    /// This consumes the [`DndDestinationRectangles`].
237    pub fn into_rectangles(mut self) -> Vec<DndDestinationRectangle> {
238        self.rectangles.reverse();
239        self.rectangles
240    }
241}
242
243impl AsRef<[DndDestinationRectangle]> for DndDestinationRectangles {
244    fn as_ref(&self) -> &[DndDestinationRectangle] {
245        &self.rectangles
246    }
247}