nannou_egui/
lib.rs

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
11/// All `egui`-related state for a single window.
12///
13/// Includes the context, a renderer, and an input tracker.
14///
15/// For multi-window user interfaces, you will need to create an instance of this type per-window.
16pub struct Egui {
17    context: egui::Context,
18    renderer: RefCell<Renderer>,
19    input: Input,
20}
21
22/// A wrapper around all necessary state for rendering a `Egui` to a single texture (often a window
23/// texture).
24///
25/// For targeting more than one window, users should construct a `Egui` for each.
26pub struct Renderer {
27    renderer: egui_wgpu::Renderer,
28    paint_jobs: Vec<ClippedPrimitive>,
29    textures_delta: egui::TexturesDelta,
30}
31
32/// Tracking user and application event input.
33pub 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
40/// A wrapper around a `CtxRef` on which `begin_frame` was called.
41///
42/// Automatically calls `end_frame` on `drop` in the case that it wasn't already called by the
43/// usef.
44pub struct FrameCtx<'a> {
45    ui: &'a mut Egui,
46    ended: bool,
47}
48
49impl Egui {
50    /// Construct the `Egui` from its parts.
51    ///
52    /// The given `device` must be the same used to create the queue to which the Egui's render
53    /// commands will be submitted.
54    ///
55    /// The `target_format`, `target_msaa_samples`, `window_scale_factor` and `window_size_pixels`
56    /// must match the window to which the UI will be drawn.
57    ///
58    /// The `context` should have the desired initial styling and fonts already set.
59    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    /// Construct a `Egui` associated with the given window.
77    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    /// Access to the inner `egui::CtxRef`.
87    pub fn ctx(&self) -> &egui::Context {
88        &self.context
89    }
90
91    /// Access to the currently tracked input state.
92    pub fn input(&self) -> &Input {
93        &self.input
94    }
95
96    /// Handles a raw window event, tracking all input and events relevant to the UI as necessary.
97    pub fn handle_raw_event(&mut self, event: &winit::event::WindowEvent) {
98        self.input.handle_raw_event(event);
99    }
100
101    /// Set the elapsed time since the `Egui` app started running.
102    pub fn set_elapsed_time(&mut self, elapsed: Duration) {
103        self.input.set_elapsed_time(elapsed);
104    }
105
106    /// Begin describing a UI frame.
107    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    /// Registers a wgpu::Texture with a egui::TextureId.
119    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    /// Registers a wgpu::Texture with an existing egui::TextureId.
133    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    /// Draws the contents of the inner `context` to the given frame.
153    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    /// Initialise user input and window event tracking with the given target scale factor and size
177    /// in pixels.
178    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    /// Handles a raw window event, tracking all input and events relevant to the UI as necessary.
195    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                        // Actually point delta
236                        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                        // TODO figure out why if I enable this the characters get ignored
262                        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    /// Set the elapsed time since the `Egui` app started running.
281    pub fn set_elapsed_time(&mut self, elapsed: Duration) {
282        self.raw.time = Some(elapsed.as_secs_f64());
283    }
284
285    /// Small helper for the common task of producing an `egui::Rect` describing the window.
286    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    /// Create a new `Renderer` from its parts.
297    ///
298    /// The `device` must be the same that was used to create the queue to which the `Renderer`s
299    /// render passes will be submitted.
300    ///
301    /// The `target_format` and `target_msaa_samples` should describe the target texture to which
302    /// the `Egui` will be rendered.
303    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    /// Construct a `Renderer` ready for drawing to the given window.
316    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    /// Encode a render pass for drawing the given context's texture to the given `dst_texture`.
324    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    /// Encodes a render pass for drawing the given context's texture to the given frame.
361    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    /// Produces a `CtxRef` ready for describing the UI for this frame.
383    pub fn context(&self) -> egui::Context {
384        self.ui.context.clone()
385    }
386
387    /// End the current frame,
388    pub fn end(mut self) {
389        self.end_inner();
390    }
391
392    // The inner `end` implementation, shared between `end` and `drop`.
393    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/// Translates winit to egui keycodes.
415#[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/// Translates winit to egui modifier keys.
481#[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
498/// We only want printable characters and ignore all special keys.
499fn 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}