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