diff --git a/packages/native-core/src/dioxus.rs b/packages/native-core/src/dioxus.rs index 8d67d9e2c..d5dc59e2b 100644 --- a/packages/native-core/src/dioxus.rs +++ b/packages/native-core/src/dioxus.rs @@ -77,14 +77,14 @@ impl DioxusState { } CreatePlaceholder { id } => { let node = NodeType::Placeholder; - let node = rdom.create_node(node, true); + let node = rdom.create_node(node); let node_id = node.id(); self.set_element_id(node, id); self.stack.push(node_id); } CreateTextNode { value, id } => { let node_data = NodeType::Text(value.to_string()); - let node = rdom.create_node(node_data, true); + let node = rdom.create_node(node_data); let node_id = node.id(); self.set_element_id(node, id); self.stack.push(node_id); @@ -228,20 +228,16 @@ fn create_template_node(rdom: &mut RealDom, node: &TemplateNode) -> NodeId { .collect(), listeners: FxHashSet::default(), }); - let node_id = rdom.create_node(node, true).id(); + let node_id = rdom.create_node(node).id(); for child in *children { let child_id = create_template_node(rdom, child); rdom.add_child(node_id, child_id); } node_id } - TemplateNode::Text { text } => rdom - .create_node(NodeType::Text(text.to_string()), true) - .id(), - TemplateNode::Dynamic { .. } => rdom.create_node(NodeType::Placeholder, true).id(), - TemplateNode::DynamicText { .. } => { - rdom.create_node(NodeType::Text(String::new()), true).id() - } + TemplateNode::Text { text } => rdom.create_node(NodeType::Text(text.to_string())).id(), + TemplateNode::Dynamic { .. } => rdom.create_node(NodeType::Placeholder).id(), + TemplateNode::DynamicText { .. } => rdom.create_node(NodeType::Text(String::new())).id(), } } diff --git a/packages/native-core/src/lib.rs b/packages/native-core/src/lib.rs index 5e07f68aa..05e9a2c93 100644 --- a/packages/native-core/src/lib.rs +++ b/packages/native-core/src/lib.rs @@ -13,6 +13,7 @@ pub mod dioxus; pub mod layout_attributes; pub mod node; pub mod node_ref; +pub mod node_watcher; mod passes; pub mod real_dom; pub mod tree; @@ -22,7 +23,7 @@ pub mod prelude { pub use crate::node::{ElementNode, FromAnyValue, NodeType, OwnedAttributeView}; pub use crate::node_ref::{AttributeMaskBuilder, NodeMaskBuilder, NodeView}; pub use crate::passes::{Dependancy, Pass}; - pub use crate::real_dom::{NodeImmutable, NodeMut, NodeMutRaw, NodeMutable, NodeRef, RealDom}; + pub use crate::real_dom::{NodeImmutable, NodeMut, NodeRef, RealDom}; pub use crate::tree::NodeId; pub use crate::SendAnyMap; } diff --git a/packages/native-core/src/node_watcher.rs b/packages/native-core/src/node_watcher.rs new file mode 100644 index 000000000..b9b79ebab --- /dev/null +++ b/packages/native-core/src/node_watcher.rs @@ -0,0 +1,15 @@ +use crate::{node::FromAnyValue, NodeMut}; + +/// A trait for watching for changes in the DOM tree. +pub trait NodeWatcher { + /// Called after a node is added to the tree. + fn on_node_added(&self, _node: NodeMut) {} + /// Called before a node is removed from the tree. + fn on_node_removed(&self, _node: NodeMut) {} + /// Called after a node is moved to a new parent. + fn on_node_moved(&self, _node: NodeMut) {} + // /// Called after the text content of a node is changed. + // fn on_text_changed(&self, _node: NodeMut) {} + // /// Called after an attribute of an element is changed. + // fn on_attribute_changed(&self, _node: NodeMut, attribute: &str) {} +} diff --git a/packages/native-core/src/passes.rs b/packages/native-core/src/passes.rs index 86059508a..761e4a72a 100644 --- a/packages/native-core/src/passes.rs +++ b/packages/native-core/src/passes.rs @@ -380,12 +380,12 @@ impl_dependancy!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O); impl_dependancy!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); pub fn resolve_passes( - tree: &mut RealDom, + dom: &mut RealDom, dirty_nodes: DirtyNodeStates, ctx: SendAnyMap, parallel: bool, ) -> FxDashSet { - let passes = &tree.passes; + let passes = &dom.dirty_nodes.passes; let mut resolved_passes: FxHashSet = FxHashSet::default(); let mut resolving = Vec::new(); let nodes_updated = Arc::new(FxDashSet::default()); @@ -393,7 +393,7 @@ pub fn resolve_passes( let mut pass_indexes_remaining: Vec<_> = (0..passes.len()).collect::>(); while !pass_indexes_remaining.is_empty() { let mut currently_in_use = FxHashSet::::default(); - let dynamically_borrowed_tree = tree.tree.dynamically_borrowed(); + let dynamically_borrowed_tree = dom.tree.dynamically_borrowed(); rayon::in_place_scope(|s| { let mut i = 0; while i < pass_indexes_remaining.len() { diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index 8cc4d29de..f466d2803 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -2,20 +2,22 @@ use rustc_hash::{FxHashMap, FxHashSet}; use std::any::{Any, TypeId}; use std::collections::VecDeque; use std::rc::Rc; +use std::sync::RwLock; use crate::node::{ ElementNode, FromAnyValue, NodeType, OwnedAttributeDiscription, OwnedAttributeValue, }; use crate::node_ref::{NodeMask, NodeMaskBuilder}; +use crate::node_watcher::NodeWatcher; use crate::passes::{resolve_passes, DirtyNodeStates, TypeErasedPass}; use crate::prelude::AttributeMaskBuilder; use crate::tree::{NodeId, Tree}; use crate::{FxDashSet, SendAnyMap}; -struct NodesDirty { +pub(crate) struct NodesDirty { passes_updated: FxHashMap>, nodes_updated: FxHashMap, - passes: Rc]>>, + pub(crate) passes: Box<[TypeErasedPass]>, } impl NodesDirty { @@ -35,7 +37,7 @@ impl NodesDirty { fn mark_parent_added_or_removed(&mut self, node_id: NodeId) { let hm = self.passes_updated.entry(node_id).or_default(); - for pass in &**self.passes { + for pass in &*self.passes { if pass.parent_dependant { hm.insert(pass.this_type_id); } @@ -44,7 +46,7 @@ impl NodesDirty { fn mark_child_changed(&mut self, node_id: NodeId) { let hm = self.passes_updated.entry(node_id).or_default(); - for pass in &**self.passes { + for pass in &*self.passes { if pass.child_dependant { hm.insert(pass.this_type_id); } @@ -52,6 +54,8 @@ impl NodesDirty { } } +type NodeWatchers = Rc>>>>; + /// A Dom that can sync with the VirtualDom mutations intended for use in lazy renderers. /// The render state passes from parent to children and or accumulates state from children to parents. /// To get started implement [crate::state::ParentDepState], [crate::state::NodeDepState], or [crate::state::ChildDepState] and call [RealDom::apply_mutations] to update the dom and [RealDom::update_state] to update the state of the nodes. @@ -61,8 +65,8 @@ impl NodesDirty { pub struct RealDom { pub(crate) tree: Tree, nodes_listening: FxHashMap>, - pub(crate) passes: Rc]>>, - dirty_nodes: NodesDirty, + pub(crate) dirty_nodes: NodesDirty, + node_watchers: NodeWatchers, phantom: std::marker::PhantomData, } @@ -94,7 +98,6 @@ impl RealDom { } } } - let passes = Rc::new(passes); let mut passes_updated = FxHashMap::default(); let mut nodes_updated = FxHashMap::default(); @@ -106,34 +109,32 @@ impl RealDom { RealDom { tree, nodes_listening: FxHashMap::default(), - passes: passes.clone(), dirty_nodes: NodesDirty { passes_updated, nodes_updated, passes, }, + node_watchers: Default::default(), phantom: std::marker::PhantomData, } } - pub fn create_node(&mut self, node: NodeType, mark_dirty: bool) -> NodeMut<'_, V> { + pub fn create_node(&mut self, node: NodeType) -> NodeMut<'_, V> { let mut node_entry = self.tree.create_node(); let id = node_entry.id(); - if mark_dirty { - self.dirty_nodes - .passes_updated - .entry(id) - .or_default() - .extend(self.passes.iter().map(|x| x.this_type_id)); - } + self.dirty_nodes + .passes_updated + .entry(id) + .or_default() + .extend(self.dirty_nodes.passes.iter().map(|x| x.this_type_id)); node_entry.insert(node); + let watchers = self.node_watchers.clone(); + for watcher in &*watchers.read().unwrap() { + watcher.on_node_added(NodeMut::new(id, self)); + } NodeMut::new(id, self) } - pub fn add_child(&mut self, node_id: NodeId, child_id: NodeId) { - self.tree.add_child(node_id, child_id); - } - /// Find all nodes that are listening for an event, sorted by there height in the dom progressing starting at the bottom and progressing up. /// This can be useful to avoid creating duplicate events. pub fn get_listening_sorted(&self, event: &str) -> Vec> { @@ -166,12 +167,12 @@ impl RealDom { pub fn clone_node(&mut self, node_id: NodeId) -> NodeId { let node = self.get(node_id).unwrap(); let new_node = node.node_type().clone(); - let new_id = self.create_node(new_node, true).id(); + let new_id = self.create_node(new_node).id(); let children = self.tree.children_ids(node_id).unwrap().to_vec(); for child in children { let child_id = self.clone_node(child); - self.add_child(new_id, child_id); + self.get_mut(new_id).unwrap().add_child(child_id); } new_id } @@ -187,11 +188,9 @@ impl RealDom { } /// WARNING: This escapes the reactive system that the real dom uses. Any changes made with this method will not trigger updates in states when [RealDom::update_state] is called. - pub fn get_mut_raw(&mut self, id: NodeId) -> Option> { + pub fn get_state_mut_raw(&mut self, id: NodeId) -> Option<&mut T> { let id = id.into_node_id(self); - self.tree - .contains(id) - .then_some(NodeMutRaw { id, dom: self }) + self.tree.get_mut(id) } /// Update the state of the dom, after appling some mutations. This will keep the nodes in the dom up to date with their VNode counterparts. @@ -202,7 +201,8 @@ impl RealDom { ) -> (FxDashSet, FxHashMap) { let passes = std::mem::take(&mut self.dirty_nodes.passes_updated); let nodes_updated = std::mem::take(&mut self.dirty_nodes.nodes_updated); - let dirty_nodes = DirtyNodeStates::with_passes(self.passes.iter().map(|p| p.this_type_id)); + let dirty_nodes = + DirtyNodeStates::with_passes(self.dirty_nodes.passes.iter().map(|p| p.this_type_id)); for (node_id, passes) in passes { // remove any nodes that were created and then removed in the same mutations from the dirty nodes list if let Some(height) = self.tree.height(node_id) { @@ -218,21 +218,6 @@ impl RealDom { ) } - pub fn remove(&mut self, id: NodeId) { - if let Some(parent_id) = self.tree.parent_id(id) { - self.dirty_nodes.mark_child_changed(parent_id); - } - self.tree.remove(id) - } - - pub fn replace(&mut self, old: NodeId, new: NodeId) { - if let Some(parent_id) = self.tree.parent_id(old) { - self.dirty_nodes.mark_child_changed(parent_id); - self.dirty_nodes.mark_parent_added_or_removed(new); - } - self.tree.replace(old, new); - } - pub fn traverse_depth_first(&self, mut f: impl FnMut(NodeRef)) { let mut stack = vec![self.root_id()]; while let Some(id) = stack.pop() { @@ -293,6 +278,10 @@ impl RealDom { pub fn insert_slab(&mut self) { self.tree.insert_slab::(); } + + pub fn add_node_watcher(&mut self, watcher: impl NodeWatcher + 'static) { + self.node_watchers.write().unwrap().push(Box::new(watcher)); + } } pub trait IntoNodeId { @@ -343,40 +332,52 @@ pub trait NodeImmutable: Sized { dom: self.real_dom(), }) } + + fn next(&self) -> Option> { + let parent = self.parent_id()?; + let children = self.real_dom().tree.children_ids(parent)?; + let index = children.iter().position(|id| *id == self.id())?; + if index + 1 < children.len() { + Some(NodeRef { + id: children[index + 1], + dom: self.real_dom(), + }) + } else { + None + } + } + + fn prev(&self) -> Option> { + let parent = self.parent_id()?; + let children = self.real_dom().tree.children_ids(parent)?; + let index = children.iter().position(|id| *id == self.id())?; + if index > 0 { + Some(NodeRef { + id: children[index - 1], + dom: self.real_dom(), + }) + } else { + None + } + } } -pub trait NodeMutable: Sized + NodeImmutable { - fn real_dom_mut(&mut self) -> &mut RealDom; - fn get_mut(&mut self) -> Option<&mut T> { - let id = self.id(); - self.real_dom_mut().tree.get_mut(id) - } - fn insert(&mut self, value: T) { - let id = self.id(); - self.real_dom_mut().tree.insert(id, value); - } - fn add_child(&mut self, child: NodeId) { - let id = self.id(); - self.real_dom_mut().tree.add_child(id, child); - } - fn insert_after(&mut self, old: NodeId) { - let id = self.id(); - self.real_dom_mut().tree.insert_after(old, id); - } - fn insert_before(&mut self, old: NodeId) { - let id = self.id(); - self.real_dom_mut().tree.insert_before(old, id); - } - fn add_event_listener(&mut self, event: &str); - fn remove_event_listener(&mut self, event: &str); -} - -#[derive(Clone, Copy)] pub struct NodeRef<'a, V: FromAnyValue + Send + Sync = ()> { id: NodeId, dom: &'a RealDom, } +impl<'a, V: FromAnyValue + Send + Sync> Clone for NodeRef<'a, V> { + fn clone(&self) -> Self { + Self { + id: self.id, + dom: self.dom, + } + } +} + +impl<'a, V: FromAnyValue + Send + Sync> Copy for NodeRef<'a, V> {} + impl<'a, V: FromAnyValue + Send + Sync> NodeImmutable for NodeRef<'a, V> { fn real_dom(&self) -> &RealDom { self.dom @@ -408,12 +409,12 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeImmutable for NodeMut<'a, V> { } } -impl<'a, V: FromAnyValue + Send + Sync> NodeMutable for NodeMut<'a, V> { - fn real_dom_mut(&mut self) -> &mut RealDom { +impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> { + pub fn real_dom_mut(&mut self) -> &mut RealDom { self.dom } - fn get_mut(&mut self) -> Option<&mut T> { + pub fn get_mut(&mut self) -> Option<&mut T> { // mark the node state as dirty self.dom .dirty_nodes @@ -424,7 +425,7 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMutable for NodeMut<'a, V> { self.dom.tree.get_mut(self.id) } - fn insert(&mut self, value: T) { + pub fn insert(&mut self, value: T) { // mark the node state as dirty self.dom .dirty_nodes @@ -435,25 +436,87 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMutable for NodeMut<'a, V> { self.dom.tree.insert(self.id, value); } - fn insert_after(&mut self, old: NodeId) { + pub fn next_mut(self) -> Option> { + let parent = self.parent_id()?; + let children = self.dom.tree.children_ids(parent)?; + let index = children.iter().position(|id| *id == self.id)?; + if index + 1 < children.len() { + Some(NodeMut::new(children[index + 1], self.dom)) + } else { + None + } + } + + pub fn prev_mut(self) -> Option> { + let parent = self.parent_id()?; + let children = self.dom.tree.children_ids(parent)?; + let index = children.iter().position(|id| *id == self.id)?; + if index > 0 { + Some(NodeMut::new(children[index - 1], self.dom)) + } else { + None + } + } + + pub fn add_child(&mut self, child: NodeId) { + self.dom.dirty_nodes.mark_child_changed(self.id); + self.dom.dirty_nodes.mark_parent_added_or_removed(child); + self.dom.tree.add_child(self.id, child); + NodeMut::new(child, self.dom).mark_moved(); + } + + pub fn insert_after(&mut self, old: NodeId) { let id = self.id(); if let Some(parent_id) = self.dom.tree.parent_id(old) { self.dom.dirty_nodes.mark_child_changed(parent_id); self.dom.dirty_nodes.mark_parent_added_or_removed(id); } self.dom.tree.insert_after(old, id); + self.mark_moved(); } - fn insert_before(&mut self, old: NodeId) { + pub fn insert_before(&mut self, old: NodeId) { let id = self.id(); if let Some(parent_id) = self.dom.tree.parent_id(old) { self.dom.dirty_nodes.mark_child_changed(parent_id); self.dom.dirty_nodes.mark_parent_added_or_removed(id); } self.dom.tree.insert_before(old, id); + self.mark_moved(); } - fn add_event_listener(&mut self, event: &str) { + pub fn remove(&mut self) { + let id = self.id(); + self.mark_removed(); + if let Some(parent_id) = self.real_dom_mut().tree.parent_id(id) { + self.real_dom_mut() + .dirty_nodes + .mark_child_changed(parent_id); + } + if let Some(children_ids) = self.child_ids() { + let children_ids_vec = children_ids.to_vec(); + for child in children_ids_vec { + self.dom.get_mut(child).unwrap().remove(); + } + } + self.dom.tree.remove_single(id); + } + + pub fn replace(&mut self, new: NodeId) { + self.mark_removed(); + if let Some(parent_id) = self.parent_id() { + self.real_dom_mut() + .dirty_nodes + .mark_child_changed(parent_id); + self.real_dom_mut() + .dirty_nodes + .mark_parent_added_or_removed(new); + } + let id = self.id(); + self.dom.tree.replace(id, new); + } + + pub fn add_event_listener(&mut self, event: &str) { let id = self.id(); let node_type: &mut NodeType = self.dom.tree.get_mut(self.id).unwrap(); if let NodeType::Element(ElementNode { listeners, .. }) = node_type { @@ -474,7 +537,7 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMutable for NodeMut<'a, V> { } } - fn remove_event_listener(&mut self, event: &str) { + pub fn remove_event_listener(&mut self, event: &str) { let id = self.id(); let node_type: &mut NodeType = self.dom.tree.get_mut(self.id).unwrap(); if let NodeType::Element(ElementNode { listeners, .. }) = node_type { @@ -486,9 +549,21 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMutable for NodeMut<'a, V> { self.dom.nodes_listening.get_mut(event).unwrap().remove(&id); } } -} -impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> { + fn mark_removed(&mut self) { + let watchers = self.dom.node_watchers.clone(); + for watcher in &*watchers.read().unwrap() { + watcher.on_node_removed(NodeMut::new(self.id(), self.dom)); + } + } + + fn mark_moved(&mut self) { + let watchers = self.dom.node_watchers.clone(); + for watcher in &*watchers.read().unwrap() { + watcher.on_node_moved(NodeMut::new(self.id(), self.dom)); + } + } + pub fn node_type_mut(&mut self) -> NodeTypeMut<'_, V> { let Self { id, dom } = self; let RealDom { @@ -503,6 +578,7 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> { }), NodeType::Text(text) => { dirty_nodes.mark_dirty(self.id, NodeMaskBuilder::new().with_text().build()); + NodeTypeMut::Text(text) } NodeType::Placeholder => NodeTypeMut::Placeholder, @@ -554,18 +630,6 @@ impl ElementNodeMut<'_, V> { &self.element.attributes } - pub fn attributes_mut( - &mut self, - ) -> &mut FxHashMap> { - self.dirty_nodes.mark_dirty( - self.id, - NodeMaskBuilder::new() - .with_attrs(AttributeMaskBuilder::All) - .build(), - ); - &mut self.element.attributes - } - pub fn set_attribute( &mut self, name: OwnedAttributeDiscription, @@ -610,55 +674,3 @@ impl ElementNodeMut<'_, V> { &self.element.listeners } } - -pub struct NodeMutRaw<'a, V: FromAnyValue + Send + Sync = ()> { - id: NodeId, - dom: &'a mut RealDom, -} - -impl<'a, V: FromAnyValue + Send + Sync> NodeMutRaw<'a, V> { - fn node_type_mut(&mut self) -> &mut NodeType { - self.dom.tree.get_mut::>(self.id).unwrap() - } -} - -impl<'a, V: FromAnyValue + Send + Sync> NodeImmutable for NodeMutRaw<'a, V> { - fn real_dom(&self) -> &RealDom { - self.dom - } - - fn id(&self) -> NodeId { - self.id - } -} - -impl<'a, V: FromAnyValue + Send + Sync> NodeMutable for NodeMutRaw<'a, V> { - fn real_dom_mut(&mut self) -> &mut RealDom { - self.dom - } - - fn add_event_listener(&mut self, event: &str) { - let id = self.id(); - if let NodeType::Element(element) = self.node_type_mut() { - element.listeners.insert(event.to_string()); - match self.dom.nodes_listening.get_mut(event) { - Some(hs) => { - hs.insert(id); - } - None => { - let mut hs = FxHashSet::default(); - hs.insert(id); - self.dom.nodes_listening.insert(event.to_string(), hs); - } - } - } - } - - fn remove_event_listener(&mut self, event: &str) { - let id = self.id(); - if let NodeType::Element(element) = self.node_type_mut() { - element.listeners.remove(event); - } - self.dom.nodes_listening.get_mut(event).unwrap().remove(&id); - } -} diff --git a/packages/native-core/src/tree.rs b/packages/native-core/src/tree.rs index 390ac42b2..580dd4848 100644 --- a/packages/native-core/src/tree.rs +++ b/packages/native-core/src/tree.rs @@ -80,6 +80,18 @@ impl Tree { recurse(self, id); } + pub fn remove_single(&mut self, id: NodeId) { + { + let node_data_mut = self.node_slab_mut(); + if let Some(parent) = node_data_mut.get(id).unwrap().parent { + let parent = node_data_mut.get_mut(parent).unwrap(); + parent.children.retain(|&child| child != id); + } + } + + self.nodes.remove(id); + } + fn set_height(&mut self, node: NodeId, height: u16) { let children = { let mut node = self.get_node_data_mut(node); diff --git a/packages/native-core/src/utils/mod.rs b/packages/native-core/src/utils/mod.rs index 9ddb1a1c7..58e98b0fa 100644 --- a/packages/native-core/src/utils/mod.rs +++ b/packages/native-core/src/utils/mod.rs @@ -1,3 +1,3 @@ -// mod persistant_iterator; -// pub use persistant_iterator::*; +mod persistant_iterator; +pub use persistant_iterator::*; pub mod cursor; diff --git a/packages/native-core/src/utils/persistant_iterator.rs b/packages/native-core/src/utils/persistant_iterator.rs index eb2cd1269..b922d0f56 100644 --- a/packages/native-core/src/utils/persistant_iterator.rs +++ b/packages/native-core/src/utils/persistant_iterator.rs @@ -1,10 +1,15 @@ +use smallvec::SmallVec; + use crate::{ - node::{ElementNode, FromAnyValue, NodeType}, + node::FromAnyValue, + node_watcher::NodeWatcher, real_dom::{NodeImmutable, RealDom}, - NodeId, + NodeId, NodeRef, +}; +use std::{ + fmt::Debug, + sync::{Arc, Mutex}, }; -use dioxus_core::{Mutation, Mutations}; -use std::fmt::Debug; #[derive(Debug)] pub enum ElementProduced { @@ -23,27 +28,36 @@ impl ElementProduced { } } -#[derive(Debug)] -enum NodePosition { - AtNode, - InChild(usize), +struct PersistantElementIterUpdater { + stack: Arc>>, + phantom: std::marker::PhantomData, } -impl NodePosition { - fn map(&self, mut f: impl FnMut(usize) -> usize) -> Self { - match self { - Self::AtNode => Self::AtNode, - Self::InChild(i) => Self::InChild(f(*i)), +impl NodeWatcher for PersistantElementIterUpdater { + fn on_node_moved(&self, node: crate::NodeMut) { + // if any element is moved, update its parents in the stack + let mut stack = self.stack.lock().unwrap(); + let moved = node.id(); + let rdom = node.real_dom(); + if let Some(r) = stack.iter().position(|el_id| *el_id == moved) { + let back = &stack[r..]; + let mut new = SmallVec::new(); + let mut parent = node.parent_id(); + while let Some(p) = parent.and_then(|id| rdom.get(id)) { + new.push(p.id()); + parent = p.parent_id(); + } + new.extend(back.iter().copied()); + *stack = new; } } - fn get_or_insert(&mut self, child_idx: usize) -> usize { - match self { - Self::AtNode => { - *self = Self::InChild(child_idx); - child_idx - } - Self::InChild(i) => *i, + fn on_node_removed(&self, node: crate::NodeMut) { + // if any element is removed in the chain, remove it and its children from the stack + let mut stack = self.stack.lock().unwrap(); + let removed = node.id(); + if let Some(r) = stack.iter().position(|el_id| *el_id == removed) { + stack.truncate(r); } } } @@ -53,118 +67,47 @@ impl NodePosition { /// Iterate through it with [PersistantElementIter::next] [PersistantElementIter::prev], and update it with [PersistantElementIter::prune] (with data from [`dioxus_core::prelude::VirtualDom::work_with_deadline`]). /// The iterator loops around when it reaches the end or the beginning. pub struct PersistantElementIter { - // stack of elements and fragments - stack: smallvec::SmallVec<[(NodeId, NodePosition); 5]>, -} - -impl Default for PersistantElementIter { - fn default() -> Self { - PersistantElementIter { - stack: smallvec::smallvec![(NodeId(0), NodePosition::AtNode)], - } - } + // stack of elements and fragments, the last element is the last element that was yielded + stack: Arc>>, } impl PersistantElementIter { - pub fn new() -> Self { - Self::default() - } + pub fn create(rdom: &mut RealDom) -> Self { + let inner = Arc::new(Mutex::new(smallvec::smallvec![NodeId(0)])); - /// remove stale element refreneces - /// returns true if the focused element is removed - pub fn prune( - &mut self, - mutations: &Mutations, - rdom: &RealDom, - ) -> bool { - let mut changed = false; - let ids_removed: Vec<_> = mutations - .edits - .iter() - .filter_map(|m| { - // nodes within templates will never be removed - match m { - Mutation::Remove { id } => Some(rdom.element_to_node_id(*id)), - Mutation::ReplaceWith { id, .. } => Some(rdom.element_to_node_id(*id)), - _ => None, - } - }) - .collect(); - // if any element is removed in the chain, remove it and its children from the stack - if let Some(r) = self - .stack - .iter() - .position(|(el_id, _)| ids_removed.iter().any(|id| el_id == id)) - { - self.stack.truncate(r); - changed = true; - } - // if a child is removed or inserted before or at the current element, update the child index - for (el_id, child_idx) in self.stack.iter_mut() { - if let NodePosition::InChild(child_idx) = child_idx { - if let Some(children) = &rdom.get(*el_id).unwrap().child_ids() { - for m in &mutations.edits { - match m { - Mutation::Remove { id } => { - let id = rdom.element_to_node_id(*id); - if children.iter().take(*child_idx + 1).any(|c| *c == id) { - *child_idx -= 1; - } - } - Mutation::InsertBefore { id, m } => { - let id = rdom.element_to_node_id(*id); - if children.iter().take(*child_idx + 1).any(|c| *c == id) { - *child_idx += *m; - } - } - Mutation::InsertAfter { id, m } => { - let id = rdom.element_to_node_id(*id); - if children.iter().take(*child_idx).any(|c| *c == id) { - *child_idx += *m; - } - } - _ => (), - } - } - } - } - } - changed + rdom.add_node_watcher(PersistantElementIterUpdater { + stack: inner.clone(), + phantom: std::marker::PhantomData, + }); + + PersistantElementIter { stack: inner } } /// get the next element pub fn next(&mut self, rdom: &RealDom) -> ElementProduced { - if self.stack.is_empty() { + let mut stack = self.stack.lock().unwrap(); + if stack.is_empty() { let id = NodeId(0); - let new = (id, NodePosition::AtNode); - self.stack.push(new); + let new = id; + stack.push(new); ElementProduced::Looped(id) } else { - let (last, old_child_idx) = self.stack.last_mut().unwrap(); - let node = rdom.get(*last).unwrap(); - match node.node_type() { - NodeType::Element(ElementNode { .. }) => { - let children = node.child_ids().unwrap(); - *old_child_idx = old_child_idx.map(|i| i + 1); - // if we have children, go to the next child - let child_idx = old_child_idx.get_or_insert(0); - if child_idx >= children.len() { - self.pop(); - self.next(rdom) - } else { - let id = children[child_idx]; - if let NodeType::Element(ElementNode { .. }) = - rdom.get(id).unwrap().node_type() - { - self.stack.push((id, NodePosition::AtNode)); - } - ElementProduced::Progressed(id) + loop { + let last = stack.pop().unwrap(); + if let Some(current) = rdom.get(last) { + if let Some(new) = current.next() { + // the next element exists, add it to the stack and return it + let new = new.id(); + stack.push(new); + return ElementProduced::Progressed(new); } - } - - NodeType::Text { .. } | NodeType::Placeholder { .. } => { - // we are at a leaf, so we are done - ElementProduced::Progressed(self.pop()) + // otherwise, continue the loop and go to the parent + } else { + // if there is no parent, loop back to the root + let new = NodeId(0); + stack.clear(); + stack.push(new); + return ElementProduced::Looped(new); } } } @@ -174,68 +117,44 @@ impl PersistantElementIter { pub fn prev(&mut self, rdom: &RealDom) -> ElementProduced { // recursively add the last child element to the stack fn push_back( - stack: &mut smallvec::SmallVec<[(NodeId, NodePosition); 5]>, - new_node: NodeId, - rdom: &RealDom, + stack: &mut smallvec::SmallVec<[NodeId; 5]>, + node: NodeRef, ) -> NodeId { - let node = rdom.get(new_node).unwrap(); - match node.node_type() { - NodeType::Element(ElementNode { .. }) => { - let children = node.child_ids().unwrap(); - if children.is_empty() { - new_node - } else { - stack.push((new_node, NodePosition::InChild(children.len() - 1))); - push_back(stack, *children.last().unwrap(), rdom) - } + stack.push(node.id()); + if let Some(children) = node.children() { + if let Some(last) = children.last() { + push_back(stack, *last) + } else { + node.id() } - _ => new_node, + } else { + node.id() } } - if self.stack.is_empty() { - let new_node = NodeId(0); - ElementProduced::Looped(push_back(&mut self.stack, new_node, rdom)) + let mut stack = self.stack.lock().unwrap(); + if stack.is_empty() { + let id = NodeId(0); + let last = push_back(&mut stack, rdom.get(id).unwrap()); + ElementProduced::Looped(last) } else { - let (last, old_child_idx) = self.stack.last_mut().unwrap(); - let node = rdom.get(*last).unwrap(); - match node.node_type() { - NodeType::Element(ElementNode { .. }) => { - let children = node.child_ids().unwrap(); - // if we have children, go to the next child - if let NodePosition::InChild(0) = old_child_idx { - ElementProduced::Progressed(self.pop()) - } else { - *old_child_idx = old_child_idx.map(|i| i - 1); - if let NodePosition::InChild(child_idx) = old_child_idx { - if *child_idx >= children.len() || children.is_empty() { - self.pop(); - self.prev(rdom) - } else { - let new_node = children[*child_idx]; - ElementProduced::Progressed(push_back( - &mut self.stack, - new_node, - rdom, - )) - } - } else { - self.pop(); - self.prev(rdom) - } + loop { + let last = stack.pop().unwrap(); + if let Some(current) = rdom.get(last) { + if let Some(new) = current.prev() { + // the next element exists, add it to the stack and return it + let new = push_back(&mut stack, new); + return ElementProduced::Progressed(new); } - } - - NodeType::Text { .. } | NodeType::Placeholder { .. } => { - // we are at a leaf, so we are done - ElementProduced::Progressed(self.pop()) + // otherwise, continue the loop and go to the parent + } else { + // if there is no parent, loop back to the root + let id = NodeId(0); + let last = push_back(&mut stack, rdom.get(id).unwrap()); + return ElementProduced::Looped(last); } } } } - - fn pop(&mut self) -> NodeId { - self.stack.pop().unwrap().0 - } } #[test] diff --git a/packages/native-core/tests/passes.rs b/packages/native-core/tests/passes.rs index f0023ae35..40fe58717 100644 --- a/packages/native-core/tests/passes.rs +++ b/packages/native-core/tests/passes.rs @@ -307,14 +307,14 @@ fn down_pass() { } let mut tree: RealDom = RealDom::new(Box::new([AddNumber::to_type_erased()])); - let grandchild1 = tree.create_node(create_blank_element(), true); + let grandchild1 = tree.create_node(create_blank_element()); let grandchild1 = grandchild1.id(); - let mut child1 = tree.create_node(create_blank_element(), true); + let mut child1 = tree.create_node(create_blank_element()); child1.add_child(grandchild1); let child1 = child1.id(); - let grandchild2 = tree.create_node(create_blank_element(), true); + let grandchild2 = tree.create_node(create_blank_element()); let grandchild2 = grandchild2.id(); - let mut child2 = tree.create_node(create_blank_element(), true); + let mut child2 = tree.create_node(create_blank_element()); child2.add_child(grandchild2); let child2 = child2.id(); let mut parent = tree.get_mut(tree.root_id()).unwrap(); @@ -395,14 +395,14 @@ fn up_pass() { } let mut tree: RealDom = RealDom::new(Box::new([AddNumber::to_type_erased()])); - let grandchild1 = tree.create_node(create_blank_element(), true); + let grandchild1 = tree.create_node(create_blank_element()); let grandchild1 = grandchild1.id(); - let mut child1 = tree.create_node(create_blank_element(), true); + let mut child1 = tree.create_node(create_blank_element()); child1.add_child(grandchild1); let child1 = child1.id(); - let grandchild2 = tree.create_node(create_blank_element(), true); + let grandchild2 = tree.create_node(create_blank_element()); let grandchild2 = grandchild2.id(); - let mut child2 = tree.create_node(create_blank_element(), true); + let mut child2 = tree.create_node(create_blank_element()); child2.add_child(grandchild2); let child2 = child2.id(); let mut parent = tree.get_mut(tree.root_id()).unwrap();