Skip to main content

ferroui_base/input/
data_transfer_item.rs

1use super::platform::ClipboardError;
2use super::{DataFormat, DataFormatOf, IAsyncDataTransferItem, IDataTransferItem, LocalBoxFuture};
3use crate::media::imaging::Bitmap;
4use crate::platform::storage::IStorageItem;
5use std::any::Any;
6use std::cell::RefCell;
7use std::rc::Rc;
8
9#[derive(Clone)]
10enum DataAccessor {
11    Value(Rc<dyn Any>),
12    Lazy(Rc<dyn Fn() -> Option<Rc<dyn Any>>>),
13}
14
15impl DataAccessor {
16    fn get_value(&self) -> Option<Rc<dyn Any>> {
17        match self {
18            DataAccessor::Value(value) => Some(value.clone()),
19            DataAccessor::Lazy(get_value) => get_value(),
20        }
21    }
22}
23
24/// A mutable implementation of [`IDataTransferItem`] and
25/// [`IAsyncDataTransferItem`]. This type also provides several functions to
26/// easily create a `DataTransferItem` for common usages.
27///
28/// While it also implements [`IAsyncDataTransferItem`], this type always
29/// returns data synchronously. For advanced usages, consider implementing
30/// [`IAsyncDataTransferItem`] directly.
31pub struct DataTransferItem {
32    // Kept in the order the formats were first set; the common case is a
33    // single format.
34    accessors: RefCell<Vec<(DataFormat, DataAccessor)>>,
35    formats: RefCell<Option<Rc<[DataFormat]>>>,
36}
37
38impl DataTransferItem {
39    /// Creates an item without any format.
40    pub fn new() -> Rc<DataTransferItem> {
41        Rc::new(DataTransferItem { accessors: RefCell::new(Vec::new()), formats: RefCell::new(None) })
42    }
43
44    /// Gets the formats supported by this item.
45    pub fn formats(&self) -> Rc<[DataFormat]> {
46        if let Some(formats) = self.formats.borrow().as_ref() {
47            return formats.clone();
48        }
49
50        let formats: Rc<[DataFormat]> = self.accessors.borrow().iter().map(|(format, _)| format.clone()).collect();
51        *self.formats.borrow_mut() = Some(formats.clone());
52        formats
53    }
54
55    /// Gets whether the item supports a specific format.
56    pub fn contains(&self, format: &DataFormat) -> bool {
57        self.accessors.borrow().iter().any(|(key, _)| key == format)
58    }
59
60    /// Tries to get a value for a given format. Returns `None` if the
61    /// format is not supported.
62    pub fn try_get_raw(&self, format: &DataFormat) -> Option<Rc<dyn Any>> {
63        // The accessor runs user code: it is cloned out of the borrow first.
64        let accessor = self.find_accessor(format)?;
65        accessor.get_value()
66    }
67
68    /// Tries to get a typed value for a given format.
69    pub fn try_get_value<T: Clone + 'static>(&self, format: &DataFormatOf<T>) -> Option<T> {
70        self.try_get_raw(format).and_then(|value| value.downcast_ref::<T>().cloned())
71    }
72
73    fn find_accessor(&self, format: &DataFormat) -> Option<DataAccessor> {
74        self.accessors.borrow().iter().find(|(key, _)| key == format).map(|(_, accessor)| accessor.clone())
75    }
76
77    /// Sets the value for a given format.
78    ///
79    /// If `value` is `None`, the format won't be part of the item.
80    pub fn set<T: 'static>(&self, format: &DataFormatOf<T>, value: Option<T>) {
81        match value {
82            None => self.remove_core(format),
83            Some(value) => self.set_core(format, DataAccessor::Value(Rc::new(value))),
84        }
85    }
86
87    /// Sets a value created on demand for a given format.
88    pub fn set_with<T: 'static>(&self, format: &DataFormatOf<T>, get_value: impl Fn() -> Option<T> + 'static) {
89        self.set_core(
90            format,
91            DataAccessor::Lazy(Rc::new(move || get_value().map(|value| Rc::new(value) as Rc<dyn Any>))),
92        );
93    }
94
95    fn set_core(&self, format: &DataFormat, accessor: DataAccessor) {
96        let old = {
97            let mut accessors = self.accessors.borrow_mut();
98            match accessors.iter_mut().find(|(key, _)| key == format) {
99                Some(entry) => Some(std::mem::replace(&mut entry.1, accessor)),
100                None => {
101                    accessors.push((format.clone(), accessor));
102                    None
103                }
104            }
105        };
106        // Dropped outside of the borrow: the old value may run user code.
107        drop(old);
108
109        *self.formats.borrow_mut() = None;
110    }
111
112    fn remove_core(&self, format: &DataFormat) {
113        let removed = {
114            let mut accessors = self.accessors.borrow_mut();
115            accessors.iter().position(|(key, _)| key == format).map(|index| accessors.remove(index))
116        };
117
118        if removed.is_some() {
119            *self.formats.borrow_mut() = None;
120        }
121    }
122
123    /// Sets the value for the [`DataFormat::text`] format.
124    ///
125    /// If `value` is `None`, the format won't be part of the item.
126    pub fn set_text(&self, value: Option<&str>) {
127        self.set(&DataFormat::text(), value.map(str::to_string))
128    }
129
130    /// Sets the value for the [`DataFormat::file`] format.
131    ///
132    /// If `value` is `None`, the format won't be part of the item.
133    pub fn set_file(&self, value: Option<Rc<dyn IStorageItem>>) {
134        self.set(&DataFormat::file(), value)
135    }
136
137    /// Sets the value for the [`DataFormat::bitmap`] format.
138    ///
139    /// If `value` is `None`, the format won't be part of the item.
140    pub fn set_bitmap(&self, value: Option<Rc<Bitmap>>) {
141        self.set(&DataFormat::bitmap(), value)
142    }
143
144    /// Creates a new item for a single format with a given value.
145    ///
146    /// If `value` is `None`, the format won't be part of the item.
147    pub fn create<T: 'static>(format: &DataFormatOf<T>, value: Option<T>) -> Rc<DataTransferItem> {
148        let item = DataTransferItem::new();
149        item.set(format, value);
150        item
151    }
152
153    /// Creates a new item for a single format with a given value created on
154    /// demand.
155    pub fn create_with<T: 'static>(
156        format: &DataFormatOf<T>,
157        get_value: impl Fn() -> Option<T> + 'static,
158    ) -> Rc<DataTransferItem> {
159        let item = DataTransferItem::new();
160        item.set_with(format, get_value);
161        item
162    }
163
164    /// Creates a new item with [`DataFormat::text`] as a single format.
165    ///
166    /// If `value` is `None`, the format won't be part of the item.
167    pub fn create_text(value: Option<&str>) -> Rc<DataTransferItem> {
168        Self::create(&DataFormat::text(), value.map(str::to_string))
169    }
170
171    /// Creates a new item with [`DataFormat::file`] as a single format.
172    ///
173    /// If `value` is `None`, the format won't be part of the item.
174    pub fn create_file(value: Option<Rc<dyn IStorageItem>>) -> Rc<DataTransferItem> {
175        Self::create(&DataFormat::file(), value)
176    }
177}
178
179impl IDataTransferItem for DataTransferItem {
180    fn formats(&self) -> Rc<[DataFormat]> {
181        DataTransferItem::formats(self)
182    }
183
184    fn try_get_raw(&self, format: &DataFormat) -> Option<Rc<dyn Any>> {
185        DataTransferItem::try_get_raw(self, format)
186    }
187}
188
189impl IAsyncDataTransferItem for DataTransferItem {
190    fn formats(&self) -> Rc<[DataFormat]> {
191        DataTransferItem::formats(self)
192    }
193
194    fn try_get_raw_async(&self, format: &DataFormat) -> LocalBoxFuture<Result<Option<Rc<dyn Any>>, ClipboardError>> {
195        Box::pin(std::future::ready(Ok(DataTransferItem::try_get_raw(self, format))))
196    }
197}