Skip to main content

ferroui_base/interactivity/
interactive.rs

1use super::{EventRoute, IRoutedEventArgs, RouteHandler, RoutedEvent, RoutingStrategies};
2use crate::metadata::MarkupDelegate;
3use crate::BoxedValue;
4use crate::layout::{Layoutable, LayoutableImpl};
5use crate::{
6    ferro_class, ferro_impl_classes, instantiate, FerroObjectImpl, Ref, StyledElementImpl, VisualImpl,
7};
8use std::cell::{Cell, RefCell};
9use std::rc::Rc;
10
11/// Identifies a handler added with [`Interactive::add_handler`], for
12/// [`Interactive::remove_handler`].
13#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14pub struct RoutedEventHandlerToken(u64);
15
16struct EventSubscription {
17    token: RoutedEventHandlerToken,
18    handler: RouteHandler,
19    routes: RoutingStrategies,
20    handled_events_too: bool,
21}
22
23struct EventSubscriptions {
24    routed_event: RoutedEvent,
25    subscriptions: Vec<EventSubscription>,
26}
27
28crate::ferro_thread_local! {
29    static TOTAL_HANDLERS_COUNT: Cell<usize> = const { Cell::new(0) };
30    static NEXT_TOKEN: Cell<u64> = const { Cell::new(1) };
31}
32
33/// Base class for objects that raise routed events.
34#[repr(C)]
35pub struct Interactive {
36    base: Layoutable,
37    event_handlers: RefCell<Vec<EventSubscriptions>>,
38}
39
40ferro_class! {
41    Interactive: Layoutable, virtuals InteractiveImpl: LayoutableImpl {
42        /// The interactive parent of the object for bubbling and tunneling
43        /// events. By default this is the visual parent, when it is an
44        /// interactive.
45        fn interactive_parent(this) -> Option<Ref<Interactive>>;
46    }
47}
48crate::ferro_class_info!(Interactive { new: Interactive::new });
49
50ferro_impl_classes!(Interactive: FerroObjectImpl, StyledElementImpl, VisualImpl, LayoutableImpl);
51
52impl InteractiveImpl for Interactive {
53    fn interactive_parent(this: &Self) -> Option<Ref<Interactive>> {
54        this.visual_parent().and_then(|parent| parent.downcast::<Interactive>().ok())
55    }
56}
57
58impl Interactive {
59    /// The routes a handler listens to unless specified otherwise: the
60    /// direct and bubble routes.
61    pub const DEFAULT_ROUTES: RoutingStrategies = RoutingStrategies::DIRECT.union(RoutingStrategies::BUBBLE);
62
63    /// Creates the class data; see [`crate::FerroObject::construct`].
64    pub fn construct() -> Self {
65        Self { base: Layoutable::construct(), event_handlers: RefCell::new(Vec::new()) }
66    }
67
68    pub fn new() -> Ref<Self> {
69        instantiate(Self::construct())
70    }
71
72    /// The number of handlers currently attached to all interactives of the
73    /// current thread.
74    #[cfg_attr(not(test), allow(dead_code))]
75    pub(crate) fn total_handlers_count() -> usize {
76        TOTAL_HANDLERS_COUNT.get()
77    }
78
79    /// Adds a handler for a routed event, listening to the direct and
80    /// bubble routes of unhandled events.
81    ///
82    /// Returns a token for [`remove_handler`](Self::remove_handler).
83    pub fn add_handler<TEventArgs: IRoutedEventArgs>(
84        &self,
85        routed_event: &RoutedEvent<TEventArgs>,
86        handler: impl Fn(&Interactive, &TEventArgs) + 'static,
87    ) -> RoutedEventHandlerToken {
88        self.add_handler_with(routed_event, handler, Self::DEFAULT_ROUTES, false)
89    }
90
91    /// Adds a handler for a routed event.
92    ///
93    /// `routes` selects the routing strategies to listen to, and
94    /// `handled_events_too` whether the handler is also invoked for events
95    /// that are already handled. The handler receives the element it is
96    /// attached to and the event args.
97    pub fn add_handler_with<TEventArgs: IRoutedEventArgs>(
98        &self,
99        routed_event: &RoutedEvent<TEventArgs>,
100        handler: impl Fn(&Interactive, &TEventArgs) + 'static,
101        routes: RoutingStrategies,
102        handled_events_too: bool,
103    ) -> RoutedEventHandlerToken {
104        // The event's args type is `TEventArgs` by construction, so the
105        // conversion below cannot fail for args accepted by `raise_event`.
106        self.add_handler_as::<TEventArgs, TEventArgs>(routed_event, handler, routes, handled_events_too)
107    }
108
109    /// Adds a handler written against args type `THandlerArgs` to any
110    /// routed event whose args type is `THandlerArgs` or derives from it.
111    ///
112    /// This is how a handler taking the base
113    /// [`RoutedEventArgs`](super::RoutedEventArgs) is attached to an event
114    /// with more specific args, and how handlers are attached to an untyped
115    /// [`RoutedEvent`].
116    ///
117    /// Panics if the event's args type does not derive from `THandlerArgs`.
118    pub fn add_handler_as<THandlerArgs: IRoutedEventArgs, TEventArgs: ?Sized>(
119        &self,
120        routed_event: &RoutedEvent<TEventArgs>,
121        handler: impl Fn(&Interactive, &THandlerArgs) + 'static,
122        routes: RoutingStrategies,
123        handled_events_too: bool,
124    ) -> RoutedEventHandlerToken {
125        assert!(
126            routed_event.event_args_is::<THandlerArgs>(),
127            "A handler taking {} cannot be attached to {}, whose args are {}.",
128            std::any::type_name::<THandlerArgs>(),
129            routed_event,
130            routed_event.event_args_type_name()
131        );
132
133        let adapter: RouteHandler = Rc::new(move |sender: &Interactive, args: &dyn IRoutedEventArgs| {
134            match args.downcast_ref::<THandlerArgs>() {
135                Some(typed_args) => handler(sender, typed_args),
136                None => panic!(
137                    "Event args of the wrong type were raised: expected {}.",
138                    std::any::type_name::<THandlerArgs>()
139                ),
140            }
141        });
142
143        self.add_route_handler(routed_event, adapter, routes, handled_events_too)
144    }
145
146    /// Adds an untyped handler for a routed event: what markup attaches for
147    /// an event attribute.
148    ///
149    /// The handler is called with two untyped values: the element it is
150    /// attached to (a `Ref<Interactive>`) and a shared handle to the event
151    /// args (an `Rc<dyn IRoutedEventArgs>`, see
152    /// [`IRoutedEventArgs::share`]). The handle refers to the live args:
153    /// marking it as handled marks the event as handled for the handlers
154    /// further along the route.
155    pub fn add_handler_untyped<TEventArgs: ?Sized>(
156        &self,
157        routed_event: &RoutedEvent<TEventArgs>,
158        handler: MarkupDelegate,
159        routes: RoutingStrategies,
160        handled_events_too: bool,
161    ) -> RoutedEventHandlerToken {
162        let adapter: RouteHandler = Rc::new(move |sender: &Interactive, args: &dyn IRoutedEventArgs| {
163            let sender: BoxedValue = Rc::new(sender.to_ref());
164            let args: BoxedValue = Rc::new(args.share());
165            handler.invoke(&[Some(sender), Some(args)]);
166        });
167
168        self.add_route_handler(routed_event, adapter, routes, handled_events_too)
169    }
170
171    /// Adds an already adapted handler for a routed event.
172    pub fn add_route_handler<TEventArgs: ?Sized>(
173        &self,
174        routed_event: &RoutedEvent<TEventArgs>,
175        handler: RouteHandler,
176        routes: RoutingStrategies,
177        handled_events_too: bool,
178    ) -> RoutedEventHandlerToken {
179        let token = RoutedEventHandlerToken(NEXT_TOKEN.get());
180        NEXT_TOKEN.set(token.0 + 1);
181
182        let subscription = EventSubscription { token, handler, routes, handled_events_too };
183        self.add_event_subscription(routed_event.as_routed_event(), subscription);
184        token
185    }
186
187    /// Removes a handler for a routed event. Returns whether the handler was
188    /// attached.
189    pub fn remove_handler<TEventArgs: ?Sized>(
190        &self,
191        routed_event: &RoutedEvent<TEventArgs>,
192        handler: RoutedEventHandlerToken,
193    ) -> bool {
194        let routed_event = routed_event.as_routed_event();
195        // The removed subscription is dropped after the borrow is released:
196        // dropping a handler can run arbitrary code.
197        let removed = {
198            let mut event_handlers = self.event_handlers.borrow_mut();
199            event_handlers.iter_mut().find(|entry| entry.routed_event == routed_event).and_then(|entry| {
200                let index = entry.subscriptions.iter().rposition(|s| s.token == handler)?;
201                Some(entry.subscriptions.remove(index))
202            })
203        };
204
205        if removed.is_some() {
206            TOTAL_HANDLERS_COUNT.set(TOTAL_HANDLERS_COUNT.get() - 1);
207        }
208        removed.is_some()
209    }
210
211    /// Raises a routed event.
212    ///
213    /// Panics if the args have no routed event.
214    pub fn raise_event(&self, e: &dyn IRoutedEventArgs) {
215        let Some(routed_event) = e.routed_event() else {
216            panic!("Cannot raise an event whose RoutedEvent is null.");
217        };
218
219        let route = self.build_event_route(&routed_event);
220        route.raise_event(&self.to_ref(), e);
221    }
222
223    /// Builds an event route for a routed event: the handlers the event
224    /// would visit if raised on this object, in bubbling order.
225    pub fn build_event_route<TEventArgs: ?Sized>(&self, e: &RoutedEvent<TEventArgs>) -> EventRoute {
226        let e = e.as_routed_event();
227        let mut result = EventRoute::new(&e);
228        let has_class_handlers = e.has_raised_subscriptions();
229        let strategies = e.routing_strategies();
230        let this = self.to_ref();
231
232        if strategies.contains(RoutingStrategies::BUBBLE) || strategies.contains(RoutingStrategies::TUNNEL) {
233            let mut element = Some(this);
234
235            while let Some(current) = element {
236                if has_class_handlers {
237                    result.add_class_handler(&current);
238                }
239
240                current.add_to_event_route(&current, e, &mut result);
241                element = current.interactive_parent();
242            }
243        } else {
244            if has_class_handlers {
245                result.add_class_handler(&this);
246            }
247
248            self.add_to_event_route(&this, e, &mut result);
249        }
250
251        result
252    }
253
254    fn add_event_subscription(&self, routed_event: RoutedEvent, subscription: EventSubscription) {
255        let mut event_handlers = self.event_handlers.borrow_mut();
256
257        match event_handlers.iter_mut().find(|entry| entry.routed_event == routed_event) {
258            Some(entry) => entry.subscriptions.push(subscription),
259            None => event_handlers.push(EventSubscriptions { routed_event, subscriptions: vec![subscription] }),
260        }
261
262        TOTAL_HANDLERS_COUNT.set(TOTAL_HANDLERS_COUNT.get() + 1);
263    }
264
265    fn add_to_event_route(&self, this: &Ref<Interactive>, routed_event: RoutedEvent, route: &mut EventRoute) {
266        // The route takes its own references to the handlers, so handlers
267        // may add or remove handlers while the event is being raised; no
268        // user code runs while the list is borrowed here.
269        let event_handlers = self.event_handlers.borrow();
270
271        if let Some(entry) = event_handlers.iter().find(|entry| entry.routed_event == routed_event) {
272            for sub in &entry.subscriptions {
273                route.add(this, sub.handler.clone(), sub.routes, sub.handled_events_too);
274            }
275        }
276    }
277}