Skip to main content

ferroui_base/input/platform/
platform_data_transfer.rs

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