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