virtual_dom.rs 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. // use crate::{changelist::EditList, nodes::VNode};
  2. use crate::{dodriodiff::DiffMachine, nodes::VNode};
  3. use crate::{events::EventTrigger, innerlude::*};
  4. use any::Any;
  5. use bumpalo::Bump;
  6. use generational_arena::{Arena, Index};
  7. use std::{
  8. any::{self, TypeId},
  9. borrow::BorrowMut,
  10. cell::{RefCell, UnsafeCell},
  11. collections::{vec_deque, VecDeque},
  12. future::Future,
  13. marker::PhantomData,
  14. rc::Rc,
  15. sync::atomic::AtomicUsize,
  16. };
  17. /// An integrated virtual node system that progresses events and diffs UI trees.
  18. /// Differences are converted into patches which a renderer can use to draw the UI.
  19. pub struct VirtualDom {
  20. // pub struct VirtualDom<P: Properties> {
  21. /// All mounted components are arena allocated to make additions, removals, and references easy to work with
  22. /// A generational arean is used to re-use slots of deleted scopes without having to resize the underlying arena.
  23. pub(crate) components: Arena<Scope>,
  24. /// The index of the root component.
  25. base_scope: Index,
  26. event_queue: Rc<RefCell<VecDeque<LifecycleEvent>>>,
  27. // Mark the root props with P, even though they're held by the root component
  28. // This is done so we don't have a "generic" vdom, making it easier to hold references to it, especially when the holders
  29. // don't care about the generic props type
  30. // Most implementations that use the VirtualDom won't care about the root props anyways.
  31. #[doc(hidden)]
  32. _root_prop_type: std::any::TypeId,
  33. }
  34. impl VirtualDom {
  35. /// Create a new instance of the Dioxus Virtual Dom with no properties for the root component.
  36. ///
  37. /// This means that the root component must either consumes its own context, or statics are used to generate the page.
  38. /// The root component can access things like routing in its context.
  39. pub fn new(root: FC<()>) -> Self {
  40. Self::new_with_props(root, ())
  41. }
  42. /// Start a new VirtualDom instance with a dependent props.
  43. /// Later, the props can be updated by calling "update" with a new set of props, causing a set of re-renders.
  44. ///
  45. /// This is useful when a component tree can be driven by external state (IE SSR) but it would be too expensive
  46. /// to toss out the entire tree.
  47. pub fn new_with_props<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self {
  48. // 1. Create the component arena
  49. // 2. Create the base scope (can never be removed)
  50. // 3. Create the lifecycle queue
  51. // 4. Create the event queue
  52. todo!();
  53. // Arena allocate all the components
  54. // This should make it *really* easy to store references in events and such
  55. // let mut components = Arena::new();
  56. // Create a reference to the component in the arena
  57. // let base_scope = components.insert(Scope::new(root, None));
  58. // // Create a new mount event with no root container
  59. // let first_event = LifecycleEvent::mount(base_scope, None, 0, root_props);
  60. // // Create an event queue with a mount for the base scope
  61. // let event_queue = Rc::new(RefCell::new(vec![first_event].into_iter().collect()));
  62. // let _root_prop_type = TypeId::of::<P>();
  63. // Self {
  64. // components,
  65. // base_scope,
  66. // event_queue,
  67. // _root_prop_type,
  68. // }
  69. }
  70. /// With access to the virtual dom, schedule an update to the Root component's props
  71. pub fn update_props<P: Properties + 'static>(&mut self, new_props: P) -> Result<()> {
  72. // Ensure the props match
  73. if TypeId::of::<P>() != self._root_prop_type {
  74. return Err(Error::WrongProps);
  75. }
  76. self.event_queue
  77. .as_ref()
  78. .borrow_mut()
  79. .push_back(LifecycleEvent {
  80. event_type: LifecycleType::PropsChanged {
  81. props: Box::new(new_props),
  82. },
  83. component_index: self.base_scope,
  84. });
  85. Ok(())
  86. }
  87. /// Schedule a future update for a component from outside the vdom!
  88. ///
  89. /// This lets services external to the virtual dom interact directly with the component and event system.
  90. pub fn queue_update() {}
  91. /// Pop an event off the event queue and process it
  92. /// Update the root props, and progress
  93. /// Takes a bump arena to allocate into, making the diff phase as fast as possible
  94. pub fn progress(&mut self) -> Result<()> {
  95. let event = self
  96. .event_queue
  97. .as_ref()
  98. .borrow_mut()
  99. .pop_front()
  100. .ok_or(Error::NoEvent)?;
  101. self.process_event(event)
  102. }
  103. /// This method is the most sophisticated way of updating the virtual dom after an external event has been triggered.
  104. ///
  105. /// Given a synthetic event, the component that triggered the event, and the index of the callback, this runs the virtual
  106. /// dom to completion, tagging components that need updates, compressing events together, and finally emitting a single
  107. /// change list.
  108. ///
  109. /// If implementing an external renderer, this is the perfect method to combine with an async event loop that waits on
  110. /// listeners.
  111. ///
  112. /// ```ignore
  113. ///
  114. ///
  115. ///
  116. ///
  117. /// ```
  118. pub async fn progress_with_event(&mut self, evt: EventTrigger) -> Result<()> {
  119. // pub async fn progress_with_event(&mut self, evt: EventTrigger) -> Result<EditList<'_>> {
  120. let EventTrigger {
  121. component_id,
  122. listener_id,
  123. event,
  124. } = evt;
  125. let component = self
  126. .components
  127. .get(component_id)
  128. // todo: update this with a dedicated error type so implementors know what went wrong
  129. .expect("Component should exist if an event was triggered");
  130. let listener = component
  131. .listeners
  132. .get(listener_id as usize)
  133. .expect("Listener should exist if it was triggered")
  134. .as_ref();
  135. // Run the callback
  136. // This should cause internal state to progress, dumping events into the event queue
  137. // todo: integrate this with a tracing mechanism exposed to a dev tool
  138. listener();
  139. // Run through our events, tagging which Indexes are receiving updates
  140. // Prop updates take prescedence over subscription updates
  141. // Run all prop updates *first* as they will cascade into children.
  142. // *then* run the non-prop updates that were not already covered by props
  143. let mut affected_components = Vec::new();
  144. // It's essentially draining the vec, but with some dancing to release the RefMut
  145. // We also want to be able to push events into the queue from processing the event
  146. while let Some(event) = {
  147. let new_evt = self.event_queue.as_ref().borrow_mut().pop_front();
  148. new_evt
  149. } {
  150. affected_components.push(event.component_index);
  151. self.process_event(event)?;
  152. }
  153. let diff_bump = Bump::new();
  154. let diff_machine = DiffMachine::new(&diff_bump);
  155. Ok(())
  156. }
  157. pub async fn progress_completely(&mut self) -> Result<()> {
  158. Ok(())
  159. }
  160. /// Using mutable access to the Virtual Dom, progress a given lifecycle event
  161. ///
  162. ///
  163. ///
  164. ///
  165. ///
  166. ///
  167. fn process_event(
  168. &mut self,
  169. LifecycleEvent {
  170. component_index: index,
  171. event_type,
  172. }: LifecycleEvent,
  173. ) -> Result<()> {
  174. let scope = self.components.get_mut(index).ok_or(Error::NoEvent)?;
  175. match event_type {
  176. // Component needs to be mounted to the virtual dom
  177. LifecycleType::Mount { to, under, props } => {
  178. if let Some(other) = to {
  179. // mount to another component
  180. } else {
  181. // mount to the root
  182. }
  183. // let g = props.as_ref();
  184. // scope.run(g);
  185. // scope.run(runner, props, dom);
  186. }
  187. // The parent for this component generated new props and the component needs update
  188. LifecycleType::PropsChanged { props } => {
  189. //
  190. }
  191. // Component was successfully mounted to the dom
  192. LifecycleType::Mounted {} => {
  193. //
  194. }
  195. // Component was removed from the DOM
  196. // Run any destructors and cleanup for the hooks and the dump the component
  197. LifecycleType::Removed {} => {
  198. let f = self.components.remove(index);
  199. // let f = dom.components.remove(index);
  200. }
  201. // Component was messaged via the internal subscription service
  202. LifecycleType::Messaged => {
  203. //
  204. }
  205. // Event from renderer was fired with a given listener ID
  206. //
  207. LifecycleType::Callback { listener_id } => {}
  208. // Run any post-render callbacks on a component
  209. LifecycleType::Rendered => {}
  210. }
  211. Ok(())
  212. }
  213. }
  214. pub struct LifecycleEvent {
  215. pub component_index: Index,
  216. pub event_type: LifecycleType,
  217. }
  218. /// The internal lifecycle event system is managed by these
  219. /// Right now, we box the properties and but them in the enum
  220. /// Later, we could directly call the chain of children without boxing
  221. /// We could try to reuse the boxes somehow
  222. pub enum LifecycleType {
  223. Mount {
  224. to: Option<Index>,
  225. under: usize,
  226. props: Box<dyn Properties>,
  227. },
  228. PropsChanged {
  229. props: Box<dyn Properties>,
  230. },
  231. Rendered,
  232. Mounted,
  233. Removed,
  234. Messaged,
  235. Callback {
  236. listener_id: i32,
  237. },
  238. }
  239. impl LifecycleEvent {
  240. // helper method for shortcutting to the enum type
  241. // probably not necessary
  242. fn mount<P: Properties + 'static>(
  243. which: Index,
  244. to: Option<Index>,
  245. under: usize,
  246. props: P,
  247. ) -> Self {
  248. Self {
  249. component_index: which,
  250. event_type: LifecycleType::Mount {
  251. to,
  252. under,
  253. props: Box::new(props),
  254. },
  255. }
  256. }
  257. }