|
1 |
| -use crate::{properties::SuperFrom, runtime::RuntimeGuard, Runtime, ScopeId}; |
| 1 | +use crate::{ |
| 2 | + prelude::current_scope_id, properties::SuperFrom, runtime::RuntimeGuard, Runtime, ScopeId, |
| 3 | +}; |
2 | 4 | use generational_box::GenerationalBox;
|
3 |
| -use std::{cell::RefCell, marker::PhantomData, panic::Location, rc::Rc}; |
| 5 | +use std::{any::Any, cell::RefCell, marker::PhantomData, panic::Location, rc::Rc}; |
4 | 6 |
|
5 | 7 | /// A wrapper around some generic data that handles the event's state
|
6 | 8 | ///
|
@@ -67,6 +69,17 @@ impl<T: ?Sized> Event<T> {
|
67 | 69 | }
|
68 | 70 | }
|
69 | 71 |
|
| 72 | + /// Convert this event into a boxed event with a dynamic type |
| 73 | + pub fn into_any(self) -> Event<dyn Any> |
| 74 | + where |
| 75 | + T: Sized, |
| 76 | + { |
| 77 | + Event { |
| 78 | + data: self.data as Rc<dyn Any>, |
| 79 | + metadata: self.metadata, |
| 80 | + } |
| 81 | + } |
| 82 | + |
70 | 83 | /// Prevent this event from continuing to bubble up the tree to parent elements.
|
71 | 84 | ///
|
72 | 85 | /// # Example
|
@@ -376,6 +389,27 @@ impl<
|
376 | 389 | }
|
377 | 390 | }
|
378 | 391 |
|
| 392 | +impl< |
| 393 | + Function: FnMut(Event<T>) -> Spawn + 'static, |
| 394 | + T: 'static, |
| 395 | + Spawn: SpawnIfAsync<Marker> + 'static, |
| 396 | + Marker, |
| 397 | + > SuperFrom<Function, MarkerWrapper<Marker>> for ListenerCallback<T> |
| 398 | +{ |
| 399 | + fn super_from(input: Function) -> Self { |
| 400 | + ListenerCallback::new(input) |
| 401 | + } |
| 402 | +} |
| 403 | + |
| 404 | +// ListenerCallback<T> can be created from Callback<Event<T>> |
| 405 | +impl<T: 'static> SuperFrom<Callback<Event<T>>> for ListenerCallback<T> { |
| 406 | + fn super_from(input: Callback<Event<T>>) -> Self { |
| 407 | + // https://github.com/rust-lang/rust-clippy/issues/15072 |
| 408 | + #[allow(clippy::redundant_closure)] |
| 409 | + ListenerCallback::new(move |event| input(event)) |
| 410 | + } |
| 411 | +} |
| 412 | + |
379 | 413 | #[doc(hidden)]
|
380 | 414 | pub struct UnitClosure<Marker>(PhantomData<Marker>);
|
381 | 415 |
|
@@ -471,14 +505,6 @@ impl<Args: 'static, Ret: 'static> Callback<Args, Ret> {
|
471 | 505 | Self { callback, origin }
|
472 | 506 | }
|
473 | 507 |
|
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 | 508 | /// Call this callback with the appropriate argument type
|
483 | 509 | ///
|
484 | 510 | /// This borrows the callback using a RefCell. Recursively calling a callback will cause a panic.
|
@@ -559,3 +585,87 @@ impl<Args: 'static, Ret: 'static> std::ops::Deref for Callback<Args, Ret> {
|
559 | 585 | reference_to_closure as &_
|
560 | 586 | }
|
561 | 587 | }
|
| 588 | + |
| 589 | +type AnyEventHandler = Rc<RefCell<dyn FnMut(Event<dyn Any>)>>; |
| 590 | + |
| 591 | +/// An owned callback type used in [`AttributeValue::Listener`](crate::AttributeValue::Listener). |
| 592 | +/// |
| 593 | +/// This is the type that powers the `on` attributes in the `rsx!` macro, allowing you to pass event |
| 594 | +/// handlers to elements. |
| 595 | +/// |
| 596 | +/// ```rust, ignore |
| 597 | +/// rsx! { |
| 598 | +/// button { |
| 599 | +/// onclick: AttributeValue::Listener(ListenerCallback::new(move |evt: Event<MouseData>| { |
| 600 | +/// // ... |
| 601 | +/// } |
| 602 | +/// } |
| 603 | +/// } |
| 604 | +/// ``` |
| 605 | +pub struct ListenerCallback<T = ()> { |
| 606 | + pub(crate) origin: ScopeId, |
| 607 | + callback: AnyEventHandler, |
| 608 | + _marker: PhantomData<T>, |
| 609 | +} |
| 610 | + |
| 611 | +impl<T> Clone for ListenerCallback<T> { |
| 612 | + fn clone(&self) -> Self { |
| 613 | + Self { |
| 614 | + origin: self.origin, |
| 615 | + callback: self.callback.clone(), |
| 616 | + _marker: PhantomData, |
| 617 | + } |
| 618 | + } |
| 619 | +} |
| 620 | + |
| 621 | +impl<T> PartialEq for ListenerCallback<T> { |
| 622 | + fn eq(&self, other: &Self) -> bool { |
| 623 | + // We compare the pointers of the callbacks, since they are unique |
| 624 | + Rc::ptr_eq(&self.callback, &other.callback) && self.origin == other.origin |
| 625 | + } |
| 626 | +} |
| 627 | + |
| 628 | +impl<T> ListenerCallback<T> { |
| 629 | + /// Create a new [`ListenerCallback`] from a callback |
| 630 | + /// |
| 631 | + /// This is expected to be called within a runtime scope. Make sure a runtime is current before |
| 632 | + /// calling this method. |
| 633 | + pub fn new<MaybeAsync, Marker>(mut f: impl FnMut(Event<T>) -> MaybeAsync + 'static) -> Self |
| 634 | + where |
| 635 | + T: 'static, |
| 636 | + MaybeAsync: SpawnIfAsync<Marker>, |
| 637 | + { |
| 638 | + Self { |
| 639 | + origin: current_scope_id().expect("ListenerCallback must be created within a scope"), |
| 640 | + callback: Rc::new(RefCell::new(move |event: Event<dyn Any>| { |
| 641 | + let data = event.data.downcast::<T>().unwrap(); |
| 642 | + f(Event { |
| 643 | + metadata: event.metadata.clone(), |
| 644 | + data, |
| 645 | + }) |
| 646 | + .spawn(); |
| 647 | + })), |
| 648 | + _marker: PhantomData, |
| 649 | + } |
| 650 | + } |
| 651 | + |
| 652 | + /// Call the callback with an event |
| 653 | + /// |
| 654 | + /// This is expected to be called within a runtime scope. Make sure a runtime is current before |
| 655 | + /// calling this method. |
| 656 | + pub fn call(&self, event: Event<dyn Any>) { |
| 657 | + let runtime = Runtime::current().expect("ListenerCallback must be called within a runtime"); |
| 658 | + runtime.with_scope_on_stack(self.origin, || { |
| 659 | + (self.callback.borrow_mut())(event); |
| 660 | + }); |
| 661 | + } |
| 662 | + |
| 663 | + /// Erase the type of the callback, allowing it to be used with any type of event |
| 664 | + pub fn erase(self) -> ListenerCallback { |
| 665 | + ListenerCallback { |
| 666 | + origin: self.origin, |
| 667 | + callback: self.callback, |
| 668 | + _marker: PhantomData, |
| 669 | + } |
| 670 | + } |
| 671 | +} |
0 commit comments