diff --git a/packages/core/src/arena.rs b/packages/core/src/arena.rs index b7ec46623..c8f7ce3c9 100644 --- a/packages/core/src/arena.rs +++ b/packages/core/src/arena.rs @@ -19,6 +19,9 @@ pub(crate) struct ElementRef { // The actual template pub template: Option>>, + + // The scope the element belongs to + pub scope: ScopeId, } #[derive(Clone, Copy, Debug)] @@ -32,6 +35,7 @@ impl ElementRef { Self { template: None, path: ElementPath::Root(0), + scope: ScopeId(0), } } } @@ -56,11 +60,13 @@ impl VirtualDom { fn next_reference(&mut self, template: &VNode, path: ElementPath) -> ElementId { let entry = self.elements.vacant_entry(); let id = entry.key(); + let scope = self.runtime.current_scope_id().unwrap_or(ScopeId(0)); entry.insert(ElementRef { // We know this is non-null because it comes from a reference template: Some(unsafe { NonNull::new_unchecked(template as *const _ as *mut _) }), path, + scope, }); ElementId(id) } @@ -163,7 +169,7 @@ impl VirtualDom { let listener = unsafe { &*listener }; match &listener.value { AttributeValue::Listener(l) => { - _ = l.callback.take(); + _ = l.take(); } AttributeValue::Any(a) => { _ = a.take(); diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs index c734ebd4a..90511a8e6 100644 --- a/packages/core/src/events.rs +++ b/packages/core/src/events.rs @@ -1,9 +1,8 @@ +use crate::{runtime::with_runtime, ScopeId}; use std::{ cell::{Cell, RefCell}, rc::Rc, - }; -use crate::ScopeId; /// A wrapper around some generic data that handles the event's state /// @@ -136,7 +135,7 @@ impl std::fmt::Debug for Event { /// } /// /// ``` -pub struct EventHandler<'bump, T:?Sized = ()> { +pub struct EventHandler<'bump, T: ?Sized = ()> { pub(crate) origin: ScopeId, pub(super) callback: RefCell>>, } @@ -158,7 +157,13 @@ impl EventHandler<'_, T> { /// This borrows the event using a RefCell. Recursively calling a listener will cause a panic. pub fn call(&self, event: T) { if let Some(callback) = self.callback.borrow_mut().as_mut() { + with_runtime(|rt| { + rt.scope_stack.borrow_mut().push(self.origin); + }); callback(event); + with_runtime(|rt| { + rt.scope_stack.borrow_mut().pop(); + }); } } diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 5406e8f07..a108f3577 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -1,4 +1,4 @@ -use crate::prelude::EventHandler;use crate::{ +use crate::{ any_props::AnyProps, arena::ElementId, Element, Event, LazyNodes, ScopeId, ScopeState, }; use bumpalo::boxed::Box as BumpBox; @@ -476,7 +476,7 @@ pub enum AttributeValue<'a> { Bool(bool), /// A listener, like "onclick" - Listener(EventHandler<'a, Event>), + Listener(RefCell>>), /// An arbitrary value that implements PartialEq and is static Any(RefCell>>), @@ -485,6 +485,8 @@ pub enum AttributeValue<'a> { None, } +pub type ListenerCb<'a> = BumpBox<'a, dyn FnMut(Event) + 'a>; + /// Any of the built-in values that the Dioxus VirtualDom supports as dynamic attributes on elements that are borrowed /// /// These varients are used to communicate what the value of an attribute is that needs to be updated diff --git a/packages/core/src/scopes.rs b/packages/core/src/scopes.rs index b43b2143a..f255c4175 100644 --- a/packages/core/src/scopes.rs +++ b/packages/core/src/scopes.rs @@ -446,7 +446,10 @@ impl<'src> ScopeState { let handler: &mut dyn FnMut(T) = self.bump().alloc(f); let caller = unsafe { BumpBox::from_raw(handler as *mut dyn FnMut(T)) }; let callback = RefCell::new(Some(caller)); - EventHandler { callback, origin: self.context().id } + EventHandler { + callback, + origin: self.context().id, + } } /// Create a new [`AttributeValue`] with the listener variant from a callback @@ -456,16 +459,22 @@ impl<'src> ScopeState { &'src self, mut callback: impl FnMut(Event) + 'src, ) -> AttributeValue<'src> { - + // safety: there's no other way to create a dynamicly-dispatched bump box other than alloc + from-raw + // This is the suggested way to build a bumpbox + // + // In theory, we could just use regular boxes + let boxed: BumpBox<'src, dyn FnMut(_) + 'src> = unsafe { + BumpBox::from_raw(self.bump().alloc(move |event: Event| { + if let Ok(data) = event.data.downcast::() { + callback(Event { + propagates: event.propagates, + data, + }); + } + })) + }; - AttributeValue::Listener(self.event_handler(move |event: Event| { - if let Ok(data) = event.data.downcast::() { - callback(Event { - propagates: event.propagates, - data, - }); - } - })) + AttributeValue::Listener(RefCell::new(Some(boxed))) } /// Create a new [`AttributeValue`] with a value that implements [`AnyValue`] diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index c43af2254..f20b80412 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -391,12 +391,12 @@ impl VirtualDom { // We check the bubble state between each call to see if the event has been stopped from bubbling for listener in listeners.drain(..).rev() { if let AttributeValue::Listener(listener) = listener { - let origin = listener.origin; - self.runtime.scope_stack.borrow_mut().push(origin); - if let Some(cb) = listener.callback.borrow_mut().as_deref_mut() { + let origin = el_ref.scope; + self.runtime.scope_stack.borrow_mut().push(origin); + if let Some(cb) = listener.borrow_mut().as_deref_mut() { cb(uievent.clone()); } - self.runtime.scope_stack.borrow_mut().pop(); + self.runtime.scope_stack.borrow_mut().pop(); if !uievent.propagates.get() { return; @@ -425,9 +425,9 @@ impl VirtualDom { // 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 { - let origin = listener.origin; + let origin = el_ref.scope; self.runtime.scope_stack.borrow_mut().push(origin); - if let Some(cb) = listener.callback.borrow_mut().as_deref_mut() { + if let Some(cb) = listener.borrow_mut().as_deref_mut() { cb(uievent.clone()); } self.runtime.scope_stack.borrow_mut().pop();