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