freya_winit/
renderer.rs

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
76/// Returns `true` for accessibility roles that require IME input (text fields, terminals, etc.).
77fn 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/// Proxy wrapper provided to launch tasks so they can post callbacks executed inside the renderer.
189#[derive(Clone)]
190pub struct LaunchProxy(pub EventLoopProxy<NativeEvent>);
191
192impl LaunchProxy {
193    /// Send a callback to the renderer to get access to [RendererContext].
194    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
259/// # Safety
260/// The values are never sent, received or accessed by other threads other than the main thread.
261/// This is needed to send `Rc<T>` and other non-Send and non-Sync values.
262unsafe 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                                        // Its fine to ignore if the window doesnt exist anymore
552                                        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                                        // Only exit when there is no window and no tray
567                                        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                        // Only exit when there is no windows and no tray
670                        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                    // Workaround for winit sending a Tab event when alt-tabbing
898                    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}