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