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 push_hit_test(&mut self, input_id: InputPointId, hit_test: FullHitTest) {
53 let history = self
54 .hover_histories
55 .entry(input_id)
56 .or_insert_with(|| VecDeque::with_capacity(MAX_HOVER_HISTORY));
57
58 history.push_front(hit_test);
60
61 if history.len() > MAX_HOVER_HISTORY {
63 history.pop_back();
64 }
65 }
66
67 pub fn remove_input_point(&mut self, input_id: &InputPointId) {
69 self.hover_histories.remove(input_id);
70 }
71
72 pub fn get_current(&self, input_id: &InputPointId) -> Option<&FullHitTest> {
76 self.hover_histories
77 .get(input_id)
78 .and_then(|history| history.front())
79 }
80
81 pub fn get_current_mouse(&self) -> Option<&FullHitTest> {
83 self.get_current(&InputPointId::Mouse)
84 }
85
86 pub fn get_frame(&self, input_id: &InputPointId, frames_ago: usize) -> Option<&FullHitTest> {
91 self.hover_histories
92 .get(input_id)
93 .and_then(|history| history.get(frames_ago))
94 }
95
96 pub fn get_history(&self, input_id: &InputPointId) -> Option<&VecDeque<FullHitTest>> {
98 self.hover_histories.get(input_id)
99 }
100
101 pub fn get_active_input_points(&self) -> Vec<InputPointId> {
103 self.hover_histories.keys().copied().collect()
104 }
105
106 pub fn frame_count(&self, input_id: &InputPointId) -> usize {
108 self.hover_histories
109 .get(input_id)
110 .map(|h| h.len())
111 .unwrap_or(0)
112 }
113
114 pub fn clear(&mut self) {
116 self.hover_histories.clear();
117 }
118
119 pub fn clear_input_point(&mut self, input_id: &InputPointId) {
121 if let Some(history) = self.hover_histories.get_mut(input_id) {
122 history.clear();
123 }
124 }
125
126 pub fn has_sufficient_history_for_gestures(&self, input_id: &InputPointId) -> bool {
131 self.frame_count(input_id) >= 2
132 }
133
134 pub fn any_has_sufficient_history_for_gestures(&self) -> bool {
136 self.hover_histories
137 .iter()
138 .any(|(_, history)| history.len() >= 2)
139 }
140
141 pub fn current_hover_node(&self) -> Option<azul_core::id::NodeId> {
146 let current = self.get_current_mouse()?;
147 let dom_id = azul_core::dom::DomId { inner: 0 };
148 let ht = current.hovered_nodes.get(&dom_id)?;
149 ht.regular_hit_test_nodes.keys().last().copied()
150 }
151
152 pub fn previous_hover_node(&self) -> Option<azul_core::id::NodeId> {
157 let history = self.hover_histories.get(&InputPointId::Mouse)?;
158 let previous = history.get(1)?; let dom_id = azul_core::dom::DomId { inner: 0 };
160 let ht = previous.hovered_nodes.get(&dom_id)?;
161 ht.regular_hit_test_nodes.keys().last().copied()
162 }
163
164 pub fn remap_node_ids(
170 &mut self,
171 dom_id: azul_core::dom::DomId,
172 node_id_map: &std::collections::BTreeMap<azul_core::id::NodeId, azul_core::id::NodeId>,
173 ) {
174 for history in self.hover_histories.values_mut() {
175 for hit_test in history.iter_mut() {
176 if let Some(ht) = hit_test.hovered_nodes.get_mut(&dom_id) {
177 let old_regular: Vec<_> = ht.regular_hit_test_nodes.keys().cloned().collect();
179 let mut new_regular = std::collections::BTreeMap::new();
180 for old_nid in old_regular {
181 if let Some(&new_nid) = node_id_map.get(&old_nid) {
182 if let Some(item) = ht.regular_hit_test_nodes.remove(&old_nid) {
183 new_regular.insert(new_nid, item);
184 }
185 }
186 }
187 ht.regular_hit_test_nodes = new_regular;
188
189 let old_scroll: Vec<_> = ht.scroll_hit_test_nodes.keys().cloned().collect();
191 let mut new_scroll = std::collections::BTreeMap::new();
192 for old_nid in old_scroll {
193 if let Some(&new_nid) = node_id_map.get(&old_nid) {
194 if let Some(item) = ht.scroll_hit_test_nodes.remove(&old_nid) {
195 new_scroll.insert(new_nid, item);
196 }
197 }
198 }
199 ht.scroll_hit_test_nodes = new_scroll;
200
201 let old_cursor: Vec<_> = ht.cursor_hit_test_nodes.keys().cloned().collect();
203 let mut new_cursor = std::collections::BTreeMap::new();
204 for old_nid in old_cursor {
205 if let Some(&new_nid) = node_id_map.get(&old_nid) {
206 if let Some(item) = ht.cursor_hit_test_nodes.remove(&old_nid) {
207 new_cursor.insert(new_nid, item);
208 }
209 }
210 }
211 ht.cursor_hit_test_nodes = new_cursor;
212
213 let old_sb: Vec<_> = ht.scrollbar_hit_test_nodes.keys().cloned().collect();
215 let mut new_sb = std::collections::BTreeMap::new();
216 for old_key in old_sb {
217 let new_key = remap_scrollbar_hit_id(&old_key, dom_id, node_id_map);
218 if let Some(item) = ht.scrollbar_hit_test_nodes.remove(&old_key) {
219 new_sb.insert(new_key, item);
220 }
221 }
222 ht.scrollbar_hit_test_nodes = new_sb;
223 }
224 }
225 }
226 }
227}
228
229impl Default for HoverManager {
230 fn default() -> Self {
231 Self::new()
232 }
233}
234
235fn remap_scrollbar_hit_id(
238 id: &azul_core::hit_test::ScrollbarHitId,
239 dom_id: azul_core::dom::DomId,
240 node_id_map: &std::collections::BTreeMap<azul_core::id::NodeId, azul_core::id::NodeId>,
241) -> azul_core::hit_test::ScrollbarHitId {
242 use azul_core::hit_test::ScrollbarHitId;
243 match id {
244 ScrollbarHitId::VerticalTrack(d, n) if *d == dom_id => {
245 ScrollbarHitId::VerticalTrack(*d, *node_id_map.get(n).unwrap_or(n))
246 }
247 ScrollbarHitId::VerticalThumb(d, n) if *d == dom_id => {
248 ScrollbarHitId::VerticalThumb(*d, *node_id_map.get(n).unwrap_or(n))
249 }
250 ScrollbarHitId::HorizontalTrack(d, n) if *d == dom_id => {
251 ScrollbarHitId::HorizontalTrack(*d, *node_id_map.get(n).unwrap_or(n))
252 }
253 ScrollbarHitId::HorizontalThumb(d, n) if *d == dom_id => {
254 ScrollbarHitId::HorizontalThumb(*d, *node_id_map.get(n).unwrap_or(n))
255 }
256 other => other.clone(),
257 }
258}