1use freya_engine::prelude::*;
2use freya_native_core::{
3 real_dom::NodeImmutable,
4 tree::TreeRef,
5 NodeId,
6};
7use itertools::sorted;
8
9use super::{
10 PlatformEventData,
11 PotentialEvent,
12};
13pub use crate::events::{
14 DomEvent,
15 NodesState,
16 PlatformEvent,
17};
18use crate::{
19 dom::{
20 DioxusDOM,
21 FreyaDOM,
22 },
23 elements::{
24 ElementUtils,
25 ElementUtilsResolver,
26 },
27 states::{
28 StyleState,
29 ViewportState,
30 },
31 types::{
32 EventEmitter,
33 EventsQueue,
34 PotentialEvents,
35 },
36 values::Fill,
37};
38
39pub fn process_events(
41 fdom: &FreyaDOM,
42 events: &mut EventsQueue,
43 event_emitter: &EventEmitter,
44 nodes_state: &mut NodesState,
45 scale_factor: f64,
46
47 focus_id: Option<NodeId>,
48) {
49 let potential_events = measure_potential_event_listeners(events, fdom, scale_factor, focus_id);
51
52 let mut dom_events = measure_dom_events(&potential_events, fdom, scale_factor);
54
55 let potential_collateral_events =
57 nodes_state.process_collateral(fdom, &potential_events, &mut dom_events, events);
58
59 let collateral_dom_events =
61 measure_dom_events(&potential_collateral_events, fdom, scale_factor);
62
63 measure_platform_global_events(fdom, events, &mut dom_events, scale_factor);
65
66 dom_events.extend(collateral_dom_events);
68 dom_events.sort_unstable();
69
70 event_emitter.send(dom_events).unwrap();
72
73 events.clear();
75}
76
77pub fn measure_platform_global_events(
79 fdom: &FreyaDOM,
80 events: &EventsQueue,
81 dom_events: &mut Vec<DomEvent>,
82 scale_factor: f64,
83) {
84 let rdom = fdom.rdom();
85 for PlatformEvent { name, data } in events {
86 let derived_events_names = name.get_derived_events();
87
88 for derived_event_name in derived_events_names {
89 let Some(global_name) = derived_event_name.get_global_event() else {
90 continue;
91 };
92
93 let listeners = rdom.get_listeners(&global_name);
94
95 for listener in listeners {
96 let event = DomEvent::new(
97 PotentialEvent {
98 node_id: listener.id(),
99 layer: None,
100 name: global_name,
101 data: data.clone(),
102 },
103 None,
104 scale_factor,
105 );
106 dom_events.push(event)
107 }
108 }
109 }
110}
111
112pub fn measure_potential_event_listeners(
114 events: &EventsQueue,
115 fdom: &FreyaDOM,
116 scale_factor: f64,
117 focus_id: Option<NodeId>,
118) -> PotentialEvents {
119 let mut potential_events = PotentialEvents::default();
120
121 let layout = fdom.layout();
122 let rdom = fdom.rdom();
123 let layers = fdom.layers();
124
125 for (layer, layer_nodes) in sorted(layers.iter()) {
127 for node_id in layer_nodes.iter() {
128 let Some(layout_node) = layout.get(*node_id) else {
129 continue;
130 };
131 'events: for PlatformEvent { name, data } in events {
132 let cursor = match data {
133 PlatformEventData::Mouse { cursor, .. } => cursor,
134 PlatformEventData::Wheel { cursor, .. } => cursor,
135 PlatformEventData::Touch { location, .. } => location,
136 PlatformEventData::File { cursor, .. } => cursor,
137 PlatformEventData::Keyboard { .. } if focus_id == Some(*node_id) => {
138 let potential_event = PotentialEvent {
139 node_id: *node_id,
140 layer: Some(*layer),
141 name: *name,
142 data: data.clone(),
143 };
144 potential_events
145 .entry(*name)
146 .or_default()
147 .push(potential_event);
148 continue;
149 }
150 _ => continue,
151 };
152
153 let node = rdom.get(*node_id).unwrap();
154 let node_type = node.node_type();
155
156 let Some(element_utils) = node_type.tag().and_then(|tag| tag.utils()) else {
157 continue;
158 };
159
160 if !element_utils.is_point_inside_area(
162 cursor,
163 &node,
164 layout_node,
165 scale_factor as f32,
166 ) {
167 continue;
168 }
169
170 let node = rdom.get(*node_id).unwrap();
171 let node_viewports = node.get::<ViewportState>().unwrap();
172
173 for node_id in &node_viewports.viewports {
175 let node_ref = rdom.get(*node_id).unwrap();
176 let node_type = node_ref.node_type();
177 let Some(element_utils) = node_type.tag().and_then(|tag| tag.utils()) else {
178 continue;
179 };
180 let layout_node = layout.get(*node_id).unwrap();
181 if !element_utils.is_point_inside_area(
182 cursor,
183 &node_ref,
184 layout_node,
185 scale_factor as f32,
186 ) {
187 continue 'events;
188 }
189 }
190
191 let potential_event = PotentialEvent {
192 node_id: *node_id,
193 layer: Some(*layer),
194 name: *name,
195 data: data.clone(),
196 };
197
198 potential_events
199 .entry(*name)
200 .or_insert_with(Vec::new)
201 .push(potential_event);
202 }
203 }
204 }
205
206 potential_events
207}
208
209pub fn is_node_parent_of(rdom: &DioxusDOM, node: NodeId, parent_node: NodeId) -> bool {
210 let mut head = Some(node);
211 while let Some(id) = head.take() {
212 let tree = rdom.tree_ref();
213 if let Some(parent_id) = tree.parent_id(id) {
214 if parent_id == parent_node {
215 return true;
216 }
217
218 head = Some(parent_id)
219 }
220 }
221 false
222}
223
224fn measure_dom_events(
226 potential_events: &PotentialEvents,
227 fdom: &FreyaDOM,
228 scale_factor: f64,
229) -> Vec<DomEvent> {
230 let mut dom_events = Vec::new();
231 let rdom = fdom.rdom();
232 let layout = fdom.layout();
233
234 for (event_name, event_nodes) in potential_events {
235 let derived_events = event_name
237 .get_derived_events()
238 .into_iter()
239 .filter(|event| !event.is_global());
240
241 'event: for derived_event in derived_events {
243 let mut child_node: Option<NodeId> = None;
244
245 for PotentialEvent {
247 node_id,
248 data,
249 name,
250 layer,
251 } in event_nodes.iter().rev()
252 {
253 let Some(node) = rdom.get(*node_id) else {
254 continue;
255 };
256
257 if let Some(child_node) = child_node {
258 if !is_node_parent_of(rdom, child_node, *node_id) {
259 continue;
260 }
261 }
262
263 if rdom.is_node_listening(node_id, &derived_event) {
264 let potential_event = PotentialEvent {
265 node_id: *node_id,
266 name: derived_event,
267 data: data.clone(),
268 layer: *layer,
269 };
270
271 let layout_node = layout.get(*node_id).unwrap();
272 let dom_event = DomEvent::new(
273 potential_event,
274 Some(layout_node.visible_area()),
275 scale_factor,
276 );
277 dom_events.push(dom_event);
278
279 if name.does_bubble() {
282 continue 'event;
283 }
284 }
285
286 let StyleState { background, .. } = &*node.get::<StyleState>().unwrap();
287
288 if background != &Fill::Color(Color::TRANSPARENT) && !name.does_go_through_solid() {
289 child_node = Some(*node_id);
293 }
294 }
295 }
296 }
297
298 dom_events
299}