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#[doc(hidden)]
21pub struct EventManager {
22 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 pub fn flush_events(
40 &mut self,
41 cx: &mut Context,
42 mut window_event_callback: impl FnMut(&WindowEvent),
43 ) {
44 while {
45 self.event_queue.clear();
47
48 self.event_queue.extend(cx.event_queue.drain(0..));
51
52 'events: for event in self.event_queue.iter_mut() {
54 let keyboard_lock_root = keyboard_event_lock_root(cx, event);
55 let mut is_drop_event = false;
56 event.map(|window_event: &WindowEvent, _| {
57 is_drop_event = matches!(window_event, WindowEvent::Drop(_));
58 });
59
60 event.take(|internal_event, _| match internal_event {
62 InternalEvent::Redraw => cx.needs_redraw(Entity::root()),
63 InternalEvent::LoadImage { path, image, policy } => {
64 if let Some(image) = image.lock().unwrap().take() {
65 ResourceContext::new(cx).load_image(path, image, policy);
66 }
67 }
68 });
69
70 let mut global_listeners = vec![];
72 std::mem::swap(&mut cx.global_listeners, &mut global_listeners);
73 for listener in &global_listeners {
74 cx.with_current(Entity::root(), |cx| {
75 listener(&mut EventContext::new(cx), event)
76 });
77 }
78 std::mem::swap(&mut cx.global_listeners, &mut global_listeners);
79
80 let listeners = cx.listeners.keys().copied().collect::<Vec<Entity>>();
82 for entity in listeners {
83 if let Some(lock_root) = keyboard_lock_root {
84 if !entity.is_descendant_of(&cx.tree, lock_root) {
85 continue;
86 }
87 }
88
89 if let Some(listener) = cx.listeners.remove(&entity) {
90 if let Some(mut event_handler) = cx.views.remove(&entity) {
91 cx.with_current(entity, |cx| {
92 (listener)(
93 event_handler.as_mut(),
94 &mut EventContext::new(cx),
95 event,
96 );
97 });
98
99 cx.views.insert(entity, event_handler);
100 }
101
102 cx.listeners.insert(entity, listener);
103 }
104
105 if event.meta.consumed {
106 clear_drop_state_for_drop_event(is_drop_event, &mut cx.drop_data);
107 continue 'events;
108 }
109 }
110
111 event.map(|window_event, meta| {
113 if cx.windows.contains_key(&meta.origin) {
114 internal_state_updates(cx, window_event, meta);
115 }
116 });
117
118 if event.meta.consumed {
120 clear_drop_state_for_drop_event(is_drop_event, &mut cx.drop_data);
121 continue 'events;
122 }
123
124 let cx = &mut EventContext::new(cx);
125
126 let target = event.meta.target;
128
129 if keyboard_lock_root
131 .map(|lock_root| target.is_descendant_of(cx.tree, lock_root))
132 .unwrap_or(true)
133 {
134 visit_entity(cx, target, event);
135 }
136
137 if event.meta.consumed {
139 clear_drop_state_for_drop_event(is_drop_event, &mut *cx.drop_data);
140 continue 'events;
141 }
142
143 if event.meta.propagation == Propagation::Up {
145 let iter = target.parent_iter(cx.tree).skip(1);
147
148 for entity in iter {
149 if let Some(lock_root) = keyboard_lock_root {
150 if !entity.is_descendant_of(cx.tree, lock_root) {
151 break;
152 }
153 }
154
155 visit_entity(cx, entity, event);
157
158 if event.meta.consumed {
160 clear_drop_state_for_drop_event(is_drop_event, &mut *cx.drop_data);
161 continue 'events;
162 }
163 }
164 }
165
166 if event.meta.propagation == Propagation::Subtree {
168 let iter = target.branch_iter(cx.tree).skip(1);
170
171 for entity in iter {
172 if keyboard_lock_root
173 .map(|lock_root| entity.is_descendant_of(cx.tree, lock_root))
174 .unwrap_or(true)
175 {
176 visit_entity(cx, entity, event);
178 }
179
180 if event.meta.consumed {
182 clear_drop_state_for_drop_event(is_drop_event, &mut *cx.drop_data);
183 continue 'events;
184 }
185 }
186 }
187
188 event.map(|window_event: &WindowEvent, _| {
189 (window_event_callback)(window_event);
190 });
191
192 clear_drop_state_for_drop_event(is_drop_event, &mut *cx.drop_data);
193 }
194
195 binding_system(cx);
196
197 !cx.event_queue.is_empty()
199 } {}
200 }
201}
202
203fn clear_drop_state_for_drop_event(is_drop_event: bool, drop_data: &mut Option<DropData>) {
204 if is_drop_event && drop_data.is_some() {
205 *drop_data = None;
206 }
207}
208
209fn is_keyboard_window_event(window_event: &WindowEvent) -> bool {
210 matches!(
211 window_event,
212 WindowEvent::KeyDown(_, _)
213 | WindowEvent::KeyUp(_, _)
214 | WindowEvent::CharInput(_)
215 | WindowEvent::ImeActivate(_)
216 | WindowEvent::ImeCommit(_)
217 | WindowEvent::ImePreedit(_, _)
218 | WindowEvent::SetImeCursorArea(_, _)
219 )
220}
221
222fn keyboard_event_lock_root(cx: &Context, event: &mut Event) -> Option<Entity> {
223 let mut lock_root = None;
224
225 event.map(|window_event: &WindowEvent, _| {
226 if is_keyboard_window_event(window_event) {
227 let candidate = cx.tree.lock_focus_within(cx.focused);
228 if candidate != Entity::root() {
229 lock_root = Some(candidate);
230 }
231 }
232 });
233
234 lock_root
235}
236
237fn visit_entity(cx: &mut EventContext, entity: Entity, event: &mut Event) {
238 if let Some(ids) =
240 cx.models.get(&entity).map(|models| models.keys().cloned().collect::<Vec<_>>())
241 {
242 for id in ids {
243 if let Some(mut model) =
244 cx.models.get_mut(&entity).and_then(|models| models.remove(&id))
245 {
246 cx.current = entity;
247
248 model.event(cx, event);
249
250 cx.models.get_mut(&entity).and_then(|models| models.insert(id, model));
251 }
252 }
253 }
254
255 if event.meta.consumed {
257 return;
258 }
259
260 if let Some(mut view) = cx.views.remove(&entity) {
262 cx.current = entity;
263 view.event(cx, event);
264
265 cx.views.insert(entity, view);
266 }
267}
268
269fn internal_state_updates(cx: &mut Context, window_event: &WindowEvent, meta: &mut EventMeta) {
271 cx.current = meta.target;
272
273 match window_event {
274 WindowEvent::Drop(drop_data) => {
275 cx.drop_data = Some(drop_data.clone());
276 }
277
278 WindowEvent::MouseMove(x, y) => {
279 if !x.is_nan() && !y.is_nan() {
280 cx.mouse.previous_cursor_x = cx.mouse.cursor_x;
281 cx.mouse.previous_cursor_y = cx.mouse.cursor_y;
282 cx.mouse.cursor_x = *x;
283 cx.mouse.cursor_y = *y;
284
285 hover_system(cx, meta.origin);
286 if cx.drop_data.is_some() || cx.drag_hovered != Entity::null() {
287 dispatch_drag_events(cx, *x, *y);
288 }
289
290 if let Some(drag_view) = cx.active_drag_view {
291 if cx.drop_data.is_some() {
292 position_drag_view(cx, drag_view, *x, *y);
293 } else {
294 hide_drag_view(cx, drag_view);
295 cx.active_drag_view = None;
296 }
297 }
298
299 mutate_direct_or_up(meta, cx.captured, cx.hovered, false);
300 }
301
302 }
317 WindowEvent::MouseDown(button) => {
318 match button {
320 MouseButton::Left => {
321 cx.mouse.left.state = MouseButtonState::Pressed;
322
323 cx.mouse.left.pos_down = (cx.mouse.cursor_x, cx.mouse.cursor_y);
324 cx.mouse.left.pressed = cx.hovered;
325 cx.triggered = cx.hovered;
326
327 let disabled = cx.style.disabled.get(cx.hovered).copied().unwrap_or_default();
328
329 if let Some(pseudo_classes) = cx.style.pseudo_classes.get_mut(cx.triggered) {
330 if !disabled {
331 pseudo_classes.set(PseudoClassFlags::ACTIVE, true);
332 cx.needs_restyle(cx.triggered);
333 }
334 }
335 let focusable = cx
336 .style
337 .abilities
338 .get(cx.hovered)
339 .filter(|abilities| abilities.contains(Abilities::FOCUSABLE))
340 .is_some();
341
342 cx.drop_data = None;
344 cx.drag_hovered = Entity::null();
345 if let Some(drag_view) = cx.active_drag_view.take() {
346 hide_drag_view(cx, drag_view);
347 }
348
349 cx.with_current(if focusable { cx.hovered } else { cx.focused }, |cx| {
350 cx.focus_with_visibility(false)
351 });
352 }
353 MouseButton::Right => {
354 cx.mouse.right.state = MouseButtonState::Pressed;
355 cx.mouse.right.pos_down = (cx.mouse.cursor_x, cx.mouse.cursor_y);
356 cx.mouse.right.pressed = cx.hovered;
357 }
358 MouseButton::Middle => {
359 cx.mouse.middle.state = MouseButtonState::Pressed;
360 cx.mouse.middle.pos_down = (cx.mouse.cursor_x, cx.mouse.cursor_y);
361 cx.mouse.middle.pressed = cx.hovered;
362 }
363 _ => {}
364 }
365
366 if matches!(button, MouseButton::Left) {
368 emit_direct_or_up(
369 cx,
370 WindowEvent::PressDown { mouse: true },
371 cx.captured,
372 cx.triggered,
373 true,
374 );
375 }
376
377 let new_click_time = Instant::now();
379 let click_duration = new_click_time - cx.click_time;
380 let new_click_pos = (cx.mouse.cursor_x, cx.mouse.cursor_y);
381 let double_click_interval = cx.environment().double_click_interval;
382 if click_duration <= double_click_interval
383 && new_click_pos == cx.click_pos
384 && *button == cx.click_button
385 {
386 if cx.clicks <= 2 {
387 cx.clicks += 1;
388 let event = if cx.clicks == 3 {
389 WindowEvent::MouseTripleClick(*button)
390 } else {
391 WindowEvent::MouseDoubleClick(*button)
392 };
393 meta.consume();
394 emit_direct_or_up(cx, event, cx.captured, cx.hovered, true);
395 }
396 } else {
397 cx.clicks = 1;
398 }
399 cx.click_time = new_click_time;
400 cx.click_pos = new_click_pos;
401 cx.click_button = *button;
402 mutate_direct_or_up(meta, cx.captured, cx.hovered, true);
403 }
404 WindowEvent::MouseUp(button) => {
405 let had_drop_data = cx.drop_data.is_some();
406
407 if let Some(drag_view) = cx.active_drag_view.take() {
408 hide_drag_view(cx, drag_view);
409 }
410
411 match button {
412 MouseButton::Left => {
413 cx.mouse.left.pos_up = (cx.mouse.cursor_x, cx.mouse.cursor_y);
414 cx.mouse.left.released = cx.hovered;
415 cx.mouse.left.state = MouseButtonState::Released;
416 }
417 MouseButton::Right => {
418 cx.mouse.right.pos_up = (cx.mouse.cursor_x, cx.mouse.cursor_y);
419 cx.mouse.right.released = cx.hovered;
420 cx.mouse.right.state = MouseButtonState::Released;
421 }
422 MouseButton::Middle => {
423 cx.mouse.middle.pos_up = (cx.mouse.cursor_x, cx.mouse.cursor_y);
424 cx.mouse.middle.released = cx.hovered;
425 cx.mouse.middle.state = MouseButtonState::Released;
426 }
427 _ => {}
428 }
429
430 if matches!(button, MouseButton::Left) {
431 if cx.hovered == cx.triggered {
432 let disabled = cx.style.disabled.get(cx.hovered).copied().unwrap_or_default();
433
434 if !disabled {
435 emit_direct_or_up(
436 cx,
437 WindowEvent::Press { mouse: true },
438 cx.captured,
439 cx.triggered,
440 true,
441 );
442 }
443 }
444
445 if let Some(pseudo_classes) = cx.style.pseudo_classes.get_mut(cx.triggered) {
446 pseudo_classes.set(PseudoClassFlags::ACTIVE, false);
447 }
448
449 cx.needs_restyle(cx.triggered);
450
451 cx.triggered = Entity::null();
452 }
453
454 if had_drop_data {
455 let drop_data = cx.drop_data.take();
456
457 if cx.drag_hovered != Entity::null() {
459 if let Some(data) = drop_data {
460 cx.event_queue
461 .push_back(Event::new(WindowEvent::Drop(data)).target(cx.drag_hovered));
462 }
463
464 cx.event_queue
466 .push_back(Event::new(WindowEvent::DragLeave).target(cx.drag_hovered));
467 }
468 cx.drag_hovered = Entity::null();
469 }
470 mutate_direct_or_up(meta, cx.captured, cx.hovered, true);
475 }
476 WindowEvent::MouseScroll(_, _) => {
477 meta.target = cx.hovered;
478 }
479 WindowEvent::KeyDown(code, _) => {
480 meta.target = cx.focused;
481
482 #[cfg(debug_assertions)]
483 if *code == Code::KeyP && cx.modifiers.ctrl() {
484 for entity in TreeIterator::full(&cx.tree) {
485 if let Some(models) = cx.models.get(&entity) {
486 if !models.is_empty() {
487 debug!("Models for {}", entity);
488 for (_, model) in models.iter() {
489 debug!("M: {:?}", model.name())
490 }
491 }
492 }
493 }
494 }
495
496 #[cfg(debug_assertions)]
497 if *code == Code::KeyI {
498 debug!("Entity tree");
499 let (tree, views, cache) = (&cx.tree, &cx.views, &cx.cache);
500 let has_next_sibling = |entity| tree.get_next_sibling(entity).is_some();
501 let root_indents = |entity: Entity| {
502 let parent_iter = ParentIterator::new(tree, Some(entity));
503 parent_iter
504 .skip(1)
505 .collect::<Vec<_>>()
506 .into_iter()
507 .rev()
508 .skip(1)
509 .map(|entity| if has_next_sibling(entity) { "│ " } else { " " })
510 .collect::<String>()
511 };
512 let local_idents =
513 |entity| if has_next_sibling(entity) { "├── " } else { "└── " };
514 let indents = |entity| root_indents(entity) + local_idents(entity);
515
516 for entity in TreeIterator::full(tree).skip(1) {
517 if let Some(element_name) = views.get(&entity).and_then(|view| view.element()) {
518 let w = cache.get_bounds(entity).w;
519 let h = cache.get_bounds(entity).h;
520 let classes = cx.style.classes.get(entity);
521 let mut class_names = String::new();
522 if let Some(classes) = classes {
523 for class in classes.iter() {
524 class_names += &format!(".{}", class);
525 }
526 }
527 println!(
528 "{}{} {}{} [x: {} y: {} w: {} h: {}]",
529 indents(entity),
530 entity,
531 element_name,
532 class_names,
533 cache.get_bounds(entity).x,
534 cache.get_bounds(entity).y,
535 if w == f32::MAX { "inf".to_string() } else { w.to_string() },
536 if h == f32::MAX { "inf".to_string() } else { h.to_string() },
537 );
538 } else if let Some(binding_name) =
539 cx.bindings.get(&entity).map(|binding| format!("{:?}", binding))
540 {
541 println!(
542 "{}{} binding observing {}",
543 indents(entity),
544 entity,
545 binding_name,
546 );
547 } else {
548 println!(
549 "{}{} {}",
550 indents(entity),
551 entity,
552 if views.get(&entity).is_some() {
553 "unnamed view"
554 } else {
555 "no binding or view"
556 }
557 );
558 }
559 }
560 }
561
562 #[cfg(debug_assertions)]
563 if *code == Code::KeyS
564 && cx.modifiers == Modifiers::CTRL | Modifiers::SHIFT | Modifiers::ALT
565 {
566 use crate::systems::compute_element_hash;
567 use vizia_style::selectors::bloom::BloomFilter;
568
569 let mut filter = BloomFilter::default();
570 compute_element_hash(cx.hovered, &cx.tree, &cx.style, &mut filter);
571 let result = compute_matched_rules(cx.hovered, &cx.style, &cx.tree, &filter);
572
573 let entity = cx.hovered;
574 debug!(
575 "/* Matched rules for Entity: {} Parent: {:?} View: {} posx: {} posy: {} width: {} height: {}",
576 entity,
577 entity.parent(&cx.tree),
578 cx.views
579 .get(&entity)
580 .map_or("<None>", |view| view.element().unwrap_or("<Unnamed>")),
581 cx.cache.get_posx(entity),
582 cx.cache.get_posy(entity),
583 cx.cache.get_width(entity),
584 cx.cache.get_height(entity)
585 );
586 for rule in result.into_iter() {
587 for selectors in cx.style.rules.iter() {
588 if *selectors.0 == rule.0 {
589 debug!("{:?}", selectors.1.selector);
590 }
591 }
592 }
593 }
594
595 #[cfg(debug_assertions)]
596 if *code == Code::KeyT
597 && cx.modifiers == Modifiers::CTRL | Modifiers::SHIFT | Modifiers::ALT
598 {
599 }
612
613 if *code == Code::F5 {
614 EventContext::new(cx).reload_styles().unwrap();
615 }
616
617 if *code == Code::Tab {
618 if cx.ime_state.is_composing() {
619 return;
620 }
621
622 let lock_focus_to = cx.tree.lock_focus_within(cx.focused);
623
624 let effective_lock = if lock_focus_to != Entity::root()
629 && !TreeIterator::full(&cx.tree)
630 .any(|node| is_navigatable(&cx.tree, &cx.style, node, lock_focus_to))
631 {
632 Entity::root()
633 } else {
634 lock_focus_to
635 };
636
637 if cx.modifiers.shift() {
638 let prev_focused = if let Some(prev_focused) =
639 focus_backward(&cx.tree, &cx.style, cx.focused, effective_lock)
640 {
641 prev_focused
642 } else {
643 TreeIterator::full(&cx.tree)
644 .rfind(|node| {
645 is_navigatable(&cx.tree, &cx.style, *node, effective_lock)
646 })
647 .unwrap_or(Entity::root())
648 };
649
650 if prev_focused != cx.focused {
651 cx.set_focus_pseudo_classes(cx.focused, false, true);
652 cx.set_focus_pseudo_classes(prev_focused, true, true);
653 cx.event_queue.push_back(
654 Event::new(WindowEvent::FocusOut)
655 .target(cx.focused)
656 .origin(Entity::root()),
657 );
658 cx.event_queue.push_back(
659 Event::new(WindowEvent::FocusIn)
660 .target(prev_focused)
661 .origin(Entity::root()),
662 );
663 cx.event_queue.push_back(
664 Event::new(ScrollEvent::ScrollToView(prev_focused))
665 .target(prev_focused)
666 .origin(Entity::root()),
667 );
668
669 cx.focused = prev_focused;
670
671 if let Some(pseudo_classes) = cx.style.pseudo_classes.get_mut(cx.triggered)
672 {
673 pseudo_classes.set(PseudoClassFlags::ACTIVE, false);
674 cx.needs_restyle(cx.triggered);
675 }
676 cx.triggered = Entity::null();
677 }
678 } else {
679 let next_focused = if let Some(next_focused) =
680 focus_forward(&cx.tree, &cx.style, cx.focused, effective_lock)
681 {
682 next_focused
683 } else {
684 TreeIterator::full(&cx.tree)
685 .find(|node| is_navigatable(&cx.tree, &cx.style, *node, effective_lock))
686 .unwrap_or(Entity::root())
687 };
688
689 if next_focused != cx.focused {
690 cx.set_focus_pseudo_classes(cx.focused, false, true);
691 cx.set_focus_pseudo_classes(next_focused, true, true);
692 cx.event_queue.push_back(
693 Event::new(WindowEvent::FocusOut)
694 .target(cx.focused)
695 .origin(Entity::root()),
696 );
697 cx.event_queue.push_back(
698 Event::new(WindowEvent::FocusIn)
699 .target(next_focused)
700 .origin(Entity::root()),
701 );
702 cx.event_queue.push_back(
703 Event::new(ScrollEvent::ScrollToView(next_focused))
704 .target(next_focused)
705 .origin(Entity::root()),
706 );
707
708 cx.focused = next_focused;
709
710 if let Some(pseudo_classes) = cx.style.pseudo_classes.get_mut(cx.triggered)
711 {
712 pseudo_classes.set(PseudoClassFlags::ACTIVE, false);
713 cx.needs_restyle(cx.triggered);
714 }
715 cx.triggered = Entity::null();
716 }
717 }
718 }
719
720 if matches!(*code, Code::Enter | Code::NumpadEnter | Code::Space) {
721 cx.triggered = cx.focused;
722 if let Some(pseudo_classes) = cx.style.pseudo_classes.get_mut(cx.triggered) {
723 pseudo_classes.set(PseudoClassFlags::ACTIVE, true);
724 }
725 cx.with_current(cx.focused, |cx| cx.emit(WindowEvent::PressDown { mouse: false }));
726 }
727 }
728 WindowEvent::KeyUp(code, _) => {
729 meta.target = cx.focused;
730 if matches!(code, Code::Enter | Code::NumpadEnter | Code::Space) {
731 if cx.focused == cx.triggered {
732 cx.with_current(cx.triggered, |cx| {
733 cx.emit(WindowEvent::Press { mouse: false })
734 });
735 }
736 if let Some(pseudo_classes) = cx.style.pseudo_classes.get_mut(cx.triggered) {
737 pseudo_classes.set(PseudoClassFlags::ACTIVE, false);
738 }
739 cx.needs_restyle(cx.triggered);
740 cx.triggered = Entity::null();
741 }
742 }
743 WindowEvent::CharInput(_) => {
744 meta.target = cx.focused;
745 }
746 WindowEvent::ImeActivate(_) => {
747 meta.target = cx.focused;
748 }
749 WindowEvent::ImeCommit(_) => {
750 meta.target = cx.focused;
751 }
752 WindowEvent::ImePreedit(_, _) => {
753 meta.target = cx.focused;
754 }
755 WindowEvent::SetImeCursorArea(_, _) => {
756 meta.target = cx.focused;
757 }
758 WindowEvent::WindowFocused(is_focused) => {
759 if *is_focused {
760 cx.set_focus_pseudo_classes(cx.focused, true, true);
761 cx.needs_restyle(cx.focused);
762 cx.needs_redraw(cx.focused);
763 } else {
764 cx.set_focus_pseudo_classes(cx.focused, false, true);
765 cx.needs_restyle(cx.focused);
766
767 cx.event_queue.push_back(
768 Event::new(WindowEvent::FocusVisibility(false))
769 .target(cx.focused)
770 .origin(Entity::root()), );
772
773 cx.event_queue.push_back(
774 Event::new(WindowEvent::MouseOut).target(cx.hovered).origin(Entity::root()), );
776 }
777 }
778 WindowEvent::MouseEnter => {
779 if let Some(pseudo_class) = cx.style.pseudo_classes.get_mut(meta.origin) {
780 pseudo_class.set(PseudoClassFlags::OVER, true);
781 }
782 }
783 WindowEvent::MouseLeave => {
784 if let Some(pseudo_class) = cx.style.pseudo_classes.get_mut(meta.origin) {
785 pseudo_class.set(PseudoClassFlags::OVER, false);
786 }
787
788 let parent_iter = LayoutParentIterator::new(&cx.tree, cx.hovered);
789 for ancestor in parent_iter {
790 if let Some(pseudo_classes) = cx.style.pseudo_classes.get_mut(ancestor) {
791 pseudo_classes.set(PseudoClassFlags::HOVER, false);
792 cx.style.needs_restyle(ancestor);
793 }
794 }
795
796 cx.hovered = Entity::null();
797 }
798
799 _ => {}
800 }
801}
802
803fn position_drag_view(cx: &mut Context, drag_view: Entity, x: f32, y: f32) {
804 if !cx.entity_manager.is_alive(drag_view) {
805 cx.active_drag_view = None;
806 return;
807 }
808
809 let left = cx.style.physical_to_logical(x);
810 let top = cx.style.physical_to_logical(y);
811
812 cx.with_current(drag_view, |cx| {
813 let mut ex = EventContext::new(cx);
814 ex.set_display(Display::Flex);
815 ex.set_left(Units::Pixels(left));
816 ex.set_top(Units::Pixels(top));
817 });
818}
819
820fn hide_drag_view(cx: &mut Context, drag_view: Entity) {
821 if !cx.entity_manager.is_alive(drag_view) {
822 return;
823 }
824
825 cx.with_current(drag_view, |cx| {
826 let mut ex = EventContext::new(cx);
827 ex.set_display(Display::None);
828 });
829}
830
831fn dispatch_drag_events(cx: &mut Context, x: f32, y: f32) {
832 if cx.drop_data.is_some() {
833 let hovered = cx.hovered;
834
835 if hovered != cx.drag_hovered {
836 if cx.drag_hovered != Entity::null() {
837 cx.event_queue
838 .push_back(Event::new(WindowEvent::DragLeave).target(cx.drag_hovered));
839 }
840
841 if hovered != Entity::null() {
842 cx.event_queue.push_back(Event::new(WindowEvent::DragEnter).target(hovered));
843 }
844
845 cx.drag_hovered = hovered;
846 }
847
848 if hovered != Entity::null() {
849 cx.event_queue.push_back(Event::new(WindowEvent::DragMove(x, y)).target(hovered));
850 }
851 } else if cx.drag_hovered != Entity::null() {
852 cx.event_queue.push_back(Event::new(WindowEvent::DragLeave).target(cx.drag_hovered));
853 cx.drag_hovered = Entity::null();
854 }
855}
856
857fn mutate_direct_or_up(meta: &mut EventMeta, direct: Entity, up: Entity, root: bool) {
858 if direct != Entity::null() {
859 meta.target = direct;
860 meta.propagation = Propagation::Direct;
861 } else if up != Entity::root() || root {
862 meta.target = up;
863 meta.propagation = Propagation::Up;
864 } else {
865 meta.consume();
866 }
867}
868
869fn emit_direct_or_up<M: Any + Send>(
870 cx: &mut Context,
871 message: M,
872 direct: Entity,
873 up: Entity,
874 root: bool,
875) {
876 let mut event = Event::new(message);
877 mutate_direct_or_up(&mut event.meta, direct, up, root);
878 cx.emit_custom(event);
879}