ferroui_base/interactivity/
interactive.rs1use 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#[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#[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 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 pub const DEFAULT_ROUTES: RoutingStrategies = RoutingStrategies::DIRECT.union(RoutingStrategies::BUBBLE);
62
63 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 #[cfg_attr(not(test), allow(dead_code))]
75 pub(crate) fn total_handlers_count() -> usize {
76 TOTAL_HANDLERS_COUNT.get()
77 }
78
79 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 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 self.add_handler_as::<TEventArgs, TEventArgs>(routed_event, handler, routes, handled_events_too)
107 }
108
109 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 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 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 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 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 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 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(¤t);
238 }
239
240 current.add_to_event_route(¤t, 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 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}