dioxus_spring/
controller.rs

1use futures::Future;
2use slotmap::{DefaultKey, SlotMap};
3use std::{
4    cell::RefCell,
5    pin::Pin,
6    rc::Rc,
7    task::{Context, Poll, Waker},
8};
9use wasm_bindgen::{prelude::Closure, JsCast};
10
11pub async fn request_animation_frame() {
12    RequestFuture { key: None }.await
13}
14
15#[derive(Default)]
16struct Control {
17    wakers: SlotMap<DefaultKey, Waker>,
18    pending: Option<Rc<Closure<dyn FnMut()>>>,
19}
20
21thread_local! {
22    static CONTROL: RefCell<Option<Control>> = RefCell::new(None);
23}
24
25struct RequestFuture {
26    key: Option<DefaultKey>,
27}
28
29impl Future for RequestFuture {
30    type Output = ();
31
32    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
33        CONTROL
34            .try_with(|cell| {
35                if let Some(key) = self.key {
36                    let maybe_controller = cell.borrow();
37                    let controller = maybe_controller.as_ref().unwrap();
38                    if controller.wakers.get(key).is_none() {
39                        Poll::Ready(())
40                    } else {
41                        Poll::Pending
42                    }
43                } else {
44                    let mut maybe_controller = cell.borrow_mut();
45                    if maybe_controller.is_none() {
46                        *maybe_controller = Some(Control::default());
47                    }
48                    let controller = maybe_controller.as_mut().unwrap();
49
50                    let key = controller.wakers.insert(cx.waker().clone());
51                    self.key = Some(key);
52
53                    if controller.pending.is_none() {
54                        let f: Closure<dyn FnMut()> = Closure::new(move || {
55                            CONTROL
56                                .try_with(|cell| {
57                                    let mut maybe_controller = cell.borrow_mut();
58                                    let controller = maybe_controller.as_mut().unwrap();
59
60                                    for waker in controller.wakers.values() {
61                                        waker.wake_by_ref();
62                                    }
63                                    controller.wakers.clear();
64                                    controller.pending.take();
65                                })
66                                .unwrap();
67                        });
68
69                        web_sys::window()
70                            .unwrap()
71                            .request_animation_frame(f.as_ref().as_ref().unchecked_ref())
72                            .unwrap();
73                        controller.pending = Some(Rc::new(f));
74                    }
75
76                    Poll::Pending
77                }
78            })
79            .unwrap()
80    }
81}