use crate::{properties::SuperFrom, runtime::RuntimeGuard, Runtime, ScopeId};
use generational_box::GenerationalBox;
use std::{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(),
}
}
#[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) {
crate::prelude::spawn(async move {
self.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) {
crate::prelude::spawn(async move {
if let Err(err) = self.await {
crate::prelude::throw_error(err)
}
});
}
}
impl SpawnIfAsync<()> for crate::Result<()> {
#[inline]
fn spawn(self) {
if let Err(err) = self {
crate::prelude::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)
}
}
#[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().unwrap_or_else(|e| panic!("{}", e));
let origin = runtime
.current_scope_id()
.unwrap_or_else(|e| panic!("{}", e));
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().unwrap_or_else(|e| panic!("{}", e));
let origin = runtime
.current_scope_id()
.unwrap_or_else(|e| panic!("{}", e));
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 }
}
pub(crate) fn leak_reference(&self) -> generational_box::BorrowResult<Callback<Args, Ret>> {
Ok(Callback {
callback: self.callback.leak_reference()?,
origin: self.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().unwrap_or_else(|e| panic!("{}", e));
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 &_
}
}