diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index cd02bee00045e..feef6cc39b6cb 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -606,6 +606,7 @@ impl Plugin for AnimationPlugin { app.add_asset::() .register_asset_reflect::() .register_type::() + .register_type::() .add_systems( PostUpdate, animation_player.before(TransformSystem::TransformPropagate), diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 2e5700f2fdb30..4bdc7572e02e0 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -645,7 +645,7 @@ impl Components { /// A value that tracks when a system ran relative to other systems. /// This is used to power change detection. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Tick { tick: u32, } diff --git a/crates/bevy_ecs/src/schedule/graph_utils.rs b/crates/bevy_ecs/src/schedule/graph_utils.rs index 26b0a076fc1a7..9209ff05b94b8 100644 --- a/crates/bevy_ecs/src/schedule/graph_utils.rs +++ b/crates/bevy_ecs/src/schedule/graph_utils.rs @@ -76,7 +76,6 @@ pub(crate) struct GraphInfo { pub(crate) sets: Vec, pub(crate) dependencies: Vec, pub(crate) ambiguous_with: Ambiguity, - pub(crate) base_set: Option, } /// Converts 2D row-major pair of indices into a 1D array index. diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index 93f0d9232a0aa..4f848a003276c 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -747,10 +747,6 @@ impl ScheduleGraph { self.check_set(id, &**set)?; } - if let Some(base_set) = &graph_info.base_set { - self.check_set(id, &**base_set)?; - } - Ok(()) } diff --git a/crates/bevy_gizmos/src/lib.rs b/crates/bevy_gizmos/src/lib.rs index 89b265e19e19d..15e99651092a1 100644 --- a/crates/bevy_gizmos/src/lib.rs +++ b/crates/bevy_gizmos/src/lib.rs @@ -16,7 +16,6 @@ //! //! See the documentation on [`Gizmos`](crate::gizmos::Gizmos) for more examples. -use std::hash::{Hash, Hasher}; use std::mem; use bevy_app::{Last, Plugin, Update}; @@ -52,7 +51,6 @@ use bevy_render::{ Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_transform::components::{GlobalTransform, Transform}; -use bevy_utils::AHasher; pub mod gizmos; @@ -235,12 +233,13 @@ fn draw_all_aabbs( } fn color_from_entity(entity: Entity) -> Color { + use bevy_utils::RandomState; const U64_TO_DEGREES: f32 = 360.0 / u64::MAX as f32; + const STATE: RandomState = + RandomState::with_seeds(5952553601252303067, 16866614500153072625, 0, 0); - let mut hasher = AHasher::default(); - entity.hash(&mut hasher); - - let hue = hasher.finish() as f32 * U64_TO_DEGREES; + let hash = STATE.hash_one(entity); + let hue = hash as f32 * U64_TO_DEGREES; Color::hsl(hue, 1., 0.5) } diff --git a/crates/bevy_gizmos/src/pipeline_3d.rs b/crates/bevy_gizmos/src/pipeline_3d.rs index d87c678da2a42..14f033f30455f 100644 --- a/crates/bevy_gizmos/src/pipeline_3d.rs +++ b/crates/bevy_gizmos/src/pipeline_3d.rs @@ -70,7 +70,6 @@ impl SpecializedRenderPipeline for LineGizmoPipeline { fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { let mut shader_defs = vec![ - "GIZMO_3D".into(), #[cfg(feature = "webgl")] "SIXTEEN_BYTE_ALIGNMENT".into(), ]; diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index b38cf3d539c48..a0364acd7fd0e 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -1,4 +1,4 @@ -use bevy_app::{Plugin, PreUpdate, Update}; +use bevy_app::{Plugin, PreUpdate}; use bevy_asset::{load_internal_asset, AssetServer, Handle, HandleUntyped}; use bevy_core_pipeline::{ prelude::Camera3d, @@ -141,9 +141,15 @@ where if no_prepass_plugin_loaded { app.insert_resource(AnyPrepassPluginLoaded) - .add_systems(Update, update_previous_view_projections) // At the start of each frame, last frame's GlobalTransforms become this frame's PreviousGlobalTransforms - .add_systems(PreUpdate, update_mesh_previous_global_transforms); + // and last frame's view projection matrices become this frame's PreviousViewProjections + .add_systems( + PreUpdate, + ( + update_mesh_previous_global_transforms, + update_previous_view_projections, + ), + ); } let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { diff --git a/crates/bevy_pbr/src/prepass/prepass.wgsl b/crates/bevy_pbr/src/prepass/prepass.wgsl index fee9cd9fcf15e..50cbca994886e 100644 --- a/crates/bevy_pbr/src/prepass/prepass.wgsl +++ b/crates/bevy_pbr/src/prepass/prepass.wgsl @@ -57,18 +57,18 @@ struct VertexOutput { #ifdef MORPH_TARGETS fn morph_vertex(vertex_in: Vertex) -> Vertex { var vertex = vertex_in; - let weight_count = layer_count(); + let weight_count = bevy_pbr::morph::layer_count(); for (var i: u32 = 0u; i < weight_count; i ++) { - let weight = weight_at(i); + let weight = bevy_pbr::morph::weight_at(i); if weight == 0.0 { continue; } - vertex.position += weight * morph(vertex.index, position_offset, i); + vertex.position += weight * bevy_pbr::morph::morph(vertex.index, bevy_pbr::morph::position_offset, i); #ifdef VERTEX_NORMALS - vertex.normal += weight * morph(vertex.index, normal_offset, i); + vertex.normal += weight * bevy_pbr::morph::morph(vertex.index, bevy_pbr::morph::normal_offset, i); #endif #ifdef VERTEX_TANGENTS - vertex.tangent += vec4(weight * morph(vertex.index, tangent_offset, i), 0.0); + vertex.tangent += vec4(weight * bevy_pbr::morph::morph(vertex.index, bevy_pbr::morph::tangent_offset, i), 0.0); #endif } return vertex; diff --git a/crates/bevy_pbr/src/prepass/prepass_utils.wgsl b/crates/bevy_pbr/src/prepass/prepass_utils.wgsl index d4e9b5992764a..f36e373684934 100644 --- a/crates/bevy_pbr/src/prepass/prepass_utils.wgsl +++ b/crates/bevy_pbr/src/prepass/prepass_utils.wgsl @@ -20,7 +20,7 @@ fn prepass_normal(frag_coord: vec4, sample_index: u32) -> vec3 { #else let normal_sample = textureLoad(view_bindings::normal_prepass_texture, vec2(frag_coord.xy), 0); #endif // MULTISAMPLED - return normal_sample.xyz * 2.0 - vec3(1.0); + return normalize(normal_sample.xyz * 2.0 - vec3(1.0)); } #endif // NORMAL_PREPASS diff --git a/crates/bevy_pbr/src/render/pbr.wgsl b/crates/bevy_pbr/src/render/pbr.wgsl index cdb9586d435d2..0a9bd3959b8ba 100644 --- a/crates/bevy_pbr/src/render/pbr.wgsl +++ b/crates/bevy_pbr/src/render/pbr.wgsl @@ -55,7 +55,7 @@ fn fragment( #endif #ifdef VERTEX_UVS if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) { - output_color = output_color * textureSampleBias(pbr_bindings::base_color_texture, pbr_bindings::base_color_sampler, in.uv, view.mip_bias); + output_color = output_color * textureSampleBias(pbr_bindings::base_color_texture, pbr_bindings::base_color_sampler, uv, view.mip_bias); } #endif @@ -74,7 +74,7 @@ fn fragment( var emissive: vec4 = pbr_bindings::material.emissive; #ifdef VERTEX_UVS if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) { - emissive = vec4(emissive.rgb * textureSampleBias(pbr_bindings::emissive_texture, pbr_bindings::emissive_sampler, in.uv, view.mip_bias).rgb, 1.0); + emissive = vec4(emissive.rgb * textureSampleBias(pbr_bindings::emissive_texture, pbr_bindings::emissive_sampler, uv, view.mip_bias).rgb, 1.0); } #endif pbr_input.material.emissive = emissive; @@ -83,7 +83,7 @@ fn fragment( var perceptual_roughness: f32 = pbr_bindings::material.perceptual_roughness; #ifdef VERTEX_UVS if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) { - let metallic_roughness = textureSampleBias(pbr_bindings::metallic_roughness_texture, pbr_bindings::metallic_roughness_sampler, in.uv, view.mip_bias); + let metallic_roughness = textureSampleBias(pbr_bindings::metallic_roughness_texture, pbr_bindings::metallic_roughness_sampler, uv, view.mip_bias); // Sampling from GLTF standard channels for now metallic = metallic * metallic_roughness.b; perceptual_roughness = perceptual_roughness * metallic_roughness.g; @@ -96,7 +96,7 @@ fn fragment( var occlusion: vec3 = vec3(1.0); #ifdef VERTEX_UVS if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) { - occlusion = vec3(textureSampleBias(pbr_bindings::occlusion_texture, pbr_bindings::occlusion_sampler, in.uv, view.mip_bias).r); + occlusion = vec3(textureSampleBias(pbr_bindings::occlusion_texture, pbr_bindings::occlusion_sampler, uv, view.mip_bias).r); } #endif #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION diff --git a/crates/bevy_render/Cargo.toml b/crates/bevy_render/Cargo.toml index 08ee1fa9a7344..739208248325b 100644 --- a/crates/bevy_render/Cargo.toml +++ b/crates/bevy_render/Cargo.toml @@ -65,7 +65,6 @@ serde = { version = "1", features = ["derive"] } bitflags = "2.3" bytemuck = { version = "1.5", features = ["derive"] } smallvec = { version = "1.6", features = ["union", "const_generics"] } -once_cell = "1.4.1" # TODO: replace once_cell with std equivalent if/when this lands: https://github.com/rust-lang/rfcs/pull/2788 downcast-rs = "1.2.0" thread_local = "1.1" thiserror = "1.0" diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index cb625bf49d9db..61e814e9bc2d9 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -43,7 +43,6 @@ pub mod prelude { use bevy_window::{PrimaryWindow, RawHandleWrapper}; use globals::GlobalsPlugin; -pub use once_cell; use renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue}; use wgpu::Instance; diff --git a/crates/bevy_ui/src/focus.rs b/crates/bevy_ui/src/focus.rs index 1d0f3bf4e3372..534a72f2e4fec 100644 --- a/crates/bevy_ui/src/focus.rs +++ b/crates/bevy_ui/src/focus.rs @@ -1,4 +1,4 @@ -use crate::{camera_config::UiCameraConfig, CalculatedClip, Node, UiStack}; +use crate::{camera_config::UiCameraConfig, CalculatedClip, Node, UiScale, UiStack}; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ change_detection::DetectChangesMut, @@ -206,6 +206,7 @@ pub fn ui_focus_system( windows: Query<&Window>, mouse_button_input: Res>, touches_input: Res, + ui_scale: Res, ui_stack: Res, mut node_query: Query, primary_window: Query>, @@ -255,7 +256,10 @@ pub fn ui_focus_system( .ok() .and_then(|window| window.cursor_position()) }) - .or_else(|| touches_input.first_pressed_position()); + .or_else(|| touches_input.first_pressed_position()) + // The cursor position returned by `Window` only takes into account the window scale factor and not `UiScale`. + // To convert the cursor position to logical UI viewport coordinates we have to divide it by `UiScale`. + .map(|cursor_position| cursor_position / ui_scale.scale as f32); // prepare an iterator that contains all the nodes that have the cursor in their rect, // from the top node to the bottom one. this will also reset the interaction to `None` @@ -336,7 +340,7 @@ pub fn ui_focus_system( .collect::>() .into_iter(); - // set Clicked or Hovered on top nodes. as soon as a node with a `Block` focus policy is detected, + // set Pressed or Hovered on top nodes. as soon as a node with a `Block` focus policy is detected, // the iteration will stop on it because it "captures" the interaction. let mut iter = node_query.iter_many_mut(hovered_nodes.by_ref()); while let Some(node) = iter.fetch_next() { @@ -355,7 +359,7 @@ pub fn ui_focus_system( FocusPolicy::Block => { break; } - FocusPolicy::Pass => { /* allow the next node to be hovered/clicked */ } + FocusPolicy::Pass => { /* allow the next node to be hovered/pressed */ } } } // reset `Interaction` for the remaining lower nodes to `None`. those are the nodes that remain in diff --git a/crates/bevy_ui/src/layout/mod.rs b/crates/bevy_ui/src/layout/mod.rs index ca38a415003b8..cc4cc9301db91 100644 --- a/crates/bevy_ui/src/layout/mod.rs +++ b/crates/bevy_ui/src/layout/mod.rs @@ -301,7 +301,7 @@ pub fn ui_layout_system( // compute layouts ui_surface.compute_window_layouts(); - let physical_to_logical_factor = 1. / logical_to_physical_factor; + let physical_to_logical_factor = 1. / scale_factor; let to_logical = |v| (physical_to_logical_factor * v as f64) as f32; diff --git a/crates/bevy_ui/src/measurement.rs b/crates/bevy_ui/src/measurement.rs index fd31ae9ab4f96..344efd3775d2e 100644 --- a/crates/bevy_ui/src/measurement.rs +++ b/crates/bevy_ui/src/measurement.rs @@ -4,6 +4,7 @@ use bevy_math::Vec2; use bevy_reflect::Reflect; use std::fmt::Formatter; pub use taffy::style::AvailableSpace; +use taffy::{node::MeasureFunc, prelude::Size as TaffySize}; impl std::fmt::Debug for ContentSize { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { @@ -50,33 +51,27 @@ impl Measure for FixedMeasure { pub struct ContentSize { /// The `Measure` used to compute the intrinsic size #[reflect(ignore)] - pub(crate) measure_func: Option, + pub(crate) measure_func: Option, } impl ContentSize { /// Set a `Measure` for this function pub fn set(&mut self, measure: impl Measure) { - let measure_func = - move |size: taffy::prelude::Size>, - available: taffy::prelude::Size| { - let size = - measure.measure(size.width, size.height, available.width, available.height); - taffy::prelude::Size { - width: size.x, - height: size.y, - } - }; - self.measure_func = Some(taffy::node::MeasureFunc::Boxed(Box::new(measure_func))); + let measure_func = move |size: TaffySize<_>, available: TaffySize<_>| { + let size = measure.measure(size.width, size.height, available.width, available.height); + TaffySize { + width: size.x, + height: size.y, + } + }; + self.measure_func = Some(MeasureFunc::Boxed(Box::new(measure_func))); } } -#[allow(clippy::derivable_impls)] impl Default for ContentSize { fn default() -> Self { Self { - measure_func: Some(taffy::node::MeasureFunc::Raw(|_, _| { - taffy::prelude::Size::ZERO - })), + measure_func: Some(MeasureFunc::Raw(|_, _| TaffySize::ZERO)), } } } diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 66fa5e15be7be..4eb26df186957 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -8,11 +8,11 @@ use bevy_window::{PrimaryWindow, Window}; pub use pipeline::*; pub use render_pass::*; -use crate::UiTextureAtlasImage; use crate::{ - prelude::UiCameraConfig, BackgroundColor, BorderColor, CalculatedClip, Node, UiImage, UiStack, + prelude::UiCameraConfig, BackgroundColor, BorderColor, CalculatedClip, ContentSize, Node, + Style, UiImage, UiScale, UiStack, UiTextureAtlasImage, Val, }; -use crate::{ContentSize, Style, Val}; + use bevy_app::prelude::*; use bevy_asset::{load_internal_asset, AssetEvent, Assets, Handle, HandleUntyped}; use bevy_ecs::prelude::*; @@ -170,7 +170,6 @@ pub fn extract_atlas_uinodes( mut extracted_uinodes: ResMut, images: Extract>>, texture_atlases: Extract>>, - ui_stack: Extract>, uinode_query: Extract< Query< @@ -258,6 +257,7 @@ fn resolve_border_thickness(value: Val, parent_width: f32, viewport_size: Vec2) pub fn extract_uinode_borders( mut extracted_uinodes: ResMut, windows: Extract>>, + ui_scale: Extract>, ui_stack: Extract>, uinode_query: Extract< Query< @@ -277,10 +277,13 @@ pub fn extract_uinode_borders( ) { let image = bevy_render::texture::DEFAULT_IMAGE_HANDLE.typed(); - let viewport_size = windows + let ui_logical_viewport_size = windows .get_single() .map(|window| Vec2::new(window.resolution.width(), window.resolution.height())) - .unwrap_or(Vec2::ZERO); + .unwrap_or(Vec2::ZERO) + // The logical window resolutin returned by `Window` only takes into account the window scale factor and not `UiScale`, + // so we have to divide by `UiScale` to get the size of the UI viewport. + / ui_scale.scale as f32; for (stack_index, entity) in ui_stack.uinodes.iter().enumerate() { if let Ok((node, global_transform, style, border_color, parent, visibility, clip)) = @@ -300,11 +303,21 @@ pub fn extract_uinode_borders( let parent_width = parent .and_then(|parent| parent_node_query.get(parent.get()).ok()) .map(|parent_node| parent_node.size().x) - .unwrap_or(viewport_size.x); - let left = resolve_border_thickness(style.border.left, parent_width, viewport_size); - let right = resolve_border_thickness(style.border.right, parent_width, viewport_size); - let top = resolve_border_thickness(style.border.top, parent_width, viewport_size); - let bottom = resolve_border_thickness(style.border.bottom, parent_width, viewport_size); + .unwrap_or(ui_logical_viewport_size.x); + let left = + resolve_border_thickness(style.border.left, parent_width, ui_logical_viewport_size); + let right = resolve_border_thickness( + style.border.right, + parent_width, + ui_logical_viewport_size, + ); + let top = + resolve_border_thickness(style.border.top, parent_width, ui_logical_viewport_size); + let bottom = resolve_border_thickness( + style.border.bottom, + parent_width, + ui_logical_viewport_size, + ); // Calculate the border rects, ensuring no overlap. // The border occupies the space between the node's bounding rect and the node's bounding rect inset in each direction by the node's corresponding border value. @@ -396,7 +409,7 @@ pub fn extract_uinodes( } (image.texture.clone_weak(), image.flip_x, image.flip_y) } else { - (DEFAULT_IMAGE_HANDLE.typed().clone_weak(), false, false) + (DEFAULT_IMAGE_HANDLE.typed(), false, false) }; extracted_uinodes.uinodes.push(ExtractedUiNode { @@ -433,8 +446,10 @@ pub struct DefaultCameraView(pub Entity); pub fn extract_default_ui_camera_view( mut commands: Commands, + ui_scale: Extract>, query: Extract), With>>, ) { + let scale = (ui_scale.scale as f32).recip(); for (entity, camera, camera_ui) in &query { // ignore cameras with disabled ui if matches!(camera_ui, Some(&UiCameraConfig { show_ui: false, .. })) { @@ -446,8 +461,14 @@ pub fn extract_default_ui_camera_view( camera.physical_viewport_size(), ) { // use a projection matrix with the origin in the top left instead of the bottom left that comes with OrthographicProjection - let projection_matrix = - Mat4::orthographic_rh(0.0, logical_size.x, logical_size.y, 0.0, 0.0, UI_CAMERA_FAR); + let projection_matrix = Mat4::orthographic_rh( + 0.0, + logical_size.x * scale, + logical_size.y * scale, + 0.0, + 0.0, + UI_CAMERA_FAR, + ); let default_camera_view = commands .spawn(ExtractedView { projection: projection_matrix, @@ -481,6 +502,7 @@ pub fn extract_text_uinodes( texture_atlases: Extract>>, windows: Extract>>, ui_stack: Extract>, + ui_scale: Extract>, uinode_query: Extract< Query<( &Node, @@ -495,10 +517,11 @@ pub fn extract_text_uinodes( // TODO: Support window-independent UI scale: https://github.com/bevyengine/bevy/issues/5621 let scale_factor = windows .get_single() - .map(|window| window.resolution.scale_factor() as f32) - .unwrap_or(1.0); + .map(|window| window.resolution.scale_factor()) + .unwrap_or(1.0) + * ui_scale.scale; - let inverse_scale_factor = scale_factor.recip(); + let inverse_scale_factor = (scale_factor as f32).recip(); for (stack_index, entity) in ui_stack.uinodes.iter().enumerate() { if let Ok((uinode, global_transform, text, text_layout_info, visibility, clip)) = diff --git a/crates/bevy_ui/src/ui_node.rs b/crates/bevy_ui/src/ui_node.rs index 532007ed3b752..c4c128198e840 100644 --- a/crates/bevy_ui/src/ui_node.rs +++ b/crates/bevy_ui/src/ui_node.rs @@ -29,12 +29,12 @@ impl Node { self.calculated_size } - /// Returns the size of the node in physical pixels based on the given scale factor. + /// Returns the size of the node in physical pixels based on the given scale factor and `UiScale`. #[inline] - pub fn physical_size(&self, scale_factor: f64) -> Vec2 { + pub fn physical_size(&self, scale_factor: f64, ui_scale: f64) -> Vec2 { Vec2::new( - (self.calculated_size.x as f64 * scale_factor) as f32, - (self.calculated_size.y as f64 * scale_factor) as f32, + (self.calculated_size.x as f64 * scale_factor * ui_scale) as f32, + (self.calculated_size.y as f64 * scale_factor * ui_scale) as f32, ) } @@ -46,16 +46,21 @@ impl Node { /// Returns the physical pixel coordinates of the UI node, based on its [`GlobalTransform`] and the scale factor. #[inline] - pub fn physical_rect(&self, transform: &GlobalTransform, scale_factor: f64) -> Rect { + pub fn physical_rect( + &self, + transform: &GlobalTransform, + scale_factor: f64, + ui_scale: f64, + ) -> Rect { let rect = self.logical_rect(transform); Rect { min: Vec2::new( - (rect.min.x as f64 * scale_factor) as f32, - (rect.min.y as f64 * scale_factor) as f32, + (rect.min.x as f64 * scale_factor * ui_scale) as f32, + (rect.min.y as f64 * scale_factor * ui_scale) as f32, ), max: Vec2::new( - (rect.max.x as f64 * scale_factor) as f32, - (rect.max.y as f64 * scale_factor) as f32, + (rect.max.x as f64 * scale_factor * ui_scale) as f32, + (rect.max.y as f64 * scale_factor * ui_scale) as f32, ), } } diff --git a/crates/bevy_ui/src/widget/image.rs b/crates/bevy_ui/src/widget/image.rs index be9df0590c09c..f46dd5b4faa38 100644 --- a/crates/bevy_ui/src/widget/image.rs +++ b/crates/bevy_ui/src/widget/image.rs @@ -3,7 +3,6 @@ use crate::{ }; use bevy_asset::{Assets, Handle}; -#[cfg(feature = "bevy_text")] use bevy_ecs::query::Without; use bevy_ecs::{ prelude::Component, @@ -15,8 +14,6 @@ use bevy_math::Vec2; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_render::texture::Image; use bevy_sprite::TextureAtlas; -#[cfg(feature = "bevy_text")] -use bevy_text::Text; use bevy_window::{PrimaryWindow, Window}; /// The size of the image's texture @@ -73,20 +70,18 @@ impl Measure for ImageMeasure { } } +#[cfg(feature = "bevy_text")] +type UpdateImageFilter = (With, Without); +#[cfg(not(feature = "bevy_text"))] +type UpdateImageFilter = With; + /// Updates content size of the node based on the image provided pub fn update_image_content_size_system( mut previous_combined_scale_factor: Local, windows: Query<&Window, With>, ui_scale: Res, textures: Res>, - #[cfg(feature = "bevy_text")] mut query: Query< - (&mut ContentSize, &UiImage, &mut UiImageSize), - (With, Without), - >, - #[cfg(not(feature = "bevy_text"))] mut query: Query< - (&mut ContentSize, &UiImage, &mut UiImageSize), - With, - >, + mut query: Query<(&mut ContentSize, &UiImage, &mut UiImageSize), UpdateImageFilter>, ) { let combined_scale_factor = windows .get_single() @@ -120,23 +115,14 @@ pub fn update_atlas_content_size_system( windows: Query<&Window, With>, ui_scale: Res, atlases: Res>, - #[cfg(feature = "bevy_text")] mut atlas_query: Query< - ( - &mut ContentSize, - &Handle, - &UiTextureAtlasImage, - &mut UiImageSize, - ), - (With, Without, Without), - >, - #[cfg(not(feature = "bevy_text"))] mut atlas_query: Query< + mut atlas_query: Query< ( &mut ContentSize, &Handle, &UiTextureAtlasImage, &mut UiImageSize, ), - (With, Without), + (UpdateImageFilter, Without), >, ) { let combined_scale_factor = windows diff --git a/crates/bevy_ui/src/widget/text.rs b/crates/bevy_ui/src/widget/text.rs index 06d876eb7a684..82b47ceb5bd89 100644 --- a/crates/bevy_ui/src/widget/text.rs +++ b/crates/bevy_ui/src/widget/text.rs @@ -184,7 +184,8 @@ fn queue_text( // With `NoWrap` set, no constraints are placed on the width of the text. Vec2::splat(f32::INFINITY) } else { - node.physical_size(scale_factor) + // `scale_factor` is already multiplied by `UiScale` + node.physical_size(scale_factor, 1.) }; match text_pipeline.queue_text( diff --git a/crates/bevy_window/src/event.rs b/crates/bevy_window/src/event.rs index bc0dc9d872d7d..2a5250ab75359 100644 --- a/crates/bevy_window/src/event.rs +++ b/crates/bevy_window/src/event.rs @@ -91,6 +91,26 @@ pub struct WindowClosed { /// by the time this event is received. pub window: Entity, } + +/// An event that is sent whenever a window is destroyed by the underlying window system. +/// +/// Note that if your application only has a single window, this event may be your last chance to +/// persist state before the application terminates. +#[derive(Event, Debug, Clone, PartialEq, Eq, Reflect)] +#[reflect(Debug, PartialEq)] +#[cfg_attr( + feature = "serialize", + derive(serde::Serialize, serde::Deserialize), + reflect(Serialize, Deserialize) +)] +pub struct WindowDestroyed { + /// Window that has been destroyed. + /// + /// Note that this entity probably no longer exists + /// by the time this event is received. + pub window: Entity, +} + /// An event reporting that the mouse cursor has moved inside a window. /// /// The event is sent only if the cursor is over one of the application's windows. diff --git a/crates/bevy_window/src/lib.rs b/crates/bevy_window/src/lib.rs index 8850aa7f8de83..3ae1c083c8f2e 100644 --- a/crates/bevy_window/src/lib.rs +++ b/crates/bevy_window/src/lib.rs @@ -74,6 +74,7 @@ impl Plugin for WindowPlugin { .add_event::() .add_event::() .add_event::() + .add_event::() .add_event::() .add_event::() .add_event::() diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index ac4db6d29fb74..46b3e3e195f59 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -41,8 +41,8 @@ use bevy_utils::{ use bevy_window::{ exit_on_all_closed, CursorEntered, CursorLeft, CursorMoved, FileDragAndDrop, Ime, ReceivedCharacter, RequestRedraw, Window, WindowBackendScaleFactorChanged, - WindowCloseRequested, WindowCreated, WindowFocused, WindowMoved, WindowResized, - WindowScaleFactorChanged, WindowThemeChanged, + WindowCloseRequested, WindowCreated, WindowDestroyed, WindowFocused, WindowMoved, + WindowResized, WindowScaleFactorChanged, WindowThemeChanged, }; #[cfg(target_os = "android")] @@ -231,6 +231,7 @@ struct WindowEvents<'w> { window_focused: EventWriter<'w, WindowFocused>, window_moved: EventWriter<'w, WindowMoved>, window_theme_changed: EventWriter<'w, WindowThemeChanged>, + window_destroyed: EventWriter<'w, WindowDestroyed>, } #[derive(SystemParam)] @@ -638,6 +639,11 @@ pub fn winit_runner(mut app: App) { theme: convert_winit_theme(theme), }); } + WindowEvent::Destroyed => { + window_events.window_destroyed.send(WindowDestroyed { + window: window_entity, + }); + } _ => {} } diff --git a/examples/mobile/Cargo.toml b/examples/mobile/Cargo.toml index 8101ca0da6b4f..02c7d3f8a3363 100644 --- a/examples/mobile/Cargo.toml +++ b/examples/mobile/Cargo.toml @@ -18,6 +18,8 @@ package = "org.bevyengine.example" apk_name = "bevyexample" assets = "../../assets" resources = "../../assets/android-res" +# This strips debug symbols from the shared libraries, drastically reducing APK size. If you need them, remove the option. +strip = "strip" build_targets = ["aarch64-linux-android", "armv7-linux-androideabi"] [package.metadata.android.sdk] diff --git a/examples/stress_tests/many_buttons.rs b/examples/stress_tests/many_buttons.rs index d14866862db68..5513749f84539 100644 --- a/examples/stress_tests/many_buttons.rs +++ b/examples/stress_tests/many_buttons.rs @@ -3,6 +3,9 @@ //! To start the demo without text run //! `cargo run --example many_buttons --release no-text` //! +//! //! To start the demo without borders run +//! `cargo run --example many_buttons --release no-borders` +//! //| To do a full layout update each frame run //! `cargo run --example many_buttons --release recompute-layout` //! @@ -90,22 +93,40 @@ fn setup(mut commands: Commands) { }) .with_children(|commands| { let spawn_text = std::env::args().all(|arg| arg != "no-text"); + let border = if std::env::args().all(|arg| arg != "no-borders") { + UiRect::all(Val::Percent(10. / count_f)) + } else { + UiRect::DEFAULT + }; for i in 0..count { for j in 0..count { let color = as_rainbow(j % i.max(1)).into(); - spawn_button(commands, color, count_f, i, j, spawn_text); + let border_color = as_rainbow(i % j.max(1)).into(); + spawn_button( + commands, + color, + count_f, + i, + j, + spawn_text, + border, + border_color, + ); } } }); } +#[allow(clippy::too_many_arguments)] fn spawn_button( commands: &mut ChildBuilder, - color: BackgroundColor, + background_color: BackgroundColor, total: f32, i: usize, j: usize, spawn_text: bool, + border: UiRect, + border_color: BorderColor, ) { let width = 90.0 / total; let mut builder = commands.spawn(( @@ -117,12 +138,14 @@ fn spawn_button( left: Val::Percent(100.0 / total * j as f32), align_items: AlignItems::Center, position_type: PositionType::Absolute, + border, ..default() }, - background_color: color, + background_color, + border_color, ..default() }, - IdleColor(color), + IdleColor(background_color), )); if spawn_text { diff --git a/examples/ui/viewport_debug.rs b/examples/ui/viewport_debug.rs index 691d70ee6a3d8..bec9809b33ae4 100644 --- a/examples/ui/viewport_debug.rs +++ b/examples/ui/viewport_debug.rs @@ -1,10 +1,14 @@ -//! An example for debugging viewport coordinates - +//! A simple example for debugging viewport coordinates +//! +//! This example creates two uinode trees, one using viewport coordinates and one using pixel coordinates, +//! and then switches between them once per second using the `Display` style property. +//! If there are no problems both layouts should be identical, except for the color of the margin changing which is used to signal that the displayed uinode tree has changed +//! (red for viewport, yellow for pixel). use bevy::prelude::*; const PALETTE: [Color; 10] = [ - Color::ORANGE, - Color::BLUE, + Color::RED, + Color::YELLOW, Color::WHITE, Color::BEIGE, Color::CYAN, @@ -15,7 +19,7 @@ const PALETTE: [Color; 10] = [ Color::BLACK, ]; -#[derive(Default, Debug, Hash, Eq, PartialEq, Clone, States)] +#[derive(Component, Default, PartialEq)] enum Coords { #[default] Viewport, @@ -24,66 +28,66 @@ enum Coords { fn main() { App::new() + .insert_resource(UiScale { scale: 2.0 }) .add_plugins(DefaultPlugins.set(WindowPlugin { primary_window: Some(Window { - resolution: [800., 600.].into(), + resolution: [1600., 1200.].into(), title: "Viewport Coordinates Debug".to_string(), resizable: false, ..Default::default() }), ..Default::default() })) - .add_state::() .add_systems(Startup, setup) - .add_systems(OnEnter(Coords::Viewport), spawn_with_viewport_coords) - .add_systems(OnEnter(Coords::Pixel), spawn_with_pixel_coords) - .add_systems(OnExit(Coords::Viewport), despawn_nodes) - .add_systems(OnExit(Coords::Pixel), despawn_nodes) .add_systems(Update, update) .run(); } -fn despawn_nodes(mut commands: Commands, query: Query>) { - for entity in query.iter() { - commands.entity(entity).despawn(); - } -} - fn update( mut timer: Local, + mut visible_tree: Local, time: Res