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 key = winit_mappings::map_winit_key(&event.logical_key);
909 let code = winit_mappings::map_winit_physical_key(&event.physical_key);
910 let modifiers = winit_mappings::map_winit_modifiers(app.modifiers_state);
911
912 self.plugins.send(
913 PluginEvent::KeyboardInput {
914 window: &app.window,
915 key: key.clone(),
916 code,
917 modifiers,
918 is_pressed: event.state.is_pressed(),
919 },
920 PluginHandle::new(&self.proxy),
921 );
922
923 let platform_event = PlatformEvent::Keyboard {
924 name,
925 key,
926 code,
927 modifiers,
928 };
929 let mut events_measurer_adapter = EventsMeasurerAdapter {
930 tree: &mut app.tree,
931 scale_factor: app.window.scale_factor(),
932 };
933 let processed_events = events_measurer_adapter.run(
934 &mut vec![platform_event],
935 &mut app.nodes_state,
936 app.accessibility.focused_node_id(),
937 );
938 app.events_sender
939 .unbounded_send(EventsChunk::Processed(processed_events))
940 .unwrap();
941 }
942
943 WindowEvent::MouseWheel { delta, phase, .. } => {
944 const WHEEL_SPEED_MODIFIER: f64 = 53.0;
945 const TOUCHPAD_SPEED_MODIFIER: f64 = 2.0;
946
947 if TouchPhase::Moved == phase {
948 let scroll_data = {
949 match delta {
950 MouseScrollDelta::LineDelta(x, y) => (
951 (x as f64 * WHEEL_SPEED_MODIFIER),
952 (y as f64 * WHEEL_SPEED_MODIFIER),
953 ),
954 MouseScrollDelta::PixelDelta(pos) => (
955 (pos.x * TOUCHPAD_SPEED_MODIFIER),
956 (pos.y * TOUCHPAD_SPEED_MODIFIER),
957 ),
958 }
959 };
960
961 let platform_event = PlatformEvent::Wheel {
962 name: WheelEventName::Wheel,
963 scroll: scroll_data.into(),
964 cursor: app.position,
965 source: WheelSource::Device,
966 };
967 let mut events_measurer_adapter = EventsMeasurerAdapter {
968 tree: &mut app.tree,
969 scale_factor: app.window.scale_factor(),
970 };
971 let processed_events = events_measurer_adapter.run(
972 &mut vec![platform_event],
973 &mut app.nodes_state,
974 app.accessibility.focused_node_id(),
975 );
976 app.events_sender
977 .unbounded_send(EventsChunk::Processed(processed_events))
978 .unwrap();
979 }
980 }
981
982 WindowEvent::CursorLeft { .. } => {
983 if app.mouse_state == ElementState::Released {
984 app.position = CursorPoint::from((-1., -1.));
985 let platform_event = PlatformEvent::Mouse {
986 name: MouseEventName::MouseMove,
987 cursor: app.position,
988 button: None,
989 };
990 let mut events_measurer_adapter = EventsMeasurerAdapter {
991 tree: &mut app.tree,
992 scale_factor: app.window.scale_factor(),
993 };
994 let processed_events = events_measurer_adapter.run(
995 &mut vec![platform_event],
996 &mut app.nodes_state,
997 app.accessibility.focused_node_id(),
998 );
999 app.events_sender
1000 .unbounded_send(EventsChunk::Processed(processed_events))
1001 .unwrap();
1002 }
1003 }
1004 WindowEvent::CursorMoved { position, .. } => {
1005 app.just_focused = false;
1006 app.position = CursorPoint::from((position.x, position.y));
1007
1008 let mut platform_event = vec![PlatformEvent::Mouse {
1009 name: MouseEventName::MouseMove,
1010 cursor: app.position,
1011 button: None,
1012 }];
1013
1014 for dropped_file_path in app.dropped_file_paths.drain(..) {
1015 platform_event.push(PlatformEvent::File {
1016 name: FileEventName::FileDrop,
1017 file_path: Some(dropped_file_path),
1018 cursor: app.position,
1019 });
1020 }
1021
1022 let mut events_measurer_adapter = EventsMeasurerAdapter {
1023 tree: &mut app.tree,
1024 scale_factor: app.window.scale_factor(),
1025 };
1026 let processed_events = events_measurer_adapter.run(
1027 &mut platform_event,
1028 &mut app.nodes_state,
1029 app.accessibility.focused_node_id(),
1030 );
1031 app.events_sender
1032 .unbounded_send(EventsChunk::Processed(processed_events))
1033 .unwrap();
1034 }
1035
1036 WindowEvent::Touch(Touch {
1037 location,
1038 phase,
1039 id,
1040 force,
1041 ..
1042 }) => {
1043 app.position = CursorPoint::from((location.x, location.y));
1044
1045 let name = match phase {
1046 TouchPhase::Cancelled => TouchEventName::TouchCancel,
1047 TouchPhase::Ended => TouchEventName::TouchEnd,
1048 TouchPhase::Moved => TouchEventName::TouchMove,
1049 TouchPhase::Started => TouchEventName::TouchStart,
1050 };
1051
1052 let platform_event = PlatformEvent::Touch {
1053 name,
1054 location: app.position,
1055 finger_id: id,
1056 phase: map_winit_touch_phase(phase),
1057 force: force.map(map_winit_touch_force),
1058 };
1059 let mut events_measurer_adapter = EventsMeasurerAdapter {
1060 tree: &mut app.tree,
1061 scale_factor: app.window.scale_factor(),
1062 };
1063 let processed_events = events_measurer_adapter.run(
1064 &mut vec![platform_event],
1065 &mut app.nodes_state,
1066 app.accessibility.focused_node_id(),
1067 );
1068 app.events_sender
1069 .unbounded_send(EventsChunk::Processed(processed_events))
1070 .unwrap();
1071 app.position = CursorPoint::from((location.x, location.y));
1072 }
1073 WindowEvent::Ime(Ime::Commit(text)) => {
1074 let platform_event = PlatformEvent::Keyboard {
1075 name: KeyboardEventName::KeyDown,
1076 key: keyboard_types::Key::Character(text),
1077 code: keyboard_types::Code::Unidentified,
1078 modifiers: winit_mappings::map_winit_modifiers(app.modifiers_state),
1079 };
1080 let mut events_measurer_adapter = EventsMeasurerAdapter {
1081 tree: &mut app.tree,
1082 scale_factor: app.window.scale_factor(),
1083 };
1084 let processed_events = events_measurer_adapter.run(
1085 &mut vec![platform_event],
1086 &mut app.nodes_state,
1087 app.accessibility.focused_node_id(),
1088 );
1089 app.events_sender
1090 .unbounded_send(EventsChunk::Processed(processed_events))
1091 .unwrap();
1092 }
1093 WindowEvent::Ime(Ime::Preedit(text, pos)) => {
1094 let platform_event = PlatformEvent::ImePreedit {
1095 name: ImeEventName::Preedit,
1096 text,
1097 cursor: pos,
1098 };
1099 let mut events_measurer_adapter = EventsMeasurerAdapter {
1100 tree: &mut app.tree,
1101 scale_factor: app.window.scale_factor(),
1102 };
1103 let processed_events = events_measurer_adapter.run(
1104 &mut vec![platform_event],
1105 &mut app.nodes_state,
1106 app.accessibility.focused_node_id(),
1107 );
1108 app.events_sender
1109 .unbounded_send(EventsChunk::Processed(processed_events))
1110 .unwrap();
1111 }
1112 WindowEvent::DroppedFile(file_path) => {
1113 app.dropped_file_paths.push(file_path);
1114 }
1115 WindowEvent::HoveredFile(file_path) => {
1116 let platform_event = PlatformEvent::File {
1117 name: FileEventName::FileHover,
1118 file_path: Some(file_path),
1119 cursor: app.position,
1120 };
1121 let mut events_measurer_adapter = EventsMeasurerAdapter {
1122 tree: &mut app.tree,
1123 scale_factor: app.window.scale_factor(),
1124 };
1125 let processed_events = events_measurer_adapter.run(
1126 &mut vec![platform_event],
1127 &mut app.nodes_state,
1128 app.accessibility.focused_node_id(),
1129 );
1130 app.events_sender
1131 .unbounded_send(EventsChunk::Processed(processed_events))
1132 .unwrap();
1133 }
1134 WindowEvent::HoveredFileCancelled => {
1135 let platform_event = PlatformEvent::File {
1136 name: FileEventName::FileHoverCancelled,
1137 file_path: None,
1138 cursor: app.position,
1139 };
1140 let mut events_measurer_adapter = EventsMeasurerAdapter {
1141 tree: &mut app.tree,
1142 scale_factor: app.window.scale_factor(),
1143 };
1144 let processed_events = events_measurer_adapter.run(
1145 &mut vec![platform_event],
1146 &mut app.nodes_state,
1147 app.accessibility.focused_node_id(),
1148 );
1149 app.events_sender
1150 .unbounded_send(EventsChunk::Processed(processed_events))
1151 .unwrap();
1152 }
1153 _ => {}
1154 }
1155 }
1156 }
1157}