Allow Multiple dependencies for states in the RealDom (native core) (#412)
* WIP multable deps * WIP: refactor and genralize state macro * add traversable trait * update tui to use elementid to index dom
This commit is contained in:
1 parent
c271fc132e
commit
4a5ae758ee
19 files changed
+990
-1103
No files matched your search
@@ -2,4 +2,6 @@ pub mod layout_attributes;
|
||||
pub mod node_ref;
|
||||
pub mod real_dom;
|
||||
pub mod state;
|
||||
#[doc(hidden)]
|
||||
pub mod traversable;
|
||||
pub mod utils;
|
||||
@@ -70,7 +70,7 @@ impl<'a> NodeView<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub enum AttributeMask {
|
||||
All,
|
||||
Dynamic(Vec<&'static str>),
|
||||
@@ -175,7 +175,7 @@ impl Default for AttributeMask {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Clone, Debug)]
|
||||
#[derive(Default, PartialEq, Eq, Clone, Debug)]
|
||||
pub struct NodeMask {
|
||||
// must be sorted
|
||||
attritutes: AttributeMask,
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
use anymap::AnyMap;
|
||||
use fxhash::{FxHashMap, FxHashSet};
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
ops::{Index, IndexMut},
|
||||
};
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
use dioxus_core::{ElementId, Mutations, VNode, VirtualDom};
|
||||
|
||||
use crate::state::{union_ordered_iter, State};
|
||||
use crate::{
|
||||
node_ref::{AttributeMask, NodeMask},
|
||||
state::MemberId,
|
||||
};
|
||||
use crate::node_ref::{AttributeMask, NodeMask};
|
||||
use crate::state::State;
|
||||
use crate::traversable::Traversable;
|
||||
|
||||
/// 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.
|
||||
@@ -20,7 +15,7 @@ use crate::{
|
||||
pub struct RealDom<S: State> {
|
||||
root: usize,
|
||||
nodes: Vec<Option<Node<S>>>,
|
||||
nodes_listening: FxHashMap<&'static str, FxHashSet<usize>>,
|
||||
nodes_listening: FxHashMap<&'static str, FxHashSet<ElementId>>,
|
||||
node_stack: smallvec::SmallVec<[usize; 10]>,
|
||||
}
|
||||
|
||||
@@ -51,7 +46,7 @@ impl<S: State> RealDom<S> {
|
||||
}
|
||||
|
||||
/// Updates the dom, up and down state and return a set of nodes that were updated pass this to update_state.
|
||||
pub fn apply_mutations(&mut self, mutations_vec: Vec<Mutations>) -> Vec<(usize, NodeMask)> {
|
||||
pub fn apply_mutations(&mut self, mutations_vec: Vec<Mutations>) -> Vec<(ElementId, NodeMask)> {
|
||||
let mut nodes_updated = Vec::new();
|
||||
for mutations in mutations_vec {
|
||||
for e in mutations.edits {
|
||||
@@ -72,40 +67,44 @@ impl<S: State> RealDom<S> {
|
||||
.drain(self.node_stack.len() - many as usize..)
|
||||
.collect();
|
||||
for ns in drained {
|
||||
self.link_child(ns, target).unwrap();
|
||||
nodes_updated.push((ns, NodeMask::ALL));
|
||||
let id = ElementId(ns);
|
||||
self.link_child(id, ElementId(target)).unwrap();
|
||||
nodes_updated.push((id, NodeMask::ALL));
|
||||
}
|
||||
}
|
||||
ReplaceWith { root, m } => {
|
||||
let root = self.remove(root as usize).unwrap();
|
||||
let root = self.remove(ElementId(root as usize)).unwrap();
|
||||
let target = root.parent.unwrap().0;
|
||||
let drained: Vec<_> = self.node_stack.drain(0..m as usize).collect();
|
||||
for ns in drained {
|
||||
nodes_updated.push((ns, NodeMask::ALL));
|
||||
self.link_child(ns, target).unwrap();
|
||||
let id = ElementId(ns);
|
||||
nodes_updated.push((id, NodeMask::ALL));
|
||||
self.link_child(id, ElementId(target)).unwrap();
|
||||
}
|
||||
}
|
||||
InsertAfter { root, n } => {
|
||||
let target = self[root as usize].parent.unwrap().0;
|
||||
let target = self[ElementId(root as usize)].parent.unwrap().0;
|
||||
let drained: Vec<_> = self.node_stack.drain(0..n as usize).collect();
|
||||
for ns in drained {
|
||||
nodes_updated.push((ns, NodeMask::ALL));
|
||||
self.link_child(ns, target).unwrap();
|
||||
let id = ElementId(ns);
|
||||
nodes_updated.push((id, NodeMask::ALL));
|
||||
self.link_child(id, ElementId(target)).unwrap();
|
||||
}
|
||||
}
|
||||
InsertBefore { root, n } => {
|
||||
let target = self[root as usize].parent.unwrap().0;
|
||||
let target = self[ElementId(root as usize)].parent.unwrap().0;
|
||||
let drained: Vec<_> = self.node_stack.drain(0..n as usize).collect();
|
||||
for ns in drained {
|
||||
nodes_updated.push((ns, NodeMask::ALL));
|
||||
self.link_child(ns, target).unwrap();
|
||||
let id = ElementId(ns);
|
||||
nodes_updated.push((id, NodeMask::ALL));
|
||||
self.link_child(id, ElementId(target)).unwrap();
|
||||
}
|
||||
}
|
||||
Remove { root } => {
|
||||
if let Some(parent) = self[root as usize].parent {
|
||||
nodes_updated.push((parent.0, NodeMask::NONE));
|
||||
if let Some(parent) = self[ElementId(root as usize)].parent {
|
||||
nodes_updated.push((parent, NodeMask::NONE));
|
||||
}
|
||||
self.remove(root as usize).unwrap();
|
||||
self.remove(ElementId(root as usize)).unwrap();
|
||||
}
|
||||
CreateTextNode { root, text } => {
|
||||
let n = Node::new(
|
||||
@@ -152,26 +151,29 @@ impl<S: State> RealDom<S> {
|
||||
scope: _,
|
||||
root,
|
||||
} => {
|
||||
nodes_updated.push((root as usize, NodeMask::new().with_listeners()));
|
||||
let id = ElementId(root as usize);
|
||||
nodes_updated.push((id, NodeMask::new().with_listeners()));
|
||||
if let Some(v) = self.nodes_listening.get_mut(event_name) {
|
||||
v.insert(root as usize);
|
||||
v.insert(id);
|
||||
} else {
|
||||
let mut hs = FxHashSet::default();
|
||||
hs.insert(root as usize);
|
||||
hs.insert(id);
|
||||
self.nodes_listening.insert(event_name, hs);
|
||||
}
|
||||
}
|
||||
RemoveEventListener { root, event } => {
|
||||
nodes_updated.push((root as usize, NodeMask::new().with_listeners()));
|
||||
let id = ElementId(root as usize);
|
||||
nodes_updated.push((id, NodeMask::new().with_listeners()));
|
||||
let v = self.nodes_listening.get_mut(event).unwrap();
|
||||
v.remove(&(root as usize));
|
||||
v.remove(&id);
|
||||
}
|
||||
SetText {
|
||||
root,
|
||||
text: new_text,
|
||||
} => {
|
||||
let target = &mut self[root as usize];
|
||||
nodes_updated.push((root as usize, NodeMask::new().with_text()));
|
||||
let id = ElementId(root as usize);
|
||||
let target = &mut self[id];
|
||||
nodes_updated.push((id, NodeMask::new().with_text()));
|
||||
match &mut target.node_type {
|
||||
NodeType::Text { text } => {
|
||||
*text = new_text.to_string();
|
||||
@@ -180,18 +182,16 @@ impl<S: State> RealDom<S> {
|
||||
}
|
||||
}
|
||||
SetAttribute { root, field, .. } => {
|
||||
nodes_updated.push((
|
||||
root as usize,
|
||||
NodeMask::new_with_attrs(AttributeMask::single(field)),
|
||||
));
|
||||
let id = ElementId(root as usize);
|
||||
nodes_updated
|
||||
.push((id, NodeMask::new_with_attrs(AttributeMask::single(field))));
|
||||
}
|
||||
RemoveAttribute {
|
||||
root, name: field, ..
|
||||
} => {
|
||||
nodes_updated.push((
|
||||
root as usize,
|
||||
NodeMask::new_with_attrs(AttributeMask::single(field)),
|
||||
));
|
||||
let id = ElementId(root as usize);
|
||||
nodes_updated
|
||||
.push((id, NodeMask::new_with_attrs(AttributeMask::single(field))));
|
||||
}
|
||||
PopRoot {} => {
|
||||
self.node_stack.pop();
|
||||
@@ -203,309 +203,60 @@ impl<S: State> RealDom<S> {
|
||||
nodes_updated
|
||||
}
|
||||
|
||||
/// Seperated from apply_mutations because Mutations require a mutable reference to the VirtualDom.
|
||||
pub fn update_state(
|
||||
&mut self,
|
||||
vdom: &VirtualDom,
|
||||
nodes_updated: Vec<(usize, NodeMask)>,
|
||||
nodes_updated: Vec<(ElementId, NodeMask)>,
|
||||
ctx: AnyMap,
|
||||
) -> Option<FxHashSet<usize>> {
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
enum StatesToCheck {
|
||||
All,
|
||||
Some(Vec<MemberId>),
|
||||
}
|
||||
impl StatesToCheck {
|
||||
fn union(&self, other: &Self) -> Self {
|
||||
match (self, other) {
|
||||
(Self::Some(s), Self::Some(o)) => Self::Some(union_ordered_iter(
|
||||
s.iter().copied(),
|
||||
o.iter().copied(),
|
||||
s.len() + o.len(),
|
||||
)),
|
||||
_ => Self::All,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct NodeRef {
|
||||
id: usize,
|
||||
height: u16,
|
||||
node_mask: NodeMask,
|
||||
to_check: StatesToCheck,
|
||||
}
|
||||
impl NodeRef {
|
||||
fn union_with(&mut self, other: &Self) {
|
||||
self.node_mask = self.node_mask.union(&other.node_mask);
|
||||
self.to_check = self.to_check.union(&other.to_check);
|
||||
}
|
||||
}
|
||||
impl Eq for NodeRef {}
|
||||
impl PartialEq for NodeRef {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id == other.id && self.height == other.height
|
||||
}
|
||||
}
|
||||
impl Ord for NodeRef {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.partial_cmp(other).unwrap()
|
||||
}
|
||||
}
|
||||
impl PartialOrd for NodeRef {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
// Sort nodes first by height, then if the height is the same id.
|
||||
// The order of the id does not matter it just helps with binary search later
|
||||
Some(self.height.cmp(&other.height).then(self.id.cmp(&other.id)))
|
||||
}
|
||||
}
|
||||
|
||||
let mut to_rerender = FxHashSet::default();
|
||||
to_rerender.extend(nodes_updated.iter().map(|(id, _)| id));
|
||||
let mut nodes_updated: Vec<_> = nodes_updated
|
||||
.into_iter()
|
||||
.map(|(id, mask)| NodeRef {
|
||||
id,
|
||||
height: self[id].height,
|
||||
node_mask: mask,
|
||||
to_check: StatesToCheck::All,
|
||||
})
|
||||
.collect();
|
||||
nodes_updated.sort();
|
||||
|
||||
// Combine mutations that affect the same node.
|
||||
let mut last_node: Option<NodeRef> = None;
|
||||
let mut new_nodes_updated = VecDeque::new();
|
||||
for current in nodes_updated.into_iter() {
|
||||
if let Some(node) = &mut last_node {
|
||||
if *node == current {
|
||||
node.union_with(¤t);
|
||||
} else {
|
||||
new_nodes_updated.push_back(last_node.replace(current).unwrap());
|
||||
}
|
||||
} else {
|
||||
last_node = Some(current);
|
||||
}
|
||||
}
|
||||
if let Some(node) = last_node {
|
||||
new_nodes_updated.push_back(node);
|
||||
}
|
||||
let nodes_updated = new_nodes_updated;
|
||||
|
||||
// update the state that only depends on nodes. The order does not matter.
|
||||
for node_ref in &nodes_updated {
|
||||
let mut changed = false;
|
||||
let node = &mut self[node_ref.id];
|
||||
let mut ids = match &node_ref.to_check {
|
||||
StatesToCheck::All => node.state.node_dep_types(&node_ref.node_mask),
|
||||
// this should only be triggered from the current node, so all members will need to be checked
|
||||
StatesToCheck::Some(_) => unreachable!(),
|
||||
};
|
||||
let mut i = 0;
|
||||
while i < ids.len() {
|
||||
let id = ids[i];
|
||||
let node = &mut self[node_ref.id];
|
||||
let vnode = node.element(vdom);
|
||||
if let Some(members_effected) =
|
||||
node.state.update_node_dep_state(id, vnode, vdom, &ctx)
|
||||
{
|
||||
debug_assert!(members_effected.node_dep.iter().all(|i| i >= &id));
|
||||
for m in &members_effected.node_dep {
|
||||
if let Err(idx) = ids.binary_search(m) {
|
||||
ids.insert(idx, *m);
|
||||
}
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
if changed {
|
||||
to_rerender.insert(node_ref.id);
|
||||
}
|
||||
}
|
||||
|
||||
// bubble up state. To avoid calling reduce more times than nessisary start from the bottom and go up.
|
||||
let mut to_bubble = nodes_updated.clone();
|
||||
while let Some(node_ref) = to_bubble.pop_back() {
|
||||
let NodeRef {
|
||||
id,
|
||||
height,
|
||||
node_mask,
|
||||
to_check,
|
||||
} = node_ref;
|
||||
let (node, children) = self.get_node_children_mut(id).unwrap();
|
||||
let children_state: Vec<_> = children.iter().map(|c| &c.state).collect();
|
||||
let mut ids = match to_check {
|
||||
StatesToCheck::All => node.state.child_dep_types(&node_mask),
|
||||
StatesToCheck::Some(ids) => ids,
|
||||
};
|
||||
let mut changed = Vec::new();
|
||||
let mut i = 0;
|
||||
while i < ids.len() {
|
||||
let id = ids[i];
|
||||
let vnode = node.element(vdom);
|
||||
if let Some(members_effected) =
|
||||
node.state
|
||||
.update_child_dep_state(id, vnode, vdom, &children_state, &ctx)
|
||||
{
|
||||
debug_assert!(members_effected.node_dep.iter().all(|i| i >= &id));
|
||||
for m in members_effected.node_dep {
|
||||
if let Err(idx) = ids.binary_search(&m) {
|
||||
ids.insert(idx, m);
|
||||
}
|
||||
}
|
||||
for m in members_effected.child_dep {
|
||||
changed.push(m);
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
if let Some(parent_id) = node.parent {
|
||||
if !changed.is_empty() {
|
||||
to_rerender.insert(id);
|
||||
let i = to_bubble.partition_point(
|
||||
|NodeRef {
|
||||
id: other_id,
|
||||
height: h,
|
||||
..
|
||||
}| {
|
||||
*h < height - 1 || (*h == height - 1 && *other_id < parent_id.0)
|
||||
},
|
||||
);
|
||||
// make sure the parent is not already queued
|
||||
if i < to_bubble.len() && to_bubble[i].id == parent_id.0 {
|
||||
to_bubble[i].to_check =
|
||||
to_bubble[i].to_check.union(&StatesToCheck::Some(changed));
|
||||
} else {
|
||||
to_bubble.insert(
|
||||
i,
|
||||
NodeRef {
|
||||
id: parent_id.0,
|
||||
height: height - 1,
|
||||
node_mask: NodeMask::NONE,
|
||||
to_check: StatesToCheck::Some(changed),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// push down state. To avoid calling reduce more times than nessisary start from the top and go down.
|
||||
let mut to_push = nodes_updated;
|
||||
while let Some(node_ref) = to_push.pop_front() {
|
||||
let NodeRef {
|
||||
id,
|
||||
height,
|
||||
node_mask,
|
||||
to_check,
|
||||
} = node_ref;
|
||||
let node = &self[id];
|
||||
let mut ids = match to_check {
|
||||
StatesToCheck::All => node.state.parent_dep_types(&node_mask),
|
||||
StatesToCheck::Some(ids) => ids,
|
||||
};
|
||||
let mut changed = Vec::new();
|
||||
let (node, parent) = self.get_node_parent_mut(id).unwrap();
|
||||
let mut i = 0;
|
||||
while i < ids.len() {
|
||||
let id = ids[i];
|
||||
let vnode = node.element(vdom);
|
||||
let parent = parent.as_deref();
|
||||
let state = &mut node.state;
|
||||
if let Some(members_effected) =
|
||||
state.update_parent_dep_state(id, vnode, vdom, parent.map(|n| &n.state), &ctx)
|
||||
{
|
||||
debug_assert!(members_effected.node_dep.iter().all(|i| i >= &id));
|
||||
for m in members_effected.node_dep {
|
||||
if let Err(idx) = ids.binary_search(&m) {
|
||||
ids.insert(idx, m);
|
||||
}
|
||||
}
|
||||
for m in members_effected.parent_dep {
|
||||
changed.push(m);
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
to_rerender.insert(id);
|
||||
if !changed.is_empty() {
|
||||
let node = &self[id];
|
||||
if let NodeType::Element { children, .. } = &node.node_type {
|
||||
for c in children {
|
||||
let i = to_push.partition_point(
|
||||
|NodeRef {
|
||||
id: other_id,
|
||||
height: h,
|
||||
..
|
||||
}| {
|
||||
*h < height + 1 || (*h == height + 1 && *other_id < c.0)
|
||||
},
|
||||
);
|
||||
if i < to_push.len() && to_push[i].id == c.0 {
|
||||
to_push[i].to_check = to_push[i]
|
||||
.to_check
|
||||
.union(&StatesToCheck::Some(changed.clone()));
|
||||
} else {
|
||||
to_push.insert(
|
||||
i,
|
||||
NodeRef {
|
||||
id: c.0,
|
||||
height: height + 1,
|
||||
node_mask: NodeMask::NONE,
|
||||
to_check: StatesToCheck::Some(changed.clone()),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(to_rerender)
|
||||
) -> FxHashSet<ElementId> {
|
||||
S::update(
|
||||
&nodes_updated,
|
||||
&mut self.map(|n| &n.state, |n| &mut n.state),
|
||||
vdom,
|
||||
&ctx,
|
||||
)
|
||||
}
|
||||
|
||||
fn link_child(&mut self, child_id: usize, parent_id: usize) -> Option<()> {
|
||||
fn link_child(&mut self, child_id: ElementId, parent_id: ElementId) -> Option<()> {
|
||||
debug_assert_ne!(child_id, parent_id);
|
||||
let parent = &mut self[parent_id];
|
||||
parent.add_child(ElementId(child_id));
|
||||
parent.add_child(child_id);
|
||||
let parent_height = parent.height + 1;
|
||||
self[child_id].set_parent(ElementId(parent_id));
|
||||
self[child_id].set_parent(parent_id);
|
||||
self.increase_height(child_id, parent_height);
|
||||
Some(())
|
||||
}
|
||||
|
||||
fn increase_height(&mut self, id: usize, amount: u16) {
|
||||
fn increase_height(&mut self, id: ElementId, amount: u16) {
|
||||
let n = &mut self[id];
|
||||
n.height += amount;
|
||||
if let NodeType::Element { children, .. } = &n.node_type {
|
||||
for c in children.clone() {
|
||||
self.increase_height(c.0, amount);
|
||||
self.increase_height(c, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove a node and it's children from the dom.
|
||||
fn remove(&mut self, id: usize) -> Option<Node<S>> {
|
||||
fn remove(&mut self, id: ElementId) -> Option<Node<S>> {
|
||||
// We do not need to remove the node from the parent's children list for children.
|
||||
fn inner<S: State>(dom: &mut RealDom<S>, id: usize) -> Option<Node<S>> {
|
||||
let mut node = dom.nodes[id as usize].take()?;
|
||||
fn inner<S: State>(dom: &mut RealDom<S>, id: ElementId) -> Option<Node<S>> {
|
||||
let mut node = dom.nodes[id.0].take()?;
|
||||
if let NodeType::Element { children, .. } = &mut node.node_type {
|
||||
for c in children {
|
||||
inner(dom, c.0)?;
|
||||
inner(dom, *c)?;
|
||||
}
|
||||
}
|
||||
Some(node)
|
||||
}
|
||||
let mut node = self.nodes[id as usize].take()?;
|
||||
let mut node = self.nodes[id.0].take()?;
|
||||
if let Some(parent) = node.parent {
|
||||
let parent = &mut self[parent];
|
||||
parent.remove_child(ElementId(id));
|
||||
parent.remove_child(id);
|
||||
}
|
||||
if let NodeType::Element { children, .. } = &mut node.node_type {
|
||||
for c in children {
|
||||
inner(self, c.0)?;
|
||||
inner(self, *c)?;
|
||||
}
|
||||
}
|
||||
Some(node)
|
||||
@@ -521,62 +272,6 @@ impl<S: State> RealDom<S> {
|
||||
self.nodes[id] = Some(node);
|
||||
}
|
||||
|
||||
pub fn get(&self, id: usize) -> Option<&Node<S>> {
|
||||
self.nodes.get(id)?.as_ref()
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, id: usize) -> Option<&mut Node<S>> {
|
||||
self.nodes.get_mut(id)?.as_mut()
|
||||
}
|
||||
|
||||
// this is safe because no node will have itself as a child
|
||||
pub fn get_node_children_mut(
|
||||
&mut self,
|
||||
id: usize,
|
||||
) -> Option<(&mut Node<S>, Vec<&mut Node<S>>)> {
|
||||
let ptr = self.nodes.as_mut_ptr();
|
||||
unsafe {
|
||||
if id >= self.nodes.len() {
|
||||
None
|
||||
} else {
|
||||
let node = &mut *ptr.add(id);
|
||||
if let Some(node) = node.as_mut() {
|
||||
let children = match &node.node_type {
|
||||
NodeType::Element { children, .. } => children
|
||||
.iter()
|
||||
.map(|id| (&mut *ptr.add(id.0)).as_mut().unwrap())
|
||||
.collect(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
Some((node, children))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this is safe because no node will have itself as a parent
|
||||
pub fn get_node_parent_mut(
|
||||
&mut self,
|
||||
id: usize,
|
||||
) -> Option<(&mut Node<S>, Option<&mut Node<S>>)> {
|
||||
let ptr = self.nodes.as_mut_ptr();
|
||||
unsafe {
|
||||
let node = &mut *ptr.add(id);
|
||||
if id >= self.nodes.len() {
|
||||
None
|
||||
} else if let Some(node) = node.as_mut() {
|
||||
let parent = node
|
||||
.parent
|
||||
.map(|id| (&mut *ptr.add(id.0)).as_mut().unwrap());
|
||||
Some((node, parent))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_listening_sorted(&self, event: &'static str) -> Vec<&Node<S>> {
|
||||
if let Some(nodes) = self.nodes_listening.get(event) {
|
||||
let mut listening: Vec<_> = nodes.iter().map(|id| &self[*id]).collect();
|
||||
@@ -658,7 +353,7 @@ impl<S: State> RealDom<S> {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let NodeType::Element { children, .. } = &self[self.root].node_type {
|
||||
if let NodeType::Element { children, .. } = &self[ElementId(self.root)].node_type {
|
||||
for c in children {
|
||||
inner(self, *c, &mut f);
|
||||
}
|
||||
@@ -677,7 +372,7 @@ impl<S: State> RealDom<S> {
|
||||
}
|
||||
}
|
||||
let root = self.root;
|
||||
if let NodeType::Element { children, .. } = &mut self[root].node_type {
|
||||
if let NodeType::Element { children, .. } = &mut self[ElementId(root)].node_type {
|
||||
for c in children.clone() {
|
||||
inner(self, c, &mut f);
|
||||
}
|
||||
@@ -685,30 +380,17 @@ impl<S: State> RealDom<S> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: State> Index<usize> for RealDom<S> {
|
||||
type Output = Node<S>;
|
||||
|
||||
fn index(&self, idx: usize) -> &Self::Output {
|
||||
self.get(idx).expect("Node does not exist")
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: State> Index<ElementId> for RealDom<S> {
|
||||
type Output = Node<S>;
|
||||
|
||||
fn index(&self, idx: ElementId) -> &Self::Output {
|
||||
&self[idx.0]
|
||||
self.get(idx).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: State> IndexMut<usize> for RealDom<S> {
|
||||
fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
|
||||
self.get_mut(idx).expect("Node does not exist")
|
||||
}
|
||||
}
|
||||
impl<S: State> IndexMut<ElementId> for RealDom<S> {
|
||||
fn index_mut(&mut self, idx: ElementId) -> &mut Self::Output {
|
||||
&mut self[idx.0]
|
||||
self.get_mut(idx).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -772,3 +454,35 @@ impl<S: State> Node<S> {
|
||||
self.parent = Some(parent);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: State> Traversable for RealDom<T> {
|
||||
type Id = ElementId;
|
||||
type Node = Node<T>;
|
||||
|
||||
fn height(&self, id: Self::Id) -> Option<u16> {
|
||||
Some(<Self as Traversable>::get(self, id)?.height)
|
||||
}
|
||||
|
||||
fn get(&self, id: Self::Id) -> Option<&Self::Node> {
|
||||
self.nodes.get(id.0)?.as_ref()
|
||||
}
|
||||
|
||||
fn get_mut(&mut self, id: Self::Id) -> Option<&mut Self::Node> {
|
||||
self.nodes.get_mut(id.0)?.as_mut()
|
||||
}
|
||||
|
||||
fn children(&self, id: Self::Id) -> &[Self::Id] {
|
||||
if let Some(node) = <Self as Traversable>::get(self, id) {
|
||||
match &node.node_type {
|
||||
NodeType::Element { children, .. } => children,
|
||||
_ => &[],
|
||||
}
|
||||
} else {
|
||||
&[]
|
||||
}
|
||||
}
|
||||
|
||||
fn parent(&self, id: Self::Id) -> Option<Self::Id> {
|
||||
<Self as Traversable>::get(self, id).and_then(|n| n.parent)
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
fmt::Debug,
|
||||
ops::{Add, AddAssign, Sub, SubAssign},
|
||||
};
|
||||
use std::{cmp::Ordering, fmt::Debug};
|
||||
|
||||
use anymap::AnyMap;
|
||||
use dioxus_core::VNode;
|
||||
use dioxus_core::ElementId;
|
||||
use fxhash::FxHashSet;
|
||||
|
||||
use crate::node_ref::{NodeMask, NodeView};
|
||||
use crate::traversable::Traversable;
|
||||
|
||||
pub(crate) fn union_ordered_iter<T: Ord + Debug>(
|
||||
s_iter: impl Iterator<Item = T>,
|
||||
@@ -50,7 +48,7 @@ pub trait ChildDepState {
|
||||
/// The context is passed to the [ChildDepState::reduce] when it is pushed down.
|
||||
/// This is sometimes nessisary for lifetime purposes.
|
||||
type Ctx;
|
||||
/// This must be either a [ChildDepState] or [NodeDepState]
|
||||
/// This must be either a [ChildDepState], or [NodeDepState]
|
||||
type DepState;
|
||||
const NODE_MASK: NodeMask = NodeMask::NONE;
|
||||
fn reduce<'a>(
|
||||
@@ -73,70 +71,77 @@ pub trait ParentDepState {
|
||||
/// This must be either a [ParentDepState] or [NodeDepState]
|
||||
type DepState;
|
||||
const NODE_MASK: NodeMask = NodeMask::NONE;
|
||||
fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, ctx: &Self::Ctx) -> bool;
|
||||
fn reduce<'a>(
|
||||
&mut self,
|
||||
node: NodeView,
|
||||
parent: Option<&'a Self::DepState>,
|
||||
ctx: &Self::Ctx,
|
||||
) -> bool;
|
||||
}
|
||||
|
||||
/// This state that is upadated lazily. For example any propertys that do not effect other parts of the dom like bg-color.
|
||||
/// Called when the current node's node properties are modified or a sibling's [NodeDepState] is modified.
|
||||
/// Called at most once per update.
|
||||
pub trait NodeDepState {
|
||||
/// NodeDepState is the only state that can accept multiple dependancies, but only from the current node.
|
||||
/// ```rust
|
||||
/// impl<'a, 'b> NodeDepState<(&'a TextWrap, &'b ChildLayout)> for Layout {
|
||||
/// type Ctx = LayoutCache;
|
||||
/// const NODE_MASK: NodeMask =
|
||||
/// NodeMask::new_with_attrs(AttributeMask::Static(&sorted_str_slice!([
|
||||
/// "width", "height"
|
||||
/// ])))
|
||||
/// .with_text();
|
||||
/// fn reduce<'a>(
|
||||
/// &mut self,
|
||||
/// node: NodeView,
|
||||
/// siblings: (&'a TextWrap, &'b ChildLayout),
|
||||
/// ctx: &Self::Ctx,
|
||||
/// ) -> bool {
|
||||
/// let old = self.clone();
|
||||
/// let (text_wrap, child_layout) = siblings;
|
||||
/// if TextWrap::Wrap == text_wrap {
|
||||
/// if let Some(text) = node.text() {
|
||||
/// let lines = text_wrap.get_lines(text);
|
||||
/// self.width = lines.max_by(|l| l.len());
|
||||
/// self.height = lines.len();
|
||||
/// return old != self;
|
||||
/// }
|
||||
/// }
|
||||
/// let mut width = child_layout.width;
|
||||
/// let mut height = child_layout.width;
|
||||
/// for attr in node.attributes() {
|
||||
/// match attr.name {
|
||||
/// "width" => {
|
||||
/// width = attr.value.as_text().unwrap().parse().unwrap();
|
||||
/// }
|
||||
/// "height" => {
|
||||
/// height = attr.value.as_text().unwrap().parse().unwrap();
|
||||
/// }
|
||||
/// _ => unreachable!(),
|
||||
/// }
|
||||
/// }
|
||||
/// self.width = width;
|
||||
/// self.height = height;
|
||||
/// old != self
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// The generic argument (Depstate) must be a tuple containing any number of borrowed elments that are either a [ChildDepState], [ParentDepState] or [NodeDepState].
|
||||
pub trait NodeDepState<DepState> {
|
||||
type Ctx;
|
||||
type DepState: NodeDepState;
|
||||
const NODE_MASK: NodeMask = NodeMask::NONE;
|
||||
fn reduce(&mut self, node: NodeView, sibling: &Self::DepState, ctx: &Self::Ctx) -> bool;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ChildStatesChanged {
|
||||
pub node_dep: Vec<MemberId>,
|
||||
pub child_dep: Vec<MemberId>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ParentStatesChanged {
|
||||
pub node_dep: Vec<MemberId>,
|
||||
pub parent_dep: Vec<MemberId>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NodeStatesChanged {
|
||||
pub node_dep: Vec<MemberId>,
|
||||
fn reduce(&mut self, node: NodeView, siblings: DepState, ctx: &Self::Ctx) -> bool;
|
||||
}
|
||||
|
||||
/// Do not implement this trait. It is only meant to be derived and used through [crate::real_dom::RealDom].
|
||||
pub trait State: Default + Clone {
|
||||
const SIZE: usize;
|
||||
|
||||
fn update_node_dep_state<'a>(
|
||||
&'a mut self,
|
||||
ty: MemberId,
|
||||
node: &'a VNode<'a>,
|
||||
#[doc(hidden)]
|
||||
fn update<'a, T: Traversable<Node = Self, Id = ElementId>>(
|
||||
dirty: &[(ElementId, NodeMask)],
|
||||
state_tree: &'a mut T,
|
||||
vdom: &'a dioxus_core::VirtualDom,
|
||||
ctx: &AnyMap,
|
||||
) -> Option<NodeStatesChanged>;
|
||||
/// This must be a valid resolution order. (no nodes updated before a state they rely on)
|
||||
fn child_dep_types(&self, mask: &NodeMask) -> Vec<MemberId>;
|
||||
|
||||
fn update_parent_dep_state<'a>(
|
||||
&'a mut self,
|
||||
ty: MemberId,
|
||||
node: &'a VNode<'a>,
|
||||
vdom: &'a dioxus_core::VirtualDom,
|
||||
parent: Option<&Self>,
|
||||
ctx: &AnyMap,
|
||||
) -> Option<ParentStatesChanged>;
|
||||
/// This must be a valid resolution order. (no nodes updated before a state they rely on)
|
||||
fn parent_dep_types(&self, mask: &NodeMask) -> Vec<MemberId>;
|
||||
|
||||
fn update_child_dep_state<'a>(
|
||||
&'a mut self,
|
||||
ty: MemberId,
|
||||
node: &'a VNode<'a>,
|
||||
vdom: &'a dioxus_core::VirtualDom,
|
||||
children: &[&Self],
|
||||
ctx: &AnyMap,
|
||||
) -> Option<ChildStatesChanged>;
|
||||
/// This must be a valid resolution order. (no nodes updated before a state they rely on)
|
||||
fn node_dep_types(&self, mask: &NodeMask) -> Vec<MemberId>;
|
||||
) -> FxHashSet<ElementId>;
|
||||
}
|
||||
|
||||
// Todo: once GATs land we can model multable dependencies
|
||||
@@ -159,44 +164,14 @@ impl ChildDepState for () {
|
||||
impl ParentDepState for () {
|
||||
type Ctx = ();
|
||||
type DepState = ();
|
||||
fn reduce(&mut self, _: NodeView, _: Option<&Self::DepState>, _: &Self::Ctx) -> bool {
|
||||
fn reduce<'a>(&mut self, _: NodeView, _: Option<&'a Self::DepState>, _: &Self::Ctx) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeDepState for () {
|
||||
impl NodeDepState<()> for () {
|
||||
type Ctx = ();
|
||||
type DepState = ();
|
||||
fn reduce(&mut self, _: NodeView, _sibling: &Self::DepState, _: &Self::Ctx) -> bool {
|
||||
fn reduce(&mut self, _: NodeView, _sibling: (), _: &Self::Ctx) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct MemberId(pub usize);
|
||||
|
||||
impl Sub<usize> for MemberId {
|
||||
type Output = MemberId;
|
||||
fn sub(self, rhs: usize) -> Self::Output {
|
||||
MemberId(self.0 - rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<usize> for MemberId {
|
||||
type Output = MemberId;
|
||||
fn add(self, rhs: usize) -> Self::Output {
|
||||
MemberId(self.0 + rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<usize> for MemberId {
|
||||
fn sub_assign(&mut self, rhs: usize) {
|
||||
*self = *self - rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<usize> for MemberId {
|
||||
fn add_assign(&mut self, rhs: usize) {
|
||||
*self = *self + rhs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
pub trait Traversable {
|
||||
type Id: Copy;
|
||||
type Node;
|
||||
|
||||
fn height(&self, id: Self::Id) -> Option<u16>;
|
||||
|
||||
fn get(&self, id: Self::Id) -> Option<&Self::Node>;
|
||||
fn get_mut(&mut self, id: Self::Id) -> Option<&mut Self::Node>;
|
||||
|
||||
fn children(&self, node: Self::Id) -> &[Self::Id];
|
||||
fn parent(&self, node: Self::Id) -> Option<Self::Id>;
|
||||
|
||||
fn map<N, F: Fn(&Self::Node) -> &N, FMut: Fn(&mut Self::Node) -> &mut N>(
|
||||
&mut self,
|
||||
f: F,
|
||||
f_mut: FMut,
|
||||
) -> Map<Self, N, F, FMut>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Map {
|
||||
tree: self,
|
||||
f,
|
||||
f_mut,
|
||||
}
|
||||
}
|
||||
|
||||
// this is safe because no node will have itself as it's parent
|
||||
fn get_node_parent_mut(
|
||||
&mut self,
|
||||
id: Self::Id,
|
||||
) -> (Option<&mut Self::Node>, Option<&mut Self::Node>) {
|
||||
let node = self.get_mut(id).map(|n| n as *mut _);
|
||||
let parent = self
|
||||
.parent(id)
|
||||
.and_then(|n| self.get_mut(n))
|
||||
.map(|n| n as *mut _);
|
||||
unsafe { (node.map(|n| &mut *n), parent.map(|n| &mut *n)) }
|
||||
}
|
||||
|
||||
// this is safe because no node will have itself as a child
|
||||
fn get_node_children_mut(
|
||||
&mut self,
|
||||
id: Self::Id,
|
||||
) -> (Option<&mut Self::Node>, Vec<&mut Self::Node>) {
|
||||
let node = self.get_mut(id).map(|n| n as *mut _);
|
||||
let mut children = Vec::new();
|
||||
let children_indexes = self.children(id).to_vec();
|
||||
for id in children_indexes {
|
||||
if let Some(n) = self.get_mut(id) {
|
||||
children.push(unsafe { &mut *(n as *mut _) });
|
||||
}
|
||||
}
|
||||
unsafe { (node.map(|n| &mut *n), children) }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Map<
|
||||
'a,
|
||||
T: Traversable,
|
||||
N,
|
||||
F: Fn(&<T as Traversable>::Node) -> &N,
|
||||
FMut: Fn(&mut <T as Traversable>::Node) -> &mut N,
|
||||
> {
|
||||
f: F,
|
||||
f_mut: FMut,
|
||||
tree: &'a mut T,
|
||||
}
|
||||
|
||||
impl<
|
||||
'a,
|
||||
T: Traversable,
|
||||
N,
|
||||
F: Fn(&<T as Traversable>::Node) -> &N,
|
||||
FMut: Fn(&mut <T as Traversable>::Node) -> &mut N,
|
||||
> Traversable for Map<'a, T, N, F, FMut>
|
||||
{
|
||||
type Id = <T as Traversable>::Id;
|
||||
type Node = N;
|
||||
|
||||
fn height(&self, id: Self::Id) -> Option<u16> {
|
||||
self.tree.height(id)
|
||||
}
|
||||
|
||||
fn get(&self, id: Self::Id) -> Option<&Self::Node> {
|
||||
self.tree.get(id).map(&self.f)
|
||||
}
|
||||
|
||||
fn get_mut(&mut self, id: Self::Id) -> Option<&mut Self::Node> {
|
||||
self.tree.get_mut(id).map(&self.f_mut)
|
||||
}
|
||||
|
||||
fn children(&self, id: Self::Id) -> &[Self::Id] {
|
||||
self.tree.children(id)
|
||||
}
|
||||
|
||||
fn parent(&self, id: Self::Id) -> Option<Self::Id> {
|
||||
self.tree.parent(id)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user