diff --git a/packages/core/src/scopes.rs b/packages/core/src/scopes.rs index b15d5af81..2409f9c07 100644 --- a/packages/core/src/scopes.rs +++ b/packages/core/src/scopes.rs @@ -44,10 +44,6 @@ impl Drop for ScopeState { } impl ScopeState { - pub(crate) fn context(&self) -> Ref<'_, ScopeContext> { - self.runtime.get_context(self.context_id).unwrap() - } - /// Get a handle to the currently active head node arena for this Scope /// /// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR. @@ -66,4 +62,8 @@ impl ScopeState { pub fn try_root_node(&self) -> Option<&RenderReturn> { self.last_rendered_node.as_ref() } + + pub(crate) fn context(&self) -> Ref<'_, ScopeContext> { + self.runtime.get_context(self.context_id).unwrap() + } } diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index d96ce7b43..b9e555a9a 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -181,7 +181,6 @@ use std::{any::Any, collections::BTreeSet, future::Future, rc::Rc}; /// } /// ``` pub struct VirtualDom { - // TODO: I don't think we need the box here anymore? pub(crate) scopes: Slab, pub(crate) dirty_scopes: BTreeSet, @@ -203,7 +202,7 @@ pub struct VirtualDom { // Currently suspended scopes pub(crate) suspended_scopes: FxHashSet, - pub(crate) rx: futures_channel::mpsc::UnboundedReceiver, + rx: futures_channel::mpsc::UnboundedReceiver, } impl VirtualDom { @@ -376,106 +375,6 @@ impl VirtualDom { } } - /* - ------------------------ - The algorithm works by walking through the list of dynamic attributes, checking their paths, and breaking when - we find the target path. - - With the target path, we try and move up to the parent until there is no parent. - Due to how bubbling works, we call the listeners before walking to the parent. - - If we wanted to do capturing, then we would accumulate all the listeners and call them in reverse order. - ---------------------- - - For a visual demonstration, here we present a tree on the left and whether or not a listener is collected on the - right. - - | <-- yes (is ascendant) - | | | <-- no (is not direct ascendant) - | | <-- yes (is ascendant) - | | | | | <--- target element, break early, don't check other listeners - | | | <-- no, broke early - | <-- no, broke early - */ - fn handle_bubbling_event( - &mut self, - mut parent: Option, - name: &str, - uievent: Event, - ) { - // If the event bubbles, we traverse through the tree until we find the target element. - // Loop through each dynamic attribute (in a depth first order) in this template before moving up to the template's parent. - while let Some(path) = parent { - let mut listeners = vec![]; - - let el_ref = &self.mounts[path.mount.0].node; - let node_template = el_ref.template.get(); - let target_path = path.path; - - // Accumulate listeners into the listener list bottom to top - for (idx, attrs) in el_ref.dynamic_attrs.iter().enumerate() { - let this_path = node_template.attr_paths[idx]; - - for attr in attrs.iter() { - // Remove the "on" prefix if it exists, TODO, we should remove this and settle on one - if attr.name.trim_start_matches("on") == name - && target_path.is_decendant(&this_path) - { - listeners.push(&attr.value); - - // Break if this is the exact target element. - // This means we won't call two listeners with the same name on the same element. This should be - // documented, or be rejected from the rsx! macro outright - if target_path == this_path { - break; - } - } - } - } - - // 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.into_iter().rev() { - if let AttributeValue::Listener(listener) = listener { - self.runtime.rendering.set(false); - listener.call(uievent.clone()); - self.runtime.rendering.set(true); - - if !uievent.propagates.get() { - return; - } - } - } - - let mount = el_ref.mount.get().as_usize(); - parent = mount.and_then(|id| self.mounts.get(id).and_then(|el| el.parent)); - } - } - - /// Call an event listener in the simplest way possible without bubbling upwards - fn handle_non_bubbling_event(&mut self, node: ElementRef, name: &str, uievent: Event) { - let el_ref = &self.mounts[node.mount.0].node; - let node_template = el_ref.template.get(); - let target_path = node.path; - - for (idx, attr) in el_ref.dynamic_attrs.iter().enumerate() { - let this_path = node_template.attr_paths[idx]; - - for attr in attr.iter() { - // Remove the "on" prefix if it exists, TODO, we should remove this and settle on one - // Only call the listener if this is the exact target element. - if attr.name.trim_start_matches("on") == name && target_path == this_path { - if let AttributeValue::Listener(listener) = &attr.value { - self.runtime.rendering.set(false); - listener.call(uievent.clone()); - self.runtime.rendering.set(true); - break; - } - } - } - } - } - /// Wait for the scheduler to have any work. /// /// This method polls the internal future queue, waiting for suspense nodes, tasks, or other work. This completes when @@ -531,15 +430,6 @@ impl VirtualDom { } } - /// Handle notifications by tasks inside the scheduler - /// - /// This is precise, meaning we won't poll every task, just tasks that have woken up as notified to use by the - /// queue - fn handle_task_wakeup(&mut self, id: Task) { - let _runtime = RuntimeGuard::new(self.runtime.clone()); - self.runtime.handle_task_wakeup(id); - } - /// Replace a template at runtime. This will re-render all components that use this template. /// This is the primitive that enables hot-reloading. /// @@ -712,11 +602,120 @@ impl VirtualDom { } /// Flush any queued template changes - pub fn flush_templates(&mut self, to: &mut impl WriteMutations) { + fn flush_templates(&mut self, to: &mut impl WriteMutations) { for template in self.queued_templates.drain(..) { to.register_template(template); } } + + /// Handle notifications by tasks inside the scheduler + /// + /// This is precise, meaning we won't poll every task, just tasks that have woken up as notified to use by the + /// queue + fn handle_task_wakeup(&mut self, id: Task) { + let _runtime = RuntimeGuard::new(self.runtime.clone()); + self.runtime.handle_task_wakeup(id); + } + + /* + ------------------------ + The algorithm works by walking through the list of dynamic attributes, checking their paths, and breaking when + we find the target path. + + With the target path, we try and move up to the parent until there is no parent. + Due to how bubbling works, we call the listeners before walking to the parent. + + If we wanted to do capturing, then we would accumulate all the listeners and call them in reverse order. + ---------------------- + + For a visual demonstration, here we present a tree on the left and whether or not a listener is collected on the + right. + + | <-- yes (is ascendant) + | | | <-- no (is not direct ascendant) + | | <-- yes (is ascendant) + | | | | | <--- target element, break early, don't check other listeners + | | | <-- no, broke early + | <-- no, broke early + */ + fn handle_bubbling_event( + &mut self, + mut parent: Option, + name: &str, + uievent: Event, + ) { + // If the event bubbles, we traverse through the tree until we find the target element. + // Loop through each dynamic attribute (in a depth first order) in this template before moving up to the template's parent. + while let Some(path) = parent { + let mut listeners = vec![]; + + let el_ref = &self.mounts[path.mount.0].node; + let node_template = el_ref.template.get(); + let target_path = path.path; + + // Accumulate listeners into the listener list bottom to top + for (idx, attrs) in el_ref.dynamic_attrs.iter().enumerate() { + let this_path = node_template.attr_paths[idx]; + + for attr in attrs.iter() { + // Remove the "on" prefix if it exists, TODO, we should remove this and settle on one + if attr.name.trim_start_matches("on") == name + && target_path.is_decendant(&this_path) + { + listeners.push(&attr.value); + + // Break if this is the exact target element. + // This means we won't call two listeners with the same name on the same element. This should be + // documented, or be rejected from the rsx! macro outright + if target_path == this_path { + break; + } + } + } + } + + // 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.into_iter().rev() { + if let AttributeValue::Listener(listener) = listener { + self.runtime.rendering.set(false); + listener.call(uievent.clone()); + self.runtime.rendering.set(true); + + if !uievent.propagates.get() { + return; + } + } + } + + let mount = el_ref.mount.get().as_usize(); + parent = mount.and_then(|id| self.mounts.get(id).and_then(|el| el.parent)); + } + } + + /// Call an event listener in the simplest way possible without bubbling upwards + fn handle_non_bubbling_event(&mut self, node: ElementRef, name: &str, uievent: Event) { + let el_ref = &self.mounts[node.mount.0].node; + let node_template = el_ref.template.get(); + let target_path = node.path; + + for (idx, attr) in el_ref.dynamic_attrs.iter().enumerate() { + let this_path = node_template.attr_paths[idx]; + + for attr in attr.iter() { + // Remove the "on" prefix if it exists, TODO, we should remove this and settle on one + // Only call the listener if this is the exact target element. + if attr.name.trim_start_matches("on") == name && target_path == this_path { + if let AttributeValue::Listener(listener) = &attr.value { + self.runtime.rendering.set(false); + listener.call(uievent.clone()); + self.runtime.rendering.set(true); + break; + } + } + } + } + } } impl Drop for VirtualDom {