dioxus_core/events.rs
1use crate::{Runtime, ScopeId, current_scope_id, properties::SuperFrom, runtime::RuntimeGuard};
2use futures_util::FutureExt;
3use generational_box::GenerationalBox;
4use std::{any::Any, cell::RefCell, marker::PhantomData, panic::Location, rc::Rc};
5
6/// A wrapper around some generic data that handles the event's state
7///
8///
9/// Prevent this event from continuing to bubble up the tree to parent elements.
10///
11/// # Example
12///
13/// ```rust, no_run
14/// # use dioxus::prelude::*;
15/// rsx! {
16/// button {
17/// onclick: move |evt: Event<MouseData>| {
18/// evt.stop_propagation();
19/// }
20/// }
21/// };
22/// ```
23pub struct Event<T: 'static + ?Sized> {
24 /// The data associated with this event
25 pub data: Rc<T>,
26 pub(crate) metadata: Rc<RefCell<EventMetadata>>,
27}
28
29#[derive(Clone, Copy)]
30pub(crate) struct EventMetadata {
31 pub(crate) propagates: bool,
32 pub(crate) prevent_default: bool,
33}
34
35impl<T: ?Sized + 'static> Event<T> {
36 /// Create a new event from the inner data
37 pub fn new(data: Rc<T>, propagates: bool) -> Self {
38 Self {
39 data,
40 metadata: Rc::new(RefCell::new(EventMetadata {
41 propagates,
42 prevent_default: false,
43 })),
44 }
45 }
46}
47
48impl<T: ?Sized> Event<T> {
49 /// Map the event data to a new type
50 ///
51 /// # Example
52 ///
53 /// ```rust, no_run
54 /// # use dioxus::prelude::*;
55 /// rsx! {
56 /// button {
57 /// onclick: move |evt: MouseEvent| {
58 /// let data = evt.map(|data| data.client_coordinates());
59 /// println!("{:?}", data.data());
60 /// }
61 /// }
62 /// };
63 /// ```
64 pub fn map<U: 'static, F: FnOnce(&T) -> U>(&self, f: F) -> Event<U> {
65 Event {
66 data: Rc::new(f(&self.data)),
67 metadata: self.metadata.clone(),
68 }
69 }
70
71 /// Convert this event into a boxed event with a dynamic type
72 pub fn into_any(self) -> Event<dyn Any>
73 where
74 T: Sized,
75 {
76 Event {
77 data: self.data as Rc<dyn Any>,
78 metadata: self.metadata,
79 }
80 }
81
82 /// Prevent this event from continuing to bubble up the tree to parent elements.
83 ///
84 /// # Example
85 ///
86 /// ```rust, no_run
87 /// # use dioxus::prelude::*;
88 /// rsx! {
89 /// button {
90 /// onclick: move |evt: Event<MouseData>| {
91 /// # #[allow(deprecated)]
92 /// evt.cancel_bubble();
93 /// }
94 /// }
95 /// };
96 /// ```
97 #[deprecated = "use stop_propagation instead"]
98 pub fn cancel_bubble(&self) {
99 self.metadata.borrow_mut().propagates = false;
100 }
101
102 /// Check if the event propagates up the tree to parent elements
103 pub fn propagates(&self) -> bool {
104 self.metadata.borrow().propagates
105 }
106
107 /// Prevent this event from continuing to bubble up the tree to parent elements.
108 ///
109 /// # Example
110 ///
111 /// ```rust, no_run
112 /// # use dioxus::prelude::*;
113 /// rsx! {
114 /// button {
115 /// onclick: move |evt: Event<MouseData>| {
116 /// evt.stop_propagation();
117 /// }
118 /// }
119 /// };
120 /// ```
121 pub fn stop_propagation(&self) {
122 self.metadata.borrow_mut().propagates = false;
123 }
124
125 /// Get a reference to the inner data from this event
126 ///
127 /// ```rust, no_run
128 /// # use dioxus::prelude::*;
129 /// rsx! {
130 /// button {
131 /// onclick: move |evt: Event<MouseData>| {
132 /// let data = evt.data();
133 /// async move {
134 /// println!("{:?}", data);
135 /// }
136 /// }
137 /// }
138 /// };
139 /// ```
140 pub fn data(&self) -> Rc<T> {
141 self.data.clone()
142 }
143
144 /// Prevent the default action of the event.
145 ///
146 /// # Example
147 ///
148 /// ```rust
149 /// # use dioxus::prelude::*;
150 /// fn App() -> Element {
151 /// rsx! {
152 /// a {
153 /// // You can prevent the default action of the event with `prevent_default`
154 /// onclick: move |event| {
155 /// event.prevent_default();
156 /// },
157 /// href: "https://dioxuslabs.com",
158 /// "don't go to the link"
159 /// }
160 /// }
161 /// }
162 /// ```
163 ///
164 /// Note: This must be called synchronously when handling the event. Calling it after the event has been handled will have no effect.
165 ///
166 /// <div class="warning">
167 ///
168 /// This method is not available on the LiveView renderer because LiveView handles all events over a websocket which cannot block.
169 ///
170 /// </div>
171 #[track_caller]
172 pub fn prevent_default(&self) {
173 self.metadata.borrow_mut().prevent_default = true;
174 }
175
176 /// Check if the default action of the event is enabled.
177 pub fn default_action_enabled(&self) -> bool {
178 !self.metadata.borrow().prevent_default
179 }
180}
181
182impl<T: ?Sized> Clone for Event<T> {
183 fn clone(&self) -> Self {
184 Self {
185 metadata: self.metadata.clone(),
186 data: self.data.clone(),
187 }
188 }
189}
190
191impl<T> std::ops::Deref for Event<T> {
192 type Target = Rc<T>;
193 fn deref(&self) -> &Self::Target {
194 &self.data
195 }
196}
197
198impl<T: std::fmt::Debug> std::fmt::Debug for Event<T> {
199 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
200 f.debug_struct("UiEvent")
201 .field("bubble_state", &self.propagates())
202 .field("prevent_default", &!self.default_action_enabled())
203 .field("data", &self.data)
204 .finish()
205 }
206}
207
208/// The callback type generated by the `rsx!` macro when an `on` field is specified for components.
209///
210/// This makes it possible to pass `move |evt| {}` style closures into components as property fields.
211///
212/// # Example
213///
214/// ```rust, no_run
215/// # use dioxus::prelude::*;
216/// rsx! {
217/// MyComponent { onclick: move |evt| tracing::debug!("clicked") }
218/// };
219///
220/// #[derive(Props, Clone, PartialEq)]
221/// struct MyProps {
222/// onclick: EventHandler<MouseEvent>,
223/// }
224///
225/// fn MyComponent(cx: MyProps) -> Element {
226/// rsx! {
227/// button {
228/// onclick: move |evt| cx.onclick.call(evt),
229/// }
230/// }
231/// }
232/// ```
233pub type EventHandler<T = ()> = Callback<T>;
234
235/// The callback type generated by the `rsx!` macro when an `on` field is specified for components.
236///
237/// This makes it possible to pass `move |evt| {}` style closures into components as property fields.
238///
239///
240/// # Example
241///
242/// ```rust, ignore
243/// rsx! {
244/// MyComponent { onclick: move |evt| {
245/// tracing::debug!("clicked");
246/// 42
247/// } }
248/// }
249///
250/// #[derive(Props)]
251/// struct MyProps {
252/// onclick: Callback<MouseEvent, i32>,
253/// }
254///
255/// fn MyComponent(cx: MyProps) -> Element {
256/// rsx! {
257/// button {
258/// onclick: move |evt| println!("number: {}", cx.onclick.call(evt)),
259/// }
260/// }
261/// }
262/// ```
263pub struct Callback<Args = (), Ret = ()> {
264 pub(crate) origin: ScopeId,
265 /// During diffing components with EventHandler, we move the EventHandler over in place instead of rerunning the child component.
266 ///
267 /// ```rust
268 /// # use dioxus::prelude::*;
269 /// #[component]
270 /// fn Child(onclick: EventHandler<MouseEvent>) -> Element {
271 /// rsx! {
272 /// button {
273 /// // Diffing Child will not rerun this component, it will just update the callback in place so that if this callback is called, it will run the latest version of the callback
274 /// onclick: move |evt| onclick(evt),
275 /// }
276 /// }
277 /// }
278 /// ```
279 ///
280 /// This is both more efficient and allows us to avoid out of date EventHandlers.
281 ///
282 /// We double box here because we want the data to be copy (GenerationalBox) and still update in place (ExternalListenerCallback)
283 /// This isn't an ideal solution for performance, but it is non-breaking and fixes the issues described in <https://github.com/DioxusLabs/dioxus/pull/2298>
284 pub(super) callback: GenerationalBox<Option<ExternalListenerCallback<Args, Ret>>>,
285}
286
287impl<Args, Ret> std::fmt::Debug for Callback<Args, Ret> {
288 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
289 f.debug_struct("Callback")
290 .field("origin", &self.origin)
291 .field("callback", &self.callback)
292 .finish()
293 }
294}
295
296impl<T: 'static, Ret: Default + 'static> Default for Callback<T, Ret> {
297 fn default() -> Self {
298 Callback::new(|_| Ret::default())
299 }
300}
301
302/// A helper trait for [`Callback`]s that allows functions to accept a [`Callback`] that may return an async block which will automatically be spawned.
303///
304/// ```rust, no_run
305/// use dioxus::prelude::*;
306/// fn accepts_fn<Ret: dioxus_core::SpawnIfAsync<Marker>, Marker>(callback: impl FnMut(u32) -> Ret + 'static) {
307/// let callback = Callback::new(callback);
308/// }
309/// // You can accept both async and non-async functions
310/// accepts_fn(|x| async move { println!("{}", x) });
311/// accepts_fn(|x| println!("{}", x));
312/// ```
313#[rustversion::attr(
314 since(1.78.0),
315 diagnostic::on_unimplemented(
316 message = "`SpawnIfAsync` is not implemented for `{Self}`",
317 label = "Return Value",
318 note = "Closures (or event handlers) in dioxus need to return either: nothing (the unit type `()`), or an async block that dioxus will automatically spawn",
319 note = "You likely need to add a semicolon to the end of the event handler to make it return nothing",
320 )
321)]
322pub trait SpawnIfAsync<Marker, Ret = ()>: Sized {
323 /// Spawn the value into the dioxus runtime if it is an async block
324 fn spawn(self) -> Ret;
325}
326
327// Support for FnMut -> Ret for any return type
328impl<Ret> SpawnIfAsync<(), Ret> for Ret {
329 fn spawn(self) -> Ret {
330 self
331 }
332}
333
334// Support for FnMut -> async { unit } for the unit return type
335/// Marker for callbacks that return a future with `()` output.
336#[doc(hidden)]
337pub struct AsyncMarker;
338impl<F: std::future::Future<Output = ()> + 'static> SpawnIfAsync<AsyncMarker> for F {
339 fn spawn(self) {
340 // Quick poll once to deal with things like prevent_default in the same tick
341 let mut fut = Box::pin(self);
342 let res = fut.as_mut().now_or_never();
343
344 if res.is_none() {
345 crate::spawn(async move {
346 fut.await;
347 });
348 }
349 }
350}
351
352// Support for FnMut -> async { Result(()) } for the unit return type
353/// Marker for callbacks that return a future with [`crate::Result<()>`] output.
354#[doc(hidden)]
355pub struct AsyncResultMarker;
356
357impl<T> SpawnIfAsync<AsyncResultMarker> for T
358where
359 T: std::future::Future<Output = crate::Result<()>> + 'static,
360{
361 #[inline]
362 fn spawn(self) {
363 // Quick poll once to deal with things like prevent_default in the same tick
364 let mut fut = Box::pin(self);
365 let res = fut.as_mut().now_or_never();
366
367 if res.is_none() {
368 crate::spawn(async move {
369 if let Err(err) = fut.await {
370 crate::throw_error(err)
371 }
372 });
373 }
374 }
375}
376
377// Support for FnMut -> Result(()) for the unit return type
378impl SpawnIfAsync<()> for crate::Result<()> {
379 #[inline]
380 fn spawn(self) {
381 if let Err(err) = self {
382 crate::throw_error(err)
383 }
384 }
385}
386
387// We can't directly forward the marker because it would overlap with a bunch of other impls, so we wrap it in another type instead
388/// Wrapper marker used to disambiguate callback conversion impls.
389#[doc(hidden)]
390pub struct MarkerWrapper<T>(PhantomData<T>);
391
392// Closure can be created from FnMut -> async { anything } or FnMut -> Ret
393impl<
394 Function: FnMut(Args) -> Spawn + 'static,
395 Args: 'static,
396 Spawn: SpawnIfAsync<Marker, Ret> + 'static,
397 Ret: 'static,
398 Marker,
399> SuperFrom<Function, MarkerWrapper<Marker>> for Callback<Args, Ret>
400{
401 fn super_from(input: Function) -> Self {
402 Callback::new(input)
403 }
404}
405
406impl<
407 Function: FnMut(Event<T>) -> Spawn + 'static,
408 T: 'static,
409 Spawn: SpawnIfAsync<Marker> + 'static,
410 Marker,
411> SuperFrom<Function, MarkerWrapper<Marker>> for ListenerCallback<T>
412{
413 fn super_from(input: Function) -> Self {
414 ListenerCallback::new(input)
415 }
416}
417
418// ListenerCallback<T> can be created from Callback<Event<T>>
419impl<T: 'static> SuperFrom<Callback<Event<T>>> for ListenerCallback<T> {
420 fn super_from(input: Callback<Event<T>>) -> Self {
421 // https://github.com/rust-lang/rust-clippy/issues/15072
422 #[allow(clippy::redundant_closure)]
423 ListenerCallback::new(move |event| input(event))
424 }
425}
426
427/// Marker for callbacks built from closures that take no arguments.
428#[doc(hidden)]
429pub struct UnitClosure<Marker>(PhantomData<Marker>);
430
431// Closure can be created from FnMut -> async { () } or FnMut -> Ret
432impl<
433 Function: FnMut() -> Spawn + 'static,
434 Spawn: SpawnIfAsync<Marker, Ret> + 'static,
435 Ret: 'static,
436 Marker,
437> SuperFrom<Function, UnitClosure<Marker>> for Callback<(), Ret>
438{
439 fn super_from(mut input: Function) -> Self {
440 Callback::new(move |()| input())
441 }
442}
443
444#[test]
445fn closure_types_infer() {
446 #[allow(unused)]
447 fn compile_checks() {
448 // You should be able to use a closure as a callback
449 let callback: Callback<(), ()> = Callback::new(|_| {});
450 // Or an async closure
451 let callback: Callback<(), ()> = Callback::new(|_| async {});
452
453 // You can also pass in a closure that returns a value
454 let callback: Callback<(), u32> = Callback::new(|_| 123);
455
456 // Or pass in a value
457 let callback: Callback<u32, ()> = Callback::new(|value: u32| async move {
458 println!("{}", value);
459 });
460
461 // Unit closures shouldn't require an argument
462 let callback: Callback<(), ()> = Callback::super_from(|| async move {
463 println!("hello world");
464 });
465 }
466}
467
468impl<Args, Ret> Copy for Callback<Args, Ret> {}
469
470impl<Args, Ret> Clone for Callback<Args, Ret> {
471 fn clone(&self) -> Self {
472 *self
473 }
474}
475
476impl<Args, Ret> PartialEq for Callback<Args, Ret> {
477 fn eq(&self, other: &Self) -> bool {
478 self.callback.ptr_eq(&other.callback) && self.origin == other.origin
479 }
480}
481
482pub(super) struct ExternalListenerCallback<Args, Ret> {
483 callback: Box<dyn FnMut(Args) -> Ret>,
484 runtime: std::rc::Weak<Runtime>,
485}
486
487impl<Args: 'static, Ret: 'static> Callback<Args, Ret> {
488 /// Create a new [`Callback`] from an [`FnMut`]. The callback is owned by the current scope and will be dropped when the scope is dropped.
489 /// This should not be called directly in the body of a component because it will not be dropped until the component is dropped.
490 #[track_caller]
491 pub fn new<MaybeAsync: SpawnIfAsync<Marker, Ret>, Marker>(
492 mut f: impl FnMut(Args) -> MaybeAsync + 'static,
493 ) -> Self {
494 let runtime = Runtime::current();
495 let origin = runtime.current_scope_id();
496 let owner = crate::innerlude::current_owner::<generational_box::UnsyncStorage>();
497 let callback = owner.insert_rc(Some(ExternalListenerCallback {
498 callback: Box::new(move |event: Args| f(event).spawn()),
499 runtime: Rc::downgrade(&runtime),
500 }));
501 Self { callback, origin }
502 }
503
504 /// Leak a new [`Callback`] that will not be dropped unless it is manually dropped.
505 #[track_caller]
506 pub fn leak(mut f: impl FnMut(Args) -> Ret + 'static) -> Self {
507 let runtime = Runtime::current();
508 let origin = runtime.current_scope_id();
509 let callback = GenerationalBox::leak_rc(
510 Some(ExternalListenerCallback {
511 callback: Box::new(move |event: Args| f(event).spawn()),
512 runtime: Rc::downgrade(&runtime),
513 }),
514 Location::caller(),
515 );
516 Self { callback, origin }
517 }
518
519 /// Call this callback with the appropriate argument type
520 ///
521 /// This borrows the callback using a RefCell. Recursively calling a callback will cause a panic.
522 #[track_caller]
523 pub fn call(&self, arguments: Args) -> Ret {
524 if let Some(callback) = self.callback.write().as_mut() {
525 let runtime = callback
526 .runtime
527 .upgrade()
528 .expect("Callback was called after the runtime was dropped");
529 let _guard = RuntimeGuard::new(runtime.clone());
530 runtime.with_scope_on_stack(self.origin, || (callback.callback)(arguments))
531 } else {
532 panic!("Callback was manually dropped")
533 }
534 }
535
536 /// Create a `impl FnMut + Copy` closure from the Closure type
537 pub fn into_closure(self) -> impl FnMut(Args) -> Ret + Copy + 'static {
538 move |args| self.call(args)
539 }
540
541 /// Forcibly drop the internal handler callback, releasing memory
542 ///
543 /// This will force any future calls to "call" to not doing anything
544 pub fn release(&self) {
545 self.callback.set(None);
546 }
547
548 /// Replace the function in the callback with a new one
549 pub fn replace(&mut self, callback: Box<dyn FnMut(Args) -> Ret>) {
550 let runtime = Runtime::current();
551 self.callback.set(Some(ExternalListenerCallback {
552 callback,
553 runtime: Rc::downgrade(&runtime),
554 }));
555 }
556
557 /// This should only be used by the `rsx!` macro.
558 pub fn __point_to(&mut self, other: &Self) {
559 self.callback.point_to(other.callback).unwrap();
560 }
561}
562
563impl<Args: 'static, Ret: 'static> std::ops::Deref for Callback<Args, Ret> {
564 type Target = dyn Fn(Args) -> Ret + 'static;
565
566 fn deref(&self) -> &Self::Target {
567 // https://github.com/dtolnay/case-studies/tree/master/callable-types
568
569 // First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit<Self>).
570 let uninit_callable = std::mem::MaybeUninit::<Self>::uninit();
571 // Then move that value into the closure. We assume that the closure now has a in memory layout of Self.
572 let uninit_closure = move |t| Self::call(unsafe { &*uninit_callable.as_ptr() }, t);
573
574 // 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.
575 let size_of_closure = std::mem::size_of_val(&uninit_closure);
576 assert_eq!(size_of_closure, std::mem::size_of::<Self>());
577
578 // Then cast the lifetime of the closure to the lifetime of &self.
579 fn cast_lifetime<'a, T>(_a: &T, b: &'a T) -> &'a T {
580 b
581 }
582 let reference_to_closure = cast_lifetime(
583 {
584 // The real closure that we will never use.
585 &uninit_closure
586 },
587 #[allow(clippy::missing_transmute_annotations)]
588 // 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.
589 unsafe {
590 std::mem::transmute(self)
591 },
592 );
593
594 // Cast the closure to a trait object.
595 reference_to_closure as &_
596 }
597}
598
599type AnyEventHandler = Rc<RefCell<dyn FnMut(Event<dyn Any>)>>;
600
601/// An owned callback type used in [`AttributeValue::Listener`](crate::AttributeValue::Listener).
602///
603/// This is the type that powers the `on` attributes in the `rsx!` macro, allowing you to pass event
604/// handlers to elements.
605///
606/// ```rust, ignore
607/// rsx! {
608/// button {
609/// onclick: AttributeValue::Listener(ListenerCallback::new(move |evt: Event<MouseData>| {
610/// // ...
611/// }))
612/// }
613/// }
614/// ```
615pub struct ListenerCallback<T = ()> {
616 pub(crate) origin: ScopeId,
617 callback: AnyEventHandler,
618 _marker: PhantomData<T>,
619}
620
621impl<T> Clone for ListenerCallback<T> {
622 fn clone(&self) -> Self {
623 Self {
624 origin: self.origin,
625 callback: self.callback.clone(),
626 _marker: PhantomData,
627 }
628 }
629}
630
631impl<T> PartialEq for ListenerCallback<T> {
632 fn eq(&self, other: &Self) -> bool {
633 // We compare the pointers of the callbacks, since they are unique
634 Rc::ptr_eq(&self.callback, &other.callback) && self.origin == other.origin
635 }
636}
637
638impl<T> ListenerCallback<T> {
639 /// Create a new [`ListenerCallback`] from a callback
640 ///
641 /// This is expected to be called within a runtime scope. Make sure a runtime is current before
642 /// calling this method.
643 pub fn new<MaybeAsync, Marker>(mut f: impl FnMut(Event<T>) -> MaybeAsync + 'static) -> Self
644 where
645 T: 'static,
646 MaybeAsync: SpawnIfAsync<Marker>,
647 {
648 Self {
649 origin: current_scope_id(),
650 callback: Rc::new(RefCell::new(move |event: Event<dyn Any>| {
651 let data = event.data.downcast::<T>().unwrap();
652 f(Event {
653 metadata: event.metadata.clone(),
654 data,
655 })
656 .spawn();
657 })),
658 _marker: PhantomData,
659 }
660 }
661
662 /// Call the callback with an event
663 ///
664 /// This is expected to be called within a runtime scope. Make sure a runtime is current before
665 /// calling this method.
666 pub fn call(&self, event: Event<dyn Any>) {
667 Runtime::current().with_scope_on_stack(self.origin, || {
668 if let Ok(mut borrow_mut) = self.callback.try_borrow_mut() {
669 borrow_mut(event);
670 } else {
671 tracing::warn!("ListenerCallback was called recursively, ignoring recursive call to avoid re-entrance issues");
672 }
673 });
674 }
675
676 /// Erase the type of the callback, allowing it to be used with any type of event
677 pub fn erase(self) -> ListenerCallback {
678 ListenerCallback {
679 origin: self.origin,
680 callback: self.callback,
681 _marker: PhantomData,
682 }
683 }
684}