ferroui_base/interactivity/
routed_event.rs1use 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 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
16 pub struct RoutingStrategies: i32 {
17 const DIRECT = 0x01;
19 const TUNNEL = 0x02;
21 const BUBBLE = 0x04;
23 }
24}
25
26type RaisedHandler = dyn Fn(&Interactive, &dyn IRoutedEventArgs);
27type RouteFinishedHandler = dyn Fn(&dyn IRoutedEventArgs);
28
29pub struct RoutedEventObservable {
33 handlers: HandlerList<RaisedHandler>,
34}
35
36impl RoutedEventObservable {
37 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 #[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
66pub struct RouteFinishedObservable {
69 handlers: HandlerList<RouteFinishedHandler>,
70}
71
72impl RouteFinishedObservable {
73 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 event_args_is: fn(TypeId) -> bool,
100 owner_type: &'static TypeInfo,
101 raised: RoutedEventObservable,
102 route_finished: RouteFinishedObservable,
103}
104
105pub 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 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 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 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 #[inline]
210 pub fn as_routed_event(&self) -> RoutedEvent {
211 RoutedEvent { inner: self.inner, _marker: PhantomData }
212 }
213
214 #[inline]
216 pub fn event_args_type(&self) -> TypeId {
217 self.inner.event_args_type
218 }
219
220 #[inline]
222 pub fn event_args_type_name(&self) -> &'static str {
223 self.inner.event_args_type_name
224 }
225
226 #[inline]
229 pub fn event_args_is<T: IRoutedEventArgs>(&self) -> bool {
230 (self.inner.event_args_is)(TypeId::of::<T>())
231 }
232
233 #[inline]
235 pub fn name(&self) -> &'static str {
236 &self.inner.name
237 }
238
239 #[inline]
241 pub fn owner_type(&self) -> &'static TypeInfo {
242 self.inner.owner_type
243 }
244
245 #[inline]
247 pub fn routing_strategies(&self) -> RoutingStrategies {
248 self.inner.routing_strategies
249 }
250
251 #[inline]
254 pub fn has_raised_subscriptions(&self) -> bool {
255 self.inner.raised.has_observers()
256 }
257
258 #[inline]
260 pub fn raised(&self) -> &'static RoutedEventObservable {
261 &self.inner.raised
262 }
263
264 #[inline]
266 pub fn route_finished(&self) -> &'static RouteFinishedObservable {
267 &self.inner.route_finished
268 }
269
270 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 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 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 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#[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}