diff --git a/packages/native-core-macro/src/lib.rs b/packages/native-core-macro/src/lib.rs index 5ec2fceba..cc99ee746 100644 --- a/packages/native-core-macro/src/lib.rs +++ b/packages/native-core-macro/src/lib.rs @@ -105,8 +105,8 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream { let gen = quote! { impl State for #type_name{ - fn update_node_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: Option<&'a dioxus_core::VElement<'a>>, ctx: &anymap::AnyMap) -> bool{ - use dioxus_native_core::real_dom_new_api::NodeDepState as _; + fn update_node_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: &'a dioxus_core::VNode<'a>, ctx: &anymap::AnyMap) -> bool{ + use dioxus_native_core::state::NodeDepState as _; // println!("called update_node_dep_state with ty: {:?}", ty); if false { unreachable!(); @@ -117,8 +117,8 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream { } } - fn update_parent_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: Option<&'a dioxus_core::VElement<'a>>, parent: Option<&Self>, ctx: &anymap::AnyMap) -> bool{ - use dioxus_native_core::real_dom_new_api::ParentDepState as _; + fn update_parent_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: &'a dioxus_core::VNode<'a>, parent: Option<&Self>, ctx: &anymap::AnyMap) -> bool{ + use dioxus_native_core::state::ParentDepState as _; // println!("called update_parent_dep_state with ty: {:?}", ty); if false { unreachable!(); @@ -129,8 +129,8 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream { } } - fn update_child_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: Option<&'a dioxus_core::VElement<'a>>, children: &[&Self], ctx: &anymap::AnyMap) -> bool{ - use dioxus_native_core::real_dom_new_api::ChildDepState as _; + fn update_child_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: &'a dioxus_core::VNode<'a>, children: &[&Self], ctx: &anymap::AnyMap) -> bool{ + use dioxus_native_core::state::ChildDepState as _; // println!("called update_child_dep_state with ty: {:?}", ty); if false { unreachable!() @@ -141,7 +141,7 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream { } } - fn child_dep_types(&self, mask: &dioxus_native_core::real_dom_new_api::NodeMask) -> Vec{ + fn child_dep_types(&self, mask: &dioxus_native_core::state::NodeMask) -> Vec{ let mut dep_types = Vec::new(); #(if #child_types::NODE_MASK.overlaps(mask) { dep_types.push(std::any::TypeId::of::<#child_types>()); @@ -149,7 +149,7 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream { dep_types } - fn parent_dep_types(&self, mask: &dioxus_native_core::real_dom_new_api::NodeMask) -> Vec{ + fn parent_dep_types(&self, mask: &dioxus_native_core::state::NodeMask) -> Vec{ let mut dep_types = Vec::new(); #(if #parent_types::NODE_MASK.overlaps(mask) { dep_types.push(std::any::TypeId::of::<#parent_types>()); @@ -157,7 +157,7 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream { dep_types } - fn node_dep_types(&self, mask: &dioxus_native_core::real_dom_new_api::NodeMask) -> Vec{ + fn node_dep_types(&self, mask: &dioxus_native_core::state::NodeMask) -> Vec{ let mut dep_types = Vec::new(); #(if #node_types::NODE_MASK.overlaps(mask) { dep_types.push(std::any::TypeId::of::<#node_types>()); @@ -314,7 +314,7 @@ impl<'a> StateMember<'a> { quote!(self.#ident.reduce(#node_view, #get_ctx)) } DepKind::ChildDepState => { - quote!(self.#ident.reduce(#node_view, children.iter().map(|s| &s.#dep_ident).collect(), #get_ctx)) + quote!(self.#ident.reduce(#node_view, children.iter().map(|s| &s.#dep_ident), #get_ctx)) } DepKind::ParentDepState => { quote!(self.#ident.reduce(#node_view, parent.as_ref().map(|p| &p.#dep_ident), #get_ctx)) @@ -337,10 +337,8 @@ impl<'a> StateMember<'a> { fn type_id(&self) -> quote::__private::TokenStream { let ty = &self.mem.ty; - // quote!(std::any::TypeId::of::<#ty>()) quote!({ let type_id = std::any::TypeId::of::<#ty>(); - // println!("{:?}", type_id); type_id }) } diff --git a/packages/native-core/src/lib.rs b/packages/native-core/src/lib.rs index 580380f68..7692a5ae1 100644 --- a/packages/native-core/src/lib.rs +++ b/packages/native-core/src/lib.rs @@ -1,4 +1,4 @@ pub mod layout_attributes; pub mod real_dom; -pub mod real_dom_new_api; +pub mod state; pub use dioxus_native_core_macro; diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index 55ae7e0a8..1c25806c3 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -8,7 +8,7 @@ use std::{ use dioxus_core::{ElementId, Mutations, VNode, VirtualDom}; -use crate::real_dom_new_api::{AttributeMask, NodeMask, State}; +use crate::state::{union_ordered_iter, AttributeMask, NodeMask, State}; /// 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. @@ -166,7 +166,10 @@ impl RealDom { text: new_text, } => { let target = &mut self[root as usize]; - nodes_updated.push((root as usize, NodeMask::NONE)); + nodes_updated.push(( + root as usize, + NodeMask::new(AttributeMask::NONE, false, false, true), + )); match &mut target.node_type { NodeType::Text { text } => { *text = new_text.to_string(); @@ -177,7 +180,7 @@ impl RealDom { SetAttribute { root, field, .. } => { nodes_updated.push(( root as usize, - NodeMask::new(AttributeMask::single(field), false, false), + NodeMask::new(AttributeMask::single(field), false, false, false), )); } RemoveAttribute { @@ -185,7 +188,7 @@ impl RealDom { } => { nodes_updated.push(( root as usize, - NodeMask::new(AttributeMask::single(field), false, false), + NodeMask::new(AttributeMask::single(field), false, false, false), )); } PopRoot {} => { @@ -210,65 +213,122 @@ impl RealDom { All, Some(Vec), } + 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 { + // 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)| (id, self[id].height, mask, StatesToCheck::All)) + .map(|(id, mask)| NodeRef { + id, + height: self[id].height, + node_mask: mask, + to_check: StatesToCheck::All, + }) .collect(); - // Sort nodes first by height, then if the height is the same id. - nodes_updated.sort_by(|fst, snd| fst.1.cmp(&snd.1).then(fst.0.cmp(&snd.0))); - { - // Combine mutations that affect the same node. - let current_key = None; - for i in 0..nodes_updated.len() { - let current = nodes_updated; + nodes_updated.sort(); + + // Combine mutations that affect the same node. + let mut last_node: Option = 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); } } - println!("{:?}", nodes_updated); + 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 (id, _height, mask, to_check) in &nodes_updated { + for node_ref in &nodes_updated { let mut changed = false; - let node = &mut self[*id as usize]; - let ids = match to_check { - StatesToCheck::All => node.state.node_dep_types(&mask), + let node = &mut self[node_ref.id]; + let ids = match &node_ref.to_check { + StatesToCheck::All => node.state.node_dep_types(&node_ref.node_mask), StatesToCheck::Some(_) => unreachable!(), }; for ty in ids { - let node = &mut self[*id as usize]; - let el = if let &VNode::Element(e) = node.element(vdom) { - Some(e) - } else { - None - }; - changed |= node.state.update_node_dep_state(ty, el, &ctx); + let node = &mut self[node_ref.id]; + let vnode = node.element(vdom); + changed |= node.state.update_node_dep_state(ty, vnode, &ctx); } if changed { - to_rerender.insert(*id); + 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: VecDeque<_> = nodes_updated.clone().into(); - while let Some((id, height, mask, to_check)) = to_bubble.pop_back() { + 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 ids = match to_check { - StatesToCheck::All => node.state.child_dep_types(&mask), + StatesToCheck::All => node.state.child_dep_types(&node_mask), StatesToCheck::Some(ids) => ids, }; let mut changed = Vec::new(); for ty in ids { - let el = if let &VNode::Element(e) = node.element(vdom) { - Some(e) - } else { - None - }; + let vnode = node.element(vdom); if node .state - .update_child_dep_state(ty, el, &children_state, &ctx) + .update_child_dep_state(ty, vnode, &children_state, &ctx) { changed.push(ty); } @@ -276,19 +336,28 @@ impl RealDom { if let Some(parent_id) = node.parent { if !changed.is_empty() { to_rerender.insert(id); - let i = to_bubble.partition_point(|(other_id, h, ..)| { - *h < height - 1 || (*h == height - 1 && *other_id < parent_id.0) - }); + 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].0 != parent_id.0 { + 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, - ( - parent_id.0, - height - 1, - NodeMask::NONE, - StatesToCheck::Some(changed), - ), + NodeRef { + id: parent_id.0, + height: height - 1, + node_mask: NodeMask::NONE, + to_check: StatesToCheck::Some(changed), + }, ); } } @@ -296,26 +365,28 @@ impl RealDom { } // push down state. To avoid calling reduce more times than nessisary start from the top and go down. - let mut to_push: VecDeque<_> = nodes_updated.clone().into(); - while let Some((id, height, mask, to_check)) = to_push.pop_front() { + let mut to_push = nodes_updated.clone(); + while let Some(node_ref) = to_push.pop_front() { + let NodeRef { + id, + height, + node_mask, + to_check, + } = node_ref; let node = &self[id]; let ids = match to_check { - StatesToCheck::All => node.state.parent_dep_types(&mask), + 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(); for ty in ids { - let el = if let &VNode::Element(e) = node.element(vdom) { - Some(e) - } else { - None - }; + let vnode = node.element(vdom); let parent = parent.as_deref(); let state = &mut node.state; if state.update_parent_dep_state( ty, - el, + vnode, parent.filter(|n| n.id.0 != 0).map(|n| &n.state), &ctx, ) { @@ -328,18 +399,28 @@ impl RealDom { let node = &self[id]; if let NodeType::Element { children, .. } = &node.node_type { for c in children { - let i = to_push.partition_point(|(other_id, h, ..)| { - *h < height + 1 || (*h == height + 1 && *other_id < c.0) - }); - if i >= to_push.len() || to_push[i].0 != c.0 { + 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, - ( - c.0, - height + 1, - NodeMask::NONE, - StatesToCheck::Some(changed.clone()), - ), + NodeRef { + id: c.0, + height: height + 1, + node_mask: NodeMask::NONE, + to_check: StatesToCheck::Some(changed.clone()), + }, ); } } diff --git a/packages/native-core/src/real_dom_new_api.rs b/packages/native-core/src/state.rs similarity index 66% rename from packages/native-core/src/real_dom_new_api.rs rename to packages/native-core/src/state.rs index 042e625c1..1e7135580 100644 --- a/packages/native-core/src/real_dom_new_api.rs +++ b/packages/native-core/src/state.rs @@ -1,43 +1,96 @@ -use std::any::TypeId; +use std::{any::TypeId, fmt::Debug}; use anymap::AnyMap; -use dioxus_core::{Attribute, VElement}; +use dioxus_core::{Attribute, ElementId, VElement, VNode, VText}; + +pub(crate) fn union_ordered_iter( + s_iter: impl Iterator, + o_iter: impl Iterator, + new_len_guess: usize, +) -> Vec { + let mut s_peekable = s_iter.peekable(); + let mut o_peekable = o_iter.peekable(); + let mut v = Vec::with_capacity(new_len_guess); + while let Some(s_i) = s_peekable.peek() { + loop { + if let Some(o_i) = o_peekable.peek() { + if o_i > s_i { + break; + } else if o_i < s_i { + v.push(o_peekable.next().unwrap()); + } + } else { + break; + } + } + v.push(s_peekable.next().unwrap()); + } + while let Some(o_i) = o_peekable.next() { + v.push(o_i); + } + for w in v.windows(2) { + debug_assert!(w[1] > w[0]); + } + v +} #[derive(Debug)] pub struct NodeView<'a> { - inner: Option<&'a VElement<'a>>, - view: NodeMask, + inner: &'a VNode<'a>, + mask: NodeMask, } impl<'a> NodeView<'a> { - pub fn new(velement: Option<&'a VElement<'a>>, view: NodeMask) -> Self { + pub fn new(vnode: &'a VNode<'a>, view: NodeMask) -> Self { Self { - inner: velement, - view: view, + inner: vnode, + mask: view, } } + pub fn id(&self) -> ElementId { + self.inner.mounted_id() + } + pub fn tag(&self) -> Option<&'a str> { - if self.view.tag { - self.inner.map(|el| el.tag) - } else { - None - } + self.mask.tag.then(|| self.el().map(|el| el.tag)).flatten() } pub fn namespace(&self) -> Option<&'a str> { - if self.view.namespace { - self.inner.map(|el| el.namespace).flatten() + self.mask + .namespace + .then(|| self.el().map(|el| el.namespace).flatten()) + .flatten() + } + + pub fn attributes(&self) -> impl Iterator> { + self.el() + .map(|el| el.attributes) + .unwrap_or_default() + .iter() + .filter(|a| self.mask.attritutes.contains_attribute(&a.name)) + } + + pub fn text(&self) -> Option<&str> { + self.mask + .text + .then(|| self.txt().map(|txt| txt.text)) + .flatten() + } + + fn el(&self) -> Option<&'a VElement<'a>> { + if let VNode::Element(el) = &self.inner { + Some(el) } else { None } } - pub fn attributes(&self) -> impl Iterator> { - self.inner - .map(|el| el.attributes) - .unwrap_or_default() - .iter() - .filter(|a| self.view.attritutes.contains_attribute(&a.name)) + fn txt(&self) -> Option<&'a VText<'a>> { + if let VNode::Text(txt) = &self.inner { + Some(txt) + } else { + None + } } } @@ -54,8 +107,8 @@ impl AttributeMask { fn contains_attribute(&self, attr: &'static str) -> bool { match self { AttributeMask::All => true, - AttributeMask::Dynamic(l) => l.contains(&attr), - AttributeMask::Static(l) => l.contains(&attr), + AttributeMask::Dynamic(l) => l.binary_search(&attr).is_ok(), + AttributeMask::Static(l) => l.binary_search(&attr).is_ok(), } } @@ -78,46 +131,19 @@ impl AttributeMask { } pub fn union(&self, other: &Self) -> Self { - pub fn union_iter( - s_iter: impl Iterator, - o_iter: impl Iterator, - ) -> Vec<&'static str> { - let mut s_peekable = s_iter.peekable(); - let mut o_peekable = o_iter.peekable(); - let mut v = Vec::new(); - while let Some(s_i) = s_peekable.peek() { - loop { - if let Some(o_i) = o_peekable.peek() { - if o_i > s_i { - break; - } else { - v.push(o_peekable.next().unwrap()); - } - } else { - break; - } - } - v.push(s_peekable.next().unwrap()); - } - while let Some(o_i) = o_peekable.next() { - v.push(o_i); - } - v - } - let new = match (self, other) { - (AttributeMask::Dynamic(s), AttributeMask::Dynamic(o)) => { - AttributeMask::Dynamic(union_iter(s.iter().copied(), o.iter().copied())) - } - (AttributeMask::Static(s), AttributeMask::Dynamic(o)) => { - AttributeMask::Dynamic(union_iter(s.iter().copied(), o.iter().copied())) - } - (AttributeMask::Dynamic(s), AttributeMask::Static(o)) => { - AttributeMask::Dynamic(union_iter(s.iter().copied(), o.iter().copied())) - } - (AttributeMask::Static(s), AttributeMask::Static(o)) => { - AttributeMask::Dynamic(union_iter(s.iter().copied(), o.iter().copied())) - } + (AttributeMask::Dynamic(s), AttributeMask::Dynamic(o)) => AttributeMask::Dynamic( + union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()), + ), + (AttributeMask::Static(s), AttributeMask::Dynamic(o)) => AttributeMask::Dynamic( + union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()), + ), + (AttributeMask::Dynamic(s), AttributeMask::Static(o)) => AttributeMask::Dynamic( + union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()), + ), + (AttributeMask::Static(s), AttributeMask::Static(o)) => AttributeMask::Dynamic( + union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()), + ), _ => AttributeMask::All, }; new.verify(); @@ -179,29 +205,37 @@ pub struct NodeMask { attritutes: AttributeMask, tag: bool, namespace: bool, + text: bool, } impl NodeMask { - pub const NONE: Self = Self::new(AttributeMask::Static(&[]), false, false); - pub const ALL: Self = Self::new(AttributeMask::All, true, true); + pub const NONE: Self = Self::new(AttributeMask::Static(&[]), false, false, false); + pub const ALL: Self = Self::new(AttributeMask::All, true, true, true); /// attritutes must be sorted! - pub const fn new(attritutes: AttributeMask, tag: bool, namespace: bool) -> Self { + pub const fn new(attritutes: AttributeMask, tag: bool, namespace: bool, text: bool) -> Self { Self { attritutes, tag, namespace, + text, } } pub fn overlaps(&self, other: &Self) -> bool { (self.tag && other.tag) || (self.namespace && other.namespace) - || self.attritutes_overlap(other) + || self.attritutes.overlaps(&other.attritutes) + || (self.text && other.text) } - fn attritutes_overlap(&self, other: &Self) -> bool { - self.attritutes.overlaps(&other.attritutes) + pub fn union(&self, other: &Self) -> Self { + Self { + attritutes: self.attritutes.union(&other.attritutes), + tag: self.tag | other.tag, + namespace: self.namespace | other.namespace, + text: self.text | other.text, + } } } @@ -213,8 +247,15 @@ pub trait ChildDepState { /// This is sometimes nessisary for lifetime purposes. type Ctx; type DepState: ChildDepState; - const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::NONE, false, false); - fn reduce(&mut self, node: NodeView, children: Vec<&Self::DepState>, ctx: &Self::Ctx) -> bool; + const NODE_MASK: NodeMask = NodeMask::NONE; + fn reduce<'a>( + &mut self, + node: NodeView, + children: impl Iterator, + ctx: &Self::Ctx, + ) -> bool + where + Self::DepState: 'a; } /// This state that is passed down to children. For example text properties (`` `` ``) would be passed to children. @@ -225,7 +266,7 @@ pub trait ParentDepState { /// This is sometimes nessisary for lifetime purposes. type Ctx; type DepState: ParentDepState; - const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::NONE, false, false); + const NODE_MASK: NodeMask = NodeMask::NONE; fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, ctx: &Self::Ctx) -> bool; } @@ -234,7 +275,7 @@ pub trait ParentDepState { /// Called at most once per update. pub trait NodeDepState { type Ctx; - const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::NONE, false, false); + const NODE_MASK: NodeMask = NodeMask::NONE; fn reduce(&mut self, node: NodeView, ctx: &Self::Ctx) -> bool; } @@ -242,7 +283,7 @@ pub trait State: Default + Clone { fn update_node_dep_state<'a>( &'a mut self, ty: TypeId, - node: Option<&'a VElement<'a>>, + node: &'a VNode<'a>, ctx: &AnyMap, ) -> bool; /// This must be a valid resolution order. (no nodes updated before a state they rely on) @@ -251,7 +292,7 @@ pub trait State: Default + Clone { fn update_parent_dep_state<'a>( &'a mut self, ty: TypeId, - node: Option<&'a VElement<'a>>, + node: &'a VNode<'a>, parent: Option<&Self>, ctx: &AnyMap, ) -> bool; @@ -261,7 +302,7 @@ pub trait State: Default + Clone { fn update_child_dep_state<'a>( &'a mut self, ty: TypeId, - node: Option<&'a VElement<'a>>, + node: &'a VNode<'a>, children: &[&Self], ctx: &AnyMap, ) -> bool; diff --git a/packages/native-core/tests/change_nodes.rs b/packages/native-core/tests/change_nodes.rs index bf3de4368..8f4e6c8f0 100644 --- a/packages/native-core/tests/change_nodes.rs +++ b/packages/native-core/tests/change_nodes.rs @@ -3,7 +3,7 @@ use dioxus_core::*; use dioxus_core_macro::*; use dioxus_html as dioxus_elements; use dioxus_native_core::real_dom::RealDom; -use dioxus_native_core::real_dom_new_api::State; +use dioxus_native_core::state::State; use dioxus_native_core_macro::State; use std::cell::Cell; diff --git a/packages/native-core/tests/initial_build.rs b/packages/native-core/tests/initial_build.rs index 321820b7b..750987a29 100644 --- a/packages/native-core/tests/initial_build.rs +++ b/packages/native-core/tests/initial_build.rs @@ -5,7 +5,7 @@ use dioxus_core::*; use dioxus_core_macro::*; use dioxus_html as dioxus_elements; use dioxus_native_core::real_dom::RealDom; -use dioxus_native_core::real_dom_new_api::State; +use dioxus_native_core::state::State; use dioxus_native_core_macro::State; #[derive(State, Default, Clone)] diff --git a/packages/native-core/tests/parse.rs b/packages/native-core/tests/parse.rs index a3cf69ecf..a0bbd4f27 100644 --- a/packages/native-core/tests/parse.rs +++ b/packages/native-core/tests/parse.rs @@ -1,4 +1,4 @@ -use dioxus_native_core::real_dom_new_api::*; +use dioxus_native_core::state::*; use dioxus_native_core_macro::*; #[derive(State, Default, Clone)] @@ -20,7 +20,7 @@ struct Z { // z: C, // } -use dioxus_native_core::real_dom_new_api::NodeDepState; +use dioxus_native_core::state::NodeDepState; #[derive(Default, Clone)] struct A; @@ -36,12 +36,15 @@ struct B; impl ChildDepState for B { type Ctx = i32; type DepState = Self; - fn reduce( + fn reduce<'a>( &mut self, - _: dioxus_native_core::real_dom_new_api::NodeView, - _: Vec<&Self::DepState>, + _: dioxus_native_core::state::NodeView, + _: impl Iterator, _: &i32, - ) -> bool { + ) -> bool + where + Self::DepState: 'a, + { todo!() } } @@ -53,7 +56,7 @@ impl ParentDepState for C { type DepState = Self; fn reduce( &mut self, - _: dioxus_native_core::real_dom_new_api::NodeView, + _: dioxus_native_core::state::NodeView, _: Option<&Self::DepState>, _: &u8, ) -> bool { diff --git a/packages/native-core/tests/state.rs b/packages/native-core/tests/update_state.rs similarity index 93% rename from packages/native-core/tests/state.rs rename to packages/native-core/tests/update_state.rs index 2a17ac520..5c614922f 100644 --- a/packages/native-core/tests/state.rs +++ b/packages/native-core/tests/update_state.rs @@ -4,7 +4,7 @@ use dioxus_core::*; use dioxus_core_macro::*; use dioxus_html as dioxus_elements; use dioxus_native_core::real_dom::*; -use dioxus_native_core::real_dom_new_api::{ +use dioxus_native_core::state::{ AttributeMask, ChildDepState, NodeDepState, NodeMask, NodeView, ParentDepState, State, }; use dioxus_native_core_macro::State; @@ -25,12 +25,15 @@ impl ChildDepState for ChildDepCallCounter { type Ctx = (); type DepState = Self; const NODE_MASK: NodeMask = NodeMask::ALL; - fn reduce( + fn reduce<'a>( &mut self, - _node: NodeView, - _children: Vec<&Self::DepState>, - _ctx: &Self::Ctx, - ) -> bool { + _: NodeView, + _: impl Iterator, + _: &Self::Ctx, + ) -> bool + where + Self::DepState: 'a, + { self.0 += 1; true } @@ -70,7 +73,15 @@ impl ChildDepState for BubbledUpStateTester { type Ctx = u32; type DepState = Self; const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::NONE, true, false); - fn reduce(&mut self, node: NodeView, children: Vec<&Self::DepState>, ctx: &Self::Ctx) -> bool { + fn reduce<'a>( + &mut self, + node: NodeView, + children: impl Iterator, + ctx: &Self::Ctx, + ) -> bool + where + Self::DepState: 'a, + { assert_eq!(*ctx, 42); *self = BubbledUpStateTester( node.tag().map(|s| s.to_string()), @@ -245,6 +256,10 @@ fn state_reduce_initally_called_minimally() { let nodes_updated = dom.apply_mutations(vec![mutations]); let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new()); + dom.traverse_depth_first(|n| { + println!("{:#?}", n.state); + }); + dom.traverse_depth_first(|n| { assert_eq!(n.state.child_counter.0, 1); assert_eq!(n.state.parent_counter.0, 1); @@ -314,10 +329,10 @@ fn state_reduce_down_called_minimally_on_update() { let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new()); dom.traverse_depth_first(|n| { - println!("{:?}", n.state); - // assert_eq!(n.state.parent_counter.0, 2); + // println!("{:?}", n.state); + assert_eq!(n.state.parent_counter.0, 2); }); - panic!() + // panic!() } #[test] @@ -382,8 +397,6 @@ fn state_reduce_up_called_minimally_on_update() { let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new()); dom.traverse_depth_first(|n| { - println!("{:?}", n.state); - // assert_eq!(n.state.child_counter.0, if n.id.0 > 4 { 1 } else { 2 }); + assert_eq!(n.state.child_counter.0, if n.id.0 > 4 { 1 } else { 2 }); }); - panic!() } diff --git a/packages/tui/src/hooks.rs b/packages/tui/src/hooks.rs index 512fab8ba..434e9e7c5 100644 --- a/packages/tui/src/hooks.rs +++ b/packages/tui/src/hooks.rs @@ -5,7 +5,6 @@ use dioxus_core::*; use fxhash::{FxHashMap, FxHashSet}; use dioxus_html::{on::*, KeyCode}; -use dioxus_native_core::real_dom::{Node, RealDom}; use std::{ any::Any, cell::RefCell, @@ -17,6 +16,7 @@ use stretch2::{prelude::Layout, Stretch}; use crate::layout::StretchLayout; use crate::style_attributes::StyleModifier; +use crate::{Dom, Node}; // a wrapper around the input state for easier access // todo: fix loop @@ -166,7 +166,7 @@ impl InnerInputState { evts: &mut Vec, resolved_events: &mut Vec, layout: &Stretch, - dom: &mut RealDom, + dom: &mut Dom, ) { let previous_mouse = self .mouse @@ -191,7 +191,7 @@ impl InnerInputState { previous_mouse: Option<(MouseData, Vec)>, resolved_events: &mut Vec, layout: &Stretch, - dom: &mut RealDom, + dom: &mut Dom, ) { struct Data<'b> { new_pos: (i32, i32), @@ -215,8 +215,8 @@ impl InnerInputState { data: Arc, will_bubble: &mut FxHashSet, resolved_events: &mut Vec, - node: &Node, - dom: &RealDom, + node: &Node, + dom: &Dom, ) { // only trigger event if the event was not triggered already by a child if will_bubble.insert(node.id) { @@ -261,7 +261,7 @@ impl InnerInputState { // mousemove let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mousemove") { - let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap(); + let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); let previously_contained = data .old_pos .filter(|pos| layout_contains_point(node_layout, *pos)) @@ -285,7 +285,7 @@ impl InnerInputState { // mouseenter let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mouseenter") { - let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap(); + let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); let previously_contained = data .old_pos .filter(|pos| layout_contains_point(node_layout, *pos)) @@ -309,7 +309,7 @@ impl InnerInputState { // mouseover let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mouseover") { - let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap(); + let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); let previously_contained = data .old_pos .filter(|pos| layout_contains_point(node_layout, *pos)) @@ -333,7 +333,7 @@ impl InnerInputState { // mousedown let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mousedown") { - let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap(); + let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); let currently_contains = layout_contains_point(node_layout, data.new_pos); if currently_contains && data.clicked { @@ -353,7 +353,7 @@ impl InnerInputState { // mouseup let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mouseup") { - let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap(); + let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); let currently_contains = layout_contains_point(node_layout, data.new_pos); if currently_contains && data.released { @@ -373,7 +373,7 @@ impl InnerInputState { // click let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("click") { - let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap(); + let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); let currently_contains = layout_contains_point(node_layout, data.new_pos); if currently_contains && data.released && data.mouse_data.button == 0 { @@ -393,7 +393,7 @@ impl InnerInputState { // contextmenu let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("contextmenu") { - let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap(); + let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); let currently_contains = layout_contains_point(node_layout, data.new_pos); if currently_contains && data.released && data.mouse_data.button == 2 { @@ -413,7 +413,7 @@ impl InnerInputState { // wheel let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("wheel") { - let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap(); + let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); let currently_contains = layout_contains_point(node_layout, data.new_pos); if let Some(w) = data.wheel_data { @@ -435,7 +435,7 @@ impl InnerInputState { // mouseleave let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mouseleave") { - let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap(); + let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); let previously_contained = data .old_pos .filter(|pos| layout_contains_point(node_layout, *pos)) @@ -459,7 +459,7 @@ impl InnerInputState { // mouseout let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mouseout") { - let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap(); + let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap(); let previously_contained = data .old_pos .filter(|pos| layout_contains_point(node_layout, *pos)) @@ -522,11 +522,7 @@ impl RinkInputHandler { ) } - pub fn get_events( - &self, - layout: &Stretch, - dom: &mut RealDom, - ) -> Vec { + pub(crate) fn get_events(&self, layout: &Stretch, dom: &mut Dom) -> Vec { let mut resolved_events = Vec::new(); (*self.state).borrow_mut().update( diff --git a/packages/tui/src/layout.rs b/packages/tui/src/layout.rs index 4224fbdd5..5924a0fd1 100644 --- a/packages/tui/src/layout.rs +++ b/packages/tui/src/layout.rs @@ -1,83 +1,90 @@ +use std::cell::RefCell; +use std::rc::Rc; + use dioxus_core::*; use dioxus_native_core::layout_attributes::apply_layout_attributes; -use dioxus_native_core::real_dom::BubbledUpState; +use dioxus_native_core::state::{AttributeMask, ChildDepState, NodeMask, NodeView}; use stretch2::prelude::*; -/// the size #[derive(Clone, PartialEq, Default, Debug)] pub struct StretchLayout { pub style: Style, pub node: Option, } -impl BubbledUpState for StretchLayout { - type Ctx = Stretch; +impl ChildDepState for StretchLayout { + type Ctx = Rc>; + type DepState = Self; + // todo: update mask + const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::All, false, false, true); /// Setup the layout - fn reduce<'a, I>(&mut self, children: I, vnode: &VNode, stretch: &mut Self::Ctx) + fn reduce<'a>( + &mut self, + node: NodeView, + children: impl Iterator, + ctx: &Self::Ctx, + ) -> bool where - I: Iterator, - Self: 'a, + Self::DepState: 'a, { - match vnode { - VNode::Text(t) => { - let char_len = t.text.chars().count(); + let mut stretch = ctx.borrow_mut(); + if let Some(text) = node.text() { + let char_len = text.chars().count(); - let style = Style { - size: Size { - // characters are 1 point tall - height: Dimension::Points(1.0), + let style = Style { + size: Size { + // characters are 1 point tall + height: Dimension::Points(1.0), - // text is as long as it is declared - width: Dimension::Points(char_len as f32), - }, - ..Default::default() - }; + // text is as long as it is declared + width: Dimension::Points(char_len as f32), + }, + ..Default::default() + }; - if let Some(n) = self.node { - if self.style != style { - stretch.set_style(n, style).unwrap(); - } - } else { - self.node = Some(stretch.new_node(style, &[]).unwrap()); + if let Some(n) = self.node { + if self.style != style { + stretch.set_style(n, style).unwrap(); } - - self.style = style; + } else { + self.node = Some(stretch.new_node(style, &[]).unwrap()); } - VNode::Element(el) => { - // gather up all the styles from the attribute list - let mut style = Style::default(); - for &Attribute { name, value, .. } in el.attributes { - apply_layout_attributes(name, value, &mut style); - } + self.style = style; + } else { + // gather up all the styles from the attribute list + let mut style = Style::default(); - // the root node fills the entire area - if el.id.get() == Some(ElementId(0)) { - apply_layout_attributes("width", "100%", &mut style); - apply_layout_attributes("height", "100%", &mut style); - } - - // Set all direct nodes as our children - let mut child_layout = vec![]; - for l in children { - child_layout.push(l.node.unwrap()); - } - - if let Some(n) = self.node { - if stretch.children(n).unwrap() != child_layout { - stretch.set_children(n, &child_layout).unwrap(); - } - if self.style != style { - stretch.set_style(n, style).unwrap(); - } - } else { - self.node = Some(stretch.new_node(style, &child_layout).unwrap()); - } - - self.style = style; + for &Attribute { name, value, .. } in node.attributes() { + apply_layout_attributes(name, value, &mut style); } - _ => (), + + // the root node fills the entire area + if node.id() == ElementId(0) { + apply_layout_attributes("width", "100%", &mut style); + apply_layout_attributes("height", "100%", &mut style); + } + + // Set all direct nodes as our children + let mut child_layout = vec![]; + for l in children { + child_layout.push(l.node.unwrap()); + } + + if let Some(n) = self.node { + if stretch.children(n).unwrap() != child_layout { + stretch.set_children(n, &child_layout).unwrap(); + } + if self.style != style { + stretch.set_style(n, style).unwrap(); + } + } else { + self.node = Some(stretch.new_node(style, &child_layout).unwrap()); + } + + self.style = style; } + true } } diff --git a/packages/tui/src/lib.rs b/packages/tui/src/lib.rs index 4aa9b1313..1286eba06 100644 --- a/packages/tui/src/lib.rs +++ b/packages/tui/src/lib.rs @@ -1,4 +1,5 @@ use anyhow::Result; +use anymap::AnyMap; use crossterm::{ event::{DisableMouseCapture, EnableMouseCapture, Event as TermEvent, KeyCode, KeyModifiers}, execute, @@ -6,14 +7,14 @@ use crossterm::{ }; use dioxus_core::exports::futures_channel::mpsc::unbounded; use dioxus_core::*; -use dioxus_native_core::{ - dioxus_native_core_macro::State, real_dom::RealDom, real_dom_new_api::State, -}; +use dioxus_native_core::{dioxus_native_core_macro::State, real_dom::RealDom, state::*}; use futures::{ channel::mpsc::{UnboundedReceiver, UnboundedSender}, pin_mut, StreamExt, }; use layout::StretchLayout; +use std::cell::RefCell; +use std::rc::Rc; use std::{io, time::Duration}; use stretch2::{prelude::Size, Stretch}; use style_attributes::StyleModifier; @@ -31,8 +32,17 @@ pub use config::*; pub use hooks::*; pub use render::*; +type Dom = RealDom; +type Node = dioxus_native_core::real_dom::Node; + #[derive(Debug, Clone, State, Default)] -struct NodeState {} +struct NodeState { + #[child_dep_state(StretchLayout, RefCell)] + layout: StretchLayout, + // depends on attributes, the C component of it's parent and a u8 context + #[parent_dep_state(StyleModifier)] + style: StyleModifier, +} #[derive(Clone)] pub struct TuiContext { @@ -75,13 +85,13 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) { cx.provide_root_context(state); cx.provide_root_context(TuiContext { tx: event_tx_clone }); - let mut rdom: RealDom = RealDom::new(); + let mut rdom: Dom = RealDom::new(); let mutations = dom.rebuild(); let to_update = rdom.apply_mutations(vec![mutations]); - let mut stretch = Stretch::new(); - let _to_rerender = rdom - .update_state(&dom, to_update, &mut stretch, &mut ()) - .unwrap(); + let stretch = Rc::new(RefCell::new(Stretch::new())); + let mut any_map = AnyMap::new(); + any_map.insert(stretch.clone()); + let _to_rerender = rdom.update_state(&dom, to_update, any_map).unwrap(); render_vdom( &mut dom, @@ -100,8 +110,8 @@ fn render_vdom( mut event_reciever: UnboundedReceiver, handler: RinkInputHandler, cfg: Config, - mut rdom: RealDom, - mut stretch: Stretch, + mut rdom: Dom, + mut stretch: Rc>, mut register_event: impl FnMut(crossterm::event::Event), ) -> Result<()> { tokio::runtime::Builder::new_current_thread() @@ -119,7 +129,7 @@ fn render_vdom( terminal.clear().unwrap(); } - let mut to_rerender: fxhash::FxHashSet = vec![0].into_iter().collect(); + let to_rerender: fxhash::FxHashSet = vec![0].into_iter().collect(); let mut resized = true; loop { @@ -136,15 +146,11 @@ fn render_vdom( if !to_rerender.is_empty() || resized { resized = false; - fn resize( - dims: Rect, - stretch: &mut Stretch, - rdom: &RealDom, - ) { + fn resize(dims: Rect, stretch: &mut Stretch, rdom: &Dom) { let width = dims.width; let height = dims.height; let root_id = rdom.root_id(); - let root_node = rdom[root_id].up_state.node.unwrap(); + let root_node = rdom[root_id].state.layout.node.unwrap(); stretch .compute_layout( @@ -159,9 +165,9 @@ fn render_vdom( if let Some(terminal) = &mut terminal { terminal.draw(|frame| { // size is guaranteed to not change when rendering - resize(frame.size(), &mut stretch, &rdom); + resize(frame.size(), &mut stretch.borrow_mut(), &rdom); let root = &rdom[rdom.root_id()]; - render::render_vnode(frame, &stretch, &rdom, &root, cfg); + render::render_vnode(frame, &stretch.borrow(), &rdom, &root, cfg); })?; } else { resize( @@ -171,7 +177,7 @@ fn render_vdom( width: 300, height: 300, }, - &mut stretch, + &mut stretch.borrow_mut(), &rdom, ); } @@ -212,7 +218,7 @@ fn render_vdom( { // resolve events before rendering - let evts = handler.get_events(&stretch, &mut rdom); + let evts = handler.get_events(&stretch.borrow(), &mut rdom); for e in evts { vdom.handle_message(SchedulerMsg::Event(e)); } @@ -220,11 +226,9 @@ fn render_vdom( // updates the dom's nodes let to_update = rdom.apply_mutations(mutations); // update the style and layout - to_rerender.extend( - rdom.update_state(vdom, to_update, &mut stretch, &mut ()) - .unwrap() - .iter(), - ) + let mut any_map = AnyMap::new(); + any_map.insert(stretch.clone()); + let _to_rerender = rdom.update_state(&vdom, to_update, any_map).unwrap(); } } diff --git a/packages/tui/src/render.rs b/packages/tui/src/render.rs index 94cd90a62..8cee7c55c 100644 --- a/packages/tui/src/render.rs +++ b/packages/tui/src/render.rs @@ -1,8 +1,5 @@ use crate::layout::StretchLayout; -use dioxus_native_core::{ - layout_attributes::UnitSystem, - real_dom::{Node, RealDom}, -}; +use dioxus_native_core::layout_attributes::UnitSystem; use std::io::Stdout; use stretch2::{ geometry::Point, @@ -15,16 +12,16 @@ use crate::{ style::{RinkColor, RinkStyle}, style_attributes::{BorderEdge, BorderStyle}, widget::{RinkBuffer, RinkCell, RinkWidget, WidgetWithContext}, - Config, StyleModifier, + Config, Dom, Node, StyleModifier, }; const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5]; -pub fn render_vnode( +pub(crate) fn render_vnode( frame: &mut tui::Frame>, layout: &Stretch, - rdom: &RealDom, - node: &Node, + rdom: &Dom, + node: &Node, cfg: Config, ) { use dioxus_native_core::real_dom::NodeType; @@ -33,7 +30,7 @@ pub fn render_vnode( return; } - let Layout { location, size, .. } = layout.layout(node.up_state.node.unwrap()).unwrap(); + let Layout { location, size, .. } = layout.layout(node.state.layout.node.unwrap()).unwrap(); let Point { x, y } = location; let Size { width, height } = size; @@ -59,7 +56,7 @@ pub fn render_vnode( let label = Label { text, - style: node.down_state.style, + style: node.state.style.style, }; let area = Rect::new(*x as u16, *y as u16, *width as u16, *height as u16); @@ -84,7 +81,7 @@ pub fn render_vnode( } } -impl RinkWidget for &Node { +impl RinkWidget for &Node { fn render(self, area: Rect, mut buf: RinkBuffer<'_>) { use tui::symbols::line::*; @@ -268,14 +265,14 @@ impl RinkWidget for &Node { for x in area.left()..area.right() { for y in area.top()..area.bottom() { let mut new_cell = RinkCell::default(); - if let Some(c) = self.down_state.style.bg { + if let Some(c) = self.state.style.style.bg { new_cell.bg = c; } buf.set(x, y, new_cell); } } - let borders = &self.down_state.modifier.borders; + let borders = &self.state.style.modifier.borders; let last_edge = &borders.left; let current_edge = &borders.top; @@ -292,7 +289,7 @@ impl RinkWidget for &Node { (last_r * RADIUS_MULTIPLIER[0]) as u16, (last_r * RADIUS_MULTIPLIER[1]) as u16, ]; - let color = current_edge.color.or(self.down_state.style.fg); + let color = current_edge.color.or(self.state.style.style.fg); let mut new_cell = RinkCell::default(); if let Some(c) = color { new_cell.fg = c; @@ -327,7 +324,7 @@ impl RinkWidget for &Node { (last_r * RADIUS_MULTIPLIER[0]) as u16, (last_r * RADIUS_MULTIPLIER[1]) as u16, ]; - let color = current_edge.color.or(self.down_state.style.fg); + let color = current_edge.color.or(self.state.style.style.fg); let mut new_cell = RinkCell::default(); if let Some(c) = color { new_cell.fg = c; @@ -362,7 +359,7 @@ impl RinkWidget for &Node { (last_r * RADIUS_MULTIPLIER[0]) as u16, (last_r * RADIUS_MULTIPLIER[1]) as u16, ]; - let color = current_edge.color.or(self.down_state.style.fg); + let color = current_edge.color.or(self.state.style.style.fg); let mut new_cell = RinkCell::default(); if let Some(c) = color { new_cell.fg = c; @@ -397,7 +394,7 @@ impl RinkWidget for &Node { (last_r * RADIUS_MULTIPLIER[0]) as u16, (last_r * RADIUS_MULTIPLIER[1]) as u16, ]; - let color = current_edge.color.or(self.down_state.style.fg); + let color = current_edge.color.or(self.state.style.style.fg); let mut new_cell = RinkCell::default(); if let Some(c) = color { new_cell.fg = c; diff --git a/packages/tui/src/style_attributes.rs b/packages/tui/src/style_attributes.rs index 710610104..5438eb53d 100644 --- a/packages/tui/src/style_attributes.rs +++ b/packages/tui/src/style_attributes.rs @@ -32,7 +32,7 @@ use dioxus_core::{Attribute, VNode}; use dioxus_native_core::{ layout_attributes::{parse_value, UnitSystem}, - real_dom::PushedDownState, + state::{AttributeMask, NodeMask, NodeView, ParentDepState}, }; use crate::style::{RinkColor, RinkStyle}; @@ -43,18 +43,22 @@ pub struct StyleModifier { pub modifier: TuiModifier, } -impl PushedDownState for StyleModifier { +impl ParentDepState for StyleModifier { type Ctx = (); + type DepState = Self; + // todo: seperate each attribute into it's own class + const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::All, true, true, false); - fn reduce(&mut self, parent: Option<&Self>, vnode: &VNode, _ctx: &mut Self::Ctx) { + fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, ctx: &Self::Ctx) -> bool { *self = StyleModifier::default(); if parent.is_some() { self.style.fg = None; } - if let VNode::Element(el) = vnode { - // handle text modifier elements - if el.namespace.is_none() { - match el.tag { + + // handle text modifier elements + if node.namespace().is_none() { + if let Some(tag) = node.tag() { + match tag { "b" => apply_style_attributes("font-weight", "bold", self), "strong" => apply_style_attributes("font-weight", "bold", self), "u" => apply_style_attributes("text-decoration", "underline", self), @@ -68,11 +72,11 @@ impl PushedDownState for StyleModifier { _ => (), } } + } - // gather up all the styles from the attribute list - for &Attribute { name, value, .. } in el.attributes { - apply_style_attributes(name, value, self); - } + // gather up all the styles from the attribute list + for &Attribute { name, value, .. } in node.attributes() { + apply_style_attributes(name, value, self); } // keep the text styling from the parent element @@ -81,6 +85,7 @@ impl PushedDownState for StyleModifier { new_style.bg = self.style.bg; self.style = new_style; } + true } }