Skip to main content

iced_winit/
clipboard.rs

1//! Access the clipboard.
2use crate::core::clipboard::{Content, Error, Kind};
3
4pub use platform::*;
5
6impl Default for Clipboard {
7    fn default() -> Self {
8        Self::new()
9    }
10}
11
12#[cfg(not(target_arch = "wasm32"))]
13mod platform {
14    use super::*;
15
16    use std::sync::{Arc, Mutex};
17    use std::thread;
18
19    /// A buffer for short-term storage and transfer within and between
20    /// applications.
21    pub struct Clipboard {
22        state: State,
23    }
24
25    enum State {
26        Connected {
27            clipboard: Arc<Mutex<arboard::Clipboard>>,
28        },
29        Unavailable,
30    }
31
32    impl Clipboard {
33        /// Creates a new [`Clipboard`] for the given window.
34        pub fn new() -> Self {
35            let clipboard = arboard::Clipboard::new();
36
37            let state = match clipboard {
38                Ok(clipboard) => State::Connected {
39                    clipboard: Arc::new(Mutex::new(clipboard)),
40                },
41                Err(_) => State::Unavailable,
42            };
43
44            Clipboard { state }
45        }
46
47        /// Reads the current content of the [`Clipboard`] as text.
48        pub fn read(
49            &self,
50            kind: Kind,
51            callback: impl FnOnce(Result<Content, Error>) + Send + 'static,
52        ) {
53            let State::Connected { clipboard } = &self.state else {
54                callback(Err(Error::ClipboardUnavailable));
55                return;
56            };
57
58            let clipboard = clipboard.clone();
59
60            let _ = thread::spawn(move || {
61                let Ok(mut clipboard) = clipboard.lock() else {
62                    callback(Err(Error::ClipboardUnavailable));
63                    return;
64                };
65
66                let get = clipboard.get();
67
68                let result = match kind {
69                    Kind::Text => get.text().map(Content::Text),
70                    Kind::Html => get.html().map(Content::Html),
71                    #[cfg(feature = "image")]
72                    Kind::Image => get.image().map(|image| {
73                        let rgba = crate::core::Bytes::from_owner(image.bytes);
74                        let size = crate::core::Size {
75                            width: image.width as u32,
76                            height: image.height as u32,
77                        };
78
79                        Content::Image(crate::core::clipboard::Image { rgba, size })
80                    }),
81                    Kind::Files => get.file_list().map(Content::Files),
82                    kind => {
83                        log::warn!("unsupported clipboard kind: {kind:?}");
84
85                        Err(arboard::Error::ContentNotAvailable)
86                    }
87                }
88                .map_err(to_error);
89
90                callback(result);
91            });
92        }
93
94        /// Writes the given text contents to the [`Clipboard`].
95        pub fn write(
96            &mut self,
97            content: Content,
98            callback: impl FnOnce(Result<(), Error>) + Send + 'static,
99        ) {
100            let State::Connected { clipboard } = &self.state else {
101                callback(Err(Error::ClipboardUnavailable));
102                return;
103            };
104
105            let clipboard = clipboard.clone();
106
107            let _ = thread::spawn(move || {
108                let Ok(mut clipboard) = clipboard.lock() else {
109                    callback(Err(Error::ClipboardUnavailable));
110                    return;
111                };
112
113                let set = clipboard.set();
114
115                let result = match content {
116                    Content::Text(text) => set.text(text),
117                    Content::Html(html) => set.html(html, None),
118                    #[cfg(feature = "image")]
119                    Content::Image(image) => set.image(arboard::ImageData {
120                        bytes: image.rgba.as_ref().into(),
121                        width: image.size.width as usize,
122                        height: image.size.height as usize,
123                    }),
124                    Content::Files(files) => set.file_list(&files),
125                    content => {
126                        log::warn!("unsupported clipboard content: {content:?}");
127
128                        Err(arboard::Error::ClipboardNotSupported)
129                    }
130                }
131                .map_err(to_error);
132
133                callback(result);
134            });
135        }
136    }
137
138    fn to_error(error: arboard::Error) -> Error {
139        match error {
140            arboard::Error::ContentNotAvailable => Error::ContentNotAvailable,
141            arboard::Error::ClipboardNotSupported => Error::ClipboardUnavailable,
142            arboard::Error::ClipboardOccupied => Error::ClipboardOccupied,
143            arboard::Error::ConversionFailure => Error::ConversionFailure,
144            arboard::Error::Unknown { description } => Error::Unknown {
145                description: Arc::new(description),
146            },
147            error => Error::Unknown {
148                description: Arc::new(error.to_string()),
149            },
150        }
151    }
152}
153
154// TODO: Wasm support
155#[cfg(target_arch = "wasm32")]
156mod platform {
157    use super::*;
158
159    /// A buffer for short-term storage and transfer within and between
160    /// applications.
161    pub struct Clipboard;
162
163    impl Clipboard {
164        /// Creates a new [`Clipboard`] for the given window.
165        pub fn new() -> Self {
166            Self
167        }
168
169        /// Reads the current content of the [`Clipboard`] as text.
170        pub fn read(&self, _kind: Kind, callback: impl FnOnce(Result<Content, Error>)) {
171            callback(Err(Error::ClipboardUnavailable));
172        }
173
174        /// Writes the given text contents to the [`Clipboard`].
175        pub fn write(&mut self, _content: Content, callback: impl FnOnce(Result<(), Error>)) {
176            callback(Err(Error::ClipboardUnavailable));
177        }
178    }
179}