1use crate::control::*;
2use crate::core::*;
3use egui_glow::Painter;
4use std::cell::RefCell;
5
6#[doc(hidden)]
7pub use egui;
8
9pub struct GUI {
13 painter: RefCell<Painter>,
14 egui_context: egui::Context,
15 output: RefCell<Option<egui::FullOutput>>,
16 viewport: Viewport,
17 modifiers: Modifiers,
18}
19
20impl GUI {
21 pub fn new(context: &Context) -> Self {
25 use std::ops::Deref;
26 Self::from_gl_context(context.deref().clone())
27 }
28
29 pub fn from_gl_context(context: std::sync::Arc<crate::context::Context>) -> Self {
33 GUI {
34 egui_context: egui::Context::default(),
35 painter: RefCell::new(Painter::new(context, "", None, true).unwrap()),
36 output: RefCell::new(None),
37 viewport: Viewport::new_at_origo(1, 1),
38 modifiers: Modifiers::default(),
39 }
40 }
41
42 pub fn context(&self) -> &egui::Context {
46 &self.egui_context
47 }
48
49 pub fn update(
55 &mut self,
56 events: &mut [Event],
57 accumulated_time_in_ms: f64,
58 viewport: Viewport,
59 device_pixel_ratio: f32,
60 callback: impl FnOnce(&egui::Context),
61 ) -> bool {
62 self.egui_context
63 .set_pixels_per_point(device_pixel_ratio as f32);
64 self.viewport = viewport;
65 let egui_input = egui::RawInput {
66 screen_rect: Some(egui::Rect {
67 min: egui::Pos2 {
68 x: viewport.x as f32 / device_pixel_ratio as f32,
69 y: viewport.y as f32 / device_pixel_ratio as f32,
70 },
71 max: egui::Pos2 {
72 x: viewport.x as f32 / device_pixel_ratio as f32
73 + viewport.width as f32 / device_pixel_ratio as f32,
74 y: viewport.y as f32 / device_pixel_ratio as f32
75 + viewport.height as f32 / device_pixel_ratio as f32,
76 },
77 }),
78 time: Some(accumulated_time_in_ms * 0.001),
79 modifiers: (&self.modifiers).into(),
80 events: events
81 .iter()
82 .filter_map(|event| match event {
83 Event::KeyPress {
84 kind,
85 modifiers,
86 handled,
87 } => {
88 if !handled {
89 Some(egui::Event::Key {
90 key: kind.into(),
91 pressed: true,
92 modifiers: modifiers.into(),
93 repeat: false,
94 physical_key: None,
95 })
96 } else {
97 None
98 }
99 }
100 Event::KeyRelease {
101 kind,
102 modifiers,
103 handled,
104 } => {
105 if !handled {
106 Some(egui::Event::Key {
107 key: kind.into(),
108 pressed: false,
109 modifiers: modifiers.into(),
110 repeat: false,
111 physical_key: None,
112 })
113 } else {
114 None
115 }
116 }
117 Event::MousePress {
118 button,
119 position,
120 modifiers,
121 handled,
122 } => {
123 if !handled {
124 Some(egui::Event::PointerButton {
125 pos: egui::Pos2 {
126 x: position.x / device_pixel_ratio as f32,
127 y: (viewport.height as f32 - position.y)
128 / device_pixel_ratio as f32,
129 },
130 button: button.into(),
131 pressed: true,
132 modifiers: modifiers.into(),
133 })
134 } else {
135 None
136 }
137 }
138 Event::MouseRelease {
139 button,
140 position,
141 modifiers,
142 handled,
143 } => {
144 if !handled {
145 Some(egui::Event::PointerButton {
146 pos: egui::Pos2 {
147 x: position.x / device_pixel_ratio as f32,
148 y: (viewport.height as f32 - position.y)
149 / device_pixel_ratio as f32,
150 },
151 button: button.into(),
152 pressed: false,
153 modifiers: modifiers.into(),
154 })
155 } else {
156 None
157 }
158 }
159 Event::MouseMotion {
160 position, handled, ..
161 } => {
162 if !handled {
163 Some(egui::Event::PointerMoved(egui::Pos2 {
164 x: position.x / device_pixel_ratio as f32,
165 y: (viewport.height as f32 - position.y)
166 / device_pixel_ratio as f32,
167 }))
168 } else {
169 None
170 }
171 }
172 Event::Text(text) => Some(egui::Event::Text(text.clone())),
173 Event::MouseLeave => Some(egui::Event::PointerGone),
174 Event::MouseWheel {
175 delta,
176 handled,
177 modifiers,
178 ..
179 } => {
180 if !handled {
181 Some(egui::Event::MouseWheel {
182 delta: egui::Vec2::new(delta.0 as f32, delta.1 as f32),
183 unit: egui::MouseWheelUnit::Point,
184 modifiers: modifiers.into(),
185 })
186 } else {
187 None
188 }
189 }
190 Event::PinchGesture { delta, handled, .. } => {
191 if !handled {
192 Some(egui::Event::Zoom(delta.exp()))
193 } else {
194 None
195 }
196 }
197 _ => None,
198 })
199 .collect::<Vec<_>>(),
200 ..Default::default()
201 };
202
203 self.egui_context.begin_pass(egui_input);
204 callback(&self.egui_context);
205 *self.output.borrow_mut() = Some(self.egui_context.end_pass());
206
207 for event in events.iter_mut() {
208 if let Event::ModifiersChange { modifiers } = event {
209 self.modifiers = *modifiers;
210 }
211 if self.egui_context.wants_pointer_input() {
212 match event {
213 Event::MousePress {
214 ref mut handled, ..
215 } => {
216 *handled = true;
217 }
218 Event::MouseRelease {
219 ref mut handled, ..
220 } => {
221 *handled = true;
222 }
223 Event::MouseWheel {
224 ref mut handled, ..
225 } => {
226 *handled = true;
227 }
228 Event::MouseMotion {
229 ref mut handled, ..
230 } => {
231 *handled = true;
232 }
233 Event::PinchGesture {
234 ref mut handled, ..
235 } => {
236 *handled = true;
237 }
238 Event::RotationGesture {
239 ref mut handled, ..
240 } => {
241 *handled = true;
242 }
243 _ => {}
244 }
245 }
246
247 if self.egui_context.wants_keyboard_input() {
248 match event {
249 Event::KeyRelease {
250 ref mut handled, ..
251 } => {
252 *handled = true;
253 }
254 Event::KeyPress {
255 ref mut handled, ..
256 } => {
257 *handled = true;
258 }
259 _ => {}
260 }
261 }
262 }
263 self.egui_context.wants_pointer_input() || self.egui_context.wants_keyboard_input()
264 }
265
266 pub fn render(&self) -> Result<(), crate::CoreError> {
271 let output = self
272 .output
273 .borrow_mut()
274 .take()
275 .expect("need to call GUI::update before GUI::render");
276 let scale = self.egui_context.pixels_per_point();
277 let clipped_meshes = self.egui_context.tessellate(output.shapes, scale);
278 self.painter.borrow_mut().paint_and_update_textures(
279 [self.viewport.width, self.viewport.height],
280 scale,
281 &clipped_meshes,
282 &output.textures_delta,
283 );
284 #[cfg(not(target_arch = "wasm32"))]
285 #[allow(unsafe_code)]
286 unsafe {
287 use glow::HasContext as _;
288 self.painter.borrow().gl().disable(glow::FRAMEBUFFER_SRGB);
289 }
290 Ok(())
291 }
292}
293
294impl Drop for GUI {
295 fn drop(&mut self) {
296 self.painter.borrow_mut().destroy();
297 }
298}
299
300impl From<&Key> for egui::Key {
301 fn from(key: &Key) -> Self {
302 use crate::control::Key::*;
303 use egui::Key;
304 match key {
305 ArrowDown => Key::ArrowDown,
306 ArrowLeft => Key::ArrowLeft,
307 ArrowRight => Key::ArrowRight,
308 ArrowUp => Key::ArrowUp,
309 Escape => Key::Escape,
310 Tab => Key::Tab,
311 Backspace => Key::Backspace,
312 Enter => Key::Enter,
313 Space => Key::Space,
314 Insert => Key::Insert,
315 Delete => Key::Delete,
316 Home => Key::Home,
317 End => Key::End,
318 PageUp => Key::PageUp,
319 PageDown => Key::PageDown,
320 Num0 => Key::Num0,
321 Num1 => Key::Num1,
322 Num2 => Key::Num2,
323 Num3 => Key::Num3,
324 Num4 => Key::Num4,
325 Num5 => Key::Num5,
326 Num6 => Key::Num6,
327 Num7 => Key::Num7,
328 Num8 => Key::Num8,
329 Num9 => Key::Num9,
330 A => Key::A,
331 B => Key::B,
332 C => Key::C,
333 D => Key::D,
334 E => Key::E,
335 F => Key::F,
336 G => Key::G,
337 H => Key::H,
338 I => Key::I,
339 J => Key::J,
340 K => Key::K,
341 L => Key::L,
342 M => Key::M,
343 N => Key::N,
344 O => Key::O,
345 P => Key::P,
346 Q => Key::Q,
347 R => Key::R,
348 S => Key::S,
349 T => Key::T,
350 U => Key::U,
351 V => Key::V,
352 W => Key::W,
353 X => Key::X,
354 Y => Key::Y,
355 Z => Key::Z,
356 }
357 }
358}
359
360impl From<&Modifiers> for egui::Modifiers {
361 fn from(modifiers: &Modifiers) -> Self {
362 Self {
363 alt: modifiers.alt,
364 ctrl: modifiers.ctrl,
365 shift: modifiers.shift,
366 command: modifiers.command,
367 mac_cmd: cfg!(target_os = "macos") && modifiers.command,
368 }
369 }
370}
371
372impl From<&MouseButton> for egui::PointerButton {
373 fn from(button: &MouseButton) -> Self {
374 match button {
375 MouseButton::Left => egui::PointerButton::Primary,
376 MouseButton::Right => egui::PointerButton::Secondary,
377 MouseButton::Middle => egui::PointerButton::Middle,
378 }
379 }
380}