Skip to main content

ferroui_base/input/platform/
platform_async_data_transfer.rs

1use super::ClipboardError;
2use crate::input::{DataFormat, IAsyncDataTransfer, IAsyncDataTransferItem};
3use crate::reactive::IDisposable;
4use std::cell::RefCell;
5use std::rc::Rc;
6
7/// The members a platform backend provides for a
8/// [`PlatformAsyncDataTransfer`].
9pub trait PlatformAsyncDataTransferImpl {
10    /// Provides the formats supported by the data transfer. Called until it
11    /// succeeds: a failure is reported to the caller and not kept.
12    fn provide_formats(&self) -> Result<Vec<DataFormat>, ClipboardError>;
13
14    /// Provides the items of the data transfer. Called until it succeeds: a
15    /// failure is reported to the caller and not kept.
16    fn provide_items(&self) -> Result<Vec<Rc<dyn IAsyncDataTransferItem>>, ClipboardError>;
17
18    /// Releases the platform resources of the data transfer. `owner` tells
19    /// whether the formats and the items have been provided.
20    fn dispose(&self, owner: &PlatformAsyncDataTransfer);
21}
22
23/// The base of the asynchronous data transfers of the platform backends:
24/// caches the formats and the items.
25pub struct PlatformAsyncDataTransfer {
26    formats: RefCell<Option<Rc<[DataFormat]>>>,
27    items: RefCell<Option<Rc<[Rc<dyn IAsyncDataTransferItem>]>>>,
28    platform_impl: Box<dyn PlatformAsyncDataTransferImpl>,
29}
30
31impl PlatformAsyncDataTransfer {
32    /// Creates the data transfer for a backend implementation.
33    pub fn new(platform_impl: impl PlatformAsyncDataTransferImpl + 'static) -> Rc<PlatformAsyncDataTransfer> {
34        Rc::new(PlatformAsyncDataTransfer {
35            formats: RefCell::new(None),
36            items: RefCell::new(None),
37            platform_impl: Box::new(platform_impl),
38        })
39    }
40
41    /// Gets the formats supported by the data transfer.
42    ///
43    /// # Panics
44    /// Panics if the backend fails to provide them (where the reference
45    /// throws from the property); [`try_formats`](Self::try_formats) reports the
46    /// failure instead.
47    #[track_caller]
48    pub fn formats(&self) -> Rc<[DataFormat]> {
49        match self.try_formats() {
50            Ok(formats) => formats,
51            Err(error) => panic!("{error}"),
52        }
53    }
54
55    /// Gets the formats supported by the data transfer, or the failure of
56    /// the backend providing them.
57    pub fn try_formats(&self) -> Result<Rc<[DataFormat]>, ClipboardError> {
58        if let Some(formats) = self.formats.borrow().as_ref() {
59            return Ok(formats.clone());
60        }
61
62        let formats: Rc<[DataFormat]> = Rc::from(self.platform_impl.provide_formats()?);
63        *self.formats.borrow_mut() = Some(formats.clone());
64        Ok(formats)
65    }
66
67    /// Whether the formats have been provided by the backend.
68    pub fn are_formats_initialized(&self) -> bool {
69        self.formats.borrow().is_some()
70    }
71
72    /// Gets the items of the data transfer.
73    ///
74    /// # Panics
75    /// Panics if the backend fails to provide them (where the reference
76    /// throws from the property); [`try_items`](Self::try_items) reports the
77    /// failure instead.
78    #[track_caller]
79    pub fn items(&self) -> Rc<[Rc<dyn IAsyncDataTransferItem>]> {
80        match self.try_items() {
81            Ok(items) => items,
82            Err(error) => panic!("{error}"),
83        }
84    }
85
86    /// Gets the items of the data transfer, or the failure of the backend
87    /// providing them.
88    pub fn try_items(&self) -> Result<Rc<[Rc<dyn IAsyncDataTransferItem>]>, ClipboardError> {
89        if let Some(items) = self.items.borrow().as_ref() {
90            return Ok(items.clone());
91        }
92
93        let items: Rc<[Rc<dyn IAsyncDataTransferItem>]> = Rc::from(self.platform_impl.provide_items()?);
94        *self.items.borrow_mut() = Some(items.clone());
95        Ok(items)
96    }
97
98    /// Whether the items have been provided by the backend.
99    pub fn are_items_initialized(&self) -> bool {
100        self.items.borrow().is_some()
101    }
102}
103
104impl IDisposable for PlatformAsyncDataTransfer {
105    fn dispose(&self) {
106        self.platform_impl.dispose(self)
107    }
108}
109
110impl IAsyncDataTransfer for PlatformAsyncDataTransfer {
111    fn formats(&self) -> Rc<[DataFormat]> {
112        PlatformAsyncDataTransfer::formats(self)
113    }
114
115    fn items(&self) -> Rc<[Rc<dyn IAsyncDataTransferItem>]> {
116        PlatformAsyncDataTransfer::items(self)
117    }
118
119    fn try_formats(&self) -> Result<Rc<[DataFormat]>, ClipboardError> {
120        PlatformAsyncDataTransfer::try_formats(self)
121    }
122
123    fn try_items(&self) -> Result<Rc<[Rc<dyn IAsyncDataTransferItem>]>, ClipboardError> {
124        PlatformAsyncDataTransfer::try_items(self)
125    }
126}