Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 0 additions & 203 deletions packages/core-types/src/event.rs

This file was deleted.

130 changes: 120 additions & 10 deletions packages/core/src/events.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::{properties::SuperFrom, runtime::RuntimeGuard, Runtime, ScopeId};
use crate::{
prelude::current_scope_id, properties::SuperFrom, runtime::RuntimeGuard, Runtime, ScopeId,
};
use generational_box::GenerationalBox;
use std::{cell::RefCell, marker::PhantomData, panic::Location, rc::Rc};
use std::{any::Any, cell::RefCell, marker::PhantomData, panic::Location, rc::Rc};

/// A wrapper around some generic data that handles the event's state
///
Expand Down Expand Up @@ -67,6 +69,17 @@ impl<T: ?Sized> Event<T> {
}
}

/// Convert this event into a boxed event with a dynamic type
pub fn into_any(self) -> Event<dyn Any>
where
T: Sized,
{
Event {
data: self.data as Rc<dyn Any>,
metadata: self.metadata,
}
}

/// Prevent this event from continuing to bubble up the tree to parent elements.
///
/// # Example
Expand Down Expand Up @@ -376,6 +389,27 @@ impl<
}
}

impl<
Function: FnMut(Event<T>) -> Spawn + 'static,
T: 'static,
Spawn: SpawnIfAsync<Marker> + 'static,
Marker,
> SuperFrom<Function, MarkerWrapper<Marker>> for ListenerCallback<T>
{
fn super_from(input: Function) -> Self {
ListenerCallback::new(input)
}
}

// ListenerCallback<T> can be created from Callback<Event<T>>
impl<T: 'static> SuperFrom<Callback<Event<T>>> for ListenerCallback<T> {
fn super_from(input: Callback<Event<T>>) -> Self {
// https://github.com/rust-lang/rust-clippy/issues/15072
#[allow(clippy::redundant_closure)]
ListenerCallback::new(move |event| input(event))
}
}

#[doc(hidden)]
pub struct UnitClosure<Marker>(PhantomData<Marker>);

Expand Down Expand Up @@ -471,14 +505,6 @@ impl<Args: 'static, Ret: 'static> Callback<Args, Ret> {
Self { callback, origin }
}

/// Leak a new reference to the [`Callback`] that will not be dropped unless the callback is dropped manually
pub(crate) fn leak_reference(&self) -> generational_box::BorrowResult<Callback<Args, Ret>> {
Ok(Callback {
callback: self.callback.leak_reference()?,
origin: self.origin,
})
}

/// Call this callback with the appropriate argument type
///
/// This borrows the callback using a RefCell. Recursively calling a callback will cause a panic.
Expand Down Expand Up @@ -559,3 +585,87 @@ impl<Args: 'static, Ret: 'static> std::ops::Deref for Callback<Args, Ret> {
reference_to_closure as &_
}
}

type AnyEventHandler = Rc<RefCell<dyn FnMut(Event<dyn Any>)>>;

/// An owned callback type used in [`AttributeValue::Listener`](crate::AttributeValue::Listener).
///
/// This is the type that powers the `on` attributes in the `rsx!` macro, allowing you to pass event
/// handlers to elements.
///
/// ```rust, ignore
/// rsx! {
/// button {
/// onclick: AttributeValue::Listener(ListenerCallback::new(move |evt: Event<MouseData>| {
/// // ...
/// }
/// }
/// }
/// ```
pub struct ListenerCallback<T = ()> {
pub(crate) origin: ScopeId,
callback: AnyEventHandler,
_marker: PhantomData<T>,
}

impl<T> Clone for ListenerCallback<T> {
fn clone(&self) -> Self {
Self {
origin: self.origin,
callback: self.callback.clone(),
_marker: PhantomData,
}
}
}

impl<T> PartialEq for ListenerCallback<T> {
fn eq(&self, other: &Self) -> bool {
// We compare the pointers of the callbacks, since they are unique
Rc::ptr_eq(&self.callback, &other.callback) && self.origin == other.origin
}
}

impl<T> ListenerCallback<T> {
/// Create a new [`ListenerCallback`] from a callback
///
/// This is expected to be called within a runtime scope. Make sure a runtime is current before
/// calling this method.
pub fn new<MaybeAsync, Marker>(mut f: impl FnMut(Event<T>) -> MaybeAsync + 'static) -> Self
where
T: 'static,
MaybeAsync: SpawnIfAsync<Marker>,
{
Self {
origin: current_scope_id().expect("ListenerCallback must be created within a scope"),
callback: Rc::new(RefCell::new(move |event: Event<dyn Any>| {
let data = event.data.downcast::<T>().unwrap();
f(Event {
metadata: event.metadata.clone(),
data,
})
.spawn();
})),
_marker: PhantomData,
}
}

/// Call the callback with an event
///
/// This is expected to be called within a runtime scope. Make sure a runtime is current before
/// calling this method.
pub fn call(&self, event: Event<dyn Any>) {
let runtime = Runtime::current().expect("ListenerCallback must be called within a runtime");
runtime.with_scope_on_stack(self.origin, || {
(self.callback.borrow_mut())(event);
});
}

/// Erase the type of the callback, allowing it to be used with any type of event
pub fn erase(self) -> ListenerCallback {
ListenerCallback {
origin: self.origin,
callback: self.callback,
_marker: PhantomData,
}
}
}
8 changes: 4 additions & 4 deletions packages/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,10 @@ pub(crate) mod innerlude {
pub use crate::innerlude::{
fc_to_builder, generation, schedule_update, schedule_update_any, use_hook, vdom_is_rendering,
AnyValue, Attribute, AttributeValue, CapturedError, Component, ComponentFunction, DynamicNode,
Element, ElementId, Event, Fragment, HasAttributes, IntoDynNode, LaunchConfig, MarkerWrapper,
Mutation, Mutations, NoOpMutations, Ok, Properties, Result, Runtime, ScopeId, ScopeState,
SpawnIfAsync, Task, Template, TemplateAttribute, TemplateNode, VComponent, VNode, VNodeInner,
VPlaceholder, VText, VirtualDom, WriteMutations,
Element, ElementId, Event, Fragment, HasAttributes, IntoDynNode, LaunchConfig,
ListenerCallback, MarkerWrapper, Mutation, Mutations, NoOpMutations, Ok, Properties, Result,
Runtime, ScopeId, ScopeState, SpawnIfAsync, Task, Template, TemplateAttribute, TemplateNode,
VComponent, VNode, VNodeInner, VPlaceholder, VText, VirtualDom, WriteMutations,
};

/// The purpose of this module is to alleviate imports of many common types
Expand Down
Loading
Loading