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