Skip to main content

ragnarok/
nodes_state.rs

1use rustc_hash::{
2    FxHashMap,
3    FxHashSet,
4};
5
6use crate::{
7    EmmitableEvent,
8    EventsMeasurer,
9    NameOfEvent,
10    NodeKey,
11    PotentialEvent,
12    SourceEvent,
13};
14
15/// [`NodesState`] stores the nodes states given incoming events.
16pub struct NodesState<Key: NodeKey> {
17    pressed_nodes: FxHashSet<Key>,
18    hovered_nodes: FxHashSet<Key>,
19    visible_nodes: FxHashSet<Key>,
20    entered_node: Option<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            visible_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        let source_press_event = source_events.iter().any(|e| e.is_pressed());
57
58        #[allow(unused_variables)]
59        self.pressed_nodes.retain(|node_key| {
60            let emmitable_press_event = emmitable_events
61                .iter()
62                .any(|event| event.name().is_pressed() && &event.key() == node_key);
63
64            // A press occurred but not on this node, so it's no longer pressed
65            if !emmitable_press_event && source_press_event {
66                #[cfg(debug_assertions)]
67                tracing::info!("Unmarked as pressed {:?}", node_key);
68
69                return false;
70            }
71
72            true
73        });
74
75        let source_movement_event = source_events
76            .iter()
77            .find(|e| e.is_moved() || e.is_touch_released());
78        let mut removed_from_hovered = FxHashSet::default();
79
80        self.hovered_nodes.retain(|node_key| {
81            let Some(area) = events_measurer.try_area_of(node_key) else {
82                removed_from_hovered.insert(*node_key);
83                return false;
84            };
85
86            let cursor_still_inside = source_movement_event
87                .and_then(|e| e.try_location())
88                .is_none_or(|cursor| events_measurer.is_point_inside(node_key, cursor));
89
90            if cursor_still_inside {
91                return true;
92            }
93
94            // Cursor moved outside this node, emit leave events
95            let source_event = source_movement_event.unwrap();
96            for derived_event in Name::new_leave().get_derived_events() {
97                if events_measurer.is_listening_to(node_key, &derived_event) {
98                    collateral_emmitable_events.push(events_measurer.new_emmitable_event(
99                        *node_key,
100                        derived_event,
101                        source_event.clone(),
102                        Some(area),
103                    ));
104                }
105            }
106
107            #[cfg(debug_assertions)]
108            tracing::info!("Unmarked as hovered {:?}", node_key);
109
110            removed_from_hovered.insert(*node_key);
111
112            false
113        });
114
115        // Emit exclusive leave when the deepest node changes
116        // but the old node is still hovered (otherwise the regular leave covers it).
117        if let Some(source_event) = source_movement_event {
118            let new_deepest = emmitable_events
119                .iter()
120                .find(|e| e.name().is_exclusive_enter())
121                .map(|e| e.key());
122
123            if let Some(old_entered) = self.entered_node {
124                let deepest_changed = new_deepest != Some(old_entered);
125                let still_hovered = !removed_from_hovered.contains(&old_entered);
126
127                if deepest_changed && still_hovered {
128                    let exclusive_leave = Name::new_exclusive_leave();
129                    if events_measurer.is_listening_to(&old_entered, &exclusive_leave)
130                        && let Some(area) = events_measurer.try_area_of(&old_entered)
131                    {
132                        collateral_emmitable_events.push(events_measurer.new_emmitable_event(
133                            old_entered,
134                            exclusive_leave,
135                            source_event.clone(),
136                            Some(area),
137                        ));
138                    }
139                }
140            }
141        }
142
143        collateral_emmitable_events
144    }
145
146    pub(crate) fn filter_emmitable_events<
147        Emmitable: EmmitableEvent<Key = Key, Name = Name>,
148        Name: NameOfEvent,
149    >(
150        &mut self,
151        emmitable_events: &mut Vec<Emmitable>,
152    ) {
153        let entered_node = emmitable_events
154            .iter()
155            .rev()
156            .find(|e| e.name().is_moved() || e.name().is_exclusive_enter())
157            .map(|e| e.key());
158
159        emmitable_events.retain(|ev| match ev.name() {
160            // Deduplicate exclusive enter against `entered_node`
161            _ if ev.name().is_exclusive_enter() => {
162                entered_node.as_ref() == Some(&ev.key()) && entered_node != self.entered_node
163            }
164            // Deduplicate non-exclusive enter against `hovered_nodes`
165            _ if ev.name().is_enter() => !self.hovered_nodes.contains(&ev.key()),
166            // Only emit release events for already-pressed nodes
167            _ if ev.name().is_released() => self.pressed_nodes.contains(&ev.key()),
168            _ => true,
169        });
170
171        self.entered_node = entered_node;
172    }
173
174    /// Create the nodes states given the [PotentialEvent]s.
175    pub fn create_update<Name: NameOfEvent, Source: SourceEvent>(
176        &self,
177        events_measurer: &impl EventsMeasurer<Key = Key, Name = Name>,
178        potential_events: &PotentialEvents<Key, Name, Source>,
179    ) -> NodesStatesUpdate<Key> {
180        let mut hovered_nodes = FxHashSet::default();
181        let mut pressed_nodes = FxHashSet::default();
182
183        for events in potential_events.values() {
184            let mut child_node: Option<Key> = None;
185
186            for PotentialEvent { node_key, name, .. } in events.iter().rev() {
187                if let Some(child_node) = child_node
188                    && !events_measurer.is_node_parent_of(&child_node, *node_key)
189                {
190                    continue;
191                }
192
193                // If the background isn't transparent,
194                // we must make sure that next nodes are parent of it.
195                // This only matters for events that don't go through solids (e.g. cursor click events)
196                if !events_measurer.is_node_transparent(node_key) && !name.does_go_through_solid() {
197                    child_node = Some(*node_key);
198                }
199
200                match name {
201                    name if name.is_moved() => {
202                        hovered_nodes.insert(*node_key);
203
204                        #[cfg(debug_assertions)]
205                        tracing::info!("Marked as hovered {:?}", node_key);
206                    }
207                    name if name.is_pressed() => {
208                        pressed_nodes.insert(*node_key);
209
210                        #[cfg(debug_assertions)]
211                        tracing::info!("Marked as pressed {:?}", node_key);
212                    }
213                    _ => {}
214                }
215            }
216        }
217
218        NodesStatesUpdate {
219            pressed_nodes,
220            hovered_nodes,
221        }
222    }
223
224    /// Apply the given [NodesStatesUpdate], extending the cached hovered/pressed nodes.
225    pub fn apply_update(&mut self, update: NodesStatesUpdate<Key>) {
226        self.hovered_nodes.extend(update.hovered_nodes);
227        self.pressed_nodes.extend(update.pressed_nodes);
228    }
229
230    pub fn is_hovered(&self, key: Key) -> bool {
231        self.hovered_nodes.contains(&key)
232    }
233
234    pub fn is_pressed(&self, key: Key) -> bool {
235        self.pressed_nodes.contains(&key)
236    }
237
238    pub fn is_visible(&self, key: Key) -> bool {
239        self.visible_nodes.contains(&key)
240    }
241
242    /// Replace the visible nodes with the result of a new visibility pass.
243    pub fn set_visible_nodes(&mut self, visible_nodes: FxHashSet<Key>) {
244        self.visible_nodes = visible_nodes;
245    }
246}
247
248#[derive(Clone, Debug, PartialEq)]
249pub struct NodesStatesUpdate<Key: NodeKey> {
250    pressed_nodes: FxHashSet<Key>,
251    hovered_nodes: FxHashSet<Key>,
252}
253
254impl<Key: NodeKey> Default for NodesStatesUpdate<Key> {
255    fn default() -> Self {
256        Self {
257            pressed_nodes: FxHashSet::default(),
258            hovered_nodes: FxHashSet::default(),
259        }
260    }
261}
262
263impl<Key: NodeKey> NodesStatesUpdate<Key> {
264    /// Discard the state of a given [NodeKey] and [NameOfEvent] in this [NodesStatesUpdate].
265    pub fn discard<Name: NameOfEvent>(&mut self, name: &Name, node_key: &Key) {
266        match name {
267            _ if name.is_moved() => {
268                self.hovered_nodes.remove(node_key);
269            }
270            _ if name.is_pressed() => {
271                self.pressed_nodes.remove(node_key);
272            }
273            _ => {}
274        }
275    }
276}