freya_core/events/
nodes_state.rs1#![allow(clippy::type_complexity)]
2
3use freya_engine::prelude::Color;
4use freya_native_core::{
5 events::EventName,
6 prelude::NodeImmutable,
7 NodeId,
8};
9use rustc_hash::FxHashMap;
10
11use super::PlatformEventData;
12use crate::{
13 dom::FreyaDOM,
14 events::{
15 is_node_parent_of,
16 DomEvent,
17 PlatformEvent,
18 PotentialEvent,
19 },
20 states::StyleState,
21 types::PotentialEvents,
22 values::Fill,
23};
24
25#[derive(Clone, Debug)]
26struct NodeMetadata {
27 layer: Option<i16>,
28}
29
30#[derive(Default)]
32pub struct NodesState {
33 pressed_nodes: FxHashMap<NodeId, NodeMetadata>,
34 hovered_nodes: FxHashMap<NodeId, NodeMetadata>,
35}
36
37impl NodesState {
38 pub fn process_collateral(
40 &mut self,
41 fdom: &FreyaDOM,
42 potential_events: &PotentialEvents,
43 dom_events: &mut Vec<DomEvent>,
44 events: &[PlatformEvent],
45 ) -> PotentialEvents {
46 let rdom = fdom.rdom();
47 let layout = fdom.layout();
48 let mut potential_collateral_events = PotentialEvents::default();
49
50 let recent_mouse_press_event = any_event_of(events, |e| e.is_pressed());
52
53 #[allow(unused_variables)]
55 self.pressed_nodes.retain(|node_id, _| {
56 let no_desire_to_press = filter_dom_events_by(dom_events, node_id, |e| e.is_pressed());
58
59 if no_desire_to_press && recent_mouse_press_event.is_some() {
62 #[cfg(debug_assertions)]
63 tracing::info!("Unmarked as pressed {:?}", node_id);
64
65 return false;
67 }
68
69 true
70 });
71
72 let recent_mouse_movement_event = any_event_of(events, |e| e.is_moved());
74
75 self.hovered_nodes.retain(|node_id, metadata| {
77 let no_desire_to_hover = filter_dom_events_by(dom_events, node_id, |e| e.is_moved());
79
80 if no_desire_to_hover {
81 if let Some(PlatformEventData::Mouse { cursor, button, .. }) =
84 recent_mouse_movement_event
85 {
86 if layout.get(*node_id).is_some() {
87 let events = potential_collateral_events
88 .entry(EventName::MouseLeave)
89 .or_default();
90
91 events.push(PotentialEvent {
93 node_id: *node_id,
94 layer: metadata.layer,
95 name: EventName::MouseLeave,
96 data: PlatformEventData::Mouse { cursor, button },
97 });
98
99 #[cfg(debug_assertions)]
100 tracing::info!("Unmarked as hovered {:?}", node_id);
101 }
102
103 return false;
105 }
106 }
107 true
108 });
109
110 dom_events.retain(|ev| {
111 match ev.name {
112 _ if ev.name.is_enter() => !self.hovered_nodes.contains_key(&ev.node_id),
114
115 _ if ev.name.is_released() => self.pressed_nodes.contains_key(&ev.node_id),
117
118 _ => true,
119 }
120 });
121
122 for events in potential_events.values() {
124 let mut child_node: Option<NodeId> = None;
125
126 for PotentialEvent {
127 node_id,
128 name,
129 layer,
130 ..
131 } in events.iter().rev()
132 {
133 if let Some(child_node) = child_node {
134 if !is_node_parent_of(rdom, child_node, *node_id) {
135 continue;
136 }
137 }
138
139 let node = rdom.get(*node_id).unwrap();
140 let StyleState { background, .. } = &*node.get::<StyleState>().unwrap();
141
142 if background != &Fill::Color(Color::TRANSPARENT) && !name.does_go_through_solid() {
143 child_node = Some(*node_id);
147 }
148
149 match name {
150 name if name.is_hovered() => {
152 self.hovered_nodes.entry(*node_id).or_insert_with(|| {
154 #[cfg(debug_assertions)]
155 tracing::info!("Marked as hovered {:?}", node_id);
156
157 NodeMetadata { layer: *layer }
158 });
159 }
160
161 name if name.is_pressed() => {
163 self.pressed_nodes.entry(*node_id).or_insert_with(|| {
165 #[cfg(debug_assertions)]
166 tracing::info!("Marked as pressed {:?}", node_id);
167
168 NodeMetadata { layer: *layer }
169 });
170 }
171 _ => {}
172 }
173 }
174 }
175
176 for events in potential_collateral_events.values_mut() {
178 events.sort_by(|left, right| left.layer.cmp(&right.layer))
179 }
180
181 potential_collateral_events
182 }
183}
184
185fn any_event_of(
186 events: &[PlatformEvent],
187 filter: impl Fn(EventName) -> bool,
188) -> Option<PlatformEventData> {
189 events
190 .iter()
191 .find_map(|event| {
192 if filter(event.name) {
193 Some(&event.data)
194 } else {
195 None
196 }
197 })
198 .cloned()
199}
200
201fn filter_dom_events_by(
202 events_to_emit: &[DomEvent],
203 node_id: &NodeId,
204 filter: impl Fn(EventName) -> bool,
205) -> bool {
206 events_to_emit
207 .iter()
208 .find_map(|event| {
209 if filter(event.name) && &event.node_id == node_id {
210 Some(false)
211 } else {
212 None
213 }
214 })
215 .unwrap_or(true)
216}