Skip to main content

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    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/// Proxy wrapper provided to launch tasks so they can post callbacks executed inside the renderer.
171#[derive(Clone)]
172pub struct LaunchProxy(pub EventLoopProxy<NativeEvent>);
173
174impl LaunchProxy {
175    /// Send a callback to the renderer to get access to [RendererContext].
176    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
241/// # Safety
242/// The values are never sent, received or accessed by other threads other than the main thread.
243/// This is needed to send `Rc<T>` and other non-Send and non-Sync values.
244unsafe 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            // [Android] Recreate the GraphicsDriver when the app gets brought into the foreground after being suspended,
311            // so we don't end up with a completely black surface with broken rendering.
312            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                                        // Its fine to ignore if the window doesnt exist anymore
557                                        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                                        // Only exit when there is no window and no tray
572                                        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                        // Only exit when there is no windows and no tray
676                        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                        // The focused workaround is only for desktop targets
687                        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                    // Workaround for winit sending a Tab event when alt-tabbing
907                    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}