ragnarok/
nodes_state.rs

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