瀏覽代碼

finish shadow dom docs

Evan Almloff 2 年之前
父節點
當前提交
9877b0f0b1

+ 1 - 1
packages/native-core/examples/custom_attr.rs

@@ -215,7 +215,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
                 let _to_rerender = rdom.update_state(ctx);
 
                 // render...
-                rdom.traverse_depth_first(true, |node| {
+                rdom.traverse_depth_first_advanced(true, |node| {
                     let indent = " ".repeat(node.height() as usize);
                     let color = *node.get::<TextColor>().unwrap();
                     let size = *node.get::<Size>().unwrap();

+ 1 - 1
packages/native-core/examples/font_size.rs

@@ -160,7 +160,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
                 let _to_rerender = rdom.update_state(ctx);
 
                 // render...
-                rdom.traverse_depth_first(true, |node| {
+                rdom.traverse_depth_first_advanced(true, |node| {
                     let indent = " ".repeat(node.height() as usize);
                     let font_size = *node.get::<FontSize>().unwrap();
                     let size = *node.get::<Size>().unwrap();

+ 1 - 1
packages/native-core/examples/simple.rs

@@ -206,7 +206,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
                 let _to_rerender = rdom.update_state(ctx);
 
                 // render...
-                rdom.traverse_depth_first(true, |node| {
+                rdom.traverse_depth_first_advanced(true, |node| {
                     let indent = " ".repeat(node.height() as usize);
                     let color = *node.get::<TextColor>().unwrap();
                     let size = *node.get::<Size>().unwrap();

+ 1 - 1
packages/native-core/examples/simple_dioxus.rs

@@ -234,7 +234,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
                 let _to_rerender = rdom.update_state(ctx);
 
                 // render...
-                rdom.traverse_depth_first(true, |node| {
+                rdom.traverse_depth_first_advanced(true, |node| {
                     let indent = " ".repeat(node.height() as usize);
                     let color = *node.get::<TextColor>().unwrap();
                     let size = *node.get::<Size>().unwrap();

+ 44 - 37
packages/native-core/src/custom_element.rs

@@ -1,5 +1,10 @@
-//! A custom element is a controlled element that renders to a shadow DOM.
-//! Each custom element is registered with a element name
+//! A custom element is a controlled element that renders to a shadow DOM. This allows you to create elements that act like widgets without relying on a specific framework.
+//!
+//! Each custom element is registered with a element name and namespace with [`RealDom::register_custom_element`] or [`RealDom::register_custom_element_with_factory`]. Once registered, they will be created automatically when the element is added to the DOM.
+
+// Used in doc links
+#[allow(unused)]
+use crate::real_dom::RealDom;
 
 use std::sync::{Arc, RwLock};
 
@@ -48,22 +53,22 @@ impl<V: FromAnyValue + Send + Sync> CustomElementRegistry<V> {
         };
         if let Some((tag, ns)) = element_tag {
             if let Some(builder) = self.builders.get(&(tag.as_str(), ns.as_deref())) {
-                let boxed_widget = { (builder.create)(node.reborrow()) };
+                let boxed_custom_element = { (builder.create)(node.reborrow()) };
 
-                let shadow_roots = boxed_widget.roots();
+                let shadow_roots = boxed_custom_element.roots();
 
                 let light_id = node.id();
                 node.real_dom_mut().tree_mut().create_subtree(
                     light_id,
                     shadow_roots,
-                    boxed_widget.slot(),
+                    boxed_custom_element.slot(),
                 );
 
-                let boxed_widget = CustomElementManager {
-                    inner: Arc::new(RwLock::new(boxed_widget)),
+                let boxed_custom_element = CustomElementManager {
+                    inner: Arc::new(RwLock::new(boxed_custom_element)),
                 };
 
-                node.insert(boxed_widget);
+                node.insert(boxed_custom_element);
             }
         }
     }
@@ -73,40 +78,49 @@ struct CustomElementBuilder<V: FromAnyValue + Send + Sync> {
     create: fn(NodeMut<V>) -> Box<dyn CustomElementUpdater<V>>,
 }
 
-/// A controlled element that renders to a shadow DOM
+/// A controlled element that renders to a shadow DOM.
+///
+/// Register with [`RealDom::register_custom_element`]
+///
+/// This is a simplified custom element trait for elements that can create themselves. For more granular control, implement [`CustomElementFactory`] and [`CustomElementUpdater`] instead.
 pub trait CustomElement<V: FromAnyValue + Send + Sync = ()>: Send + Sync + 'static {
-    /// The tag the widget is registered under.
+    /// The tag of the element
     const NAME: &'static str;
 
-    /// The namespace the widget is registered under.
+    /// The namespace of the element
     const NAMESPACE: Option<&'static str> = None;
 
-    /// Create a new widget *without mounting* it.
-    fn create(node: NodeMut<V>) -> Self;
+    /// Create a new element *without mounting* it.
+    /// The node passed in is the light DOM node. The element should not modify the light DOM node, but it can get the [`NodeMut::real_dom_mut`] from the node to create new nodes.
+    fn create(light_root: NodeMut<V>) -> Self;
 
-    /// The root node of the widget. This must be static once the element is created.
+    /// The root node of the custom element. These roots must be not change once the element is created.
     fn roots(&self) -> Vec<NodeId>;
 
-    /// The slot to render children of the element into. This must be static once the element is created.
+    /// The slot to render children of the element into. The slot must be not change once the element is created.
     fn slot(&self) -> Option<NodeId> {
         None
     }
 
-    /// Called when the attributes of the widget are changed.
+    /// Update the custom element's shadow tree with the new attributes.
+    /// Called when the attributes of the custom element are changed.
     fn attributes_changed(&mut self, light_node: NodeMut<V>, attributes: &AttributeMask);
 }
 
-/// A factory for creating widgets
+/// A factory for creating custom elements
+///
+/// Register with [`RealDom::register_custom_element_with_factory`]
 pub trait CustomElementFactory<W: CustomElementUpdater<V>, V: FromAnyValue + Send + Sync = ()>:
     Send + Sync + 'static
 {
-    /// The tag the widget is registered under.
+    /// The tag of the element
     const NAME: &'static str;
 
-    /// The namespace the widget is registered under.
+    /// The namespace of the element
     const NAMESPACE: Option<&'static str> = None;
 
-    /// Create a new widget.
+    /// Create a new element *without mounting* it.
+    /// The node passed in is the light DOM node. The element should not modify the light DOM node, but it can get the [`NodeMut::real_dom_mut`] from the node to create new nodes.
     fn create(dom: NodeMut<V>) -> W;
 }
 
@@ -120,16 +134,19 @@ impl<W: CustomElement<V>, V: FromAnyValue + Send + Sync> CustomElementFactory<W,
     }
 }
 
-/// A trait for updating widgets
+/// A trait for updating custom elements
 pub trait CustomElementUpdater<V: FromAnyValue + Send + Sync = ()>: Send + Sync + 'static {
-    /// Called when the attributes of the widget are changed.
+    /// Update the custom element's shadow tree with the new attributes.
+    /// Called when the attributes of the custom element are changed.
     fn attributes_changed(&mut self, light_root: NodeMut<V>, attributes: &AttributeMask);
 
-    /// The root node of the widget.
+    /// The root node of the custom element. These roots must be not change once the element is created.
     fn roots(&self) -> Vec<NodeId>;
 
-    /// The slot to render children of the element into.
-    fn slot(&self) -> Option<NodeId>;
+    /// The slot to render children of the element into. The slot must be not change once the element is created.
+    fn slot(&self) -> Option<NodeId> {
+        None
+    }
 }
 
 impl<W: CustomElement<V>, V: FromAnyValue + Send + Sync> CustomElementUpdater<V> for W {
@@ -146,23 +163,13 @@ impl<W: CustomElement<V>, V: FromAnyValue + Send + Sync> CustomElementUpdater<V>
     }
 }
 
-/// A concrete structure for managing a any widget.
+/// A dynamic trait object wrapper for [`CustomElementUpdater`]
 #[derive(Component, Clone)]
-pub struct CustomElementManager<V: FromAnyValue = ()> {
+pub(crate) struct CustomElementManager<V: FromAnyValue = ()> {
     inner: Arc<RwLock<Box<dyn CustomElementUpdater<V>>>>,
 }
 
 impl<V: FromAnyValue + Send + Sync> CustomElementManager<V> {
-    /// The root node of the widget's shadow DOM.
-    pub fn roots(&self) -> Vec<NodeId> {
-        self.inner.read().unwrap().roots()
-    }
-
-    /// The slot to render children of the element into.
-    pub fn slot(&self) -> Option<NodeId> {
-        self.inner.read().unwrap().slot()
-    }
-
     /// Update the custom element based on attributes changed.
     pub fn on_attributes_changed(&self, light_root: NodeMut<V>, attributes: &AttributeMask) {
         self.inner

+ 55 - 7
packages/native-core/src/real_dom.rs

@@ -384,20 +384,35 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
     }
 
     /// Traverses the dom in a depth first manner, calling the provided function on each node.
-    pub fn traverse_depth_first(&self, enter_shadow_doms: bool, mut f: impl FnMut(NodeRef<V>)) {
+    /// If `enter_shadow_dom` is true, then the traversal will enter shadow doms in the tree.
+    pub fn traverse_depth_first_advanced(
+        &self,
+        enter_shadow_dom: bool,
+        mut f: impl FnMut(NodeRef<V>),
+    ) {
         let mut stack = vec![self.root_id()];
         let tree = self.tree_ref();
         while let Some(id) = stack.pop() {
             if let Some(node) = self.get(id) {
                 f(node);
-                let children = tree.children_ids_advanced(id, enter_shadow_doms);
+                let children = tree.children_ids_advanced(id, enter_shadow_dom);
                 stack.extend(children.iter().copied().rev());
             }
         }
     }
 
+    /// Traverses the dom in a depth first manner, calling the provided function on each node.
+    pub fn traverse_depth_first(&self, f: impl FnMut(NodeRef<V>)) {
+        self.traverse_depth_first_advanced(true, f)
+    }
+
     /// Traverses the dom in a breadth first manner, calling the provided function on each node.
-    pub fn traverse_breadth_first(&self, enter_shadow_doms: bool, mut f: impl FnMut(NodeRef<V>)) {
+    /// If `enter_shadow_dom` is true, then the traversal will enter shadow doms in the tree.
+    pub fn traverse_breadth_first_advanced(
+        &self,
+        enter_shadow_doms: bool,
+        mut f: impl FnMut(NodeRef<V>),
+    ) {
         let mut queue = VecDeque::new();
         queue.push_back(self.root_id());
         let tree = self.tree_ref();
@@ -412,8 +427,14 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
         }
     }
 
+    /// Traverses the dom in a breadth first manner, calling the provided function on each node.
+    pub fn traverse_breadth_first(&self, f: impl FnMut(NodeRef<V>)) {
+        self.traverse_breadth_first_advanced(true, f);
+    }
+
     /// Traverses the dom in a depth first manner mutably, calling the provided function on each node.
-    pub fn traverse_depth_first_mut(
+    /// If `enter_shadow_dom` is true, then the traversal will enter shadow doms in the tree.
+    pub fn traverse_depth_first_mut_advanced(
         &mut self,
         enter_shadow_doms: bool,
         mut f: impl FnMut(NodeMut<V>),
@@ -432,8 +453,14 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
         }
     }
 
+    /// Traverses the dom in a depth first manner mutably, calling the provided function on each node.
+    pub fn traverse_depth_first_mut(&mut self, f: impl FnMut(NodeMut<V>)) {
+        self.traverse_depth_first_mut_advanced(true, f)
+    }
+
     /// Traverses the dom in a breadth first manner mutably, calling the provided function on each node.
-    pub fn traverse_breadth_first_mut(
+    /// If `enter_shadow_dom` is true, then the traversal will enter shadow doms in the tree.
+    pub fn traverse_breadth_first_mut_advanced(
         &mut self,
         enter_shadow_doms: bool,
         mut f: impl FnMut(NodeMut<V>),
@@ -453,6 +480,11 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
         }
     }
 
+    /// Traverses the dom in a breadth first manner mutably, calling the provided function on each node.
+    pub fn traverse_breadth_first_mut(&mut self, f: impl FnMut(NodeMut<V>)) {
+        self.traverse_breadth_first_mut_advanced(true, f);
+    }
+
     /// Adds a [`NodeWatcher`] to the dom. Node watchers are called whenever a node is created or removed.
     pub fn add_node_watcher(&mut self, watcher: impl NodeWatcher<V> + 'static + Send + Sync) {
         self.node_watchers.write().unwrap().push(Box::new(watcher));
@@ -481,11 +513,11 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
 
     /// Registers a new custom element.
     pub fn register_custom_element<E: CustomElement<V>>(&mut self) {
-        self.register_custom_element_factory::<E, E>()
+        self.register_custom_element_with_factory::<E, E>()
     }
 
     /// Registers a new custom element with a custom factory.
-    pub fn register_custom_element_factory<F, U>(&mut self)
+    pub fn register_custom_element_with_factory<F, U>(&mut self)
     where
         F: CustomElementFactory<U, V>,
         U: CustomElementUpdater<V>,
@@ -563,6 +595,14 @@ pub trait NodeImmutable<V: FromAnyValue + Send + Sync = ()>: Sized {
             .then(|| ViewEntry::new(view, self.id()))
     }
 
+    /// Get the ids of the children of the current node, if enter_shadow_dom is true and the current node is a shadow slot, the ids of the nodes under the node the shadow slot is attached to will be returned
+    #[inline]
+    fn children_ids_advanced(&self, id: NodeId, enter_shadow_dom: bool) -> Vec<NodeId> {
+        self.real_dom()
+            .tree_ref()
+            .children_ids_advanced(id, enter_shadow_dom)
+    }
+
     /// Get the ids of the children of the current node
     #[inline]
     fn child_ids(&self) -> Vec<NodeId> {
@@ -581,6 +621,14 @@ pub trait NodeImmutable<V: FromAnyValue + Send + Sync = ()>: Sized {
             .collect()
     }
 
+    /// Get the id of the parent of the current node, if enter_shadow_dom is true and the current node is a shadow root, the node the shadow root is attached to will be returned
+    #[inline]
+    fn parent_id_advanced(&self, id: NodeId, enter_shadow_dom: bool) -> Option<NodeId> {
+        self.real_dom()
+            .tree_ref()
+            .parent_id_advanced(id, enter_shadow_dom)
+    }
+
     /// Get the id of the parent of the current node
     #[inline]
     fn parent_id(&self) -> Option<NodeId> {

+ 15 - 16
packages/native-core/src/tree.rs

@@ -4,10 +4,10 @@ use crate::NodeId;
 use shipyard::{Component, EntitiesViewMut, Get, View, ViewMut};
 use std::fmt::Debug;
 
-/// A shadow_tree of a tree.
+/// A shadow tree reference inside of a tree. This tree is isolated from the main tree.
 #[derive(PartialEq, Eq, Clone, Debug, Component)]
 pub struct ShadowTree {
-    /// The root of the shadow_tree
+    /// The root of the shadow tree
     pub shadow_roots: Vec<NodeId>,
     /// The node that children of the super tree should be inserted under.
     pub slot: Option<NodeId>,
@@ -22,7 +22,7 @@ pub struct Node {
     /// 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<NodeId>,
     /// 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<NodeId>,
+    root_for_light_tree: Option<NodeId>,
     height: u16,
 }
 
@@ -37,8 +37,8 @@ pub trait TreeRef {
     #[inline]
     fn parent_id_advanced(&self, id: NodeId, enter_shadow_dom: bool) -> Option<NodeId> {
         // If this node is the root of a shadow_tree, return the node the shadow_tree is attached
-        let light_tree_root = self.light_tree_root(id);
-        match (light_tree_root, enter_shadow_dom) {
+        let root_for_light_tree = self.root_for_light_tree(id);
+        match (root_for_light_tree, enter_shadow_dom) {
             (Some(id), true) => Some(id),
             _ => {
                 let parent_id = self.parent_id(id);
@@ -74,11 +74,10 @@ pub trait TreeRef {
     fn children_ids(&self, id: NodeId) -> Vec<NodeId>;
     /// 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<NodeId>;
     /// The node that contains the shadow tree this node is a root of
-    fn light_tree_root(&self, id: NodeId) -> Option<NodeId>;
+    fn root_for_light_tree(&self, id: NodeId) -> Option<NodeId>;
     /// The height of the node.
     fn height(&self, id: NodeId) -> Option<u16>;
     /// Returns true if the node exists.
@@ -132,8 +131,8 @@ impl<'a> TreeRef for TreeRefView<'a> {
         self.get(id).ok()?.slot_for_light_tree
     }
 
-    fn light_tree_root(&self, id: NodeId) -> Option<NodeId> {
-        self.get(id).ok()?.light_tree_root
+    fn root_for_light_tree(&self, id: NodeId) -> Option<NodeId> {
+        self.get(id).ok()?.root_for_light_tree
     }
 }
 
@@ -151,14 +150,14 @@ impl<'a> TreeMut for TreeMutView<'a> {
 
             // 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();
+                let root_for_light_tree = (&mut tree.1).get(light_tree).unwrap();
 
-                if let Some(shadow_tree) = &mut light_tree_root.child_subtree {
+                if let Some(shadow_tree) = &mut root_for_light_tree.child_subtree {
                     shadow_tree.slot = None;
                 }
 
                 debug_assert!(
-                    light_tree_root.children.is_empty(),
+                    root_for_light_tree.children.is_empty(),
                     "ShadowTree root should have no children when slot is removed."
                 );
             }
@@ -186,7 +185,7 @@ impl<'a> TreeMut for TreeMutView<'a> {
                 height: 0,
                 child_subtree: None,
                 slot_for_light_tree: None,
-                light_tree_root: None,
+                root_for_light_tree: None,
             },
         );
     }
@@ -283,7 +282,7 @@ impl<'a> TreeMut for TreeMutView<'a> {
 
         // Now that we have created the shadow_tree, we need to update the height of the shadow_tree roots
         for root in shadow_roots {
-            (&mut self.1).get(root).unwrap().light_tree_root = Some(id);
+            (&mut self.1).get(root).unwrap().root_for_light_tree = Some(id);
             set_height(self, root, light_root_height + 1);
         }
     }
@@ -403,9 +402,9 @@ impl<'a> TreeRef for TreeMutView<'a> {
         node_data.get(id).ok()?.slot_for_light_tree
     }
 
-    fn light_tree_root(&self, id: NodeId) -> Option<NodeId> {
+    fn root_for_light_tree(&self, id: NodeId) -> Option<NodeId> {
         let node_data = &self.1;
-        node_data.get(id).ok()?.light_tree_root
+        node_data.get(id).ok()?.root_for_light_tree
     }
 }
 

+ 1 - 1
packages/native-core/tests/called_minimally_on_build.rs

@@ -146,7 +146,7 @@ macro_rules! test_state{
             dioxus_state.apply_mutations(&mut dom, mutations);
             dom.update_state(SendAnyMap::new());
 
-            dom.traverse_depth_first(false, |n| {
+            dom.traverse_depth_first_advanced(false, |n| {
                 $(
                     assert_eq!(n.get::<$state>().unwrap().0, 1);
                 )*

+ 1 - 1
packages/native-core/tests/custom_element.rs

@@ -301,7 +301,7 @@ fn custom_elements_work() {
             rdom.update_state(ctx);
 
             // render...
-            rdom.traverse_depth_first(true, |node| {
+            rdom.traverse_depth_first_advanced(true, |node| {
                 let node_type = &*node.node_type();
                 let height = node.height() as usize;
                 let indent = " ".repeat(height);

+ 2 - 2
packages/rink/src/focus.rs

@@ -183,7 +183,7 @@ impl FocusState {
 
                 if forward {
                     // find the closest focusable element after the current level
-                    rdom.traverse_depth_first(true, |n| {
+                    rdom.traverse_depth_first(|n| {
                         let node_level = n.get::<Focus>().unwrap().level;
                         if node_level != *focus_level
                             && node_level.focusable()
@@ -200,7 +200,7 @@ impl FocusState {
                     });
                 } else {
                     // find the closest focusable element before the current level
-                    rdom.traverse_depth_first(true, |n| {
+                    rdom.traverse_depth_first(|n| {
                         let node_level = n.get::<Focus>().unwrap().level;
                         if node_level != *focus_level
                             && node_level.focusable()

+ 1 - 1
packages/rink/src/hooks.rs

@@ -550,7 +550,7 @@ impl InnerInputState {
             // update focus
             if was_released {
                 let mut focus_id = None;
-                dom.traverse_depth_first(true, |node| {
+                dom.traverse_depth_first(|node| {
                     let node_layout = layout
                         .layout(node.get::<TaffyLayout>().unwrap().node.unwrap())
                         .unwrap();