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