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