concoct/
vdom.rs

1use crate::{macros::trace, Runtime, Tree};
2use slotmap::Key;
3use std::{
4    task::{Poll, Waker},
5    time::{Duration, Instant},
6};
7
8/// A virtual dom that renders a view on any backend.
9pub struct VirtualDom<T> {
10    cx: Runtime,
11    tree: T,
12}
13
14impl<T> VirtualDom<T> {
15    /// Create a new virtual dom from a tree.
16    pub fn new(tree: T) -> Self {
17        VirtualDom {
18            cx: Runtime::default(),
19            tree,
20        }
21    }
22
23    /// Build the initial virtual dom.
24    pub fn build(&mut self)
25    where
26        T: Tree,
27    {
28        self.cx.enter();
29
30        // Safety: Context is dropped when the tree is
31        unsafe { self.tree.build() }
32    }
33
34    /// Rebuild the virtual dom.
35    pub async fn rebuild(&mut self) {
36        futures::future::poll_fn(|cx| {
37            self.try_rebuild_with_limit_inner(None, Some(cx.waker().clone()));
38
39            Poll::Pending
40        })
41        .await
42    }
43
44    pub async fn rebuild_with_limit(&mut self, limit: Duration) {
45        futures::future::poll_fn(|cx| {
46            let instant = Instant::now() + limit;
47            self.try_rebuild_with_limit_inner(Some(instant), Some(cx.waker().clone()));
48            Poll::Pending
49        })
50        .await
51    }
52
53    pub fn try_rebuild(&mut self) {
54        self.try_rebuild_with_limit_inner(None, None)
55    }
56
57    pub fn try_rebuild_with_limit(&mut self, limit: Duration) {
58        let instant = Instant::now() + limit;
59        self.try_rebuild_with_limit_inner(Some(instant), None)
60    }
61
62    fn try_rebuild_with_limit_inner(&mut self, limit: Option<Instant>, waker: Option<Waker>) {
63        let mut inner = self.cx.inner.borrow_mut();
64        inner.limit = limit;
65        inner.waker = waker;
66
67        if let Some(key) = inner.pending.pop_front() {
68            trace!("Received rebuild event for {:?}", key.data());
69
70            if let Some(raw) = inner.nodes.get(key).copied() {
71                drop(inner);
72
73                self.cx.enter();
74
75                // Safety: `raw` is guaranteed to be an `&mut dyn Tree`.
76                unsafe { (&mut *raw).build() };
77            }
78        }
79    }
80}