ferroui_base/interactivity/
event_route.rs1use super::{IRoutedEventArgs, Interactive, RoutedEvent, RoutingStrategies};
2use crate::Ref;
3use std::cell::RefCell;
4use std::rc::Rc;
5
6pub type RouteHandler = Rc<dyn Fn(&Interactive, &dyn IRoutedEventArgs)>;
9
10struct RouteItem {
11 target: Ref<Interactive>,
12 handler: Option<RouteHandler>,
13 routes: RoutingStrategies,
14 handled_events_too: bool,
15}
16
17const POOL_SIZE: usize = 8;
20
21const INITIAL_CAPACITY: usize = 16;
23
24crate::ferro_thread_local! {
25 static POOL: RefCell<Vec<Vec<RouteItem>>> = const { RefCell::new(Vec::new()) };
26}
27
28fn rent() -> Vec<RouteItem> {
29 POOL.with(|pool| pool.borrow_mut().pop()).unwrap_or_else(|| Vec::with_capacity(INITIAL_CAPACITY))
30}
31
32fn give_back(mut route: Vec<RouteItem>) {
33 route.clear();
36 POOL.with(|pool| {
37 let mut pool = pool.borrow_mut();
38 if pool.len() < POOL_SIZE {
39 pool.push(route);
40 }
41 });
42}
43
44pub struct EventRoute {
51 event: RoutedEvent,
52 route: Option<Vec<RouteItem>>,
53}
54
55impl EventRoute {
56 pub fn new<T: ?Sized>(e: &RoutedEvent<T>) -> Self {
58 Self { event: e.as_routed_event(), route: None }
59 }
60
61 pub fn has_handlers(&self) -> bool {
63 self.route.as_ref().is_some_and(|route| !route.is_empty())
64 }
65
66 pub fn add(
68 &mut self,
69 target: &Ref<Interactive>,
70 handler: RouteHandler,
71 routes: RoutingStrategies,
72 handled_events_too: bool,
73 ) {
74 self.route.get_or_insert_with(rent).push(RouteItem {
75 target: target.clone(),
76 handler: Some(handler),
77 routes,
78 handled_events_too,
79 });
80 }
81
82 pub fn add_class_handler(&mut self, target: &Ref<Interactive>) {
85 self.route.get_or_insert_with(rent).push(RouteItem {
86 target: target.clone(),
87 handler: None,
88 routes: RoutingStrategies::empty(),
89 handled_events_too: false,
90 });
91 }
92
93 pub fn raise_event(&self, source: &Ref<Interactive>, e: &dyn IRoutedEventArgs) {
95 e.set_source(source);
96
97 let strategies = self.event.routing_strategies();
98
99 if strategies == RoutingStrategies::DIRECT {
100 e.set_route(RoutingStrategies::DIRECT);
101 self.raise_event_impl(e);
102 self.event.invoke_route_finished(e);
103 } else {
104 if strategies.contains(RoutingStrategies::TUNNEL) {
105 e.set_route(RoutingStrategies::TUNNEL);
106 self.raise_event_impl(e);
107 self.event.invoke_route_finished(e);
108 }
109
110 if strategies.contains(RoutingStrategies::BUBBLE) {
111 e.set_route(RoutingStrategies::BUBBLE);
112 self.raise_event_impl(e);
113 self.event.invoke_route_finished(e);
114 }
115 }
116 }
117
118 pub fn dispose(&mut self) {
120 if let Some(route) = self.route.take() {
121 give_back(route);
122 }
123 }
124
125 fn raise_event_impl(&self, e: &dyn IRoutedEventArgs) {
126 let Some(route) = &self.route else { return };
127
128 let route_strategy = e.route();
129 let count = route.len();
130 let mut last_target: Option<&Ref<Interactive>> = None;
131
132 for step in 0..count {
133 let index = if route_strategy == RoutingStrategies::TUNNEL { count - 1 - step } else { step };
134 let entry = &route[index];
135
136 if last_target != Some(&entry.target) {
139 self.event.invoke_raised(&entry.target, e);
140
141 if e.route() == RoutingStrategies::DIRECT && last_target.is_some() {
144 return;
145 }
146
147 last_target = Some(&entry.target);
148 }
149
150 if let Some(handler) = &entry.handler {
152 if entry.routes.contains(e.route()) && (!e.handled() || entry.handled_events_too) {
153 handler(&entry.target, e);
154 }
155 }
156 }
157 }
158}
159
160impl Drop for EventRoute {
161 fn drop(&mut self) {
162 self.dispose();
163 }
164}