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 plugins::{
64 PluginEvent,
65 PluginHandle,
66 PluginsManager,
67 },
68 window::AppWindow,
69 winit_mappings::{
70 self,
71 map_winit_mouse_button,
72 map_winit_touch_force,
73 map_winit_touch_phase,
74 },
75};
76
77fn is_ime_role(role: AccessibilityRole) -> bool {
79 matches!(
80 role,
81 AccessibilityRole::TextInput
82 | AccessibilityRole::MultilineTextInput
83 | AccessibilityRole::PasswordInput
84 | AccessibilityRole::SearchInput
85 | AccessibilityRole::DateInput
86 | AccessibilityRole::DateTimeInput
87 | AccessibilityRole::WeekInput
88 | AccessibilityRole::MonthInput
89 | AccessibilityRole::TimeInput
90 | AccessibilityRole::EmailInput
91 | AccessibilityRole::NumberInput
92 | AccessibilityRole::PhoneNumberInput
93 | AccessibilityRole::UrlInput
94 | AccessibilityRole::Terminal
95 )
96}
97
98pub struct WinitRenderer {
99 pub windows_configs: Vec<WindowConfig>,
100 #[cfg(feature = "tray")]
101 pub(crate) tray: (
102 Option<crate::config::TrayIconGetter>,
103 Option<crate::config::TrayHandler>,
104 ),
105 #[cfg(all(feature = "tray", not(target_os = "linux")))]
106 pub(crate) tray_icon: Option<TrayIcon>,
107 pub resumed: bool,
108 pub windows: FxHashMap<WindowId, AppWindow>,
109 pub proxy: EventLoopProxy<NativeEvent>,
110 pub plugins: PluginsManager,
111 pub fallback_fonts: Vec<Cow<'static, str>>,
112 pub screen_reader: ScreenReader,
113 pub font_manager: FontMgr,
114 pub font_collection: FontCollection,
115 pub futures: Vec<Pin<Box<dyn std::future::Future<Output = ()>>>>,
116 pub waker: Waker,
117 pub exit_on_close: bool,
118}
119
120pub struct RendererContext<'a> {
121 pub windows: &'a mut FxHashMap<WindowId, AppWindow>,
122 pub proxy: &'a mut EventLoopProxy<NativeEvent>,
123 pub plugins: &'a mut PluginsManager,
124 pub fallback_fonts: &'a mut Vec<Cow<'static, str>>,
125 pub screen_reader: &'a mut ScreenReader,
126 pub font_manager: &'a mut FontMgr,
127 pub font_collection: &'a mut FontCollection,
128 pub active_event_loop: &'a ActiveEventLoop,
129}
130
131impl RendererContext<'_> {
132 pub fn launch_window(&mut self, window_config: WindowConfig) -> WindowId {
133 let app_window = AppWindow::new(
134 window_config,
135 self.active_event_loop,
136 self.proxy,
137 self.plugins,
138 self.font_collection,
139 self.font_manager,
140 self.fallback_fonts,
141 self.screen_reader.clone(),
142 );
143
144 let window_id = app_window.window.id();
145
146 self.proxy
147 .send_event(NativeEvent::Window(NativeWindowEvent {
148 window_id,
149 action: NativeWindowEventAction::PollRunner,
150 }))
151 .ok();
152
153 self.windows.insert(window_id, app_window);
154
155 window_id
156 }
157
158 pub fn windows(&self) -> &FxHashMap<WindowId, AppWindow> {
159 self.windows
160 }
161
162 pub fn windows_mut(&mut self) -> &mut FxHashMap<WindowId, AppWindow> {
163 self.windows
164 }
165
166 pub fn exit(&mut self) {
167 self.active_event_loop.exit();
168 }
169}
170
171#[derive(Debug)]
172pub enum NativeWindowEventAction {
173 PollRunner,
174
175 Accessibility(AccessibilityWindowEvent),
176
177 PlatformEvent(PlatformEvent),
178
179 User(UserEvent),
180}
181
182pub struct WithWindowCallback(pub(crate) Box<dyn FnOnce(&mut Window)>);
183
184impl fmt::Debug for WithWindowCallback {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 f.write_str("WithWindowCallback")
187 }
188}
189
190#[derive(Clone)]
192pub struct LaunchProxy(pub EventLoopProxy<NativeEvent>);
193
194impl LaunchProxy {
195 pub fn with<F, T: 'static>(&self, f: F) -> futures_channel::oneshot::Receiver<T>
197 where
198 F: FnOnce(&mut RendererContext) -> T + 'static,
199 {
200 let (tx, rx) = futures_channel::oneshot::channel::<T>();
201 let cb = Box::new(move |ctx: &mut RendererContext| {
202 let res = (f)(ctx);
203 let _ = tx.send(res);
204 });
205 let _ = self
206 .0
207 .send_event(NativeEvent::Generic(NativeGenericEvent::RendererCallback(
208 cb,
209 )));
210 rx
211 }
212}
213
214#[derive(Debug)]
215pub enum NativeWindowErasedEventAction {
216 LaunchWindow {
217 window_config: WindowConfig,
218 ack: futures_channel::oneshot::Sender<WindowId>,
219 },
220 CloseWindow(WindowId),
221 WithWindow {
222 window_id: Option<WindowId>,
223 callback: WithWindowCallback,
224 },
225}
226
227#[derive(Debug)]
228pub struct NativeWindowEvent {
229 pub window_id: WindowId,
230 pub action: NativeWindowEventAction,
231}
232
233#[cfg(feature = "tray")]
234#[derive(Debug)]
235pub enum NativeTrayEventAction {
236 TrayEvent(tray_icon::TrayIconEvent),
237 MenuEvent(tray_icon::menu::MenuEvent),
238 LaunchWindow(SingleThreadErasedEvent),
239}
240
241#[cfg(feature = "tray")]
242#[derive(Debug)]
243pub struct NativeTrayEvent {
244 pub action: NativeTrayEventAction,
245}
246
247pub enum NativeGenericEvent {
248 PollFutures,
249 RendererCallback(Box<dyn FnOnce(&mut RendererContext) + 'static>),
250}
251
252impl fmt::Debug for NativeGenericEvent {
253 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254 match self {
255 NativeGenericEvent::PollFutures => f.write_str("PollFutures"),
256 NativeGenericEvent::RendererCallback(_) => f.write_str("RendererCallback"),
257 }
258 }
259}
260
261unsafe impl Send for NativeGenericEvent {}
265unsafe impl Sync for NativeGenericEvent {}
266
267#[derive(Debug)]
268pub enum NativeEvent {
269 Window(NativeWindowEvent),
270 #[cfg(feature = "tray")]
271 Tray(NativeTrayEvent),
272 Generic(NativeGenericEvent),
273}
274
275impl From<accesskit_winit::Event> for NativeEvent {
276 fn from(event: accesskit_winit::Event) -> Self {
277 NativeEvent::Window(NativeWindowEvent {
278 window_id: event.window_id,
279 action: NativeWindowEventAction::Accessibility(event.window_event),
280 })
281 }
282}
283
284impl ApplicationHandler<NativeEvent> for WinitRenderer {
285 fn resumed(&mut self, active_event_loop: &winit::event_loop::ActiveEventLoop) {
286 if !self.resumed {
287 #[cfg(feature = "tray")]
288 {
289 #[cfg(not(target_os = "linux"))]
290 if let Some(tray_icon) = self.tray.0.take() {
291 self.tray_icon = Some((tray_icon)());
292 }
293
294 #[cfg(target_os = "macos")]
295 {
296 use objc2_core_foundation::CFRunLoop;
297
298 let rl = CFRunLoop::main().expect("Failed to run CFRunLoop");
299 CFRunLoop::wake_up(&rl);
300 }
301 }
302
303 for window_config in self.windows_configs.drain(..) {
304 let app_window = AppWindow::new(
305 window_config,
306 active_event_loop,
307 &self.proxy,
308 &mut self.plugins,
309 &mut self.font_collection,
310 &self.font_manager,
311 &self.fallback_fonts,
312 self.screen_reader.clone(),
313 );
314
315 self.proxy
316 .send_event(NativeEvent::Window(NativeWindowEvent {
317 window_id: app_window.window.id(),
318 action: NativeWindowEventAction::PollRunner,
319 }))
320 .ok();
321
322 self.windows.insert(app_window.window.id(), app_window);
323 }
324 self.resumed = true;
325
326 let _ = self
327 .proxy
328 .send_event(NativeEvent::Generic(NativeGenericEvent::PollFutures));
329 } else {
330 let old_windows: Vec<_> = self.windows.drain().collect();
333 for (_, mut app_window) in old_windows {
334 let (new_driver, new_window) =
335 GraphicsDriver::new(active_event_loop, app_window.window_attributes.clone());
336
337 let new_id = new_window.id();
338 app_window.driver = new_driver;
339 app_window.window = new_window;
340 app_window.process_layout_on_next_render = true;
341 app_window.tree.layout.reset();
342
343 self.windows.insert(new_id, app_window);
344
345 self.proxy
346 .send_event(NativeEvent::Window(NativeWindowEvent {
347 window_id: new_id,
348 action: NativeWindowEventAction::PollRunner,
349 }))
350 .ok();
351 }
352 }
353 }
354
355 fn user_event(
356 &mut self,
357 active_event_loop: &winit::event_loop::ActiveEventLoop,
358 event: NativeEvent,
359 ) {
360 match event {
361 NativeEvent::Generic(NativeGenericEvent::RendererCallback(cb)) => {
362 let mut renderer_context = RendererContext {
363 fallback_fonts: &mut self.fallback_fonts,
364 active_event_loop,
365 windows: &mut self.windows,
366 proxy: &mut self.proxy,
367 plugins: &mut self.plugins,
368 screen_reader: &mut self.screen_reader,
369 font_manager: &mut self.font_manager,
370 font_collection: &mut self.font_collection,
371 };
372 (cb)(&mut renderer_context);
373 }
374 NativeEvent::Generic(NativeGenericEvent::PollFutures) => {
375 let mut cx = std::task::Context::from_waker(&self.waker);
376 self.futures
377 .retain_mut(|fut| fut.poll(&mut cx).is_pending());
378 }
379 #[cfg(feature = "tray")]
380 NativeEvent::Tray(NativeTrayEvent { action }) => {
381 let renderer_context = RendererContext {
382 fallback_fonts: &mut self.fallback_fonts,
383 active_event_loop,
384 windows: &mut self.windows,
385 proxy: &mut self.proxy,
386 plugins: &mut self.plugins,
387 screen_reader: &mut self.screen_reader,
388 font_manager: &mut self.font_manager,
389 font_collection: &mut self.font_collection,
390 };
391 match action {
392 NativeTrayEventAction::TrayEvent(icon_event) => {
393 use crate::tray::TrayEvent;
394 if let Some(tray_handler) = &mut self.tray.1 {
395 (tray_handler)(TrayEvent::Icon(icon_event), renderer_context)
396 }
397 }
398 NativeTrayEventAction::MenuEvent(menu_event) => {
399 use crate::tray::TrayEvent;
400 if let Some(tray_handler) = &mut self.tray.1 {
401 (tray_handler)(TrayEvent::Menu(menu_event), renderer_context)
402 }
403 }
404 NativeTrayEventAction::LaunchWindow(data) => {
405 let window_config = data
406 .0
407 .downcast::<WindowConfig>()
408 .expect("Expected WindowConfig");
409 let app_window = AppWindow::new(
410 *window_config,
411 active_event_loop,
412 &self.proxy,
413 &mut self.plugins,
414 &mut self.font_collection,
415 &self.font_manager,
416 &self.fallback_fonts,
417 self.screen_reader.clone(),
418 );
419
420 self.proxy
421 .send_event(NativeEvent::Window(NativeWindowEvent {
422 window_id: app_window.window.id(),
423 action: NativeWindowEventAction::PollRunner,
424 }))
425 .ok();
426
427 self.windows.insert(app_window.window.id(), app_window);
428 }
429 }
430 }
431 NativeEvent::Window(NativeWindowEvent { action, window_id }) => {
432 if let Some(app) = &mut self.windows.get_mut(&window_id) {
433 match action {
434 NativeWindowEventAction::PollRunner => {
435 let mut cx = std::task::Context::from_waker(&app.waker);
436
437 {
438 let fut = std::pin::pin!(async {
439 select! {
440 events_chunk = app.events_receiver.next() => {
441 match events_chunk {
442 Some(EventsChunk::Processed(processed_events)) => {
443 let events_executor_adapter = EventsExecutorAdapter {
444 runner: &mut app.runner,
445 };
446 events_executor_adapter.run(&mut app.nodes_state, processed_events);
447 }
448 Some(EventsChunk::Batch(events)) => {
449 for event in events {
450 app.runner.handle_event(event.node_id, event.name, event.data, event.bubbles);
451 }
452 }
453 _ => {}
454 }
455
456 },
457 _ = app.runner.handle_events().fuse() => {},
458 }
459 });
460
461 match fut.poll(&mut cx) {
462 std::task::Poll::Ready(_) => {
463 self.proxy
464 .send_event(NativeEvent::Window(NativeWindowEvent {
465 window_id: app.window.id(),
466 action: NativeWindowEventAction::PollRunner,
467 }))
468 .ok();
469 }
470 std::task::Poll::Pending => {}
471 }
472 }
473
474 self.plugins.send(
475 PluginEvent::StartedUpdatingTree {
476 window: &app.window,
477 tree: &app.tree,
478 },
479 PluginHandle::new(&self.proxy),
480 );
481 let mutations = app.runner.sync_and_update();
482 let result = app.runner.run_in(|| app.tree.apply_mutations(mutations));
483 if result.needs_render {
484 app.process_layout_on_next_render = true;
485 app.window.request_redraw();
486 }
487 if result.needs_accessibility {
488 app.accessibility_tasks_for_next_render |=
489 AccessibilityTask::ProcessUpdate { mode: None };
490 app.window.request_redraw();
491 }
492 self.plugins.send(
493 PluginEvent::FinishedUpdatingTree {
494 window: &app.window,
495 tree: &app.tree,
496 },
497 PluginHandle::new(&self.proxy),
498 );
499 #[cfg(debug_assertions)]
500 {
501 tracing::info!("Updated app tree.");
502 tracing::info!("{:#?}", app.tree);
503 tracing::info!("{:#?}", app.runner);
504 }
505 }
506 NativeWindowEventAction::Accessibility(
507 accesskit_winit::WindowEvent::AccessibilityDeactivated,
508 ) => {
509 self.screen_reader.set(false);
510 }
511 NativeWindowEventAction::Accessibility(
512 accesskit_winit::WindowEvent::ActionRequested(_),
513 ) => {}
514 NativeWindowEventAction::Accessibility(
515 accesskit_winit::WindowEvent::InitialTreeRequested,
516 ) => {
517 app.accessibility_tasks_for_next_render = AccessibilityTask::Init;
518 app.window.request_redraw();
519 self.screen_reader.set(true);
520 }
521 NativeWindowEventAction::User(user_event) => match user_event {
522 UserEvent::RequestRedraw => {
523 app.window.request_redraw();
524 }
525 UserEvent::FocusAccessibilityNode(strategy) => {
526 let task = match strategy {
527 AccessibilityFocusStrategy::Backward(_)
528 | AccessibilityFocusStrategy::Forward(_) => {
529 AccessibilityTask::ProcessUpdate {
530 mode: Some(NavigationMode::Keyboard),
531 }
532 }
533 _ => AccessibilityTask::ProcessUpdate { mode: None },
534 };
535 app.tree.accessibility_diff.request_focus(strategy);
536 app.accessibility_tasks_for_next_render = task;
537 app.window.request_redraw();
538 }
539 UserEvent::SetCursorIcon(cursor_icon) => {
540 app.window.set_cursor(cursor_icon);
541 }
542 UserEvent::Erased(data) => {
543 let action = data
544 .0
545 .downcast::<NativeWindowErasedEventAction>()
546 .expect("Expected NativeWindowErasedEventAction");
547 match *action {
548 NativeWindowErasedEventAction::LaunchWindow {
549 window_config,
550 ack,
551 } => {
552 let app_window = AppWindow::new(
553 window_config,
554 active_event_loop,
555 &self.proxy,
556 &mut self.plugins,
557 &mut self.font_collection,
558 &self.font_manager,
559 &self.fallback_fonts,
560 self.screen_reader.clone(),
561 );
562
563 let window_id = app_window.window.id();
564
565 let _ = self.proxy.send_event(NativeEvent::Window(
566 NativeWindowEvent {
567 window_id,
568 action: NativeWindowEventAction::PollRunner,
569 },
570 ));
571
572 self.windows.insert(window_id, app_window);
573 let _ = ack.send(window_id);
574 }
575 NativeWindowErasedEventAction::CloseWindow(window_id) => {
576 let _ = self.windows.remove(&window_id);
578 let has_windows = !self.windows.is_empty();
579
580 let has_tray = {
581 #[cfg(feature = "tray")]
582 {
583 self.tray.1.is_some()
584 }
585 #[cfg(not(feature = "tray"))]
586 {
587 false
588 }
589 };
590
591 if !has_windows && !has_tray && self.exit_on_close {
593 active_event_loop.exit();
594 }
595 }
596 NativeWindowErasedEventAction::WithWindow {
597 window_id,
598 callback,
599 } => {
600 if let Some(window_id) = window_id {
601 if let Some(app) = self.windows.get_mut(&window_id) {
602 (callback.0)(&mut app.window)
603 }
604 } else {
605 (callback.0)(&mut app.window)
606 }
607 }
608 }
609 }
610 },
611 NativeWindowEventAction::PlatformEvent(platform_event) => {
612 let mut events_measurer_adapter = EventsMeasurerAdapter {
613 tree: &mut app.tree,
614 scale_factor: app.window.scale_factor(),
615 };
616 let processed_events = events_measurer_adapter.run(
617 &mut vec![platform_event],
618 &mut app.nodes_state,
619 app.accessibility.focused_node_id(),
620 );
621 app.events_sender
622 .unbounded_send(EventsChunk::Processed(processed_events))
623 .unwrap();
624 }
625 }
626 }
627 }
628 }
629 }
630
631 fn window_event(
632 &mut self,
633 event_loop: &winit::event_loop::ActiveEventLoop,
634 window_id: winit::window::WindowId,
635 event: winit::event::WindowEvent,
636 ) {
637 if let Some(app) = &mut self.windows.get_mut(&window_id) {
638 app.accessibility_adapter.process_event(&app.window, &event);
639 match event {
640 WindowEvent::ThemeChanged(theme) => {
641 app.platform.preferred_theme.set(match theme {
642 Theme::Light => PreferredTheme::Light,
643 Theme::Dark => PreferredTheme::Dark,
644 });
645 }
646 WindowEvent::ScaleFactorChanged { .. } => {
647 app.window.request_redraw();
648 app.process_layout_on_next_render = true;
649 app.tree.layout.reset();
650 app.tree.text_cache.reset();
651 }
652 WindowEvent::CloseRequested => {
653 let mut on_close_hook = self
654 .windows
655 .get_mut(&window_id)
656 .and_then(|app| app.on_close.take());
657
658 let decision = if let Some(ref mut on_close) = on_close_hook {
659 let renderer_context = RendererContext {
660 fallback_fonts: &mut self.fallback_fonts,
661 active_event_loop: event_loop,
662 windows: &mut self.windows,
663 proxy: &mut self.proxy,
664 plugins: &mut self.plugins,
665 screen_reader: &mut self.screen_reader,
666 font_manager: &mut self.font_manager,
667 font_collection: &mut self.font_collection,
668 };
669 on_close(renderer_context, window_id)
670 } else {
671 CloseDecision::Close
672 };
673
674 if matches!(decision, CloseDecision::KeepOpen)
675 && let Some(app) = self.windows.get_mut(&window_id)
676 {
677 app.on_close = on_close_hook;
678 }
679
680 if matches!(decision, CloseDecision::Close) {
681 self.windows.remove(&window_id);
682 let has_windows = !self.windows.is_empty();
683
684 let has_tray = {
685 #[cfg(feature = "tray")]
686 {
687 self.tray.1.is_some()
688 }
689 #[cfg(not(feature = "tray"))]
690 {
691 false
692 }
693 };
694
695 if !has_windows && !has_tray && self.exit_on_close {
697 event_loop.exit();
698 }
699 }
700 }
701 WindowEvent::ModifiersChanged(modifiers) => {
702 app.modifiers_state = modifiers.state();
703 }
704 WindowEvent::Focused(is_focused) => {
705 app.just_focused = is_focused;
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}