Skip to main content

ferroui_xaml_interactivity/events/handlers/
func_event_handler.rs

1//! Port of `Events/Handlers/FuncEventHandler.cs` (`FuncAddEventHandler<TTarget, TEventArgs>`).
2
3use crate::events::{AddEventHandlerCallback, IAddEventHandler};
4use crate::DisposableAction;
5use ferroui_base::data::core::WeakValue;
6use ferroui_base::interactivity::{CancelRoutedEventArgs, IRoutedEventArgs, RoutedEventArgs};
7use ferroui_base::metadata::from_markup_value;
8use ferroui_base::reactive::IDisposable;
9use ferroui_base::utilities::EventArgs;
10use ferroui_base::BoxedValue;
11use std::cell::RefCell;
12use std::rc::Rc;
13
14/// The event args of an event as the untyped value an [`IAddEventHandler`] passes on (the args
15/// are an object in the reference).
16pub trait EventArgsValue {
17    /// The args as an untyped value.
18    fn to_event_args_value(&self) -> BoxedValue;
19}
20
21impl EventArgsValue for EventArgs {
22    fn to_event_args_value(&self) -> BoxedValue {
23        Rc::new(*self)
24    }
25}
26
27/// Routed event args are passed as a shared handle to the live args.
28impl EventArgsValue for RoutedEventArgs {
29    fn to_event_args_value(&self) -> BoxedValue {
30        Rc::new(self.share())
31    }
32}
33
34/// Routed event args are passed as a shared handle to the live args.
35impl EventArgsValue for CancelRoutedEventArgs {
36    fn to_event_args_value(&self) -> BoxedValue {
37        Rc::new(self.share())
38    }
39}
40
41/// The handler [`FuncAddEventHandler`] gives to the subscription function: the sender and the
42/// typed event args (`EventHandler<TEventArgs>`).
43pub type EventHandler<TEventArgs> = Rc<dyn Fn(Option<&BoxedValue>, &TEventArgs)>;
44
45/// An [`IAddEventHandler`] for the event `name` of sources of type `TTarget`, from a function
46/// that subscribes a handler and a function that removes it.
47///
48/// `TTarget` is the handle of the source (`Ref<Button>`, `Rc<Model>`). Deviation (DEVIATIONS.md,
49/// Behaviors): the reference removes a handler by delegate identity; a subscription of the port
50/// returns a token (`TToken`: a routed event handler token, a disposable), which is what `remove`
51/// takes.
52pub struct FuncAddEventHandler<TTarget, TEventArgs, TToken = Rc<dyn IDisposable>> {
53    name: String,
54    add: Box<dyn Fn(&TTarget, EventHandler<TEventArgs>) -> TToken>,
55    remove: Rc<dyn Fn(&TTarget, TToken)>,
56}
57
58impl<TTarget, TEventArgs, TToken> FuncAddEventHandler<TTarget, TEventArgs, TToken>
59where
60    TTarget: Clone + 'static,
61    TEventArgs: EventArgsValue + 'static,
62    TToken: 'static,
63{
64    /// Creates the handler of the event `name`: `add` subscribes a handler to a source, `remove`
65    /// removes it with the token `add` returned.
66    pub fn new(
67        name: &str,
68        add: impl Fn(&TTarget, EventHandler<TEventArgs>) -> TToken + 'static,
69        remove: impl Fn(&TTarget, TToken) + 'static,
70    ) -> Self {
71        Self { name: name.to_string(), add: Box::new(add), remove: Rc::new(remove) }
72    }
73
74    /// The source as a `TTarget` (`source is TTarget target`).
75    fn target_of(source: &BoxedValue) -> Option<TTarget> {
76        from_markup_value::<TTarget>(&Some(source.clone()))
77    }
78}
79
80impl<TTarget, TEventArgs, TToken> IAddEventHandler for FuncAddEventHandler<TTarget, TEventArgs, TToken>
81where
82    TTarget: Clone + 'static,
83    TEventArgs: EventArgsValue + 'static,
84    TToken: 'static,
85{
86    fn matches(&self, source: &BoxedValue, event_name: &str) -> bool {
87        Self::target_of(source).is_some() && event_name == self.name
88    }
89
90    fn add_handler(
91        &self,
92        source: &BoxedValue,
93        event_name: &str,
94        handler: AddEventHandlerCallback,
95    ) -> Option<Rc<dyn IDisposable>> {
96        let target = Self::target_of(source)?;
97        if event_name != self.name {
98            return None;
99        }
100
101        let event_handler: EventHandler<TEventArgs> =
102            Rc::new(move |sender: Option<&BoxedValue>, e: &TEventArgs| handler(sender, &e.to_event_args_value()));
103        let token = (self.add)(&target, event_handler);
104
105        // The subscription does not keep the source alive: it is the source that holds its
106        // handlers.
107        let source = WeakValue::new(source);
108        let token = RefCell::new(Some(token));
109        let remove = self.remove.clone();
110        Some(DisposableAction::create(move || {
111            let Some(token) = token.borrow_mut().take() else {
112                return;
113            };
114            if let Some(target) = source.upgrade().and_then(|source| Self::target_of(&source)) {
115                remove(&target, token);
116            }
117        }))
118    }
119}