1#[cfg(not(feature = "std"))]
67use alloc::string::{String, ToString};
68use alloc::{
69 boxed::Box,
70 collections::{btree_map::BTreeMap, btree_set::BTreeSet},
71 vec::Vec,
72};
73
74use azul_css::{AzString, CssProperty, LayoutPoint, LayoutRect, LayoutSize};
75use rust_fontconfig::FcFontCache;
76
77use crate::{
78 app_resources::{ImageCache, RendererResources},
79 callbacks::{DocumentId, DomNodeId, HitTestItem, ScrollPosition, Update},
80 dom::{EventFilter, FocusEventFilter, HoverEventFilter, NotEventFilter, WindowEventFilter},
81 gl::OptionGlContextPtr,
82 id_tree::NodeId,
83 styled_dom::{ChangedCssProperty, DomId, NodeHierarchyItemId},
84 task::ExternalSystemCallbacks,
85 ui_solver::{GpuEventChanges, LayoutResult, RelayoutChanges},
86 window::{CallCallbacksResult, FullHitTest, FullWindowState, RawWindowHandle, ScrollStates},
87 FastBTreeSet, FastHashMap,
88};
89
90#[derive(Debug, Clone, PartialEq)]
91pub struct Events {
92 pub window_events: Vec<WindowEventFilter>,
93 pub hover_events: Vec<HoverEventFilter>,
94 pub focus_events: Vec<FocusEventFilter>,
95 pub old_hit_node_ids: BTreeMap<DomId, BTreeMap<NodeId, HitTestItem>>,
96 pub old_focus_node: Option<DomNodeId>,
97 pub current_window_state_mouse_is_down: bool,
98 pub previous_window_state_mouse_is_down: bool,
99 pub event_was_mouse_down: bool,
100 pub event_was_mouse_leave: bool,
101 pub event_was_mouse_release: bool,
102}
103
104impl Events {
105 pub fn new(
107 current_window_state: &FullWindowState,
108 previous_window_state: &Option<FullWindowState>,
109 ) -> Self {
110 let mut current_window_events =
111 get_window_events(current_window_state, previous_window_state);
112 let mut current_hover_events = get_hover_events(¤t_window_events);
113 let mut current_focus_events = get_focus_events(¤t_hover_events);
114
115 let event_was_mouse_down = current_window_events
116 .iter()
117 .any(|e| *e == WindowEventFilter::MouseDown);
118 let event_was_mouse_release = current_window_events
119 .iter()
120 .any(|e| *e == WindowEventFilter::MouseUp);
121 let event_was_mouse_leave = current_window_events
122 .iter()
123 .any(|e| *e == WindowEventFilter::MouseLeave);
124 let current_window_state_mouse_is_down = current_window_state.mouse_state.mouse_down();
125 let previous_window_state_mouse_is_down = previous_window_state
126 .as_ref()
127 .map(|f| f.mouse_state.mouse_down())
128 .unwrap_or(false);
129
130 let old_focus_node = previous_window_state
131 .as_ref()
132 .and_then(|f| f.focused_node.clone());
133 let old_hit_node_ids = previous_window_state
134 .as_ref()
135 .map(|f| {
136 if f.last_hit_test.hovered_nodes.is_empty() {
137 BTreeMap::new()
138 } else {
139 f.last_hit_test
140 .hovered_nodes
141 .iter()
142 .map(|(dom_id, hit_test)| {
143 (*dom_id, hit_test.regular_hit_test_nodes.clone())
144 })
145 .collect()
146 }
147 })
148 .unwrap_or_default();
149
150 if let Some(prev_state) = previous_window_state.as_ref() {
151 if prev_state.theme != current_window_state.theme {
152 current_window_events.push(WindowEventFilter::ThemeChanged);
153 }
154 if current_window_state.last_hit_test.hovered_nodes
155 != prev_state.last_hit_test.hovered_nodes.clone()
156 {
157 current_hover_events.push(HoverEventFilter::MouseLeave);
158 current_hover_events.push(HoverEventFilter::MouseEnter);
159 }
160 }
161
162 if current_window_state.focused_node != old_focus_node {
164 current_focus_events.push(FocusEventFilter::FocusReceived);
165 current_focus_events.push(FocusEventFilter::FocusLost);
166 }
167
168 Events {
169 window_events: current_window_events,
170 hover_events: current_hover_events,
171 focus_events: current_focus_events,
172 event_was_mouse_down,
173 event_was_mouse_release,
174 event_was_mouse_leave,
175 current_window_state_mouse_is_down,
176 previous_window_state_mouse_is_down,
177 old_focus_node,
178 old_hit_node_ids,
179 }
180 }
181
182 pub fn is_empty(&self) -> bool {
183 self.window_events.is_empty()
184 && self.hover_events.is_empty()
185 && self.focus_events.is_empty()
186 }
187
188 pub fn contains_resize_event(&self) -> bool {
190 self.window_events.contains(&WindowEventFilter::Resized)
191 }
192
193 pub fn event_was_mouse_scroll(&self) -> bool {
194 self.window_events.contains(&WindowEventFilter::Scroll)
196 }
197
198 pub fn needs_hit_test(&self) -> bool {
199 !(self.hover_events.is_empty() && self.focus_events.is_empty())
200 }
201}
202
203#[derive(Debug, Clone, PartialEq)]
204pub struct NodesToCheck {
205 pub new_hit_node_ids: BTreeMap<DomId, BTreeMap<NodeId, HitTestItem>>,
206 pub old_hit_node_ids: BTreeMap<DomId, BTreeMap<NodeId, HitTestItem>>,
207 pub onmouseenter_nodes: BTreeMap<DomId, BTreeMap<NodeId, HitTestItem>>,
208 pub onmouseleave_nodes: BTreeMap<DomId, BTreeMap<NodeId, HitTestItem>>,
209 pub old_focus_node: Option<DomNodeId>,
210 pub new_focus_node: Option<DomNodeId>,
211 pub current_window_state_mouse_is_down: bool,
212}
213
214impl NodesToCheck {
215 pub fn simulated_mouse_move(
218 hit_test: &FullHitTest,
219 old_focus_node: Option<DomNodeId>,
220 mouse_down: bool,
221 ) -> Self {
222 let new_hit_node_ids = hit_test
223 .hovered_nodes
224 .iter()
225 .map(|(k, v)| (k.clone(), v.regular_hit_test_nodes.clone()))
226 .collect::<BTreeMap<_, _>>();
227
228 Self {
229 new_hit_node_ids: new_hit_node_ids.clone(),
230 old_hit_node_ids: BTreeMap::new(),
231 onmouseenter_nodes: new_hit_node_ids,
232 onmouseleave_nodes: BTreeMap::new(),
233 old_focus_node,
234 new_focus_node: old_focus_node,
235 current_window_state_mouse_is_down: mouse_down,
236 }
237 }
238
239 pub fn new(hit_test: &FullHitTest, events: &Events) -> Self {
243 let new_hit_node_ids = if events.event_was_mouse_leave {
248 BTreeMap::new()
249 } else {
250 hit_test
251 .hovered_nodes
252 .iter()
253 .map(|(k, v)| (k.clone(), v.regular_hit_test_nodes.clone()))
254 .collect()
255 };
256
257 let new_focus_node = if events.event_was_mouse_release {
259 hit_test.focused_node.clone().map(|o| DomNodeId {
260 dom: o.0,
261 node: NodeHierarchyItemId::from_crate_internal(Some(o.1)),
262 })
263 } else {
264 events.old_focus_node.clone()
265 };
266
267 let default_map = BTreeMap::new();
269 let onmouseenter_nodes = new_hit_node_ids
270 .iter()
271 .filter_map(|(dom_id, nhnid)| {
272 let old_hit_node_ids = events.old_hit_node_ids.get(dom_id).unwrap_or(&default_map);
273 let new = nhnid
274 .iter()
275 .filter(|(current_node_id, _)| old_hit_node_ids.get(current_node_id).is_none())
276 .map(|(x, y)| (*x, y.clone()))
277 .collect::<BTreeMap<_, _>>();
278 if new.is_empty() {
279 None
280 } else {
281 Some((*dom_id, new))
282 }
283 })
284 .collect::<BTreeMap<_, _>>();
285
286 let onmouseleave_nodes = events
288 .old_hit_node_ids
289 .iter()
290 .filter_map(|(dom_id, ohnid)| {
291 let old = ohnid
292 .iter()
293 .filter(|(prev_node_id, _)| {
294 new_hit_node_ids
295 .get(dom_id)
296 .and_then(|d| d.get(prev_node_id))
297 .is_none()
298 })
299 .map(|(x, y)| (*x, y.clone()))
300 .collect::<BTreeMap<_, _>>();
301 if old.is_empty() {
302 None
303 } else {
304 Some((*dom_id, old))
305 }
306 })
307 .collect::<BTreeMap<_, _>>();
308
309 NodesToCheck {
310 new_hit_node_ids,
311 old_hit_node_ids: events.old_hit_node_ids.clone(),
312 onmouseenter_nodes,
313 onmouseleave_nodes,
314 old_focus_node: events.old_focus_node.clone(),
315 new_focus_node,
316 current_window_state_mouse_is_down: events.current_window_state_mouse_is_down,
317 }
318 }
319
320 pub fn empty(mouse_down: bool, old_focus_node: Option<DomNodeId>) -> Self {
321 Self {
322 new_hit_node_ids: BTreeMap::new(),
323 old_hit_node_ids: BTreeMap::new(),
324 onmouseenter_nodes: BTreeMap::new(),
325 onmouseleave_nodes: BTreeMap::new(),
326 old_focus_node,
327 new_focus_node: old_focus_node,
328 current_window_state_mouse_is_down: mouse_down,
329 }
330 }
331
332 pub fn needs_hover_active_restyle(&self) -> bool {
333 !(self.onmouseenter_nodes.is_empty() && self.onmouseleave_nodes.is_empty())
334 }
335
336 pub fn needs_focus_result(&self) -> bool {
337 self.old_focus_node != self.new_focus_node
338 }
339}
340
341pub type RestyleNodes = BTreeMap<NodeId, Vec<ChangedCssProperty>>;
342pub type RelayoutNodes = BTreeMap<NodeId, Vec<ChangedCssProperty>>;
343pub type RelayoutWords = BTreeMap<NodeId, AzString>;
344
345#[derive(Debug, Clone, PartialEq)]
347pub struct StyleAndLayoutChanges {
348 pub style_changes: Option<BTreeMap<DomId, RestyleNodes>>,
350 pub layout_changes: Option<BTreeMap<DomId, RelayoutNodes>>,
352 pub focus_change: Option<FocusChange>,
354 pub nodes_that_changed_size: Option<BTreeMap<DomId, Vec<NodeId>>>,
356 pub nodes_that_changed_text_content: Option<BTreeMap<DomId, Vec<NodeId>>>,
358 pub gpu_key_changes: Option<BTreeMap<DomId, GpuEventChanges>>,
360}
361
362#[derive(Debug, Clone, PartialEq)]
363pub struct FocusChange {
364 pub old: Option<DomNodeId>,
365 pub new: Option<DomNodeId>,
366}
367
368pub type RelayoutFn = fn(
370 DomId,
371 LayoutRect,
372 &mut LayoutResult,
373 &ImageCache,
374 &mut RendererResources,
375 &DocumentId,
376 Option<&RelayoutNodes>,
377 Option<&RelayoutWords>,
378) -> RelayoutChanges;
379
380impl StyleAndLayoutChanges {
381 pub fn new(
383 nodes: &NodesToCheck,
384 layout_results: &mut [LayoutResult],
385 image_cache: &ImageCache,
386 renderer_resources: &mut RendererResources,
387 window_size: LayoutSize,
388 document_id: &DocumentId,
389 css_changes: Option<&BTreeMap<DomId, BTreeMap<NodeId, Vec<CssProperty>>>>,
390 word_changes: Option<&BTreeMap<DomId, BTreeMap<NodeId, AzString>>>,
391 callbacks_new_focus: &Option<Option<DomNodeId>>,
392 relayout_cb: RelayoutFn,
393 ) -> StyleAndLayoutChanges {
394 let mut style_changes = None;
397 let mut layout_changes = None;
398
399 let is_mouse_down = nodes.current_window_state_mouse_is_down;
400 let nodes_that_changed_text_content = word_changes.and_then(|word_changes| {
401 if word_changes.is_empty() {
402 None
403 } else {
404 Some(
405 word_changes
406 .iter()
407 .map(|(dom_id, m)| (*dom_id, m.keys().cloned().collect()))
408 .collect(),
409 )
410 }
411 });
412
413 macro_rules! insert_props {
414 ($dom_id:expr, $prop_map:expr) => {{
415 let dom_id: DomId = $dom_id;
416 for (node_id, prop_map) in $prop_map.into_iter() {
417 for changed_prop in prop_map.into_iter() {
418 let prop_key = changed_prop.previous_prop.get_type();
419 if prop_key.can_trigger_relayout() {
420 layout_changes
421 .get_or_insert_with(|| BTreeMap::new())
422 .entry(dom_id)
423 .or_insert_with(|| BTreeMap::new())
424 .entry(node_id)
425 .or_insert_with(|| Vec::new())
426 .push(changed_prop);
427 } else {
428 style_changes
429 .get_or_insert_with(|| BTreeMap::new())
430 .entry(dom_id)
431 .or_insert_with(|| BTreeMap::new())
432 .entry(node_id)
433 .or_insert_with(|| Vec::new())
434 .push(changed_prop);
435 }
436 }
437 }
438 }};
439 }
440
441 for (dom_id, onmouseenter_nodes) in nodes.onmouseenter_nodes.iter() {
442 let layout_result = &mut layout_results[dom_id.inner];
443
444 let keys = onmouseenter_nodes.keys().copied().collect::<Vec<_>>();
445 let onmouseenter_nodes_hover_restyle_props = layout_result
446 .styled_dom
447 .restyle_nodes_hover(&keys, true);
448 let onmouseleave_nodes_active_restyle_props = layout_result
449 .styled_dom
450 .restyle_nodes_active(&keys, is_mouse_down);
451
452 insert_props!(*dom_id, onmouseenter_nodes_hover_restyle_props);
453 insert_props!(*dom_id, onmouseleave_nodes_active_restyle_props);
454 }
455
456 for (dom_id, onmouseleave_nodes) in nodes.onmouseleave_nodes.iter() {
457 let layout_result = &mut layout_results[dom_id.inner];
458 let keys = onmouseleave_nodes.keys().copied().collect::<Vec<_>>();
459 let onmouseleave_nodes_hover_restyle_props = layout_result
460 .styled_dom
461 .restyle_nodes_hover(&keys, false);
462 let onmouseleave_nodes_active_restyle_props = layout_result
463 .styled_dom
464 .restyle_nodes_active(&keys, false);
465
466 insert_props!(*dom_id, onmouseleave_nodes_hover_restyle_props);
467 insert_props!(*dom_id, onmouseleave_nodes_active_restyle_props);
468 }
469
470 let new_focus_node = if let Some(new) = callbacks_new_focus.as_ref() {
471 new
472 } else {
473 &nodes.new_focus_node
474 };
475
476 let focus_change = if nodes.old_focus_node != *new_focus_node {
477 if let Some(DomNodeId { dom, node }) = nodes.old_focus_node.as_ref() {
478 if let Some(node_id) = node.into_crate_internal() {
479 let layout_result = &mut layout_results[dom.inner];
480 let onfocus_leave_restyle_props = layout_result
481 .styled_dom
482 .restyle_nodes_focus(&[node_id], false);
483 let dom_id: DomId = *dom;
484 insert_props!(dom_id, onfocus_leave_restyle_props);
485 }
486 }
487
488 if let Some(DomNodeId { dom, node }) = new_focus_node.as_ref() {
489 if let Some(node_id) = node.into_crate_internal() {
490 let layout_result = &mut layout_results[dom.inner];
491 let onfocus_enter_restyle_props = layout_result
492 .styled_dom
493 .restyle_nodes_focus(&[node_id], true);
494 let dom_id: DomId = *dom;
495 insert_props!(dom_id, onfocus_enter_restyle_props);
496 }
497 }
498
499 Some(FocusChange {
500 old: nodes.old_focus_node,
501 new: *new_focus_node,
502 })
503 } else {
504 None
505 };
506
507 if let Some(css_changes) = css_changes {
509 for (dom_id, existing_changes_map) in css_changes.iter() {
510 let layout_result = &mut layout_results[dom_id.inner];
511 let dom_id: DomId = *dom_id;
512 for (node_id, changed_css_property_vec) in existing_changes_map.iter() {
513 let current_prop_changes = layout_result
514 .styled_dom
515 .restyle_user_property(node_id, &changed_css_property_vec);
516 insert_props!(dom_id, current_prop_changes);
517 }
518 }
519 }
520
521 let mut nodes_that_changed_size = None;
522 let mut gpu_key_change_events = None;
523
524 let window_was_resized = window_size != layout_results[DomId::ROOT_ID.inner].root_size;
526 let need_root_relayout = layout_changes.is_some()
527 || window_was_resized
528 || nodes_that_changed_text_content.is_some();
529
530 let mut doms_to_relayout = Vec::new();
531 if need_root_relayout {
532 doms_to_relayout.push(DomId::ROOT_ID);
533 } else {
534 for (dom_id, layout_result) in layout_results.iter_mut().enumerate() {
537 let gpu_key_changes = layout_result
538 .gpu_value_cache
539 .synchronize(&layout_result.rects.as_ref(), &layout_result.styled_dom);
540
541 if !gpu_key_changes.is_empty() {
542 gpu_key_change_events
543 .get_or_insert_with(|| BTreeMap::new())
544 .insert(DomId { inner: dom_id }, gpu_key_changes);
545 }
546 }
547 }
548
549 loop {
550 let mut new_iframes_to_relayout = Vec::new();
551
552 for dom_id in doms_to_relayout.drain(..) {
553 let parent_rect = match layout_results[dom_id.inner].parent_dom_id.as_ref() {
554 None => LayoutRect::new(LayoutPoint::zero(), window_size),
555 Some(parent_dom_id) => {
556 let parent_layout_result = &layout_results[parent_dom_id.inner];
557 let parent_iframe_node_id = parent_layout_result
558 .iframe_mapping
559 .iter()
560 .find_map(|(k, v)| if *v == dom_id { Some(*k) } else { None })
561 .unwrap();
562 parent_layout_result.rects.as_ref()[parent_iframe_node_id]
563 .get_approximate_static_bounds()
564 }
565 };
566
567 let layout_changes = layout_changes.as_ref().and_then(|w| w.get(&dom_id));
568 let word_changes = word_changes.and_then(|w| w.get(&dom_id));
569
570 let RelayoutChanges {
572 resized_nodes,
573 gpu_key_changes,
574 } = (relayout_cb)(
575 dom_id,
576 parent_rect,
577 &mut layout_results[dom_id.inner],
578 image_cache,
579 renderer_resources,
580 document_id,
581 layout_changes,
582 word_changes,
583 );
584
585 if !gpu_key_changes.is_empty() {
586 gpu_key_change_events
587 .get_or_insert_with(|| BTreeMap::new())
588 .insert(dom_id, gpu_key_changes);
589 }
590
591 if !resized_nodes.is_empty() {
592 new_iframes_to_relayout.extend(
593 layout_results[dom_id.inner]
594 .iframe_mapping
595 .iter()
596 .filter_map(|(node_id, dom_id)| {
597 if resized_nodes.contains(node_id) {
598 Some(dom_id)
599 } else {
600 None
601 }
602 }),
603 );
604 nodes_that_changed_size
605 .get_or_insert_with(|| BTreeMap::new())
606 .insert(dom_id, resized_nodes);
607 }
608 }
609
610 if new_iframes_to_relayout.is_empty() {
611 break;
612 } else {
613 doms_to_relayout = new_iframes_to_relayout;
614 }
615 }
616
617 StyleAndLayoutChanges {
618 style_changes,
619 layout_changes,
620 nodes_that_changed_size,
621 nodes_that_changed_text_content,
622 focus_change,
623 gpu_key_changes: gpu_key_change_events,
624 }
625 }
626
627 pub fn did_resize_nodes(&self) -> bool {
628 use azul_css::CssPropertyType;
629
630 if let Some(l) = self.nodes_that_changed_size.as_ref() {
631 if !l.is_empty() {
632 return true;
633 }
634 }
635
636 if let Some(l) = self.nodes_that_changed_text_content.as_ref() {
637 if !l.is_empty() {
638 return true;
639 }
640 }
641
642 if let Some(s) = self.style_changes.as_ref() {
644 for restyle_nodes in s.values() {
645 for changed in restyle_nodes.values() {
646 for changed in changed.iter() {
647 if changed.current_prop.get_type() == CssPropertyType::Transform {
648 return true;
649 }
650 }
651 }
652 }
653 }
654 false
655 }
656
657 pub fn need_regenerate_display_list(&self) -> bool {
659 if !self.nodes_that_changed_size.is_none() {
660 return true;
661 }
662 if !self.nodes_that_changed_text_content.is_none() {
663 return true;
664 }
665 if !self.need_redraw() {
666 return false;
667 }
668
669 if let Some(style_changes) = self.style_changes.as_ref() {
672 !(style_changes.iter().all(|(_, restyle_nodes)| {
673 restyle_nodes.iter().all(|(_, changed_css_properties)| {
674 changed_css_properties.iter().all(|changed_prop| {
675 changed_prop.current_prop.get_type().is_gpu_only_property()
676 })
677 })
678 }))
679 } else {
680 false
681 }
682 }
683
684 pub fn is_empty(&self) -> bool {
685 self.style_changes.is_none()
686 && self.layout_changes.is_none()
687 && self.focus_change.is_none()
688 && self.nodes_that_changed_size.is_none()
689 && self.nodes_that_changed_text_content.is_none()
690 && self.gpu_key_changes.is_none()
691 }
692
693 pub fn need_redraw(&self) -> bool {
694 !(self.style_changes.is_none()
695 && self.layout_changes.is_none()
696 && self.nodes_that_changed_text_content.is_none()
697 && self.nodes_that_changed_size.is_none())
698 }
699}
700
701#[derive(Debug, Clone, PartialEq)]
702pub struct CallbackToCall {
703 pub node_id: NodeId,
704 pub hit_test_item: Option<HitTestItem>,
705 pub event_filter: EventFilter,
706}
707
708#[derive(Debug, Clone)]
709pub struct CallbacksOfHitTest {
710 pub nodes_with_callbacks: BTreeMap<DomId, Vec<CallbackToCall>>,
714}
715
716impl CallbacksOfHitTest {
717 pub fn new(
722 nodes_to_check: &NodesToCheck,
723 events: &Events,
724 layout_results: &[LayoutResult],
725 ) -> Self {
726 let mut nodes_with_callbacks = BTreeMap::new();
727
728 if events.is_empty() {
729 return Self {
730 nodes_with_callbacks,
731 };
732 }
733
734 let default_map = BTreeMap::new();
735 let mouseenter_filter = EventFilter::Hover(HoverEventFilter::MouseEnter);
736 let mouseleave_filter = EventFilter::Hover(HoverEventFilter::MouseEnter);
737 let focus_received_filter = EventFilter::Focus(FocusEventFilter::FocusReceived);
738 let focus_lost_filter = EventFilter::Focus(FocusEventFilter::FocusLost);
739
740 for (dom_id, layout_result) in layout_results.iter().enumerate() {
741 let dom_id = DomId { inner: dom_id };
742
743 let mut window_callbacks_this_dom = layout_result
745 .styled_dom
746 .nodes_with_window_callbacks
747 .iter()
748 .flat_map(|nid| {
749 let node_id = match nid.into_crate_internal() {
750 Some(s) => s,
751 None => return Vec::new(),
752 };
753 layout_result.styled_dom.node_data.as_container()[node_id]
754 .get_callbacks()
755 .iter()
756 .filter_map(|cb| match cb.event {
757 EventFilter::Window(wev) => {
758 if events.window_events.contains(&wev) {
759 Some(CallbackToCall {
760 event_filter: EventFilter::Window(wev),
761 hit_test_item: None,
762 node_id,
763 })
764 } else {
765 None
766 }
767 }
768 _ => None,
769 })
770 .collect::<Vec<_>>()
771 })
772 .collect::<Vec<_>>();
773
774 window_callbacks_this_dom.extend(
778 nodes_to_check
779 .onmouseenter_nodes
780 .get(&dom_id)
781 .unwrap_or(&default_map)
782 .iter()
783 .filter_map(|(node_id, ht)| {
784 if layout_result.styled_dom.node_data.as_container()[*node_id]
785 .get_callbacks()
786 .iter()
787 .any(|e| e.event == mouseenter_filter)
788 {
789 Some(CallbackToCall {
790 event_filter: mouseenter_filter.clone(),
791 hit_test_item: Some(*ht),
792 node_id: *node_id,
793 })
794 } else {
795 None
796 }
797 }),
798 );
799
800 window_callbacks_this_dom.extend(
802 nodes_to_check
803 .onmouseleave_nodes
804 .get(&dom_id)
805 .unwrap_or(&default_map)
806 .iter()
807 .filter_map(|(node_id, ht)| {
808 if layout_result.styled_dom.node_data.as_container()[*node_id]
809 .get_callbacks()
810 .iter()
811 .any(|e| e.event == mouseleave_filter)
812 {
813 Some(CallbackToCall {
814 event_filter: mouseleave_filter.clone(),
815 hit_test_item: Some(*ht),
816 node_id: *node_id,
817 })
818 } else {
819 None
820 }
821 }),
822 );
823
824 for (nid, ht) in nodes_to_check
826 .new_hit_node_ids
827 .get(&dom_id)
828 .unwrap_or(&default_map)
829 .iter()
830 {
831 for hev in events.hover_events.iter() {
832 window_callbacks_this_dom.extend(
833 layout_result.styled_dom.node_data.as_container()[*nid]
834 .get_callbacks()
835 .iter()
836 .filter_map(|e| {
837 if e.event == EventFilter::Hover(*hev)
838 && e.event != mouseenter_filter
839 && e.event != mouseleave_filter
840 {
841 Some(CallbackToCall {
842 event_filter: EventFilter::Hover(hev.clone()),
843 hit_test_item: Some(*ht),
844 node_id: *nid,
845 })
846 } else {
847 None
848 }
849 }),
850 );
851 }
852 }
853
854 if nodes_to_check.new_focus_node != nodes_to_check.old_focus_node {
856 if let Some(DomNodeId {
857 dom,
858 node: az_node_id,
859 }) = nodes_to_check.old_focus_node
860 {
861 if dom == dom_id {
862 if let Some(nid) = az_node_id.into_crate_internal() {
863 if layout_result.styled_dom.node_data.as_container()[nid]
864 .get_callbacks()
865 .iter()
866 .any(|e| e.event == focus_lost_filter)
867 {
868 window_callbacks_this_dom.push(CallbackToCall {
869 event_filter: focus_lost_filter.clone(),
870 hit_test_item: events
871 .old_hit_node_ids
872 .get(&dom_id)
873 .and_then(|map| map.get(&nid))
874 .cloned(),
875 node_id: nid,
876 })
877 }
878 }
879 }
880 }
881
882 if let Some(DomNodeId {
883 dom,
884 node: az_node_id,
885 }) = nodes_to_check.new_focus_node
886 {
887 if dom == dom_id {
888 if let Some(nid) = az_node_id.into_crate_internal() {
889 if layout_result.styled_dom.node_data.as_container()[nid]
890 .get_callbacks()
891 .iter()
892 .any(|e| e.event == focus_received_filter)
893 {
894 window_callbacks_this_dom.push(CallbackToCall {
895 event_filter: focus_received_filter.clone(),
896 hit_test_item: events
897 .old_hit_node_ids
898 .get(&dom_id)
899 .and_then(|map| map.get(&nid))
900 .cloned(),
901 node_id: nid,
902 })
903 }
904 }
905 }
906 }
907 }
908
909 if let Some(DomNodeId {
911 dom,
912 node: az_node_id,
913 }) = nodes_to_check.new_focus_node
914 {
915 if dom == dom_id {
916 if let Some(nid) = az_node_id.into_crate_internal() {
917 for fev in events.focus_events.iter() {
918 for cb in layout_result.styled_dom.node_data.as_container()[nid]
919 .get_callbacks()
920 .iter()
921 {
922 if cb.event == EventFilter::Focus(*fev)
923 && cb.event != focus_received_filter
924 && cb.event != focus_lost_filter
925 {
926 window_callbacks_this_dom.push(CallbackToCall {
927 event_filter: EventFilter::Focus(fev.clone()),
928 hit_test_item: events
929 .old_hit_node_ids
930 .get(&dom_id)
931 .and_then(|map| map.get(&nid))
932 .cloned(),
933 node_id: nid,
934 })
935 }
936 }
937 }
938 }
939 }
940 }
941
942 if !window_callbacks_this_dom.is_empty() {
943 nodes_with_callbacks.insert(dom_id, window_callbacks_this_dom);
944 }
945 }
946
947 for (dom_id, layout_result) in layout_results.iter().enumerate() {
949 let dom_id = DomId { inner: dom_id };
950
951 let not_event_filters = layout_result
952 .styled_dom
953 .nodes_with_not_callbacks
954 .iter()
955 .flat_map(|node_id| {
956 let node_id = match node_id.into_crate_internal() {
957 Some(s) => s,
958 None => return Vec::new(),
959 };
960 layout_result.styled_dom.node_data.as_container()[node_id]
961 .get_callbacks()
962 .iter()
963 .filter_map(|cb| match cb.event {
964 EventFilter::Not(nev) => {
965 if nodes_with_callbacks.get(&dom_id).map(|v| {
966 v.iter().any(|cb| {
967 cb.node_id == node_id
968 && cb.event_filter == nev.as_event_filter()
969 })
970 }) != Some(true)
971 {
972 Some(CallbackToCall {
973 event_filter: EventFilter::Not(nev.clone()),
974 hit_test_item: events
975 .old_hit_node_ids
976 .get(&dom_id)
977 .and_then(|map| map.get(&node_id))
978 .cloned(),
979 node_id,
980 })
981 } else {
982 None
983 }
984 }
985 _ => None,
986 })
987 .collect::<Vec<_>>()
988 })
989 .collect::<Vec<_>>();
990
991 for cb in not_event_filters {
992 nodes_with_callbacks
993 .entry(dom_id)
994 .or_insert_with(|| Vec::new())
995 .push(cb);
996 }
997 }
998
999 CallbacksOfHitTest {
1000 nodes_with_callbacks,
1001 }
1002 }
1003
1004 pub fn call(
1006 &mut self,
1007 previous_window_state: &Option<FullWindowState>,
1008 full_window_state: &FullWindowState,
1009 raw_window_handle: &RawWindowHandle,
1010 scroll_states: &BTreeMap<DomId, BTreeMap<NodeHierarchyItemId, ScrollPosition>>,
1011 gl_context: &OptionGlContextPtr,
1012 layout_results: &mut Vec<LayoutResult>,
1013 modifiable_scroll_states: &mut ScrollStates,
1014 image_cache: &mut ImageCache,
1015 system_fonts: &mut FcFontCache,
1016 system_callbacks: &ExternalSystemCallbacks,
1017 renderer_resources: &RendererResources,
1018 ) -> CallCallbacksResult {
1019 use crate::{
1020 callbacks::CallbackInfo, styled_dom::ParentWithNodeDepth, window::WindowState,
1021 };
1022
1023 let mut ret = CallCallbacksResult {
1024 should_scroll_render: false,
1025 callbacks_update_screen: Update::DoNothing,
1026 modified_window_state: None,
1027 css_properties_changed: None,
1028 words_changed: None,
1029 images_changed: None,
1030 image_masks_changed: None,
1031 nodes_scrolled_in_callbacks: None,
1032 update_focused_node: None,
1033 timers: None,
1034 threads: None,
1035 timers_removed: None,
1036 threads_removed: None,
1037 windows_created: Vec::new(),
1038 cursor_changed: false,
1039 };
1040 let mut new_focus_target = None;
1041
1042 let current_cursor = full_window_state.mouse_state.mouse_cursor_type.clone();
1043
1044 if self.nodes_with_callbacks.is_empty() {
1045 return ret;
1047 }
1048
1049 let mut ret_modified_window_state: WindowState = full_window_state.clone().into();
1050 let mut ret_modified_window_state_unmodified = ret_modified_window_state.clone();
1051 let mut ret_timers = FastHashMap::new();
1052 let mut ret_timers_removed = FastBTreeSet::new();
1053 let mut ret_threads = FastHashMap::new();
1054 let mut ret_threads_removed = FastBTreeSet::new();
1055 let mut ret_words_changed = BTreeMap::new();
1056 let mut ret_images_changed = BTreeMap::new();
1057 let mut ret_image_masks_changed = BTreeMap::new();
1058 let mut ret_css_properties_changed = BTreeMap::new();
1059 let mut ret_nodes_scrolled_in_callbacks = BTreeMap::new();
1060
1061 {
1062 for (dom_id, callbacks_filter_list) in self.nodes_with_callbacks.iter() {
1063 let mut callbacks = BTreeMap::new();
1064 for cbtc in callbacks_filter_list {
1065 callbacks
1066 .entry(cbtc.node_id)
1067 .or_insert_with(|| Vec::new())
1068 .push((cbtc.hit_test_item, cbtc.event_filter));
1069 }
1070 let callbacks = callbacks;
1071 let mut empty_vec = Vec::new();
1072 let lr = match layout_results.get(dom_id.inner) {
1073 Some(s) => s,
1074 None => continue,
1075 };
1076
1077 let mut blacklisted_event_types = BTreeSet::new();
1078
1079 for ParentWithNodeDepth { depth: _, node_id } in
1081 lr.styled_dom.non_leaf_nodes.as_ref().iter().rev()
1082 {
1083 let parent_node_id = node_id;
1084 for child_id in parent_node_id
1085 .into_crate_internal()
1086 .unwrap()
1087 .az_children(&lr.styled_dom.node_hierarchy.as_container())
1088 {
1089 for (hit_test_item, event_filter) in
1090 callbacks.get(&child_id).unwrap_or(&empty_vec)
1091 {
1092 if blacklisted_event_types.contains(&*event_filter) {
1093 continue;
1094 }
1095
1096 let mut new_focus = None;
1097 let mut stop_propagation = false;
1098
1099 let mut callback_info = CallbackInfo::new(
1100 &layout_results,
1101 renderer_resources,
1102 &previous_window_state,
1103 &full_window_state,
1104 &mut ret_modified_window_state,
1106 gl_context,
1107 image_cache,
1108 system_fonts,
1109 &mut ret_timers,
1110 &mut ret_threads,
1111 &mut ret_timers_removed,
1112 &mut ret_threads_removed,
1113 raw_window_handle,
1114 &mut ret.windows_created,
1115 system_callbacks,
1116 &mut stop_propagation,
1117 &mut new_focus,
1118 &mut ret_words_changed,
1119 &mut ret_images_changed,
1120 &mut ret_image_masks_changed,
1122 &mut ret_css_properties_changed,
1124 scroll_states,
1125 &mut ret_nodes_scrolled_in_callbacks,
1127 DomNodeId {
1129 dom: *dom_id,
1130 node: NodeHierarchyItemId::from_crate_internal(Some(child_id)),
1131 },
1132 hit_test_item
1134 .as_ref()
1135 .map(|hi| hi.point_relative_to_item)
1136 .into(),
1137 hit_test_item.as_ref().map(|hi| hi.point_in_viewport).into(),
1139 );
1140
1141 let callback_return = {
1142 let node_data_container = lr.styled_dom.node_data.as_container();
1144 if let Some(callback_data) =
1145 node_data_container.get(child_id).and_then(|nd| {
1146 nd.callbacks
1147 .as_ref()
1148 .iter()
1149 .find(|i| i.event == *event_filter)
1150 })
1151 {
1152 let mut callback_data_clone = callback_data.clone();
1153 (callback_data_clone.callback.cb)(
1155 &mut callback_data_clone.data,
1156 &mut callback_info,
1157 )
1158 } else {
1159 Update::DoNothing
1160 }
1161 };
1162
1163 ret.callbacks_update_screen.max_self(callback_return);
1164
1165 if let Some(new_focus) = new_focus.clone() {
1166 new_focus_target = Some(new_focus);
1167 }
1168
1169 if stop_propagation {
1170 blacklisted_event_types.insert(event_filter.clone());
1171 }
1172 }
1173 }
1174 }
1175
1176 loop {
1178 for ((hit_test_item, event_filter), root_id) in lr
1179 .styled_dom
1180 .root
1181 .into_crate_internal()
1182 .map(|root_id| {
1183 callbacks
1184 .get(&root_id)
1185 .unwrap_or(&empty_vec)
1186 .iter()
1187 .map(|item| (item, root_id))
1188 .collect::<Vec<_>>()
1189 })
1190 .unwrap_or_default()
1191 {
1192 if blacklisted_event_types.contains(&event_filter) {
1193 break; }
1195
1196 let mut new_focus = None;
1197 let mut stop_propagation = false;
1198
1199 let mut callback_info = CallbackInfo::new(
1200 &layout_results,
1201 renderer_resources,
1202 &previous_window_state,
1203 &full_window_state,
1204 &mut ret_modified_window_state,
1205 gl_context,
1206 image_cache,
1207 system_fonts,
1208 &mut ret_timers,
1209 &mut ret_threads,
1210 &mut ret_timers_removed,
1211 &mut ret_threads_removed,
1212 raw_window_handle,
1213 &mut ret.windows_created,
1214 system_callbacks,
1215 &mut stop_propagation,
1216 &mut new_focus,
1217 &mut ret_words_changed,
1218 &mut ret_images_changed,
1219 &mut ret_image_masks_changed,
1221 &mut ret_css_properties_changed,
1223 scroll_states,
1224 &mut ret_nodes_scrolled_in_callbacks,
1226 DomNodeId {
1228 dom: *dom_id,
1229 node: NodeHierarchyItemId::from_crate_internal(Some(root_id)),
1230 },
1231 hit_test_item
1233 .as_ref()
1234 .map(|hi| hi.point_relative_to_item)
1235 .into(),
1236 hit_test_item.as_ref().map(|hi| hi.point_in_viewport).into(),
1238 );
1239
1240 let callback_return = {
1241 let node_data_container = lr.styled_dom.node_data.as_container();
1243 if let Some(callback_data) =
1244 node_data_container.get(root_id).and_then(|nd| {
1245 nd.callbacks
1246 .as_ref()
1247 .iter()
1248 .find(|i| i.event == *event_filter)
1249 })
1250 {
1251 let mut callback_data_clone = callback_data.clone();
1253 (callback_data_clone.callback.cb)(
1254 &mut callback_data_clone.data,
1255 &mut callback_info,
1256 )
1257 } else {
1258 Update::DoNothing
1259 }
1260 };
1261
1262 ret.callbacks_update_screen.max_self(callback_return);
1263
1264 if let Some(new_focus) = new_focus.clone() {
1265 new_focus_target = Some(new_focus);
1266 }
1267
1268 if stop_propagation {
1269 blacklisted_event_types.insert(event_filter.clone());
1270 }
1271 }
1272
1273 break;
1274 }
1275 }
1276 }
1277
1278 for (dom_id, callback_scrolled_nodes) in ret_nodes_scrolled_in_callbacks.iter() {
1280 let scrollable_nodes = &layout_results[dom_id.inner].scrollable_nodes;
1281 for (scroll_node_id, scroll_position) in callback_scrolled_nodes.iter() {
1282 let scroll_node = match scrollable_nodes.overflowing_nodes.get(&scroll_node_id) {
1283 Some(s) => s,
1284 None => continue,
1285 };
1286
1287 modifiable_scroll_states.set_scroll_position(&scroll_node, *scroll_position);
1288 ret.should_scroll_render = true;
1289 }
1290 }
1291
1292 if let Some(ft) = new_focus_target {
1294 if let Ok(new_focus_node) = ft.resolve(&layout_results, full_window_state.focused_node)
1295 {
1296 ret.update_focused_node = Some(new_focus_node);
1297 }
1298 }
1299
1300 if current_cursor != ret_modified_window_state.mouse_state.mouse_cursor_type {
1301 ret.cursor_changed = true;
1302 }
1303
1304 if !ret_timers.is_empty() {
1305 ret.timers = Some(ret_timers);
1306 }
1307 if !ret_threads.is_empty() {
1308 ret.threads = Some(ret_threads);
1309 }
1310 if ret_modified_window_state != ret_modified_window_state_unmodified {
1311 ret.modified_window_state = Some(ret_modified_window_state);
1312 }
1313 if !ret_threads_removed.is_empty() {
1314 ret.threads_removed = Some(ret_threads_removed);
1315 }
1316 if !ret_timers_removed.is_empty() {
1317 ret.timers_removed = Some(ret_timers_removed);
1318 }
1319 if !ret_words_changed.is_empty() {
1320 ret.words_changed = Some(ret_words_changed);
1321 }
1322 if !ret_images_changed.is_empty() {
1323 ret.images_changed = Some(ret_images_changed);
1324 }
1325 if !ret_image_masks_changed.is_empty() {
1326 ret.image_masks_changed = Some(ret_image_masks_changed);
1327 }
1328 if !ret_css_properties_changed.is_empty() {
1329 ret.css_properties_changed = Some(ret_css_properties_changed);
1330 }
1331 if !ret_nodes_scrolled_in_callbacks.is_empty() {
1332 ret.nodes_scrolled_in_callbacks = Some(ret_nodes_scrolled_in_callbacks);
1333 }
1334
1335 ret
1336 }
1337}
1338
1339fn get_window_events(
1340 current_window_state: &FullWindowState,
1341 previous_window_state: &Option<FullWindowState>,
1342) -> Vec<WindowEventFilter> {
1343 use crate::window::{CursorPosition::*, WindowPosition};
1344
1345 let mut events = Vec::new();
1346
1347 let previous_window_state = match previous_window_state.as_ref() {
1348 Some(s) => s,
1349 None => return events,
1350 };
1351
1352 match (
1355 previous_window_state.mouse_state.cursor_position,
1356 current_window_state.mouse_state.cursor_position,
1357 ) {
1358 (InWindow(_), OutOfWindow(_)) | (InWindow(_), Uninitialized) => {
1359 events.push(WindowEventFilter::MouseLeave);
1360 }
1361 (OutOfWindow(_), InWindow(_)) | (Uninitialized, InWindow(_)) => {
1362 events.push(WindowEventFilter::MouseEnter);
1363 }
1364 (InWindow(a), InWindow(b)) => {
1365 if a != b {
1366 events.push(WindowEventFilter::MouseOver);
1367 }
1368 }
1369 _ => {}
1370 }
1371
1372 if current_window_state.mouse_state.mouse_down()
1373 && !previous_window_state.mouse_state.mouse_down()
1374 {
1375 events.push(WindowEventFilter::MouseDown);
1376 }
1377
1378 if current_window_state.mouse_state.left_down && !previous_window_state.mouse_state.left_down {
1379 events.push(WindowEventFilter::LeftMouseDown);
1380 }
1381
1382 if current_window_state.mouse_state.right_down && !previous_window_state.mouse_state.right_down
1383 {
1384 events.push(WindowEventFilter::RightMouseDown);
1385 }
1386
1387 if current_window_state.mouse_state.middle_down
1388 && !previous_window_state.mouse_state.middle_down
1389 {
1390 events.push(WindowEventFilter::MiddleMouseDown);
1391 }
1392
1393 if previous_window_state.mouse_state.mouse_down()
1394 && !current_window_state.mouse_state.mouse_down()
1395 {
1396 events.push(WindowEventFilter::MouseUp);
1397 }
1398
1399 if previous_window_state.mouse_state.left_down && !current_window_state.mouse_state.left_down {
1400 events.push(WindowEventFilter::LeftMouseUp);
1401 }
1402
1403 if previous_window_state.mouse_state.right_down && !current_window_state.mouse_state.right_down
1404 {
1405 events.push(WindowEventFilter::RightMouseUp);
1406 }
1407
1408 if previous_window_state.mouse_state.middle_down
1409 && !current_window_state.mouse_state.middle_down
1410 {
1411 events.push(WindowEventFilter::MiddleMouseUp);
1412 }
1413
1414 if current_window_state.flags.has_focus != previous_window_state.flags.has_focus {
1417 if current_window_state.flags.has_focus {
1418 events.push(WindowEventFilter::FocusReceived);
1419 events.push(WindowEventFilter::WindowFocusReceived);
1420 } else {
1421 events.push(WindowEventFilter::FocusLost);
1422 events.push(WindowEventFilter::WindowFocusLost);
1423 }
1424 }
1425
1426 if current_window_state.size.dimensions != previous_window_state.size.dimensions
1427 || current_window_state.size.dpi != previous_window_state.size.dpi
1428 {
1429 events.push(WindowEventFilter::Resized);
1430 }
1431
1432 match (
1433 current_window_state.position,
1434 previous_window_state.position,
1435 ) {
1436 (WindowPosition::Initialized(cur_pos), WindowPosition::Initialized(prev_pos)) => {
1437 if prev_pos != cur_pos {
1438 events.push(WindowEventFilter::Moved);
1439 }
1440 }
1441 (WindowPosition::Initialized(_), WindowPosition::Uninitialized) => {
1442 events.push(WindowEventFilter::Moved);
1443 }
1444 _ => {}
1445 }
1446
1447 let about_to_close_equals = current_window_state.flags.is_about_to_close
1448 == previous_window_state.flags.is_about_to_close;
1449 if current_window_state.flags.is_about_to_close && !about_to_close_equals {
1450 events.push(WindowEventFilter::CloseRequested);
1451 }
1452
1453 let is_scroll_previous = previous_window_state.mouse_state.scroll_x.is_some()
1456 || previous_window_state.mouse_state.scroll_y.is_some();
1457
1458 let is_scroll_now = current_window_state.mouse_state.scroll_x.is_some()
1459 || current_window_state.mouse_state.scroll_y.is_some();
1460
1461 if !is_scroll_previous && is_scroll_now {
1462 events.push(WindowEventFilter::ScrollStart);
1463 }
1464
1465 if is_scroll_now {
1466 events.push(WindowEventFilter::Scroll);
1467 }
1468
1469 if is_scroll_previous && !is_scroll_now {
1470 events.push(WindowEventFilter::ScrollEnd);
1471 }
1472
1473 let cur_vk_equal = current_window_state.keyboard_state.current_virtual_keycode
1475 == previous_window_state.keyboard_state.current_virtual_keycode;
1476 let cur_char_equal = current_window_state.keyboard_state.current_char
1477 == previous_window_state.keyboard_state.current_char;
1478
1479 if !cur_vk_equal
1480 && previous_window_state
1481 .keyboard_state
1482 .current_virtual_keycode
1483 .is_none()
1484 && current_window_state
1485 .keyboard_state
1486 .current_virtual_keycode
1487 .is_some()
1488 {
1489 events.push(WindowEventFilter::VirtualKeyDown);
1490 }
1491
1492 if !cur_char_equal && current_window_state.keyboard_state.current_char.is_some() {
1493 events.push(WindowEventFilter::TextInput);
1494 }
1495
1496 if !cur_vk_equal
1497 && previous_window_state
1498 .keyboard_state
1499 .current_virtual_keycode
1500 .is_some()
1501 && current_window_state
1502 .keyboard_state
1503 .current_virtual_keycode
1504 .is_none()
1505 {
1506 events.push(WindowEventFilter::VirtualKeyUp);
1507 }
1508
1509 let hovered_file_equals =
1512 previous_window_state.hovered_file == current_window_state.hovered_file;
1513 if previous_window_state.hovered_file.is_none()
1514 && current_window_state.hovered_file.is_some()
1515 && !hovered_file_equals
1516 {
1517 events.push(WindowEventFilter::HoveredFile);
1518 }
1519
1520 if previous_window_state.hovered_file.is_some() && current_window_state.hovered_file.is_none() {
1521 if current_window_state.dropped_file.is_some() {
1522 events.push(WindowEventFilter::DroppedFile);
1523 } else {
1524 events.push(WindowEventFilter::HoveredFileCancelled);
1525 }
1526 }
1527
1528 if current_window_state.theme != previous_window_state.theme {
1529 events.push(WindowEventFilter::ThemeChanged);
1530 }
1531
1532 events
1533}
1534
1535fn get_hover_events(input: &[WindowEventFilter]) -> Vec<HoverEventFilter> {
1536 input
1537 .iter()
1538 .filter_map(|window_event| window_event.to_hover_event_filter())
1539 .collect()
1540}
1541
1542fn get_focus_events(input: &[HoverEventFilter]) -> Vec<FocusEventFilter> {
1543 input
1544 .iter()
1545 .filter_map(|hover_event| hover_event.to_focus_event_filter())
1546 .collect()
1547}