inset_embedder/
drag_drop.rs1use std::cell::Cell;
20use std::fmt;
21use std::rc::Rc;
22
23use crate::{ClipboardError, ClipboardReadFuture, DataTransferItem, Offset, ViewId};
24
25#[derive(Clone)]
27pub struct DragReport {
28 pub view_id: ViewId,
30 pub change: DragChange,
32 pub physical_position: Option<Offset>,
35 pub items: Vec<Rc<dyn DataTransferItem>>,
39}
40
41impl fmt::Debug for DragReport {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 let types: Vec<&[String]> = self.items.iter().map(|item| item.types()).collect();
44 f.debug_struct("DragReport")
45 .field("view_id", &self.view_id)
46 .field("change", &self.change)
47 .field("physical_position", &self.physical_position)
48 .field("items", &types)
49 .finish()
50 }
51}
52
53#[derive(Clone, Copy, Debug, PartialEq, Eq)]
55pub enum DragChange {
56 Entered,
58 Moved,
60 Dropped,
62 Exited,
64}
65
66pub struct DraggedItems {
69 reading: Rc<Cell<Reading>>,
70 items: Vec<Rc<dyn DataTransferItem>>,
71}
72
73#[derive(Clone, Copy, Debug, PartialEq, Eq)]
75enum Reading {
76 NotDropped,
78 Dropping,
80 Over,
82}
83
84impl DraggedItems {
85 pub fn new(items: Vec<Box<dyn DataTransferItem>>) -> DraggedItems {
87 let reading = Rc::new(Cell::new(Reading::NotDropped));
88 let items = items
89 .into_iter()
90 .map(|item| {
91 Rc::new(DraggedItem {
92 item,
93 reading: Rc::clone(&reading),
94 }) as Rc<dyn DataTransferItem>
95 })
96 .collect();
97 DraggedItems { reading, items }
98 }
99
100 pub fn is_empty(&self) -> bool {
102 self.items.is_empty()
103 }
104
105 pub fn items(&self) -> Vec<Rc<dyn DataTransferItem>> {
107 self.items.clone()
108 }
109
110 pub fn report_drop<R>(&self, report: impl FnOnce() -> R) -> R {
112 self.reading.set(Reading::Dropping);
113 let _over = ReadingEnds(&self.reading);
114 report()
115 }
116}
117
118struct ReadingEnds<'a>(&'a Cell<Reading>);
120
121impl Drop for ReadingEnds<'_> {
122 fn drop(&mut self) {
123 self.0.set(Reading::Over);
124 }
125}
126
127struct DraggedItem {
129 item: Box<dyn DataTransferItem>,
130 reading: Rc<Cell<Reading>>,
131}
132
133impl DataTransferItem for DraggedItem {
134 fn types(&self) -> &[String] {
135 self.item.types()
136 }
137
138 fn read(&self, mime: &str) -> ClipboardReadFuture {
139 match self.reading.get() {
140 Reading::NotDropped => Box::pin(std::future::ready(Err(ClipboardError::Denied))),
141 Reading::Dropping => self.item.read(mime),
142 Reading::Over => Box::pin(std::future::ready(Err(ClipboardError::Changed))),
143 }
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use std::future::Future;
150 use std::pin::Pin;
151 use std::task::{Context, Poll, Waker};
152
153 use super::*;
154
155 struct Text([String; 1]);
157
158 impl Text {
159 fn item() -> Box<dyn DataTransferItem> {
160 Box::new(Text(["text/plain".to_owned()]))
161 }
162 }
163
164 impl DataTransferItem for Text {
165 fn types(&self) -> &[String] {
166 &self.0
167 }
168
169 fn read(&self, _mime: &str) -> ClipboardReadFuture {
170 Box::pin(std::future::ready(Ok(b"hi".to_vec())))
171 }
172 }
173
174 fn now<T>(mut future: Pin<Box<dyn Future<Output = T>>>) -> T {
175 match future
176 .as_mut()
177 .poll(&mut Context::from_waker(Waker::noop()))
178 {
179 Poll::Ready(output) => output,
180 Poll::Pending => panic!("the item answers in the call"),
181 }
182 }
183
184 #[test]
185 fn an_item_shows_its_types_while_the_drag_passes_over_but_not_its_content() {
186 let dragged = DraggedItems::new(vec![Text::item()]);
187 let items = dragged.items();
188 assert_eq!(items[0].types(), ["text/plain"]);
189 assert_eq!(
190 now(items[0].read("text/plain")),
191 Err(ClipboardError::Denied)
192 );
193 }
194
195 #[test]
196 fn an_item_is_read_while_its_drop_is_reported_and_not_after() {
197 let dragged = DraggedItems::new(vec![Text::item()]);
198 let items = dragged.items();
199 let read = dragged.report_drop(|| now(items[0].read("text/plain")));
200 assert_eq!(read, Ok(b"hi".to_vec()));
201 assert_eq!(
202 now(items[0].read("text/plain")),
203 Err(ClipboardError::Changed)
204 );
205 }
206
207 #[test]
208 fn a_read_asked_for_during_the_drop_completes_after_it() {
209 let dragged = DraggedItems::new(vec![Text::item()]);
210 let items = dragged.items();
211 let read = dragged.report_drop(|| items[0].read("text/plain"));
212 assert_eq!(now(read), Ok(b"hi".to_vec()));
213 }
214
215 #[test]
216 fn a_drag_carrying_nothing_is_empty() {
217 assert!(DraggedItems::new(Vec::new()).is_empty());
218 assert!(!DraggedItems::new(vec![Text::item()]).is_empty());
219 }
220}