eframe/native/
winit_integration.rs

1use std::{sync::Arc, time::Instant};
2
3use winit::{
4    event_loop::ActiveEventLoop,
5    window::{Window, WindowId},
6};
7
8use egui::ViewportId;
9#[cfg(feature = "accesskit")]
10use egui_winit::accesskit_winit;
11
12/// Create an egui context, restoring it from storage if possible.
13pub fn create_egui_context(storage: Option<&dyn crate::Storage>) -> egui::Context {
14    profiling::function_scope!();
15
16    pub const IS_DESKTOP: bool = cfg!(any(
17        target_os = "freebsd",
18        target_os = "linux",
19        target_os = "macos",
20        target_os = "openbsd",
21        target_os = "windows",
22    ));
23
24    let egui_ctx = egui::Context::default();
25
26    egui_ctx.set_embed_viewports(!IS_DESKTOP);
27
28    egui_ctx.options_mut(|o| {
29        // eframe supports multi-pass (Context::request_discard).
30        o.max_passes = 2.try_into().unwrap();
31    });
32
33    let memory = crate::native::epi_integration::load_egui_memory(storage).unwrap_or_default();
34    egui_ctx.memory_mut(|mem| *mem = memory);
35
36    egui_ctx
37}
38
39/// The custom even `eframe` uses with the [`winit`] event loop.
40#[derive(Debug)]
41pub enum UserEvent {
42    /// A repaint is requested.
43    RequestRepaint {
44        /// What to repaint.
45        viewport_id: ViewportId,
46
47        /// When to repaint.
48        when: Instant,
49
50        /// What the cumulative pass number was when the repaint was _requested_.
51        cumulative_pass_nr: u64,
52    },
53
54    /// A request related to [`accesskit`](https://accesskit.dev/).
55    #[cfg(feature = "accesskit")]
56    AccessKitActionRequest(accesskit_winit::Event),
57}
58
59#[cfg(feature = "accesskit")]
60impl From<accesskit_winit::Event> for UserEvent {
61    fn from(inner: accesskit_winit::Event) -> Self {
62        Self::AccessKitActionRequest(inner)
63    }
64}
65
66pub trait WinitApp {
67    fn egui_ctx(&self) -> Option<&egui::Context>;
68
69    fn window(&self, window_id: WindowId) -> Option<Arc<Window>>;
70
71    fn window_id_from_viewport_id(&self, id: ViewportId) -> Option<WindowId>;
72
73    fn save_and_destroy(&mut self);
74
75    fn run_ui_and_paint(
76        &mut self,
77        event_loop: &ActiveEventLoop,
78        window_id: WindowId,
79    ) -> crate::Result<EventResult>;
80
81    fn suspended(&mut self, event_loop: &ActiveEventLoop) -> crate::Result<EventResult>;
82
83    fn resumed(&mut self, event_loop: &ActiveEventLoop) -> crate::Result<EventResult>;
84
85    fn device_event(
86        &mut self,
87        event_loop: &ActiveEventLoop,
88        device_id: winit::event::DeviceId,
89        event: winit::event::DeviceEvent,
90    ) -> crate::Result<EventResult>;
91
92    fn window_event(
93        &mut self,
94        event_loop: &ActiveEventLoop,
95        window_id: WindowId,
96        event: winit::event::WindowEvent,
97    ) -> crate::Result<EventResult>;
98
99    #[cfg(feature = "accesskit")]
100    fn on_accesskit_event(&mut self, event: accesskit_winit::Event) -> crate::Result<EventResult>;
101}
102
103#[derive(Clone, Copy, Debug, PartialEq, Eq)]
104pub enum EventResult {
105    Wait,
106
107    /// Causes a synchronous repaint inside the event handler. This should only
108    /// be used in special situations if the window must be repainted while
109    /// handling a specific event. This occurs on Windows when handling resizes.
110    ///
111    /// `RepaintNow` creates a new frame synchronously, and should therefore
112    /// only be used for extremely urgent repaints.
113    RepaintNow(WindowId),
114
115    /// Queues a repaint for once the event loop handles its next redraw. Exists
116    /// so that multiple input events can be handled in one frame. Does not
117    /// cause any delay like `RepaintNow`.
118    RepaintNext(WindowId),
119
120    RepaintAt(WindowId, Instant),
121
122    Exit,
123}
124
125#[cfg(feature = "accesskit")]
126pub(crate) fn on_accesskit_window_event(
127    egui_winit: &mut egui_winit::State,
128    window_id: WindowId,
129    event: &accesskit_winit::WindowEvent,
130) -> EventResult {
131    match event {
132        accesskit_winit::WindowEvent::InitialTreeRequested => {
133            egui_winit.egui_ctx().enable_accesskit();
134            // Because we can't provide the initial tree synchronously
135            // (because that would require the activation handler to access
136            // the same mutable state as the winit event handler), some
137            // AccessKit platform adapters will use a placeholder tree
138            // until we send the first tree update. To minimize the possible
139            // bad effects of that workaround, repaint and send the tree
140            // immediately.
141            EventResult::RepaintNow(window_id)
142        }
143        accesskit_winit::WindowEvent::ActionRequested(request) => {
144            egui_winit.on_accesskit_action_request(request.clone());
145            // As a form of user input, accessibility actions should cause
146            // a repaint, but not until the next regular frame.
147            EventResult::RepaintNext(window_id)
148        }
149        accesskit_winit::WindowEvent::AccessibilityDeactivated => {
150            egui_winit.egui_ctx().disable_accesskit();
151            // Disabling AccessKit support should have no visible effect,
152            // so there's no need to repaint.
153            EventResult::Wait
154        }
155    }
156}