servoshell/desktop/
egui_glue.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! A modified version of EguiGlow [from egui_glow 0.22.0][0] that retains its shapes,
6//! allowing [`EguiGlow::paint`] to be called multiple times.
7//!
8//! [0]: https://github.com/emilk/egui/blob/0.22.0/crates/egui_glow/src/winit.rs
9
10// Copyright (c) 2018-2021 Emil Ernerfeldt <[email protected]>
11//
12// Permission is hereby granted, free of charge, to any
13// person obtaining a copy of this software and associated
14// documentation files (the "Software"), to deal in the
15// Software without restriction, including without
16// limitation the rights to use, copy, modify, merge,
17// publish, distribute, sublicense, and/or sell copies of
18// the Software, and to permit persons to whom the Software
19// is furnished to do so, subject to the following
20// conditions:
21//
22// The above copyright notice and this permission notice
23// shall be included in all copies or substantial portions
24// of the Software.
25//
26// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
27// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
28// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
29// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
30// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
31// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
33// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
34// DEALINGS IN THE SOFTWARE.
35
36use egui::{ViewportId, ViewportOutput};
37use egui_glow::ShaderVersion;
38pub use egui_winit;
39pub use egui_winit::EventResponse;
40use egui_winit::winit;
41use winit::event_loop::ActiveEventLoop;
42use winit::window::Theme;
43
44use super::events_loop::EventLoopProxy;
45use super::headed_window::Window;
46use super::window_trait::WindowPortsMethods;
47
48/// Use [`egui`] from a [`glow`] app based on [`winit`].
49pub struct EguiGlow {
50    pub egui_ctx: egui::Context,
51    pub egui_winit: egui_winit::State,
52    pub painter: egui_glow::Painter,
53
54    shapes: Vec<egui::epaint::ClippedShape>,
55    textures_delta: egui::TexturesDelta,
56}
57
58impl EguiGlow {
59    /// For automatic shader version detection set `shader_version` to `None`.
60    pub fn new(
61        window: &Window,
62        event_loop: &ActiveEventLoop,
63        event_loop_proxy: EventLoopProxy,
64        gl: std::sync::Arc<glow::Context>,
65        shader_version: Option<ShaderVersion>,
66    ) -> Self {
67        let painter = egui_glow::Painter::new(gl, "", shader_version, false)
68            .map_err(|err| {
69                log::error!("error occurred in initializing painter:\n{err}");
70            })
71            .unwrap();
72
73        let theme = event_loop.system_theme().unwrap_or(Theme::Light);
74        let egui_ctx = egui::Context::default();
75        let mut egui_winit = egui_winit::State::new(
76            egui_ctx.clone(),
77            ViewportId::ROOT,
78            event_loop,
79            None,
80            Some(theme),
81            None,
82        );
83        let window = window.winit_window().unwrap();
84        egui_winit.init_accesskit(event_loop, window, event_loop_proxy);
85        window.set_visible(true);
86        Self {
87            egui_winit,
88            egui_ctx,
89            painter,
90            shapes: Default::default(),
91            textures_delta: Default::default(),
92        }
93    }
94
95    pub fn on_window_event(
96        &mut self,
97        window: &winit::window::Window,
98        event: &winit::event::WindowEvent,
99    ) -> EventResponse {
100        self.egui_winit.on_window_event(window, event)
101    }
102
103    /// Returns the `Duration` of the timeout after which egui should be repainted even if there's no new events.
104    ///
105    /// Call [`Self::paint`] later to paint.
106    pub fn run(
107        &mut self,
108        window: &winit::window::Window,
109        run_ui: impl FnMut(&egui::Context),
110    ) -> std::time::Duration {
111        let raw_input = self.egui_winit.take_egui_input(window);
112        let egui::FullOutput {
113            platform_output,
114            viewport_output,
115            textures_delta,
116            shapes,
117            pixels_per_point: _pixels_per_point,
118        } = self.egui_ctx.run(raw_input, run_ui);
119
120        self.egui_winit
121            .handle_platform_output(window, platform_output);
122
123        self.shapes = shapes;
124        self.textures_delta.append(textures_delta);
125        match viewport_output.get(&ViewportId::ROOT) {
126            Some(&ViewportOutput { repaint_delay, .. }) => repaint_delay,
127            None => std::time::Duration::ZERO,
128        }
129    }
130
131    /// Paint the results of the last call to [`Self::run`].
132    pub fn paint(&mut self, window: &winit::window::Window) {
133        /////// let shapes = std::mem::take(&mut self.shapes);
134        let shapes = &self.shapes;
135        let mut textures_delta = std::mem::take(&mut self.textures_delta);
136
137        for (id, image_delta) in textures_delta.set {
138            self.painter.set_texture(id, &image_delta);
139        }
140
141        let pixels_per_point = self.egui_ctx.pixels_per_point();
142        /////// let clipped_primitives = self.egui_ctx.tessellate(shapes);
143        let clipped_primitives = self.egui_ctx.tessellate(shapes.clone(), pixels_per_point);
144        let dimensions: [u32; 2] = window.inner_size().into();
145        self.painter
146            .paint_primitives(dimensions, pixels_per_point, &clipped_primitives);
147
148        for id in textures_delta.free.drain(..) {
149            self.painter.free_texture(id);
150        }
151    }
152
153    /// Call to release the allocated graphics resources.
154    pub fn destroy(&mut self) {
155        self.painter.destroy();
156    }
157}