Skip to main content

ferroui_base/interactivity/
routed_event.rs

1use super::{IRoutedEventArgs, Interactive, RoutedEventRegistry};
2use crate::reactive::{Disposable, IDisposable};
3use crate::utilities::HandlerList;
4use crate::{ObjectType, StaticType, TypeInfo};
5use bitflags::bitflags;
6use std::any::TypeId;
7use std::fmt;
8use std::hash::{Hash, Hasher};
9use std::marker::PhantomData;
10use std::rc::Rc;
11
12bitflags! {
13    /// The ways a routed event can travel through the tree, and the parts of
14    /// a route a handler listens to.
15    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
16    pub struct RoutingStrategies: i32 {
17        /// The event is only raised on its source.
18        const DIRECT = 0x01;
19        /// The event travels from the root of the tree down to its source.
20        const TUNNEL = 0x02;
21        /// The event travels from its source up to the root of the tree.
22        const BUBBLE = 0x04;
23    }
24}
25
26type RaisedHandler = dyn Fn(&Interactive, &dyn IRoutedEventArgs);
27type RouteFinishedHandler = dyn Fn(&dyn IRoutedEventArgs);
28
29/// The stream of `(sender, args)` notifications of a routed event: one for
30/// every element on the route of every raise. This is what class handlers
31/// are built on.
32pub struct RoutedEventObservable {
33    handlers: HandlerList<RaisedHandler>,
34}
35
36impl RoutedEventObservable {
37    /// Subscribes to the notifications. Disposing the returned handle
38    /// unsubscribes.
39    pub fn subscribe(
40        &'static self,
41        handler: impl Fn(&Interactive, &dyn IRoutedEventArgs) + 'static,
42    ) -> Rc<dyn IDisposable> {
43        let token = self.handlers.add(Rc::new(handler));
44        Disposable::create(move || {
45            self.handlers.remove(token);
46        })
47    }
48
49    /// Whether there is any subscriber.
50    #[inline]
51    pub fn has_observers(&self) -> bool {
52        !self.handlers.is_empty()
53    }
54
55    #[inline]
56    fn notify(&self, sender: &Interactive, e: &dyn IRoutedEventArgs) {
57        if self.handlers.is_empty() {
58            return;
59        }
60        for (_, handler) in self.handlers.snapshot().iter() {
61            handler(sender, e);
62        }
63    }
64}
65
66/// The stream of notifications raised when a routed event has finished
67/// travelling one of its routes (tunnel, bubble or direct).
68pub struct RouteFinishedObservable {
69    handlers: HandlerList<RouteFinishedHandler>,
70}
71
72impl RouteFinishedObservable {
73    /// Subscribes to the notifications. Disposing the returned handle
74    /// unsubscribes.
75    pub fn subscribe(&'static self, handler: impl Fn(&dyn IRoutedEventArgs) + 'static) -> Rc<dyn IDisposable> {
76        let token = self.handlers.add(Rc::new(handler));
77        Disposable::create(move || {
78            self.handlers.remove(token);
79        })
80    }
81
82    #[inline]
83    fn notify(&self, e: &dyn IRoutedEventArgs) {
84        if self.handlers.is_empty() {
85            return;
86        }
87        for (_, handler) in self.handlers.snapshot().iter() {
88            handler(e);
89        }
90    }
91}
92
93struct RoutedEventInner {
94    name: Box<str>,
95    routing_strategies: RoutingStrategies,
96    event_args_type: TypeId,
97    event_args_type_name: &'static str,
98    /// Whether the event's args type is, or derives from, a given args type.
99    event_args_is: fn(TypeId) -> bool,
100    owner_type: &'static TypeInfo,
101    raised: RoutedEventObservable,
102    route_finished: RouteFinishedObservable,
103}
104
105/// The identity and description of a routed event.
106///
107/// `RoutedEvent<TEventArgs>` is a routed event whose handlers receive
108/// `TEventArgs`; plain `RoutedEvent` is the untyped view of any routed
109/// event (the base class of the typed ones). Both are cheap copyable
110/// handles to the same definition and compare equal when they denote the
111/// same event; [`as_routed_event`](Self::as_routed_event) converts a typed
112/// handle to the untyped one.
113///
114/// Like property definitions, routed events are registered once per thread
115/// and live for the rest of the program; declare them with
116/// [`ferro_routed_event!`](crate::ferro_routed_event).
117pub struct RoutedEvent<TEventArgs: ?Sized = dyn IRoutedEventArgs> {
118    inner: &'static RoutedEventInner,
119    _marker: PhantomData<fn(&TEventArgs)>,
120}
121
122impl<T: ?Sized> Clone for RoutedEvent<T> {
123    #[inline]
124    fn clone(&self) -> Self {
125        *self
126    }
127}
128
129impl<T: ?Sized> Copy for RoutedEvent<T> {}
130
131impl<T: ?Sized, U: ?Sized> PartialEq<RoutedEvent<U>> for RoutedEvent<T> {
132    #[inline]
133    fn eq(&self, other: &RoutedEvent<U>) -> bool {
134        std::ptr::eq(self.inner, other.inner)
135    }
136}
137
138impl<T: ?Sized> Eq for RoutedEvent<T> {}
139
140impl<T: ?Sized> Hash for RoutedEvent<T> {
141    fn hash<H: Hasher>(&self, state: &mut H) {
142        (self.inner as *const RoutedEventInner as usize).hash(state)
143    }
144}
145
146impl<T: ?Sized> fmt::Debug for RoutedEvent<T> {
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        fmt::Display::fmt(self, f)
149    }
150}
151
152impl<T: ?Sized> fmt::Display for RoutedEvent<T> {
153    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154        write!(f, "{}.{}", self.inner.owner_type.name(), self.inner.name)
155    }
156}
157
158fn create_inner<TEventArgs: IRoutedEventArgs>(
159    name: &str,
160    routing_strategies: RoutingStrategies,
161    owner_type: &'static TypeInfo,
162) -> &'static RoutedEventInner {
163    Box::leak(Box::new(RoutedEventInner {
164        name: name.into(),
165        routing_strategies,
166        event_args_type: TypeId::of::<TEventArgs>(),
167        event_args_type_name: std::any::type_name::<TEventArgs>(),
168        event_args_is: TEventArgs::is_args_type,
169        owner_type,
170        raised: RoutedEventObservable { handlers: HandlerList::new() },
171        route_finished: RouteFinishedObservable { handlers: HandlerList::new() },
172    }))
173}
174
175impl RoutedEvent {
176    /// Creates an untyped routed event whose args are of type `TEventArgs`,
177    /// without registering it.
178    pub fn new_untyped<TEventArgs: IRoutedEventArgs>(
179        name: &str,
180        routing_strategies: RoutingStrategies,
181        owner_type: &'static TypeInfo,
182    ) -> RoutedEvent {
183        RoutedEvent { inner: create_inner::<TEventArgs>(name, routing_strategies, owner_type), _marker: PhantomData }
184    }
185
186    /// Registers a routed event owned by class `TOwner`.
187    pub fn register<TOwner: StaticType, TEventArgs: IRoutedEventArgs>(
188        name: &str,
189        routing_strategy: RoutingStrategies,
190    ) -> &'static RoutedEvent<TEventArgs> {
191        Self::register_with_owner::<TEventArgs>(name, routing_strategy, TOwner::TYPE)
192    }
193
194    /// Registers a routed event owned by the given type.
195    pub fn register_with_owner<TEventArgs: IRoutedEventArgs>(
196        name: &str,
197        routing_strategy: RoutingStrategies,
198        owner_type: &'static TypeInfo,
199    ) -> &'static RoutedEvent<TEventArgs> {
200        let routed_event: &'static RoutedEvent<TEventArgs> =
201            Box::leak(Box::new(RoutedEvent::<TEventArgs>::new(name, routing_strategy, owner_type)));
202        RoutedEventRegistry::instance().register(owner_type, routed_event.as_routed_event());
203        routed_event
204    }
205}
206
207impl<TEventArgs: ?Sized> RoutedEvent<TEventArgs> {
208    /// The untyped handle of this routed event.
209    #[inline]
210    pub fn as_routed_event(&self) -> RoutedEvent {
211        RoutedEvent { inner: self.inner, _marker: PhantomData }
212    }
213
214    /// The type of the event's args.
215    #[inline]
216    pub fn event_args_type(&self) -> TypeId {
217        self.inner.event_args_type
218    }
219
220    /// The Rust type name of the event's args.
221    #[inline]
222    pub fn event_args_type_name(&self) -> &'static str {
223        self.inner.event_args_type_name
224    }
225
226    /// Whether the event's args type is `T` or derives from it, i.e. whether
227    /// a handler taking `T` can be attached to the event.
228    #[inline]
229    pub fn event_args_is<T: IRoutedEventArgs>(&self) -> bool {
230        (self.inner.event_args_is)(TypeId::of::<T>())
231    }
232
233    /// The name of the event.
234    #[inline]
235    pub fn name(&self) -> &'static str {
236        &self.inner.name
237    }
238
239    /// The class that registered the event.
240    #[inline]
241    pub fn owner_type(&self) -> &'static TypeInfo {
242        self.inner.owner_type
243    }
244
245    /// How the event travels through the tree.
246    #[inline]
247    pub fn routing_strategies(&self) -> RoutingStrategies {
248        self.inner.routing_strategies
249    }
250
251    /// Whether anything is subscribed to [`raised`](Self::raised), i.e.
252    /// whether the event has class handlers.
253    #[inline]
254    pub fn has_raised_subscriptions(&self) -> bool {
255        self.inner.raised.has_observers()
256    }
257
258    /// Notifications of the event being raised on any element.
259    #[inline]
260    pub fn raised(&self) -> &'static RoutedEventObservable {
261        &self.inner.raised
262    }
263
264    /// Notifications of the event having finished one of its routes.
265    #[inline]
266    pub fn route_finished(&self) -> &'static RouteFinishedObservable {
267        &self.inner.route_finished
268    }
269
270    /// Adds a handler that is invoked for every element of type
271    /// `target_type` (or derived from it) on the route of the event, before
272    /// the handlers attached to that element.
273    ///
274    /// The handler is invoked on the direct route, and on the tunnel or
275    /// bubble routes selected by `routes`; for events that are already
276    /// handled only if `handled_events_too` is set.
277    pub fn add_class_handler_untyped(
278        &self,
279        target_type: &'static TypeInfo,
280        handler: impl Fn(&Interactive, &dyn IRoutedEventArgs) + 'static,
281        routes: RoutingStrategies,
282        handled_events_too: bool,
283    ) -> Rc<dyn IDisposable> {
284        self.inner.raised.subscribe(move |sender, e| {
285            if target_type.is_assignable_from(sender.get_type())
286                && (e.route() == RoutingStrategies::DIRECT || e.route().intersects(routes))
287                && (!e.handled() || handled_events_too)
288            {
289                handler(sender, e);
290            }
291        })
292    }
293
294    #[inline]
295    pub(crate) fn invoke_raised(&self, sender: &Interactive, e: &dyn IRoutedEventArgs) {
296        self.inner.raised.notify(sender, e)
297    }
298
299    #[inline]
300    pub(crate) fn invoke_route_finished(&self, e: &dyn IRoutedEventArgs) {
301        self.inner.route_finished.notify(e)
302    }
303}
304
305impl<TEventArgs: IRoutedEventArgs> RoutedEvent<TEventArgs> {
306    /// Creates a routed event without registering it.
307    pub fn new(name: &str, routing_strategies: RoutingStrategies, owner_type: &'static TypeInfo) -> Self {
308        RoutedEvent { inner: create_inner::<TEventArgs>(name, routing_strategies, owner_type), _marker: PhantomData }
309    }
310
311    /// Adds a class handler for elements of class `TTarget`, listening to
312    /// the direct and bubble routes of unhandled events.
313    pub fn add_class_handler<TTarget: ObjectType>(
314        &self,
315        handler: impl Fn(&TTarget, &TEventArgs) + 'static,
316    ) -> Rc<dyn IDisposable> {
317        self.add_class_handler_with(handler, RoutingStrategies::DIRECT | RoutingStrategies::BUBBLE, false)
318    }
319
320    /// Adds a class handler for elements of class `TTarget`.
321    ///
322    /// See [`add_class_handler_untyped`](Self::add_class_handler_untyped).
323    pub fn add_class_handler_with<TTarget: ObjectType>(
324        &self,
325        handler: impl Fn(&TTarget, &TEventArgs) + 'static,
326        routes: RoutingStrategies,
327        handled_events_too: bool,
328    ) -> Rc<dyn IDisposable> {
329        self.add_class_handler_untyped(
330            TTarget::TYPE,
331            move |sender, e| {
332                if let (Some(target), Some(args)) = (sender.downcast_ref::<TTarget>(), e.downcast_ref::<TEventArgs>())
333                {
334                    handler(target, args);
335                }
336            },
337            routes,
338            handled_events_too,
339        )
340    }
341}
342
343/// Declares the accessor of a routed event definition.
344///
345/// ```ignore
346/// impl Button {
347///     ferro_routed_event!(pub fn click_event() -> RoutedEvent<RoutedEventArgs> {
348///         RoutedEvent::register::<Button, _>("Click", RoutingStrategies::BUBBLE)
349///     });
350/// }
351/// ```
352///
353/// The event is registered the first time the accessor is called on a
354/// thread. Routed event definitions, like the objects that raise them,
355/// belong to the thread that uses them.
356#[macro_export]
357macro_rules! ferro_routed_event {
358    ($(#[$meta:meta])* $vis:vis fn $name:ident() -> $ty:ty $body:block) => {
359        $(#[$meta])*
360        #[inline]
361        $vis fn $name() -> &'static $ty {
362            $crate::ferro_thread_local! {
363                static CELL: ::std::cell::Cell<::std::option::Option<&'static $ty>> =
364                    const { ::std::cell::Cell::new(::std::option::Option::None) };
365            }
366            match CELL.get() {
367                ::std::option::Option::Some(routed_event) => routed_event,
368                ::std::option::Option::None => {
369                    let routed_event: &'static $ty = $body;
370                    CELL.set(::std::option::Option::Some(routed_event));
371                    routed_event
372                }
373            }
374        }
375    };
376}