Browse Source

feat: bless up, no more segfaults

Jonathan Kelley 3 years ago
parent
commit
4a0068f099

+ 1 - 0
examples/reducer.rs

@@ -7,6 +7,7 @@
 
 use dioxus::prelude::*;
 fn main() {
+    env_logger::init();
     dioxus::desktop::launch(App, |c| c);
 }
 

+ 23 - 5
examples/webview.rs

@@ -9,7 +9,7 @@
 //!
 //! Currently, NodeRefs won't work properly, but all other event functionality will.
 
-use dioxus::prelude::*;
+use dioxus::{events::on::MouseEvent, prelude::*};
 
 fn main() {
     env_logger::init();
@@ -17,13 +17,31 @@ fn main() {
 }
 
 static App: FC<()> = |cx| {
-    let mut count = use_state(cx, || 0);
+    let state = use_state(cx, || String::from("hello"));
+    let clear_text = state == "hello";
 
+    dbg!("rednering parent");
     cx.render(rsx! {
         div {
-            h1 { "Hifive counter: {count}" }
-            button { onclick: move |_| count += 1, "Up high!" }
-            button { onclick: move |_| count -= 1, "Down low!" }
+            h1 {"{state}"}
+            CalculatorKey { name: "key-clear", onclick: move |_| state.get_mut().push_str("hello"), "{clear_text}" }
+            CalculatorKey { name: "key-sign", onclick: move |_| { state.get_mut().pop(); }, "±"}
         }
     })
 };
+
+#[derive(Props)]
+struct CalculatorKeyProps<'a> {
+    name: &'static str,
+    onclick: &'a dyn Fn(MouseEvent),
+}
+
+fn CalculatorKey<'a, 'r>(cx: Context<'a, CalculatorKeyProps<'r>>) -> DomTree<'a> {
+    cx.render(rsx! {
+        button {
+            class: "calculator-key {cx.name}"
+            onclick: {cx.onclick}
+            {cx.children()}
+        }
+    })
+}

+ 63 - 0
examples/webview_web.rs

@@ -0,0 +1,63 @@
+//! Example: Webview Renderer
+//! -------------------------
+//!
+//! This example shows how to use the dioxus_desktop crate to build a basic desktop application.
+//!
+//! Under the hood, the dioxus_desktop crate bridges a native Dioxus VirtualDom with a custom prebuit application running
+//! in the webview runtime. Custom handlers are provided for the webview instance to consume patches and emit user events
+//! into the native VDom instance.
+//!
+//! Currently, NodeRefs won't work properly, but all other event functionality will.
+
+use dioxus::prelude::*;
+
+// #[cfg]
+fn main() {
+    // env_logger::init();
+    dioxus::web::launch(App, |c| c);
+}
+
+static App: FC<()> = |cx| {
+    dbg!("rednering parent");
+    cx.render(rsx! {
+        div {
+            But {
+                h1 {"he"}
+            }
+            // But {
+            //     h1 {"llo"}
+            // }
+            // But {
+            //     h1 {"world"}
+            // }
+        }
+    })
+};
+
+static But: FC<()> = |cx| {
+    let mut count = use_state(cx, || 0);
+
+    // let d = Dropper { name: "asd" };
+    // let handler = move |_| {
+    //     dbg!(d.name);
+    // };
+
+    cx.render(rsx! {
+        div {
+            h1 { "Hifive counter: {count}" }
+            {cx.children()}
+            button { onclick: move |_| count += 1, "Up high!" }
+            button { onclick: move |_| count -= 1, "Down low!" }
+            // button { onclick: {handler}, "Down low!" }
+        }
+    })
+};
+
+// struct Dropper {
+//     name: &'static str,
+// }
+// impl Drop for Dropper {
+//     fn drop(&mut self) {
+//         dbg!("dropped");
+//     }
+// }

+ 21 - 23
packages/core/src/bumpframe.rs

@@ -14,7 +14,7 @@ pub struct BumpFrame {
     pub bump: Bump,
     pub head_node: VNode<'static>,
 
-    #[cfg(test)]
+    // used internally for debugging
     name: &'static str,
 }
 
@@ -23,16 +23,12 @@ impl ActiveFrame {
         let frame_a = BumpFrame {
             bump: Bump::new(),
             head_node: NodeFactory::unstable_place_holder(),
-
-            #[cfg(test)]
-            name: "old",
+            name: "wip",
         };
         let frame_b = BumpFrame {
             bump: Bump::new(),
             head_node: NodeFactory::unstable_place_holder(),
-
-            #[cfg(test)]
-            name: "new",
+            name: "fin",
         };
         Self {
             generation: 0.into(),
@@ -40,42 +36,44 @@ impl ActiveFrame {
         }
     }
 
-    pub fn prev_frame(&self) -> &BumpFrame {
+    /// The "work in progress frame" represents the frame that is currently being worked on.
+    pub fn wip_frame(&self) -> &BumpFrame {
         match self.generation.get() & 1 == 0 {
             true => &self.frames[0],
             false => &self.frames[1],
         }
     }
 
-    pub fn prev_frame_mut(&mut self) -> &mut BumpFrame {
+    pub fn wip_frame_mut(&mut self) -> &mut BumpFrame {
         match self.generation.get() & 1 == 0 {
             true => &mut self.frames[0],
             false => &mut self.frames[1],
         }
     }
 
-    pub fn cur_frame(&self) -> &BumpFrame {
+    /// The finished frame represents the frame that has been "finished" and cannot be modified again
+    pub fn finished_frame(&self) -> &BumpFrame {
         match self.generation.get() & 1 == 1 {
             true => &self.frames[0],
             false => &self.frames[1],
         }
     }
 
-    pub fn cur_frame_mut(&mut self) -> &mut BumpFrame {
+    pub fn finished_frame_mut(&mut self) -> &mut BumpFrame {
         match self.generation.get() & 1 == 1 {
             true => &mut self.frames[0],
             false => &mut self.frames[1],
         }
     }
     /// Give out our self-referential item with our own borrowed lifetime
-    pub fn current_head_node<'b>(&'b self) -> &'b VNode<'b> {
-        let cur_head = &self.cur_frame().head_node;
+    pub fn fin_head<'b>(&'b self) -> &'b VNode<'b> {
+        let cur_head = &self.finished_frame().head_node;
         unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
     }
 
     /// Give out our self-referential item with our own borrowed lifetime
-    pub fn prev_head_node<'b>(&'b self) -> &'b VNode<'b> {
-        let cur_head = &self.prev_frame().head_node;
+    pub fn wip_head<'b>(&'b self) -> &'b VNode<'b> {
+        let cur_head = &self.wip_frame().head_node;
         unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
     }
 
@@ -95,16 +93,16 @@ mod tests {
 
         // just cycle a few times and make sure we get the right frames out
         for _ in 0..5 {
-            let old = frames.prev_frame();
-            let new = frames.cur_frame();
-            assert_eq!(old.name, "old");
-            assert_eq!(new.name, "new");
+            let fin = frames.finished_frame();
+            let wip = frames.wip_frame();
+            assert_eq!(wip.name, "wip");
+            assert_eq!(fin.name, "fin");
             frames.cycle_frame();
 
-            let old = frames.prev_frame();
-            let new = frames.cur_frame();
-            assert_eq!(old.name, "new");
-            assert_eq!(new.name, "old");
+            let fin = frames.finished_frame();
+            let wip = frames.wip_frame();
+            assert_eq!(wip.name, "fin");
+            assert_eq!(fin.name, "wip");
             frames.cycle_frame();
         }
         assert_eq!(frames.generation.get(), 10);

+ 2 - 3
packages/core/src/context.rs

@@ -5,9 +5,8 @@ use std::{
     any::{Any, TypeId},
     cell::{Cell, RefCell},
     future::Future,
-    marker::PhantomData,
     ops::Deref,
-    rc::{Rc, Weak},
+    rc::Rc,
 };
 
 /// Components in Dioxus use the "Context" object to interact with their lifecycle.
@@ -98,7 +97,7 @@ impl<'src, P> Context<'src, P> {
     ///     })
     /// }
     /// ```
-    pub fn children(&self) -> &'src [VNode<'src>] {
+    pub fn children(&self) -> ScopeChildren<'src> {
         self.scope.child_nodes()
     }
 

+ 195 - 132
packages/core/src/diff.rs

@@ -85,7 +85,8 @@ impl<'r, 'b> DiffMachine<'r, 'b> {
     pub fn get_scope_mut(&mut self, id: &ScopeId) -> Option<&'b mut Scope> {
         // ensure we haven't seen this scope before
         // if we have, then we're trying to alias it, which is not allowed
-        // debug_assert!(!self.seen_nodes.contains(id));
+        debug_assert!(!self.seen_nodes.contains(id));
+
         unsafe { self.vdom.get_scope_mut(*id) }
     }
     pub fn get_scope(&mut self, id: &ScopeId) -> Option<&'b Scope> {
@@ -120,17 +121,17 @@ impl<'real, 'bump> DiffMachine<'real, 'bump> {
     //
     // each function call assumes the stack is fresh (empty).
     pub fn diff_node(&mut self, old_node: &'bump VNode<'bump>, new_node: &'bump VNode<'bump>) {
-        // currently busted for components - need to fid
-        let root = old_node.dom_id.get().expect(&format!(
-            "Should not be diffing old nodes that were never assigned, {:#?}",
-            old_node
-        ));
-
         match (&old_node.kind, &new_node.kind) {
             // Handle the "sane" cases first.
             // The rsx and html macros strongly discourage dynamic lists not encapsulated by a "Fragment".
             // So the sane (and fast!) cases are where the virtual structure stays the same and is easily diffable.
             (VNodeKind::Text(old), VNodeKind::Text(new)) => {
+                // currently busted for components - need to fid
+                let root = old_node.dom_id.get().expect(&format!(
+                    "Should not be diffing old nodes that were never assigned, {:#?}",
+                    old_node
+                ));
+
                 if old.text != new.text {
                     self.edits.push_root(root);
                     log::debug!("Text has changed {}, {}", old.text, new.text);
@@ -142,6 +143,12 @@ impl<'real, 'bump> DiffMachine<'real, 'bump> {
             }
 
             (VNodeKind::Element(old), VNodeKind::Element(new)) => {
+                // currently busted for components - need to fid
+                let root = old_node.dom_id.get().expect(&format!(
+                    "Should not be diffing old nodes that were never assigned, {:#?}",
+                    old_node
+                ));
+
                 // If the element type is completely different, the element needs to be re-rendered completely
                 // This is an optimization React makes due to how users structure their code
                 //
@@ -159,11 +166,25 @@ impl<'real, 'bump> DiffMachine<'real, 'bump> {
 
                 // push it just in case
                 // TODO: remove this - it clogs up things and is inefficient
+                // self.edits.push_root(root);
+
+                let mut has_comitted = false;
                 self.edits.push_root(root);
-                self.diff_listeners(old.listeners, new.listeners);
-                self.diff_attr(old.attributes, new.attributes, new.namespace);
-                self.diff_children(old.children, new.children);
+                // dbg!("diffing listeners");
+                self.diff_listeners(&mut has_comitted, old.listeners, new.listeners);
+                // dbg!("diffing attrs");
+                self.diff_attr(
+                    &mut has_comitted,
+                    old.attributes,
+                    new.attributes,
+                    new.namespace,
+                );
+                // dbg!("diffing childrne");
+                self.diff_children(&mut has_comitted, old.children, new.children);
                 self.edits.pop();
+                // if has_comitted {
+                //     self.edits.pop();
+                // }
             }
 
             (VNodeKind::Component(old), VNodeKind::Component(new)) => {
@@ -182,17 +203,18 @@ impl<'real, 'bump> DiffMachine<'real, 'bump> {
                     scope.caller = new.caller.clone();
 
                     // ack - this doesn't work on its own!
-                    scope.update_children(new.children);
+                    scope.update_children(ScopeChildren(new.children));
 
                     // React doesn't automatically memoize, but we do.
-                    let should_render = match old.comparator {
-                        Some(comparator) => comparator(new),
-                        None => true,
-                    };
+                    let should_render = true;
+                    // let should_render = match old.comparator {
+                    //     Some(comparator) => comparator(new),
+                    //     None => true,
+                    // };
 
                     if should_render {
                         scope.run_scope().unwrap();
-                        self.diff_node(scope.old_frame(), scope.next_frame());
+                        self.diff_node(scope.frames.wip_head(), scope.frames.fin_head());
                     } else {
                         //
                     }
@@ -239,7 +261,8 @@ impl<'real, 'bump> DiffMachine<'real, 'bump> {
                 // Diff where we think the elements are the same
                 if old.children.len() == new.children.len() {}
 
-                self.diff_children(old.children, new.children);
+                let mut has_comitted = false;
+                self.diff_children(&mut has_comitted, old.children, new.children);
             }
 
             // The strategy here is to pick the first possible node from the previous set and use that as our replace with root
@@ -259,6 +282,16 @@ impl<'real, 'bump> DiffMachine<'real, 'bump> {
                 | VNodeKind::Text(_)
                 | VNodeKind::Element(_),
             ) => {
+                log::info!(
+                    "taking the awkard diffing path {:#?}, {:#?}",
+                    old_node,
+                    new_node
+                );
+                // currently busted for components - need to fid
+                let root = old_node.dom_id.get().expect(&format!(
+                    "Should not be diffing old nodes that were never assigned, {:#?}",
+                    old_node
+                ));
                 // Choose the node to use as the placeholder for replacewith
                 let back_node_id = match old_node.kind {
                     // We special case these two types to avoid allocating the small-vecs
@@ -429,7 +462,7 @@ impl<'real, 'bump> DiffMachine<'real, 'bump> {
                         new_idx,
                         Some(parent_idx),
                         height,
-                        vcomponent.children,
+                        ScopeChildren(vcomponent.children),
                         self.vdom.clone(),
                     )
                 });
@@ -455,10 +488,11 @@ impl<'real, 'bump> DiffMachine<'real, 'bump> {
                 new_component.run_scope().unwrap();
 
                 // TODO: we need to delete (IE relcaim this node, otherwise the arena will grow infinitely)
-                let nextnode = new_component.next_frame();
+                let nextnode = new_component.frames.fin_head();
                 self.cur_idxs.push(new_idx);
                 let meta = self.create(nextnode);
                 self.cur_idxs.pop();
+                node.dom_id.set(nextnode.dom_id.get());
 
                 // Finally, insert this node as a seen node.
                 self.seen_nodes.insert(new_idx);
@@ -535,38 +569,49 @@ impl<'a, 'bump> DiffMachine<'a, 'bump> {
     //     [... node]
     //
     // The change list stack is left unchanged.
-    fn diff_listeners(&mut self, old: &[Listener<'_>], new: &[Listener<'_>]) {
+    fn diff_listeners(&mut self, committed: &mut bool, old: &[Listener<'_>], new: &[Listener<'_>]) {
         if !old.is_empty() || !new.is_empty() {
             // self.edits.commit_traversal();
         }
         // TODO
         // what does "diffing listeners" even mean?
 
-        'outer1: for (_l_idx, new_l) in new.iter().enumerate() {
-            // go through each new listener
-            // find its corresponding partner in the old list
-            // if any characteristics changed, remove and then re-add
-
-            // if nothing changed, then just move on
-            let _event_type = new_l.event;
-
-            for old_l in old {
-                if new_l.event == old_l.event {
-                    new_l.mounted_node.set(old_l.mounted_node.get());
-                    // if new_l.id != old_l.id {
-                    //     self.edits.remove_event_listener(event_type);
-                    //     // TODO! we need to mess with events and assign them by ElementId
-                    //     // self.edits
-                    //     //     .update_event_listener(event_type, new_l.scope, new_l.id)
-                    // }
-
-                    continue 'outer1;
-                }
-            }
-
-            // self.edits
-            //     .new_event_listener(event_type, new_l.scope, new_l.id);
+        for (old_l, new_l) in old.iter().zip(new.iter()) {
+            log::info!(
+                "moving listener forward with event. old: {:#?}",
+                old_l.mounted_node.get()
+            );
+            new_l.mounted_node.set(old_l.mounted_node.get());
         }
+        // 'outer1: for (_l_idx, new_l) in new.iter().enumerate() {
+        //     // go through each new listener
+        //     // find its corresponding partner in the old list
+        //     // if any characteristics changed, remove and then re-add
+
+        //     // if nothing changed, then just move on
+        //     let _event_type = new_l.event;
+
+        //     for old_l in old {
+        //         if new_l.event == old_l.event {
+        //             log::info!(
+        //                 "moving listener forward with event. old: {:#?}",
+        //                 old_l.mounted_node.get()
+        //             );
+        //             new_l.mounted_node.set(old_l.mounted_node.get());
+        //             // if new_l.id != old_l.id {
+        //             //     self.edits.remove_event_listener(event_type);
+        //             //     // TODO! we need to mess with events and assign them by ElementId
+        //             //     // self.edits
+        //             //     //     .update_event_listener(event_type, new_l.scope, new_l.id)
+        //             // }
+
+        //             continue 'outer1;
+        //         }
+        //     }
+
+        // self.edits
+        //     .new_event_listener(event_type, new_l.scope, new_l.id);
+        // }
 
         // 'outer2: for old_l in old {
         //     for new_l in new {
@@ -587,50 +632,61 @@ impl<'a, 'bump> DiffMachine<'a, 'bump> {
     // The change list stack is left unchanged.
     fn diff_attr(
         &mut self,
+        committed: &mut bool,
         old: &'bump [Attribute<'bump>],
         new: &'bump [Attribute<'bump>],
         namespace: Option<&'static str>,
     ) {
+        for (old_attr, new_attr) in old.iter().zip(new.iter()) {
+            if old_attr.value != new_attr.value {
+                if !*committed {
+                    *committed = true;
+                    // self.edits.push_root();
+                }
+            }
+            // if old_attr.name == new_attr.name {
+            // }
+        }
         // Do O(n^2) passes to add/update and remove attributes, since
         // there are almost always very few attributes.
         //
         // The "fast" path is when the list of attributes name is identical and in the same order
         // With the Rsx and Html macros, this will almost always be the case
-        'outer: for new_attr in new {
-            if new_attr.is_volatile {
-                // self.edits.commit_traversal();
-                self.edits
-                    .set_attribute(new_attr.name, new_attr.value, namespace);
-            } else {
-                for old_attr in old {
-                    if old_attr.name == new_attr.name {
-                        if old_attr.value != new_attr.value {
-                            // self.edits.commit_traversal();
-                            self.edits
-                                .set_attribute(new_attr.name, new_attr.value, namespace);
-                        }
-                        continue 'outer;
-                    } else {
-                        // names are different, a varying order of attributes has arrived
-                    }
-                }
+        // 'outer: for new_attr in new {
+        //     if new_attr.is_volatile {
+        //         // self.edits.commit_traversal();
+        //         self.edits
+        //             .set_attribute(new_attr.name, new_attr.value, namespace);
+        //     } else {
+        //         for old_attr in old {
+        //             if old_attr.name == new_attr.name {
+        //                 if old_attr.value != new_attr.value {
+        //                     // self.edits.commit_traversal();
+        //                     self.edits
+        //                         .set_attribute(new_attr.name, new_attr.value, namespace);
+        //                 }
+        //                 continue 'outer;
+        //             } else {
+        //                 // names are different, a varying order of attributes has arrived
+        //             }
+        //         }
 
-                // self.edits.commit_traversal();
-                self.edits
-                    .set_attribute(new_attr.name, new_attr.value, namespace);
-            }
-        }
+        //         // self.edits.commit_traversal();
+        //         self.edits
+        //             .set_attribute(new_attr.name, new_attr.value, namespace);
+        //     }
+        // }
 
-        'outer2: for old_attr in old {
-            for new_attr in new {
-                if old_attr.name == new_attr.name {
-                    continue 'outer2;
-                }
-            }
+        // 'outer2: for old_attr in old {
+        //     for new_attr in new {
+        //         if old_attr.name == new_attr.name {
+        //             continue 'outer2;
+        //         }
+        //     }
 
-            // self.edits.commit_traversal();
-            self.edits.remove_attribute(old_attr.name);
-        }
+        //     // self.edits.commit_traversal();
+        //     self.edits.remove_attribute(old_attr.name);
+        // }
     }
 
     // Diff the given set of old and new children.
@@ -641,66 +697,72 @@ impl<'a, 'bump> DiffMachine<'a, 'bump> {
     //     [... parent]
     //
     // the change list stack is in the same state when this function returns.
-    fn diff_children(&mut self, old: &'bump [VNode<'bump>], new: &'bump [VNode<'bump>]) {
-        if new.is_empty() {
-            if !old.is_empty() {
-                // self.edits.commit_traversal();
-                self.remove_all_children(old);
-            }
-            return;
-        }
+    fn diff_children(
+        &mut self,
+        committed: &mut bool,
+        old: &'bump [VNode<'bump>],
+        new: &'bump [VNode<'bump>],
+    ) {
+        // if new.is_empty() {
+        //     if !old.is_empty() {
+        //         // self.edits.commit_traversal();
+        //         self.remove_all_children(old);
+        //     }
+        //     return;
+        // }
 
-        if new.len() == 1 {
-            match (&old.first(), &new[0]) {
-                // (Some(VNodeKind::Text(old_vtext)), VNodeKind::Text(new_vtext))
-                //     if old_vtext.text == new_vtext.text =>
-                // {
-                //     // Don't take this fast path...
-                // }
+        // if new.len() == 1 {
+        //     match (&old.first(), &new[0]) {
+        // (Some(VNodeKind::Text(old_vtext)), VNodeKind::Text(new_vtext))
+        //     if old_vtext.text == new_vtext.text =>
+        // {
+        //     // Don't take this fast path...
+        // }
 
-                // (_, VNodeKind::Text(text)) => {
-                //     // self.edits.commit_traversal();
-                //     log::debug!("using optimized text set");
-                //     self.edits.set_text(text.text);
-                //     return;
-                // }
+        // (_, VNodeKind::Text(text)) => {
+        //     // self.edits.commit_traversal();
+        //     log::debug!("using optimized text set");
+        //     self.edits.set_text(text.text);
+        //     return;
+        // }
 
-                // todo: any more optimizations
-                (_, _) => {}
-            }
-        }
+        // todo: any more optimizations
+        //         (_, _) => {}
+        //     }
+        // }
 
-        if old.is_empty() {
-            if !new.is_empty() {
-                // self.edits.commit_traversal();
-                self.create_and_append_children(new);
-            }
-            return;
-        }
+        // if old.is_empty() {
+        //     if !new.is_empty() {
+        //         // self.edits.commit_traversal();
+        //         self.create_and_append_children(new);
+        //     }
+        //     return;
+        // }
 
-        let new_is_keyed = new[0].key.is_some();
-        let old_is_keyed = old[0].key.is_some();
+        // let new_is_keyed = new[0].key.is_some();
+        // let old_is_keyed = old[0].key.is_some();
 
-        debug_assert!(
-            new.iter().all(|n| n.key.is_some() == new_is_keyed),
-            "all siblings must be keyed or all siblings must be non-keyed"
-        );
-        debug_assert!(
-            old.iter().all(|o| o.key.is_some() == old_is_keyed),
-            "all siblings must be keyed or all siblings must be non-keyed"
-        );
+        // debug_assert!(
+        //     new.iter().all(|n| n.key.is_some() == new_is_keyed),
+        //     "all siblings must be keyed or all siblings must be non-keyed"
+        // );
+        // debug_assert!(
+        //     old.iter().all(|o| o.key.is_some() == old_is_keyed),
+        //     "all siblings must be keyed or all siblings must be non-keyed"
+        // );
 
-        if new_is_keyed && old_is_keyed {
-            log::warn!("using the wrong approach");
-            self.diff_non_keyed_children(old, new);
-            // todo!("Not yet implemented a migration away from temporaries");
-            // let t = self.edits.next_temporary();
-            // self.diff_keyed_children(old, new);
-            // self.edits.set_next_temporary(t);
-        } else {
-            // log::debug!("diffing non keyed children");
-            self.diff_non_keyed_children(old, new);
-        }
+        // if new_is_keyed && old_is_keyed {
+        //     // log::warn!("using the wrong approach");
+        //     self.diff_non_keyed_children(old, new);
+        //     // todo!("Not yet implemented a migration away from temporaries");
+        //     // let t = self.edits.next_temporary();
+        //     // self.diff_keyed_children(old, new);
+        //     // self.edits.set_next_temporary(t);
+        // } else {
+        //     // log::debug!("diffing non keyed children");
+        //     self.diff_non_keyed_children(old, new);
+        // }
+        self.diff_non_keyed_children(old, new);
     }
 
     // Diffing "keyed" children.
@@ -1146,8 +1208,8 @@ impl<'a, 'bump> DiffMachine<'a, 'bump> {
     // the change list stack is in the same state when this function returns.
     fn diff_non_keyed_children(&mut self, old: &'bump [VNode<'bump>], new: &'bump [VNode<'bump>]) {
         // Handled these cases in `diff_children` before calling this function.
-        debug_assert!(!new.is_empty());
-        debug_assert!(!old.is_empty());
+        // debug_assert!(!new.is_empty());
+        // debug_assert!(!old.is_empty());
 
         //     [... parent]
         // self.edits.go_down();
@@ -1165,6 +1227,7 @@ impl<'a, 'bump> DiffMachine<'a, 'bump> {
             //     log::debug!("Root is bad: {:#?}", old_child);
             // }
             // self.edits.push_root(did);
+            // dbg!("dffing child", new_child, old_child);
             self.diff_node(old_child, new_child);
 
             // let old_id = old_child.get_mounted_id(self.components).unwrap();
@@ -1333,7 +1396,7 @@ impl<'a> Iterator for RealChildIterator<'a> {
                         // let scope = self.scopes.get(sc.ass_scope.get().unwrap()).unwrap();
 
                         // Simply swap the current node on the stack with the root of the component
-                        *node = scope.root();
+                        *node = scope.frames.fin_head();
                     }
                 }
             } else {

+ 1 - 1
packages/core/src/events.rs

@@ -199,7 +199,7 @@ pub mod on {
                             event: shortname,
                             mounted_node: Cell::new(None),
                             scope: c.scope.our_arena_idx,
-                            callback: RefCell::new(callback),
+                            callback: RefCell::new(Some(callback)),
                         }
                     }
                 )*

+ 86 - 48
packages/core/src/nodes.rs

@@ -113,7 +113,7 @@ pub struct Listener<'bump> {
 
     pub mounted_node: Cell<Option<ElementId>>,
 
-    pub(crate) callback: RefCell<BumpBox<'bump, dyn FnMut(VirtualEvent) + 'bump>>,
+    pub(crate) callback: RefCell<Option<BumpBox<'bump, dyn FnMut(VirtualEvent) + 'bump>>>,
 }
 
 /// Virtual Components for custom user-defined components
@@ -133,6 +133,7 @@ pub struct VComponent<'src> {
     pub(crate) raw_props: *const (),
 
     // a pointer to the raw fn typ
+    // pub(crate) user_fc: BumpB\,
     pub(crate) user_fc: *const (),
 }
 
@@ -149,7 +150,8 @@ pub struct NodeFactory<'a> {
 impl<'a> NodeFactory<'a> {
     #[inline]
     pub fn bump(&self) -> &'a bumpalo::Bump {
-        &self.scope.cur_frame().bump
+        &self.scope.frames.wip_frame().bump
+        // &self.scope.cur_frame().bump
     }
 
     pub fn unstable_place_holder() -> VNode<'static> {
@@ -393,36 +395,14 @@ impl<'a> NodeFactory<'a> {
         unsafe { std::mem::transmute::<_, Captured<'static>>(caller) }
     }
 
-    pub fn fragment_from_iter(
-        self,
-        node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
-    ) -> VNode<'a> {
-        let mut nodes = bumpalo::collections::Vec::new_in(self.bump());
+    pub fn fragment_from_iter(self, node_iter: impl IntoVNodeList<'a> + 'a) -> VNode<'a> {
+        let children = node_iter.into_vnode_list(self);
 
-        for node in node_iter.into_iter() {
-            nodes.push(node.into_vnode(self));
-        }
-
-        if cfg!(debug_assertions) {
-            if nodes.len() > 1 {
-                if nodes.last().unwrap().key().is_none() {
-                    log::error!(
-                        r#"
-Warning: Each child in an array or iterator should have a unique "key" prop. 
-Not providing a key will lead to poor performance with lists.
-See docs.rs/dioxus for more information. 
----
-To help you identify where this error is coming from, we've generated a backtrace.
-                        "#,
-                    );
-                }
-            }
-        }
         VNode {
             dom_id: empty_cell(),
             key: None,
             kind: VNodeKind::Fragment(VFragment {
-                children: nodes.into_bump_slice(),
+                children,
                 is_static: false,
             }),
         }
@@ -448,6 +428,63 @@ pub trait IntoVNode<'a> {
     fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
 }
 
+pub trait IntoVNodeList<'a> {
+    fn into_vnode_list(self, cx: NodeFactory<'a>) -> &'a [VNode<'a>];
+}
+
+impl<'a, T, V> IntoVNodeList<'a> for T
+where
+    T: IntoIterator<Item = V>,
+    V: IntoVNode<'a>,
+{
+    fn into_vnode_list(self, cx: NodeFactory<'a>) -> &'a [VNode<'a>] {
+        let mut nodes = bumpalo::collections::Vec::new_in(cx.bump());
+
+        for node in self.into_iter() {
+            nodes.push(node.into_vnode(cx));
+        }
+
+        if cfg!(debug_assertions) {
+            if nodes.len() > 1 {
+                if nodes.last().unwrap().key().is_none() {
+                    log::error!(
+                        r#"
+        Warning: Each child in an array or iterator should have a unique "key" prop.
+        Not providing a key will lead to poor performance with lists.
+        See docs.rs/dioxus for more information.
+        ---
+        To help you identify where this error is coming from, we've generated a backtrace.
+                                "#,
+                    );
+                }
+            }
+        }
+
+        nodes.into_bump_slice()
+    }
+}
+
+pub struct ScopeChildren<'a>(pub &'a [VNode<'a>]);
+impl Copy for ScopeChildren<'_> {}
+impl<'a> Clone for ScopeChildren<'a> {
+    fn clone(&self) -> Self {
+        ScopeChildren(self.0)
+    }
+}
+impl ScopeChildren<'_> {
+    pub unsafe fn extend_lifetime(self) -> ScopeChildren<'static> {
+        std::mem::transmute(self)
+    }
+    pub unsafe fn unextend_lfetime<'a>(self) -> ScopeChildren<'a> {
+        std::mem::transmute(self)
+    }
+}
+impl<'a> IntoVNodeList<'a> for ScopeChildren<'a> {
+    fn into_vnode_list(self, _: NodeFactory<'a>) -> &'a [VNode<'a>] {
+        self.0
+    }
+}
+
 // For the case where a rendered VNode is passed into the rsx! macro through curly braces
 impl<'a> IntoIterator for VNode<'a> {
     type Item = VNode<'a>;
@@ -470,27 +507,28 @@ impl<'a> IntoVNode<'a> for VNode<'a> {
 // Designed to support indexing
 impl<'a> IntoVNode<'a> for &VNode<'a> {
     fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
-        let kind = match &self.kind {
-            VNodeKind::Element(element) => VNodeKind::Element(element),
-            VNodeKind::Text(old) => VNodeKind::Text(VText {
-                text: old.text,
-                is_static: old.is_static,
-            }),
-            VNodeKind::Fragment(fragment) => VNodeKind::Fragment(VFragment {
-                children: fragment.children,
-                is_static: fragment.is_static,
-            }),
-            VNodeKind::Component(component) => VNodeKind::Component(component),
-
-            // todo: it doesn't make much sense to pass in suspended nodes
-            // I think this is right but I'm not too sure.
-            VNodeKind::Suspended { node } => VNodeKind::Suspended { node: node.clone() },
-        };
-        VNode {
-            kind,
-            dom_id: self.dom_id.clone(),
-            key: self.key.clone(),
-        }
+        todo!()
+        // let kind = match &self.kind {
+        //     VNodeKind::Element(element) => VNodeKind::Element(element),
+        //     VNodeKind::Text(old) => VNodeKind::Text(VText {
+        //         text: old.text,
+        //         is_static: old.is_static,
+        //     }),
+        //     VNodeKind::Fragment(fragment) => VNodeKind::Fragment(VFragment {
+        //         children: fragment.children,
+        //         is_static: fragment.is_static,
+        //     }),
+        //     VNodeKind::Component(component) => VNodeKind::Component(component),
+
+        //     // todo: it doesn't make much sense to pass in suspended nodes
+        //     // I think this is right but I'm not too sure.
+        //     VNodeKind::Suspended { node } => VNodeKind::Suspended { node: node.clone() },
+        // };
+        // VNode {
+        //     kind,
+        //     dom_id: self.dom_id.clone(),
+        //     key: self.key.clone(),
+        // }
     }
 }
 

+ 51 - 34
packages/core/src/scope.rs

@@ -2,6 +2,7 @@ use crate::innerlude::*;
 use bumpalo::boxed::Box as BumpBox;
 use std::{
     any::{Any, TypeId},
+    borrow::BorrowMut,
     cell::{Cell, RefCell},
     collections::{HashMap, HashSet},
     future::Future,
@@ -29,7 +30,7 @@ pub struct Scope {
     // an internal, highly efficient storage of vnodes
     pub(crate) frames: ActiveFrame,
     pub(crate) caller: Rc<WrappedCaller>,
-    pub(crate) child_nodes: &'static [VNode<'static>],
+    pub(crate) child_nodes: ScopeChildren<'static>,
 
     // Listeners
     pub(crate) listeners: RefCell<Vec<*const Listener<'static>>>,
@@ -66,11 +67,14 @@ impl Scope {
 
         height: u32,
 
-        child_nodes: &'creator_node [VNode<'creator_node>],
-
+        child_nodes: ScopeChildren,
+        // child_nodes: &'creator_node [VNode<'creator_node>],
         vdom: SharedResources,
     ) -> Self {
-        let child_nodes = unsafe { std::mem::transmute(child_nodes) };
+        let child_nodes = unsafe { child_nodes.extend_lifetime() };
+        // let child_nodes = unsafe { std::mem::transmute(child_nodes) };
+
+        // dbg!(child_nodes);
         Self {
             child_nodes,
             caller,
@@ -93,9 +97,11 @@ impl Scope {
 
     pub(crate) fn update_children<'creator_node>(
         &mut self,
-        child_nodes: &'creator_node [VNode<'creator_node>],
+        child_nodes: ScopeChildren,
+        // child_nodes: &'creator_node [VNode<'creator_node>],
     ) {
-        let child_nodes = unsafe { std::mem::transmute(child_nodes) };
+        // let child_nodes = unsafe { std::mem::transmute(child_nodes) };
+        let child_nodes = unsafe { child_nodes.extend_lifetime() };
         self.child_nodes = child_nodes;
     }
 
@@ -104,17 +110,29 @@ impl Scope {
         // This breaks any latent references, invalidating every pointer referencing into it.
         // Remove all the outdated listeners
 
-        // This is a very dangerous operation
-        let next_frame = self.frames.prev_frame_mut();
-        next_frame.bump.reset();
+        log::debug!("reset okay");
 
         // make sure we call the drop implementation on all the listeners
         // this is important to not leak memory
-        self.listeners.borrow_mut().clear();
+        for li in self.listeners.borrow_mut().drain(..) {
+            log::debug!("dropping listener");
+            let d = unsafe { &*li };
+            let mut r = d.callback.borrow_mut();
+            let p = r.take().unwrap();
+            std::mem::drop(p);
+            // d.callback.borrow_mut();
+        }
+
+        // self.listeners.borrow_mut().clear();
 
         unsafe { self.hooks.reset() };
+
         self.listener_idx.set(0);
 
+        // This is a very dangerous operation
+        let next_frame = self.frames.wip_frame_mut();
+        next_frame.bump.reset();
+
         // Cast the caller ptr from static to one with our own reference
         let c3: &WrappedCaller = self.caller.as_ref();
 
@@ -126,8 +144,9 @@ impl Scope {
             }
             Some(new_head) => {
                 // the user's component succeeded. We can safely cycle to the next frame
-                self.frames.prev_frame_mut().head_node = unsafe { std::mem::transmute(new_head) };
+                self.frames.wip_frame_mut().head_node = unsafe { std::mem::transmute(new_head) };
                 self.frames.cycle_frame();
+                log::debug!("Cycle okay");
                 Ok(())
             }
         }
@@ -159,7 +178,11 @@ impl Scope {
         let raw_listener = listners.iter().find(|lis| {
             let search = unsafe { &***lis };
             let search_id = search.mounted_node.get();
-            log::info!("searching listener {:#?}", search_id);
+            log::info!(
+                "searching listener {:#?} for real {:?}",
+                search_id,
+                real_node_id
+            );
 
             match (real_node_id, search_id) {
                 (Some(e), Some(search_id)) => search_id == e,
@@ -169,8 +192,16 @@ impl Scope {
 
         if let Some(raw_listener) = raw_listener {
             let listener = unsafe { &**raw_listener };
+
+            log::info!(
+                "calling listener {:?}, {:?}",
+                listener.event,
+                listener.scope
+            );
             let mut cb = listener.callback.borrow_mut();
-            (cb)(event);
+            if let Some(cb) = cb.as_mut() {
+                (cb)(event);
+            }
         } else {
             log::warn!("An event was triggered but there was no listener to handle it");
         }
@@ -178,28 +209,14 @@ impl Scope {
         Ok(())
     }
 
-    #[inline]
-    pub(crate) fn next_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
-        self.frames.current_head_node()
-    }
-
-    #[inline]
-    pub(crate) fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
-        self.frames.prev_head_node()
-    }
-
-    #[inline]
-    pub(crate) fn cur_frame(&self) -> &BumpFrame {
-        self.frames.cur_frame()
+    pub fn root(&self) -> &VNode {
+        self.frames.fin_head()
     }
 
-    /// Get the root VNode of this component
-    #[inline]
-    pub fn root<'a>(&'a self) -> &'a VNode<'a> {
-        &self.frames.current_head_node()
-    }
-    #[inline]
-    pub fn child_nodes<'a>(&'a self) -> &'a [VNode<'a>] {
-        unsafe { std::mem::transmute(self.child_nodes) }
+    // #[inline]
+    pub fn child_nodes<'a>(&'a self) -> ScopeChildren {
+        // self.child_nodes
+        unsafe { self.child_nodes.unextend_lfetime() }
+        // unsafe { std::mem::transmute(self.child_nodes) }
     }
 }

+ 12 - 7
packages/core/src/virtual_dom.rs

@@ -135,7 +135,7 @@ impl VirtualDom {
 
         let base_scope = components.insert_scope_with_key(move |myidx| {
             let caller = NodeFactory::create_component_caller(root, props_ptr as *const _);
-            Scope::new(caller, myidx, None, 0, &[], link)
+            Scope::new(caller, myidx, None, 0, ScopeChildren(&[]), link)
         });
 
         Self {
@@ -192,7 +192,7 @@ impl VirtualDom {
 
         // We run the component. If it succeeds, then we can diff it and add the changes to the dom.
         if cur_component.run_scope().is_ok() {
-            let meta = diff_machine.create(cur_component.next_frame());
+            let meta = diff_machine.create(cur_component.frames.fin_head());
             diff_machine.edits.append_children(meta.added_to_stack);
         } else {
             // todo: should this be a hard error?
@@ -289,7 +289,8 @@ impl VirtualDom {
                         log::warn!("Suspense event came through, but there was no mounted node to update >:(");
                     }
                     Some(nodes) => {
-                        let nodes = scope.cur_frame().bump.alloc(nodes);
+                        todo!("using the wrong frame");
+                        let nodes = scope.frames.finished_frame().bump.alloc(nodes);
 
                         // push the old node's root onto the stack
                         let real_id = domnode.get().ok_or(Error::NotMounted)?;
@@ -329,20 +330,24 @@ impl VirtualDom {
                     // Make sure this isn't a node we've already seen, we don't want to double-render anything
                     // If we double-renderer something, this would cause memory safety issues
                     if diff_machine.seen_nodes.contains(&update.idx) {
+                        log::debug!("Skipping update for: {:#?}", update);
                         continue;
                     }
 
-                    // Now, all the "seen nodes" are nodes that got notified by running this listener
-                    diff_machine.seen_nodes.insert(update.idx.clone());
-
                     // Start a new mutable borrow to components
                     // We are guaranteeed that this scope is unique because we are tracking which nodes have modified in the diff machine
                     let cur_component = diff_machine
                         .get_scope_mut(&update.idx)
                         .expect("Failed to find scope or borrow would be aliasing");
 
+                    // Now, all the "seen nodes" are nodes that got notified by running this listener
+                    diff_machine.seen_nodes.insert(update.idx.clone());
+
                     if cur_component.run_scope().is_ok() {
-                        let (old, new) = (cur_component.old_frame(), cur_component.next_frame());
+                        let (old, new) = (
+                            cur_component.frames.wip_head(),
+                            cur_component.frames.fin_head(),
+                        );
                         diff_machine.diff_node(old, new);
                     }
                 }

+ 13 - 13
packages/desktop/src/dom.rs

@@ -2,32 +2,32 @@
 
 use dioxus_core::{DomEdit, RealDom};
 
-pub struct WebviewRegistry {}
+// pub struct WebviewRegistry {}
 
-impl WebviewRegistry {
-    pub fn new() -> Self {
-        Self {}
-    }
-}
+// impl WebviewRegistry {
+//     pub fn new() -> Self {
+//         Self {}
+//     }
+// }
 
 pub struct WebviewDom<'bump> {
     pub edits: Vec<DomEdit<'bump>>,
     pub node_counter: u64,
-    pub registry: WebviewRegistry,
+    // pub registry: WebviewRegistry,
 }
 impl WebviewDom<'_> {
-    pub fn new(registry: WebviewRegistry) -> Self {
+    pub fn new() -> Self {
         Self {
             edits: Vec::new(),
             node_counter: 0,
-            registry,
+            // registry,
         }
     }
 
-    // Finish using the dom (for its edit list) and give back the node and event registry
-    pub fn consume(self) -> WebviewRegistry {
-        self.registry
-    }
+    // // Finish using the dom (for its edit list) and give back the node and event registry
+    // pub fn consume(self) -> WebviewRegistry {
+    //     self.registry
+    // }
 }
 impl<'bump> RealDom<'bump> for WebviewDom<'bump> {
     fn raw_node_as_any(&self) -> &mut dyn std::any::Any {

+ 4 - 0
packages/desktop/src/index.html

@@ -29,6 +29,10 @@
                 this.stack.push(node);
             }
 
+            PopRoot(edit) {
+                this.stack.pop();
+            }
+
             AppendChildren(edit) {
                 let root = this.stack[this.stack.length - (edit.many + 1)];
                 for (let i = 0; i < edit.many; i++) {

+ 9 - 9
packages/desktop/src/lib.rs

@@ -70,7 +70,7 @@ impl<T: Properties + 'static> WebviewRenderer<T> {
 
         // todo: combine these or something
         let vdom = Arc::new(RwLock::new(vir));
-        let registry = Arc::new(RwLock::new(Some(WebviewRegistry::new())));
+        // let registry = Arc::new(RwLock::new(Some(WebviewRegistry::new())));
 
         let webview = WebViewBuilder::new(window)?
             // .with_visible(false)
@@ -83,10 +83,10 @@ impl<T: Properties + 'static> WebviewRenderer<T> {
                             serde_json::to_value(edits).unwrap()
                         } else {
                             let mut lock = vdom.write().unwrap();
-                            let mut reg_lock = registry.write().unwrap();
+                            // let mut reg_lock = registry.write().unwrap();
 
                             // Create the thin wrapper around the registry to collect the edits into
-                            let mut real = dom::WebviewDom::new(reg_lock.take().unwrap());
+                            let mut real = dom::WebviewDom::new();
 
                             // Serialize the edit stream
                             let edits = {
@@ -96,7 +96,7 @@ impl<T: Properties + 'static> WebviewRenderer<T> {
                             };
 
                             // Give back the registry into its slot
-                            *reg_lock = Some(real.consume());
+                            // *reg_lock = Some(real.consume());
                             edits
                         };
 
@@ -106,11 +106,11 @@ impl<T: Properties + 'static> WebviewRenderer<T> {
                     "user_event" => {
                         log::debug!("User event received");
 
-                        let registry = registry.clone();
+                        // let registry = registry.clone();
                         let vdom = vdom.clone();
                         let response = async_std::task::block_on(async move {
                             let mut lock = vdom.write().unwrap();
-                            let mut reg_lock = registry.write().unwrap();
+                            // let mut reg_lock = registry.write().unwrap();
 
                             // a deserialized event
                             let data = req.params.unwrap();
@@ -120,7 +120,7 @@ impl<T: Properties + 'static> WebviewRenderer<T> {
                             lock.queue_event(event);
 
                             // Create the thin wrapper around the registry to collect the edits into
-                            let mut real = dom::WebviewDom::new(reg_lock.take().unwrap());
+                            let mut real = dom::WebviewDom::new();
 
                             // Serialize the edit stream
                             //
@@ -131,7 +131,7 @@ impl<T: Properties + 'static> WebviewRenderer<T> {
                             let edits = serde_json::to_value(edits).unwrap();
 
                             // Give back the registry into its slot
-                            *reg_lock = Some(real.consume());
+                            // *reg_lock = Some(real.consume());
 
                             // Return the edits into the webview runtime
                             Some(RpcResponse::new_result(req.id.take(), Some(edits)))
@@ -206,7 +206,7 @@ impl<T: Properties + 'static> WebviewRenderer<T> {
 use serde::{Deserialize, Serialize};
 use serde_json::Value;
 
-use crate::dom::WebviewRegistry;
+// use crate::dom::WebviewRegistry;
 
 #[derive(Debug, Serialize, Deserialize)]
 struct MessageParameters {

+ 68 - 0
packages/web/examples/btns.rs

@@ -0,0 +1,68 @@
+//! Example: Webview Renderer
+//! -------------------------
+//!
+//! This example shows how to use the dioxus_desktop crate to build a basic desktop application.
+//!
+//! Under the hood, the dioxus_desktop crate bridges a native Dioxus VirtualDom with a custom prebuit application running
+//! in the webview runtime. Custom handlers are provided for the webview instance to consume patches and emit user events
+//! into the native VDom instance.
+//!
+//! Currently, NodeRefs won't work properly, but all other event functionality will.
+
+use dioxus::prelude::*;
+use dioxus_core as dioxus;
+use dioxus_hooks::*;
+use dioxus_html as dioxus_elements;
+
+// #[cfg]
+fn main() {
+    wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
+    dioxus_web::launch(App, |c| c);
+    // env_logger::init();
+    // dioxus::web::launch(App, |c| c);
+}
+
+static App: FC<()> = |cx| {
+    dbg!("rednering parent");
+    cx.render(rsx! {
+        div {
+            But {
+                h1 {"he"}
+            }
+            // But {
+            //     h1 {"llo"}
+            // }
+            // But {
+            //     h1 {"world"}
+            // }
+        }
+    })
+};
+
+static But: FC<()> = |cx| {
+    let mut count = use_state(cx, || 0);
+
+    // let d = Dropper { name: "asd" };
+    // let handler = move |_| {
+    //     dbg!(d.name);
+    // };
+
+    cx.render(rsx! {
+        div {
+            h1 { "Hifive counter: {count}" }
+            {cx.children()}
+            button { onclick: move |_| count += 1, "Up high!" }
+            button { onclick: move |_| count -= 1, "Down low!" }
+            // button { onclick: {handler}, "Down low!" }
+        }
+    })
+};
+
+// struct Dropper {
+//     name: &'static str,
+// }
+// impl Drop for Dropper {
+//     fn drop(&mut self) {
+//         dbg!("dropped");
+//     }
+// }