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}
22
23impl<Key: NodeKey> Default for NodesState<Key> {
24    fn default() -> Self {
25        Self {
26            pressed_nodes: FxHashSet::default(),
27            hovered_nodes: FxHashSet::default(),
28        }
29    }
30}
31
32pub type PotentialEvents<Key, Name, Source> =
33    FxHashMap<Name, Vec<PotentialEvent<Key, Name, Source>>>;
34
35impl<Key: NodeKey> NodesState<Key> {
36    /// Retain or not the states of the nodes given the [EmmitableEvent]s and based on the sideffects of these removals
37    /// a set of [PotentialEvent]s are returned.
38    pub(crate) fn retain_states<
39        Emmitable: EmmitableEvent<Key = Key, Name = Name>,
40        Name: NameOfEvent,
41        Source: SourceEvent,
42    >(
43        &mut self,
44        events_measurer: &impl EventsMeasurer<
45            Key = Key,
46            Name = Name,
47            Emmitable = Emmitable,
48            Source = Source,
49        >,
50        emmitable_events: &[Emmitable],
51        source_events: &[Source],
52    ) -> Vec<Emmitable> {
53        let mut collateral_emmitable_events = Vec::default();
54
55        // Any press event at all
56        let source_press_event = source_events.iter().any(|e| e.is_pressed());
57
58        // Pressed Nodes
59        #[allow(unused_variables)]
60        self.pressed_nodes.retain(|node_key| {
61            // Check if a Tree event that presses this Node will get emitted
62            let emmitable_press_event = emmitable_events
63                .iter()
64                .any(|event| event.name().is_pressed() && &event.key() == node_key);
65
66            // If there has been a mouse press but a Tree event was not emitted to this node, then we safely assume
67            // the user does no longer want to press this Node
68            if !emmitable_press_event && source_press_event {
69                #[cfg(debug_assertions)]
70                tracing::info!("Unmarked as pressed {:?}", node_key);
71
72                // Remove the node from the list of pressed nodes
73                return false;
74            }
75
76            true
77        });
78
79        // Any movement event at all
80        let source_movement_event = source_events.iter().find(|e| e.is_moved());
81
82        // Hovered Nodes
83        self.hovered_nodes.retain(|node_key| {
84            // Check if a Tree event that moves the cursor in this Node will get emitted
85            let emmitable_movement_event = emmitable_events.iter().any(|event| {
86                (event.name().is_moved() || event.name().is_enter()) && &event.key() == node_key
87            });
88
89            if !emmitable_movement_event {
90                // If there has been a mouse movement but a Tree event was not emitted to this node, then we safely assume
91                // the user does no longer want to hover this Node
92                if let Some(source_event) = source_movement_event {
93                    if let Some(area) = events_measurer.try_area_of(node_key) {
94                        // Emit a leave event as the cursor was moved outside the Node bounds
95                        let event = Name::new_leave();
96                        for derived_event in event.get_derived_events() {
97                            let is_node_listening =
98                                events_measurer.is_listening_to(node_key, &derived_event);
99                            if is_node_listening {
100                                collateral_emmitable_events.push(
101                                    events_measurer.new_emmitable_event(
102                                        *node_key,
103                                        derived_event,
104                                        source_event.clone(),
105                                        Some(area),
106                                    ),
107                                );
108                            }
109                        }
110
111                        #[cfg(debug_assertions)]
112                        tracing::info!("Unmarked as hovered {:?}", node_key);
113                    }
114
115                    // Remove the node from the list of hovered nodes
116                    return false;
117                }
118            }
119            true
120        });
121
122        collateral_emmitable_events
123    }
124
125    pub(crate) fn filter_emmitable_events<
126        Emmitable: EmmitableEvent<Key = Key, Name = Name>,
127        Name: NameOfEvent,
128    >(
129        &self,
130        emmitable_events: &mut Vec<Emmitable>,
131    ) {
132        emmitable_events.retain(|ev| {
133            match ev.name() {
134                // Only let through enter events when the node was not hovered
135                _ if ev.name().is_enter() => !self.hovered_nodes.contains(&ev.key()),
136
137                // Only let through release events when the node was already pressed
138                _ if ev.name().is_released() => self.pressed_nodes.contains(&ev.key()),
139
140                _ => true,
141            }
142        });
143    }
144
145    /// Create the nodes states given the [PotentialEvent]s.
146    pub fn create_update<
147        Emmitable: EmmitableEvent<Key = Key, Name = Name>,
148        Name: NameOfEvent,
149        Source: SourceEvent,
150    >(
151        &self,
152        events_measurer: &impl EventsMeasurer<Key = Key, Name = Name>,
153        potential_events: &PotentialEvents<Key, Name, Source>,
154    ) -> NodesStatesUpdate<Key> {
155        let mut hovered_nodes = FxHashSet::default();
156        let mut pressed_nodes = FxHashSet::default();
157
158        // Update the state of the nodes given the new events.
159        for events in potential_events.values() {
160            let mut child_node: Option<Key> = None;
161
162            for PotentialEvent { node_key, name, .. } in events.iter().rev() {
163                if let Some(child_node) = child_node
164                    && !events_measurer.is_node_parent_of(&child_node, *node_key)
165                {
166                    continue;
167                }
168
169                if !events_measurer.is_node_transparent(node_key) && !name.does_go_through_solid() {
170                    // If the background isn't transparent,
171                    // we must make sure that next nodes are parent of it
172                    // This only matters for events that bubble up (e.g. cursor click events)
173                    child_node = Some(*node_key);
174                }
175
176                match name {
177                    // Update hovered nodes state
178                    name if name.is_moved() => {
179                        // Mark the Node as hovered if it wasn't already
180                        hovered_nodes.insert(*node_key);
181
182                        #[cfg(debug_assertions)]
183                        tracing::info!("Marked as hovered {:?}", node_key);
184                    }
185
186                    // Update pressed nodes state
187                    name if name.is_pressed() => {
188                        // Mark the Node as pressed if it wasn't already
189                        pressed_nodes.insert(*node_key);
190
191                        #[cfg(debug_assertions)]
192                        tracing::info!("Marked as pressed {:?}", node_key);
193                    }
194                    _ => {}
195                }
196            }
197        }
198        NodesStatesUpdate {
199            pressed_nodes,
200            hovered_nodes,
201        }
202    }
203
204    /// Apply the given [NodesStatesUpdate] in a way so that only newly hovered/pressed nodes are cached.
205    /// Any discard of nodes in the [NodesStatesUpdate] wont matter here.
206    pub fn apply_update(&mut self, update: NodesStatesUpdate<Key>) {
207        self.hovered_nodes.extend(update.hovered_nodes);
208        self.pressed_nodes.extend(update.pressed_nodes);
209    }
210
211    pub fn is_hovered(&self, key: Key) -> bool {
212        self.hovered_nodes.contains(&key)
213    }
214
215    pub fn is_pressed(&self, key: Key) -> bool {
216        self.pressed_nodes.contains(&key)
217    }
218}
219
220#[derive(Clone, Debug, PartialEq)]
221pub struct NodesStatesUpdate<Key: NodeKey> {
222    pressed_nodes: FxHashSet<Key>,
223    hovered_nodes: FxHashSet<Key>,
224}
225
226impl<Key: NodeKey> Default for NodesStatesUpdate<Key> {
227    fn default() -> Self {
228        Self {
229            pressed_nodes: HashSet::default(),
230            hovered_nodes: HashSet::default(),
231        }
232    }
233}
234
235impl<Key: NodeKey> NodesStatesUpdate<Key> {
236    /// Discard the state of a given [NodeKey] and a [NameOfEvent] in this [NodesStatesUpdate].
237    pub fn discard<Name: NameOfEvent>(&mut self, name: &Name, node_key: &Key) {
238        match name {
239            // Just like a movement makes the node hover, a discard movement also unhovers it
240            _ if name.is_moved() => {
241                self.hovered_nodes.remove(node_key);
242            }
243            _ if name.is_pressed() => {
244                self.pressed_nodes.remove(node_key);
245            }
246            _ => {}
247        }
248    }
249}