use crate::{current_scope_id, properties::SuperFrom, runtime::RuntimeGuard, Runtime, ScopeId};
use futures_util::FutureExt;
use generational_box::GenerationalBox;
use std::{any::Any, cell::RefCell, marker::PhantomData, panic::Location, rc::Rc};
pub struct Event<T: 'static + ?Sized> {
pub data: Rc<T>,
pub(crate) metadata: Rc<RefCell<EventMetadata>>,
}
#[derive(Clone, Copy)]
pub(crate) struct EventMetadata {
pub(crate) propagates: bool,
pub(crate) prevent_default: bool,
}
impl<T: ?Sized + 'static> Event<T> {
pub fn new(data: Rc<T>, propagates: bool) -> Self {
Self {
data,
metadata: Rc::new(RefCell::new(EventMetadata {
propagates,
prevent_default: false,
})),
}
}
}
impl<T: ?Sized> Event<T> {
pub fn map<U: 'static, F: FnOnce(&T) -> U>(&self, f: F) -> Event<U> {
Event {
data: Rc::new(f(&self.data)),
metadata: self.metadata.clone(),
}
}
pub fn into_any(self) -> Event<dyn Any>
where
T: Sized,
{
Event {
data: self.data as Rc<dyn Any>,
metadata: self.metadata,
}
}
#[deprecated = "use stop_propagation instead"]
pub fn cancel_bubble(&self) {
self.metadata.borrow_mut().propagates = false;
}
pub fn propagates(&self) -> bool {
self.metadata.borrow().propagates
}
pub fn stop_propagation(&self) {
self.metadata.borrow_mut().propagates = false;
}
pub fn data(&self) -> Rc<T> {
self.data.clone()
}
#[track_caller]
pub fn prevent_default(&self) {
self.metadata.borrow_mut().prevent_default = true;
}
pub fn default_action_enabled(&self) -> bool {
!self.metadata.borrow().prevent_default
}
}
impl<T: ?Sized> Clone for Event<T> {
fn clone(&self) -> Self {
Self {
metadata: self.metadata.clone(),
data: self.data.clone(),
}
}
}
impl<T> std::ops::Deref for Event<T> {
type Target = Rc<T>;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<T: std::fmt::Debug> std::fmt::Debug for Event<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("UiEvent")
.field("bubble_state", &self.propagates())
.field("prevent_default", &!self.default_action_enabled())
.field("data", &self.data)
.finish()
}
}
pub type EventHandler<T = ()> = Callback<T>;
pub struct Callback<Args = (), Ret = ()> {
pub(crate) origin: ScopeId,
pub(super) callback: GenerationalBox<Option<ExternalListenerCallback<Args, Ret>>>,
}
impl<Args, Ret> std::fmt::Debug for Callback<Args, Ret> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Callback")
.field("origin", &self.origin)
.field("callback", &self.callback)
.finish()
}
}
impl<T: 'static, Ret: Default + 'static> Default for Callback<T, Ret> {
fn default() -> Self {
Callback::new(|_| Ret::default())
}
}
#[rustversion::attr(
since(1.78.0),
diagnostic::on_unimplemented(
message = "`SpawnIfAsync` is not implemented for `{Self}`",
label = "Return Value",
note = "Closures (or event handlers) in dioxus need to return either: nothing (the unit type `()`), or an async block that dioxus will automatically spawn",
note = "You likely need to add a semicolon to the end of the event handler to make it return nothing",
)
)]
pub trait SpawnIfAsync<Marker, Ret = ()>: Sized {
fn spawn(self) -> Ret;
}
impl<Ret> SpawnIfAsync<(), Ret> for Ret {
fn spawn(self) -> Ret {
self
}
}
#[doc(hidden)]
pub struct AsyncMarker;
impl<F: std::future::Future<Output = ()> + 'static> SpawnIfAsync<AsyncMarker> for F {
fn spawn(self) {
let mut fut = Box::pin(self);
let res = fut.as_mut().now_or_never();
if res.is_none() {
crate::spawn(async move {
fut.await;
});
}
}
}
#[doc(hidden)]
pub struct AsyncResultMarker;
impl<T> SpawnIfAsync<AsyncResultMarker> for T
where
T: std::future::Future<Output = crate::Result<()>> + 'static,
{
#[inline]
fn spawn(self) {
let mut fut = Box::pin(self);
let res = fut.as_mut().now_or_never();
if res.is_none() {
crate::spawn(async move {
if let Err(err) = fut.await {
crate::throw_error(err)
}
});
}
}
}
impl SpawnIfAsync<()> for crate::Result<()> {
#[inline]
fn spawn(self) {
if let Err(err) = self {
crate::throw_error(err)
}
}
}
#[doc(hidden)]
pub struct MarkerWrapper<T>(PhantomData<T>);
impl<
Function: FnMut(Args) -> Spawn + 'static,
Args: 'static,
Spawn: SpawnIfAsync<Marker, Ret> + 'static,
Ret: 'static,
Marker,
> SuperFrom<Function, MarkerWrapper<Marker>> for Callback<Args, Ret>
{
fn super_from(input: Function) -> Self {
Callback::new(input)
}
}
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)
}
}
impl<T: 'static> SuperFrom<Callback<Event<T>>> for ListenerCallback<T> {
fn super_from(input: Callback<Event<T>>) -> Self {
#[allow(clippy::redundant_closure)]
ListenerCallback::new(move |event| input(event))
}
}
#[doc(hidden)]
pub struct UnitClosure<Marker>(PhantomData<Marker>);
impl<
Function: FnMut() -> Spawn + 'static,
Spawn: SpawnIfAsync<Marker, Ret> + 'static,
Ret: 'static,
Marker,
> SuperFrom<Function, UnitClosure<Marker>> for Callback<(), Ret>
{
fn super_from(mut input: Function) -> Self {
Callback::new(move |()| input())
}
}
#[test]
fn closure_types_infer() {
#[allow(unused)]
fn compile_checks() {
let callback: Callback<(), ()> = Callback::new(|_| {});
let callback: Callback<(), ()> = Callback::new(|_| async {});
let callback: Callback<(), u32> = Callback::new(|_| 123);
let callback: Callback<u32, ()> = Callback::new(|value: u32| async move {
println!("{}", value);
});
let callback: Callback<(), ()> = Callback::super_from(|| async move {
println!("hello world");
});
}
}
impl<Args, Ret> Copy for Callback<Args, Ret> {}
impl<Args, Ret> Clone for Callback<Args, Ret> {
fn clone(&self) -> Self {
*self
}
}
impl<Args: 'static, Ret: 'static> PartialEq for Callback<Args, Ret> {
fn eq(&self, other: &Self) -> bool {
self.callback.ptr_eq(&other.callback) && self.origin == other.origin
}
}
pub(super) struct ExternalListenerCallback<Args, Ret> {
callback: Box<dyn FnMut(Args) -> Ret>,
runtime: std::rc::Weak<Runtime>,
}
impl<Args: 'static, Ret: 'static> Callback<Args, Ret> {
#[track_caller]
pub fn new<MaybeAsync: SpawnIfAsync<Marker, Ret>, Marker>(
mut f: impl FnMut(Args) -> MaybeAsync + 'static,
) -> Self {
let runtime = Runtime::current();
let origin = runtime.current_scope_id();
let owner = crate::innerlude::current_owner::<generational_box::UnsyncStorage>();
let callback = owner.insert_rc(Some(ExternalListenerCallback {
callback: Box::new(move |event: Args| f(event).spawn()),
runtime: Rc::downgrade(&runtime),
}));
Self { callback, origin }
}
#[track_caller]
pub fn leak(mut f: impl FnMut(Args) -> Ret + 'static) -> Self {
let runtime = Runtime::current();
let origin = runtime.current_scope_id();
let callback = GenerationalBox::leak_rc(
Some(ExternalListenerCallback {
callback: Box::new(move |event: Args| f(event).spawn()),
runtime: Rc::downgrade(&runtime),
}),
Location::caller(),
);
Self { callback, origin }
}
#[track_caller]
pub fn call(&self, arguments: Args) -> Ret {
if let Some(callback) = self.callback.write().as_mut() {
let runtime = callback
.runtime
.upgrade()
.expect("Callback was called after the runtime was dropped");
let _guard = RuntimeGuard::new(runtime.clone());
runtime.with_scope_on_stack(self.origin, || (callback.callback)(arguments))
} else {
panic!("Callback was manually dropped")
}
}
pub fn into_closure(self) -> impl FnMut(Args) -> Ret + Copy + 'static {
move |args| self.call(args)
}
pub fn release(&self) {
self.callback.set(None);
}
pub fn replace(&mut self, callback: Box<dyn FnMut(Args) -> Ret>) {
let runtime = Runtime::current();
self.callback.set(Some(ExternalListenerCallback {
callback,
runtime: Rc::downgrade(&runtime),
}));
}
#[doc(hidden)]
pub fn __point_to(&mut self, other: &Self) {
self.callback.point_to(other.callback).unwrap();
}
}
impl<Args: 'static, Ret: 'static> std::ops::Deref for Callback<Args, Ret> {
type Target = dyn Fn(Args) -> Ret + 'static;
fn deref(&self) -> &Self::Target {
let uninit_callable = std::mem::MaybeUninit::<Self>::uninit();
let uninit_closure = move |t| Self::call(unsafe { &*uninit_callable.as_ptr() }, t);
let size_of_closure = std::mem::size_of_val(&uninit_closure);
assert_eq!(size_of_closure, std::mem::size_of::<Self>());
fn cast_lifetime<'a, T>(_a: &T, b: &'a T) -> &'a T {
b
}
let reference_to_closure = cast_lifetime(
{
&uninit_closure
},
#[allow(clippy::missing_transmute_annotations)]
unsafe {
std::mem::transmute(self)
},
);
reference_to_closure as &_
}
}
type AnyEventHandler = Rc<RefCell<dyn FnMut(Event<dyn Any>)>>;
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 {
Rc::ptr_eq(&self.callback, &other.callback) && self.origin == other.origin
}
}
impl<T> ListenerCallback<T> {
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(),
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,
}
}
pub fn call(&self, event: Event<dyn Any>) {
Runtime::current().with_scope_on_stack(self.origin, || {
(self.callback.borrow_mut())(event);
});
}
pub fn erase(self) -> ListenerCallback {
ListenerCallback {
origin: self.origin,
callback: self.callback,
_marker: PhantomData,
}
}
}