azul_layout/managers/
hover.rs1use std::collections::{BTreeMap, VecDeque};
9
10use crate::hit_test::FullHitTest;
11
12const MAX_HOVER_HISTORY: usize = 5;
14
15#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub enum InputPointId {
18 Mouse,
20 Touch(u64),
22}
23
24#[derive(Debug, Clone, PartialEq)]
34pub struct HoverManager {
35 hover_histories: BTreeMap<InputPointId, VecDeque<FullHitTest>>,
38}
39
40impl HoverManager {
41 pub fn new() -> Self {
43 Self {
44 hover_histories: BTreeMap::new(),
45 }
46 }
47
48 pub fn debug_counts(&self) -> (usize, usize) {
51 let points = self.hover_histories.len();
52 let total: usize = self.hover_histories.values().map(|h| h.len()).sum();
53 (points, total)
54 }
55
56 pub fn push_hit_test(&mut self, input_id: InputPointId, hit_test: FullHitTest) {
61 let history = self
62 .hover_histories
63 .entry(input_id)
64 .or_insert_with(|| VecDeque::with_capacity(MAX_HOVER_HISTORY));
65
66 history.push_front(hit_test);
68
69 if history.len() > MAX_HOVER_HISTORY {
71 history.pop_back();
72 }
73 }
74
75 pub fn remove_input_point(&mut self, input_id: &InputPointId) {
77 self.hover_histories.remove(input_id);
78 }
79
80 pub fn get_current(&self, input_id: &InputPointId) -> Option<&FullHitTest> {
84 self.hover_histories
85 .get(input_id)
86 .and_then(|history| history.front())
87 }
88
89 pub fn get_current_mouse(&self) -> Option<&FullHitTest> {
91 self.get_current(&InputPointId::Mouse)
92 }
93
94 pub fn get_frame(&self, input_id: &InputPointId, frames_ago: usize) -> Option<&FullHitTest> {
99 self.hover_histories
100 .get(input_id)
101 .and_then(|history| history.get(frames_ago))
102 }
103
104 pub fn get_history(&self, input_id: &InputPointId) -> Option<&VecDeque<FullHitTest>> {
106 self.hover_histories.get(input_id)
107 }
108
109 pub fn get_active_input_points(&self) -> Vec<InputPointId> {
111 self.hover_histories.keys().copied().collect()
112 }
113
114 pub fn frame_count(&self, input_id: &InputPointId) -> usize {
116 self.hover_histories
117 .get(input_id)
118 .map(|h| h.len())
119 .unwrap_or(0)
120 }
121
122 pub fn clear(&mut self) {
124 self.hover_histories.clear();
125 }
126
127 pub fn clear_input_point(&mut self, input_id: &InputPointId) {
129 if let Some(history) = self.hover_histories.get_mut(input_id) {
130 history.clear();
131 }
132 }
133
134 pub fn has_sufficient_history_for_gestures(&self, input_id: &InputPointId) -> bool {
139 self.frame_count(input_id) >= 2
140 }
141
142 pub fn any_has_sufficient_history_for_gestures(&self) -> bool {
144 self.hover_histories
145 .iter()
146 .any(|(_, history)| history.len() >= 2)
147 }
148
149 pub fn current_hover_node(&self) -> Option<azul_core::id::NodeId> {
156 let current = self.get_current_mouse()?;
157 let dom_id = azul_core::dom::DomId { inner: 0 };
158 let ht = current.hovered_nodes.get(&dom_id)?;
159 ht.regular_hit_test_nodes.keys().last().copied()
160 }
161
162 pub fn previous_hover_node(&self) -> Option<azul_core::id::NodeId> {
169 let history = self.hover_histories.get(&InputPointId::Mouse)?;
170 let previous = history.get(1)?; let dom_id = azul_core::dom::DomId { inner: 0 };
172 let ht = previous.hovered_nodes.get(&dom_id)?;
173 ht.regular_hit_test_nodes.keys().last().copied()
174 }
175
176 pub fn remap_node_ids(
182 &mut self,
183 dom_id: azul_core::dom::DomId,
184 node_id_map: &std::collections::BTreeMap<azul_core::id::NodeId, azul_core::id::NodeId>,
185 ) {
186 for history in self.hover_histories.values_mut() {
187 for hit_test in history.iter_mut() {
188 if let Some(ht) = hit_test.hovered_nodes.get_mut(&dom_id) {
189 remap_btreemap(&mut ht.regular_hit_test_nodes, node_id_map);
190 remap_btreemap(&mut ht.scroll_hit_test_nodes, node_id_map);
191 remap_btreemap(&mut ht.cursor_hit_test_nodes, node_id_map);
192
193 let old_sb: Vec<_> = ht.scrollbar_hit_test_nodes.keys().cloned().collect();
195 let mut new_sb = std::collections::BTreeMap::new();
196 for old_key in old_sb {
197 let new_key = remap_scrollbar_hit_id(&old_key, dom_id, node_id_map);
198 if let Some(item) = ht.scrollbar_hit_test_nodes.remove(&old_key) {
199 new_sb.insert(new_key, item);
200 }
201 }
202 ht.scrollbar_hit_test_nodes = new_sb;
203 }
204 }
205 }
206 }
207}
208
209impl Default for HoverManager {
210 fn default() -> Self {
211 Self::new()
212 }
213}
214
215fn remap_btreemap<V>(
218 map: &mut std::collections::BTreeMap<azul_core::id::NodeId, V>,
219 node_id_map: &std::collections::BTreeMap<azul_core::id::NodeId, azul_core::id::NodeId>,
220) {
221 let old_keys: Vec<_> = map.keys().cloned().collect();
222 let mut new_map = std::collections::BTreeMap::new();
223 for old_nid in old_keys {
224 if let Some(&new_nid) = node_id_map.get(&old_nid) {
225 if let Some(item) = map.remove(&old_nid) {
226 new_map.insert(new_nid, item);
227 }
228 }
229 }
230 *map = new_map;
231}
232
233fn remap_scrollbar_hit_id(
236 id: &azul_core::hit_test::ScrollbarHitId,
237 dom_id: azul_core::dom::DomId,
238 node_id_map: &std::collections::BTreeMap<azul_core::id::NodeId, azul_core::id::NodeId>,
239) -> azul_core::hit_test::ScrollbarHitId {
240 use azul_core::hit_test::ScrollbarHitId;
241 match id {
242 ScrollbarHitId::VerticalTrack(d, n) if *d == dom_id => {
243 ScrollbarHitId::VerticalTrack(*d, *node_id_map.get(n).unwrap_or(n))
244 }
245 ScrollbarHitId::VerticalThumb(d, n) if *d == dom_id => {
246 ScrollbarHitId::VerticalThumb(*d, *node_id_map.get(n).unwrap_or(n))
247 }
248 ScrollbarHitId::HorizontalTrack(d, n) if *d == dom_id => {
249 ScrollbarHitId::HorizontalTrack(*d, *node_id_map.get(n).unwrap_or(n))
250 }
251 ScrollbarHitId::HorizontalThumb(d, n) if *d == dom_id => {
252 ScrollbarHitId::HorizontalThumb(*d, *node_id_map.get(n).unwrap_or(n))
253 }
254 other => other.clone(),
255 }
256}