diff --git a/Cargo.toml b/Cargo.toml index 1cbf7b24d..4bc309c83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ default_fonts = ["egui/default_fonts"] serde = ["egui/serde"] [dependencies] -bevy = { version = "0.9.0", default-features = false, features = ["bevy_render", "bevy_core_pipeline", "bevy_asset"] } +bevy = { version = "0.10", default-features = false, features = ["bevy_render", "bevy_core_pipeline", "bevy_asset"] } egui = { version = "0.21.0", default-features = false, features = ["bytemuck"] } webbrowser = { version = "0.8.2", optional = true } @@ -33,7 +33,7 @@ thread_local = { version = "1.1.0", optional = true } [dev-dependencies] once_cell = "1.16.0" version-sync = "0.9.4" -bevy = { version = "0.9.0", default-features = false, features = [ +bevy = { version = "0.10", default-features = false, features = [ "x11", "png", "bevy_pbr", diff --git a/examples/render_to_image_widget.rs b/examples/render_to_image_widget.rs index 8e76c202c..d629c0a82 100644 --- a/examples/render_to_image_widget.rs +++ b/examples/render_to_image_widget.rs @@ -9,7 +9,7 @@ use bevy::{ view::RenderLayers, }, }; -use bevy_egui::{egui, EguiContext, EguiPlugin}; +use bevy_egui::{egui, EguiContext, EguiPlugin, EguiUserTextures}; use egui::Widget; fn main() { @@ -34,7 +34,7 @@ struct MainPassCube; struct CubePreviewImage(Handle); fn setup( - mut egui_ctx: ResMut, + mut egui_ctx: ResMut, mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, @@ -58,6 +58,7 @@ fn setup( usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST | TextureUsages::RENDER_ATTACHMENT, + view_formats: &[], }, ..default() }; @@ -107,7 +108,7 @@ fn setup( }, camera: Camera { // render before the "main pass" camera - priority: -1, + order: -1, target: RenderTarget::Image(image_handle), ..default() }, @@ -145,17 +146,18 @@ fn setup( } fn render_to_image_example_system( - mut egui_ctx: ResMut, + egui_user_textures: Res, cube_preview_image: Res, preview_cube_query: Query<&Handle, With>, main_cube_query: Query<&Handle, With>, mut materials: ResMut>, + egui_ctx: Query<&EguiContext, With>, ) { - let cube_preview_texture_id = egui_ctx.image_id(&cube_preview_image).unwrap(); + let cube_preview_texture_id = egui_user_textures.image_id(&cube_preview_image).unwrap(); let preview_material_handle = preview_cube_query.single(); let preview_material = materials.get_mut(preview_material_handle).unwrap(); - let ctx = egui_ctx.ctx_mut(); + let ctx = egui_ctx.iter().next().unwrap(); let mut apply = false; egui::Window::new("Cube material preview").show(ctx, |ui| { ui.image(cube_preview_texture_id, [300.0, 300.0]); diff --git a/examples/side_panel.rs b/examples/side_panel.rs index 60411a678..4c071b55b 100644 --- a/examples/side_panel.rs +++ b/examples/side_panel.rs @@ -26,12 +26,14 @@ fn main() { } fn ui_example_system( - mut egui_context: ResMut, + egui_ctx: Query<&EguiContext>, mut occupied_screen_space: ResMut, ) { + let ctx = egui_ctx.iter().next().unwrap(); + occupied_screen_space.left = egui::SidePanel::left("left_panel") .resizable(true) - .show(egui_context.ctx_mut(), |ui| { + .show(ctx, |ui| { ui.allocate_rect(ui.available_rect_before_wrap(), egui::Sense::hover()); }) .response @@ -39,7 +41,7 @@ fn ui_example_system( .width(); occupied_screen_space.right = egui::SidePanel::right("right_panel") .resizable(true) - .show(egui_context.ctx_mut(), |ui| { + .show(ctx, |ui| { ui.allocate_rect(ui.available_rect_before_wrap(), egui::Sense::hover()); }) .response @@ -47,7 +49,7 @@ fn ui_example_system( .width(); occupied_screen_space.top = egui::TopBottomPanel::top("top_panel") .resizable(true) - .show(egui_context.ctx_mut(), |ui| { + .show(ctx, |ui| { ui.allocate_rect(ui.available_rect_before_wrap(), egui::Sense::hover()); }) .response @@ -55,7 +57,7 @@ fn ui_example_system( .height(); occupied_screen_space.bottom = egui::TopBottomPanel::bottom("bottom_panel") .resizable(true) - .show(egui_context.ctx_mut(), |ui| { + .show(ctx, |ui| { ui.allocate_rect(ui.available_rect_before_wrap(), egui::Sense::hover()); }) .response @@ -69,7 +71,10 @@ fn setup_system( mut materials: ResMut>, ) { commands.spawn(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })), + mesh: meshes.add(Mesh::from(shape::Plane { + size: 5.0, + subdivisions: 0, + })), material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), ..Default::default() }); @@ -103,7 +108,7 @@ fn setup_system( fn update_camera_transform_system( occupied_screen_space: Res, original_camera_transform: Res, - windows: Res, + windows: Query<&Window>, mut camera_query: Query<(&Projection, &mut Transform)>, ) { let (camera_projection, mut transform) = match camera_query.get_single_mut() { @@ -115,7 +120,7 @@ fn update_camera_transform_system( let frustum_height = 2.0 * distance_to_target * (camera_projection.fov * 0.5).tan(); let frustum_width = frustum_height * camera_projection.aspect_ratio; - let window = windows.get_primary().unwrap(); + let window = windows.iter().next().unwrap(); let left_taken = occupied_screen_space.left / window.width(); let right_taken = occupied_screen_space.right / window.width(); diff --git a/examples/simple.rs b/examples/simple.rs index cdfd0f996..fb688a7e2 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -11,8 +11,8 @@ fn main() { .run(); } -fn ui_example_system(mut egui_context: ResMut) { - egui::Window::new("Hello").show(egui_context.ctx_mut(), |ui| { +fn ui_example_system(egui_ctx: Query<&EguiContext>) { + egui::Window::new("Hello").show(egui_ctx.iter().next().unwrap(), |ui| { ui.label("world"); }); } diff --git a/examples/two_windows.rs b/examples/two_windows.rs index 2a5f3a672..5d871bb99 100644 --- a/examples/two_windows.rs +++ b/examples/two_windows.rs @@ -1,12 +1,9 @@ use bevy::{ prelude::*, render::camera::RenderTarget, - window::{CreateWindow, PresentMode, WindowId}, + window::{PresentMode, WindowRef, WindowResolution}, }; -use bevy_egui::{EguiContext, EguiPlugin}; -use once_cell::sync::Lazy; - -static SECOND_WINDOW_ID: Lazy = Lazy::new(WindowId::new); +use bevy_egui::{EguiContext, EguiPlugin, EguiUserTextures}; #[derive(Resource)] struct Images { @@ -26,25 +23,21 @@ fn main() { app.run(); } -fn create_new_window_system( - mut create_window_events: EventWriter, - mut commands: Commands, -) { - // sends out a "CreateWindow" event, which will be received by the windowing backend - create_window_events.send(CreateWindow { - id: *SECOND_WINDOW_ID, - descriptor: WindowDescriptor { - width: 800., - height: 600., +fn create_new_window_system(mut commands: Commands) { + // Spawn a second window + let second_window_id = commands + .spawn(Window { + title: "Second window".to_owned(), + resolution: WindowResolution::new(800.0, 600.0), present_mode: PresentMode::AutoVsync, - title: "Second window".to_string(), ..Default::default() - }, - }); + }) + .id(); + // second window camera commands.spawn(Camera3dBundle { camera: Camera { - target: RenderTarget::Window(*SECOND_WINDOW_ID), + target: RenderTarget::Window(WindowRef::Entity(second_window_id)), ..Default::default() }, transform: Transform::from_xyz(6.0, 0.0, 0.0).looking_at(Vec3::ZERO, Vec3::Y), @@ -69,15 +62,16 @@ struct SharedUiState { } fn ui_first_window_system( - mut egui_context: ResMut, + mut egui_user_textures: ResMut, mut ui_state: Local, mut shared_ui_state: ResMut, images: Res, + egui_ctx: Query<&EguiContext>, ) { - let bevy_texture_id = egui_context.add_image(images.bevy_icon.clone_weak()); + let bevy_texture_id = egui_user_textures.add_image(images.bevy_icon.clone_weak()); egui::Window::new("First Window") .vscroll(true) - .show(egui_context.ctx_mut(), |ui| { + .show(egui_ctx.iter().next().unwrap(), |ui| { ui.horizontal(|ui| { ui.label("Write something: "); ui.text_edit_singleline(&mut ui_state.input); @@ -92,15 +86,18 @@ fn ui_first_window_system( } fn ui_second_window_system( - mut egui_context: ResMut, + mut egui_user_textures: ResMut, mut ui_state: Local, mut shared_ui_state: ResMut, images: Res, + egui_ctx: Query<&EguiContext>, ) { - let bevy_texture_id = egui_context.add_image(images.bevy_icon.clone_weak()); - let ctx = match egui_context.try_ctx_for_window_mut(*SECOND_WINDOW_ID) { + let bevy_texture_id = egui_user_textures.add_image(images.bevy_icon.clone_weak()); + let ctx = match egui_ctx.iter().nth(1) { Some(ctx) => ctx, - None => return, + None => { + return; + } }; egui::Window::new("Second Window") .vscroll(true) diff --git a/examples/ui.rs b/examples/ui.rs index 11e1dae11..f8c1db137 100644 --- a/examples/ui.rs +++ b/examples/ui.rs @@ -1,5 +1,5 @@ use bevy::prelude::*; -use bevy_egui::{egui, EguiContext, EguiPlugin, EguiSettings}; +use bevy_egui::{egui, EguiContext, EguiPlugin, EguiSettings, EguiUserTextures}; struct Images { bevy_icon: Handle, @@ -23,7 +23,7 @@ impl FromWorld for Images { fn main() { App::new() .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0))) - .insert_resource(Msaa { samples: 4 }) + .insert_resource(Msaa::Sample4) .init_resource::() .add_plugins(DefaultPlugins) .add_plugin(EguiPlugin) @@ -43,8 +43,8 @@ struct UiState { is_window_open: bool, } -fn configure_visuals_system(mut egui_ctx: ResMut) { - egui_ctx.ctx_mut().set_visuals(egui::Visuals { +fn configure_visuals_system(egui_ctx: Query<&EguiContext>) { + egui_ctx.iter().next().unwrap().set_visuals(egui::Visuals { window_rounding: 0.0.into(), ..Default::default() }); @@ -58,12 +58,12 @@ fn update_ui_scale_factor_system( keyboard_input: Res>, mut toggle_scale_factor: Local>, mut egui_settings: ResMut, - windows: Res, + windows: Query<&Window>, ) { if keyboard_input.just_pressed(KeyCode::Slash) || toggle_scale_factor.is_none() { *toggle_scale_factor = Some(!toggle_scale_factor.unwrap_or(true)); - if let Some(window) = windows.get_primary() { + if let Some(window) = windows.iter().next() { let scale_factor = if toggle_scale_factor.unwrap() { 1.0 } else { @@ -75,7 +75,7 @@ fn update_ui_scale_factor_system( } fn ui_example_system( - mut egui_ctx: ResMut, + mut egui_user_textures: ResMut, mut ui_state: ResMut, // You are not required to store Egui texture ids in systems. We store this one here just to // demonstrate that rendering by using a texture id of a removed image is handled without @@ -85,11 +85,13 @@ fn ui_example_system( // If you need to access the ids from multiple systems, you can also initialize the `Images` // resource while building the app and use `Res` instead. images: Local, + egui_ctx: Query<&EguiContext>, ) { + let ctx = egui_ctx.iter().next().unwrap(); let egui_texture_handle = ui_state .egui_texture_handle .get_or_insert_with(|| { - egui_ctx.ctx_mut().load_texture( + ctx.load_texture( "example-image", egui::ColorImage::example(), Default::default(), @@ -103,12 +105,14 @@ fn ui_example_system( if !*is_initialized { *is_initialized = true; - *rendered_texture_id = egui_ctx.add_image(images.bevy_icon.clone_weak()); + *rendered_texture_id = egui_user_textures.add_image(images.bevy_icon.clone_weak()); } + let ctx = egui_ctx.iter().next().unwrap(); + egui::SidePanel::left("side_panel") .default_width(200.0) - .show(egui_ctx.ctx_mut(), |ui| { + .show(ctx, |ui| { ui.heading("Side Panel"); ui.horizontal(|ui| { @@ -149,7 +153,7 @@ fn ui_example_system( }); }); - egui::TopBottomPanel::top("top_panel").show(egui_ctx.ctx_mut(), |ui| { + egui::TopBottomPanel::top("top_panel").show(ctx, |ui| { // The top panel is often a good place for a menu bar: egui::menu::bar(ui, |ui| { egui::menu::menu_button(ui, "File", |ui| { @@ -160,7 +164,7 @@ fn ui_example_system( }); }); - egui::CentralPanel::default().show(egui_ctx.ctx_mut(), |ui| { + egui::CentralPanel::default().show(ctx, |ui| { ui.heading("Egui Template"); ui.hyperlink("https://github.com/emilk/egui_template"); ui.add(egui::github_link_file_line!( @@ -185,7 +189,7 @@ fn ui_example_system( egui::Window::new("Window") .vscroll(true) .open(&mut ui_state.is_window_open) - .show(egui_ctx.ctx_mut(), |ui| { + .show(ctx, |ui| { ui.label("Windows can be moved by dragging them."); ui.label("They are automatically sized based on contents."); ui.label("You can turn on resizing and scrolling if you like."); @@ -198,14 +202,15 @@ fn ui_example_system( if load || invert { // If an image is already added to the context, it'll return an existing texture id. if ui_state.inverted { - *rendered_texture_id = egui_ctx.add_image(images.bevy_icon_inverted.clone_weak()); + *rendered_texture_id = + egui_user_textures.add_image(images.bevy_icon_inverted.clone_weak()); } else { - *rendered_texture_id = egui_ctx.add_image(images.bevy_icon.clone_weak()); + *rendered_texture_id = egui_user_textures.add_image(images.bevy_icon.clone_weak()); }; } if remove { - egui_ctx.remove_image(&images.bevy_icon); - egui_ctx.remove_image(&images.bevy_icon_inverted); + egui_user_textures.remove_image(&images.bevy_icon); + egui_user_textures.remove_image(&images.bevy_icon_inverted); } } diff --git a/src/egui_node.rs b/src/egui_node.rs index 1ee93cf90..e29fbbc54 100644 --- a/src/egui_node.rs +++ b/src/egui_node.rs @@ -1,11 +1,14 @@ -use crate::render_systems::{ - EguiPipelines, EguiTextureBindGroups, EguiTextureId, EguiTransform, EguiTransforms, - ExtractedEguiContext, ExtractedEguiSettings, ExtractedRenderOutput, ExtractedWindowSizes, +use crate::{ + render_systems::{ + EguiPipelines, EguiTextureBindGroups, EguiTextureId, EguiTransform, EguiTransforms, + ExtractedEguiSettings, + }, + EguiContext, EguiRenderOutput, WindowSize, }; use bevy::{ core::cast_slice, ecs::world::{FromWorld, World}, - prelude::{HandleUntyped, Resource}, + prelude::{Entity, HandleUntyped, Resource}, reflect::TypeUuid, render::{ render_graph::{Node, NodeRunError, RenderGraphContext}, @@ -14,16 +17,16 @@ use bevy::{ BlendComponent, BlendFactor, BlendOperation, BlendState, Buffer, BufferAddress, BufferBindingType, BufferDescriptor, BufferUsages, ColorTargetState, ColorWrites, Extent3d, FragmentState, FrontFace, IndexFormat, LoadOp, MultisampleState, Operations, - PipelineCache, PrimitiveState, RenderPassColorAttachment, RenderPassDescriptor, - RenderPipelineDescriptor, SamplerBindingType, Shader, ShaderStages, ShaderType, - SpecializedRenderPipeline, TextureDimension, TextureFormat, TextureSampleType, - TextureViewDimension, VertexBufferLayout, VertexFormat, VertexState, VertexStepMode, + PipelineCache, PrimitiveState, PushConstantRange, RenderPassColorAttachment, + RenderPassDescriptor, RenderPipelineDescriptor, SamplerBindingType, Shader, + ShaderStages, ShaderType, SpecializedRenderPipeline, TextureDimension, TextureFormat, + TextureSampleType, TextureViewDimension, VertexBufferLayout, VertexFormat, VertexState, + VertexStepMode, }, renderer::{RenderContext, RenderDevice, RenderQueue}, texture::Image, view::ExtractedWindows, }, - window::WindowId, }; /// Egui shader. @@ -101,10 +104,10 @@ impl SpecializedRenderPipeline for EguiPipeline { fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { RenderPipelineDescriptor { label: Some("egui render pipeline".into()), - layout: Some(vec![ + layout: vec![ self.transform_bind_group_layout.clone(), self.texture_bind_group_layout.clone(), - ]), + ], vertex: VertexState { shader: EGUI_SHADER_HANDLE.typed(), shader_defs: Vec::new(), @@ -146,6 +149,10 @@ impl SpecializedRenderPipeline for EguiPipeline { }, depth_stencil: None, multisample: MultisampleState::default(), + push_constant_ranges: vec![PushConstantRange { + stages: ShaderStages::FRAGMENT, + range: 0..0, + }], } } } @@ -159,7 +166,7 @@ struct DrawCommand { /// Egui render node. pub struct EguiNode { - window_id: WindowId, + window_entity: Entity, vertex_data: Vec, vertex_buffer_capacity: usize, vertex_buffer: Option, @@ -171,9 +178,9 @@ pub struct EguiNode { impl EguiNode { /// Constructs Egui render node. - pub fn new(window_id: WindowId) -> Self { + pub fn new(window_entity: Entity) -> Self { EguiNode { - window_id, + window_entity, draw_commands: Vec::new(), vertex_data: Vec::new(), vertex_buffer_capacity: 0, @@ -187,16 +194,21 @@ impl EguiNode { impl Node for EguiNode { fn update(&mut self, world: &mut World) { - let mut shapes = world.get_resource_mut::().unwrap(); - let shapes = match shapes.get_mut(&self.window_id) { - Some(shapes) => shapes, - None => return, + let mut windows = world.query::<(&EguiContext, &mut EguiRenderOutput, &WindowSize)>(); + + let Ok( + (egui_context, mut render_output, window_size), + ) = windows.get_mut(world, self.window_entity) else { + // No egui context + return; }; - let shapes = std::mem::take(&mut shapes.shapes); - let window_size = &world.get_resource::().unwrap()[&self.window_id]; + let window_size = *window_size; + + let shapes = std::mem::take(&mut render_output.shapes); + let egui_paint_jobs = egui_context.tessellate(shapes); + let egui_settings = &world.get_resource::().unwrap(); - let egui_context = &world.get_resource::().unwrap(); let render_device = world.get_resource::().unwrap(); @@ -205,8 +217,6 @@ impl Node for EguiNode { return; } - let egui_paint_jobs = egui_context[&self.window_id].tessellate(shapes); - let mut index_offset = 0; self.draw_commands.clear(); @@ -252,7 +262,7 @@ impl Node for EguiNode { index_offset += mesh.vertices.len() as u32; let texture_handle = match mesh.texture_id { - egui::TextureId::Managed(id) => EguiTextureId::Managed(self.window_id, id), + egui::TextureId::Managed(id) => EguiTextureId::Managed(self.window_entity, id), egui::TextureId::User(id) => EguiTextureId::User(id), }; @@ -309,7 +319,7 @@ impl Node for EguiNode { let extracted_windows = &world.get_resource::().unwrap().windows; let extracted_window = - if let Some(extracted_window) = extracted_windows.get(&self.window_id) { + if let Some(extracted_window) = extracted_windows.get(&self.window_entity) { extracted_window } else { return Ok(()); // No window @@ -338,7 +348,7 @@ impl Node for EguiNode { let mut render_pass = render_context - .command_encoder + .command_encoder() .begin_render_pass(&RenderPassDescriptor { label: Some("egui render pass"), color_attachments: &[Some(RenderPassColorAttachment { @@ -352,7 +362,7 @@ impl Node for EguiNode { depth_stencil_attachment: None, }); - let Some(pipeline_id) = egui_pipelines.get(&extracted_window.id) else { return Ok(()) }; + let Some(pipeline_id) = egui_pipelines.get(&extracted_window.entity) else { return Ok(()) }; let Some(pipeline) = pipeline_cache.get_render_pipeline(*pipeline_id) else { return Ok(()) }; render_pass.set_pipeline(pipeline); @@ -362,7 +372,7 @@ impl Node for EguiNode { IndexFormat::Uint32, ); - let transform_buffer_offset = egui_transforms.offsets[&self.window_id]; + let transform_buffer_offset = egui_transforms.offsets[&self.window_entity]; let transform_buffer_bind_group = &egui_transforms.bind_group.as_ref().unwrap().1; render_pass.set_bind_group(0, transform_buffer_bind_group, &[transform_buffer_offset]); diff --git a/src/lib.rs b/src/lib.rs index b015d57d9..c06bc5dad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,8 +33,8 @@ //! .run(); //! } //! -//! fn ui_example_system(mut egui_context: ResMut) { -//! egui::Window::new("Hello").show(egui_context.ctx_mut(), |ui| { +//! fn ui_example_system(egui_context: Query<&EguiContext>) { +//! egui::Window::new("Hello").show(egui_context.iter().next().unwrap(), |ui| { //! ui.label("world"); //! }); //! } @@ -68,20 +68,23 @@ use crate::{ #[cfg(all(feature = "manage_clipboard", not(target_arch = "wasm32")))] use arboard::Clipboard; use bevy::{ - app::{App, CoreStage, Plugin, StartupStage}, + app::{App, Plugin}, asset::{AssetEvent, Assets, Handle}, - ecs::{event::EventReader, schedule::SystemLabel, system::ResMut}, + ecs::{event::EventReader, system::ResMut}, input::InputSystem, log, - prelude::{Deref, DerefMut, IntoSystemDescriptor, Resource, Shader}, + prelude::{ + Added, Commands, Component, CoreSet, Deref, DerefMut, Entity, IntoSystemAppConfigs, + IntoSystemConfig, IntoSystemConfigs, Query, Resource, Shader, StartupSet, SystemSet, With, + }, render::{ - render_graph::RenderGraph, render_resource::SpecializedRenderPipelines, texture::Image, - RenderApp, RenderStage, + render_resource::SpecializedRenderPipelines, texture::Image, ExtractSchedule, RenderApp, + RenderSet, }, utils::HashMap, - window::WindowId, + window::Window, }; -use egui_node::EguiNode; + use std::borrow::Cow; #[cfg(all(feature = "manage_clipboard", not(target_arch = "wasm32")))] use std::cell::{RefCell, RefMut}; @@ -101,8 +104,8 @@ pub struct EguiSettings { /// use bevy::prelude::*; /// use bevy_egui::EguiSettings; /// - /// fn update_ui_scale_factor(mut egui_settings: ResMut, windows: Res) { - /// if let Some(window) = windows.get_primary() { + /// fn update_ui_scale_factor(mut egui_settings: ResMut, windows: Query<&Window>) { + /// if let Some(window) = windows.iter().next() { /// egui_settings.scale_factor = 1.0 / window.scale_factor(); /// } /// } @@ -124,26 +127,10 @@ impl Default for EguiSettings { } } -/// Stores [`EguiRenderOutput`] for each window. -#[derive(Resource, Deref, DerefMut, Default)] -pub struct EguiRenderOutputContainer(pub HashMap); - -/// Stores [`EguiInput`] for each window. -#[derive(Resource, Deref, DerefMut, Default)] -pub struct EguiRenderInputContainer(pub HashMap); - -/// Stores [`EguiOutputContainer`] for each window. -#[derive(Resource, Deref, DerefMut, Default)] -pub struct EguiOutputContainer(pub HashMap); - -/// Stores [`WindowSize`] for each window. -#[derive(Resource, Deref, DerefMut, Default)] -pub struct EguiWindowSizeContainer(pub HashMap); - /// Is used for storing the input passed to Egui in the [`EguiRenderInputContainer`] resource. /// /// It gets reset during the [`EguiSystem::ProcessInput`] system. -#[derive(Clone, Debug, Default, Deref, DerefMut)] +#[derive(Component, Clone, Debug, Default, Deref, DerefMut)] pub struct EguiInput(pub egui::RawInput); /// A resource for accessing clipboard. @@ -218,8 +205,8 @@ impl EguiClipboard { } } -/// Is used for storing Egui shapes in the [`EguiRenderOutputContainer`] resource. -#[derive(Clone, Default, Debug, Resource)] +/// Is used for storing Egui shapes. +#[derive(Component, Clone, Default, Debug, Resource)] pub struct EguiRenderOutput { /// Pairs of rectangles and paint commands. /// @@ -230,132 +217,36 @@ pub struct EguiRenderOutput { pub textures_delta: egui::TexturesDelta, } -/// Is used for storing Egui output ine the [`EguiOutputContainer`] resource.. -#[derive(Clone, Default, Resource)] +/// Is used for storing Egui output. +#[derive(Component, Clone, Default)] pub struct EguiOutput { /// The field gets updated during the [`EguiSystem::ProcessOutput`] system in the [`CoreStage::PostUpdate`]. pub platform_output: egui::PlatformOutput, } -/// A resource for storing `bevy_egui` context. +/// A component for storing `bevy_egui` context. +#[derive(Clone, Component, Default, Deref, DerefMut)] +pub struct EguiContext(pub egui::Context); + +/// A resource for storing `bevy_egui` mouse position. +#[derive(Resource, Component, Default, Deref, DerefMut)] +pub struct EguiMousePosition(pub Option<(Entity, egui::Vec2)>); + +/// A resource for storing `bevy_egui` user textures. #[derive(Clone, Resource)] -pub struct EguiContext { - ctx: HashMap, - user_textures: HashMap, u64>, +pub struct EguiUserTextures { + textures: HashMap, u64>, last_texture_id: u64, - mouse_position: Option<(WindowId, egui::Vec2)>, } -impl EguiContext { +impl EguiUserTextures { fn new() -> Self { Self { - ctx: HashMap::default(), - user_textures: Default::default(), + textures: Default::default(), last_texture_id: 0, - mouse_position: None, } } - /// Egui context of the primary window. - /// - /// This function is only available when the `immutable_ctx` feature is enabled. - /// The preferable way is to use `ctx_mut` to avoid unpredictable blocking inside UI systems. - #[cfg(feature = "immutable_ctx")] - #[must_use] - #[track_caller] - pub fn ctx(&self) -> &egui::Context { - self.ctx.get(&WindowId::primary()).expect("`EguiContext::ctx` was called for an uninitialized context (primary window), consider moving your startup system to the `StartupStage::Startup` stage or run it after the `EguiStartupSystem::InitContexts` system") - } - - /// Egui context for a specific window. - /// If you want to display UI on a non-primary window, make sure to set up the render graph by - /// calling [`setup_pipeline`]. - /// - /// This function is only available when the `immutable_ctx` feature is enabled. - /// The preferable way is to use `ctx_for_window_mut` to avoid unpredictable blocking inside UI - /// systems. - #[cfg(feature = "immutable_ctx")] - #[must_use] - #[track_caller] - pub fn ctx_for_window(&self, window: WindowId) -> &egui::Context { - self.ctx - .get(&window) - .unwrap_or_else(|| panic!("`EguiContext::ctx_for_window` was called for an uninitialized context (window {}), consider moving your UI system to the `CoreStage::Update` stage or run it after the `EguiSystem::BeginFrame` system (`StartupStage::Startup` or `EguiStartupSystem::InitContexts` for startup systems respectively)", window)) - } - - /// Fallible variant of [`EguiContext::ctx_for_window`]. Make sure to set up the render graph by - /// calling [`setup_pipeline`]. - /// - /// This function is only available when the `immutable_ctx` feature is enabled. - /// The preferable way is to use `try_ctx_for_window_mut` to avoid unpredictable blocking inside - /// UI systems. - #[cfg(feature = "immutable_ctx")] - #[must_use] - pub fn try_ctx_for_window(&self, window: WindowId) -> Option<&egui::Context> { - self.ctx.get(&window) - } - - /// Egui context of the primary window. - #[must_use] - #[track_caller] - pub fn ctx_mut(&mut self) -> &egui::Context { - self.ctx.get(&WindowId::primary()).expect("`EguiContext::ctx_mut` was called for an uninitialized context (primary window), consider moving your startup system to the `StartupStage::Startup` stage or run it after the `EguiStartupSystem::InitContexts` system") - } - - /// Egui context for a specific window. - #[must_use] - #[track_caller] - pub fn ctx_for_window_mut(&mut self, window: WindowId) -> &egui::Context { - self.ctx - .get(&window) - .unwrap_or_else(|| panic!("`EguiContext::ctx_for_window_mut` was called for an uninitialized context (window {window}), consider moving your UI system to the `CoreStage::Update` stage or run it after the `EguiSystem::BeginFrame` system (`StartupStage::Startup` or `EguiStartupSystem::InitContexts` for startup systems respectively)")) - } - - /// Fallible variant of [`EguiContext::ctx_for_window_mut`]. - #[must_use] - pub fn try_ctx_for_window_mut(&mut self, window: WindowId) -> Option<&egui::Context> { - self.ctx.get(&window) - } - - /// Allows to get multiple contexts at the same time. This function is useful when you want - /// to get multiple window contexts without using the `immutable_ctx` feature. - /// - /// # Panics - /// - /// Panics if the passed window ids aren't unique. - #[must_use] - #[track_caller] - pub fn ctx_for_windows_mut( - &mut self, - ids: [WindowId; N], - ) -> [&egui::Context; N] { - let mut unique_ids = bevy::utils::HashSet::default(); - assert!( - ids.iter().all(move |id| unique_ids.insert(id)), - "Window ids passed to `EguiContext::ctx_for_windows_mut` must be unique: {ids:?}", - ); - ids.map(|id| self.ctx.get(&id).unwrap_or_else(|| panic!("`EguiContext::ctx_for_windows_mut` was called for an uninitialized context (window {id}), consider moving your UI system to the `CoreStage::Update` stage or run it after the `EguiSystem::BeginFrame` system (`StartupStage::Startup` or `EguiStartupSystem::InitContexts` for startup systems respectively)"))) - } - - /// Fallible variant of [`EguiContext::ctx_for_windows_mut`]. Make sure to set up the render - /// graph by calling [`setup_pipeline`]. - /// - /// # Panics - /// - /// Panics if the passed window ids aren't unique. - #[must_use] - pub fn try_ctx_for_windows_mut( - &mut self, - ids: [WindowId; N], - ) -> [Option<&egui::Context>; N] { - let mut unique_ids = bevy::utils::HashSet::default(); - assert!( - ids.iter().all(move |id| unique_ids.insert(id)), - "Window ids passed to `EguiContext::ctx_for_windows_mut` must be unique: {ids:?}", - ); - ids.map(|id| self.ctx.get(&id)) - } - /// Can accept either a strong or a weak handle. /// /// You may want to pass a weak handle if you control removing texture assets in your @@ -364,7 +255,7 @@ impl EguiContext { /// You'll want to pass a strong handle if a texture is used only in Egui and there are no /// handle copies stored anywhere else. pub fn add_image(&mut self, image: Handle) -> egui::TextureId { - let id = *self.user_textures.entry(image.clone()).or_insert_with(|| { + let id = *self.textures.entry(image.clone()).or_insert_with(|| { let id = self.last_texture_id; log::debug!("Add a new image (id: {}, handle: {:?})", id, image); self.last_texture_id += 1; @@ -375,7 +266,7 @@ impl EguiContext { /// Removes the image handle and an Egui texture id associated with it. pub fn remove_image(&mut self, image: &Handle) -> Option { - let id = self.user_textures.remove(image); + let id = self.textures.remove(image); log::debug!("Remove image (id: {:?}, handle: {:?})", id, image); id.map(egui::TextureId::User) } @@ -383,14 +274,14 @@ impl EguiContext { /// Returns an associated Egui texture id. #[must_use] pub fn image_id(&self, image: &Handle) -> Option { - self.user_textures + self.textures .get(image) .map(|&id| egui::TextureId::User(id)) } } /// Stores physical size and scale factor, is used as a helper to calculate logical size. -#[derive(Debug, Default, Clone, Copy, PartialEq)] +#[derive(Component, Debug, Default, Clone, Copy, PartialEq)] pub struct WindowSize { physical_width: f32, physical_height: f32, @@ -423,7 +314,7 @@ pub mod node { pub const EGUI_PASS: &str = "egui_pass"; } -#[derive(SystemLabel, Clone, Hash, Debug, Eq, PartialEq)] +#[derive(SystemSet, Clone, Hash, Debug, Eq, PartialEq)] /// The names of `bevy_egui` startup systems. pub enum EguiStartupSystem { /// Initializes Egui contexts for available windows. @@ -431,7 +322,7 @@ pub enum EguiStartupSystem { } /// The names of egui systems. -#[derive(SystemLabel, Clone, Hash, Debug, Eq, PartialEq)] +#[derive(SystemSet, Clone, Hash, Debug, Eq, PartialEq)] pub enum EguiSystem { /// Reads Egui inputs (keyboard, mouse, etc) and writes them into the [`EguiInput`] resource. /// @@ -449,50 +340,54 @@ impl Plugin for EguiPlugin { fn build(&self, app: &mut App) { let world = &mut app.world; world.insert_resource(EguiSettings::default()); - world.insert_resource(EguiRenderInputContainer( - HashMap::::default(), - )); - world.insert_resource(EguiOutputContainer( - HashMap::::default(), - )); - world.insert_resource(EguiWindowSizeContainer( - HashMap::::default(), - )); - world.insert_resource(EguiRenderOutputContainer(HashMap::< - WindowId, - EguiRenderOutput, - >::default())); world.insert_resource(EguiManagedTextures::default()); #[cfg(feature = "manage_clipboard")] world.insert_resource(EguiClipboard::default()); - world.insert_resource(EguiContext::new()); + world.insert_resource(EguiUserTextures::new()); + world.insert_resource(EguiMousePosition::default()); + + app.add_startup_system( + init_contexts_startup_system + .in_set(EguiStartupSystem::InitContexts) + .in_base_set(StartupSet::PreStartup), + ); - app.add_startup_system_to_stage( - StartupStage::PreStartup, - init_contexts_startup_system.label(EguiStartupSystem::InitContexts), + // TODO where is the correct place for this? + // Probably shouldn't need both add_startup_system & add_system version. + app.add_startup_system( + setup_new_windows_system + .in_set(EguiStartupSystem::InitContexts) + .in_base_set(StartupSet::PreStartup), + ); + app.add_system( + setup_new_windows_system + .in_set(EguiStartupSystem::InitContexts) + .in_base_set(StartupSet::PreStartup), ); - app.add_system_to_stage( - CoreStage::PreUpdate, + app.add_system( process_input_system - .label(EguiSystem::ProcessInput) - .after(InputSystem), + .in_set(EguiSystem::ProcessInput) + .after(InputSystem) + .in_base_set(CoreSet::PreUpdate), ); - app.add_system_to_stage( - CoreStage::PreUpdate, + app.add_system( begin_frame_system - .label(EguiSystem::BeginFrame) - .after(EguiSystem::ProcessInput), + .in_set(EguiSystem::BeginFrame) + .after(EguiSystem::ProcessInput) + .in_base_set(CoreSet::PreUpdate), ); - app.add_system_to_stage( - CoreStage::PostUpdate, - process_output_system.label(EguiSystem::ProcessOutput), + app.add_system( + process_output_system + .in_set(EguiSystem::ProcessOutput) + .in_base_set(CoreSet::PostUpdate), ); - app.add_system_to_stage( - CoreStage::PostUpdate, - update_egui_textures_system.after(EguiSystem::ProcessOutput), + app.add_system( + update_egui_textures_system + .after(EguiSystem::ProcessOutput) + .in_base_set(CoreSet::PostUpdate), ); - app.add_system_to_stage(CoreStage::Last, free_egui_textures_system); + app.add_system(free_egui_textures_system.in_base_set(CoreSet::Last)); let mut shaders = app.world.resource_mut::>(); shaders.set_untracked( @@ -505,34 +400,27 @@ impl Plugin for EguiPlugin { .init_resource::() .init_resource::>() .init_resource::() - .add_system_to_stage( - RenderStage::Extract, - render_systems::extract_egui_render_data_system, - ) - .add_system_to_stage( - RenderStage::Extract, - render_systems::extract_egui_textures_system, - ) - .add_system_to_stage( - RenderStage::Extract, - render_systems::setup_new_windows_system, + .add_systems( + ( + render_systems::extract_egui_render_data_system, + render_systems::extract_egui_textures_system, + render_systems::setup_new_windows_render_system, + ) + .into_configs() + .in_schedule(ExtractSchedule), ) - .add_system_to_stage( - RenderStage::Prepare, - render_systems::prepare_egui_transforms_system, + .add_system( + render_systems::prepare_egui_transforms_system.in_set(RenderSet::Prepare), ) - .add_system_to_stage(RenderStage::Queue, render_systems::queue_bind_groups_system) - .add_system_to_stage(RenderStage::Queue, render_systems::queue_pipelines_system); - - let mut render_graph = render_app.world.get_resource_mut::().unwrap(); - setup_pipeline(&mut render_graph, RenderGraphConfig::default()); + .add_system(render_systems::queue_bind_groups_system.in_set(RenderSet::Queue)) + .add_system(render_systems::queue_pipelines_system.in_set(RenderSet::Queue)); } } } /// Contains textures allocated and painted by Egui. #[derive(Resource, Deref, DerefMut, Default)] -pub struct EguiManagedTextures(pub HashMap<(WindowId, u64), EguiManagedTexture>); +pub struct EguiManagedTextures(pub HashMap<(Entity, u64), EguiManagedTexture>); /// Represents a texture allocated and painted by Egui. pub struct EguiManagedTexture { @@ -542,13 +430,27 @@ pub struct EguiManagedTexture { pub color_image: egui::ColorImage, } +/// Adds bevy_egui components to newly created windows. +pub fn setup_new_windows_system(mut commands: Commands, new_windows: Query>) { + for window in new_windows.iter() { + commands.entity(window).insert(( + EguiContext::default(), + EguiMousePosition::default(), + EguiRenderOutput::default(), + EguiInput::default(), + EguiOutput::default(), + WindowSize::default(), + )); + } +} + /// Updates textures painted by Egui. pub fn update_egui_textures_system( - mut egui_render_output: ResMut, + mut egui_render_output: Query<(Entity, &mut EguiRenderOutput), With>, mut egui_managed_textures: ResMut, mut image_assets: ResMut>, ) { - for (&window_id, egui_render_output) in egui_render_output.iter_mut() { + for (window_id, mut egui_render_output) in egui_render_output.iter_mut() { let set_textures = std::mem::take(&mut egui_render_output.textures_delta.set); for (texture_id, image_delta) in set_textures { @@ -596,13 +498,13 @@ pub fn update_egui_textures_system( } fn free_egui_textures_system( - mut egui_context: ResMut, - mut egui_render_output: ResMut, + mut egui_user_textures: ResMut, + mut egui_render_output: Query<(Entity, &mut EguiRenderOutput), With>, mut egui_managed_textures: ResMut, mut image_assets: ResMut>, mut image_events: EventReader>, ) { - for (&window_id, egui_render_output) in egui_render_output.iter_mut() { + for (window_id, mut egui_render_output) in egui_render_output.iter_mut() { let free_textures = std::mem::take(&mut egui_render_output.textures_delta.free); for texture_id in free_textures { if let egui::TextureId::Managed(texture_id) = texture_id { @@ -616,7 +518,7 @@ fn free_egui_textures_system( for image_event in image_events.iter() { if let AssetEvent::Removed { handle } = image_event { - egui_context.remove_image(handle); + egui_user_textures.remove_image(handle); } } } @@ -624,45 +526,19 @@ fn free_egui_textures_system( /// Egui's render graph config. pub struct RenderGraphConfig { /// Target window. - pub window_id: WindowId, + pub window: Entity, /// Render pass name. pub egui_pass: Cow<'static, str>, } -impl Default for RenderGraphConfig { - fn default() -> Self { - RenderGraphConfig { - window_id: WindowId::primary(), - egui_pass: Cow::Borrowed(node::EGUI_PASS), - } - } -} - -/// Set up egui render pipeline. -/// -/// The pipeline for the primary window will already be set up by the [`EguiPlugin`], -/// so you'll only need to manually call this if you want to use multiple windows. -pub fn setup_pipeline(render_graph: &mut RenderGraph, config: RenderGraphConfig) { - render_graph.add_node( - config.egui_pass.to_string(), - EguiNode::new(config.window_id), - ); - - render_graph - .add_node_edge( - bevy::render::main_graph::node::CAMERA_DRIVER, - config.egui_pass.to_string(), - ) - .unwrap(); - - let _ = render_graph.add_node_edge("ui_pass_driver", config.egui_pass.to_string()); -} - #[cfg(test)] mod tests { use super::*; use bevy::{ - app::PluginGroup, render::settings::WgpuSettings, winit::WinitPlugin, DefaultPlugins, + app::PluginGroup, + render::{settings::WgpuSettings, RenderPlugin}, + winit::WinitPlugin, + DefaultPlugins, }; #[test] @@ -673,60 +549,18 @@ mod tests { #[test] fn test_headless_mode() { App::new() - .insert_resource(WgpuSettings { - backends: None, - ..Default::default() - }) - .add_plugins(DefaultPlugins.build().disable::()) + .add_plugins( + DefaultPlugins + .set(RenderPlugin { + wgpu_settings: WgpuSettings { + backends: None, + ..Default::default() + }, + }) + .build() + .disable::(), + ) .add_plugin(EguiPlugin) .update(); } - - #[test] - fn test_ctx_for_windows_mut_unique_check_passes() { - let mut egui_context = EguiContext::new(); - let primary_window = WindowId::primary(); - let second_window = WindowId::new(); - egui_context.ctx.insert(primary_window, Default::default()); - egui_context.ctx.insert(second_window, Default::default()); - let [primary_ctx, second_ctx] = - egui_context.ctx_for_windows_mut([primary_window, second_window]); - assert!(primary_ctx != second_ctx); - } - - #[test] - #[should_panic( - expected = "Window ids passed to `EguiContext::ctx_for_windows_mut` must be unique" - )] - fn test_ctx_for_windows_mut_unique_check_panics() { - let mut egui_context = EguiContext::new(); - let primary_window = WindowId::primary(); - egui_context.ctx.insert(primary_window, Default::default()); - let _ = egui_context.ctx_for_windows_mut([primary_window, primary_window]); - } - - #[test] - fn test_try_ctx_for_windows_mut_unique_check_passes() { - let mut egui_context = EguiContext::new(); - let primary_window = WindowId::primary(); - let second_window = WindowId::new(); - egui_context.ctx.insert(primary_window, Default::default()); - egui_context.ctx.insert(second_window, Default::default()); - let [primary_ctx, second_ctx] = - egui_context.try_ctx_for_windows_mut([primary_window, second_window]); - assert!(primary_ctx.is_some()); - assert!(second_ctx.is_some()); - assert!(primary_ctx != second_ctx); - } - - #[test] - #[should_panic( - expected = "Window ids passed to `EguiContext::ctx_for_windows_mut` must be unique" - )] - fn test_try_ctx_for_windows_mut_unique_check_panics() { - let mut egui_context = EguiContext::new(); - let primary_window = WindowId::primary(); - egui_context.ctx.insert(primary_window, Default::default()); - let _ = egui_context.try_ctx_for_windows_mut([primary_window, primary_window]); - } } diff --git a/src/render_systems.rs b/src/render_systems.rs index b9b226d73..9f492daa4 100644 --- a/src/render_systems.rs +++ b/src/render_systems.rs @@ -1,7 +1,6 @@ use crate::{ - egui_node::{EguiPipeline, EguiPipelineKey}, - setup_pipeline, EguiContext, EguiManagedTextures, EguiRenderOutput, EguiRenderOutputContainer, - EguiSettings, EguiWindowSizeContainer, RenderGraphConfig, WindowSize, + egui_node::{EguiNode, EguiPipeline, EguiPipelineKey}, + EguiContext, EguiManagedTextures, EguiRenderOutput, EguiSettings, EguiUserTextures, WindowSize, }; use bevy::{ asset::HandleId, @@ -20,30 +19,17 @@ use bevy::{ Extract, }, utils::HashMap, - window::{CreateWindow, WindowId}, }; -/// Extracted Egui render output. -#[derive(Resource, Deref, DerefMut, Default)] -pub struct ExtractedRenderOutput(pub HashMap); - -/// Extracted window sizes. -#[derive(Resource, Deref, DerefMut, Default)] -pub struct ExtractedWindowSizes(pub HashMap); - /// Extracted Egui settings. #[derive(Resource, Deref, DerefMut, Default)] pub struct ExtractedEguiSettings(pub EguiSettings); -/// Extracted Egui contexts. -#[derive(Resource, Deref, DerefMut, Default)] -pub struct ExtractedEguiContext(pub HashMap); - /// Corresponds to Egui's [`egui::TextureId`]. #[derive(Debug, PartialEq, Eq, Hash)] pub enum EguiTextureId { /// Textures allocated via Egui. - Managed(WindowId, u64), + Managed(Entity, u64), /// Textures allocated via Bevy. User(u64), } @@ -52,7 +38,7 @@ pub enum EguiTextureId { #[derive(Resource, Default)] pub struct ExtractedEguiTextures { /// Maps Egui managed texture ids to Bevy image handles. - pub egui_textures: HashMap<(WindowId, u64), Handle>, + pub egui_textures: HashMap<(Entity, u64), Handle>, /// Maps Bevy managed texture handles to Egui user texture ids. pub user_textures: HashMap, u64>, } @@ -74,39 +60,44 @@ impl ExtractedEguiTextures { } /// Calls [`setup_pipeline`] for newly created windows to ensure egui works on them. -pub fn setup_new_windows_system( - mut new_windows: Extract>, - mut graph: ResMut, +pub fn setup_new_windows_render_system( + windows: Extract>>, + mut render_graph: ResMut, ) { - for window in new_windows.iter() { - setup_pipeline( - &mut graph, - RenderGraphConfig { - window_id: window.id, - egui_pass: std::borrow::Cow::Owned(format!("egui{}", window.id)), - }, - ) + for window in windows.iter() { + let egui_pass = format!("egui-{}-{}", window.index(), window.generation()); + + let new_node = EguiNode::new(window); + + render_graph.add_node(egui_pass.clone(), new_node); + + render_graph.add_node_edge( + bevy::render::main_graph::node::CAMERA_DRIVER, + egui_pass.to_string(), + ); } } /// Extracts Egui context, render output, settings and application window sizes. pub fn extract_egui_render_data_system( mut commands: Commands, - egui_render_output: Extract>, - window_sizes: Extract>, egui_settings: Extract>, - egui_context: Extract>, + windows: Extract>>, ) { - commands.insert_resource(ExtractedRenderOutput(egui_render_output.clone())); commands.insert_resource(ExtractedEguiSettings(egui_settings.clone())); - commands.insert_resource(ExtractedEguiContext(egui_context.ctx.clone())); - commands.insert_resource(ExtractedWindowSizes(window_sizes.clone())); + for (window_entity, ctx, egui_render_output, window_size) in windows.iter() { + commands.get_or_spawn(window_entity).insert(( + ctx.clone(), + egui_render_output.clone(), + window_size.clone(), + )); + } } /// Extracts Egui textures. pub fn extract_egui_textures_system( mut commands: Commands, - egui_context: Extract>, + egui_user_textures: Extract>, egui_managed_textures: Extract>, ) { commands.insert_resource(ExtractedEguiTextures { @@ -116,7 +107,7 @@ pub fn extract_egui_textures_system( ((window_id, texture_id), managed_texture.handle.clone()) }) .collect(), - user_textures: egui_context.user_textures.clone(), + user_textures: egui_user_textures.textures.clone(), }); } @@ -126,7 +117,7 @@ pub struct EguiTransforms { /// Uniform buffer. pub buffer: DynamicUniformBuffer, /// Offsets for each window. - pub offsets: HashMap, + pub offsets: HashMap, /// Bind group. pub bind_group: Option<(BufferId, BindGroup)>, } @@ -157,7 +148,7 @@ impl EguiTransform { /// Prepares Egui transforms. pub fn prepare_egui_transforms_system( mut egui_transforms: ResMut, - window_sizes: Res, + window_sizes: Query<(Entity, &WindowSize)>, egui_settings: Res, render_device: Res, @@ -168,12 +159,12 @@ pub fn prepare_egui_transforms_system( egui_transforms.buffer.clear(); egui_transforms.offsets.clear(); - for (window, size) in &window_sizes.0 { + for (window, size) in window_sizes.iter() { let offset = egui_transforms.buffer.push(EguiTransform::from_window_size( *size, egui_settings.scale_factor as f32, )); - egui_transforms.offsets.insert(*window, offset); + egui_transforms.offsets.insert(window, offset); } egui_transforms @@ -237,12 +228,12 @@ pub fn queue_bind_groups_system( /// Cached Pipeline IDs for the specialized `EguiPipeline`s #[derive(Resource)] -pub struct EguiPipelines(pub HashMap); +pub struct EguiPipelines(pub HashMap); /// Queue [`EguiPipeline`]s specialized on each window's swap chain texture format. pub fn queue_pipelines_system( mut commands: Commands, - mut pipeline_cache: ResMut, + pipeline_cache: Res, mut pipelines: ResMut>, egui_pipeline: Res, windows: Res, @@ -253,7 +244,7 @@ pub fn queue_pipelines_system( let key = EguiPipelineKey { texture_format: window.swap_chain_texture_format?, }; - let pipeline_id = pipelines.specialize(&mut pipeline_cache, &egui_pipeline, key); + let pipeline_id = pipelines.specialize(&pipeline_cache, &egui_pipeline, key); Some((*window_id, pipeline_id)) }) diff --git a/src/systems.rs b/src/systems.rs index 4146e8e41..a48c88f68 100644 --- a/src/systems.rs +++ b/src/systems.rs @@ -1,6 +1,6 @@ use crate::{ - EguiContext, EguiInput, EguiOutputContainer, EguiRenderInputContainer, - EguiRenderOutputContainer, EguiSettings, EguiWindowSizeContainer, WindowSize, + EguiContext, EguiInput, EguiMousePosition, EguiOutput, EguiRenderOutput, EguiSettings, + WindowSize, }; #[cfg(feature = "open_url")] use bevy::log; @@ -14,11 +14,11 @@ use bevy::{ mouse::{MouseButton, MouseButtonInput, MouseScrollUnit, MouseWheel}, ButtonState, Input, }, - prelude::{EventReader, Time}, + prelude::{Entity, EventReader, Query, Time}, utils::HashMap, window::{ - CursorEntered, CursorLeft, CursorMoved, ReceivedCharacter, RequestRedraw, WindowCreated, - WindowFocused, WindowId, Windows, + CursorEntered, CursorLeft, CursorMoved, ReceivedCharacter, RequestRedraw, Window, + WindowCreated, WindowFocused, }, }; use std::marker::PhantomData; @@ -58,7 +58,6 @@ pub struct InputResources<'w, 's> { #[cfg(feature = "manage_clipboard")] pub egui_clipboard: Res<'w, crate::EguiClipboard>, pub keyboard_input: Res<'w, Input>, - pub egui_input: ResMut<'w, EguiRenderInputContainer>, #[system_param(ignore)] _marker: PhantomData<&'s ()>, } @@ -66,53 +65,53 @@ pub struct InputResources<'w, 's> { #[allow(missing_docs)] #[derive(SystemParam)] pub struct WindowResources<'w, 's> { - pub focused_window: Local<'s, Option>, - pub windows: ResMut<'w, Windows>, - pub window_sizes: ResMut<'w, EguiWindowSizeContainer>, + pub focused_window: Local<'s, Option>, + pub windows: Query< + 'w, + 's, + ( + Entity, + &'static mut Window, + &'static mut EguiInput, + &'static mut WindowSize, + ), + >, #[system_param(ignore)] _marker: PhantomData<&'s ()>, } /// Initialises Egui contexts (for multiple windows) on startup. pub fn init_contexts_startup_system( - mut egui_context: ResMut, - mut egui_input: ResMut, mut window_resources: WindowResources, egui_settings: Res, ) { - update_window_contexts( - &mut egui_context, - &mut egui_input.0, - &mut window_resources, - &egui_settings, - ); + update_window_contexts(&mut window_resources, &egui_settings); } /// Processes Bevy input and feeds it to Egui. pub fn process_input_system( - mut egui_context: ResMut, mut input_events: InputEvents, - mut input_resources: InputResources, + input_resources: InputResources, mut window_resources: WindowResources, egui_settings: Res, + mut egui_mouse_position: ResMut, time: Res