Little bit more cleanup in core

This commit is contained in:
Jonathan Kelley 2024-01-16 18:07:29 -08:00
parent 66ba220265
commit c3d810bcd0
No known key found for this signature in database
GPG key ID: 1FBB50F7EB0A08BE
2 changed files with 115 additions and 116 deletions

View file

@ -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()
}
}

View file

@ -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<ScopeState>,
pub(crate) dirty_scopes: BTreeSet<DirtyScope>,
@ -203,7 +202,7 @@ pub struct VirtualDom {
// Currently suspended scopes
pub(crate) suspended_scopes: FxHashSet<ScopeId>,
pub(crate) rx: futures_channel::mpsc::UnboundedReceiver<SchedulerMsg>,
rx: futures_channel::mpsc::UnboundedReceiver<SchedulerMsg>,
}
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<ElementRef>,
name: &str,
uievent: Event<dyn Any>,
) {
// 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<dyn Any>) {
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<ElementRef>,
name: &str,
uievent: Event<dyn Any>,
) {
// 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<dyn Any>) {
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 {