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:
Demonthos authored and GitHub committed 2022-07-07 00:50:22 -04:00
1 parent c271fc132e
commit 4a5ae758ee
19 files changed
+990 -1103

No files matched your search

+96 -382
View File
@@ -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(&current);
} 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)
}
}