1pub use egui;
2pub use egui::color_picker;
3pub use egui_wgpu;
4
5use egui::{pos2, ClippedPrimitive, PlatformOutput};
6use egui_wgpu::renderer::ScreenDescriptor;
7use nannou::wgpu::ToTextureView;
8use nannou::{wgpu, winit::event::VirtualKeyCode, winit::event::WindowEvent::*};
9use std::{cell::RefCell, ops::Deref, time::Duration};
10
11pub struct Egui {
17 context: egui::Context,
18 renderer: RefCell<Renderer>,
19 input: Input,
20}
21
22pub struct Renderer {
27 renderer: egui_wgpu::Renderer,
28 paint_jobs: Vec<ClippedPrimitive>,
29 textures_delta: egui::TexturesDelta,
30}
31
32pub struct Input {
34 pub pointer_pos: egui::Pos2,
35 pub raw: egui::RawInput,
36 pub window_size_pixels: [u32; 2],
37 pub window_scale_factor: f32,
38}
39
40pub struct FrameCtx<'a> {
45 ui: &'a mut Egui,
46 ended: bool,
47}
48
49impl Egui {
50 pub fn new(
60 device: &wgpu::Device,
61 target_format: wgpu::TextureFormat,
62 target_msaa_samples: u32,
63 window_scale_factor: f32,
64 window_size_pixels: [u32; 2],
65 ) -> Self {
66 let renderer = RefCell::new(Renderer::new(device, target_format, target_msaa_samples));
67 let input = Input::new(window_scale_factor, window_size_pixels);
68 let context = Default::default();
69 Self {
70 renderer,
71 input,
72 context,
73 }
74 }
75
76 pub fn from_window(window: &nannou::window::Window) -> Self {
78 let device = window.device();
79 let format = nannou::Frame::TEXTURE_FORMAT;
80 let msaa_samples = window.msaa_samples();
81 let scale_factor = window.scale_factor();
82 let (w_px, h_px) = window.inner_size_pixels();
83 Self::new(device, format, msaa_samples, scale_factor, [w_px, h_px])
84 }
85
86 pub fn ctx(&self) -> &egui::Context {
88 &self.context
89 }
90
91 pub fn input(&self) -> &Input {
93 &self.input
94 }
95
96 pub fn handle_raw_event(&mut self, event: &winit::event::WindowEvent) {
98 self.input.handle_raw_event(event);
99 }
100
101 pub fn set_elapsed_time(&mut self, elapsed: Duration) {
103 self.input.set_elapsed_time(elapsed);
104 }
105
106 pub fn begin_frame(&mut self) -> FrameCtx {
108 self.begin_frame_inner();
109 let ui = self;
110 let ended = false;
111 FrameCtx { ui, ended }
112 }
113
114 pub fn end_frame(&mut self) -> PlatformOutput {
115 self.end_frame_inner()
116 }
117
118 pub fn texture_from_wgpu_texture(
120 &mut self,
121 device: &wgpu::Device,
122 texture: &wgpu::Texture,
123 texture_filter: wgpu::FilterMode,
124 ) -> egui::TextureId {
125 self.renderer.borrow_mut().renderer.register_native_texture(
126 device,
127 &texture.to_texture_view(),
128 texture_filter,
129 )
130 }
131
132 pub fn update_texture_from_wgpu_texture(
134 &mut self,
135 device: &wgpu::Device,
136 texture: &wgpu::Texture,
137 texture_filter: wgpu::FilterMode,
138 id: egui::TextureId,
139 ) -> Result<(), egui_wgpu::WgpuError> {
140 self.renderer
141 .borrow_mut()
142 .renderer
143 .update_egui_texture_from_wgpu_texture(
144 device,
145 &texture.to_texture_view(),
146 texture_filter,
147 id,
148 );
149 Ok(())
150 }
151
152 pub fn draw_to_frame(&self, frame: &nannou::Frame) -> Result<(), egui_wgpu::WgpuError> {
154 let mut renderer = self.renderer.borrow_mut();
155 renderer.draw_to_frame(frame)
156 }
157
158 fn begin_frame_inner(&mut self) {
159 self.context.begin_frame(self.input.raw.take());
160 }
161
162 fn end_frame_inner(&mut self) -> egui::PlatformOutput {
163 let egui::FullOutput {
164 shapes,
165 platform_output,
166 textures_delta,
167 ..
168 } = self.context.end_frame();
169 self.renderer.borrow_mut().paint_jobs = self.context.tessellate(shapes);
170 self.renderer.borrow_mut().textures_delta = textures_delta;
171 platform_output
172 }
173}
174
175impl Input {
176 pub fn new(window_scale_factor: f32, window_size_pixels: [u32; 2]) -> Self {
179 let raw = egui::RawInput {
180 pixels_per_point: Some(window_scale_factor),
181 ..Default::default()
182 };
183 let pointer_pos = Default::default();
184 let mut input = Self {
185 raw,
186 pointer_pos,
187 window_scale_factor,
188 window_size_pixels,
189 };
190 input.raw.screen_rect = Some(input.egui_window_rect());
191 input
192 }
193
194 pub fn handle_raw_event(&mut self, event: &winit::event::WindowEvent) {
196 match event {
197 Resized(physical_size) => {
198 self.window_size_pixels = [physical_size.width, physical_size.height];
199 self.raw.screen_rect = Some(self.egui_window_rect());
200 }
201 ScaleFactorChanged {
202 scale_factor,
203 new_inner_size,
204 } => {
205 self.window_scale_factor = *scale_factor as f32;
206 self.window_size_pixels = [new_inner_size.width, new_inner_size.height];
207 self.raw.pixels_per_point = Some(self.window_scale_factor);
208 self.raw.screen_rect = Some(self.egui_window_rect());
209 }
210 MouseInput { state, button, .. } => {
211 if let winit::event::MouseButton::Other(..) = button {
212 } else {
213 self.raw.events.push(egui::Event::PointerButton {
214 pos: self.pointer_pos,
215 button: match button {
216 winit::event::MouseButton::Left => egui::PointerButton::Primary,
217 winit::event::MouseButton::Right => egui::PointerButton::Secondary,
218 winit::event::MouseButton::Middle => egui::PointerButton::Middle,
219 winit::event::MouseButton::Other(_) => unreachable!(),
220 },
221 pressed: *state == winit::event::ElementState::Pressed,
222 modifiers: self.raw.modifiers,
223 });
224 }
225 }
226 MouseWheel { delta, .. } => {
227 match delta {
228 winit::event::MouseScrollDelta::LineDelta(x, y) => {
229 let line_height = 24.0;
230 self.raw
231 .events
232 .push(egui::Event::Scroll(egui::vec2(*x, *y) * line_height));
233 }
234 winit::event::MouseScrollDelta::PixelDelta(delta) => {
235 self.raw.events.push(egui::Event::Scroll(egui::vec2(
237 delta.x as f32,
238 delta.y as f32,
239 )));
240 }
241 }
242 }
243 CursorMoved { position, .. } => {
244 self.pointer_pos = pos2(
245 position.x as f32 / self.window_scale_factor as f32,
246 position.y as f32 / self.window_scale_factor as f32,
247 );
248 self.raw
249 .events
250 .push(egui::Event::PointerMoved(self.pointer_pos));
251 }
252 CursorLeft { .. } => {
253 self.raw.events.push(egui::Event::PointerGone);
254 }
255 ModifiersChanged(input) => {
256 self.raw.modifiers = winit_to_egui_modifiers(*input);
257 }
258 KeyboardInput { input, .. } => {
259 if let Some(virtual_keycode) = input.virtual_keycode {
260 if let Some(key) = winit_to_egui_key_code(virtual_keycode) {
261 self.raw.events.push(egui::Event::Key {
263 key,
264 pressed: input.state == winit::event::ElementState::Pressed,
265 repeat: false,
266 modifiers: self.raw.modifiers,
267 });
268 }
269 }
270 }
271 ReceivedCharacter(ch) => {
272 if is_printable(*ch) && !self.raw.modifiers.ctrl && !self.raw.modifiers.command {
273 self.raw.events.push(egui::Event::Text(ch.to_string()));
274 }
275 }
276 _ => {}
277 }
278 }
279
280 pub fn set_elapsed_time(&mut self, elapsed: Duration) {
282 self.raw.time = Some(elapsed.as_secs_f64());
283 }
284
285 fn egui_window_rect(&self) -> egui::Rect {
287 let [w, h] = self.window_size_pixels;
288 egui::Rect::from_min_size(
289 Default::default(),
290 egui::vec2(w as f32, h as f32) / self.window_scale_factor as f32,
291 )
292 }
293}
294
295impl Renderer {
296 pub fn new(
304 device: &wgpu::Device,
305 target_format: wgpu::TextureFormat,
306 target_msaa_samples: u32,
307 ) -> Self {
308 Self {
309 renderer: egui_wgpu::Renderer::new(device, target_format, None, target_msaa_samples),
310 paint_jobs: Vec::new(),
311 textures_delta: Default::default(),
312 }
313 }
314
315 pub fn from_window(window: &nannou::window::Window) -> Self {
317 let device = window.device();
318 let format = nannou::Frame::TEXTURE_FORMAT;
319 let msaa_samples = window.msaa_samples();
320 Self::new(device, format, msaa_samples)
321 }
322
323 pub fn encode_render_pass(
325 &mut self,
326 device: &wgpu::Device,
327 queue: &wgpu::Queue,
328 encoder: &mut wgpu::CommandEncoder,
329 dst_size_pixels: [u32; 2],
330 dst_scale_factor: f32,
331 dst_texture: &wgpu::TextureView,
332 ) -> Result<(), egui_wgpu::WgpuError> {
333 let renderer = &mut self.renderer;
334 let textures = &self.textures_delta;
335 let paint_jobs = &self.paint_jobs;
336 let screen_descriptor = ScreenDescriptor {
337 size_in_pixels: dst_size_pixels,
338 pixels_per_point: dst_scale_factor,
339 };
340 for (id, image_delta) in &textures.set {
341 renderer.update_texture(&device, &queue, *id, &image_delta);
342 }
343 renderer.update_buffers(device, queue, encoder, &paint_jobs, &screen_descriptor);
344 let mut render_pass = encoder.begin_render_pass(&egui_wgpu::wgpu::RenderPassDescriptor {
345 label: Some("nannou_egui_render_pass"),
346 color_attachments: &[Some(egui_wgpu::wgpu::RenderPassColorAttachment {
347 view: &dst_texture,
348 resolve_target: None,
349 ops: egui_wgpu::wgpu::Operations {
350 load: egui_wgpu::wgpu::LoadOp::Load,
351 store: true,
352 },
353 })],
354 depth_stencil_attachment: None,
355 });
356 renderer.render(&mut render_pass, &paint_jobs, &screen_descriptor);
357 Ok(())
358 }
359
360 pub fn draw_to_frame(&mut self, frame: &nannou::Frame) -> Result<(), egui_wgpu::WgpuError> {
362 let device_queue_pair = frame.device_queue_pair();
363 let device = device_queue_pair.device();
364 let queue = device_queue_pair.queue();
365 let size_pixels = frame.texture_size();
366 let [width_px, _] = size_pixels;
367 let scale_factor = width_px as f32 / frame.rect().w();
368 let texture_view = frame.texture_view();
369 let mut encoder = frame.command_encoder();
370 self.encode_render_pass(
371 device,
372 queue,
373 &mut encoder,
374 size_pixels,
375 scale_factor,
376 texture_view,
377 )
378 }
379}
380
381impl<'a> FrameCtx<'a> {
382 pub fn context(&self) -> egui::Context {
384 self.ui.context.clone()
385 }
386
387 pub fn end(mut self) {
389 self.end_inner();
390 }
391
392 fn end_inner(&mut self) {
394 if !self.ended {
395 self.ui.end_frame_inner();
396 self.ended = true;
397 }
398 }
399}
400
401impl<'a> Drop for FrameCtx<'a> {
402 fn drop(&mut self) {
403 self.end_inner();
404 }
405}
406
407impl<'a> Deref for FrameCtx<'a> {
408 type Target = egui::Context;
409 fn deref(&self) -> &Self::Target {
410 &self.ui.context
411 }
412}
413
414#[inline]
416fn winit_to_egui_key_code(key: VirtualKeyCode) -> Option<egui::Key> {
417 use egui::Key;
418
419 Some(match key {
420 VirtualKeyCode::Escape => Key::Escape,
421 VirtualKeyCode::Insert => Key::Insert,
422 VirtualKeyCode::Home => Key::Home,
423 VirtualKeyCode::Delete => Key::Delete,
424 VirtualKeyCode::End => Key::End,
425 VirtualKeyCode::PageDown => Key::PageDown,
426 VirtualKeyCode::PageUp => Key::PageUp,
427 VirtualKeyCode::Left => Key::ArrowLeft,
428 VirtualKeyCode::Up => Key::ArrowUp,
429 VirtualKeyCode::Right => Key::ArrowRight,
430 VirtualKeyCode::Down => Key::ArrowDown,
431 VirtualKeyCode::Back => Key::Backspace,
432 VirtualKeyCode::Return => Key::Enter,
433 VirtualKeyCode::Tab => Key::Tab,
434 VirtualKeyCode::Space => Key::Space,
435
436 VirtualKeyCode::A => Key::A,
437 VirtualKeyCode::B => Key::B,
438 VirtualKeyCode::C => Key::C,
439 VirtualKeyCode::D => Key::D,
440 VirtualKeyCode::E => Key::E,
441 VirtualKeyCode::F => Key::F,
442 VirtualKeyCode::G => Key::G,
443 VirtualKeyCode::H => Key::H,
444 VirtualKeyCode::I => Key::I,
445 VirtualKeyCode::J => Key::J,
446 VirtualKeyCode::K => Key::K,
447 VirtualKeyCode::L => Key::L,
448 VirtualKeyCode::M => Key::M,
449 VirtualKeyCode::N => Key::N,
450 VirtualKeyCode::O => Key::O,
451 VirtualKeyCode::P => Key::P,
452 VirtualKeyCode::Q => Key::Q,
453 VirtualKeyCode::R => Key::R,
454 VirtualKeyCode::S => Key::S,
455 VirtualKeyCode::T => Key::T,
456 VirtualKeyCode::U => Key::U,
457 VirtualKeyCode::V => Key::V,
458 VirtualKeyCode::W => Key::W,
459 VirtualKeyCode::X => Key::X,
460 VirtualKeyCode::Y => Key::Y,
461 VirtualKeyCode::Z => Key::Z,
462
463 VirtualKeyCode::Key0 => Key::Num0,
464 VirtualKeyCode::Key1 => Key::Num1,
465 VirtualKeyCode::Key2 => Key::Num2,
466 VirtualKeyCode::Key3 => Key::Num3,
467 VirtualKeyCode::Key4 => Key::Num4,
468 VirtualKeyCode::Key5 => Key::Num5,
469 VirtualKeyCode::Key6 => Key::Num6,
470 VirtualKeyCode::Key7 => Key::Num7,
471 VirtualKeyCode::Key8 => Key::Num8,
472 VirtualKeyCode::Key9 => Key::Num9,
473
474 _ => {
475 return None;
476 }
477 })
478}
479
480#[inline]
482fn winit_to_egui_modifiers(modifiers: winit::event::ModifiersState) -> egui::Modifiers {
483 egui::Modifiers {
484 alt: modifiers.alt(),
485 ctrl: modifiers.ctrl(),
486 shift: modifiers.shift(),
487 #[cfg(target_os = "macos")]
488 mac_cmd: modifiers.logo(),
489 #[cfg(target_os = "macos")]
490 command: modifiers.logo(),
491 #[cfg(not(target_os = "macos"))]
492 mac_cmd: false,
493 #[cfg(not(target_os = "macos"))]
494 command: modifiers.ctrl(),
495 }
496}
497
498fn is_printable(chr: char) -> bool {
500 let is_in_private_use_area = '\u{e000}' <= chr && chr <= '\u{f8ff}'
501 || '\u{f0000}' <= chr && chr <= '\u{ffffd}'
502 || '\u{100000}' <= chr && chr <= '\u{10fffd}';
503 !is_in_private_use_area && !chr.is_ascii_control()
504}