mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Apply scale factor to ImageMeasure
sizes (#8545)
# Objective
In Bevy main, the unconstrained size of an `ImageBundle` or
`AtlasImageBundle` UI node is based solely on the size of its texture
and doesn't change with window scale factor or `UiScale`.
## Solution
* The size field of each `ImageMeasure` should be multiplied by the
current combined scale factor.
* Each `ImageMeasure` should be updated when the combined scale factor
is changed.
## Example:
```rust
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.insert_resource(UiScale { scale: 1.5 })
.add_systems(Startup, setup)
.run();
}
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2dBundle::default());
commands.spawn(NodeBundle {
style: Style {
// The size of the "bevy_logo_dark.png" texture is 520x130 pixels
width: Val::Px(520.),
height: Val::Px(130.),
..Default::default()
},
background_color: Color::RED.into(),
..Default::default()
});
commands
.spawn(ImageBundle {
style: Style {
position_type: PositionType::Absolute,
..Default::default()
},
image: UiImage::new(asset_server.load("bevy_logo_dark.png")),
..Default::default()
});
}
```
The red node is given a size with the same dimensions as the texture. So
we would expect the texture to fill the node exactly.
* Result with Bevy main branch bb59509d44
:
<img width="400" alt="image-size-broke"
src="https://github.com/bevyengine/bevy/assets/27962798/19fd927d-ecc5-49a7-be05-c121a8df163f">
* Result with this PR (and Bevy 0.10.1):
<img width="400" alt="image-size-fixed"
src="https://github.com/bevyengine/bevy/assets/27962798/40b47820-5f2d-408f-88ef-9e2beb9c92a0">
---
## Changelog
`bevy_ui::widget::image`
* Update all `ImageMeasure`s on changes to the window scale factor or
`UiScale`.
* Multiply `ImageMeasure::size` by the window scale factor and
`UiScale`.
## Migration Guide
This commit is contained in:
parent
29f7293e30
commit
cdaae01c74
1 changed files with 48 additions and 17 deletions
|
@ -1,14 +1,15 @@
|
|||
use crate::{
|
||||
measurement::AvailableSpace, ContentSize, Measure, Node, UiImage, UiTextureAtlasImage,
|
||||
measurement::AvailableSpace, ContentSize, Measure, Node, UiImage, UiScale, UiTextureAtlasImage,
|
||||
};
|
||||
use bevy_asset::{Assets, Handle};
|
||||
|
||||
#[cfg(feature = "bevy_text")]
|
||||
use bevy_ecs::query::Without;
|
||||
use bevy_ecs::{
|
||||
prelude::Component,
|
||||
query::With,
|
||||
reflect::ReflectComponent,
|
||||
system::{Query, Res},
|
||||
system::{Local, Query, Res},
|
||||
};
|
||||
use bevy_math::Vec2;
|
||||
use bevy_reflect::{std_traits::ReflectDefault, FromReflect, Reflect, ReflectFromReflect};
|
||||
|
@ -16,26 +17,32 @@ 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 in physical pixels
|
||||
/// The size of the image's texture
|
||||
///
|
||||
/// This field is set automatically by `update_image_calculated_size_system`
|
||||
/// This component is updated automatically by [`update_image_content_size_system`]
|
||||
#[derive(Component, Debug, Copy, Clone, Default, Reflect, FromReflect)]
|
||||
#[reflect(Component, Default, FromReflect)]
|
||||
pub struct UiImageSize {
|
||||
/// The size of the image's texture
|
||||
///
|
||||
/// This field is updated automatically by [`update_image_content_size_system`]
|
||||
size: Vec2,
|
||||
}
|
||||
|
||||
impl UiImageSize {
|
||||
/// The size of the image's texture
|
||||
pub fn size(&self) -> Vec2 {
|
||||
self.size
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
/// Used to calculate the size of UI image nodes
|
||||
pub struct ImageMeasure {
|
||||
// target size of the image
|
||||
size: Vec2,
|
||||
/// The size of the image's texture
|
||||
pub size: Vec2,
|
||||
}
|
||||
|
||||
impl Measure for ImageMeasure {
|
||||
|
@ -68,6 +75,9 @@ impl Measure for ImageMeasure {
|
|||
|
||||
/// Updates content size of the node based on the image provided
|
||||
pub fn update_image_content_size_system(
|
||||
mut previous_combined_scale_factor: Local<f64>,
|
||||
windows: Query<&Window, With<PrimaryWindow>>,
|
||||
ui_scale: Res<UiScale>,
|
||||
textures: Res<Assets<Image>>,
|
||||
#[cfg(feature = "bevy_text")] mut query: Query<
|
||||
(&mut ContentSize, &UiImage, &mut UiImageSize),
|
||||
|
@ -78,23 +88,37 @@ pub fn update_image_content_size_system(
|
|||
With<Node>,
|
||||
>,
|
||||
) {
|
||||
let combined_scale_factor = windows
|
||||
.get_single()
|
||||
.map(|window| window.resolution.scale_factor())
|
||||
.unwrap_or(1.)
|
||||
* ui_scale.scale;
|
||||
|
||||
for (mut content_size, image, mut image_size) in &mut query {
|
||||
if let Some(texture) = textures.get(&image.texture) {
|
||||
let size = Vec2::new(
|
||||
texture.texture_descriptor.size.width as f32,
|
||||
texture.texture_descriptor.size.height as f32,
|
||||
);
|
||||
// Update only if size has changed to avoid needless layout calculations
|
||||
if size != image_size.size {
|
||||
// Update only if size or scale factor has changed to avoid needless layout calculations
|
||||
if size != image_size.size || combined_scale_factor != *previous_combined_scale_factor {
|
||||
image_size.size = size;
|
||||
content_size.set(ImageMeasure { size });
|
||||
content_size.set(ImageMeasure {
|
||||
// multiply the image size by the scale factor to get the physical size
|
||||
size: size * combined_scale_factor as f32,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*previous_combined_scale_factor = combined_scale_factor;
|
||||
}
|
||||
|
||||
/// Updates content size of the node based on the texture atlas sprite
|
||||
pub fn update_atlas_content_size_system(
|
||||
mut previous_combined_scale_factor: Local<f64>,
|
||||
windows: Query<&Window, With<PrimaryWindow>>,
|
||||
ui_scale: Res<UiScale>,
|
||||
atlases: Res<Assets<TextureAtlas>>,
|
||||
#[cfg(feature = "bevy_text")] mut atlas_query: Query<
|
||||
(
|
||||
|
@ -115,18 +139,25 @@ pub fn update_atlas_content_size_system(
|
|||
(With<Node>, Without<UiImage>),
|
||||
>,
|
||||
) {
|
||||
let combined_scale_factor = windows
|
||||
.get_single()
|
||||
.map(|window| window.resolution.scale_factor())
|
||||
.unwrap_or(1.)
|
||||
* ui_scale.scale;
|
||||
|
||||
for (mut content_size, atlas, atlas_image, mut image_size) in &mut atlas_query {
|
||||
if let Some(atlas) = atlases.get(atlas) {
|
||||
let texture_rect = atlas.textures[atlas_image.index];
|
||||
let size = Vec2::new(
|
||||
texture_rect.max.x - texture_rect.min.x,
|
||||
texture_rect.max.y - texture_rect.min.y,
|
||||
);
|
||||
// Update only if size has changed to avoid needless layout calculations
|
||||
if size != image_size.size {
|
||||
let size = atlas.textures[atlas_image.index].size();
|
||||
// Update only if size or scale factor has changed to avoid needless layout calculations
|
||||
if size != image_size.size || combined_scale_factor != *previous_combined_scale_factor {
|
||||
image_size.size = size;
|
||||
content_size.set(ImageMeasure { size });
|
||||
content_size.set(ImageMeasure {
|
||||
// multiply the image size by the scale factor to get the physical size
|
||||
size: size * combined_scale_factor as f32,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*previous_combined_scale_factor = combined_scale_factor;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue