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