Jelajahi Sumber

make signals non-breaking

Evan Almloff 1 tahun lalu
induk
melakukan
ddbe913e79

+ 7 - 1
packages/core/src/arena.rs

@@ -19,6 +19,9 @@ pub(crate) struct ElementRef {
 
     // The actual template
     pub template: Option<NonNull<VNode<'static>>>,
+
+    // 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();

+ 8 - 3
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<T: std::fmt::Debug> std::fmt::Debug for Event<T> {
 /// }
 ///
 /// ```
-pub struct EventHandler<'bump, T:?Sized = ()> {
+pub struct EventHandler<'bump, T: ?Sized = ()> {
     pub(crate) origin: ScopeId,
     pub(super) callback: RefCell<Option<ExternalListenerCallback<'bump, T>>>,
 }
@@ -158,7 +157,13 @@ impl<T> 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();
+            });
         }
     }
 

+ 4 - 2
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<dyn Any>>),
+    Listener(RefCell<Option<ListenerCb<'a>>>),
 
     /// An arbitrary value that implements PartialEq and is static
     Any(RefCell<Option<BumpBox<'a, dyn AnyValue>>>),
@@ -485,6 +485,8 @@ pub enum AttributeValue<'a> {
     None,
 }
 
+pub type ListenerCb<'a> = BumpBox<'a, dyn FnMut(Event<dyn Any>) + '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

+ 20 - 11
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<T>) + 'src,
     ) -> AttributeValue<'src> {
-       
-
-        AttributeValue::Listener(self.event_handler(move |event: Event<dyn Any>| {
-            if let Ok(data) = event.data.downcast::<T>() {
-                callback(Event {
-                    propagates: event.propagates,
-                    data,
-                });
-            }
-        }))
+        // 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<dyn Any>| {
+                if let Ok(data) = event.data.downcast::<T>() {
+                    callback(Event {
+                        propagates: event.propagates,
+                        data,
+                    });
+                }
+            }))
+        };
+
+        AttributeValue::Listener(RefCell::new(Some(boxed)))
     }
 
     /// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]

+ 6 - 6
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();