diff --git a/packages/native-core-macro/src/lib.rs b/packages/native-core-macro/src/lib.rs index fd25c3868..7960a0d4c 100644 --- a/packages/native-core-macro/src/lib.rs +++ b/packages/native-core-macro/src/lib.rs @@ -336,7 +336,7 @@ pub fn partial_derive_state(_: TokenStream, input: TokenStream) -> TokenStream { let (#(#split_views,)*) = data; let tree = run_view.tree.clone(); let node_types = run_view.node_type.clone(); - dioxus_native_core::prelude::run_pass(type_id, dependants.clone(), pass_direction, run_view, |id, context| { + dioxus_native_core::prelude::run_pass(type_id, dependants.clone(), pass_direction, run_view, Self::TRAVERSE_SHADOW_DOM, |id, context| { let node_data: &NodeType<_> = node_types.get(id).unwrap_or_else(|err| panic!("Failed to get node type {:?}", err)); // get all of the states from the tree view // Safety: No node has itself as a parent or child. diff --git a/packages/native-core/src/custom_element.rs b/packages/native-core/src/custom_element.rs index 9b3a19c04..f829d89f0 100644 --- a/packages/native-core/src/custom_element.rs +++ b/packages/native-core/src/custom_element.rs @@ -1,13 +1,12 @@ use std::sync::{Arc, RwLock}; use rustc_hash::FxHashMap; +use shipyard::Component; use crate::{ node::{FromAnyValue, NodeType}, node_ref::AttributeMask, prelude::{NodeImmutable, NodeMut, RealDom}, - real_dom::NodeTypeMut, - shadow_dom::ShadowDom, NodeId, }; @@ -28,7 +27,7 @@ impl CustomElementRegistry { self.builders.insert( W::NAME, CustomElementBuilder { - create: |dom, light_root_id| Box::new(W::create(dom, light_root_id)), + create: |dom| Box::new(W::create(dom)), }, ); } @@ -42,27 +41,22 @@ impl CustomElementRegistry { if let Some(element_tag) = element_tag { if let Some(builder) = self.builders.get(element_tag.as_str()) { let boxed_widget = { - let light_root_id = node.id(); let dom = node.real_dom_mut(); - (builder.create)(dom, light_root_id) + (builder.create)(dom) }; let boxed_widget = CustomElementManager { inner: Arc::new(RwLock::new(boxed_widget)), }; - let NodeTypeMut::Element(mut el) = node.node_type_mut()else{ - panic!("The type of the light element should not change when creating a shadow DOM") - }; - - *el.shadow_root_mut() = Some(ShadowDom::new(boxed_widget).into()); + node.insert(boxed_widget); } } } } struct CustomElementBuilder { - create: fn(&mut RealDom, NodeId) -> Box>, + create: fn(&mut RealDom) -> Box>, } /// A controlled element that renders to a shadow DOM @@ -71,11 +65,16 @@ pub trait CustomElement: Send + Sync + 'stat const NAME: &'static str; /// Create a new widget without mounting it. - fn create(dom: &mut RealDom, light_root_id: NodeId) -> Self; + fn create(dom: &mut RealDom) -> Self; - /// The root node of the widget. + /// The root node of the widget. This must be static once the element is created. fn root(&self) -> NodeId; + /// The slot to render children of the element into. This must be static once the element is created. + fn slot(&self) -> Option { + None + } + /// Called when the attributes of the widget are changed. fn attributes_changed(&mut self, _dom: &mut RealDom, _attributes: &AttributeMask); } @@ -88,14 +87,14 @@ trait ElementFactory, V: FromAnyValue + Send + Sync = const NAME: &'static str; /// Create a new widget. - fn create(dom: &mut RealDom, light_root_id: NodeId) -> W; + fn create(dom: &mut RealDom) -> W; } impl, V: FromAnyValue + Send + Sync> ElementFactory for W { const NAME: &'static str = W::NAME; - fn create(dom: &mut RealDom, light_root_id: NodeId) -> Self { - Self::create(dom, light_root_id) + fn create(dom: &mut RealDom) -> Self { + Self::create(dom) } } @@ -118,6 +117,7 @@ impl, V: FromAnyValue + Send + Sync> CustomElementUpdater } } +#[derive(Component, Clone)] pub struct CustomElementManager { inner: Arc>>>, } @@ -126,12 +126,11 @@ impl CustomElementManager { pub fn root(&self) -> NodeId { self.inner.read().unwrap().root() } -} -impl Clone for CustomElementManager { - fn clone(&self) -> Self { - Self { - inner: self.inner.clone(), - } + pub fn on_attributes_changed(&self, dom: &mut RealDom, attributes: &AttributeMask) { + self.inner + .write() + .unwrap() + .attributes_changed(dom, attributes); } } diff --git a/packages/native-core/src/dioxus.rs b/packages/native-core/src/dioxus.rs index 9d50b1224..3e9ff9ee0 100644 --- a/packages/native-core/src/dioxus.rs +++ b/packages/native-core/src/dioxus.rs @@ -262,7 +262,6 @@ fn create_template_node( }) .collect(), listeners: FxHashSet::default(), - shadow: None, }); let node_id = rdom.create_node(node).id(); for child in *children { diff --git a/packages/native-core/src/lib.rs b/packages/native-core/src/lib.rs index 6dfeb81cd..fcbbdd9fb 100644 --- a/packages/native-core/src/lib.rs +++ b/packages/native-core/src/lib.rs @@ -16,7 +16,6 @@ pub mod node_ref; pub mod node_watcher; mod passes; pub mod real_dom; -mod shadow_dom; pub mod tree; pub mod utils; diff --git a/packages/native-core/src/node.rs b/packages/native-core/src/node.rs index 2cbfa3fb3..42ff8229f 100644 --- a/packages/native-core/src/node.rs +++ b/packages/native-core/src/node.rs @@ -7,8 +7,6 @@ use std::{ fmt::{Debug, Display}, }; -use crate::shadow_dom::ShadowDom; - /// A element node in the RealDom #[derive(Debug, Clone, Default)] pub struct ElementNode { @@ -20,9 +18,6 @@ pub struct ElementNode { pub attributes: FxHashMap>, /// The events the element is listening for pub listeners: FxHashSet, - /// The shadow dom of the element - // This is a rare property, so it is stored in an `Box` to save space - pub shadow: Option>>, } impl ElementNode { @@ -33,7 +28,6 @@ impl ElementNode { namespace: namespace.into(), attributes: Default::default(), listeners: Default::default(), - shadow: Default::default(), } } } diff --git a/packages/native-core/src/passes.rs b/packages/native-core/src/passes.rs index 16d0f71fc..f8aa94fa5 100644 --- a/packages/native-core/src/passes.rs +++ b/packages/native-core/src/passes.rs @@ -194,6 +194,7 @@ pub fn run_pass( dependants: FxHashSet, pass_direction: PassDirection, view: RunPassView, + enter_shadow_dom: bool, mut update_node: impl FnMut(NodeId, &SendAnyMap) -> bool, ) { let RunPassView { @@ -209,9 +210,32 @@ pub fn run_pass( while let Some((height, id)) = dirty.pop_front(type_id) { if (update_node)(id, ctx) { nodes_updated.insert(id); - for id in tree.children_ids(id) { - for dependant in &dependants { - dirty.insert(*dependant, id, height + 1); + let shadow_tree = tree.shadow_tree(id); + match (enter_shadow_dom, shadow_tree) { + (true, Some(shadow_tree)) => { + // If this pass uses the shadow dom, update the shadow dom children instead of the normal children + for id in &shadow_tree.shadow_roots { + for dependant in &dependants { + dirty.insert(*dependant, *id, height + 1); + } + } + } + _ => { + for id in tree.children_ids(id) { + for dependant in &dependants { + dirty.insert(*dependant, id, height + 1); + } + } + } + } + // If this pass uses the shadow dom, update the light dom's children if this node is a slot + if enter_shadow_dom { + if let Some(slot_for_light_tree) = tree.slot_for_light_tree(id) { + for id in tree.children_ids(slot_for_light_tree) { + for dependant in &dependants { + dirty.insert(*dependant, id, height + 1); + } + } } } } @@ -221,9 +245,21 @@ pub fn run_pass( while let Some((height, id)) = dirty.pop_back(type_id) { if (update_node)(id, ctx) { nodes_updated.insert(id); - if let Some(id) = tree.parent_id(id) { - for dependant in &dependants { - dirty.insert(*dependant, id, height - 1); + + // If this pass uses the shadow dom, update the light dom root if this node is a root + let light_tree_root = tree.light_tree_root(id); + match (enter_shadow_dom, light_tree_root) { + (true, Some(light_tree_root)) => { + for dependant in &dependants { + dirty.insert(*dependant, light_tree_root, height + 1); + } + } + _ => { + if let Some(id) = tree.parent_id(id) { + for dependant in &dependants { + dirty.insert(*dependant, id, height - 1); + } + } } } } diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index 72d8bce9c..9b8ae1983 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -10,7 +10,7 @@ use std::collections::VecDeque; use std::ops::{Deref, DerefMut}; use std::sync::{Arc, RwLock}; -use crate::custom_element::CustomElementRegistry; +use crate::custom_element::{CustomElementManager, CustomElementRegistry}; use crate::node::{ ElementNode, FromAnyValue, NodeType, OwnedAttributeDiscription, OwnedAttributeValue, TextNode, }; @@ -18,7 +18,6 @@ use crate::node_ref::{NodeMask, NodeMaskBuilder}; use crate::node_watcher::{AttributeWatcher, NodeWatcher}; use crate::passes::{DirtyNodeStates, TypeErasedState}; use crate::prelude::AttributeMaskBuilder; -use crate::shadow_dom::ShadowDom; use crate::tree::{TreeMut, TreeMutView, TreeRef, TreeRefView}; use crate::NodeId; use crate::{FxDashSet, SendAnyMap}; @@ -113,7 +112,7 @@ pub struct RealDom { attribute_watchers: AttributeWatchers, workload: ScheduledWorkload, root_id: NodeId, - custom_elements: CustomElementRegistry, + custom_elements: Arc>>, phantom: std::marker::PhantomData, } @@ -142,7 +141,6 @@ impl RealDom { namespace: Some("Root".to_string()), attributes: FxHashMap::default(), listeners: FxHashSet::default(), - shadow: None, }); let root_id = world.add_entity(root_node); { @@ -189,7 +187,10 @@ impl RealDom { /// Create a new node of the given type in the dom and return a mutable reference to it. pub fn create_node(&mut self, node: impl Into>) -> NodeMut<'_, V> { - let id = self.world.add_entity(node.into()); + let node = node.into(); + let is_element = matches!(node, NodeType::Element(_)); + + let id = self.world.add_entity(node); self.tree_mut().create_node(id); self.dirty_nodes @@ -201,6 +202,15 @@ impl RealDom { .mark_dirty(id, NodeMaskBuilder::ALL.build()); self.dirty_nodes.nodes_created.insert(id); + // Create a custom element if needed + if is_element { + let custom_elements = self.custom_elements.clone(); + custom_elements + .write() + .unwrap() + .add_shadow_dom(NodeMut::new(id, self)); + } + NodeMut::new(id, self) } @@ -280,10 +290,9 @@ impl RealDom { let passes = std::mem::take(&mut self.dirty_nodes.passes_updated); let nodes_updated = std::mem::take(&mut self.dirty_nodes.nodes_updated); - // call attribute watchers for (node_id, mask) in &nodes_updated { if self.contains(*node_id) { - // ignore watchers if they are already being modified + // call attribute watchers but ignore watchers if they are already being modified let watchers = self.attribute_watchers.clone(); if let Ok(mut watchers) = watchers.try_write() { for watcher in &mut *watchers { @@ -293,6 +302,15 @@ impl RealDom { ); } }; + + // call custom element watchers + let custom_element_manager = { + let node = self.get_mut(*node_id).unwrap(); + node.get::>().map(|x| x.clone()) + }; + if let Some(custom_element_manager) = custom_element_manager { + custom_element_manager.on_attributes_changed(self, mask.attributes()); + } } } @@ -1062,16 +1080,6 @@ impl ElementNodeMut<'_, V> { pub fn listeners(&self) -> &FxHashSet { &self.element().listeners } - - /// Get the shadow root of the element - pub fn shadow_root(&self) -> Option<&ShadowDom> { - self.element().shadow.as_deref() - } - - /// Get the shadow root of the element - pub fn shadow_root_mut(&mut self) -> &mut Option>> { - &mut self.element_mut().shadow - } } // Create a workload from all of the passes. This orders the passes so that each pass will only run at most once. diff --git a/packages/native-core/src/shadow_dom.rs b/packages/native-core/src/shadow_dom.rs deleted file mode 100644 index 262d6fbb6..000000000 --- a/packages/native-core/src/shadow_dom.rs +++ /dev/null @@ -1,26 +0,0 @@ -use std::fmt::Debug; - -use crate::{custom_element::CustomElementManager, node::FromAnyValue, prelude::NodeRef, NodeId}; - -#[derive(Clone)] -pub struct ShadowDom { - shadow_root: NodeId, - updater: CustomElementManager, -} - -impl ShadowDom { - pub fn new(updater: CustomElementManager) -> Self { - Self { - shadow_root: updater.root(), - updater, - } - } -} - -impl Debug for ShadowDom { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("ShadowDom") - .field("shadow_root", &self.shadow_root) - .finish() - } -} diff --git a/packages/native-core/src/tree.rs b/packages/native-core/src/tree.rs index e44970649..e0b32f28d 100644 --- a/packages/native-core/src/tree.rs +++ b/packages/native-core/src/tree.rs @@ -4,15 +4,15 @@ use crate::NodeId; use shipyard::{Component, EntitiesViewMut, Get, View, ViewMut}; use std::fmt::Debug; -/// A subtree of a tree. +/// A shadow_tree of a tree. #[derive(PartialEq, Eq, Clone, Debug, Component)] -pub struct Subtree { - /// The root of the subtree - shadow_roots: Vec, +pub struct ShadowTree { + /// The root of the shadow_tree + pub shadow_roots: Vec, /// The node that children of the super tree should be inserted under. - slot: Option, - /// The node in the super tree that the subtree is attached to. - super_tree_root: NodeId, + pub slot: Option, + /// The node in the super tree that the shadow_tree is attached to. + pub super_tree_root: NodeId, } /// A node in a tree. @@ -20,9 +20,11 @@ pub struct Subtree { pub struct Node { parent: Option, children: Vec, - child_subtree: Option, - /// If this node is a slot in a subtree, this is node whose child_subtree is that subtree. - slot_for_supertree: Option, + child_subtree: Option, + /// If this node is a slot in a shadow_tree, this is node whose child_subtree is that shadow_tree. + slot_for_light_tree: Option, + /// If this node is a root of a shadow_tree, this is the node whose child_subtree is that shadow_tree. + light_tree_root: Option, height: u16, } @@ -37,8 +39,13 @@ pub trait TreeRef { fn parent_id(&self, id: NodeId) -> Option; /// The children ids of the node. fn children_ids(&self, id: NodeId) -> Vec; - /// The subtree tree under the node. - fn subtree(&self, id: NodeId) -> Option<&Subtree>; + /// The shadow tree tree under the node. + fn shadow_tree(&self, id: NodeId) -> Option<&ShadowTree>; + // TODO: rethink naming + /// The node that contains the shadow tree this node is a slot for + fn slot_for_light_tree(&self, id: NodeId) -> Option; + /// The node that contains the shadow tree this node is a root of + fn light_tree_root(&self, id: NodeId) -> Option; /// The height of the node. fn height(&self, id: NodeId) -> Option; /// Returns true if the node exists. @@ -59,9 +66,9 @@ pub trait TreeMut: TreeRef { fn insert_before(&mut self, old_id: NodeId, new_id: NodeId); /// Inserts a node after another node. fn insert_after(&mut self, old_id: NodeId, new_id: NodeId); - /// Creates a new subtree. + /// Creates a new shadow tree. fn create_subtree(&mut self, id: NodeId, shadow_roots: Vec, slot: Option); - /// Remove any subtree. + /// Remove any shadow tree. fn remove_subtree(&mut self, id: NodeId); } @@ -84,34 +91,42 @@ impl<'a> TreeRef for TreeRefView<'a> { self.get(id).is_ok() } - fn subtree(&self, id: NodeId) -> Option<&Subtree> { + fn shadow_tree(&self, id: NodeId) -> Option<&ShadowTree> { self.get(id).ok()?.child_subtree.as_ref() } + + fn slot_for_light_tree(&self, id: NodeId) -> Option { + self.get(id).ok()?.slot_for_light_tree + } + + fn light_tree_root(&self, id: NodeId) -> Option { + self.get(id).ok()?.light_tree_root + } } impl<'a> TreeMut for TreeMutView<'a> { fn remove(&mut self, id: NodeId) { fn recurse(tree: &mut TreeMutView<'_>, id: NodeId) { - let (supertree, children) = { + let (light_tree, children) = { let node = (&mut tree.1).get(id).unwrap(); - (node.slot_for_supertree, std::mem::take(&mut node.children)) + (node.slot_for_light_tree, std::mem::take(&mut node.children)) }; for child in children { recurse(tree, child); } - // If this node is a slot in a subtree, remove it from the subtree. - if let Some(supertree) = supertree { - let supertree_root = (&mut tree.1).get(supertree).unwrap(); + // If this node is a slot in a shadow_tree, remove it from the shadow_tree. + if let Some(light_tree) = light_tree { + let light_tree_root = (&mut tree.1).get(light_tree).unwrap(); - if let Some(subtree) = &mut supertree_root.child_subtree { - subtree.slot = None; + if let Some(shadow_tree) = &mut light_tree_root.child_subtree { + shadow_tree.slot = None; } debug_assert!( - supertree_root.children.is_empty(), - "Subtree root should have no children when slot is removed." + light_tree_root.children.is_empty(), + "ShadowTree root should have no children when slot is removed." ); } } @@ -137,7 +152,8 @@ impl<'a> TreeMut for TreeMutView<'a> { children: Vec::new(), height: 0, child_subtree: None, - slot_for_supertree: None, + slot_for_light_tree: None, + light_tree_root: None, }, ); } @@ -212,7 +228,7 @@ impl<'a> TreeMut for TreeMutView<'a> { let light_root_height; { - let subtree = Subtree { + let shadow_tree = ShadowTree { super_tree_root: id, shadow_roots: shadow_roots.clone(), slot, @@ -220,20 +236,20 @@ impl<'a> TreeMut for TreeMutView<'a> { let light_root = node_data_mut .get(id) - .expect("tried to create subtree with non-existent id"); + .expect("tried to create shadow_tree with non-existent id"); - light_root.child_subtree = Some(subtree); + light_root.child_subtree = Some(shadow_tree); light_root_height = light_root.height; if let Some(slot) = slot { let slot = node_data_mut .get(slot) - .expect("tried to create subtree with non-existent slot"); - slot.slot_for_supertree = Some(id); + .expect("tried to create shadow_tree with non-existent slot"); + slot.slot_for_light_tree = Some(id); } } - // Now that we have created the subtree, we need to update the height of the subtree roots + // Now that we have created the shadow_tree, we need to update the height of the shadow_tree roots for root in shadow_roots { set_height(self, root, light_root_height + 1); } @@ -243,13 +259,13 @@ impl<'a> TreeMut for TreeMutView<'a> { let (_, node_data_mut) = self; if let Ok(node) = node_data_mut.get(id) { - if let Some(subtree) = node.child_subtree.take() { - // Remove the slot's link to the subtree - if let Some(slot) = subtree.slot { + if let Some(shadow_tree) = node.child_subtree.take() { + // Remove the slot's link to the shadow_tree + if let Some(slot) = shadow_tree.slot { let slot = node_data_mut .get(slot) - .expect("tried to remove subtree with non-existent slot"); - slot.slot_for_supertree = None; + .expect("tried to remove shadow_tree with non-existent slot"); + slot.slot_for_light_tree = None; } let node = node_data_mut.get(id).unwrap(); @@ -262,7 +278,7 @@ impl<'a> TreeMut for TreeMutView<'a> { } // Reset the height of the shadow roots - for root in &subtree.shadow_roots { + for root in &shadow_tree.shadow_roots { set_height(self, *root, 0); } } @@ -272,13 +288,13 @@ impl<'a> TreeMut for TreeMutView<'a> { fn child_height(parent: &Node, tree: &impl TreeRef) -> u16 { match &parent.child_subtree { - Some(subtree) => { - if let Some(slot) = subtree.slot { + Some(shadow_tree) => { + if let Some(slot) = shadow_tree.slot { tree.height(slot) .expect("Attempted to read a slot that does not exist") + 1 } else { - panic!("Attempted to read the height of a subtree without a slot"); + panic!("Attempted to read the height of a shadow_tree without a slot"); } } None => parent.height + 1, @@ -287,22 +303,22 @@ fn child_height(parent: &Node, tree: &impl TreeRef) -> u16 { /// Sets the height of a node and updates the height of all its children fn set_height(tree: &mut TreeMutView<'_>, node: NodeId, height: u16) { - let (subtree, supertree, children) = { + let (shadow_tree, light_tree, children) = { let mut node_data_mut = &mut tree.1; let mut node = (&mut node_data_mut).get(node).unwrap(); node.height = height; ( node.child_subtree.clone(), - node.slot_for_supertree, + node.slot_for_light_tree, node.children.clone(), ) }; - // If the children are actually part of a subtree, there height is determined by the height of the subtree - if let Some(subtree) = subtree { - // Set the height of the subtree roots - for &shadow_root in &subtree.shadow_roots { + // If the children are actually part of a shadow_tree, there height is determined by the height of the shadow_tree + if let Some(shadow_tree) = shadow_tree { + // Set the height of the shadow_tree roots + for &shadow_root in &shadow_tree.shadow_roots { set_height(tree, shadow_root, height); } } else { @@ -312,9 +328,9 @@ fn set_height(tree: &mut TreeMutView<'_>, node: NodeId, height: u16) { } } - // If this nodes is a slot for a subtree, we need to go to the super tree and update the height of its children - if let Some(supertree) = supertree { - let children = (&tree.1).get(supertree).unwrap().children.clone(); + // If this nodes is a slot for a shadow_tree, we need to go to the super tree and update the height of its children + if let Some(light_tree) = light_tree { + let children = (&tree.1).get(light_tree).unwrap().children.clone(); for child in children { set_height(tree, child, height + 1); } @@ -344,10 +360,20 @@ impl<'a> TreeRef for TreeMutView<'a> { self.1.get(id).is_ok() } - fn subtree(&self, id: NodeId) -> Option<&Subtree> { + fn shadow_tree(&self, id: NodeId) -> Option<&ShadowTree> { let node_data = &self.1; node_data.get(id).unwrap().child_subtree.as_ref() } + + fn slot_for_light_tree(&self, id: NodeId) -> Option { + let node_data = &self.1; + node_data.get(id).unwrap().slot_for_light_tree + } + + fn light_tree_root(&self, id: NodeId) -> Option { + let node_data = &self.1; + node_data.get(id).unwrap().light_tree_root + } } #[test] @@ -375,7 +401,7 @@ fn creation() { } #[test] -fn subtree() { +fn shadow_tree() { use shipyard::World; #[derive(Component)] struct Num(i32); @@ -432,7 +458,7 @@ fn subtree() { &[shadow_parent_id] ); assert_eq!( - tree.1.get(shadow_child_id).unwrap().slot_for_supertree, + tree.1.get(shadow_child_id).unwrap().slot_for_light_tree, Some(parent_id) ); diff --git a/packages/native-core/tests/passes.rs b/packages/native-core/tests/passes.rs index 9a0fbe51d..368333fb5 100644 --- a/packages/native-core/tests/passes.rs +++ b/packages/native-core/tests/passes.rs @@ -10,7 +10,6 @@ fn create_blank_element() -> NodeType { namespace: None, attributes: FxHashMap::default(), listeners: FxHashSet::default(), - shadow: None, }) }