ferroui_xaml_interactions/
action_task.rs1use ferroui_base::reactive::IDisposable;
13use ferroui_base::threading::{Dispatcher, DispatcherPriority, DispatcherTask, DispatcherTimer, OperationCanceledError};
14use std::cell::RefCell;
15use std::fmt;
16use std::future::Future;
17use std::pin::Pin;
18use std::rc::Rc;
19use std::task::{Context, Poll, Waker};
20use std::time::Duration;
21
22pub struct TaskHandle<T: 'static> {
27 task: DispatcherTask<T>,
28 identity: Rc<()>,
29}
30
31pub type ActionTask = TaskHandle<()>;
33
34impl<T: 'static> TaskHandle<T> {
35 pub fn spawn<Fut>(future: Fut) -> Self
42 where
43 Fut: Future<Output = T> + 'static,
44 {
45 Self::from_task(Dispatcher::ui_thread().invoke_async_task_local(move || future))
46 }
47
48 pub fn from_task(task: DispatcherTask<T>) -> Self {
50 Self { task, identity: Rc::new(()) }
51 }
52
53 pub fn task(&self) -> &DispatcherTask<T> {
55 &self.task
56 }
57
58 pub fn is_completed(&self) -> bool {
60 self.task.is_completed()
61 }
62
63 pub fn is_completed_successfully(&self) -> bool {
65 self.task.is_completed_successfully()
66 }
67
68 pub fn is_faulted(&self) -> bool {
70 self.task.is_faulted()
71 }
72
73 pub fn is_canceled(&self) -> bool {
75 self.task.is_canceled()
76 }
77}
78
79impl ActionTask {
80 pub fn completed() -> Self {
82 Self::spawn(std::future::ready(()))
83 }
84}
85
86impl<T: 'static> Clone for TaskHandle<T> {
87 fn clone(&self) -> Self {
88 Self { task: self.task.clone(), identity: self.identity.clone() }
89 }
90}
91
92impl<T: 'static> PartialEq for TaskHandle<T> {
93 fn eq(&self, other: &Self) -> bool {
94 Rc::ptr_eq(&self.identity, &other.identity)
95 }
96}
97
98impl<T: 'static> fmt::Debug for TaskHandle<T> {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 write!(f, "TaskHandle({:?})", self.task)
101 }
102}
103
104impl<T: 'static> Future for TaskHandle<T> {
105 type Output = Result<T, OperationCanceledError>;
106
107 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
108 Pin::new(&mut self.get_mut().task).poll(cx)
109 }
110}
111
112#[derive(Default)]
114struct DelayState {
115 elapsed: bool,
116 waker: Option<Waker>,
117 timer: Option<Rc<dyn IDisposable>>,
118}
119
120pub struct Delay {
122 duration: Duration,
123 state: Rc<RefCell<DelayState>>,
124}
125
126pub fn delay(duration: Duration) -> Delay {
129 Delay { duration, state: Rc::new(RefCell::new(DelayState::default())) }
130}
131
132impl Future for Delay {
133 type Output = ();
134
135 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
136 let this = self.get_mut();
137 if this.duration.is_zero() {
138 return Poll::Ready(());
139 }
140
141 let mut state = this.state.borrow_mut();
142 if state.elapsed {
143 return Poll::Ready(());
144 }
145
146 state.waker = Some(cx.waker().clone());
147 if state.timer.is_none() {
148 let shared = Rc::downgrade(&this.state);
149 state.timer = Some(DispatcherTimer::run_once(
150 move || {
151 let Some(shared) = shared.upgrade() else {
152 return;
153 };
154 let waker = {
155 let mut state = shared.borrow_mut();
156 state.elapsed = true;
157 state.waker.take()
158 };
159 if let Some(waker) = waker {
160 waker.wake();
161 }
162 },
163 this.duration,
164 DispatcherPriority::BACKGROUND,
165 ));
166 }
167
168 Poll::Pending
169 }
170}
171
172impl Drop for Delay {
173 fn drop(&mut self) {
174 let timer = self.state.borrow_mut().timer.take();
175 if let Some(timer) = timer {
176 timer.dispose();
177 }
178 }
179}
180
181pub(crate) struct CatchUnwind<F> {
185 future: Pin<Box<F>>,
186}
187
188impl<F: Future> CatchUnwind<F> {
189 pub(crate) fn new(future: F) -> Self {
190 Self { future: Box::pin(future) }
191 }
192}
193
194impl<F: Future> Future for CatchUnwind<F> {
195 type Output = Option<F::Output>;
196
197 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
198 let this = self.get_mut();
199 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| this.future.as_mut().poll(cx))) {
200 Ok(Poll::Ready(output)) => Poll::Ready(Some(output)),
201 Ok(Poll::Pending) => Poll::Pending,
202 Err(_) => Poll::Ready(None),
203 }
204 }
205}