1use std::collections::{HashSet, BTreeMap};
2use crate::{
3 dom::{EventFilter, NotEventFilter, HoverEventFilter, FocusEventFilter, WindowEventFilter},
4 callbacks:: {CallbackInfo, CallbackType, HitTestItem, UpdateScreen},
5 id_tree::NodeId,
6 ui_state::UiState,
7 window::{
8 AcceleratorKey, FullWindowState, CallbacksOfHitTest, DetermineCallbackResult,
9 },
10};
11
12pub fn determine_callbacks(
17 window_state: &mut FullWindowState,
18 hit_test_items: &[HitTestItem],
19 ui_state: &UiState,
20) -> CallbacksOfHitTest {
21
22 use std::collections::BTreeSet;
23
24 let mut needs_hover_redraw = false;
25 let mut needs_hover_relayout = false;
26 let mut nodes_with_callbacks: BTreeMap<NodeId, DetermineCallbackResult> = BTreeMap::new();
27
28 let current_window_events = get_window_events(window_state);
29 let current_hover_events = get_hover_events(¤t_window_events);
30 let current_focus_events = get_focus_events(¤t_hover_events);
31
32 let event_was_mouse_down = current_window_events.contains(&WindowEventFilter::MouseDown);
33 let event_was_mouse_release = current_window_events.contains(&WindowEventFilter::MouseUp);
34 let event_was_mouse_enter = current_window_events.contains(&WindowEventFilter::MouseEnter);
35 let event_was_mouse_leave = current_window_events.contains(&WindowEventFilter::MouseLeave);
36
37 let mut previous_state = Box::new(window_state.clone());
39 previous_state.previous_window_state = None;
40
41 let mut new_hit_node_ids: BTreeMap<NodeId, HitTestItem> = hit_test_items.iter().filter_map(|hit_test_item| {
46 ui_state.tag_ids_to_node_ids
47 .get(&hit_test_item.tag)
48 .map(|node_id| (*node_id, hit_test_item.clone()))
49 }).collect();
50
51 if event_was_mouse_leave {
52 new_hit_node_ids = BTreeMap::new();
53 }
54
55 if event_was_mouse_down || event_was_mouse_release {
57
58 let closest_focus_node = hit_test_items.iter().rev()
60 .find_map(|item| ui_state.tab_index_tags.get(&item.tag))
61 .cloned();
62
63 window_state.focused_node = closest_focus_node.map(|(node_id, _tab_idx)| (ui_state.dom_id.clone(), node_id));
65 }
66
67 macro_rules! insert_only_non_empty_callbacks {
68 ($node_id:expr, $hit_test_item:expr, $normal_hover_callbacks:expr) => ({
69 if !$normal_hover_callbacks.is_empty() {
70 let mut callback_result = nodes_with_callbacks.entry(*$node_id)
71 .or_insert_with(|| DetermineCallbackResult::default());
72
73 let item: Option<HitTestItem> = $hit_test_item;
74 if let Some(hit_test_item) = item {
75 callback_result.hit_test_item = Some(hit_test_item);
76 }
77 callback_result.normal_callbacks.extend($normal_hover_callbacks.into_iter());
78 }
79 })
80 }
81
82 macro_rules! insert_callbacks {(
84 $node_id:expr,
85 $hit_test_item:expr,
86 $hover_callbacks:ident,
87 $current_hover_events:ident,
88 $event_filter:ident
89 ) => ({
90 let mut normal_hover_callbacks = BTreeMap::new();
92
93 if let Some(ui_state_hover_event_filters) = ui_state.$hover_callbacks.get($node_id) {
95 for current_hover_event in &$current_hover_events {
96 if let Some(callback) = ui_state_hover_event_filters.get(current_hover_event) {
97 normal_hover_callbacks.insert(EventFilter::$event_filter(*current_hover_event), callback.0);
98 }
99 }
100 }
101
102 insert_only_non_empty_callbacks!($node_id, $hit_test_item, normal_hover_callbacks);
103 })
104 }
105
106 for (window_node_id, window_callbacks) in &ui_state.window_callbacks {
108 let normal_window_callbacks = window_callbacks.iter()
109 .filter(|(current_window_event, _)| current_window_events.contains(current_window_event))
110 .map(|(current_window_event, callback)| (EventFilter::Window(*current_window_event), callback.0))
111 .collect::<BTreeMap<_, _>>();
112 insert_only_non_empty_callbacks!(window_node_id, None, normal_window_callbacks);
113 }
114
115 for (hover_node_id, hit_test_item) in &new_hit_node_ids {
117 insert_callbacks!(hover_node_id, Some(hit_test_item.clone()), hover_callbacks, current_hover_events, Hover);
118 }
119
120 if let Some(current_focused_node) = &window_state.focused_node {
122 insert_callbacks!(¤t_focused_node.1, None, focus_callbacks, current_focus_events, Focus);
123 }
124
125 let mut focus_received_lost_events: BTreeMap<NodeId, FocusEventFilter> = BTreeMap::new();
128 match (window_state.focused_node.as_ref(), previous_state.focused_node.as_ref()) {
129 (Some((cur_dom_id, cur_node_id)), None) => {
130 if *cur_dom_id == ui_state.dom_id {
131 focus_received_lost_events.insert(*cur_node_id, FocusEventFilter::FocusReceived);
132 }
133 },
134 (None, Some((prev_dom_id, prev_node_id))) => {
135 if *prev_dom_id == ui_state.dom_id {
136 focus_received_lost_events.insert(*prev_node_id, FocusEventFilter::FocusLost);
137 }
138 },
139 (Some(cur), Some(prev)) => {
140 if *cur != *prev {
141 let (cur_dom_id, cur_node_id) = cur;
142 let (prev_dom_id, prev_node_id) = prev;
143 if *cur_dom_id == ui_state.dom_id {
144 focus_received_lost_events.insert(*cur_node_id, FocusEventFilter::FocusReceived);
145 }
146 if *prev_dom_id == ui_state.dom_id {
147 focus_received_lost_events.insert(*prev_node_id, FocusEventFilter::FocusLost);
148 }
149 }
150 }
151 (None, None) => { },
152 }
153
154 for (node_id, focus_event) in &focus_received_lost_events {
156 let current_focus_leave_events = [focus_event.clone()];
157 insert_callbacks!(node_id, None, focus_callbacks, current_focus_leave_events, Focus);
158 }
159
160 let current_dom_id = ui_state.dom_id.clone();
161
162 macro_rules! mouse_enter {
163 ($node_id:expr, $hit_test_item:expr, $event_filter:ident) => ({
164
165 let node_is_focused = window_state.focused_node == Some((current_dom_id.clone(), $node_id));
166
167 let mut normal_callbacks = BTreeMap::new();
169
170 if let Some(ui_state_hover_event_filters) = ui_state.hover_callbacks.get(&$node_id) {
172 if let Some(callback) = ui_state_hover_event_filters.get(&HoverEventFilter::$event_filter) {
173 normal_callbacks.insert(EventFilter::Hover(HoverEventFilter::$event_filter), callback.0);
174 }
175 }
176
177 if node_is_focused {
179 if let Some(ui_state_focus_event_filters) = ui_state.focus_callbacks.get(&$node_id) {
180 if let Some(callback) = ui_state_focus_event_filters.get(&FocusEventFilter::$event_filter) {
181 normal_callbacks.insert(EventFilter::Focus(FocusEventFilter::$event_filter), callback.0);
182 }
183 }
184 }
185
186 if !normal_callbacks.is_empty() {
187
188 let mut callback_result = nodes_with_callbacks.entry($node_id)
189 .or_insert_with(|| DetermineCallbackResult::default());
190
191 callback_result.hit_test_item = Some($hit_test_item);
192 callback_result.normal_callbacks.extend(normal_callbacks.into_iter());
193 }
194
195 if let Some((_, hover_group)) = ui_state.node_ids_to_tag_ids.get(&$node_id).and_then(|tag_for_this_node| {
196 ui_state.tag_ids_to_hover_active_states.get(&tag_for_this_node)
197 }) {
198 needs_hover_redraw = true;
200 if hover_group.affects_layout {
202 needs_hover_relayout = true;
203 }
204 }
205 })
206 }
207
208 let onmouseenter_nodes: BTreeMap<NodeId, HitTestItem> = new_hit_node_ids.iter()
210 .filter(|(current_node_id, _)| previous_state.hovered_nodes.get(¤t_dom_id).and_then(|hn| hn.get(current_node_id)).is_none())
211 .map(|(x, y)| (*x, y.clone()))
212 .collect();
213
214 let onmouseenter_empty = onmouseenter_nodes.is_empty();
215
216 for (node_id, hit_test_item) in onmouseenter_nodes {
218 mouse_enter!(node_id, hit_test_item, MouseEnter);
219 }
220
221 let onmouseleave_nodes: BTreeMap<NodeId, HitTestItem> = match previous_state.hovered_nodes.get(¤t_dom_id) {
223 Some(hn) => {
224 hn.iter()
225 .filter(|(prev_node_id, _)| new_hit_node_ids.get(prev_node_id).is_none())
226 .map(|(x, y)| (*x, y.clone()))
227 .collect()
228 },
229 None => BTreeMap::new(),
230 };
231
232 let onmouseleave_empty = onmouseleave_nodes.is_empty();
233
234 for (node_id, hit_test_item) in onmouseleave_nodes {
236 mouse_enter!(node_id, hit_test_item, MouseLeave);
237 }
238
239 let event_is_click_or_release = event_was_mouse_down || event_was_mouse_release;
243 if event_is_click_or_release || event_was_mouse_enter || event_was_mouse_leave || !onmouseenter_empty || !onmouseleave_empty {
244 needs_hover_redraw = true;
245 needs_hover_relayout = true;
246 }
247
248 let mut reverse_event_hover_normal_list = BTreeMap::<HoverEventFilter, BTreeSet<NodeId>>::new();
254 let mut reverse_event_focus_normal_list = BTreeMap::<FocusEventFilter, BTreeSet<NodeId>>::new();
255
256 for (node_id, DetermineCallbackResult { normal_callbacks, .. }) in &nodes_with_callbacks {
257 for event_filter in normal_callbacks.keys() {
258 match event_filter {
259 EventFilter::Hover(h) => {
260 reverse_event_hover_normal_list.entry(*h).or_insert_with(|| BTreeSet::new()).insert(*node_id);
261 },
262 EventFilter::Focus(f) => {
263 reverse_event_focus_normal_list.entry(*f).or_insert_with(|| BTreeSet::new()).insert(*node_id);
264 },
265 _ => { },
266 }
267 }
268 }
269
270 for (node_id, not_event_filter_callback_list) in &ui_state.not_callbacks {
272 for (event_filter, event_callback) in not_event_filter_callback_list {
273 match event_filter {
275 NotEventFilter::Hover(h) => {
276 if let Some(on_node_ids) = reverse_event_hover_normal_list.get(&h) {
277 if !on_node_ids.contains(node_id) {
278 nodes_with_callbacks.entry(*node_id)
279 .or_insert_with(|| DetermineCallbackResult::default())
280 .normal_callbacks.insert(EventFilter::Not(*event_filter), event_callback.0);
281 }
282 }
283 },
284 NotEventFilter::Focus(_f) => {
285 }
287 }
288 }
289 }
290
291 window_state.hovered_nodes.insert(current_dom_id, new_hit_node_ids);
292 window_state.previous_window_state = Some(previous_state);
293
294 CallbacksOfHitTest {
295 needs_redraw_anyways: needs_hover_redraw,
296 needs_relayout_anyways: needs_hover_relayout,
297 nodes_with_callbacks,
298 }
299}
300
301pub fn get_window_events(window_state: &FullWindowState) -> HashSet<WindowEventFilter> {
302
303 use crate::window::CursorPosition::*;
304
305 let mut events_vec = HashSet::<WindowEventFilter>::new();
306
307 let previous_window_state = match &window_state.previous_window_state {
308 Some(s) => s,
309 None => return events_vec,
310 };
311
312 match (previous_window_state.mouse_state.cursor_position, window_state.mouse_state.cursor_position) {
315 (InWindow(_), OutOfWindow) |
316 (InWindow(_), Uninitialized) => {
317 events_vec.insert(WindowEventFilter::MouseLeave);
318 },
319 (OutOfWindow, InWindow(_)) |
320 (Uninitialized, InWindow(_)) => {
321 events_vec.insert(WindowEventFilter::MouseEnter);
322 },
323 (InWindow(a), InWindow(b)) => {
324 if a != b {
325 events_vec.insert(WindowEventFilter::MouseOver);
326 }
327 },
328 _ => { },
329 }
330
331 if window_state.mouse_state.mouse_down() && !previous_window_state.mouse_state.mouse_down() {
334 events_vec.insert(WindowEventFilter::MouseDown);
335 }
336
337 if window_state.mouse_state.left_down && !previous_window_state.mouse_state.left_down {
338 events_vec.insert(WindowEventFilter::LeftMouseDown);
339 }
340
341 if window_state.mouse_state.right_down && !previous_window_state.mouse_state.right_down {
342 events_vec.insert(WindowEventFilter::RightMouseDown);
343 }
344
345 if window_state.mouse_state.middle_down && !previous_window_state.mouse_state.middle_down {
346 events_vec.insert(WindowEventFilter::MiddleMouseDown);
347 }
348
349 if previous_window_state.mouse_state.mouse_down() && !window_state.mouse_state.mouse_down() {
350 events_vec.insert(WindowEventFilter::MouseUp);
351 }
352
353 if previous_window_state.mouse_state.left_down && !window_state.mouse_state.left_down {
354 events_vec.insert(WindowEventFilter::LeftMouseUp);
355 }
356
357 if previous_window_state.mouse_state.right_down && !window_state.mouse_state.right_down {
358 events_vec.insert(WindowEventFilter::RightMouseUp);
359 }
360
361 if previous_window_state.mouse_state.middle_down && !window_state.mouse_state.middle_down {
362 events_vec.insert(WindowEventFilter::MiddleMouseUp);
363 }
364
365 let is_scroll_previous =
368 previous_window_state.mouse_state.scroll_x.is_some() ||
369 previous_window_state.mouse_state.scroll_y.is_some();
370
371 let is_scroll_now =
372 window_state.mouse_state.scroll_x.is_some() ||
373 window_state.mouse_state.scroll_y.is_some();
374
375 if !is_scroll_previous && is_scroll_now {
376 events_vec.insert(WindowEventFilter::ScrollStart);
377 }
378
379 if is_scroll_now {
380 events_vec.insert(WindowEventFilter::Scroll);
381 }
382
383 if is_scroll_previous && !is_scroll_now {
384 events_vec.insert(WindowEventFilter::ScrollEnd);
385 }
386
387 if previous_window_state.keyboard_state.current_virtual_keycode.is_none() && window_state.keyboard_state.current_virtual_keycode.is_some() {
390 events_vec.insert(WindowEventFilter::VirtualKeyDown);
391 }
392
393 if window_state.keyboard_state.current_char.is_some() {
394 events_vec.insert(WindowEventFilter::TextInput);
395 }
396
397 if previous_window_state.keyboard_state.current_virtual_keycode.is_some() && window_state.keyboard_state.current_virtual_keycode.is_none() {
398 events_vec.insert(WindowEventFilter::VirtualKeyUp);
399 }
400
401 if previous_window_state.hovered_file.is_none() && window_state.hovered_file.is_some() {
404 events_vec.insert(WindowEventFilter::HoveredFile);
405 }
406
407 if previous_window_state.hovered_file.is_some() && window_state.hovered_file.is_none() {
408 if window_state.dropped_file.is_some() {
409 events_vec.insert(WindowEventFilter::DroppedFile);
410 } else {
411 events_vec.insert(WindowEventFilter::HoveredFileCancelled);
412 }
413 }
414
415 events_vec
416}
417
418pub fn get_hover_events(input: &HashSet<WindowEventFilter>) -> HashSet<HoverEventFilter> {
419 input.iter().filter_map(|window_event| window_event.to_hover_event_filter()).collect()
420}
421
422pub fn get_focus_events(input: &HashSet<HoverEventFilter>) -> HashSet<FocusEventFilter> {
423 input.iter().filter_map(|hover_event| hover_event.to_focus_event_filter()).collect()
424}
425
426pub fn keymap<T>(
444 info: CallbackInfo,
445 events: &[(Vec<AcceleratorKey>, CallbackType)]
446) -> UpdateScreen {
447
448 let keyboard_state = info.get_keyboard_state().clone();
449
450 events
451 .iter()
452 .filter(|(keymap_character, _)| {
453 keymap_character
454 .iter()
455 .all(|keymap_char| keymap_char.matches(&keyboard_state))
456 })
457 .next()
458 .and_then(|(_, callback)| (callback)(info))
459}