浏览代码

feat: diffing works on desktop!

Jonathan Kelley 2 年之前
父节点
当前提交
20f9957fbe

+ 9 - 2
packages/core/src/diff.rs

@@ -42,8 +42,10 @@ impl<'b> VirtualDom {
     pub fn diff_scope(&mut self, mutations: &mut Mutations<'b>, scope: ScopeId) {
     pub fn diff_scope(&mut self, mutations: &mut Mutations<'b>, scope: ScopeId) {
         let scope_state = &mut self.scopes[scope.0];
         let scope_state = &mut self.scopes[scope.0];
 
 
-        let cur_arena = scope_state.current_frame();
-        let prev_arena = scope_state.previous_frame();
+        let cur_arena = scope_state.previous_frame();
+        let prev_arena = scope_state.current_frame();
+        // let cur_arena = scope_state.current_frame();
+        // let prev_arena = scope_state.previous_frame();
 
 
         // relax the borrow checker
         // relax the borrow checker
         let cur_arena: &BumpFrame = unsafe { std::mem::transmute(cur_arena) };
         let cur_arena: &BumpFrame = unsafe { std::mem::transmute(cur_arena) };
@@ -62,8 +64,10 @@ impl<'b> VirtualDom {
         );
         );
 
 
         self.scope_stack.push(scope);
         self.scope_stack.push(scope);
+
         let left = unsafe { prev_arena.load_node() };
         let left = unsafe { prev_arena.load_node() };
         let right = unsafe { cur_arena.load_node() };
         let right = unsafe { cur_arena.load_node() };
+
         self.diff_maybe_node(mutations, left, right);
         self.diff_maybe_node(mutations, left, right);
         self.scope_stack.pop();
         self.scope_stack.pop();
     }
     }
@@ -105,6 +109,8 @@ impl<'b> VirtualDom {
         left_template: &'b VNode<'b>,
         left_template: &'b VNode<'b>,
         right_template: &'b VNode<'b>,
         right_template: &'b VNode<'b>,
     ) {
     ) {
+        println!("diffing {:?} and {:?}", left_template, right_template);
+
         if left_template.template.id != right_template.template.id {
         if left_template.template.id != right_template.template.id {
             // do a light diff of the roots nodes.
             // do a light diff of the roots nodes.
             return;
             return;
@@ -125,6 +131,7 @@ impl<'b> VirtualDom {
                 .set(left_attr.mounted_element.get());
                 .set(left_attr.mounted_element.get());
 
 
             if left_attr.value != right_attr.value {
             if left_attr.value != right_attr.value {
+                println!("DIFF ATTR: {:?} -> {:?}", left_attr, right_attr);
                 let value = "todo!()";
                 let value = "todo!()";
                 muts.push(Mutation::SetAttribute {
                 muts.push(Mutation::SetAttribute {
                     id: left_attr.mounted_element.get(),
                     id: left_attr.mounted_element.get(),

+ 15 - 0
packages/core/src/scheduler/suspense.rs

@@ -19,6 +19,19 @@ pub struct SuspenseBoundary {
     pub waiting_on: RefCell<HashSet<SuspenseId>>,
     pub waiting_on: RefCell<HashSet<SuspenseId>>,
     pub mutations: RefCell<Mutations<'static>>,
     pub mutations: RefCell<Mutations<'static>>,
     pub placeholder: Cell<Option<ElementId>>,
     pub placeholder: Cell<Option<ElementId>>,
+
+    // whenever the suspense resolves, we call this onresolve function
+    // this lets us do things like putting up a loading spinner
+    //
+    // todo: we need a way of controlling whether or not a component hides itself but still processes changes
+    // If we run into suspense, we perform a diff, so its important that the old elements are still around.
+    //
+    // When the timer expires, I imagine a container could hide the elements and show the spinner. This, however,
+    // can not be
+    pub onresolve: Option<Box<dyn FnOnce()>>,
+
+    /// Called when
+    pub onstart: Option<Box<dyn FnOnce()>>,
 }
 }
 
 
 impl SuspenseBoundary {
 impl SuspenseBoundary {
@@ -28,6 +41,8 @@ impl SuspenseBoundary {
             waiting_on: Default::default(),
             waiting_on: Default::default(),
             mutations: RefCell::new(Mutations::new(0)),
             mutations: RefCell::new(Mutations::new(0)),
             placeholder: Cell::new(None),
             placeholder: Cell::new(None),
+            onresolve: None,
+            onstart: None,
         })
         })
     }
     }
 }
 }

+ 22 - 14
packages/core/src/scheduler/task.rs

@@ -1,8 +1,9 @@
 use super::{waker::RcWake, Scheduler, SchedulerMsg};
 use super::{waker::RcWake, Scheduler, SchedulerMsg};
 use crate::ScopeId;
 use crate::ScopeId;
+use std::cell::RefCell;
 use std::future::Future;
 use std::future::Future;
 use std::task::Context;
 use std::task::Context;
-use std::{cell::UnsafeCell, pin::Pin, rc::Rc, task::Poll};
+use std::{pin::Pin, rc::Rc, task::Poll};
 
 
 #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
 #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
@@ -10,23 +11,19 @@ pub struct TaskId(pub usize);
 
 
 /// the task itself is the waker
 /// the task itself is the waker
 pub(crate) struct LocalTask {
 pub(crate) struct LocalTask {
-    pub id: TaskId,
     pub scope: ScopeId,
     pub scope: ScopeId,
-    pub tx: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
-
-    // todo: use rc and weak, or the bump slab instead of unsafecell
-    pub task: UnsafeCell<Pin<Box<dyn Future<Output = ()> + 'static>>>,
+    id: TaskId,
+    tx: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
+    task: RefCell<Pin<Box<dyn Future<Output = ()> + 'static>>>,
 }
 }
 
 
 impl LocalTask {
 impl LocalTask {
-    pub fn progress(self: &Rc<Self>) -> bool {
+    /// Poll this task and return whether or not it is complete
+    pub(super) fn progress(self: &Rc<Self>) -> bool {
         let waker = self.waker();
         let waker = self.waker();
         let mut cx = Context::from_waker(&waker);
         let mut cx = Context::from_waker(&waker);
 
 
-        // safety: the waker owns its task and everythig is single threaded
-        let fut = unsafe { &mut *self.task.get() };
-
-        match Pin::new(fut).poll(&mut cx) {
+        match self.task.borrow_mut().as_mut().poll(&mut cx) {
             Poll::Ready(_) => true,
             Poll::Ready(_) => true,
             _ => false,
             _ => false,
         }
         }
@@ -34,6 +31,15 @@ impl LocalTask {
 }
 }
 
 
 impl Scheduler {
 impl Scheduler {
+    /// Start a new future on the same thread as the rest of the VirtualDom.
+    ///
+    /// This future will not contribute to suspense resolving, so you should primarily use this for reacting to changes
+    /// and long running tasks.
+    ///
+    /// Whenever the component that owns this future is dropped, the future will be dropped as well.
+    ///
+    /// Spawning a future onto the root scope will cause it to be dropped when the root component is dropped - which
+    /// will only occur when the VirtuaalDom itself has been dropped.
     pub fn spawn(&self, scope: ScopeId, task: impl Future<Output = ()> + 'static) -> TaskId {
     pub fn spawn(&self, scope: ScopeId, task: impl Future<Output = ()> + 'static) -> TaskId {
         let mut tasks = self.tasks.borrow_mut();
         let mut tasks = self.tasks.borrow_mut();
         let entry = tasks.vacant_entry();
         let entry = tasks.vacant_entry();
@@ -42,7 +48,7 @@ impl Scheduler {
         entry.insert(Rc::new(LocalTask {
         entry.insert(Rc::new(LocalTask {
             id: task_id,
             id: task_id,
             tx: self.sender.clone(),
             tx: self.sender.clone(),
-            task: UnsafeCell::new(Box::pin(task)),
+            task: RefCell::new(Box::pin(task)),
             scope,
             scope,
         }));
         }));
 
 
@@ -53,9 +59,11 @@ impl Scheduler {
         task_id
         task_id
     }
     }
 
 
-    // drops the future
+    /// Drop the future with the given TaskId
+    ///
+    /// This does nto abort the task, so you'll want to wrap it in an aborthandle if that's important to you
     pub fn remove(&self, id: TaskId) {
     pub fn remove(&self, id: TaskId) {
-        //
+        self.tasks.borrow_mut().remove(id.0);
     }
     }
 }
 }
 
 

+ 2 - 2
packages/core/src/scheduler/wait.rs

@@ -4,10 +4,10 @@ use std::task::{Context, Poll};
 use crate::{
 use crate::{
     factory::RenderReturn,
     factory::RenderReturn,
     innerlude::{Mutation, Mutations, SuspenseContext},
     innerlude::{Mutation, Mutations, SuspenseContext},
-    ScopeId, TaskId, VNode, VirtualDom,
+    TaskId, VNode, VirtualDom,
 };
 };
 
 
-use super::{waker::RcWake, SuspenseId, SuspenseLeaf};
+use super::{waker::RcWake, SuspenseId};
 
 
 impl VirtualDom {
 impl VirtualDom {
     /// Handle notifications by tasks inside the scheduler
     /// Handle notifications by tasks inside the scheduler

+ 13 - 5
packages/core/src/scope_arena.rs

@@ -65,8 +65,11 @@ impl VirtualDom {
     }
     }
 
 
     pub(crate) fn run_scope(&mut self, scope_id: ScopeId) -> &RenderReturn {
     pub(crate) fn run_scope(&mut self, scope_id: ScopeId) -> &RenderReturn {
+        println!("run_scope: {:?}", scope_id);
+
         let mut new_nodes = unsafe {
         let mut new_nodes = unsafe {
             let scope = &mut self.scopes[scope_id.0];
             let scope = &mut self.scopes[scope_id.0];
+            println!("run_scope: scope: {:?}", scope.render_cnt.get());
             scope.hook_idx.set(0);
             scope.hook_idx.set(0);
 
 
             // safety: due to how we traverse the tree, we know that the scope is not currently aliased
             // safety: due to how we traverse the tree, we know that the scope is not currently aliased
@@ -123,17 +126,22 @@ impl VirtualDom {
             }
             }
         };
         };
 
 
+        /*
+        todo: use proper mutability here
+
+        right now we're aliasing the scope, which is not allowed
+        */
+
         let scope = &mut self.scopes[scope_id.0];
         let scope = &mut self.scopes[scope_id.0];
-        let frame = match scope.render_cnt % 2 {
-            0 => &mut scope.node_arena_1,
-            1 => &mut scope.node_arena_2,
-            _ => unreachable!(),
-        };
+        let frame = scope.current_frame();
 
 
         // set the head of the bump frame
         // set the head of the bump frame
         let alloced = frame.bump.alloc(new_nodes);
         let alloced = frame.bump.alloc(new_nodes);
         frame.node.set(alloced);
         frame.node.set(alloced);
 
 
+        // And move the render generation forward by one
+        scope.render_cnt.set(scope.render_cnt.get() + 1);
+
         // rebind the lifetime now that its stored internally
         // rebind the lifetime now that its stored internally
         unsafe { mem::transmute(alloced) }
         unsafe { mem::transmute(alloced) }
     }
     }

+ 3 - 3
packages/core/src/scopes.rs

@@ -43,7 +43,7 @@ impl<'a, T> std::ops::Deref for Scoped<'a, T> {
 pub struct ScopeId(pub usize);
 pub struct ScopeId(pub usize);
 
 
 pub struct ScopeState {
 pub struct ScopeState {
-    pub(crate) render_cnt: usize,
+    pub(crate) render_cnt: Cell<usize>,
 
 
     pub(crate) node_arena_1: BumpFrame,
     pub(crate) node_arena_1: BumpFrame,
     pub(crate) node_arena_2: BumpFrame,
     pub(crate) node_arena_2: BumpFrame,
@@ -69,14 +69,14 @@ pub struct ScopeState {
 
 
 impl ScopeState {
 impl ScopeState {
     pub fn current_frame(&self) -> &BumpFrame {
     pub fn current_frame(&self) -> &BumpFrame {
-        match self.render_cnt % 2 {
+        match self.render_cnt.get() % 2 {
             0 => &self.node_arena_1,
             0 => &self.node_arena_1,
             1 => &self.node_arena_2,
             1 => &self.node_arena_2,
             _ => unreachable!(),
             _ => unreachable!(),
         }
         }
     }
     }
     pub fn previous_frame(&self) -> &BumpFrame {
     pub fn previous_frame(&self) -> &BumpFrame {
-        match self.render_cnt % 2 {
+        match self.render_cnt.get() % 2 {
             1 => &self.node_arena_1,
             1 => &self.node_arena_1,
             0 => &self.node_arena_2,
             0 => &self.node_arena_2,
             _ => unreachable!(),
             _ => unreachable!(),

+ 54 - 60
packages/core/src/virtual_dom.rs

@@ -9,9 +9,9 @@ use crate::{
     nodes::{Template, TemplateId},
     nodes::{Template, TemplateId},
     scheduler::{SuspenseBoundary, SuspenseId},
     scheduler::{SuspenseBoundary, SuspenseId},
     scopes::{ScopeId, ScopeState},
     scopes::{ScopeId, ScopeState},
-    Attribute, AttributeValue, Element, EventPriority, Scope, SuspenseContext, UiEvent,
+    AttributeValue, Element, EventPriority, Scope, SuspenseContext, UiEvent,
 };
 };
-use futures_util::{pin_mut, FutureExt, StreamExt};
+use futures_util::{pin_mut, StreamExt};
 use slab::Slab;
 use slab::Slab;
 use std::rc::Rc;
 use std::rc::Rc;
 use std::{
 use std::{
@@ -244,7 +244,7 @@ impl VirtualDom {
         ))));
         ))));
 
 
         // The root component is always a suspense boundary for any async children
         // The root component is always a suspense boundary for any async children
-        // This could be unexpected, so we might rethink this behavior
+        // This could be unexpected, so we might rethink this behavior later
         root.provide_context(SuspenseBoundary::new(ScopeId(0)));
         root.provide_context(SuspenseBoundary::new(ScopeId(0)));
 
 
         // the root element is always given element 0
         // the root element is always given element 0
@@ -253,26 +253,36 @@ impl VirtualDom {
         dom
         dom
     }
     }
 
 
+    /// Get the state for any scope given its ID
+    ///
+    /// This is useful for inserting or removing contexts from a scope, or rendering out its root node
     pub fn get_scope(&self, id: ScopeId) -> Option<&ScopeState> {
     pub fn get_scope(&self, id: ScopeId) -> Option<&ScopeState> {
         self.scopes.get(id.0)
         self.scopes.get(id.0)
     }
     }
 
 
+    /// Get the single scope at the top of the VirtualDom tree that will always be around
+    ///
+    /// This scope has a ScopeId of 0 and is the root of the tree
     pub fn base_scope(&self) -> &ScopeState {
     pub fn base_scope(&self) -> &ScopeState {
         self.scopes.get(0).unwrap()
         self.scopes.get(0).unwrap()
     }
     }
 
 
     /// Build the virtualdom with a global context inserted into the base scope
     /// Build the virtualdom with a global context inserted into the base scope
+    ///
+    /// This is useful for what is essentially dependency injection, when building the app
     pub fn with_root_context<T: Clone + 'static>(self, context: T) -> Self {
     pub fn with_root_context<T: Clone + 'static>(self, context: T) -> Self {
         self.base_scope().provide_context(context);
         self.base_scope().provide_context(context);
         self
         self
     }
     }
 
 
-    fn mark_dirty_scope(&mut self, id: ScopeId) {
+    /// Manually mark a scope as requiring a re-render
+    pub fn mark_dirty_scope(&mut self, id: ScopeId) {
         let height = self.scopes[id.0].height;
         let height = self.scopes[id.0].height;
         self.dirty_scopes.insert(DirtyScope { height, id });
         self.dirty_scopes.insert(DirtyScope { height, id });
     }
     }
 
 
-    fn is_scope_suspended(&self, id: ScopeId) -> bool {
+    /// Determine whether or not a scope is currently in a suspended state
+    pub fn is_scope_suspended(&self, id: ScopeId) -> bool {
         !self.scopes[id.0]
         !self.scopes[id.0]
             .consume_context::<SuspenseContext>()
             .consume_context::<SuspenseContext>()
             .unwrap()
             .unwrap()
@@ -281,35 +291,29 @@ impl VirtualDom {
             .is_empty()
             .is_empty()
     }
     }
 
 
-    /// Returns true if there is any suspended work left to be done.
+    /// Determine is the tree is at all suspended. Used by SSR and other outside mechanisms to determine if the tree is
+    /// ready to be rendered.
     pub fn has_suspended_work(&self) -> bool {
     pub fn has_suspended_work(&self) -> bool {
         !self.scheduler.leaves.borrow().is_empty()
         !self.scheduler.leaves.borrow().is_empty()
     }
     }
 
 
     /// Call a listener inside the VirtualDom with data from outside the VirtualDom.
     /// Call a listener inside the VirtualDom with data from outside the VirtualDom.
     ///
     ///
-    /// This method will identify the appropriate element
-    ///
-    ///
-    ///
-    ///
-    ///
+    /// This method will identify the appropriate element. The data must match up with the listener delcared. Note that
+    /// this method does not give any indication as to the success of the listener call. If the listener is not found,
+    /// nothing will happen.
     ///
     ///
+    /// It is up to the listeners themselves to mark nodes as dirty.
     ///
     ///
+    /// If you have multiple events, you can call this method multiple times before calling "render_with_deadline"
     pub fn handle_event(
     pub fn handle_event(
         &mut self,
         &mut self,
         name: &str,
         name: &str,
         data: Rc<dyn Any>,
         data: Rc<dyn Any>,
         element: ElementId,
         element: ElementId,
         bubbles: bool,
         bubbles: bool,
-        // todo: priority is helpful when scheduling work around suspense, but we don't currently use it
         _priority: EventPriority,
         _priority: EventPriority,
     ) {
     ) {
-        let uievent = UiEvent {
-            bubbles: Rc::new(Cell::new(bubbles)),
-            data,
-        };
-
         /*
         /*
         ------------------------
         ------------------------
         The algorithm works by walking through the list of dynamic attributes, checking their paths, and breaking when
         The algorithm works by walking through the list of dynamic attributes, checking their paths, and breaking when
@@ -321,37 +325,35 @@ impl VirtualDom {
         If we wanted to do capturing, then we would accumulate all the listeners and call them in reverse order.
         If we wanted to do capturing, then we would accumulate all the listeners and call them in reverse order.
         ----------------------
         ----------------------
         |           <-- yes (is ascendant)
         |           <-- yes (is ascendant)
-        | | |       <-- no  (is not ascendant)
+        | | |       <-- no  (is not direct ascendant)
         | |         <-- yes (is ascendant)
         | |         <-- yes (is ascendant)
-        | | | | |   <--- target element, break early
+        | | | | |   <--- target element, break early, don't check other listeners
         | | |       <-- no, broke early
         | | |       <-- no, broke early
         |           <-- no, broke early
         |           <-- no, broke early
         */
         */
         let mut parent_path = self.elements.get(element.0);
         let mut parent_path = self.elements.get(element.0);
         let mut listeners = vec![];
         let mut listeners = vec![];
 
 
+        // We will clone this later. The data itself is wrapped in RC to be used in callbacks if required
+        let uievent = UiEvent {
+            bubbles: Rc::new(Cell::new(bubbles)),
+            data,
+        };
+
+        // Loop through each dynamic attribute in this template before moving up to the template's parent.
         while let Some(el_ref) = parent_path {
         while let Some(el_ref) = parent_path {
+            // safety: we maintain references of all vnodes in the element slab
             let template = unsafe { &*el_ref.template };
             let template = unsafe { &*el_ref.template };
             let target_path = el_ref.path;
             let target_path = el_ref.path;
 
 
-            let mut attrs = template.dynamic_attrs.iter().enumerate();
-
-            while let Some((idx, attr)) = attrs.next() {
-                pub fn is_path_ascendant(small: &[u8], big: &[u8]) -> bool {
+            for (idx, attr) in template.dynamic_attrs.iter().enumerate() {
+                fn is_path_ascendant(small: &[u8], big: &[u8]) -> bool {
                     small.len() >= big.len() && small == &big[..small.len()]
                     small.len() >= big.len() && small == &big[..small.len()]
                 }
                 }
 
 
                 let this_path = template.template.attr_paths[idx];
                 let this_path = template.template.attr_paths[idx];
 
 
-                println!(
-                    "is {:?} ascendant of {:?} ? {}",
-                    this_path,
-                    target_path,
-                    is_path_ascendant(this_path, target_path)
-                );
-
-                println!("{ } - {name}, - {}", attr.name, &attr.name[2..]);
-
+                // listeners are required to be prefixed with "on", but they come back to the virtualdom with that missing
                 if &attr.name[2..] == name && is_path_ascendant(&target_path, &this_path) {
                 if &attr.name[2..] == name && is_path_ascendant(&target_path, &this_path) {
                     listeners.push(&attr.value);
                     listeners.push(&attr.value);
 
 
@@ -367,11 +369,11 @@ impl VirtualDom {
                 }
                 }
             }
             }
 
 
+            // Now that we've accumulated all the parent attributes for the target element, call them in reverse order
+            // We check the bubble state between each call to see if the event has been stopped from bubbling
             for listener in listeners.drain(..).rev() {
             for listener in listeners.drain(..).rev() {
                 if let AttributeValue::Listener(listener) = listener {
                 if let AttributeValue::Listener(listener) = listener {
                     listener.borrow_mut()(uievent.clone());
                     listener.borrow_mut()(uievent.clone());
-
-                    // Break if the event doesn't bubble
                     if !uievent.bubbles.get() {
                     if !uievent.bubbles.get() {
                         return;
                         return;
                     }
                     }
@@ -452,13 +454,17 @@ impl VirtualDom {
     pub fn rebuild<'a>(&'a mut self) -> Mutations<'a> {
     pub fn rebuild<'a>(&'a mut self) -> Mutations<'a> {
         let mut mutations = Mutations::new(0);
         let mut mutations = Mutations::new(0);
 
 
-        let root_node = unsafe { self.run_scope_extend(ScopeId(0)) };
-        match root_node {
+        match unsafe { self.run_scope_extend(ScopeId(0)) } {
+            // Rebuilding implies we append the created elements to the root
             RenderReturn::Sync(Some(node)) => {
             RenderReturn::Sync(Some(node)) => {
                 let m = self.create_scope(ScopeId(0), &mut mutations, node);
                 let m = self.create_scope(ScopeId(0), &mut mutations, node);
                 mutations.push(Mutation::AppendChildren { m });
                 mutations.push(Mutation::AppendChildren { m });
             }
             }
-            RenderReturn::Sync(None) => {}
+            // If nothing was rendered, then insert a placeholder element instead
+            RenderReturn::Sync(None) => {
+                mutations.push(Mutation::CreatePlaceholder { id: ElementId(1) });
+                mutations.push(Mutation::AppendChildren { m: 1 });
+            }
             RenderReturn::Async(_) => unreachable!("Root scope cannot be an async component"),
             RenderReturn::Async(_) => unreachable!("Root scope cannot be an async component"),
         }
         }
 
 
@@ -475,7 +481,9 @@ impl VirtualDom {
         // Now run render with deadline but dont even try to poll any async tasks
         // Now run render with deadline but dont even try to poll any async tasks
         let fut = self.render_with_deadline(std::future::ready(()));
         let fut = self.render_with_deadline(std::future::ready(()));
         pin_mut!(fut);
         pin_mut!(fut);
-        match fut.poll_unpin(&mut cx) {
+
+        // The root component is not allowed to be async
+        match fut.poll(&mut cx) {
             std::task::Poll::Ready(mutations) => mutations,
             std::task::Poll::Ready(mutations) => mutations,
             std::task::Poll::Pending => panic!("render_immediate should never return pending"),
             std::task::Poll::Pending => panic!("render_immediate should never return pending"),
         }
         }
@@ -491,20 +499,20 @@ impl VirtualDom {
         deadline: impl Future<Output = ()>,
         deadline: impl Future<Output = ()>,
     ) -> Mutations<'a> {
     ) -> Mutations<'a> {
         use futures_util::future::{select, Either};
         use futures_util::future::{select, Either};
+        pin_mut!(deadline);
 
 
         let mut mutations = Mutations::new(0);
         let mut mutations = Mutations::new(0);
-        pin_mut!(deadline);
 
 
         loop {
         loop {
             // first, unload any complete suspense trees
             // first, unload any complete suspense trees
             for finished_fiber in self.finished_fibers.drain(..) {
             for finished_fiber in self.finished_fibers.drain(..) {
                 let scope = &mut self.scopes[finished_fiber.0];
                 let scope = &mut self.scopes[finished_fiber.0];
                 let context = scope.has_context::<SuspenseContext>().unwrap();
                 let context = scope.has_context::<SuspenseContext>().unwrap();
-                println!("unloading suspense tree {:?}", context.mutations);
 
 
                 mutations.extend(context.mutations.borrow_mut().template_mutations.drain(..));
                 mutations.extend(context.mutations.borrow_mut().template_mutations.drain(..));
                 mutations.extend(context.mutations.borrow_mut().drain(..));
                 mutations.extend(context.mutations.borrow_mut().drain(..));
 
 
+                // TODO: count how many nodes are on the stack?
                 mutations.push(Mutation::ReplaceWith {
                 mutations.push(Mutation::ReplaceWith {
                     id: context.placeholder.get().unwrap(),
                     id: context.placeholder.get().unwrap(),
                     m: 1,
                     m: 1,
@@ -525,36 +533,22 @@ impl VirtualDom {
 
 
             // Wait for suspense, or a deadline
             // Wait for suspense, or a deadline
             if self.dirty_scopes.is_empty() {
             if self.dirty_scopes.is_empty() {
+                // If there's no suspense, then we have no reason to wait
                 if self.scheduler.leaves.borrow().is_empty() {
                 if self.scheduler.leaves.borrow().is_empty() {
                     return mutations;
                     return mutations;
                 }
                 }
 
 
-                let (work, deadline) = (self.wait_for_work(), &mut deadline);
+                // Poll the suspense leaves in the meantime
+                let work = self.wait_for_work();
                 pin_mut!(work);
                 pin_mut!(work);
 
 
-                if let Either::Left((_, _)) = select(deadline, work).await {
+                // If the deadline is exceded (left) then we should return the mutations we have
+                if let Either::Left((_, _)) = select(&mut deadline, work).await {
                     return mutations;
                     return mutations;
                 }
                 }
             }
             }
         }
         }
     }
     }
-
-    // fn mark_dirty_scope(&mut self, scope_id: ScopeId) {
-    //     let scopes = &self.scopes;
-    //     if let Some(scope) = scopes.get_scope(scope_id) {
-    //         let height = scope.height;
-    //         let id = scope_id.0;
-    //         if let Err(index) = self.dirty_scopes.binary_search_by(|new| {
-    //             let scope = scopes.get_scope(*new).unwrap();
-    //             let new_height = scope.height;
-    //             let new_id = &scope.scope_id();
-    //             height.cmp(&new_height).then(new_id.0.cmp(&id))
-    //         }) {
-    //             self.dirty_scopes.insert(index, scope_id);
-    //             log::info!("mark_dirty_scope: {:?}", self.dirty_scopes);
-    //         }
-    //     }
-    // }
 }
 }
 
 
 impl Drop for VirtualDom {
 impl Drop for VirtualDom {

+ 2 - 68
packages/desktop/src/controller.rs

@@ -1,12 +1,8 @@
 use crate::desktop_context::{DesktopContext, UserWindowEvent};
 use crate::desktop_context::{DesktopContext, UserWindowEvent};
+use crate::events::{decode_event, EventMessage};
 use dioxus_core::*;
 use dioxus_core::*;
-use dioxus_html::events::*;
 use futures_channel::mpsc::UnboundedReceiver;
 use futures_channel::mpsc::UnboundedReceiver;
 use futures_util::StreamExt;
 use futures_util::StreamExt;
-use serde::Deserialize;
-use serde_json::from_value;
-use std::any::Any;
-use std::rc::Rc;
 use std::{
 use std::{
     collections::HashMap,
     collections::HashMap,
     sync::Arc,
     sync::Arc,
@@ -19,25 +15,6 @@ use wry::{
     webview::WebView,
     webview::WebView,
 };
 };
 
 
-macro_rules! match_data {
-    (
-        $m:ident;
-        $name:ident;
-        $(
-            $tip:ty => $($mname:literal)|* ;
-        )*
-    ) => {
-        match $name {
-            $( $($mname)|* => {
-                println!("casting to type {:?}", std::any::TypeId::of::<$tip>());
-                let val: $tip = from_value::<$tip>($m).ok()?;
-                Rc::new(val) as Rc<dyn Any>
-            })*
-            _ => return None,
-        }
-    };
-}
-
 pub(super) struct DesktopController {
 pub(super) struct DesktopController {
     pub(super) webviews: HashMap<WindowId, WebView>,
     pub(super) webviews: HashMap<WindowId, WebView>,
     pub(super) pending_edits: Arc<Mutex<Vec<String>>>,
     pub(super) pending_edits: Arc<Mutex<Vec<String>>>,
@@ -86,9 +63,7 @@ impl DesktopController {
                             if let Ok(value) = serde_json::from_value::<EventMessage>(json_value) {
                             if let Ok(value) = serde_json::from_value::<EventMessage>(json_value) {
                                 let name = value.event.clone();
                                 let name = value.event.clone();
                                 let el_id = ElementId(value.mounted_dom_id);
                                 let el_id = ElementId(value.mounted_dom_id);
-                                let evt = decode_event(value);
-
-                                if let Some(evt) = evt {
+                                if let Some(evt) = decode_event(value) {
                                     dom.handle_event(&name,  evt, el_id, true, EventPriority::Medium);
                                     dom.handle_event(&name,  evt, el_id, true, EventPriority::Medium);
                                 }
                                 }
                             }
                             }
@@ -143,44 +118,3 @@ impl DesktopController {
         }
         }
     }
     }
 }
 }
-
-#[derive(Deserialize)]
-struct EventMessage {
-    contents: serde_json::Value,
-    event: String,
-    mounted_dom_id: usize,
-}
-
-fn decode_event(value: EventMessage) -> Option<Rc<dyn Any>> {
-    let val = value.contents;
-    let name = value.event.as_str();
-    let el_id = ElementId(value.mounted_dom_id);
-    type DragData = MouseData;
-
-    let evt = match_data! { val; name;
-        MouseData => "click" | "contextmenu" | "dblclick" | "doubleclick" | "mousedown" | "mouseenter" | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup";
-        ClipboardData => "copy" | "cut" | "paste";
-        CompositionData => "compositionend" | "compositionstart" | "compositionupdate";
-        KeyboardData => "keydown" | "keypress" | "keyup";
-        FocusData => "blur" | "focus" | "focusin" | "focusout";
-        FormData => "change" | "input" | "invalid" | "reset" | "submit";
-        DragData => "drag" | "dragend" | "dragenter" | "dragexit" | "dragleave" | "dragover" | "dragstart" | "drop";
-        PointerData => "pointerlockchange" | "pointerlockerror" | "pointerdown" | "pointermove" | "pointerup" | "pointerover" | "pointerout" | "pointerenter" | "pointerleave" | "gotpointercapture" | "lostpointercapture";
-        SelectionData => "selectstart" | "selectionchange" | "select";
-        TouchData => "touchcancel" | "touchend" | "touchmove" | "touchstart";
-        ScrollData => "scroll";
-        WheelData => "wheel";
-        MediaData => "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied"
-            | "encrypted" | "ended" | "interruptbegin" | "interruptend" | "loadeddata"
-            | "loadedmetadata" | "loadstart" | "pause" | "play" | "playing" | "progress"
-            | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend" | "timeupdate"
-            | "volumechange" | "waiting" | "error" | "load" | "loadend" | "timeout";
-        AnimationData => "animationstart" | "animationend" | "animationiteration";
-        TransitionData => "transitionend";
-        ToggleData => "toggle";
-        // ImageData => "load" | "error";
-        // OtherData => "abort" | "afterprint" | "beforeprint" | "beforeunload" | "hashchange" | "languagechange" | "message" | "offline" | "online" | "pagehide" | "pageshow" | "popstate" | "rejectionhandled" | "storage" | "unhandledrejection" | "unload" | "userproximity" | "vrdisplayactivate" | "vrdisplayblur" | "vrdisplayconnect" | "vrdisplaydeactivate" | "vrdisplaydisconnect" | "vrdisplayfocus" | "vrdisplaypointerrestricted" | "vrdisplaypointerunrestricted" | "vrdisplaypresentchange";
-    };
-
-    Some(evt)
-}

+ 55 - 11
packages/desktop/src/events.rs

@@ -33,17 +33,61 @@ pub(crate) fn parse_ipc_message(payload: &str) -> Option<IpcMessage> {
     }
     }
 }
 }
 
 
-#[derive(Deserialize, Serialize)]
-struct ImEvent {
-    event: String,
-    mounted_dom_id: ElementId,
-    contents: serde_json::Value,
+macro_rules! match_data {
+    (
+        $m:ident;
+        $name:ident;
+        $(
+            $tip:ty => $($mname:literal)|* ;
+        )*
+    ) => {
+        match $name {
+            $( $($mname)|* => {
+                println!("casting to type {:?}", std::any::TypeId::of::<$tip>());
+                let val: $tip = from_value::<$tip>($m).ok()?;
+                Rc::new(val) as Rc<dyn Any>
+            })*
+            _ => return None,
+        }
+    };
+}
+
+#[derive(Deserialize)]
+pub struct EventMessage {
+    pub contents: serde_json::Value,
+    pub event: String,
+    pub mounted_dom_id: usize,
 }
 }
 
 
-// pub fn make_synthetic_event(name: &str, val: serde_json::Value) -> Option<Rc<dyn Any>> {
-//     // right now we don't support the datatransfer in Drag
-//     type DragData = MouseData;
-//     type ProgressData = MediaData;
+pub fn decode_event(value: EventMessage) -> Option<Rc<dyn Any>> {
+    let val = value.contents;
+    let name = value.event.as_str();
+    type DragData = MouseData;
+
+    let evt = match_data! { val; name;
+        MouseData => "click" | "contextmenu" | "dblclick" | "doubleclick" | "mousedown" | "mouseenter" | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup";
+        ClipboardData => "copy" | "cut" | "paste";
+        CompositionData => "compositionend" | "compositionstart" | "compositionupdate";
+        KeyboardData => "keydown" | "keypress" | "keyup";
+        FocusData => "blur" | "focus" | "focusin" | "focusout";
+        FormData => "change" | "input" | "invalid" | "reset" | "submit";
+        DragData => "drag" | "dragend" | "dragenter" | "dragexit" | "dragleave" | "dragover" | "dragstart" | "drop";
+        PointerData => "pointerlockchange" | "pointerlockerror" | "pointerdown" | "pointermove" | "pointerup" | "pointerover" | "pointerout" | "pointerenter" | "pointerleave" | "gotpointercapture" | "lostpointercapture";
+        SelectionData => "selectstart" | "selectionchange" | "select";
+        TouchData => "touchcancel" | "touchend" | "touchmove" | "touchstart";
+        ScrollData => "scroll";
+        WheelData => "wheel";
+        MediaData => "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied"
+            | "encrypted" | "ended" | "interruptbegin" | "interruptend" | "loadeddata"
+            | "loadedmetadata" | "loadstart" | "pause" | "play" | "playing" | "progress"
+            | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend" | "timeupdate"
+            | "volumechange" | "waiting" | "error" | "load" | "loadend" | "timeout";
+        AnimationData => "animationstart" | "animationend" | "animationiteration";
+        TransitionData => "transitionend";
+        ToggleData => "toggle";
+        // ImageData => "load" | "error";
+        // OtherData => "abort" | "afterprint" | "beforeprint" | "beforeunload" | "hashchange" | "languagechange" | "message" | "offline" | "online" | "pagehide" | "pageshow" | "popstate" | "rejectionhandled" | "storage" | "unhandledrejection" | "unload" | "userproximity" | "vrdisplayactivate" | "vrdisplayblur" | "vrdisplayconnect" | "vrdisplaydeactivate" | "vrdisplaydisconnect" | "vrdisplayfocus" | "vrdisplaypointerrestricted" | "vrdisplaypointerunrestricted" | "vrdisplaypresentchange";
+    };
 
 
-//     Some(res)
-// }
+    Some(evt)
+}

+ 1 - 1
packages/interpreter/src/interpreter.js

@@ -311,7 +311,7 @@ export class Interpreter {
         this.CreateElementNs(edit.name, edit.id, edit.ns);
         this.CreateElementNs(edit.name, edit.id, edit.ns);
         break;
         break;
       case "SetText":
       case "SetText":
-        this.SetText(edit.id, edit.text);
+        this.SetText(edit.id, edit.value);
         break;
         break;
       case "SetAttribute":
       case "SetAttribute":
         this.SetAttribute(edit.id, edit.name, edit.value, edit.ns);
         this.SetAttribute(edit.id, edit.name, edit.value, edit.ns);