ferroui_native/
clipboard_impl.rs1use crate::clipboard_data_transfer::ClipboardDataTransfer;
2use crate::clipboard_read_session::ClipboardReadSession;
3use crate::data_transfer_to_frn_clipboard_data_source_wrapper::DataTransferToFrnClipboardDataSourceWrapper;
4use crate::helpers::ClipboardResultExt;
5use crate::interop::*;
6use ferroui_base::input::platform::{ClipboardError, IClipboardImpl, IOwnedClipboardImpl};
7use ferroui_base::input::{IAsyncDataTransfer, LocalBoxFuture};
8use ferroui_base::logging::LogArea;
9use ferroui_base::reactive::IDisposable;
10use ferroui_microcom::ComPtr;
11use std::cell::{Cell, RefCell};
12use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
13use std::rc::Rc;
14
15pub struct ClipboardImpl {
17 native: RefCell<Option<ComPtr<IFrnClipboard>>>,
18 last_clear_change_count: Cell<i64>,
19}
20
21fn completed<T: 'static>(
26 operation: impl FnOnce() -> Result<T, ClipboardError>,
27) -> LocalBoxFuture<Result<T, ClipboardError>> {
28 match catch_unwind(AssertUnwindSafe(operation)) {
29 Ok(value) => Box::pin(std::future::ready(value)),
30 Err(payload) => Box::pin(async move { resume_unwind(payload) }),
31 }
32}
33
34impl ClipboardImpl {
35 pub fn new(native: ComPtr<IFrnClipboard>) -> ClipboardImpl {
36 ClipboardImpl { native: RefCell::new(Some(native)), last_clear_change_count: Cell::new(i64::MIN) }
37 }
38
39 #[track_caller]
40 pub(crate) fn native(&self) -> ComPtr<IFrnClipboard> {
41 match self.native.borrow().clone() {
42 Some(native) => native,
43 None => panic!("Cannot access a disposed object: ClipboardImpl"),
44 }
45 }
46
47 fn clear_core(&self) -> Result<(), ClipboardError> {
48 self.last_clear_change_count.set(self.native().clear().clipboard_check()?);
49 Ok(())
50 }
51
52 fn try_get_data(&self) -> Result<Option<Rc<dyn IAsyncDataTransfer>>, ClipboardError> {
53 let native = self.native();
54 let change_count = native.get_change_count().clipboard_check()?;
55 let data_transfer = ClipboardDataTransfer::new(ClipboardReadSession::new(native, change_count));
56
57 if data_transfer.try_formats()?.is_empty() {
58 data_transfer.dispose();
59 return Ok(None);
60 }
61
62 Ok(Some(data_transfer))
63 }
64
65 fn set_data(&self, data_transfer: Rc<dyn IAsyncDataTransfer>) -> Result<(), ClipboardError> {
66 self.clear_core()?;
67
68 let source = IFrnClipboardDataSource::from_impl(DataTransferToFrnClipboardDataSourceWrapper::new(
69 data_transfer.to_synchronous(LogArea::MACOS_PLATFORM),
70 ));
71 self.native().set_data(Some(&source)).clipboard_check()
72 }
73
74 pub fn dispose(&self) {
76 let native = self.native.borrow_mut().take();
77 drop(native);
78 }
79}
80
81impl IClipboardImpl for ClipboardImpl {
82 fn try_get_data_async(&self) -> LocalBoxFuture<Result<Option<Rc<dyn IAsyncDataTransfer>>, ClipboardError>> {
83 completed(|| self.try_get_data())
84 }
85
86 fn set_data_async(&self, data_transfer: Rc<dyn IAsyncDataTransfer>) -> LocalBoxFuture<Result<(), ClipboardError>> {
87 completed(|| self.set_data(data_transfer))
88 }
89
90 fn clear_async(&self) -> LocalBoxFuture<Result<(), ClipboardError>> {
91 completed(|| self.clear_core())
92 }
93
94 fn as_owned_clipboard_impl(&self) -> Option<&dyn IOwnedClipboardImpl> {
95 Some(self)
96 }
97}
98
99impl IOwnedClipboardImpl for ClipboardImpl {
100 fn is_current_owner_async(&self) -> LocalBoxFuture<Result<bool, ClipboardError>> {
101 let is_current_owner = self
102 .native()
103 .get_change_count()
104 .clipboard_check()
105 .map(|change_count| change_count == self.last_clear_change_count.get());
106 Box::pin(std::future::ready(is_current_owner))
107 }
108}