mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-14 16:37:14 +00:00
handle passes entering or avoiding the shadow tree
This commit is contained in:
parent
32945998c3
commit
60be849ff0
10 changed files with 167 additions and 133 deletions
|
@ -336,7 +336,7 @@ pub fn partial_derive_state(_: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
let (#(#split_views,)*) = data;
|
let (#(#split_views,)*) = data;
|
||||||
let tree = run_view.tree.clone();
|
let tree = run_view.tree.clone();
|
||||||
let node_types = run_view.node_type.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));
|
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
|
// get all of the states from the tree view
|
||||||
// Safety: No node has itself as a parent or child.
|
// Safety: No node has itself as a parent or child.
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
use shipyard::Component;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
node::{FromAnyValue, NodeType},
|
node::{FromAnyValue, NodeType},
|
||||||
node_ref::AttributeMask,
|
node_ref::AttributeMask,
|
||||||
prelude::{NodeImmutable, NodeMut, RealDom},
|
prelude::{NodeImmutable, NodeMut, RealDom},
|
||||||
real_dom::NodeTypeMut,
|
|
||||||
shadow_dom::ShadowDom,
|
|
||||||
NodeId,
|
NodeId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,7 +27,7 @@ impl<V: FromAnyValue + Send + Sync> CustomElementRegistry<V> {
|
||||||
self.builders.insert(
|
self.builders.insert(
|
||||||
W::NAME,
|
W::NAME,
|
||||||
CustomElementBuilder {
|
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<V: FromAnyValue + Send + Sync> CustomElementRegistry<V> {
|
||||||
if let Some(element_tag) = element_tag {
|
if let Some(element_tag) = element_tag {
|
||||||
if let Some(builder) = self.builders.get(element_tag.as_str()) {
|
if let Some(builder) = self.builders.get(element_tag.as_str()) {
|
||||||
let boxed_widget = {
|
let boxed_widget = {
|
||||||
let light_root_id = node.id();
|
|
||||||
let dom = node.real_dom_mut();
|
let dom = node.real_dom_mut();
|
||||||
(builder.create)(dom, light_root_id)
|
(builder.create)(dom)
|
||||||
};
|
};
|
||||||
|
|
||||||
let boxed_widget = CustomElementManager {
|
let boxed_widget = CustomElementManager {
|
||||||
inner: Arc::new(RwLock::new(boxed_widget)),
|
inner: Arc::new(RwLock::new(boxed_widget)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let NodeTypeMut::Element(mut el) = node.node_type_mut()else{
|
node.insert(boxed_widget);
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CustomElementBuilder<V: FromAnyValue + Send + Sync> {
|
struct CustomElementBuilder<V: FromAnyValue + Send + Sync> {
|
||||||
create: fn(&mut RealDom<V>, NodeId) -> Box<dyn CustomElementUpdater<V>>,
|
create: fn(&mut RealDom<V>) -> Box<dyn CustomElementUpdater<V>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A controlled element that renders to a shadow DOM
|
/// A controlled element that renders to a shadow DOM
|
||||||
|
@ -71,11 +65,16 @@ pub trait CustomElement<V: FromAnyValue + Send + Sync = ()>: Send + Sync + 'stat
|
||||||
const NAME: &'static str;
|
const NAME: &'static str;
|
||||||
|
|
||||||
/// Create a new widget without mounting it.
|
/// Create a new widget without mounting it.
|
||||||
fn create(dom: &mut RealDom<V>, light_root_id: NodeId) -> Self;
|
fn create(dom: &mut RealDom<V>) -> 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;
|
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<NodeId> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Called when the attributes of the widget are changed.
|
/// Called when the attributes of the widget are changed.
|
||||||
fn attributes_changed(&mut self, _dom: &mut RealDom<V>, _attributes: &AttributeMask);
|
fn attributes_changed(&mut self, _dom: &mut RealDom<V>, _attributes: &AttributeMask);
|
||||||
}
|
}
|
||||||
|
@ -88,14 +87,14 @@ trait ElementFactory<W: CustomElementUpdater<V>, V: FromAnyValue + Send + Sync =
|
||||||
const NAME: &'static str;
|
const NAME: &'static str;
|
||||||
|
|
||||||
/// Create a new widget.
|
/// Create a new widget.
|
||||||
fn create(dom: &mut RealDom<V>, light_root_id: NodeId) -> W;
|
fn create(dom: &mut RealDom<V>) -> W;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: CustomElement<V>, V: FromAnyValue + Send + Sync> ElementFactory<W, V> for W {
|
impl<W: CustomElement<V>, V: FromAnyValue + Send + Sync> ElementFactory<W, V> for W {
|
||||||
const NAME: &'static str = W::NAME;
|
const NAME: &'static str = W::NAME;
|
||||||
|
|
||||||
fn create(dom: &mut RealDom<V>, light_root_id: NodeId) -> Self {
|
fn create(dom: &mut RealDom<V>) -> Self {
|
||||||
Self::create(dom, light_root_id)
|
Self::create(dom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,6 +117,7 @@ impl<W: CustomElement<V>, V: FromAnyValue + Send + Sync> CustomElementUpdater<V>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Clone)]
|
||||||
pub struct CustomElementManager<V: FromAnyValue = ()> {
|
pub struct CustomElementManager<V: FromAnyValue = ()> {
|
||||||
inner: Arc<RwLock<Box<dyn CustomElementUpdater<V>>>>,
|
inner: Arc<RwLock<Box<dyn CustomElementUpdater<V>>>>,
|
||||||
}
|
}
|
||||||
|
@ -126,12 +126,11 @@ impl<V: FromAnyValue + Send + Sync> CustomElementManager<V> {
|
||||||
pub fn root(&self) -> NodeId {
|
pub fn root(&self) -> NodeId {
|
||||||
self.inner.read().unwrap().root()
|
self.inner.read().unwrap().root()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: FromAnyValue> Clone for CustomElementManager<V> {
|
pub fn on_attributes_changed(&self, dom: &mut RealDom<V>, attributes: &AttributeMask) {
|
||||||
fn clone(&self) -> Self {
|
self.inner
|
||||||
Self {
|
.write()
|
||||||
inner: self.inner.clone(),
|
.unwrap()
|
||||||
}
|
.attributes_changed(dom, attributes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -262,7 +262,6 @@ fn create_template_node<V: FromAnyValue + Send + Sync>(
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
listeners: FxHashSet::default(),
|
listeners: FxHashSet::default(),
|
||||||
shadow: None,
|
|
||||||
});
|
});
|
||||||
let node_id = rdom.create_node(node).id();
|
let node_id = rdom.create_node(node).id();
|
||||||
for child in *children {
|
for child in *children {
|
||||||
|
|
|
@ -16,7 +16,6 @@ pub mod node_ref;
|
||||||
pub mod node_watcher;
|
pub mod node_watcher;
|
||||||
mod passes;
|
mod passes;
|
||||||
pub mod real_dom;
|
pub mod real_dom;
|
||||||
mod shadow_dom;
|
|
||||||
pub mod tree;
|
pub mod tree;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,6 @@ use std::{
|
||||||
fmt::{Debug, Display},
|
fmt::{Debug, Display},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::shadow_dom::ShadowDom;
|
|
||||||
|
|
||||||
/// A element node in the RealDom
|
/// A element node in the RealDom
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct ElementNode<V: FromAnyValue = ()> {
|
pub struct ElementNode<V: FromAnyValue = ()> {
|
||||||
|
@ -20,9 +18,6 @@ pub struct ElementNode<V: FromAnyValue = ()> {
|
||||||
pub attributes: FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue<V>>,
|
pub attributes: FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue<V>>,
|
||||||
/// The events the element is listening for
|
/// The events the element is listening for
|
||||||
pub listeners: FxHashSet<String>,
|
pub listeners: FxHashSet<String>,
|
||||||
/// The shadow dom of the element
|
|
||||||
// This is a rare property, so it is stored in an `Box` to save space
|
|
||||||
pub shadow: Option<Box<ShadowDom<V>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementNode {
|
impl ElementNode {
|
||||||
|
@ -33,7 +28,6 @@ impl ElementNode {
|
||||||
namespace: namespace.into(),
|
namespace: namespace.into(),
|
||||||
attributes: Default::default(),
|
attributes: Default::default(),
|
||||||
listeners: Default::default(),
|
listeners: Default::default(),
|
||||||
shadow: Default::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,6 +194,7 @@ pub fn run_pass<V: FromAnyValue + Send + Sync>(
|
||||||
dependants: FxHashSet<TypeId>,
|
dependants: FxHashSet<TypeId>,
|
||||||
pass_direction: PassDirection,
|
pass_direction: PassDirection,
|
||||||
view: RunPassView<V>,
|
view: RunPassView<V>,
|
||||||
|
enter_shadow_dom: bool,
|
||||||
mut update_node: impl FnMut(NodeId, &SendAnyMap) -> bool,
|
mut update_node: impl FnMut(NodeId, &SendAnyMap) -> bool,
|
||||||
) {
|
) {
|
||||||
let RunPassView {
|
let RunPassView {
|
||||||
|
@ -209,9 +210,32 @@ pub fn run_pass<V: FromAnyValue + Send + Sync>(
|
||||||
while let Some((height, id)) = dirty.pop_front(type_id) {
|
while let Some((height, id)) = dirty.pop_front(type_id) {
|
||||||
if (update_node)(id, ctx) {
|
if (update_node)(id, ctx) {
|
||||||
nodes_updated.insert(id);
|
nodes_updated.insert(id);
|
||||||
for id in tree.children_ids(id) {
|
let shadow_tree = tree.shadow_tree(id);
|
||||||
for dependant in &dependants {
|
match (enter_shadow_dom, shadow_tree) {
|
||||||
dirty.insert(*dependant, id, height + 1);
|
(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<V: FromAnyValue + Send + Sync>(
|
||||||
while let Some((height, id)) = dirty.pop_back(type_id) {
|
while let Some((height, id)) = dirty.pop_back(type_id) {
|
||||||
if (update_node)(id, ctx) {
|
if (update_node)(id, ctx) {
|
||||||
nodes_updated.insert(id);
|
nodes_updated.insert(id);
|
||||||
if let Some(id) = tree.parent_id(id) {
|
|
||||||
for dependant in &dependants {
|
// If this pass uses the shadow dom, update the light dom root if this node is a root
|
||||||
dirty.insert(*dependant, id, height - 1);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ use std::collections::VecDeque;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use crate::custom_element::CustomElementRegistry;
|
use crate::custom_element::{CustomElementManager, CustomElementRegistry};
|
||||||
use crate::node::{
|
use crate::node::{
|
||||||
ElementNode, FromAnyValue, NodeType, OwnedAttributeDiscription, OwnedAttributeValue, TextNode,
|
ElementNode, FromAnyValue, NodeType, OwnedAttributeDiscription, OwnedAttributeValue, TextNode,
|
||||||
};
|
};
|
||||||
|
@ -18,7 +18,6 @@ use crate::node_ref::{NodeMask, NodeMaskBuilder};
|
||||||
use crate::node_watcher::{AttributeWatcher, NodeWatcher};
|
use crate::node_watcher::{AttributeWatcher, NodeWatcher};
|
||||||
use crate::passes::{DirtyNodeStates, TypeErasedState};
|
use crate::passes::{DirtyNodeStates, TypeErasedState};
|
||||||
use crate::prelude::AttributeMaskBuilder;
|
use crate::prelude::AttributeMaskBuilder;
|
||||||
use crate::shadow_dom::ShadowDom;
|
|
||||||
use crate::tree::{TreeMut, TreeMutView, TreeRef, TreeRefView};
|
use crate::tree::{TreeMut, TreeMutView, TreeRef, TreeRefView};
|
||||||
use crate::NodeId;
|
use crate::NodeId;
|
||||||
use crate::{FxDashSet, SendAnyMap};
|
use crate::{FxDashSet, SendAnyMap};
|
||||||
|
@ -113,7 +112,7 @@ pub struct RealDom<V: FromAnyValue + Send + Sync = ()> {
|
||||||
attribute_watchers: AttributeWatchers<V>,
|
attribute_watchers: AttributeWatchers<V>,
|
||||||
workload: ScheduledWorkload,
|
workload: ScheduledWorkload,
|
||||||
root_id: NodeId,
|
root_id: NodeId,
|
||||||
custom_elements: CustomElementRegistry<V>,
|
custom_elements: Arc<RwLock<CustomElementRegistry<V>>>,
|
||||||
phantom: std::marker::PhantomData<V>,
|
phantom: std::marker::PhantomData<V>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +141,6 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
||||||
namespace: Some("Root".to_string()),
|
namespace: Some("Root".to_string()),
|
||||||
attributes: FxHashMap::default(),
|
attributes: FxHashMap::default(),
|
||||||
listeners: FxHashSet::default(),
|
listeners: FxHashSet::default(),
|
||||||
shadow: None,
|
|
||||||
});
|
});
|
||||||
let root_id = world.add_entity(root_node);
|
let root_id = world.add_entity(root_node);
|
||||||
{
|
{
|
||||||
|
@ -189,7 +187,10 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
||||||
|
|
||||||
/// Create a new node of the given type in the dom and return a mutable reference to it.
|
/// 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<NodeType<V>>) -> NodeMut<'_, V> {
|
pub fn create_node(&mut self, node: impl Into<NodeType<V>>) -> 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.tree_mut().create_node(id);
|
||||||
|
|
||||||
self.dirty_nodes
|
self.dirty_nodes
|
||||||
|
@ -201,6 +202,15 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
||||||
.mark_dirty(id, NodeMaskBuilder::ALL.build());
|
.mark_dirty(id, NodeMaskBuilder::ALL.build());
|
||||||
self.dirty_nodes.nodes_created.insert(id);
|
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)
|
NodeMut::new(id, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,10 +290,9 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
||||||
let passes = std::mem::take(&mut self.dirty_nodes.passes_updated);
|
let passes = std::mem::take(&mut self.dirty_nodes.passes_updated);
|
||||||
let nodes_updated = std::mem::take(&mut self.dirty_nodes.nodes_updated);
|
let nodes_updated = std::mem::take(&mut self.dirty_nodes.nodes_updated);
|
||||||
|
|
||||||
// call attribute watchers
|
|
||||||
for (node_id, mask) in &nodes_updated {
|
for (node_id, mask) in &nodes_updated {
|
||||||
if self.contains(*node_id) {
|
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();
|
let watchers = self.attribute_watchers.clone();
|
||||||
if let Ok(mut watchers) = watchers.try_write() {
|
if let Ok(mut watchers) = watchers.try_write() {
|
||||||
for watcher in &mut *watchers {
|
for watcher in &mut *watchers {
|
||||||
|
@ -293,6 +302,15 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// call custom element watchers
|
||||||
|
let custom_element_manager = {
|
||||||
|
let node = self.get_mut(*node_id).unwrap();
|
||||||
|
node.get::<CustomElementManager<V>>().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<V: FromAnyValue + Send + Sync> ElementNodeMut<'_, V> {
|
||||||
pub fn listeners(&self) -> &FxHashSet<String> {
|
pub fn listeners(&self) -> &FxHashSet<String> {
|
||||||
&self.element().listeners
|
&self.element().listeners
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the shadow root of the element
|
|
||||||
pub fn shadow_root(&self) -> Option<&ShadowDom<V>> {
|
|
||||||
self.element().shadow.as_deref()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the shadow root of the element
|
|
||||||
pub fn shadow_root_mut(&mut self) -> &mut Option<Box<ShadowDom<V>>> {
|
|
||||||
&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.
|
// Create a workload from all of the passes. This orders the passes so that each pass will only run at most once.
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
use crate::{custom_element::CustomElementManager, node::FromAnyValue, prelude::NodeRef, NodeId};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ShadowDom<V: FromAnyValue> {
|
|
||||||
shadow_root: NodeId,
|
|
||||||
updater: CustomElementManager<V>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: FromAnyValue + Send + Sync> ShadowDom<V> {
|
|
||||||
pub fn new(updater: CustomElementManager<V>) -> Self {
|
|
||||||
Self {
|
|
||||||
shadow_root: updater.root(),
|
|
||||||
updater,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: FromAnyValue> Debug for ShadowDom<V> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("ShadowDom")
|
|
||||||
.field("shadow_root", &self.shadow_root)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,15 +4,15 @@ use crate::NodeId;
|
||||||
use shipyard::{Component, EntitiesViewMut, Get, View, ViewMut};
|
use shipyard::{Component, EntitiesViewMut, Get, View, ViewMut};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
/// A subtree of a tree.
|
/// A shadow_tree of a tree.
|
||||||
#[derive(PartialEq, Eq, Clone, Debug, Component)]
|
#[derive(PartialEq, Eq, Clone, Debug, Component)]
|
||||||
pub struct Subtree {
|
pub struct ShadowTree {
|
||||||
/// The root of the subtree
|
/// The root of the shadow_tree
|
||||||
shadow_roots: Vec<NodeId>,
|
pub shadow_roots: Vec<NodeId>,
|
||||||
/// The node that children of the super tree should be inserted under.
|
/// The node that children of the super tree should be inserted under.
|
||||||
slot: Option<NodeId>,
|
pub slot: Option<NodeId>,
|
||||||
/// The node in the super tree that the subtree is attached to.
|
/// The node in the super tree that the shadow_tree is attached to.
|
||||||
super_tree_root: NodeId,
|
pub super_tree_root: NodeId,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A node in a tree.
|
/// A node in a tree.
|
||||||
|
@ -20,9 +20,11 @@ pub struct Subtree {
|
||||||
pub struct Node {
|
pub struct Node {
|
||||||
parent: Option<NodeId>,
|
parent: Option<NodeId>,
|
||||||
children: Vec<NodeId>,
|
children: Vec<NodeId>,
|
||||||
child_subtree: Option<Subtree>,
|
child_subtree: Option<ShadowTree>,
|
||||||
/// If this node is a slot in a subtree, this is node whose child_subtree is that subtree.
|
/// If this node is a slot in a shadow_tree, this is node whose child_subtree is that shadow_tree.
|
||||||
slot_for_supertree: Option<NodeId>,
|
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>,
|
||||||
height: u16,
|
height: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,8 +39,13 @@ pub trait TreeRef {
|
||||||
fn parent_id(&self, id: NodeId) -> Option<NodeId>;
|
fn parent_id(&self, id: NodeId) -> Option<NodeId>;
|
||||||
/// The children ids of the node.
|
/// The children ids of the node.
|
||||||
fn children_ids(&self, id: NodeId) -> Vec<NodeId>;
|
fn children_ids(&self, id: NodeId) -> Vec<NodeId>;
|
||||||
/// The subtree tree under the node.
|
/// The shadow tree tree under the node.
|
||||||
fn subtree(&self, id: NodeId) -> Option<&Subtree>;
|
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>;
|
||||||
/// The height of the node.
|
/// The height of the node.
|
||||||
fn height(&self, id: NodeId) -> Option<u16>;
|
fn height(&self, id: NodeId) -> Option<u16>;
|
||||||
/// Returns true if the node exists.
|
/// 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);
|
fn insert_before(&mut self, old_id: NodeId, new_id: NodeId);
|
||||||
/// Inserts a node after another node.
|
/// Inserts a node after another node.
|
||||||
fn insert_after(&mut self, old_id: NodeId, new_id: NodeId);
|
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<NodeId>, slot: Option<NodeId>);
|
fn create_subtree(&mut self, id: NodeId, shadow_roots: Vec<NodeId>, slot: Option<NodeId>);
|
||||||
/// Remove any subtree.
|
/// Remove any shadow tree.
|
||||||
fn remove_subtree(&mut self, id: NodeId);
|
fn remove_subtree(&mut self, id: NodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,34 +91,42 @@ impl<'a> TreeRef for TreeRefView<'a> {
|
||||||
self.get(id).is_ok()
|
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()
|
self.get(id).ok()?.child_subtree.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn slot_for_light_tree(&self, id: NodeId) -> Option<NodeId> {
|
||||||
|
self.get(id).ok()?.slot_for_light_tree
|
||||||
|
}
|
||||||
|
|
||||||
|
fn light_tree_root(&self, id: NodeId) -> Option<NodeId> {
|
||||||
|
self.get(id).ok()?.light_tree_root
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TreeMut for TreeMutView<'a> {
|
impl<'a> TreeMut for TreeMutView<'a> {
|
||||||
fn remove(&mut self, id: NodeId) {
|
fn remove(&mut self, id: NodeId) {
|
||||||
fn recurse(tree: &mut TreeMutView<'_>, id: NodeId) {
|
fn recurse(tree: &mut TreeMutView<'_>, id: NodeId) {
|
||||||
let (supertree, children) = {
|
let (light_tree, children) = {
|
||||||
let node = (&mut tree.1).get(id).unwrap();
|
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 {
|
for child in children {
|
||||||
recurse(tree, child);
|
recurse(tree, child);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this node is a slot in a subtree, remove it from the subtree.
|
// If this node is a slot in a shadow_tree, remove it from the shadow_tree.
|
||||||
if let Some(supertree) = supertree {
|
if let Some(light_tree) = light_tree {
|
||||||
let supertree_root = (&mut tree.1).get(supertree).unwrap();
|
let light_tree_root = (&mut tree.1).get(light_tree).unwrap();
|
||||||
|
|
||||||
if let Some(subtree) = &mut supertree_root.child_subtree {
|
if let Some(shadow_tree) = &mut light_tree_root.child_subtree {
|
||||||
subtree.slot = None;
|
shadow_tree.slot = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
supertree_root.children.is_empty(),
|
light_tree_root.children.is_empty(),
|
||||||
"Subtree root should have no children when slot is removed."
|
"ShadowTree root should have no children when slot is removed."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,7 +152,8 @@ impl<'a> TreeMut for TreeMutView<'a> {
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
height: 0,
|
height: 0,
|
||||||
child_subtree: None,
|
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 light_root_height;
|
||||||
{
|
{
|
||||||
let subtree = Subtree {
|
let shadow_tree = ShadowTree {
|
||||||
super_tree_root: id,
|
super_tree_root: id,
|
||||||
shadow_roots: shadow_roots.clone(),
|
shadow_roots: shadow_roots.clone(),
|
||||||
slot,
|
slot,
|
||||||
|
@ -220,20 +236,20 @@ impl<'a> TreeMut for TreeMutView<'a> {
|
||||||
|
|
||||||
let light_root = node_data_mut
|
let light_root = node_data_mut
|
||||||
.get(id)
|
.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;
|
light_root_height = light_root.height;
|
||||||
|
|
||||||
if let Some(slot) = slot {
|
if let Some(slot) = slot {
|
||||||
let slot = node_data_mut
|
let slot = node_data_mut
|
||||||
.get(slot)
|
.get(slot)
|
||||||
.expect("tried to create subtree with non-existent slot");
|
.expect("tried to create shadow_tree with non-existent slot");
|
||||||
slot.slot_for_supertree = Some(id);
|
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 {
|
for root in shadow_roots {
|
||||||
set_height(self, root, light_root_height + 1);
|
set_height(self, root, light_root_height + 1);
|
||||||
}
|
}
|
||||||
|
@ -243,13 +259,13 @@ impl<'a> TreeMut for TreeMutView<'a> {
|
||||||
let (_, node_data_mut) = self;
|
let (_, node_data_mut) = self;
|
||||||
|
|
||||||
if let Ok(node) = node_data_mut.get(id) {
|
if let Ok(node) = node_data_mut.get(id) {
|
||||||
if let Some(subtree) = node.child_subtree.take() {
|
if let Some(shadow_tree) = node.child_subtree.take() {
|
||||||
// Remove the slot's link to the subtree
|
// Remove the slot's link to the shadow_tree
|
||||||
if let Some(slot) = subtree.slot {
|
if let Some(slot) = shadow_tree.slot {
|
||||||
let slot = node_data_mut
|
let slot = node_data_mut
|
||||||
.get(slot)
|
.get(slot)
|
||||||
.expect("tried to remove subtree with non-existent slot");
|
.expect("tried to remove shadow_tree with non-existent slot");
|
||||||
slot.slot_for_supertree = None;
|
slot.slot_for_light_tree = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let node = node_data_mut.get(id).unwrap();
|
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
|
// 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);
|
set_height(self, *root, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -272,13 +288,13 @@ impl<'a> TreeMut for TreeMutView<'a> {
|
||||||
|
|
||||||
fn child_height(parent: &Node, tree: &impl TreeRef) -> u16 {
|
fn child_height(parent: &Node, tree: &impl TreeRef) -> u16 {
|
||||||
match &parent.child_subtree {
|
match &parent.child_subtree {
|
||||||
Some(subtree) => {
|
Some(shadow_tree) => {
|
||||||
if let Some(slot) = subtree.slot {
|
if let Some(slot) = shadow_tree.slot {
|
||||||
tree.height(slot)
|
tree.height(slot)
|
||||||
.expect("Attempted to read a slot that does not exist")
|
.expect("Attempted to read a slot that does not exist")
|
||||||
+ 1
|
+ 1
|
||||||
} else {
|
} 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,
|
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
|
/// Sets the height of a node and updates the height of all its children
|
||||||
fn set_height(tree: &mut TreeMutView<'_>, node: NodeId, height: u16) {
|
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_data_mut = &mut tree.1;
|
||||||
let mut node = (&mut node_data_mut).get(node).unwrap();
|
let mut node = (&mut node_data_mut).get(node).unwrap();
|
||||||
node.height = height;
|
node.height = height;
|
||||||
|
|
||||||
(
|
(
|
||||||
node.child_subtree.clone(),
|
node.child_subtree.clone(),
|
||||||
node.slot_for_supertree,
|
node.slot_for_light_tree,
|
||||||
node.children.clone(),
|
node.children.clone(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// If the children are actually part of a subtree, there height is determined by the height of the subtree
|
// If the children are actually part of a shadow_tree, there height is determined by the height of the shadow_tree
|
||||||
if let Some(subtree) = subtree {
|
if let Some(shadow_tree) = shadow_tree {
|
||||||
// Set the height of the subtree roots
|
// Set the height of the shadow_tree roots
|
||||||
for &shadow_root in &subtree.shadow_roots {
|
for &shadow_root in &shadow_tree.shadow_roots {
|
||||||
set_height(tree, shadow_root, height);
|
set_height(tree, shadow_root, height);
|
||||||
}
|
}
|
||||||
} else {
|
} 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 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(supertree) = supertree {
|
if let Some(light_tree) = light_tree {
|
||||||
let children = (&tree.1).get(supertree).unwrap().children.clone();
|
let children = (&tree.1).get(light_tree).unwrap().children.clone();
|
||||||
for child in children {
|
for child in children {
|
||||||
set_height(tree, child, height + 1);
|
set_height(tree, child, height + 1);
|
||||||
}
|
}
|
||||||
|
@ -344,10 +360,20 @@ impl<'a> TreeRef for TreeMutView<'a> {
|
||||||
self.1.get(id).is_ok()
|
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;
|
let node_data = &self.1;
|
||||||
node_data.get(id).unwrap().child_subtree.as_ref()
|
node_data.get(id).unwrap().child_subtree.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn slot_for_light_tree(&self, id: NodeId) -> Option<NodeId> {
|
||||||
|
let node_data = &self.1;
|
||||||
|
node_data.get(id).unwrap().slot_for_light_tree
|
||||||
|
}
|
||||||
|
|
||||||
|
fn light_tree_root(&self, id: NodeId) -> Option<NodeId> {
|
||||||
|
let node_data = &self.1;
|
||||||
|
node_data.get(id).unwrap().light_tree_root
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -375,7 +401,7 @@ fn creation() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn subtree() {
|
fn shadow_tree() {
|
||||||
use shipyard::World;
|
use shipyard::World;
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct Num(i32);
|
struct Num(i32);
|
||||||
|
@ -432,7 +458,7 @@ fn subtree() {
|
||||||
&[shadow_parent_id]
|
&[shadow_parent_id]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
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)
|
Some(parent_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ fn create_blank_element() -> NodeType {
|
||||||
namespace: None,
|
namespace: None,
|
||||||
attributes: FxHashMap::default(),
|
attributes: FxHashMap::default(),
|
||||||
listeners: FxHashSet::default(),
|
listeners: FxHashSet::default(),
|
||||||
shadow: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue