use crate::{global_context::current_scope_id, Runtime, ScopeId}; use generational_box::GenerationalBox; use std::{cell::Cell, rc::Rc}; /// A wrapper around some generic data that handles the event's state /// /// /// Prevent this event from continuing to bubble up the tree to parent elements. /// /// # Example /// /// ```rust, ignore /// rsx! { /// button { /// onclick: move |evt: Event| { /// evt.cancel_bubble(); /// /// } /// } /// } /// ``` pub struct Event { /// The data associated with this event pub data: Rc, pub(crate) propagates: Rc>, } impl Event { pub(crate) fn new(data: Rc, bubbles: bool) -> Self { Self { data, propagates: Rc::new(Cell::new(bubbles)), } } } impl Event { /// Map the event data to a new type /// /// # Example /// /// ```rust, ignore /// rsx! { /// button { /// onclick: move |evt: Event| { /// let data = evt.map(|data| data.value()); /// assert_eq!(data.inner(), "hello world"); /// } /// } /// } /// ``` pub fn map U>(&self, f: F) -> Event { Event { data: Rc::new(f(&self.data)), propagates: self.propagates.clone(), } } /// Prevent this event from continuing to bubble up the tree to parent elements. /// /// # Example /// /// ```rust, ignore /// rsx! { /// button { /// onclick: move |evt: Event| { /// evt.cancel_bubble(); /// } /// } /// } /// ``` #[deprecated = "use stop_propagation instead"] pub fn cancel_bubble(&self) { self.propagates.set(false); } /// Prevent this event from continuing to bubble up the tree to parent elements. /// /// # Example /// /// ```rust, ignore /// rsx! { /// button { /// onclick: move |evt: Event| { /// evt.stop_propagation(); /// } /// } /// } /// ``` pub fn stop_propagation(&self) { self.propagates.set(false); } /// Get a reference to the inner data from this event /// /// ```rust, ignore /// rsx! { /// button { /// onclick: move |evt: Event| { /// let data = evt.inner.clone(); /// cx.spawn(async move { /// println!("{:?}", data); /// }); /// } /// } /// } /// ``` pub fn data(&self) -> Rc { self.data.clone() } } impl Clone for Event { fn clone(&self) -> Self { Self { propagates: self.propagates.clone(), data: self.data.clone(), } } } impl std::ops::Deref for Event { type Target = Rc; fn deref(&self) -> &Self::Target { &self.data } } impl std::fmt::Debug for Event { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("UiEvent") .field("bubble_state", &self.propagates) .field("data", &self.data) .finish() } } /// The callback type generated by the `rsx!` macro when an `on` field is specified for components. /// /// This makes it possible to pass `move |evt| {}` style closures into components as property fields. /// /// /// # Example /// /// ```rust, ignore /// rsx!{ /// MyComponent { onclick: move |evt| tracing::debug!("clicked") } /// } /// /// #[derive(Props)] /// struct MyProps { /// onclick: EventHandler, /// } /// /// fn MyComponent(cx: MyProps) -> Element { /// rsx!{ /// button { /// onclick: move |evt| cx.onclick.call(evt), /// } /// } /// } /// /// ``` pub struct EventHandler { pub(crate) origin: ScopeId, pub(super) callback: GenerationalBox>>, } impl Default for EventHandler { fn default() -> Self { EventHandler::new(|_| {}) } } impl From for EventHandler { fn from(f: F) -> Self { EventHandler::new(f) } } impl Copy for EventHandler {} impl Clone for EventHandler { fn clone(&self) -> Self { *self } } impl PartialEq for EventHandler { fn eq(&self, _: &Self) -> bool { true } } type ExternalListenerCallback = Box; impl EventHandler { /// Create a new [`EventHandler`] from an [`FnMut`] #[track_caller] pub fn new(mut f: impl FnMut(T) + 'static) -> EventHandler { let callback = GenerationalBox::leak(Some(Box::new(move |event: T| { f(event); }) as Box)); EventHandler { callback, origin: current_scope_id().expect("to be in a dioxus runtime"), } } /// Call this event handler with the appropriate event type /// /// 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.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()); } } /// Forcibly drop the internal handler callback, releasing memory /// /// This will force any future calls to "call" to not doing anything pub fn release(&self) { 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 { self.callback .manually_drop() .expect("Signal has already been dropped") .expect("EventHandler was manually dropped") } } impl std::ops::Deref for EventHandler { 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). let uninit_callable = std::mem::MaybeUninit::::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::()); // 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 &_ } }