use super::platform::ClipboardError;
use super::{DataFormat, DataFormatOf, IAsyncDataTransferItem, IDataTransferItem, LocalBoxFuture};
use crate::media::imaging::Bitmap;
use crate::platform::storage::IStorageItem;
use std::any::Any;
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Clone)]
enum DataAccessor {
Value(Rc<dyn Any>),
Lazy(Rc<dyn Fn() -> Option<Rc<dyn Any>>>),
}
impl DataAccessor {
fn get_value(&self) -> Option<Rc<dyn Any>> {
match self {
DataAccessor::Value(value) => Some(value.clone()),
DataAccessor::Lazy(get_value) => get_value(),
}
}
}
pub struct DataTransferItem {
accessors: RefCell<Vec<(DataFormat, DataAccessor)>>,
formats: RefCell<Option<Rc<[DataFormat]>>>,
}
impl DataTransferItem {
pub fn new() -> Rc<DataTransferItem> {
Rc::new(DataTransferItem { accessors: RefCell::new(Vec::new()), formats: RefCell::new(None) })
}
pub fn formats(&self) -> Rc<[DataFormat]> {
if let Some(formats) = self.formats.borrow().as_ref() {
return formats.clone();
}
let formats: Rc<[DataFormat]> = self.accessors.borrow().iter().map(|(format, _)| format.clone()).collect();
*self.formats.borrow_mut() = Some(formats.clone());
formats
}
pub fn contains(&self, format: &DataFormat) -> bool {
self.accessors.borrow().iter().any(|(key, _)| key == format)
}
pub fn try_get_raw(&self, format: &DataFormat) -> Option<Rc<dyn Any>> {
let accessor = self.find_accessor(format)?;
accessor.get_value()
}
pub fn try_get_value<T: Clone + 'static>(&self, format: &DataFormatOf<T>) -> Option<T> {
self.try_get_raw(format).and_then(|value| value.downcast_ref::<T>().cloned())
}
fn find_accessor(&self, format: &DataFormat) -> Option<DataAccessor> {
self.accessors.borrow().iter().find(|(key, _)| key == format).map(|(_, accessor)| accessor.clone())
}
pub fn set<T: 'static>(&self, format: &DataFormatOf<T>, value: Option<T>) {
match value {
None => self.remove_core(format),
Some(value) => self.set_core(format, DataAccessor::Value(Rc::new(value))),
}
}
pub fn set_with<T: 'static>(&self, format: &DataFormatOf<T>, get_value: impl Fn() -> Option<T> + 'static) {
self.set_core(
format,
DataAccessor::Lazy(Rc::new(move || get_value().map(|value| Rc::new(value) as Rc<dyn Any>))),
);
}
fn set_core(&self, format: &DataFormat, accessor: DataAccessor) {
let old = {
let mut accessors = self.accessors.borrow_mut();
match accessors.iter_mut().find(|(key, _)| key == format) {
Some(entry) => Some(std::mem::replace(&mut entry.1, accessor)),
None => {
accessors.push((format.clone(), accessor));
None
}
}
};
drop(old);
*self.formats.borrow_mut() = None;
}
fn remove_core(&self, format: &DataFormat) {
let removed = {
let mut accessors = self.accessors.borrow_mut();
accessors.iter().position(|(key, _)| key == format).map(|index| accessors.remove(index))
};
if removed.is_some() {
*self.formats.borrow_mut() = None;
}
}
pub fn set_text(&self, value: Option<&str>) {
self.set(&DataFormat::text(), value.map(str::to_string))
}
pub fn set_file(&self, value: Option<Rc<dyn IStorageItem>>) {
self.set(&DataFormat::file(), value)
}
pub fn set_bitmap(&self, value: Option<Rc<Bitmap>>) {
self.set(&DataFormat::bitmap(), value)
}
pub fn create<T: 'static>(format: &DataFormatOf<T>, value: Option<T>) -> Rc<DataTransferItem> {
let item = DataTransferItem::new();
item.set(format, value);
item
}
pub fn create_with<T: 'static>(
format: &DataFormatOf<T>,
get_value: impl Fn() -> Option<T> + 'static,
) -> Rc<DataTransferItem> {
let item = DataTransferItem::new();
item.set_with(format, get_value);
item
}
pub fn create_text(value: Option<&str>) -> Rc<DataTransferItem> {
Self::create(&DataFormat::text(), value.map(str::to_string))
}
pub fn create_file(value: Option<Rc<dyn IStorageItem>>) -> Rc<DataTransferItem> {
Self::create(&DataFormat::file(), value)
}
}
impl IDataTransferItem for DataTransferItem {
fn formats(&self) -> Rc<[DataFormat]> {
DataTransferItem::formats(self)
}
fn try_get_raw(&self, format: &DataFormat) -> Option<Rc<dyn Any>> {
DataTransferItem::try_get_raw(self, format)
}
}
impl IAsyncDataTransferItem for DataTransferItem {
fn formats(&self) -> Rc<[DataFormat]> {
DataTransferItem::formats(self)
}
fn try_get_raw_async(&self, format: &DataFormat) -> LocalBoxFuture<Result<Option<Rc<dyn Any>>, ClipboardError>> {
Box::pin(std::future::ready(Ok(DataTransferItem::try_get_raw(self, format))))
}
}