Skip to main content

ferroui_base/input/platform/
clipboard.rs

1use super::{ClipboardError, IClipboard, IClipboardImpl};
2use crate::input::{IAsyncDataTransfer, LocalBoxFuture};
3use std::cell::RefCell;
4use std::rc::{Rc, Weak};
5
6/// Implementation of [`IClipboard`] on top of a platform clipboard.
7pub struct Clipboard {
8    this: Weak<Clipboard>,
9    clipboard_impl: Rc<dyn IClipboardImpl>,
10    last_data_transfer: RefCell<Option<Rc<dyn IAsyncDataTransfer>>>,
11}
12
13impl Clipboard {
14    /// Creates the clipboard for a platform implementation.
15    pub fn new(clipboard_impl: Rc<dyn IClipboardImpl>) -> Rc<Clipboard> {
16        Rc::new_cyclic(|this| Clipboard { this: this.clone(), clipboard_impl, last_data_transfer: RefCell::new(None) })
17    }
18
19    fn take_last_data_transfer(&self) -> Option<Rc<dyn IAsyncDataTransfer>> {
20        self.last_data_transfer.borrow_mut().take()
21    }
22}
23
24impl IClipboard for Clipboard {
25    fn clear_async(&self) -> LocalBoxFuture<Result<(), ClipboardError>> {
26        if let Some(last_data_transfer) = self.take_last_data_transfer() {
27            last_data_transfer.dispose();
28        }
29
30        self.clipboard_impl.clear_async()
31    }
32
33    fn set_data_async(
34        &self,
35        data_transfer: Option<Rc<dyn IAsyncDataTransfer>>,
36    ) -> LocalBoxFuture<Result<(), ClipboardError>> {
37        let Some(data_transfer) = data_transfer else {
38            return self.clear_async();
39        };
40
41        if self.clipboard_impl.as_owned_clipboard_impl().is_some() {
42            let old = self.last_data_transfer.replace(Some(data_transfer.clone()));
43            drop(old);
44        }
45
46        self.clipboard_impl.set_data_async(data_transfer)
47    }
48
49    fn flush_async(&self) -> LocalBoxFuture<Result<(), ClipboardError>> {
50        match self.clipboard_impl.as_flushable_clipboard_impl() {
51            Some(flushable) => flushable.flush_async(),
52            None => Box::pin(std::future::ready(Ok(()))),
53        }
54    }
55
56    fn try_get_data_async(&self) -> LocalBoxFuture<Result<Option<Rc<dyn IAsyncDataTransfer>>, ClipboardError>> {
57        self.clipboard_impl.try_get_data_async()
58    }
59
60    fn try_get_in_process_data_async(
61        &self,
62    ) -> LocalBoxFuture<Result<Option<Rc<dyn IAsyncDataTransfer>>, ClipboardError>> {
63        if self.last_data_transfer.borrow().is_none() {
64            return Box::pin(std::future::ready(Ok(None)));
65        }
66
67        let Some(owned_clipboard_impl) = self.clipboard_impl.as_owned_clipboard_impl() else {
68            return Box::pin(std::future::ready(Ok(None)));
69        };
70
71        let is_current_owner = owned_clipboard_impl.is_current_owner_async();
72        let this = self.this.clone();
73
74        Box::pin(async move {
75            // A failed ownership query leaves the kept data transfer in place.
76            let is_current_owner = is_current_owner.await?;
77            let Some(this) = this.upgrade() else {
78                return Ok(None);
79            };
80
81            if !is_current_owner {
82                drop(this.take_last_data_transfer());
83            }
84
85            let result = this.last_data_transfer.borrow().clone();
86            Ok(result)
87        })
88    }
89}