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