diff --git a/README.md b/README.md index 7d0398f..39bfcc6 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ The Minimum Supported Rust Version for `pixels` will always be made available in - [Conway's Game of Life](./examples/conway) - [Custom Shader](./examples/custom-shader) - [Dear ImGui example with `winit`](./examples/imgui-winit) -- [Egui example with `winit`](./examples/egui-winit) +- [Egui example with `winit`](./examples/minimal-egui) - [Minimal example with SDL2](./examples/minimal-sdl2) - [Minimal example with `winit`](./examples/minimal-winit) - [Minimal example with `fltk`](./examples/minimal-fltk) diff --git a/examples/egui-winit/Cargo.toml b/examples/minimal-egui/Cargo.toml similarity index 55% rename from examples/egui-winit/Cargo.toml rename to examples/minimal-egui/Cargo.toml index 0c00e04..7188d43 100644 --- a/examples/egui-winit/Cargo.toml +++ b/examples/minimal-egui/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "egui-winit" +name = "minimal-egui" version = "0.1.0" authors = ["Jay Oster "] edition = "2018" @@ -11,9 +11,9 @@ optimize = ["log/release_max_level_warn"] default = ["optimize"] [dependencies] -egui = "0.14" -egui_wgpu_backend = "0.13" -egui_winit_platform = { version = "0.10", features = ["webbrowser"] } +egui = "0.15" +egui_wgpu_backend = { git = "https://github.com/hasenbanck/egui_wgpu_backend.git", rev = "961125e7bd2c71c5ead1d61a7ca7ffa8c0d17f48" } +egui-winit = { version = "0.15", default-features = false, features = ["links"] } env_logger = "0.9" log = "0.4" pixels = { path = "../.." } diff --git a/examples/egui-winit/README.md b/examples/minimal-egui/README.md similarity index 71% rename from examples/egui-winit/README.md rename to examples/minimal-egui/README.md index 5b85d66..74d0036 100644 --- a/examples/egui-winit/README.md +++ b/examples/minimal-egui/README.md @@ -1,13 +1,13 @@ # Egui Example -![Egui Example](../../img/egui-winit.png) +![Egui Example](../../img/minimal-egui.png) Minimal example with `egui` and `winit`. ## Running ```bash -cargo run --release --package egui-winit +cargo run --release --package minimal-egui ``` ## About diff --git a/examples/egui-winit/src/gui.rs b/examples/minimal-egui/src/gui.rs similarity index 71% rename from examples/egui-winit/src/gui.rs rename to examples/minimal-egui/src/gui.rs index 757efd1..1043999 100644 --- a/examples/egui-winit/src/gui.rs +++ b/examples/minimal-egui/src/gui.rs @@ -1,53 +1,53 @@ -use egui::{ClippedMesh, FontDefinitions}; +use egui::{ClippedMesh, CtxRef}; use egui_wgpu_backend::{BackendError, RenderPass, ScreenDescriptor}; -use egui_winit_platform::{Platform, PlatformDescriptor}; use pixels::{wgpu, PixelsContext}; -use std::time::Instant; use winit::window::Window; /// Manages all state required for rendering egui over `Pixels`. -pub(crate) struct Gui { +pub(crate) struct Framework { // State for egui. - start_time: Instant, - platform: Platform, + egui_ctx: CtxRef, + egui_state: egui_winit::State, screen_descriptor: ScreenDescriptor, rpass: RenderPass, paint_jobs: Vec, - // State for the demo app. + // State for the GUI + gui: Gui, +} + +/// Example application state. A real application will need a lot more state than this. +struct Gui { + /// Only show the egui window when true. window_open: bool, } -impl Gui { +impl Framework { /// Create egui. - pub(crate) fn new(width: u32, height: u32, scale_factor: f64, pixels: &pixels::Pixels) -> Self { - let platform = Platform::new(PlatformDescriptor { - physical_width: width, - physical_height: height, - scale_factor, - font_definitions: FontDefinitions::default(), - style: Default::default(), - }); + pub(crate) fn new(width: u32, height: u32, scale_factor: f32, pixels: &pixels::Pixels) -> Self { + let egui_ctx = CtxRef::default(); + let egui_state = egui_winit::State::from_pixels_per_point(scale_factor); let screen_descriptor = ScreenDescriptor { physical_width: width, physical_height: height, - scale_factor: scale_factor as f32, + scale_factor, }; let rpass = RenderPass::new(pixels.device(), pixels.render_texture_format(), 1); + let gui = Gui::new(); Self { - start_time: Instant::now(), - platform, + egui_ctx, + egui_state, screen_descriptor, rpass, paint_jobs: Vec::new(), - window_open: true, + gui, } } /// Handle input events from the window manager. - pub(crate) fn handle_event(&mut self, event: &winit::event::Event<'_, ()>) { - self.platform.handle_event(event); + pub(crate) fn handle_event(&mut self, event: &winit::event::WindowEvent) { + self.egui_state.on_event(&self.egui_ctx, event); } /// Resize egui. @@ -65,22 +65,58 @@ impl Gui { /// Prepare egui. pub(crate) fn prepare(&mut self, window: &Window) { - self.platform - .update_time(self.start_time.elapsed().as_secs_f64()); - // Begin the egui frame. - self.platform.begin_frame(); + let raw_input = self.egui_state.take_egui_input(window); + self.egui_ctx.begin_frame(raw_input); // Draw the demo application. - self.ui(&self.platform.context()); + self.gui.ui(&self.egui_ctx); // End the egui frame and create all paint jobs to prepare for rendering. - let (_output, paint_commands) = self.platform.end_frame(Some(window)); - self.paint_jobs = self.platform.context().tessellate(paint_commands); + let (output, paint_commands) = self.egui_ctx.end_frame(); + self.egui_state + .handle_output(window, &self.egui_ctx, output); + self.paint_jobs = self.egui_ctx.tessellate(paint_commands); + } + + /// Render egui. + pub(crate) fn render( + &mut self, + encoder: &mut wgpu::CommandEncoder, + render_target: &wgpu::TextureView, + context: &PixelsContext, + ) -> Result<(), BackendError> { + // Upload all resources to the GPU. + self.rpass + .update_texture(&context.device, &context.queue, &self.egui_ctx.texture()); + self.rpass + .update_user_textures(&context.device, &context.queue); + self.rpass.update_buffers( + &context.device, + &context.queue, + &self.paint_jobs, + &self.screen_descriptor, + ); + + // Record all render passes. + self.rpass.execute( + encoder, + render_target, + &self.paint_jobs, + &self.screen_descriptor, + None, + ) + } +} + +impl Gui { + /// Create a `Gui`. + fn new() -> Self { + Self { window_open: true } } /// Create the UI using egui. - fn ui(&mut self, ctx: &egui::CtxRef) { + fn ui(&mut self, ctx: &CtxRef) { egui::TopBottomPanel::top("menubar_container").show(ctx, |ui| { egui::menu::bar(ui, |ui| { egui::menu::menu(ui, "File", |ui| { @@ -106,36 +142,4 @@ impl Gui { }); }); } - - /// Render egui. - pub(crate) fn render( - &mut self, - encoder: &mut wgpu::CommandEncoder, - render_target: &wgpu::TextureView, - context: &PixelsContext, - ) -> Result<(), BackendError> { - // Upload all resources to the GPU. - self.rpass.update_texture( - &context.device, - &context.queue, - &self.platform.context().texture(), - ); - self.rpass - .update_user_textures(&context.device, &context.queue); - self.rpass.update_buffers( - &context.device, - &context.queue, - &self.paint_jobs, - &self.screen_descriptor, - ); - - // Record all render passes. - self.rpass.execute( - encoder, - render_target, - &self.paint_jobs, - &self.screen_descriptor, - None, - ) - } } diff --git a/examples/egui-winit/src/main.rs b/examples/minimal-egui/src/main.rs similarity index 70% rename from examples/egui-winit/src/main.rs rename to examples/minimal-egui/src/main.rs index 8223428..9086b09 100644 --- a/examples/egui-winit/src/main.rs +++ b/examples/minimal-egui/src/main.rs @@ -1,7 +1,7 @@ #![deny(clippy::all)] #![forbid(unsafe_code)] -use crate::gui::Gui; +use crate::gui::Framework; use log::error; use pixels::{Error, Pixels, SurfaceTexture}; use winit::dpi::LogicalSize; @@ -38,50 +38,19 @@ fn main() -> Result<(), Error> { .unwrap() }; - let (mut pixels, mut gui) = { + let (mut pixels, mut framework) = { let window_size = window.inner_size(); - let scale_factor = window.scale_factor(); + let scale_factor = window.scale_factor() as f32; let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window); let pixels = Pixels::new(WIDTH, HEIGHT, surface_texture)?; - let gui = Gui::new(window_size.width, window_size.height, scale_factor, &pixels); + let framework = + Framework::new(window_size.width, window_size.height, scale_factor, &pixels); - (pixels, gui) + (pixels, framework) }; let mut world = World::new(); event_loop.run(move |event, _, control_flow| { - // Update egui inputs - gui.handle_event(&event); - - // Draw the current frame - if let Event::RedrawRequested(_) = event { - // Draw the world - world.draw(pixels.get_frame()); - - // Prepare egui - gui.prepare(&window); - - // Render everything together - let render_result = pixels.render_with(|encoder, render_target, context| { - // Render the world texture - context.scaling_renderer.render(encoder, render_target); - - // Render egui - gui.render(encoder, render_target, context)?; - - Ok(()) - }); - - // Basic error handling - if render_result - .map_err(|e| error!("pixels.render() failed: {}", e)) - .is_err() - { - *control_flow = ControlFlow::Exit; - return; - } - } - // Handle input events if input.update(&event) { // Close events @@ -92,19 +61,54 @@ fn main() -> Result<(), Error> { // Update the scale factor if let Some(scale_factor) = input.scale_factor() { - gui.scale_factor(scale_factor); + framework.scale_factor(scale_factor); } // Resize the window if let Some(size) = input.window_resized() { pixels.resize_surface(size.width, size.height); - gui.resize(size.width, size.height); + framework.resize(size.width, size.height); } // Update internal state and request a redraw world.update(); window.request_redraw(); } + + match event { + Event::WindowEvent { event, .. } => { + // Update egui inputs + framework.handle_event(&event); + } + // Draw the current frame + Event::RedrawRequested(_) => { + // Draw the world + world.draw(pixels.get_frame()); + + // Prepare egui + framework.prepare(&window); + + // Render everything together + let render_result = pixels.render_with(|encoder, render_target, context| { + // Render the world texture + context.scaling_renderer.render(encoder, render_target); + + // Render egui + framework.render(encoder, render_target, context)?; + + Ok(()) + }); + + // Basic error handling + if render_result + .map_err(|e| error!("pixels.render() failed: {}", e)) + .is_err() + { + *control_flow = ControlFlow::Exit; + } + } + _ => (), + } }); } diff --git a/img/egui-winit.png b/img/minimal-egui.png similarity index 100% rename from img/egui-winit.png rename to img/minimal-egui.png