Skip to main content

vizia_core/events/
event_manager.rs

1use crate::context::{InternalEvent, ResourceContext};
2use crate::events::EventMeta;
3use crate::prelude::*;
4#[cfg(debug_assertions)]
5use crate::systems::compute_matched_rules;
6use crate::systems::{binding_system, hover_system};
7use crate::tree::{focus_backward, focus_forward, is_navigatable};
8#[cfg(debug_assertions)]
9use log::debug;
10use std::any::Any;
11use vizia_storage::LayoutParentIterator;
12#[cfg(debug_assertions)]
13use vizia_storage::ParentIterator;
14use vizia_storage::TreeIterator;
15
16/// Dispatches events to views and models.
17///
18/// The [EventManager] is responsible for taking the events in the event queue in cx
19/// and dispatching them to views and models based on the target and propagation metadata of the event.
20#[doc(hidden)]
21pub struct EventManager {
22    // Queue of events to be processed.
23    event_queue: Vec<Event>,
24}
25
26impl Default for EventManager {
27    fn default() -> Self {
28        Self::new()
29    }
30}
31
32impl EventManager {
33    pub fn new() -> Self {
34        EventManager { event_queue: Vec::with_capacity(10) }
35    }
36
37    /// Flush the event queue, dispatching events to their targets.
38    /// Returns whether there are still more events to process, i.e. the event handlers sent events.
39    pub fn flush_events(
40        &mut self,
41        cx: &mut Context,
42        mut window_event_callback: impl FnMut(&WindowEvent),
43    ) {
44        while {
45            // Clear the event queue in the event manager.
46            self.event_queue.clear();
47
48            // Move events from cx to event manager. This is so the cx can be passed
49            // mutably to the view when handling events.
50            self.event_queue.extend(cx.event_queue.drain(0..));
51
52            // Loop over the events in the event queue.
53            'events: for event in self.event_queue.iter_mut() {
54                let keyboard_lock_root = keyboard_event_lock_root(cx, event);
55
56                // Handle internal events.
57                event.take(|internal_event, _| match internal_event {
58                    InternalEvent::Redraw => cx.needs_redraw(Entity::root()),
59                    InternalEvent::LoadImage { path, image, policy } => {
60                        if let Some(image) = image.lock().unwrap().take() {
61                            ResourceContext::new(cx).load_image(path, image, policy);
62                        }
63                    }
64                });
65
66                // Send events to any global listeners.
67                let mut global_listeners = vec![];
68                std::mem::swap(&mut cx.global_listeners, &mut global_listeners);
69                for listener in &global_listeners {
70                    cx.with_current(Entity::root(), |cx| {
71                        listener(&mut EventContext::new(cx), event)
72                    });
73                }
74                std::mem::swap(&mut cx.global_listeners, &mut global_listeners);
75
76                // Send events to any local listeners.
77                let listeners = cx.listeners.keys().copied().collect::<Vec<Entity>>();
78                for entity in listeners {
79                    if let Some(lock_root) = keyboard_lock_root {
80                        if !entity.is_descendant_of(&cx.tree, lock_root) {
81                            continue;
82                        }
83                    }
84
85                    if let Some(listener) = cx.listeners.remove(&entity) {
86                        if let Some(mut event_handler) = cx.views.remove(&entity) {
87                            cx.with_current(entity, |cx| {
88                                (listener)(
89                                    event_handler.as_mut(),
90                                    &mut EventContext::new(cx),
91                                    event,
92                                );
93                            });
94
95                            cx.views.insert(entity, event_handler);
96                        }
97
98                        cx.listeners.insert(entity, listener);
99                    }
100
101                    if event.meta.consumed {
102                        continue 'events;
103                    }
104                }
105
106                // Handle state updates for window events.
107                event.map(|window_event, meta| {
108                    if cx.windows.contains_key(&meta.origin) {
109                        internal_state_updates(cx, window_event, meta);
110                    }
111                });
112
113                // Skip to next event if the current event was consumed when handling internal state updates.
114                if event.meta.consumed {
115                    continue 'events;
116                }
117
118                let cx = &mut EventContext::new(cx);
119
120                // Copy the target to prevent multiple mutable borrows error.
121                let target = event.meta.target;
122
123                // Send event to target.
124                if keyboard_lock_root
125                    .map(|lock_root| target.is_descendant_of(cx.tree, lock_root))
126                    .unwrap_or(true)
127                {
128                    visit_entity(cx, target, event);
129                }
130
131                // Skip to next event if the current event was consumed.
132                if event.meta.consumed {
133                    continue 'events;
134                }
135
136                // Propagate up from target to root (not including the target).
137                if event.meta.propagation == Propagation::Up {
138                    // Create a parent iterator and skip the first element which is the target.
139                    let iter = target.parent_iter(cx.tree).skip(1);
140
141                    for entity in iter {
142                        if let Some(lock_root) = keyboard_lock_root {
143                            if !entity.is_descendant_of(cx.tree, lock_root) {
144                                break;
145                            }
146                        }
147
148                        // Send event to all ancestors of the target.
149                        visit_entity(cx, entity, event);
150
151                        // Skip to the next event if the current event was consumed.
152                        if event.meta.consumed {
153                            continue 'events;
154                        }
155                    }
156                }
157
158                // Propagate the event down the subtree from the target (not including the target).
159                if event.meta.propagation == Propagation::Subtree {
160                    // Create a branch (subtree) iterator and skip the first element which is the target.
161                    let iter = target.branch_iter(cx.tree).skip(1);
162
163                    for entity in iter {
164                        if keyboard_lock_root
165                            .map(|lock_root| entity.is_descendant_of(cx.tree, lock_root))
166                            .unwrap_or(true)
167                        {
168                            // Send event to all entities in the subtree after the target.
169                            visit_entity(cx, entity, event);
170                        }
171
172                        // Skip to the next event if the current event was consumed.
173                        if event.meta.consumed {
174                            continue 'events;
175                        }
176                    }
177                }
178
179                event.map(|window_event: &WindowEvent, _| {
180                    (window_event_callback)(window_event);
181                });
182            }
183
184            binding_system(cx);
185
186            // Return true if there are new events in the queue.
187            !cx.event_queue.is_empty()
188        } {}
189    }
190}
191fn is_keyboard_window_event(window_event: &WindowEvent) -> bool {
192    matches!(
193        window_event,
194        WindowEvent::KeyDown(_, _)
195            | WindowEvent::KeyUp(_, _)
196            | WindowEvent::CharInput(_)
197            | WindowEvent::ImeActivate(_)
198            | WindowEvent::ImeCommit(_)
199            | WindowEvent::ImePreedit(_, _)
200            | WindowEvent::SetImeCursorArea(_, _)
201    )
202}
203
204fn keyboard_event_lock_root(cx: &Context, event: &mut Event) -> Option<Entity> {
205    let mut lock_root = None;
206
207    event.map(|window_event: &WindowEvent, _| {
208        if is_keyboard_window_event(window_event) {
209            let candidate = cx.tree.lock_focus_within(cx.focused);
210            if candidate != Entity::root() {
211                lock_root = Some(candidate);
212            }
213        }
214    });
215
216    lock_root
217}
218
219fn visit_entity(cx: &mut EventContext, entity: Entity, event: &mut Event) {
220    // Send event to models attached to the entity
221    if let Some(ids) =
222        cx.models.get(&entity).map(|models| models.keys().cloned().collect::<Vec<_>>())
223    {
224        for id in ids {
225            if let Some(mut model) =
226                cx.models.get_mut(&entity).and_then(|models| models.remove(&id))
227            {
228                cx.current = entity;
229
230                model.event(cx, event);
231
232                cx.models.get_mut(&entity).and_then(|models| models.insert(id, model));
233            }
234        }
235    }
236
237    // Return early if the event was consumed by a model
238    if event.meta.consumed {
239        return;
240    }
241
242    // Send event to the view attached to the entity
243    if let Some(mut view) = cx.views.remove(&entity) {
244        cx.current = entity;
245        view.event(cx, event);
246
247        cx.views.insert(entity, view);
248    }
249}
250
251/// Update the internal state of the cx based on received window event and emit window event to relevant target.
252fn internal_state_updates(cx: &mut Context, window_event: &WindowEvent, meta: &mut EventMeta) {
253    cx.current = meta.target;
254
255    match window_event {
256        WindowEvent::Drop(drop_data) => {
257            cx.drop_data = Some(drop_data.clone());
258        }
259
260        WindowEvent::MouseMove(x, y) => {
261            if !x.is_nan() && !y.is_nan() {
262                cx.mouse.previous_cursor_x = cx.mouse.cursor_x;
263                cx.mouse.previous_cursor_y = cx.mouse.cursor_y;
264                cx.mouse.cursor_x = *x;
265                cx.mouse.cursor_y = *y;
266
267                hover_system(cx, meta.origin);
268
269                mutate_direct_or_up(meta, cx.captured, cx.hovered, false);
270            }
271
272            // if cx.mouse.cursor_x != cx.mouse.previous_cursor_x
273            //     || cx.mouse.cursor_y != cx.mouse.previous_cursor_y
274            // {
275            // }
276
277            // if let Some(dropped_file) = cx.dropped_file.take() {
278            //     emit_direct_or_up(
279            //         cx,
280            //         WindowEvent::DroppedFile(dropped_file),
281            //         cx.captured,
282            //         cx.hovered,
283            //         true,
284            //     );
285            // }
286        }
287        WindowEvent::MouseDown(button) => {
288            // do direct state-updates
289            match button {
290                MouseButton::Left => {
291                    cx.mouse.left.state = MouseButtonState::Pressed;
292
293                    cx.mouse.left.pos_down = (cx.mouse.cursor_x, cx.mouse.cursor_y);
294                    cx.mouse.left.pressed = cx.hovered;
295                    cx.triggered = cx.hovered;
296
297                    let disabled = cx.style.disabled.get(cx.hovered).copied().unwrap_or_default();
298
299                    if let Some(pseudo_classes) = cx.style.pseudo_classes.get_mut(cx.triggered) {
300                        if !disabled {
301                            pseudo_classes.set(PseudoClassFlags::ACTIVE, true);
302                            cx.needs_restyle(cx.triggered);
303                        }
304                    }
305                    let focusable = cx
306                        .style
307                        .abilities
308                        .get(cx.hovered)
309                        .filter(|abilities| abilities.contains(Abilities::FOCUSABLE))
310                        .is_some();
311
312                    // Reset drag data
313                    cx.drop_data = None;
314
315                    cx.with_current(if focusable { cx.hovered } else { cx.focused }, |cx| {
316                        cx.focus_with_visibility(false)
317                    });
318                }
319                MouseButton::Right => {
320                    cx.mouse.right.state = MouseButtonState::Pressed;
321                    cx.mouse.right.pos_down = (cx.mouse.cursor_x, cx.mouse.cursor_y);
322                    cx.mouse.right.pressed = cx.hovered;
323                }
324                MouseButton::Middle => {
325                    cx.mouse.middle.state = MouseButtonState::Pressed;
326                    cx.mouse.middle.pos_down = (cx.mouse.cursor_x, cx.mouse.cursor_y);
327                    cx.mouse.middle.pressed = cx.hovered;
328                }
329                _ => {}
330            }
331
332            // emit trigger events
333            if matches!(button, MouseButton::Left) {
334                emit_direct_or_up(
335                    cx,
336                    WindowEvent::PressDown { mouse: true },
337                    cx.captured,
338                    cx.triggered,
339                    true,
340                );
341            }
342
343            // track double/triple -click
344            let new_click_time = Instant::now();
345            let click_duration = new_click_time - cx.click_time;
346            let new_click_pos = (cx.mouse.cursor_x, cx.mouse.cursor_y);
347            let double_click_interval = cx.environment().double_click_interval;
348            if click_duration <= double_click_interval
349                && new_click_pos == cx.click_pos
350                && *button == cx.click_button
351            {
352                if cx.clicks <= 2 {
353                    cx.clicks += 1;
354                    let event = if cx.clicks == 3 {
355                        WindowEvent::MouseTripleClick(*button)
356                    } else {
357                        WindowEvent::MouseDoubleClick(*button)
358                    };
359                    meta.consume();
360                    emit_direct_or_up(cx, event, cx.captured, cx.hovered, true);
361                }
362            } else {
363                cx.clicks = 1;
364            }
365            cx.click_time = new_click_time;
366            cx.click_pos = new_click_pos;
367            cx.click_button = *button;
368            mutate_direct_or_up(meta, cx.captured, cx.hovered, true);
369        }
370        WindowEvent::MouseUp(button) => {
371            match button {
372                MouseButton::Left => {
373                    cx.mouse.left.pos_up = (cx.mouse.cursor_x, cx.mouse.cursor_y);
374                    cx.mouse.left.released = cx.hovered;
375                    cx.mouse.left.state = MouseButtonState::Released;
376                }
377                MouseButton::Right => {
378                    cx.mouse.right.pos_up = (cx.mouse.cursor_x, cx.mouse.cursor_y);
379                    cx.mouse.right.released = cx.hovered;
380                    cx.mouse.right.state = MouseButtonState::Released;
381                }
382                MouseButton::Middle => {
383                    cx.mouse.middle.pos_up = (cx.mouse.cursor_x, cx.mouse.cursor_y);
384                    cx.mouse.middle.released = cx.hovered;
385                    cx.mouse.middle.state = MouseButtonState::Released;
386                }
387                _ => {}
388            }
389
390            if matches!(button, MouseButton::Left) {
391                if cx.hovered == cx.triggered {
392                    let disabled = cx.style.disabled.get(cx.hovered).copied().unwrap_or_default();
393
394                    if !disabled {
395                        emit_direct_or_up(
396                            cx,
397                            WindowEvent::Press { mouse: true },
398                            cx.captured,
399                            cx.triggered,
400                            true,
401                        );
402                    }
403                }
404
405                if let Some(pseudo_classes) = cx.style.pseudo_classes.get_mut(cx.triggered) {
406                    pseudo_classes.set(PseudoClassFlags::ACTIVE, false);
407                }
408
409                cx.needs_restyle(cx.triggered);
410
411                cx.triggered = Entity::null();
412            }
413
414            mutate_direct_or_up(meta, cx.captured, cx.hovered, true);
415        }
416        WindowEvent::MouseScroll(_, _) => {
417            meta.target = cx.hovered;
418        }
419        WindowEvent::KeyDown(code, _) => {
420            meta.target = cx.focused;
421
422            #[cfg(debug_assertions)]
423            if *code == Code::KeyP && cx.modifiers.ctrl() {
424                for entity in TreeIterator::full(&cx.tree) {
425                    if let Some(models) = cx.models.get(&entity) {
426                        if !models.is_empty() {
427                            debug!("Models for {}", entity);
428                            for (_, model) in models.iter() {
429                                debug!("M: {:?}", model.name())
430                            }
431                        }
432                    }
433                }
434            }
435
436            #[cfg(debug_assertions)]
437            if *code == Code::KeyI {
438                debug!("Entity tree");
439                let (tree, views, cache) = (&cx.tree, &cx.views, &cx.cache);
440                let has_next_sibling = |entity| tree.get_next_sibling(entity).is_some();
441                let root_indents = |entity: Entity| {
442                    let parent_iter = ParentIterator::new(tree, Some(entity));
443                    parent_iter
444                        .skip(1)
445                        .collect::<Vec<_>>()
446                        .into_iter()
447                        .rev()
448                        .skip(1)
449                        .map(|entity| if has_next_sibling(entity) { "│   " } else { "    " })
450                        .collect::<String>()
451                };
452                let local_idents =
453                    |entity| if has_next_sibling(entity) { "├── " } else { "└── " };
454                let indents = |entity| root_indents(entity) + local_idents(entity);
455
456                for entity in TreeIterator::full(tree).skip(1) {
457                    if let Some(element_name) = views.get(&entity).and_then(|view| view.element()) {
458                        let w = cache.get_bounds(entity).w;
459                        let h = cache.get_bounds(entity).h;
460                        let classes = cx.style.classes.get(entity);
461                        let mut class_names = String::new();
462                        if let Some(classes) = classes {
463                            for class in classes.iter() {
464                                class_names += &format!(".{}", class);
465                            }
466                        }
467                        println!(
468                            "{}{} {}{} [x: {} y: {} w: {} h: {}]",
469                            indents(entity),
470                            entity,
471                            element_name,
472                            class_names,
473                            cache.get_bounds(entity).x,
474                            cache.get_bounds(entity).y,
475                            if w == f32::MAX { "inf".to_string() } else { w.to_string() },
476                            if h == f32::MAX { "inf".to_string() } else { h.to_string() },
477                        );
478                    } else if let Some(binding_name) =
479                        cx.bindings.get(&entity).map(|binding| format!("{:?}", binding))
480                    {
481                        println!(
482                            "{}{} binding observing {}",
483                            indents(entity),
484                            entity,
485                            binding_name,
486                        );
487                    } else {
488                        println!(
489                            "{}{} {}",
490                            indents(entity),
491                            entity,
492                            if views.get(&entity).is_some() {
493                                "unnamed view"
494                            } else {
495                                "no binding or view"
496                            }
497                        );
498                    }
499                }
500            }
501
502            #[cfg(debug_assertions)]
503            if *code == Code::KeyS
504                && cx.modifiers == Modifiers::CTRL | Modifiers::SHIFT | Modifiers::ALT
505            {
506                use crate::systems::compute_element_hash;
507                use vizia_style::selectors::bloom::BloomFilter;
508
509                let mut filter = BloomFilter::default();
510                compute_element_hash(cx.hovered, &cx.tree, &cx.style, &mut filter);
511                let result = compute_matched_rules(cx.hovered, &cx.style, &cx.tree, &filter);
512
513                let entity = cx.hovered;
514                debug!(
515                    "/* Matched rules for Entity: {} Parent: {:?} View: {} posx: {} posy: {} width: {} height: {}",
516                    entity,
517                    entity.parent(&cx.tree),
518                    cx.views
519                        .get(&entity)
520                        .map_or("<None>", |view| view.element().unwrap_or("<Unnamed>")),
521                    cx.cache.get_posx(entity),
522                    cx.cache.get_posy(entity),
523                    cx.cache.get_width(entity),
524                    cx.cache.get_height(entity)
525                );
526                for rule in result.into_iter() {
527                    for selectors in cx.style.rules.iter() {
528                        if *selectors.0 == rule.0 {
529                            debug!("{:?}", selectors.1.selector);
530                        }
531                    }
532                }
533            }
534
535            #[cfg(debug_assertions)]
536            if *code == Code::KeyT
537                && cx.modifiers == Modifiers::CTRL | Modifiers::SHIFT | Modifiers::ALT
538            {
539                // debug!("Loaded font face info:");
540                // for face in cx.text_context.font_system().db().faces() {
541                //     debug!(
542                //         "family: {:?}\npost_script_name: {:?}\nstyle: {:?}\nweight: {:?}\nstretch: {:?}\nmonospaced: {:?}\n",
543                //         face.families,
544                //         face.post_script_name,
545                //         face.style,
546                //         face.weight,
547                //         face.stretch,
548                //         face.monospaced,
549                //     );
550                // }
551            }
552
553            if *code == Code::F5 {
554                EventContext::new(cx).reload_styles().unwrap();
555            }
556
557            if *code == Code::Tab {
558                if cx.ime_state.is_composing() {
559                    return;
560                }
561
562                let lock_focus_to = cx.tree.lock_focus_within(cx.focused);
563
564                // If the locked subtree contains no navigatable items (e.g. a menu popup
565                // where items are focusable but not navigatable), fall back to global
566                // navigation so Tab can escape the lock. Dialog boxes with navigatable
567                // items inside are unaffected.
568                let effective_lock = if lock_focus_to != Entity::root()
569                    && !TreeIterator::full(&cx.tree)
570                        .any(|node| is_navigatable(&cx.tree, &cx.style, node, lock_focus_to))
571                {
572                    Entity::root()
573                } else {
574                    lock_focus_to
575                };
576
577                if cx.modifiers.shift() {
578                    let prev_focused = if let Some(prev_focused) =
579                        focus_backward(&cx.tree, &cx.style, cx.focused, effective_lock)
580                    {
581                        prev_focused
582                    } else {
583                        TreeIterator::full(&cx.tree)
584                            .rfind(|node| {
585                                is_navigatable(&cx.tree, &cx.style, *node, effective_lock)
586                            })
587                            .unwrap_or(Entity::root())
588                    };
589
590                    if prev_focused != cx.focused {
591                        cx.set_focus_pseudo_classes(cx.focused, false, true);
592                        cx.set_focus_pseudo_classes(prev_focused, true, true);
593                        cx.event_queue.push_back(
594                            Event::new(WindowEvent::FocusOut)
595                                .target(cx.focused)
596                                .origin(Entity::root()),
597                        );
598                        cx.event_queue.push_back(
599                            Event::new(WindowEvent::FocusIn)
600                                .target(prev_focused)
601                                .origin(Entity::root()),
602                        );
603                        cx.event_queue.push_back(
604                            Event::new(ScrollEvent::ScrollToView(prev_focused))
605                                .target(prev_focused)
606                                .origin(Entity::root()),
607                        );
608
609                        cx.focused = prev_focused;
610
611                        if let Some(pseudo_classes) = cx.style.pseudo_classes.get_mut(cx.triggered)
612                        {
613                            pseudo_classes.set(PseudoClassFlags::ACTIVE, false);
614                            cx.needs_restyle(cx.triggered);
615                        }
616                        cx.triggered = Entity::null();
617                    }
618                } else {
619                    let next_focused = if let Some(next_focused) =
620                        focus_forward(&cx.tree, &cx.style, cx.focused, effective_lock)
621                    {
622                        next_focused
623                    } else {
624                        TreeIterator::full(&cx.tree)
625                            .find(|node| is_navigatable(&cx.tree, &cx.style, *node, effective_lock))
626                            .unwrap_or(Entity::root())
627                    };
628
629                    if next_focused != cx.focused {
630                        cx.set_focus_pseudo_classes(cx.focused, false, true);
631                        cx.set_focus_pseudo_classes(next_focused, true, true);
632                        cx.event_queue.push_back(
633                            Event::new(WindowEvent::FocusOut)
634                                .target(cx.focused)
635                                .origin(Entity::root()),
636                        );
637                        cx.event_queue.push_back(
638                            Event::new(WindowEvent::FocusIn)
639                                .target(next_focused)
640                                .origin(Entity::root()),
641                        );
642                        cx.event_queue.push_back(
643                            Event::new(ScrollEvent::ScrollToView(next_focused))
644                                .target(next_focused)
645                                .origin(Entity::root()),
646                        );
647
648                        cx.focused = next_focused;
649
650                        if let Some(pseudo_classes) = cx.style.pseudo_classes.get_mut(cx.triggered)
651                        {
652                            pseudo_classes.set(PseudoClassFlags::ACTIVE, false);
653                            cx.needs_restyle(cx.triggered);
654                        }
655                        cx.triggered = Entity::null();
656                    }
657                }
658            }
659
660            if matches!(*code, Code::Enter | Code::NumpadEnter | Code::Space) {
661                cx.triggered = cx.focused;
662                if let Some(pseudo_classes) = cx.style.pseudo_classes.get_mut(cx.triggered) {
663                    pseudo_classes.set(PseudoClassFlags::ACTIVE, true);
664                }
665                cx.with_current(cx.focused, |cx| cx.emit(WindowEvent::PressDown { mouse: false }));
666            }
667        }
668        WindowEvent::KeyUp(code, _) => {
669            meta.target = cx.focused;
670            if matches!(code, Code::Enter | Code::NumpadEnter | Code::Space) {
671                if cx.focused == cx.triggered {
672                    cx.with_current(cx.triggered, |cx| {
673                        cx.emit(WindowEvent::Press { mouse: false })
674                    });
675                }
676                if let Some(pseudo_classes) = cx.style.pseudo_classes.get_mut(cx.triggered) {
677                    pseudo_classes.set(PseudoClassFlags::ACTIVE, false);
678                }
679                cx.needs_restyle(cx.triggered);
680                cx.triggered = Entity::null();
681            }
682        }
683        WindowEvent::CharInput(_) => {
684            meta.target = cx.focused;
685        }
686        WindowEvent::ImeActivate(_) => {
687            meta.target = cx.focused;
688        }
689        WindowEvent::ImeCommit(_) => {
690            meta.target = cx.focused;
691        }
692        WindowEvent::ImePreedit(_, _) => {
693            meta.target = cx.focused;
694        }
695        WindowEvent::SetImeCursorArea(_, _) => {
696            meta.target = cx.focused;
697        }
698        WindowEvent::WindowFocused(is_focused) => {
699            if *is_focused {
700                cx.set_focus_pseudo_classes(cx.focused, true, true);
701                cx.needs_restyle(cx.focused);
702                cx.needs_redraw(cx.focused);
703            } else {
704                cx.set_focus_pseudo_classes(cx.focused, false, true);
705                cx.needs_restyle(cx.focused);
706
707                cx.event_queue.push_back(
708                    Event::new(WindowEvent::FocusVisibility(false))
709                        .target(cx.focused)
710                        .origin(Entity::root()), //.propagate(Propagation::Direct),
711                );
712
713                cx.event_queue.push_back(
714                    Event::new(WindowEvent::MouseOut).target(cx.hovered).origin(Entity::root()), // .propagate(Propagation::Direct),
715                );
716            }
717        }
718        WindowEvent::MouseEnter => {
719            if let Some(pseudo_class) = cx.style.pseudo_classes.get_mut(meta.origin) {
720                pseudo_class.set(PseudoClassFlags::OVER, true);
721            }
722        }
723        WindowEvent::MouseLeave => {
724            if let Some(pseudo_class) = cx.style.pseudo_classes.get_mut(meta.origin) {
725                pseudo_class.set(PseudoClassFlags::OVER, false);
726            }
727
728            let parent_iter = LayoutParentIterator::new(&cx.tree, cx.hovered);
729            for ancestor in parent_iter {
730                if let Some(pseudo_classes) = cx.style.pseudo_classes.get_mut(ancestor) {
731                    pseudo_classes.set(PseudoClassFlags::HOVER, false);
732                    cx.style.needs_restyle(ancestor);
733                }
734            }
735
736            cx.hovered = Entity::null();
737        }
738
739        _ => {}
740    }
741}
742
743fn mutate_direct_or_up(meta: &mut EventMeta, direct: Entity, up: Entity, root: bool) {
744    if direct != Entity::null() {
745        meta.target = direct;
746        meta.propagation = Propagation::Direct;
747    } else if up != Entity::root() || root {
748        meta.target = up;
749        meta.propagation = Propagation::Up;
750    } else {
751        meta.consume();
752    }
753}
754
755fn emit_direct_or_up<M: Any + Send>(
756    cx: &mut Context,
757    message: M,
758    direct: Entity,
759    up: Entity,
760    root: bool,
761) {
762    let mut event = Event::new(message);
763    mutate_direct_or_up(&mut event.meta, direct, up, root);
764    cx.emit_custom(event);
765}