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