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