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