123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- 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<MouseData>| {
- /// evt.cancel_bubble();
- ///
- /// }
- /// }
- /// }
- /// ```
- pub struct Event<T: 'static + ?Sized> {
- /// The data associated with this event
- pub data: Rc<T>,
- pub(crate) propagates: Rc<Cell<bool>>,
- }
- impl<T: ?Sized + 'static> Event<T> {
- pub(crate) fn new(data: Rc<T>, bubbles: bool) -> Self {
- Self {
- data,
- propagates: Rc::new(Cell::new(bubbles)),
- }
- }
- }
- impl<T> Event<T> {
- /// Map the event data to a new type
- ///
- /// # Example
- ///
- /// ```rust, ignore
- /// rsx! {
- /// button {
- /// onclick: move |evt: Event<FormData>| {
- /// let data = evt.map(|data| data.value());
- /// assert_eq!(data.inner(), "hello world");
- /// }
- /// }
- /// }
- /// ```
- pub fn map<U: 'static, F: FnOnce(&T) -> U>(&self, f: F) -> Event<U> {
- 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<MouseData>| {
- /// 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<MouseData>| {
- /// 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<MouseData>| {
- /// let data = evt.inner.clone();
- /// cx.spawn(async move {
- /// println!("{:?}", data);
- /// });
- /// }
- /// }
- /// }
- /// ```
- pub fn data(&self) -> Rc<T> {
- self.data.clone()
- }
- }
- impl<T: ?Sized> Clone for Event<T> {
- fn clone(&self) -> Self {
- Self {
- propagates: self.propagates.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("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<MouseEvent>,
- /// }
- ///
- /// fn MyComponent(cx: MyProps) -> Element {
- /// rsx!{
- /// button {
- /// onclick: move |evt| cx.onclick.call(evt),
- /// }
- /// }
- /// }
- ///
- /// ```
- pub struct EventHandler<T = ()> {
- pub(crate) origin: ScopeId,
- pub(super) callback: GenerationalBox<Option<ExternalListenerCallback<T>>>,
- }
- impl<T: 'static> Default for EventHandler<T> {
- fn default() -> Self {
- EventHandler::new(|_| {})
- }
- }
- impl<F: FnMut(T) + 'static, T: 'static> From<F> for EventHandler<T> {
- fn from(f: F) -> Self {
- EventHandler::new(f)
- }
- }
- impl<T> Copy for EventHandler<T> {}
- impl<T> Clone for EventHandler<T> {
- fn clone(&self) -> Self {
- *self
- }
- }
- impl<T: 'static> PartialEq for EventHandler<T> {
- fn eq(&self, _: &Self) -> bool {
- true
- }
- }
- type ExternalListenerCallback<T> = Box<dyn FnMut(T)>;
- impl<T: 'static> EventHandler<T> {
- /// Create a new [`EventHandler`] from an [`FnMut`]
- #[track_caller]
- pub fn new(mut f: impl FnMut(T) + 'static) -> EventHandler<T> {
- let callback = GenerationalBox::leak(Some(Box::new(move |event: T| {
- f(event);
- }) as Box<dyn FnMut(T)>));
- 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<T> {
- self.callback
- .manually_drop()
- .expect("Signal has already been dropped")
- .expect("EventHandler was manually dropped")
- }
- }
- impl<T: 'static> std::ops::Deref for EventHandler<T> {
- 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<Self>).
- let uninit_callable = std::mem::MaybeUninit::<Self>::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::<Self>());
- // 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 &_
- }
- }
|