ragnarok/
nodes_state.rs

1use std::collections::HashSet;
2
3use rustc_hash::{
4    FxHashMap,
5    FxHashSet,
6};
7
8use crate::{
9    EmmitableEvent,
10    EventsMeasurer,
11    NameOfEvent,
12    NodeKey,
13    PotentialEvent,
14    SourceEvent,
15};
16
17/// [`NodesState`] stores the nodes states given incoming events.
18pub struct NodesState<Key: NodeKey> {
19    pressed_nodes: FxHashSet<Key>,
20    hovered_nodes: FxHashSet<Key>,
21    entered_node: Option<Key>,
22}
23
24impl<Key: NodeKey> Default for NodesState<Key> {
25    fn default() -> Self {
26        Self {
27            pressed_nodes: FxHashSet::default(),
28            hovered_nodes: FxHashSet::default(),
29            entered_node: None,
30        }
31    }
32}
33
34pub type PotentialEvents<Key, Name, Source> =
35    FxHashMap<Name, Vec<PotentialEvent<Key, Name, Source>>>;
36
37impl<Key: NodeKey> NodesState<Key> {
38    /// Retain node states given the [EmmitableEvent]s, emitting leave events as side effects.
39    pub(crate) fn retain_states<
40        Emmitable: EmmitableEvent<Key = Key, Name = Name>,
41        Name: NameOfEvent,
42        Source: SourceEvent,
43    >(
44        &mut self,
45        events_measurer: &impl EventsMeasurer<
46            Key = Key,
47            Name = Name,
48            Emmitable = Emmitable,
49            Source = Source,
50        >,
51        emmitable_events: &[Emmitable],
52        source_events: &[Source],
53    ) -> Vec<Emmitable> {
54        let mut collateral_emmitable_events = Vec::default();
55
56        // Any press event at all
57        let source_press_event = source_events.iter().any(|e| e.is_pressed());
58
59        // Pressed Nodes
60        #[allow(unused_variables)]
61        self.pressed_nodes.retain(|node_key| {
62            // Check if a Tree event that presses this Node will get emitted
63            let emmitable_press_event = emmitable_events
64                .iter()
65                .any(|event| event.name().is_pressed() && &event.key() == node_key);
66
67            // If there has been a mouse press but a Tree event was not emitted to this node, then we safely assume
68            // the user does no longer want to press this Node
69            if !emmitable_press_event && source_press_event {
70                #[cfg(debug_assertions)]
71                tracing::info!("Unmarked as pressed {:?}", node_key);
72
73                // Remove the node from the list of pressed nodes
74                return false;
75            }
76
77            true
78        });
79
80        // Any movement event at all
81        let source_movement_event = source_events.iter().find(|e| e.is_moved());
82        let mut removed_from_hovered = FxHashSet::default();
83
84        // Hovered Nodes
85        self.hovered_nodes.retain(|node_key| {
86            // Check if a Tree event that moves the cursor in this Node will get emitted
87            let emmitable_movement_event = emmitable_events.iter().any(|event| {
88                (event.name().is_moved() || event.name().is_enter()) && &event.key() == node_key
89            });
90
91            if !emmitable_movement_event {
92                // If there has been a mouse movement but a Tree event was not emitted to this node, then we safely assume
93                // the user does no longer want to hover this Node
94                if let Some(source_event) = source_movement_event {
95                    if let Some(area) = events_measurer.try_area_of(node_key) {
96                        // Emit a leave event as the cursor was moved outside the Node bounds
97                        let event = Name::new_leave();
98                        for derived_event in event.get_derived_events() {
99                            let is_node_listening =
100                                events_measurer.is_listening_to(node_key, &derived_event);
101                            if is_node_listening {
102                                collateral_emmitable_events.push(
103                                    events_measurer.new_emmitable_event(
104                                        *node_key,
105                                        derived_event,
106                                        source_event.clone(),
107                                        Some(area),
108                                    ),
109                                );
110                            }
111                        }
112
113                        #[cfg(debug_assertions)]
114                        tracing::info!("Unmarked as hovered {:?}", node_key);
115                    }
116
117                    removed_from_hovered.insert(*node_key);
118
119                    return false;
120                }
121            }
122            true
123        });
124
125        // Emit exclusive leave when the deepest node under the cursor changes,
126        // but only if the old node is still hovered (otherwise the regular leave covers it).
127        if source_movement_event.is_some() {
128            let new_deepest = emmitable_events
129                .iter()
130                .find(|e| e.name().is_exclusive_enter())
131                .map(|e| e.key());
132
133            if let Some(old_entered) = self.entered_node {
134                let deepest_changed = new_deepest != Some(old_entered);
135                let still_hovered = !removed_from_hovered.contains(&old_entered);
136
137                if deepest_changed
138                    && still_hovered
139                    && let Some(source_event) = source_movement_event
140                {
141                    let exclusive_leave = Name::new_exclusive_leave();
142                    let is_node_listening =
143                        events_measurer.is_listening_to(&old_entered, &exclusive_leave);
144                    if is_node_listening
145                        && let Some(area) = events_measurer.try_area_of(&old_entered)
146                    {
147                        collateral_emmitable_events.push(events_measurer.new_emmitable_event(
148                            old_entered,
149                            exclusive_leave,
150                            source_event.clone(),
151                            Some(area),
152                        ));
153                    }
154                }
155            }
156        }
157
158        collateral_emmitable_events
159    }
160
161    pub(crate) fn filter_emmitable_events<
162        Emmitable: EmmitableEvent<Key = Key, Name = Name>,
163        Name: NameOfEvent,
164    >(
165        &self,
166        emmitable_events: &mut Vec<Emmitable>,
167    ) {
168        emmitable_events.retain(|ev| {
169            match ev.name() {
170                // Exclusive enter events deduplicated against `entered_node`.
171                _ if ev.name().is_exclusive_enter() => {
172                    self.entered_node.as_ref() != Some(&ev.key())
173                }
174
175                // Non-exclusive enter events deduplicated against `hovered_nodes`.
176                _ if ev.name().is_enter() => !self.hovered_nodes.contains(&ev.key()),
177
178                // Only let through release events when the node was already pressed
179                _ if ev.name().is_released() => self.pressed_nodes.contains(&ev.key()),
180
181                _ => true,
182            }
183        });
184    }
185
186    /// Create the nodes states given the [PotentialEvent]s.
187    pub fn create_update<
188        Emmitable: EmmitableEvent<Key = Key, Name = Name>,
189        Name: NameOfEvent,
190        Source: SourceEvent,
191    >(
192        &self,
193        events_measurer: &impl EventsMeasurer<Key = Key, Name = Name>,
194        potential_events: &PotentialEvents<Key, Name, Source>,
195    ) -> NodesStatesUpdate<Key> {
196        let mut hovered_nodes = FxHashSet::default();
197        let mut pressed_nodes = FxHashSet::default();
198        let mut entered_node: Option<Key> = None;
199
200        // Update the state of the nodes given the new events.
201        for events in potential_events.values() {
202            let mut child_node: Option<Key> = None;
203
204            for PotentialEvent { node_key, name, .. } in events.iter().rev() {
205                if let Some(child_node) = child_node
206                    && !events_measurer.is_node_parent_of(&child_node, *node_key)
207                {
208                    continue;
209                }
210
211                if !events_measurer.is_node_transparent(node_key) && !name.does_go_through_solid() {
212                    // If the background isn't transparent,
213                    // we must make sure that next nodes are parent of it
214                    // This only matters for events that bubble up (e.g. cursor click events)
215                    child_node = Some(*node_key);
216                }
217
218                match name {
219                    // Update hovered nodes state
220                    name if name.is_moved() => {
221                        // Mark the Node as hovered if it wasn't already
222                        hovered_nodes.insert(*node_key);
223
224                        if entered_node.is_none()
225                            && events_measurer
226                                .is_listening_to(node_key, &Name::new_exclusive_enter())
227                        {
228                            entered_node = Some(*node_key);
229                        }
230
231                        #[cfg(debug_assertions)]
232                        tracing::info!("Marked as hovered {:?}", node_key);
233                    }
234
235                    // Update pressed nodes state
236                    name if name.is_pressed() => {
237                        // Mark the Node as pressed if it wasn't already
238                        pressed_nodes.insert(*node_key);
239
240                        #[cfg(debug_assertions)]
241                        tracing::info!("Marked as pressed {:?}", node_key);
242                    }
243                    _ => {}
244                }
245            }
246        }
247        NodesStatesUpdate {
248            pressed_nodes,
249            hovered_nodes,
250            entered_node,
251        }
252    }
253
254    /// Apply the given [NodesStatesUpdate], extending the cached hovered/pressed nodes.
255    pub fn apply_update(&mut self, update: NodesStatesUpdate<Key>) {
256        self.hovered_nodes.extend(update.hovered_nodes);
257        self.pressed_nodes.extend(update.pressed_nodes);
258
259        if let Some(entered_node) = self.entered_node
260            && !self.hovered_nodes.contains(&entered_node)
261        {
262            self.entered_node = None;
263        }
264
265        if update.entered_node.is_some() {
266            self.entered_node = update.entered_node;
267        }
268    }
269
270    pub fn is_hovered(&self, key: Key) -> bool {
271        self.hovered_nodes.contains(&key)
272    }
273
274    pub fn is_pressed(&self, key: Key) -> bool {
275        self.pressed_nodes.contains(&key)
276    }
277}
278
279#[derive(Clone, Debug, PartialEq)]
280pub struct NodesStatesUpdate<Key: NodeKey> {
281    pressed_nodes: FxHashSet<Key>,
282    hovered_nodes: FxHashSet<Key>,
283    entered_node: Option<Key>,
284}
285
286impl<Key: NodeKey> Default for NodesStatesUpdate<Key> {
287    fn default() -> Self {
288        Self {
289            pressed_nodes: HashSet::default(),
290            hovered_nodes: HashSet::default(),
291            entered_node: None,
292        }
293    }
294}
295
296impl<Key: NodeKey> NodesStatesUpdate<Key> {
297    /// Discard the state of a given [NodeKey] and a [NameOfEvent] in this [NodesStatesUpdate].
298    pub fn discard<Name: NameOfEvent>(&mut self, name: &Name, node_key: &Key) {
299        match name {
300            // Just like a movement makes the node hover, a discard movement also unhovers it
301            _ if name.is_moved() => {
302                self.hovered_nodes.remove(node_key);
303            }
304            _ if name.is_pressed() => {
305                self.pressed_nodes.remove(node_key);
306            }
307            _ => {}
308        }
309    }
310}