Evan Almloff 3 gadi atpakaļ
vecāks
revīzija
7c30d93a3d

+ 43 - 1
docs/reference/src/guide/custom_renderer.md

@@ -304,7 +304,7 @@ flowchart TB
     end
 ```
 
-To help in building a Dom, native core provides four traits: State, ChildDepState, ParentDepState, and NodeDepState.
+To help in building a Dom, native core provides four traits: State, ChildDepState, ParentDepState, and NodeDepState and a RealDom struct.
 
 ```rust
 use dioxus_native_core::node_ref::*;
@@ -436,6 +436,48 @@ struct ToyState {
 }
 ```
 
+Now that we have our state, we can put it to use in our dom. Re can update the dom with update_state to update the structure of the dom (adding, removing, and chaning properties of nodes) and then apply_mutations to update the ToyState for each of the nodes that changed.
+```rust
+fn main(){
+    fn app(cx: Scope) -> Element {
+        cx.render(rsx!{
+            div{
+                color: "red",
+                "hello world"
+            }
+        })
+    }
+    let vdom = VirtualDom::new(app);
+    let rdom: RealDom<ToyState> = RealDom::new();
+
+    let mutations = dom.rebuild();
+    // update the structure of the real_dom tree
+    let to_update = rdom.apply_mutations(vec![mutations]);
+    let mut ctx = AnyMap::new();
+    // set the font size to 3.3
+    ctx.insert(3.3);
+    // update the ToyState for nodes in the real_dom tree
+    let _to_rerender = rdom.update_state(&dom, to_update, ctx).unwrap();
+
+    // we need to run the vdom in a async runtime
+    tokio::runtime::Builder::new_current_thread()
+        .enable_all()
+        .build()?
+        .block_on(async {
+            loop{
+                let wait = vdom.wait_for_work();
+                let mutations = vdom.work_with_deadline(|| false);
+                let to_update = rdom.apply_mutations(mutations);
+                let mut ctx = AnyMap::new();
+                ctx.insert(3.3);
+                let _to_rerender = rdom.update_state(vdom, to_update, ctx).unwrap();
+
+                // render...
+            }
+        })
+}
+```
+
 ## Layout
 For most platforms the layout of the Elements will stay the same. The layout_attributes module provides a way to apply html attributes to a stretch layout style.
 

+ 137 - 15
packages/native-core-macro/src/lib.rs

@@ -4,6 +4,7 @@ mod sorted_slice;
 
 use dioxus_native_core::state::MemberId;
 use proc_macro::TokenStream;
+use quote::format_ident;
 use quote::{quote, ToTokens, __private::Span};
 use sorted_slice::StrSlice;
 use syn::{
@@ -26,7 +27,10 @@ enum DepKind {
     Parent,
 }
 
-#[proc_macro_derive(State, attributes(node_dep_state, child_dep_state, parent_dep_state))]
+#[proc_macro_derive(
+    State,
+    attributes(node_dep_state, child_dep_state, parent_dep_state, state)
+)]
 pub fn state_macro_derive(input: TokenStream) -> TokenStream {
     let ast = syn::parse(input).unwrap();
     impl_derive_macro(&ast)
@@ -89,8 +93,51 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
 
             let type_name_str = type_name.to_string();
 
+            let child_states = &state_strct.child_states;
+
+            let member_size = state_strct.state_members.len();
+
+            let child_state_ty = child_states.iter().map(|m| &m.ty);
+            let child_state_idents: Vec<_> = child_states.iter().map(|m| &m.ident).collect();
+            let sum_const_declarations = child_state_ty.clone().enumerate().map(|(i, ty)| {
+                let ident = format_ident!("__{}_SUM_{}", i, type_name.to_string());
+                let ident_minus = format_ident!("__{}_SUM_{}_minus", i, type_name.to_string());
+                if i == 0 {
+                    quote!(const #ident_minus: usize = #member_size + #ty::SIZE - 1;
+                    const #ident: usize = #member_size + #ty::SIZE;)
+                } else {
+                    let prev_ident = format_ident!("__{}_SUM_{}", i - 1, type_name.to_string());
+                    quote!(const #ident_minus: usize = #prev_ident + #ty::SIZE - 1;
+                    const #ident: usize = #prev_ident + #ty::SIZE;)
+                }
+            });
+            let sum_idents: Vec<_> = std::iter::once(quote!(#member_size))
+                .chain((0..child_states.len()).map(|i| {
+                    let ident = format_ident!("__{}_SUM_{}", i, type_name.to_string());
+                    quote!(#ident)
+                }))
+                .collect();
+
+            let child_state_ranges: Vec<_> = (0..child_state_ty.len())
+                .map(|i| {
+                    let current = format_ident!("__{}_SUM_{}_minus", i, type_name.to_string());
+                    let previous = if i == 0 {
+                        quote!(#member_size)
+                    } else {
+                        let ident = format_ident!("__{}_SUM_{}", i - 1, type_name.to_string());
+                        quote!(#ident)
+                    };
+                    quote!(#previous..=#current)
+                })
+                .collect();
+
             let gen = quote! {
+                #(
+                    #sum_const_declarations
+                )*
                 impl State for #type_name{
+                    const SIZE: usize = #member_size #( + #child_state_ty::SIZE)*;
+
                     fn update_node_dep_state<'a>(
                         &'a mut self,
                         ty: dioxus_native_core::state::MemberId,
@@ -99,10 +146,26 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
                         ctx: &anymap::AnyMap,
                     ) -> Option<dioxus_native_core::state::NodeStatesChanged>{
                         use dioxus_native_core::state::NodeDepState as _;
+                        use dioxus_native_core::state::State as _;
                         match ty.0{
                             #(
                                 #node_ids => #node_dep_state_fields,
                             )*
+                            #(
+                                #child_state_ranges => {
+                                    self.#child_state_idents.update_node_dep_state(
+                                        ty - #sum_idents,
+                                        node,
+                                        vdom,
+                                        ctx,
+                                    ).map(|mut changed|{
+                                        for id in &mut changed.node_dep{
+                                            *id += #sum_idents;
+                                        }
+                                        changed
+                                    })
+                                }
+                            )*
                             _ => panic!("{:?} not in {}", ty, #type_name_str),
                         }
                     }
@@ -120,6 +183,25 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
                             #(
                                 #parent_ids => #parent_dep_state_fields,
                             )*
+                            #(
+                                #child_state_ranges => {
+                                    self.#child_state_idents.update_parent_dep_state(
+                                        ty - #sum_idents,
+                                        node,
+                                        vdom,
+                                        parent.map(|p| &p.#child_state_idents),
+                                        ctx,
+                                    ).map(|mut changed|{
+                                        for id in &mut changed.node_dep{
+                                            *id += #sum_idents;
+                                        }
+                                        for id in &mut changed.parent_dep{
+                                            *id += #sum_idents;
+                                        }
+                                        changed
+                                    })
+                                }
+                            )*
                             _ => panic!("{:?} not in {}", ty, #type_name_str),
                         }
                     }
@@ -129,13 +211,32 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
                         ty: dioxus_native_core::state::MemberId,
                         node: &'a dioxus_core::VNode<'a>,
                         vdom: &'a dioxus_core::VirtualDom,
-                        children: &[&Self],
+                        children: &Vec<&Self>,
                         ctx: &anymap::AnyMap,
                     ) -> Option<dioxus_native_core::state::ChildStatesChanged>{
                         use dioxus_native_core::state::ChildDepState as _;
                         match ty.0{
                             #(
-                                #child_ids => {#child_dep_state_fields},
+                                #child_ids => #child_dep_state_fields,
+                            )*
+                            #(
+                                #child_state_ranges => {
+                                    self.#child_state_idents.update_child_dep_state(
+                                        ty - #sum_idents,
+                                        node,
+                                        vdom,
+                                        &children.iter().map(|p| &p.#child_state_idents).collect(),
+                                        ctx,
+                                    ).map(|mut changed|{
+                                        for id in &mut changed.node_dep{
+                                            *id += #sum_idents;
+                                        }
+                                        for id in &mut changed.child_dep{
+                                            *id += #sum_idents;
+                                        }
+                                        changed
+                                    })
+                                }
                             )*
                             _ => panic!("{:?} not in {}", ty, #type_name_str),
                         }
@@ -146,6 +247,9 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
                         #(if #child_types::NODE_MASK.overlaps(mask) {
                             dep_types.push(dioxus_native_core::state::MemberId(#child_ids_clone));
                         })*
+                        #(
+                            dep_types.extend(self.#child_state_idents.child_dep_types(mask).into_iter().map(|id| id + #sum_idents));
+                        )*
                         dep_types
                     }
 
@@ -154,6 +258,9 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
                         #(if #parent_types::NODE_MASK.overlaps(mask) {
                             dep_types.push(dioxus_native_core::state::MemberId(#parent_ids_clone));
                         })*
+                        #(
+                            dep_types.extend(self.#child_state_idents.parent_dep_types(mask).into_iter().map(|id| id + #sum_idents));
+                        )*
                         dep_types
                     }
 
@@ -162,6 +269,9 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
                         #(if #node_types::NODE_MASK.overlaps(mask) {
                             dep_types.push(dioxus_native_core::state::MemberId(#node_ids_clone));
                         })*
+                        #(
+                            dep_types.extend(self.#child_state_idents.node_dep_types(mask).into_iter().map(|id| id + #sum_idents));
+                        )*
                         dep_types
                     }
                 }
@@ -186,6 +296,7 @@ impl Struct {
 
 struct StateStruct<'a> {
     state_members: Vec<StateMember<'a>>,
+    child_states: Vec<&'a Member>,
 }
 
 impl<'a> StateStruct<'a> {
@@ -203,6 +314,20 @@ impl<'a> StateStruct<'a> {
                 }
             });
 
+        let child_states = strct
+            .members
+            .iter()
+            .zip(fields.iter())
+            .filter(|(_, f)| {
+                f.attrs.iter().any(|a| {
+                    a.path
+                        .get_ident()
+                        .filter(|i| i.to_string().as_str() == "state")
+                        .is_some()
+                })
+            })
+            .map(|(m, _)| m);
+
         #[derive(Debug, Clone)]
         struct DepNode<'a> {
             state_mem: StateMember<'a>,
@@ -350,7 +475,10 @@ impl<'a> StateStruct<'a> {
                 .flat_map(|r| r.flatten().into_iter())
                 .collect();
 
-            Ok(Self { state_members })
+            Ok(Self {
+                state_members,
+                child_states: child_states.collect(),
+            })
         }
     }
 }
@@ -484,23 +612,23 @@ impl<'a> StateMember<'a> {
                 DepKind::Node => {
                     quote! {
                         dioxus_native_core::state::NodeStatesChanged{
-                            node_dep: &[#(dioxus_native_core::state::MemberId(#node_dep), )*],
+                            node_dep: vec![#(dioxus_native_core::state::MemberId(#node_dep), )*],
                         }
                     }
                 }
                 DepKind::Child => {
                     quote! {
                         dioxus_native_core::state::ChildStatesChanged{
-                            node_dep: &[#(dioxus_native_core::state::MemberId(#node_dep), )*],
-                            child_dep: &[#(dioxus_native_core::state::MemberId(#child_dep), )*],
+                            node_dep: vec![#(dioxus_native_core::state::MemberId(#node_dep), )*],
+                            child_dep: vec![#(dioxus_native_core::state::MemberId(#child_dep), )*],
                         }
                     }
                 }
                 DepKind::Parent => {
                     quote! {
                         dioxus_native_core::state::ParentStatesChanged{
-                            node_dep: &[#(dioxus_native_core::state::MemberId(#node_dep), )*],
-                            parent_dep: &[#(dioxus_native_core::state::MemberId(#parent_dep), )*],
+                            node_dep: vec![#(dioxus_native_core::state::MemberId(#node_dep), )*],
+                            parent_dep: vec![#(dioxus_native_core::state::MemberId(#parent_dep), )*],
                         }
                     }
                 }
@@ -515,7 +643,6 @@ impl<'a> StateMember<'a> {
             match self.dep_kind {
                 DepKind::Node => {
                     quote!({
-                        // println!("node: {:?} {:?} {:?}", self.#ident, #id, #node_view.id());
                         if self.#ident.reduce(#node_view, &self.#dep_ident, #get_ctx){
                             Some(#states_changed)
                         } else{
@@ -525,7 +652,6 @@ impl<'a> StateMember<'a> {
                 }
                 DepKind::Child => {
                     quote!({
-                        // println!("child: {:?} {:?} {:?}", self.#ident, #id, #node_view.id());
                         if self.#ident.reduce(#node_view, children.iter().map(|s| &s.#dep_ident), #get_ctx){
                             Some(#states_changed)
                         } else{
@@ -535,7 +661,6 @@ impl<'a> StateMember<'a> {
                 }
                 DepKind::Parent => {
                     quote!({
-                        // println!("parent: {:?} {:?} {:?}", self.#ident, #id, #node_view.id());
                         if self.#ident.reduce(#node_view, parent.as_ref().map(|p| &p.#dep_ident), #get_ctx){
                             Some(#states_changed)
                         } else{
@@ -548,7 +673,6 @@ impl<'a> StateMember<'a> {
             match self.dep_kind {
                 DepKind::Node => {
                     quote!({
-                        // println!("node: {:?} {:?} {:?}", self.#ident, #id, #node_view.id());
                         if self.#ident.reduce(#node_view, &(), #get_ctx){
                             Some(#states_changed)
                         } else{
@@ -558,7 +682,6 @@ impl<'a> StateMember<'a> {
                 }
                 DepKind::Child => {
                     quote!({
-                        // println!("child: {:?} {:?} {:?}", self.#ident, #id, #node_view.id());
                         if self.#ident.reduce(#node_view, std::iter::empty(), #get_ctx){
                             Some(#states_changed)
                         } else{
@@ -568,7 +691,6 @@ impl<'a> StateMember<'a> {
                 }
                 DepKind::Parent => {
                     quote!({
-                        println!("parent: {:?} {:?} {:?}", self.#ident, #id, #node_view.id());
                         if self.#ident.reduce(#node_view, Some(&()), #get_ctx){
                             Some(#states_changed)
                         } else{

+ 44 - 0
packages/native-core-macro/tests/update_state.rs

@@ -8,12 +8,36 @@ use dioxus_native_core::real_dom::*;
 use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State};
 use dioxus_native_core_macro::State;
 
+#[derive(Debug, Clone, Default, State)]
+struct CallCounterStatePart1 {
+    #[child_dep_state(child_counter)]
+    child_counter: ChildDepCallCounter,
+}
+
+#[derive(Debug, Clone, Default, State)]
+struct CallCounterStatePart2 {
+    #[parent_dep_state(parent_counter)]
+    parent_counter: ParentDepCallCounter,
+}
+
+#[derive(Debug, Clone, Default, State)]
+struct CallCounterStatePart3 {
+    #[node_dep_state()]
+    node_counter: NodeDepCallCounter,
+}
+
 #[derive(Debug, Clone, Default, State)]
 struct CallCounterState {
     #[child_dep_state(child_counter)]
     child_counter: ChildDepCallCounter,
+    #[state]
+    part2: CallCounterStatePart2,
     #[parent_dep_state(parent_counter)]
     parent_counter: ParentDepCallCounter,
+    #[state]
+    part1: CallCounterStatePart1,
+    #[state]
+    part3: CallCounterStatePart3,
     #[node_dep_state()]
     node_counter: NodeDepCallCounter,
 }
@@ -261,8 +285,11 @@ fn state_reduce_initally_called_minimally() {
     let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
 
     dom.traverse_depth_first(|n| {
+        assert_eq!(n.state.part1.child_counter.0, 1);
         assert_eq!(n.state.child_counter.0, 1);
+        assert_eq!(n.state.part2.parent_counter.0, 1);
         assert_eq!(n.state.parent_counter.0, 1);
+        assert_eq!(n.state.part3.node_counter.0, 1);
         assert_eq!(n.state.node_counter.0, 1);
     });
 }
@@ -329,6 +356,7 @@ fn state_reduce_parent_called_minimally_on_update() {
     let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
 
     dom.traverse_depth_first(|n| {
+        assert_eq!(n.state.part2.parent_counter.0, 2);
         assert_eq!(n.state.parent_counter.0, 2);
     });
 }
@@ -398,6 +426,10 @@ fn state_reduce_child_called_minimally_on_update() {
 
     dom.traverse_depth_first(|n| {
         println!("{:?}", n);
+        assert_eq!(
+            n.state.part1.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 });
     });
 }
@@ -487,3 +519,15 @@ fn dependancies_order_independant() {
         assert_eq!(&n.state.c, &c);
     });
 }
+
+#[derive(Clone, Default, State)]
+struct DependanciesStateTest {
+    #[node_dep_state(c)]
+    b: BDepCallCounter,
+    #[node_dep_state()]
+    c: CDepCallCounter,
+    #[node_dep_state(b)]
+    a: ADepCallCounter,
+    #[state]
+    child: UnorderedDependanciesState,
+}

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

@@ -308,7 +308,7 @@ impl<S: State> RealDom<S> {
                     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 {
+                    for m in &members_effected.node_dep {
                         if let Err(idx) = ids.binary_search(m) {
                             ids.insert(idx, *m);
                         }
@@ -348,12 +348,12 @@ impl<S: State> RealDom<S> {
                 {
                     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);
+                        if let Err(idx) = ids.binary_search(&m) {
+                            ids.insert(idx, m);
                         }
                     }
                     for m in members_effected.child_dep {
-                        changed.push(*m);
+                        changed.push(m);
                     }
                 }
                 i += 1;
@@ -416,12 +416,12 @@ impl<S: State> RealDom<S> {
                 {
                     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);
+                        if let Err(idx) = ids.binary_search(&m) {
+                            ids.insert(idx, m);
                         }
                     }
                     for m in members_effected.parent_dep {
-                        changed.push(*m);
+                        changed.push(m);
                     }
                 }
                 i += 1;

+ 39 - 7
packages/native-core/src/state.rs

@@ -1,4 +1,8 @@
-use std::{cmp::Ordering, fmt::Debug};
+use std::{
+    cmp::Ordering,
+    fmt::Debug,
+    ops::{Add, AddAssign, Sub, SubAssign},
+};
 
 use anymap::AnyMap;
 use dioxus_core::VNode;
@@ -81,22 +85,24 @@ pub trait NodeDepState {
 
 #[derive(Debug)]
 pub struct ChildStatesChanged {
-    pub node_dep: &'static [MemberId],
-    pub child_dep: &'static [MemberId],
+    pub node_dep: Vec<MemberId>,
+    pub child_dep: Vec<MemberId>,
 }
 
 #[derive(Debug)]
 pub struct ParentStatesChanged {
-    pub node_dep: &'static [MemberId],
-    pub parent_dep: &'static [MemberId],
+    pub node_dep: Vec<MemberId>,
+    pub parent_dep: Vec<MemberId>,
 }
 
 #[derive(Debug)]
 pub struct NodeStatesChanged {
-    pub node_dep: &'static [MemberId],
+    pub node_dep: Vec<MemberId>,
 }
 
 pub trait State: Default + Clone {
+    const SIZE: usize;
+
     fn update_node_dep_state<'a>(
         &'a mut self,
         ty: MemberId,
@@ -123,7 +129,7 @@ pub trait State: Default + Clone {
         ty: MemberId,
         node: &'a VNode<'a>,
         vdom: &'a dioxus_core::VirtualDom,
-        children: &[&Self],
+        children: &Vec<&Self>,
         ctx: &AnyMap,
     ) -> Option<ChildStatesChanged>;
     /// This must be a valid resolution order. (no nodes updated before a state they rely on)
@@ -165,3 +171,29 @@ impl NodeDepState for () {
 
 #[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;
+    }
+}