Make EventHandler copy (#2112)
* implement Copy for EventHandler * implement from closure for event handler and remove special on prefix * fix props implementation of EventHandler
This commit is contained in:
1 parent
4bb807a3ce
commit
58f7efafea
19 files changed
+755
-364
No files matched your search
+78
-27
@@ -1,8 +1,7 @@
|
||||
use crate::{global_context::current_scope_id, Runtime, ScopeId};
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
rc::Rc,
|
||||
};
|
||||
use generational_box::{GenerationalBox, UnsyncStorage};
|
||||
|
||||
use crate::{generational_box::current_owner, global_context::current_scope_id, Runtime, ScopeId};
|
||||
use std::{cell::Cell, rc::Rc};
|
||||
|
||||
/// A wrapper around some generic data that handles the event's state
|
||||
///
|
||||
@@ -165,41 +164,45 @@ impl<T: std::fmt::Debug> std::fmt::Debug for Event<T> {
|
||||
/// ```
|
||||
pub struct EventHandler<T = ()> {
|
||||
pub(crate) origin: ScopeId,
|
||||
pub(super) callback: Rc<RefCell<Option<ExternalListenerCallback<T>>>>,
|
||||
pub(super) callback: GenerationalBox<Option<ExternalListenerCallback<T>>>,
|
||||
}
|
||||
|
||||
impl<T: 'static> Default for EventHandler<T> {
|
||||
fn default() -> Self {
|
||||
EventHandler::new(|_| {})
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FnMut(T) + 'static, T: 'static> From<F> for EventHandler<T> {
|
||||
fn from(f: F) -> Self {
|
||||
EventHandler::new(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for EventHandler<T> {}
|
||||
|
||||
impl<T> Clone for EventHandler<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
origin: self.origin,
|
||||
callback: self.callback.clone(),
|
||||
}
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for EventHandler<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
Rc::ptr_eq(&self.callback, &other.callback)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for EventHandler<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
origin: ScopeId::ROOT,
|
||||
callback: Default::default(),
|
||||
}
|
||||
impl<T: 'static> PartialEq for EventHandler<T> {
|
||||
fn eq(&self, _: &Self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
type ExternalListenerCallback<T> = Box<dyn FnMut(T)>;
|
||||
|
||||
impl<T> EventHandler<T> {
|
||||
impl<T: 'static> EventHandler<T> {
|
||||
/// Create a new [`EventHandler`] from an [`FnMut`]
|
||||
#[track_caller]
|
||||
pub fn new(mut f: impl FnMut(T) + 'static) -> EventHandler<T> {
|
||||
let callback = Rc::new(RefCell::new(Some(Box::new(move |event: T| {
|
||||
let owner = current_owner::<UnsyncStorage>();
|
||||
let callback = owner.insert(Some(Box::new(move |event: T| {
|
||||
f(event);
|
||||
}) as Box<dyn FnMut(T)>)));
|
||||
}) as Box<dyn FnMut(T)>));
|
||||
EventHandler {
|
||||
callback,
|
||||
origin: current_scope_id().expect("to be in a dioxus runtime"),
|
||||
@@ -210,7 +213,7 @@ 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() {
|
||||
if let Some(callback) = self.callback.write().as_mut() {
|
||||
Runtime::with(|rt| rt.scope_stack.borrow_mut().push(self.origin));
|
||||
callback(event);
|
||||
Runtime::with(|rt| rt.scope_stack.borrow_mut().pop());
|
||||
@@ -221,6 +224,54 @@ impl<T> EventHandler<T> {
|
||||
///
|
||||
/// This will force any future calls to "call" to not doing anything
|
||||
pub fn release(&self) {
|
||||
self.callback.replace(None);
|
||||
self.callback.set(None);
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// This should only be used by the `rsx!` macro.
|
||||
pub fn __set(&mut self, value: impl FnMut(T) + 'static) {
|
||||
self.callback.set(Some(Box::new(value)));
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// This should only be used by the `rsx!` macro.
|
||||
pub fn __take(&self) -> ExternalListenerCallback<T> {
|
||||
self.callback
|
||||
.manually_drop()
|
||||
.expect("Signal has already been dropped")
|
||||
.expect("EventHandler was manually dropped")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> std::ops::Deref for EventHandler<T> {
|
||||
type Target = dyn Fn(T) + 'static;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
// https://github.com/dtolnay/case-studies/tree/master/callable-types
|
||||
|
||||
// First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit<Self>).
|
||||
let uninit_callable = std::mem::MaybeUninit::<Self>::uninit();
|
||||
// Then move that value into the closure. We assume that the closure now has a in memory layout of Self.
|
||||
let uninit_closure = move |t| Self::call(unsafe { &*uninit_callable.as_ptr() }, t);
|
||||
|
||||
// Check that the size of the closure is the same as the size of Self in case the compiler changed the layout of the closure.
|
||||
let size_of_closure = std::mem::size_of_val(&uninit_closure);
|
||||
assert_eq!(size_of_closure, std::mem::size_of::<Self>());
|
||||
|
||||
// Then cast the lifetime of the closure to the lifetime of &self.
|
||||
fn cast_lifetime<'a, T>(_a: &T, b: &'a T) -> &'a T {
|
||||
b
|
||||
}
|
||||
let reference_to_closure = cast_lifetime(
|
||||
{
|
||||
// The real closure that we will never use.
|
||||
&uninit_closure
|
||||
},
|
||||
// We transmute self into a reference to the closure. This is safe because we know that the closure has the same memory layout as Self so &Closure == &Self.
|
||||
unsafe { std::mem::transmute(self) },
|
||||
);
|
||||
|
||||
// Cast the closure to a trait object.
|
||||
reference_to_closure as &_
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
//! Integration with the generational-box crate for copy state management.
|
||||
//!
|
||||
//! Each scope in dioxus has a single [Owner]
|
||||
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::RefCell,
|
||||
};
|
||||
|
||||
use generational_box::{AnyStorage, Owner, SyncStorage, UnsyncStorage};
|
||||
|
||||
use crate::{innerlude::current_scope_id, ScopeId};
|
||||
|
||||
/// Run a closure with the given owner.
|
||||
///
|
||||
/// This will override the default owner for the current component.
|
||||
pub fn with_owner<S: AnyStorage, F: FnOnce() -> R, R>(owner: Owner<S>, f: F) -> R {
|
||||
let old_owner = set_owner(Some(owner));
|
||||
let result = f();
|
||||
set_owner(old_owner);
|
||||
result
|
||||
}
|
||||
|
||||
/// Set the owner for the current thread.
|
||||
fn set_owner<S: AnyStorage>(owner: Option<Owner<S>>) -> Option<Owner<S>> {
|
||||
let id = TypeId::of::<S>();
|
||||
if id == TypeId::of::<SyncStorage>() {
|
||||
SYNC_OWNER.with(|cell| {
|
||||
std::mem::replace(
|
||||
&mut *cell.borrow_mut(),
|
||||
owner.map(|owner| {
|
||||
*(Box::new(owner) as Box<dyn Any>)
|
||||
.downcast::<Owner<SyncStorage>>()
|
||||
.unwrap()
|
||||
}),
|
||||
)
|
||||
.map(|owner| *(Box::new(owner) as Box<dyn Any>).downcast().unwrap())
|
||||
})
|
||||
} else {
|
||||
UNSYNC_OWNER.with(|cell| {
|
||||
std::mem::replace(
|
||||
&mut *cell.borrow_mut(),
|
||||
owner.map(|owner| {
|
||||
*(Box::new(owner) as Box<dyn Any>)
|
||||
.downcast::<Owner<UnsyncStorage>>()
|
||||
.unwrap()
|
||||
}),
|
||||
)
|
||||
.map(|owner| *(Box::new(owner) as Box<dyn Any>).downcast().unwrap())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static SYNC_OWNER: RefCell<Option<Owner<SyncStorage>>> = const { RefCell::new(None) };
|
||||
static UNSYNC_OWNER: RefCell<Option<Owner<UnsyncStorage>>> = const { RefCell::new(None) };
|
||||
}
|
||||
|
||||
/// Returns the current owner. This owner will be used to drop any `Copy` state that is created by the `generational-box` crate.
|
||||
///
|
||||
/// If an owner has been set with `with_owner`, that owner will be returned. Otherwise, the owner from the current scope will be returned.
|
||||
pub fn current_owner<S: AnyStorage>() -> Owner<S> {
|
||||
let id = TypeId::of::<S>();
|
||||
let override_owner = if id == TypeId::of::<SyncStorage>() {
|
||||
SYNC_OWNER.with(|cell| {
|
||||
let owner = cell.borrow();
|
||||
|
||||
owner.clone().map(|owner| {
|
||||
*(Box::new(owner) as Box<dyn Any>)
|
||||
.downcast::<Owner<S>>()
|
||||
.unwrap()
|
||||
})
|
||||
})
|
||||
} else {
|
||||
UNSYNC_OWNER.with(|cell| {
|
||||
cell.borrow().clone().map(|owner| {
|
||||
*(Box::new(owner) as Box<dyn Any>)
|
||||
.downcast::<Owner<S>>()
|
||||
.unwrap()
|
||||
})
|
||||
})
|
||||
};
|
||||
if let Some(owner) = override_owner {
|
||||
return owner;
|
||||
}
|
||||
|
||||
// Otherwise get the owner from the current scope
|
||||
current_scope_id().expect("in a virtual dom").owner()
|
||||
}
|
||||
|
||||
impl ScopeId {
|
||||
/// Get the owner for the current scope.
|
||||
pub fn owner<S: AnyStorage>(self) -> Owner<S> {
|
||||
match self.has_context() {
|
||||
Some(rt) => rt,
|
||||
None => {
|
||||
let owner = S::owner();
|
||||
self.provide_context(owner)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ mod dirty_scope;
|
||||
mod error_boundary;
|
||||
mod events;
|
||||
mod fragment;
|
||||
mod generational_box;
|
||||
mod global_context;
|
||||
mod mutations;
|
||||
mod nodes;
|
||||
@@ -29,6 +30,7 @@ pub(crate) mod innerlude {
|
||||
pub use crate::error_boundary::*;
|
||||
pub use crate::events::*;
|
||||
pub use crate::fragment::*;
|
||||
pub use crate::generational_box::*;
|
||||
pub use crate::global_context::*;
|
||||
pub use crate::mutations::*;
|
||||
pub use crate::nodes::*;
|
||||
@@ -88,13 +90,13 @@ pub use crate::innerlude::{
|
||||
/// This includes types like [`Element`], and [`Component`].
|
||||
pub mod prelude {
|
||||
pub use crate::innerlude::{
|
||||
consume_context, consume_context_from_scope, current_scope_id, fc_to_builder, generation,
|
||||
has_context, needs_update, needs_update_any, parent_scope, provide_context,
|
||||
provide_root_context, remove_future, schedule_update, schedule_update_any, spawn,
|
||||
spawn_forever, spawn_isomorphic, suspend, try_consume_context, use_after_render,
|
||||
consume_context, consume_context_from_scope, current_owner, current_scope_id,
|
||||
fc_to_builder, generation, has_context, needs_update, needs_update_any, parent_scope,
|
||||
provide_context, provide_root_context, remove_future, schedule_update, schedule_update_any,
|
||||
spawn, spawn_forever, spawn_isomorphic, suspend, try_consume_context, use_after_render,
|
||||
use_before_render, use_drop, use_error_boundary, use_hook, use_hook_with_cleanup,
|
||||
wait_for_next_render, AnyValue, Attribute, Component, ComponentFunction, Element,
|
||||
ErrorBoundary, Event, EventHandler, Fragment, HasAttributes, IntoAttributeValue,
|
||||
wait_for_next_render, with_owner, AnyValue, Attribute, Component, ComponentFunction,
|
||||
Element, ErrorBoundary, Event, EventHandler, Fragment, HasAttributes, IntoAttributeValue,
|
||||
IntoDynNode, OptionStringFromMarker, Properties, Runtime, RuntimeGuard, ScopeId,
|
||||
ScopeState, SuperFrom, SuperInto, Task, Template, TemplateAttribute, TemplateNode, Throw,
|
||||
VNode, VNodeInner, VirtualDom,
|
||||
|
||||
Reference in new issue
Block a user