浏览代码

move creation functions into the structs they are creating and unify user and system events

Evan Almloff 1 年之前
父节点
当前提交
d90c71c508

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

@@ -21,9 +21,6 @@ pub struct ElementRef {
     // the pathway of the real element inside the template
     // the pathway of the real element inside the template
     pub(crate) path: ElementPath,
     pub(crate) path: ElementPath,
 
 
-    // the scope that this element belongs to
-    pub(crate) scope: ScopeId,
-
     // The actual element
     // The actual element
     pub(crate) element: VNode,
     pub(crate) element: VNode,
 }
 }
@@ -59,7 +56,7 @@ impl VirtualDom {
     // Note: This will not remove any ids from the arena
     // Note: This will not remove any ids from the arena
     pub(crate) fn drop_scope(&mut self, id: ScopeId) {
     pub(crate) fn drop_scope(&mut self, id: ScopeId) {
         self.dirty_scopes.remove(&DirtyScope {
         self.dirty_scopes.remove(&DirtyScope {
-            height: self.scopes[id.0].height(),
+            height: self.scopes[id.0].context().height,
             id,
             id,
         });
         });
 
 

+ 0 - 4
packages/core/src/create.rs

@@ -199,7 +199,6 @@ impl VirtualDom {
                         path: template.template.get().node_paths[idx],
                         path: template.template.get().node_paths[idx],
                     },
                     },
                     element: template.clone(),
                     element: template.clone(),
-                    scope: self.runtime.current_scope_id().unwrap_or(ScopeId(0)),
                 };
                 };
                 self.create_dynamic_node(&template_ref, node, to)
                 self.create_dynamic_node(&template_ref, node, to)
             }
             }
@@ -209,7 +208,6 @@ impl VirtualDom {
                         path: template.template.get().node_paths[idx],
                         path: template.template.get().node_paths[idx],
                     },
                     },
                     element: template.clone(),
                     element: template.clone(),
-                    scope: self.runtime.current_scope_id().unwrap_or(ScopeId(0)),
                 };
                 };
                 *parent.borrow_mut() = Some(template_ref);
                 *parent.borrow_mut() = Some(template_ref);
                 let id = self.set_slot(id);
                 let id = self.set_slot(id);
@@ -297,7 +295,6 @@ impl VirtualDom {
                     path: template.template.get().node_paths[idx],
                     path: template.template.get().node_paths[idx],
                 },
                 },
                 element: template.clone(),
                 element: template.clone(),
-                scope: self.runtime.current_scope_id().unwrap_or(ScopeId(0)),
             };
             };
             let m = self.create_dynamic_node(&boundary_ref, &template.dynamic_nodes[idx], to);
             let m = self.create_dynamic_node(&boundary_ref, &template.dynamic_nodes[idx], to);
             if m > 0 {
             if m > 0 {
@@ -353,7 +350,6 @@ impl VirtualDom {
                 let element_ref = ElementRef {
                 let element_ref = ElementRef {
                     path: ElementPath { path },
                     path: ElementPath { path },
                     element: template.clone(),
                     element: template.clone(),
-                    scope: self.runtime.current_scope_id().unwrap_or(ScopeId(0)),
                 };
                 };
                 self.elements[id.0] = Some(element_ref);
                 self.elements[id.0] = Some(element_ref);
                 to.create_event_listener(&unbounded_name[2..], id);
                 to.create_event_listener(&unbounded_name[2..], id);

+ 0 - 1
packages/core/src/diff.rs

@@ -142,7 +142,6 @@ impl VirtualDom {
                     path: ElementPath {
                     path: ElementPath {
                         path: left_template.template.get().node_paths[dyn_node_idx],
                         path: left_template.template.get().node_paths[dyn_node_idx],
                     },
                     },
-                    scope: self.runtime.scope_stack.borrow().last().copied().unwrap(),
                 };
                 };
                 self.diff_dynamic_node(left_node, right_node, &current_ref, to);
                 self.diff_dynamic_node(left_node, right_node, &current_ref, to);
             });
             });

+ 13 - 2
packages/core/src/events.rs

@@ -1,4 +1,4 @@
-use crate::{runtime::with_runtime, ScopeId};
+use crate::{runtime::with_runtime, scope_context::current_scope_id, ScopeId};
 use std::{
 use std::{
     cell::{Cell, RefCell},
     cell::{Cell, RefCell},
     rc::Rc,
     rc::Rc,
@@ -149,7 +149,18 @@ impl<T> Default for EventHandler<T> {
 
 
 type ExternalListenerCallback<T> = Box<dyn FnMut(T)>;
 type ExternalListenerCallback<T> = Box<dyn FnMut(T)>;
 
 
-impl<T> EventHandler< T> {
+impl<T> EventHandler<T> {
+    /// Create a new [`EventHandler`] from an [`FnMut`]
+    pub fn new(mut f: impl FnMut(T) + 'static) -> EventHandler<T> {
+        let callback = RefCell::new(Some(Box::new(move |event: T| {
+            f(event);
+        }) as Box<dyn FnMut(T)>));
+        EventHandler {
+            callback,
+            origin: current_scope_id().expect("to be in a dioxus runtime"),
+        }
+    }
+
     /// Call this event handler with the appropriate event type
     /// Call this event handler with the appropriate event type
     ///
     ///
     /// This borrows the event using a RefCell. Recursively calling a listener will cause a panic.
     /// This borrows the event using a RefCell. Recursively calling a listener will cause a panic.

+ 79 - 11
packages/core/src/nodes.rs

@@ -1,5 +1,6 @@
-use crate::any_props::BoxedAnyProps;
-use crate::innerlude::ElementRef;
+use crate::any_props::{BoxedAnyProps, VProps};
+use crate::innerlude::{ElementRef, EventHandler};
+use crate::Properties;
 use crate::{arena::ElementId, Element, Event, ScopeId};
 use crate::{arena::ElementId, Element, Event, ScopeId};
 use std::ops::Deref;
 use std::ops::Deref;
 use std::rc::Rc;
 use std::rc::Rc;
@@ -336,6 +337,13 @@ pub enum DynamicNode {
     Fragment(Vec<VNode>),
     Fragment(Vec<VNode>),
 }
 }
 
 
+impl DynamicNode {
+    /// Convert any item that implements [`IntoDynNode`] into a [`DynamicNode`]
+    pub fn make_node<'c, I>(into: impl IntoDynNode<I> + 'c) -> DynamicNode {
+        into.into_dyn_node()
+    }
+}
+
 impl Default for DynamicNode {
 impl Default for DynamicNode {
     fn default() -> Self {
     fn default() -> Self {
         Self::Placeholder(Default::default())
         Self::Placeholder(Default::default())
@@ -360,6 +368,35 @@ pub struct VComponent {
 }
 }
 
 
 impl VComponent {
 impl VComponent {
+    /// Create a new [`VComponent`] variant
+    ///
+    ///
+    /// The given component can be any of four signatures. Remember that an [`Element`] is really a [`Result<VNode>`].
+    ///
+    /// ```rust, ignore
+    /// // Without explicit props
+    /// fn(Scope) -> Element;
+    /// async fn(Scope<'_>) -> Element;
+    ///
+    /// // With explicit props
+    /// fn(Scope<Props>) -> Element;
+    /// async fn(Scope<Props<'_>>) -> Element;
+    /// ```
+    pub fn new<P>(&self, component: fn(P) -> Element, props: P, fn_name: &'static str) -> Self
+    where
+        // The properties must be valid until the next bump frame
+        P: Properties,
+    {
+        let vcomp = VProps::new(component, P::memoize, props, fn_name);
+
+        VComponent {
+            name: fn_name,
+            render_fn: component as *const (),
+            props: BoxedAnyProps::new(vcomp),
+            scope: Default::default(),
+        }
+    }
+
     /// Get the scope that this component is mounted to
     /// Get the scope that this component is mounted to
     pub fn mounted_scope(&self) -> Option<ScopeId> {
     pub fn mounted_scope(&self) -> Option<ScopeId> {
         self.scope.get()
         self.scope.get()
@@ -400,6 +437,12 @@ impl VText {
     }
     }
 }
 }
 
 
+impl From<Arguments<'_>> for VText {
+    fn from(args: Arguments) -> Self {
+        Self::new(args.to_string())
+    }
+}
+
 /// A placeholder node, used by suspense and fragments
 /// A placeholder node, used by suspense and fragments
 #[derive(Clone, Debug, Default)]
 #[derive(Clone, Debug, Default)]
 pub struct VPlaceholder {
 pub struct VPlaceholder {
@@ -471,19 +514,23 @@ pub struct Attribute {
 }
 }
 
 
 impl Attribute {
 impl Attribute {
-    /// Create a new attribute
-    pub fn new(
+    /// Create a new [`Attribute`] from a name, value, namespace, and volatile bool
+    ///
+    /// "Volatile" referes to whether or not Dioxus should always override the value. This helps prevent the UI in
+    /// some renderers stay in sync with the VirtualDom's understanding of the world
+    pub fn attr(
+        &self,
         name: &'static str,
         name: &'static str,
-        value: AttributeValue,
+        value: impl IntoAttributeValue,
         namespace: Option<&'static str>,
         namespace: Option<&'static str>,
         volatile: bool,
         volatile: bool,
-    ) -> Self {
-        Self {
+    ) -> Attribute {
+        Attribute {
             name,
             name,
-            value,
             namespace,
             namespace,
             volatile,
             volatile,
-            mounted_element: Cell::new(ElementId::default()),
+            mounted_element: Default::default(),
+            value: value.into_value(),
         }
         }
     }
     }
 
 
@@ -511,7 +558,7 @@ pub enum AttributeValue {
     Bool(bool),
     Bool(bool),
 
 
     /// A listener, like "onclick"
     /// A listener, like "onclick"
-    Listener(RefCell<ListenerCb>),
+    Listener(ListenerCb),
 
 
     /// An arbitrary value that implements PartialEq and is static
     /// An arbitrary value that implements PartialEq and is static
     Any(Box<dyn AnyValue>),
     Any(Box<dyn AnyValue>),
@@ -520,7 +567,28 @@ pub enum AttributeValue {
     None,
     None,
 }
 }
 
 
-pub type ListenerCb = Box<dyn FnMut(Event<dyn Any>)>;
+impl AttributeValue {
+    /// Create a new [`AttributeValue`] with the listener variant from a callback
+    ///
+    /// The callback must be confined to the lifetime of the ScopeState
+    pub fn listener<T: 'static>(mut callback: impl FnMut(Event<T>) + 'static) -> AttributeValue {
+        AttributeValue::Listener(EventHandler::new(move |event: Event<dyn Any>| {
+            if let Ok(data) = event.data.downcast::<T>() {
+                callback(Event {
+                    propagates: event.propagates,
+                    data,
+                });
+            }
+        }))
+    }
+
+    /// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
+    pub fn any_value<T: AnyValue>(&self, value: T) -> AttributeValue {
+        AttributeValue::Any(Box::new(value))
+    }
+}
+
+pub type ListenerCb = EventHandler<Event<dyn Any>>;
 
 
 impl std::fmt::Debug for AttributeValue {
 impl std::fmt::Debug for AttributeValue {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {

+ 5 - 0
packages/core/src/scope_context.rs

@@ -301,6 +301,11 @@ impl ScopeContext {
     pub fn generation(&self) -> usize {
     pub fn generation(&self) -> usize {
         self.render_count.get()
         self.render_count.get()
     }
     }
+
+    /// Get the height of this scope
+    pub fn height(&self) -> u32 {
+        self.height
+    }
 }
 }
 
 
 impl Drop for ScopeContext {
 impl Drop for ScopeContext {

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

@@ -1,19 +1,7 @@
 use crate::{
 use crate::{
-    any_props::{BoxedAnyProps, VProps},
-    innerlude::{DynamicNode, EventHandler, VComponent, VText},
-    nodes::{IntoAttributeValue, IntoDynNode, RenderReturn},
-    runtime::Runtime,
-    scope_context::ScopeContext,
-    AnyValue, Attribute, AttributeValue, Element, Event, Properties, TaskId,
-};
-use std::{
-    any::Any,
-    cell::{Ref, RefCell},
-    fmt::{Arguments, Debug},
-    future::Future,
-    rc::Rc,
-    sync::Arc,
+    any_props::BoxedAnyProps, nodes::RenderReturn, runtime::Runtime, scope_context::ScopeContext,
 };
 };
+use std::{cell::Ref, fmt::Debug, rc::Rc};
 
 
 /// A component's unique identifier.
 /// A component's unique identifier.
 ///
 ///
@@ -57,16 +45,11 @@ impl Drop for ScopeState {
     }
     }
 }
 }
 
 
-impl<'src> ScopeState {
+impl ScopeState {
     pub(crate) fn context(&self) -> Ref<'_, ScopeContext> {
     pub(crate) fn context(&self) -> Ref<'_, ScopeContext> {
         self.runtime.get_context(self.context_id).unwrap()
         self.runtime.get_context(self.context_id).unwrap()
     }
     }
 
 
-    /// Get the name of this component
-    pub fn name(&self) -> &str {
-        self.context().name
-    }
-
     /// Get a handle to the currently active head node arena for this Scope
     /// 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.
     /// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
@@ -85,267 +68,4 @@ impl<'src> ScopeState {
     pub fn try_root_node(&self) -> Option<&RenderReturn> {
     pub fn try_root_node(&self) -> Option<&RenderReturn> {
         self.last_rendered_node.as_ref()
         self.last_rendered_node.as_ref()
     }
     }
-
-    /// Get the height of this Scope - IE the number of scopes above it.
-    ///
-    /// A Scope with a height of `0` is the root scope - there are no other scopes above it.
-    ///
-    /// # Example
-    ///
-    /// ```rust, ignore
-    /// let mut dom = VirtualDom::new(|cx|  cx.render(rsx!{ div {} }));
-    /// dom.rebuild();
-    ///
-    /// let base = dom.base_scope();
-    ///
-    /// assert_eq!(base.height(), 0);
-    /// ```
-    pub fn height(&self) -> u32 {
-        self.context().height
-    }
-
-    /// Get the Parent of this [`Scope`] within this Dioxus [`crate::VirtualDom`].
-    ///
-    /// This ID is not unique across Dioxus [`crate::VirtualDom`]s or across time. IDs will be reused when components are unmounted.
-    ///
-    /// The base component will not have a parent, and will return `None`.
-    ///
-    /// # Example
-    ///
-    /// ```rust, ignore
-    /// let mut dom = VirtualDom::new(|cx|  cx.render(rsx!{ div {} }));
-    /// dom.rebuild();
-    ///
-    /// let base = dom.base_scope();
-    ///
-    /// assert_eq!(base.parent(), None);
-    /// ```
-    pub fn parent(&self) -> Option<ScopeId> {
-        // safety: the pointer to our parent is *always* valid thanks to the bump arena
-        self.context().parent_id()
-    }
-
-    /// Get the ID of this Scope within this Dioxus [`crate::VirtualDom`].
-    ///
-    /// This ID is not unique across Dioxus [`crate::VirtualDom`]s or across time. IDs will be reused when components are unmounted.
-    ///
-    /// # Example
-    ///
-    /// ```rust, ignore
-    /// let mut dom = VirtualDom::new(|cx|  cx.render(rsx!{ div {} }));
-    /// dom.rebuild();
-    /// let base = dom.base_scope();
-    ///
-    /// assert_eq!(base.scope_id(), 0);
-    /// ```
-    pub fn scope_id(&self) -> ScopeId {
-        self.context().scope_id()
-    }
-
-    /// Create a subscription that schedules a future render for the reference component
-    ///
-    /// ## Notice: you should prefer using [`Self::schedule_update_any`] and [`Self::scope_id`]
-    pub fn schedule_update(&self) -> Arc<dyn Fn() + Send + Sync + 'static> {
-        self.context().schedule_update()
-    }
-
-    /// Schedule an update for any component given its [`ScopeId`].
-    ///
-    /// A component's [`ScopeId`] can be obtained from `use_hook` or the [`ScopeState::scope_id`] method.
-    ///
-    /// This method should be used when you want to schedule an update for a component
-    pub fn schedule_update_any(&self) -> Arc<dyn Fn(ScopeId) + Send + Sync> {
-        self.context().schedule_update_any()
-    }
-
-    /// Mark this scope as dirty, and schedule a render for it.
-    pub fn needs_update(&self) {
-        self.context().needs_update()
-    }
-
-    /// Get the [`ScopeId`] of a mounted component.
-    ///
-    /// `ScopeId` is not unique for the lifetime of the [`crate::VirtualDom`] - a [`ScopeId`] will be reused if a component is unmounted.
-    pub fn needs_update_any(&self, id: ScopeId) {
-        self.context().needs_update_any(id)
-    }
-
-    /// Return any context of type T if it exists on this scope
-    pub fn has_context<T: 'static + Clone>(&self) -> Option<T> {
-        self.context().has_context()
-    }
-
-    /// Try to retrieve a shared state with type `T` from any parent scope.
-    ///
-    /// Clones the state if it exists.
-    pub fn consume_context<T: 'static + Clone>(&self) -> Option<T> {
-        self.context().consume_context()
-    }
-
-    /// Expose state to children further down the [`crate::VirtualDom`] Tree. Requires `Clone` on the context to allow getting values down the tree.
-    ///
-    /// This is a "fundamental" operation and should only be called during initialization of a hook.
-    ///
-    /// For a hook that provides the same functionality, use `use_provide_context` and `use_context` instead.
-    ///
-    /// # Example
-    ///
-    /// ```rust, ignore
-    /// struct SharedState(&'static str);
-    ///
-    /// static App: Component = |cx| {
-    ///     cx.use_hook(|| cx.provide_context(SharedState("world")));
-    ///     render!(Child {})
-    /// }
-    ///
-    /// static Child: Component = |cx| {
-    ///     let state = cx.consume_state::<SharedState>();
-    ///     render!(div { "hello {state.0}" })
-    /// }
-    /// ```
-    pub fn provide_context<T: 'static + Clone>(&self, value: T) -> T {
-        self.context().provide_context(value)
-    }
-
-    /// Provide a context to the root and then consume it
-    ///
-    /// This is intended for "global" state management solutions that would rather be implicit for the entire app.
-    /// Things like signal runtimes and routers are examples of "singletons" that would benefit from lazy initialization.
-    ///
-    /// Note that you should be checking if the context existed before trying to provide a new one. Providing a context
-    /// when a context already exists will swap the context out for the new one, which may not be what you want.
-    pub fn provide_root_context<T: 'static + Clone>(&self, context: T) -> T {
-        self.context().provide_root_context(context)
-    }
-
-    /// Pushes the future onto the poll queue to be polled after the component renders.
-    pub fn push_future(&self, fut: impl Future<Output = ()> + 'static) -> TaskId {
-        self.context().push_future(fut)
-    }
-
-    /// Spawns the future but does not return the [`TaskId`]
-    pub fn spawn(&self, fut: impl Future<Output = ()> + 'static) {
-        self.context().spawn(fut);
-    }
-
-    /// Spawn a future that Dioxus won't clean up when this component is unmounted
-    ///
-    /// This is good for tasks that need to be run after the component has been dropped.
-    pub fn spawn_forever(&self, fut: impl Future<Output = ()> + 'static) -> TaskId {
-        self.context().spawn_forever(fut)
-    }
-
-    /// Informs the scheduler that this task is no longer needed and should be removed.
-    ///
-    /// This drops the task immediately.
-    pub fn remove_future(&self, id: TaskId) {
-        self.context().remove_future(id);
-    }
-
-    /// Create a dynamic text node using [`Arguments`] and the [`ScopeState`]'s internal [`Bump`] allocator
-    pub fn text_node(&'src self, args: Arguments) -> DynamicNode {
-        DynamicNode::Text(VText {
-            value: args.to_string(),
-            id: Default::default(),
-        })
-    }
-
-    /// Convert any item that implements [`IntoDynNode`] into a [`DynamicNode`] using the internal [`Bump`] allocator
-    pub fn make_node<'c, I>(&'src self, into: impl IntoDynNode<I> + 'c) -> DynamicNode {
-        into.into_dyn_node()
-    }
-
-    /// Create a new [`Attribute`] from a name, value, namespace, and volatile bool
-    ///
-    /// "Volatile" referes to whether or not Dioxus should always override the value. This helps prevent the UI in
-    /// some renderers stay in sync with the VirtualDom's understanding of the world
-    pub fn attr(
-        &'src self,
-        name: &'static str,
-        value: impl IntoAttributeValue,
-        namespace: Option<&'static str>,
-        volatile: bool,
-    ) -> Attribute {
-        Attribute {
-            name,
-            namespace,
-            volatile,
-            mounted_element: Default::default(),
-            value: value.into_value(),
-        }
-    }
-
-    /// Create a new [`DynamicNode::Component`] variant
-    ///
-    ///
-    /// The given component can be any of four signatures. Remember that an [`Element`] is really a [`Result<VNode>`].
-    ///
-    /// ```rust, ignore
-    /// // Without explicit props
-    /// fn(Scope) -> Element;
-    /// async fn(Scope<'_>) -> Element;
-    ///
-    /// // With explicit props
-    /// fn(Scope<Props>) -> Element;
-    /// async fn(Scope<Props<'_>>) -> Element;
-    /// ```
-    pub fn component<P>(
-        &self,
-        component: fn(P) -> Element,
-        props: P,
-        fn_name: &'static str,
-    ) -> DynamicNode
-    where
-        // The properties must be valid until the next bump frame
-        P: Properties,
-    {
-        let vcomp = VProps::new(component, P::memoize, props, fn_name);
-
-        DynamicNode::Component(VComponent {
-            name: fn_name,
-            render_fn: component as *const (),
-            props: BoxedAnyProps::new(vcomp),
-            scope: Default::default(),
-        })
-    }
-
-    /// Create a new [`EventHandler`] from an [`FnMut`]
-    pub fn event_handler<T>(&self, mut f: impl FnMut(T) + 'static) -> EventHandler<T> {
-        let callback = RefCell::new(Some(Box::new(move |event: T| {
-            f(event);
-        }) as Box<dyn FnMut(T)>));
-        EventHandler {
-            callback,
-            origin: self.context().id,
-        }
-    }
-
-    /// Create a new [`AttributeValue`] with the listener variant from a callback
-    ///
-    /// The callback must be confined to the lifetime of the ScopeState
-    pub fn listener<T: 'static>(
-        &self,
-        mut callback: impl FnMut(Event<T>) + 'static,
-    ) -> AttributeValue {
-        AttributeValue::Listener(RefCell::new(Box::new(move |event: Event<dyn Any>| {
-            if let Ok(data) = event.data.downcast::<T>() {
-                callback(Event {
-                    propagates: event.propagates,
-                    data,
-                });
-            }
-        })))
-    }
-
-    /// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
-    pub fn any_value<T: AnyValue>(&'src self, value: T) -> AttributeValue {
-        AttributeValue::Any(Box::new(value))
-    }
-
-    /// Mark this component as suspended and then return None
-    pub fn suspend(&self) -> Option<Element> {
-        let cx = self.context();
-        cx.suspend();
-        None
-    }
 }
 }

+ 9 - 14
packages/core/src/virtual_dom.rs

@@ -276,10 +276,11 @@ impl VirtualDom {
         );
         );
 
 
         // Unlike react, we provide a default error boundary that just renders the error as a string
         // Unlike react, we provide a default error boundary that just renders the error as a string
-        root.provide_context(Rc::new(ErrorBoundary::new_in_scope(
-            ScopeId::ROOT,
-            Arc::new(|_| {}),
-        )));
+        root.context()
+            .provide_context(Rc::new(ErrorBoundary::new_in_scope(
+                ScopeId::ROOT,
+                Arc::new(|_| {}),
+            )));
 
 
         // the root element is always given element ID 0 since it's the container for the entire tree
         // the root element is always given element ID 0 since it's the container for the entire tree
         dom.elements.insert(None);
         dom.elements.insert(None);
@@ -305,7 +306,7 @@ impl VirtualDom {
     ///
     ///
     /// This is useful for what is essentially dependency injection when building the app
     /// 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().context().provide_context(context);
         self
         self
     }
     }
 
 
@@ -314,7 +315,7 @@ impl VirtualDom {
     /// Whenever the Runtime "works", it will re-render this scope
     /// Whenever the Runtime "works", it will re-render this scope
     pub fn mark_dirty(&mut self, id: ScopeId) {
     pub fn mark_dirty(&mut self, id: ScopeId) {
         if let Some(scope) = self.get_scope(id) {
         if let Some(scope) = self.get_scope(id) {
-            let height = scope.height();
+            let height = scope.context().height();
             tracing::trace!("Marking scope {:?} ({}) as dirty", id, scope.context().name);
             tracing::trace!("Marking scope {:?} ({}) as dirty", id, scope.context().name);
             self.dirty_scopes.insert(DirtyScope { height, id });
             self.dirty_scopes.insert(DirtyScope { height, id });
         }
         }
@@ -403,11 +404,8 @@ impl VirtualDom {
                 // We check the bubble state between each call to see if the event has been stopped from bubbling
                 // 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() {
                 for listener in listeners.into_iter().rev() {
                     if let AttributeValue::Listener(listener) = listener {
                     if let AttributeValue::Listener(listener) = listener {
-                        let origin = path.scope;
-                        self.runtime.scope_stack.borrow_mut().push(origin);
                         self.runtime.rendering.set(false);
                         self.runtime.rendering.set(false);
-                        (listener.borrow_mut())(uievent.clone());
-                        self.runtime.scope_stack.borrow_mut().pop();
+                        listener.call(uievent.clone());
                         self.runtime.rendering.set(true);
                         self.runtime.rendering.set(true);
 
 
                         if !uievent.propagates.get() {
                         if !uievent.propagates.get() {
@@ -432,11 +430,8 @@ impl VirtualDom {
                     // Only call the listener if this is the exact target element.
                     // Only call the listener if this is the exact target element.
                     if attr.name.trim_start_matches("on") == name && target_path == this_path {
                     if attr.name.trim_start_matches("on") == name && target_path == this_path {
                         if let AttributeValue::Listener(listener) = &attr.value {
                         if let AttributeValue::Listener(listener) = &attr.value {
-                            let origin = path.scope;
-                            self.runtime.scope_stack.borrow_mut().push(origin);
                             self.runtime.rendering.set(false);
                             self.runtime.rendering.set(false);
-                            (listener.borrow_mut())(uievent.clone());
-                            self.runtime.scope_stack.borrow_mut().pop();
+                            listener.call(uievent.clone());
                             self.runtime.rendering.set(true);
                             self.runtime.rendering.set(true);
 
 
                             break;
                             break;