1use std::{
2 borrow::Cow,
3 fmt,
4 pin::Pin,
5 task::Waker,
6};
7
8use accesskit_winit::WindowEvent as AccessibilityWindowEvent;
9use freya_core::integration::*;
10use freya_engine::prelude::{
11 FontCollection,
12 FontMgr,
13};
14use futures_lite::future::FutureExt as _;
15use futures_util::{
16 FutureExt as _,
17 StreamExt,
18 select,
19};
20use ragnarok::{
21 EventsExecutorRunner,
22 EventsMeasurerRunner,
23};
24use rustc_hash::FxHashMap;
25use torin::prelude::{
26 CursorPoint,
27 Size2D,
28};
29use winit::{
30 application::ApplicationHandler,
31 event::{
32 ElementState,
33 Ime,
34 MouseScrollDelta,
35 Touch,
36 TouchPhase,
37 WindowEvent,
38 },
39 event_loop::{
40 ActiveEventLoop,
41 EventLoopProxy,
42 },
43 window::{
44 Theme,
45 Window,
46 WindowId,
47 },
48};
49
50use crate::{
51 accessibility::AccessibilityTask,
52 config::WindowConfig,
53 plugins::{
54 PluginEvent,
55 PluginHandle,
56 PluginsManager,
57 },
58 window::AppWindow,
59 winit_mappings::{
60 self,
61 map_winit_mouse_button,
62 map_winit_touch_force,
63 map_winit_touch_phase,
64 },
65};
66
67pub struct WinitRenderer {
68 pub windows_configs: Vec<WindowConfig>,
69 #[cfg(feature = "tray")]
70 pub(crate) tray: (
71 Option<crate::config::TrayIconGetter>,
72 Option<crate::config::TrayHandler>,
73 ),
74 pub resumed: bool,
75 pub windows: FxHashMap<WindowId, AppWindow>,
76 pub proxy: EventLoopProxy<NativeEvent>,
77 pub plugins: PluginsManager,
78 pub fallback_fonts: Vec<Cow<'static, str>>,
79 pub screen_reader: ScreenReader,
80 pub font_manager: FontMgr,
81 pub font_collection: FontCollection,
82 pub futures: Vec<Pin<Box<dyn std::future::Future<Output = ()>>>>,
83 pub waker: Waker,
84}
85
86pub struct RendererContext<'a> {
87 pub windows: &'a mut FxHashMap<WindowId, AppWindow>,
88 pub proxy: &'a mut EventLoopProxy<NativeEvent>,
89 pub plugins: &'a mut PluginsManager,
90 pub fallback_fonts: &'a mut Vec<Cow<'static, str>>,
91 pub screen_reader: &'a mut ScreenReader,
92 pub font_manager: &'a mut FontMgr,
93 pub font_collection: &'a mut FontCollection,
94 pub(crate) active_event_loop: &'a ActiveEventLoop,
95}
96
97impl RendererContext<'_> {
98 pub fn launch_window(&mut self, window_config: WindowConfig) -> WindowId {
99 let app_window = AppWindow::new(
100 window_config,
101 self.active_event_loop,
102 self.proxy,
103 self.plugins,
104 self.font_collection,
105 self.font_manager,
106 self.fallback_fonts,
107 self.screen_reader.clone(),
108 );
109
110 let window_id = app_window.window.id();
111
112 self.proxy
113 .send_event(NativeEvent::Window(NativeWindowEvent {
114 window_id,
115 action: NativeWindowEventAction::PollRunner,
116 }))
117 .ok();
118
119 self.windows.insert(window_id, app_window);
120
121 window_id
122 }
123
124 pub fn windows(&self) -> &FxHashMap<WindowId, AppWindow> {
125 self.windows
126 }
127
128 pub fn windows_mut(&mut self) -> &mut FxHashMap<WindowId, AppWindow> {
129 self.windows
130 }
131
132 pub fn exit(&mut self) {
133 self.active_event_loop.exit();
134 }
135}
136
137#[derive(Debug)]
138pub enum NativeWindowEventAction {
139 PollRunner,
140
141 Accessibility(AccessibilityWindowEvent),
142
143 PlatformEvent(PlatformEvent),
144
145 User(UserEvent),
146}
147
148pub struct WithWindowCallback(pub(crate) Box<dyn FnOnce(&mut Window)>);
149
150impl fmt::Debug for WithWindowCallback {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 f.write_str("WithWindowCallback")
153 }
154}
155
156#[derive(Clone)]
158pub struct LaunchProxy(pub EventLoopProxy<NativeEvent>);
159
160impl LaunchProxy {
161 pub fn with<F, T: Send + 'static>(&self, f: F) -> futures_channel::oneshot::Receiver<T>
163 where
164 F: FnOnce(&mut RendererContext) -> T + Send + 'static,
165 {
166 let (tx, rx) = futures_channel::oneshot::channel::<T>();
167 let cb = Box::new(move |ctx: &mut RendererContext| {
168 let res = (f)(ctx);
169 let _ = tx.send(res);
170 });
171 let _ = self
172 .0
173 .send_event(NativeEvent::Generic(NativeGenericEvent::RendererCallback(
174 cb,
175 )));
176 rx
177 }
178}
179
180#[derive(Debug)]
181pub enum NativeWindowErasedEventAction {
182 LaunchWindow {
183 window_config: WindowConfig,
184 ack: futures_channel::oneshot::Sender<WindowId>,
185 },
186 CloseWindow(WindowId),
187 WithWindow {
188 window_id: Option<WindowId>,
189 callback: WithWindowCallback,
190 },
191}
192
193#[derive(Debug)]
194pub struct NativeWindowEvent {
195 pub window_id: WindowId,
196 pub action: NativeWindowEventAction,
197}
198
199#[cfg(feature = "tray")]
200#[derive(Debug)]
201pub enum NativeTrayEventAction {
202 TrayEvent(tray_icon::TrayIconEvent),
203 MenuEvent(tray_icon::menu::MenuEvent),
204 LaunchWindow(SingleThreadErasedEvent),
205}
206
207#[cfg(feature = "tray")]
208#[derive(Debug)]
209pub struct NativeTrayEvent {
210 pub action: NativeTrayEventAction,
211}
212
213pub enum NativeGenericEvent {
214 PollFutures,
215 RendererCallback(Box<dyn FnOnce(&mut RendererContext) + Send + 'static>),
216}
217
218impl fmt::Debug for NativeGenericEvent {
219 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220 match self {
221 NativeGenericEvent::PollFutures => f.write_str("PollFutures"),
222 NativeGenericEvent::RendererCallback(_) => f.write_str("RendererCallback"),
223 }
224 }
225}
226
227#[derive(Debug)]
228pub enum NativeEvent {
229 Window(NativeWindowEvent),
230 #[cfg(feature = "tray")]
231 Tray(NativeTrayEvent),
232 Generic(NativeGenericEvent),
233}
234
235impl From<accesskit_winit::Event> for NativeEvent {
236 fn from(event: accesskit_winit::Event) -> Self {
237 NativeEvent::Window(NativeWindowEvent {
238 window_id: event.window_id,
239 action: NativeWindowEventAction::Accessibility(event.window_event),
240 })
241 }
242}
243
244impl ApplicationHandler<NativeEvent> for WinitRenderer {
245 fn resumed(&mut self, active_event_loop: &winit::event_loop::ActiveEventLoop) {
246 if !self.resumed {
247 #[cfg(feature = "tray")]
248 {
249 #[cfg(not(target_os = "linux"))]
250 if let Some(tray_icon) = self.tray.0.take() {
251 let _tray_icon = (tray_icon)();
252 }
253
254 #[cfg(target_os = "macos")]
255 {
256 use objc2_core_foundation::CFRunLoop;
257
258 let rl = CFRunLoop::main().expect("Failed to run CFRunLoop");
259 CFRunLoop::wake_up(&rl);
260 }
261 }
262
263 for window_config in self.windows_configs.drain(..) {
264 let app_window = AppWindow::new(
265 window_config,
266 active_event_loop,
267 &self.proxy,
268 &mut self.plugins,
269 &self.font_collection,
270 &self.font_manager,
271 &self.fallback_fonts,
272 self.screen_reader.clone(),
273 );
274
275 self.proxy
276 .send_event(NativeEvent::Window(NativeWindowEvent {
277 window_id: app_window.window.id(),
278 action: NativeWindowEventAction::PollRunner,
279 }))
280 .ok();
281
282 self.windows.insert(app_window.window.id(), app_window);
283 }
284 self.resumed = true;
285
286 let _ = self
287 .proxy
288 .send_event(NativeEvent::Generic(NativeGenericEvent::PollFutures));
289 }
290 }
291
292 fn user_event(
293 &mut self,
294 active_event_loop: &winit::event_loop::ActiveEventLoop,
295 event: NativeEvent,
296 ) {
297 match event {
298 NativeEvent::Generic(NativeGenericEvent::RendererCallback(cb)) => {
299 let mut renderer_context = RendererContext {
300 fallback_fonts: &mut self.fallback_fonts,
301 active_event_loop,
302 windows: &mut self.windows,
303 proxy: &mut self.proxy,
304 plugins: &mut self.plugins,
305 screen_reader: &mut self.screen_reader,
306 font_manager: &mut self.font_manager,
307 font_collection: &mut self.font_collection,
308 };
309 (cb)(&mut renderer_context);
310 }
311 NativeEvent::Generic(NativeGenericEvent::PollFutures) => {
312 let mut cx = std::task::Context::from_waker(&self.waker);
313 self.futures
314 .retain_mut(|fut| fut.poll(&mut cx).is_pending());
315 }
316 #[cfg(feature = "tray")]
317 NativeEvent::Tray(NativeTrayEvent { action }) => {
318 let renderer_context = RendererContext {
319 fallback_fonts: &mut self.fallback_fonts,
320 active_event_loop,
321 windows: &mut self.windows,
322 proxy: &mut self.proxy,
323 plugins: &mut self.plugins,
324 screen_reader: &mut self.screen_reader,
325 font_manager: &mut self.font_manager,
326 font_collection: &mut self.font_collection,
327 };
328 match action {
329 NativeTrayEventAction::TrayEvent(icon_event) => {
330 use crate::tray::TrayEvent;
331 if let Some(tray_handler) = &mut self.tray.1 {
332 (tray_handler)(TrayEvent::Icon(icon_event), renderer_context)
333 }
334 }
335 NativeTrayEventAction::MenuEvent(menu_event) => {
336 use crate::tray::TrayEvent;
337 if let Some(tray_handler) = &mut self.tray.1 {
338 (tray_handler)(TrayEvent::Menu(menu_event), renderer_context)
339 }
340 }
341 NativeTrayEventAction::LaunchWindow(data) => {
342 let window_config = data
343 .0
344 .downcast::<WindowConfig>()
345 .expect("Expected WindowConfig");
346 let app_window = AppWindow::new(
347 *window_config,
348 active_event_loop,
349 &self.proxy,
350 &mut self.plugins,
351 &self.font_collection,
352 &self.font_manager,
353 &self.fallback_fonts,
354 self.screen_reader.clone(),
355 );
356
357 self.proxy
358 .send_event(NativeEvent::Window(NativeWindowEvent {
359 window_id: app_window.window.id(),
360 action: NativeWindowEventAction::PollRunner,
361 }))
362 .ok();
363
364 self.windows.insert(app_window.window.id(), app_window);
365 }
366 }
367 }
368 NativeEvent::Window(NativeWindowEvent { action, window_id }) => {
369 if let Some(app) = &mut self.windows.get_mut(&window_id) {
370 match action {
371 NativeWindowEventAction::PollRunner => {
372 let mut cx = std::task::Context::from_waker(&app.waker);
373
374 {
375 let fut = std::pin::pin!(async {
376 select! {
377 events_chunk = app.events_receiver.next() => {
378 match events_chunk {
379 Some(EventsChunk::Processed(processed_events)) => {
380 let events_executor_adapter = EventsExecutorAdapter {
381 runner: &mut app.runner,
382 };
383 events_executor_adapter.run(&mut app.nodes_state, processed_events);
384 }
385 Some(EventsChunk::Batch(events)) => {
386 for event in events {
387 app.runner.handle_event(event.node_id, event.name, event.data, event.bubbles);
388 }
389 }
390 _ => {}
391 }
392
393 },
394 _ = app.runner.handle_events().fuse() => {},
395 }
396 });
397
398 match fut.poll(&mut cx) {
399 std::task::Poll::Ready(_) => {
400 self.proxy
401 .send_event(NativeEvent::Window(NativeWindowEvent {
402 window_id: app.window.id(),
403 action: NativeWindowEventAction::PollRunner,
404 }))
405 .ok();
406 }
407 std::task::Poll::Pending => {}
408 }
409 }
410
411 self.plugins.send(
412 PluginEvent::StartedUpdatingTree {
413 window: &app.window,
414 tree: &app.tree,
415 },
416 PluginHandle::new(&self.proxy),
417 );
418 let mutations = app.runner.sync_and_update();
419 let result = app.tree.apply_mutations(mutations);
420 if result.needs_render {
421 app.process_layout_on_next_render = true;
422 app.window.request_redraw();
423 }
424 self.plugins.send(
425 PluginEvent::FinishedUpdatingTree {
426 window: &app.window,
427 tree: &app.tree,
428 },
429 PluginHandle::new(&self.proxy),
430 );
431 #[cfg(debug_assertions)]
432 {
433 tracing::info!("Updated app tree.");
434 tracing::info!("{:#?}", app.tree);
435 tracing::info!("{:#?}", app.runner);
436 }
437 }
438 NativeWindowEventAction::Accessibility(
439 accesskit_winit::WindowEvent::AccessibilityDeactivated,
440 ) => {
441 self.screen_reader.set(false);
442 }
443 NativeWindowEventAction::Accessibility(
444 accesskit_winit::WindowEvent::ActionRequested(_),
445 ) => {}
446 NativeWindowEventAction::Accessibility(
447 accesskit_winit::WindowEvent::InitialTreeRequested,
448 ) => {
449 app.accessibility_tasks_for_next_render = AccessibilityTask::Init;
450 app.window.request_redraw();
451 self.screen_reader.set(true);
452 }
453 NativeWindowEventAction::User(user_event) => match user_event {
454 UserEvent::RequestRedraw => {
455 app.window.request_redraw();
456 }
457 UserEvent::FocusAccessibilityNode(strategy) => {
458 let task = match strategy {
459 AccessibilityFocusStrategy::Backward(_)
460 | AccessibilityFocusStrategy::Forward(_) => {
461 AccessibilityTask::ProcessUpdate {
462 mode: Some(NavigationMode::Keyboard),
463 }
464 }
465 _ => AccessibilityTask::ProcessUpdate { mode: None },
466 };
467 app.tree.accessibility_diff.request_focus(strategy);
468 app.accessibility_tasks_for_next_render = task;
469 app.window.request_redraw();
470 }
471 UserEvent::SetCursorIcon(cursor_icon) => {
472 app.window.set_cursor(cursor_icon);
473 }
474 UserEvent::Erased(data) => {
475 let action = data
476 .0
477 .downcast::<NativeWindowErasedEventAction>()
478 .expect("Expected NativeWindowErasedEventAction");
479 match *action {
480 NativeWindowErasedEventAction::LaunchWindow {
481 window_config,
482 ack,
483 } => {
484 let app_window = AppWindow::new(
485 window_config,
486 active_event_loop,
487 &self.proxy,
488 &mut self.plugins,
489 &self.font_collection,
490 &self.font_manager,
491 &self.fallback_fonts,
492 self.screen_reader.clone(),
493 );
494
495 let window_id = app_window.window.id();
496
497 let _ = self.proxy.send_event(NativeEvent::Window(
498 NativeWindowEvent {
499 window_id,
500 action: NativeWindowEventAction::PollRunner,
501 },
502 ));
503
504 self.windows.insert(window_id, app_window);
505 let _ = ack.send(window_id);
506 }
507 NativeWindowErasedEventAction::CloseWindow(window_id) => {
508 let _ = self.windows.remove(&window_id);
510 }
511 NativeWindowErasedEventAction::WithWindow {
512 window_id,
513 callback,
514 } => {
515 if let Some(window_id) = window_id {
516 if let Some(app) = self.windows.get_mut(&window_id) {
517 (callback.0)(&mut app.window)
518 }
519 } else {
520 (callback.0)(&mut app.window)
521 }
522 }
523 }
524 }
525 },
526 NativeWindowEventAction::PlatformEvent(platform_event) => {
527 let mut events_measurer_adapter = EventsMeasurerAdapter {
528 tree: &mut app.tree,
529 scale_factor: app.window.scale_factor(),
530 };
531 let processed_events = events_measurer_adapter.run(
532 &mut vec![platform_event],
533 &mut app.nodes_state,
534 app.accessibility.focused_node_id(),
535 );
536 app.events_sender
537 .unbounded_send(EventsChunk::Processed(processed_events))
538 .unwrap();
539 }
540 }
541 }
542 }
543 }
544 }
545
546 fn window_event(
547 &mut self,
548 event_loop: &winit::event_loop::ActiveEventLoop,
549 window_id: winit::window::WindowId,
550 event: winit::event::WindowEvent,
551 ) {
552 if let Some(app) = &mut self.windows.get_mut(&window_id) {
553 app.accessibility_adapter.process_event(&app.window, &event);
554 match event {
555 WindowEvent::ThemeChanged(theme) => {
556 app.platform.preferred_theme.set(match theme {
557 Theme::Light => PreferredTheme::Light,
558 Theme::Dark => PreferredTheme::Dark,
559 });
560 }
561 WindowEvent::ScaleFactorChanged { .. } => {
562 app.window.request_redraw();
563 app.process_layout_on_next_render = true;
564 app.tree.layout.reset();
565 }
566 WindowEvent::CloseRequested => {
567 self.windows.remove(&window_id);
568 let has_windows = !self.windows.is_empty();
569
570 let has_tray = {
571 #[cfg(feature = "tray")]
572 {
573 self.tray.1.is_some()
574 }
575 #[cfg(not(feature = "tray"))]
576 {
577 false
578 }
579 };
580
581 if !has_windows && !has_tray {
583 event_loop.exit();
584 }
585 }
586 WindowEvent::ModifiersChanged(modifiers) => {
587 app.modifiers_state = modifiers.state();
588 }
589 WindowEvent::RedrawRequested => {
590 hotpath::measure_block!("RedrawRequested", {
591 if app.process_layout_on_next_render {
592 self.plugins.send(
593 PluginEvent::StartedMeasuringLayout {
594 window: &app.window,
595 tree: &app.tree,
596 },
597 PluginHandle::new(&self.proxy),
598 );
599 let size: Size2D = (
600 app.window.inner_size().width as f32,
601 app.window.inner_size().height as f32,
602 )
603 .into();
604
605 app.tree.measure_layout(
606 size,
607 &self.font_collection,
608 &self.font_manager,
609 &app.events_sender,
610 app.window.scale_factor(),
611 &self.fallback_fonts,
612 );
613 app.platform.root_size.set_if_modified(size);
614 app.process_layout_on_next_render = false;
615 self.plugins.send(
616 PluginEvent::FinishedMeasuringLayout {
617 window: &app.window,
618 tree: &app.tree,
619 },
620 PluginHandle::new(&self.proxy),
621 );
622 }
623
624 app.driver.present(
625 app.window.inner_size().cast(),
626 &app.window,
627 |surface| {
628 self.plugins.send(
629 PluginEvent::BeforeRender {
630 window: &app.window,
631 canvas: surface.canvas(),
632 font_collection: &self.font_collection,
633 tree: &app.tree,
634 },
635 PluginHandle::new(&self.proxy),
636 );
637
638 let render_pipeline = RenderPipeline {
639 font_collection: &mut self.font_collection,
640 font_manager: &self.font_manager,
641 tree: &app.tree,
642 canvas: surface.canvas(),
643 scale_factor: app.window.scale_factor(),
644 background: app.background,
645 };
646
647 render_pipeline.render();
648
649 self.plugins.send(
650 PluginEvent::AfterRender {
651 window: &app.window,
652 canvas: surface.canvas(),
653 font_collection: &self.font_collection,
654 tree: &app.tree,
655 animation_clock: &app.animation_clock,
656 },
657 PluginHandle::new(&self.proxy),
658 );
659 self.plugins.send(
660 PluginEvent::BeforePresenting {
661 window: &app.window,
662 font_collection: &self.font_collection,
663 tree: &app.tree,
664 },
665 PluginHandle::new(&self.proxy),
666 );
667 },
668 );
669 self.plugins.send(
670 PluginEvent::AfterPresenting {
671 window: &app.window,
672 font_collection: &self.font_collection,
673 tree: &app.tree,
674 },
675 PluginHandle::new(&self.proxy),
676 );
677
678 self.plugins.send(
679 PluginEvent::BeforeAccessibility {
680 window: &app.window,
681 font_collection: &self.font_collection,
682 tree: &app.tree,
683 },
684 PluginHandle::new(&self.proxy),
685 );
686
687 match app.accessibility_tasks_for_next_render.take() {
688 AccessibilityTask::ProcessUpdate { mode } => {
689 let update = app
690 .accessibility
691 .process_updates(&mut app.tree, &app.events_sender);
692 app.platform
693 .focused_accessibility_id
694 .set_if_modified(update.focus);
695 let node_id = app.accessibility.focused_node_id().unwrap();
696 let layout_node = app.tree.layout.get(&node_id).unwrap();
697 app.platform.focused_accessibility_node.set_if_modified(
698 AccessibilityTree::create_node(node_id, layout_node, &app.tree),
699 );
700 if let Some(mode) = mode {
701 app.platform.navigation_mode.set(mode);
702 }
703 app.accessibility_adapter.update_if_active(|| update);
704 }
705 AccessibilityTask::Init => {
706 let update = app.accessibility.init(&mut app.tree);
707 app.platform
708 .focused_accessibility_id
709 .set_if_modified(update.focus);
710 let node_id = app.accessibility.focused_node_id().unwrap();
711 let layout_node = app.tree.layout.get(&node_id).unwrap();
712 app.platform.focused_accessibility_node.set_if_modified(
713 AccessibilityTree::create_node(node_id, layout_node, &app.tree),
714 );
715 app.accessibility_adapter.update_if_active(|| update);
716 }
717 AccessibilityTask::None => {}
718 }
719
720 self.plugins.send(
721 PluginEvent::AfterAccessibility {
722 window: &app.window,
723 font_collection: &self.font_collection,
724 tree: &app.tree,
725 },
726 PluginHandle::new(&self.proxy),
727 );
728
729 if app.ticker_sender.receiver_count() > 0 {
730 app.ticker_sender.broadcast_blocking(()).unwrap();
731 }
732
733 self.plugins.send(
734 PluginEvent::AfterRedraw {
735 window: &app.window,
736 font_collection: &self.font_collection,
737 tree: &app.tree,
738 },
739 PluginHandle::new(&self.proxy),
740 );
741 });
742 }
743 WindowEvent::Resized(size) => {
744 app.driver.resize(size);
745
746 app.window.request_redraw();
747
748 app.process_layout_on_next_render = true;
749 app.tree.layout.clear_dirty();
750 app.tree.layout.invalidate(NodeId::ROOT);
751 }
752
753 WindowEvent::MouseInput { state, button, .. } => {
754 app.mouse_state = state;
755 app.platform
756 .navigation_mode
757 .set(NavigationMode::NotKeyboard);
758
759 let name = if state == ElementState::Pressed {
760 MouseEventName::MouseDown
761 } else {
762 MouseEventName::MouseUp
763 };
764 let platform_event = PlatformEvent::Mouse {
765 name,
766 cursor: (app.position.x, app.position.y).into(),
767 button: Some(map_winit_mouse_button(button)),
768 };
769 let mut events_measurer_adapter = EventsMeasurerAdapter {
770 tree: &mut app.tree,
771 scale_factor: app.window.scale_factor(),
772 };
773 let processed_events = events_measurer_adapter.run(
774 &mut vec![platform_event],
775 &mut app.nodes_state,
776 app.accessibility.focused_node_id(),
777 );
778 app.events_sender
779 .unbounded_send(EventsChunk::Processed(processed_events))
780 .unwrap();
781 }
782
783 WindowEvent::KeyboardInput { event, .. } => {
784 let name = match event.state {
785 ElementState::Pressed => KeyboardEventName::KeyDown,
786 ElementState::Released => KeyboardEventName::KeyUp,
787 };
788 let platform_event = PlatformEvent::Keyboard {
789 name,
790 key: winit_mappings::map_winit_key(&event.logical_key),
791 code: winit_mappings::map_winit_physical_key(&event.physical_key),
792 modifiers: winit_mappings::map_winit_modifiers(app.modifiers_state),
793 };
794 let mut events_measurer_adapter = EventsMeasurerAdapter {
795 tree: &mut app.tree,
796 scale_factor: app.window.scale_factor(),
797 };
798 let processed_events = events_measurer_adapter.run(
799 &mut vec![platform_event],
800 &mut app.nodes_state,
801 app.accessibility.focused_node_id(),
802 );
803 app.events_sender
804 .unbounded_send(EventsChunk::Processed(processed_events))
805 .unwrap();
806 }
807
808 WindowEvent::MouseWheel { delta, phase, .. } => {
809 const WHEEL_SPEED_MODIFIER: f64 = 53.0;
810 const TOUCHPAD_SPEED_MODIFIER: f64 = 2.0;
811
812 if TouchPhase::Moved == phase {
813 let scroll_data = {
814 match delta {
815 MouseScrollDelta::LineDelta(x, y) => (
816 (x as f64 * WHEEL_SPEED_MODIFIER),
817 (y as f64 * WHEEL_SPEED_MODIFIER),
818 ),
819 MouseScrollDelta::PixelDelta(pos) => (
820 (pos.x * TOUCHPAD_SPEED_MODIFIER),
821 (pos.y * TOUCHPAD_SPEED_MODIFIER),
822 ),
823 }
824 };
825
826 let platform_event = PlatformEvent::Wheel {
827 name: WheelEventName::Wheel,
828 scroll: scroll_data.into(),
829 cursor: app.position,
830 source: WheelSource::Device,
831 };
832 let mut events_measurer_adapter = EventsMeasurerAdapter {
833 tree: &mut app.tree,
834 scale_factor: app.window.scale_factor(),
835 };
836 let processed_events = events_measurer_adapter.run(
837 &mut vec![platform_event],
838 &mut app.nodes_state,
839 app.accessibility.focused_node_id(),
840 );
841 app.events_sender
842 .unbounded_send(EventsChunk::Processed(processed_events))
843 .unwrap();
844 }
845 }
846
847 WindowEvent::CursorLeft { .. } => {
848 if app.mouse_state == ElementState::Released {
849 app.position = CursorPoint::from((-1., -1.));
850 let platform_event = PlatformEvent::Mouse {
851 name: MouseEventName::MouseMove,
852 cursor: app.position,
853 button: None,
854 };
855 let mut events_measurer_adapter = EventsMeasurerAdapter {
856 tree: &mut app.tree,
857 scale_factor: app.window.scale_factor(),
858 };
859 let processed_events = events_measurer_adapter.run(
860 &mut vec![platform_event],
861 &mut app.nodes_state,
862 app.accessibility.focused_node_id(),
863 );
864 app.events_sender
865 .unbounded_send(EventsChunk::Processed(processed_events))
866 .unwrap();
867 }
868 }
869 WindowEvent::CursorMoved { position, .. } => {
870 app.position = CursorPoint::from((position.x, position.y));
871
872 let mut platform_event = vec![PlatformEvent::Mouse {
873 name: MouseEventName::MouseMove,
874 cursor: app.position,
875 button: None,
876 }];
877
878 for dropped_file_path in app.dropped_file_paths.drain(..) {
879 platform_event.push(PlatformEvent::File {
880 name: FileEventName::FileDrop,
881 file_path: Some(dropped_file_path),
882 cursor: app.position,
883 });
884 }
885
886 let mut events_measurer_adapter = EventsMeasurerAdapter {
887 tree: &mut app.tree,
888 scale_factor: app.window.scale_factor(),
889 };
890 let processed_events = events_measurer_adapter.run(
891 &mut platform_event,
892 &mut app.nodes_state,
893 app.accessibility.focused_node_id(),
894 );
895 app.events_sender
896 .unbounded_send(EventsChunk::Processed(processed_events))
897 .unwrap();
898 }
899
900 WindowEvent::Touch(Touch {
901 location,
902 phase,
903 id,
904 force,
905 ..
906 }) => {
907 app.position = CursorPoint::from((location.x, location.y));
908
909 let name = match phase {
910 TouchPhase::Cancelled => TouchEventName::TouchCancel,
911 TouchPhase::Ended => TouchEventName::TouchEnd,
912 TouchPhase::Moved => TouchEventName::TouchMove,
913 TouchPhase::Started => TouchEventName::TouchStart,
914 };
915
916 let platform_event = PlatformEvent::Touch {
917 name,
918 location: app.position,
919 finger_id: id,
920 phase: map_winit_touch_phase(phase),
921 force: force.map(map_winit_touch_force),
922 };
923 let mut events_measurer_adapter = EventsMeasurerAdapter {
924 tree: &mut app.tree,
925 scale_factor: app.window.scale_factor(),
926 };
927 let processed_events = events_measurer_adapter.run(
928 &mut vec![platform_event],
929 &mut app.nodes_state,
930 app.accessibility.focused_node_id(),
931 );
932 app.events_sender
933 .unbounded_send(EventsChunk::Processed(processed_events))
934 .unwrap();
935 app.position = CursorPoint::from((location.x, location.y));
936 }
937 WindowEvent::Ime(Ime::Preedit(text, pos)) => {
938 let platform_event = PlatformEvent::ImePreedit {
939 name: ImeEventName::Preedit,
940 text,
941 cursor: pos,
942 };
943 let mut events_measurer_adapter = EventsMeasurerAdapter {
944 tree: &mut app.tree,
945 scale_factor: app.window.scale_factor(),
946 };
947 let processed_events = events_measurer_adapter.run(
948 &mut vec![platform_event],
949 &mut app.nodes_state,
950 app.accessibility.focused_node_id(),
951 );
952 app.events_sender
953 .unbounded_send(EventsChunk::Processed(processed_events))
954 .unwrap();
955 }
956 WindowEvent::DroppedFile(file_path) => {
957 app.dropped_file_paths.push(file_path);
958 }
959 WindowEvent::HoveredFile(file_path) => {
960 let platform_event = PlatformEvent::File {
961 name: FileEventName::FileHover,
962 file_path: Some(file_path),
963 cursor: app.position,
964 };
965 let mut events_measurer_adapter = EventsMeasurerAdapter {
966 tree: &mut app.tree,
967 scale_factor: app.window.scale_factor(),
968 };
969 let processed_events = events_measurer_adapter.run(
970 &mut vec![platform_event],
971 &mut app.nodes_state,
972 app.accessibility.focused_node_id(),
973 );
974 app.events_sender
975 .unbounded_send(EventsChunk::Processed(processed_events))
976 .unwrap();
977 }
978 WindowEvent::HoveredFileCancelled => {
979 let platform_event = PlatformEvent::File {
980 name: FileEventName::FileHoverCancelled,
981 file_path: None,
982 cursor: app.position,
983 };
984 let mut events_measurer_adapter = EventsMeasurerAdapter {
985 tree: &mut app.tree,
986 scale_factor: app.window.scale_factor(),
987 };
988 let processed_events = events_measurer_adapter.run(
989 &mut vec![platform_event],
990 &mut app.nodes_state,
991 app.accessibility.focused_node_id(),
992 );
993 app.events_sender
994 .unbounded_send(EventsChunk::Processed(processed_events))
995 .unwrap();
996 }
997 _ => {}
998 }
999 }
1000 }
1001}