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,14 +1,15 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
measurement::AvailableSpace, ContentSize, Measure, Node, UiImage, UiTextureAtlasImage,
|
measurement::AvailableSpace, ContentSize, Measure, Node, UiImage, UiScale, UiTextureAtlasImage,
|
||||||
};
|
};
|
||||||
use bevy_asset::{Assets, Handle};
|
use bevy_asset::{Assets, Handle};
|
||||||
|
|
||||||
#[cfg(feature = "bevy_text")]
|
#[cfg(feature = "bevy_text")]
|
||||||
use bevy_ecs::query::Without;
|
use bevy_ecs::query::Without;
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
prelude::Component,
|
prelude::Component,
|
||||||
query::With,
|
query::With,
|
||||||
reflect::ReflectComponent,
|
reflect::ReflectComponent,
|
||||||
system::{Query, Res},
|
system::{Local, Query, Res},
|
||||||
};
|
};
|
||||||
use bevy_math::Vec2;
|
use bevy_math::Vec2;
|
||||||
use bevy_reflect::{std_traits::ReflectDefault, FromReflect, Reflect, ReflectFromReflect};
|
use bevy_reflect::{std_traits::ReflectDefault, FromReflect, Reflect, ReflectFromReflect};
|
||||||
@ -16,26 +17,32 @@ use bevy_render::texture::Image;
|
|||||||
use bevy_sprite::TextureAtlas;
|
use bevy_sprite::TextureAtlas;
|
||||||
#[cfg(feature = "bevy_text")]
|
#[cfg(feature = "bevy_text")]
|
||||||
use bevy_text::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)]
|
#[derive(Component, Debug, Copy, Clone, Default, Reflect, FromReflect)]
|
||||||
#[reflect(Component, Default, FromReflect)]
|
#[reflect(Component, Default, FromReflect)]
|
||||||
pub struct UiImageSize {
|
pub struct UiImageSize {
|
||||||
|
/// The size of the image's texture
|
||||||
|
///
|
||||||
|
/// This field is updated automatically by [`update_image_content_size_system`]
|
||||||
size: Vec2,
|
size: Vec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UiImageSize {
|
impl UiImageSize {
|
||||||
|
/// The size of the image's texture
|
||||||
pub fn size(&self) -> Vec2 {
|
pub fn size(&self) -> Vec2 {
|
||||||
self.size
|
self.size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
/// Used to calculate the size of UI image nodes
|
||||||
pub struct ImageMeasure {
|
pub struct ImageMeasure {
|
||||||
// target size of the image
|
/// The size of the image's texture
|
||||||
size: Vec2,
|
pub size: Vec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Measure for ImageMeasure {
|
impl Measure for ImageMeasure {
|
||||||
@ -68,6 +75,9 @@ impl Measure for ImageMeasure {
|
|||||||
|
|
||||||
/// Updates content size of the node based on the image provided
|
/// Updates content size of the node based on the image provided
|
||||||
pub fn update_image_content_size_system(
|
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>>,
|
textures: Res<Assets<Image>>,
|
||||||
#[cfg(feature = "bevy_text")] mut query: Query<
|
#[cfg(feature = "bevy_text")] mut query: Query<
|
||||||
(&mut ContentSize, &UiImage, &mut UiImageSize),
|
(&mut ContentSize, &UiImage, &mut UiImageSize),
|
||||||
@ -78,23 +88,37 @@ pub fn update_image_content_size_system(
|
|||||||
With<Node>,
|
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 {
|
for (mut content_size, image, mut image_size) in &mut query {
|
||||||
if let Some(texture) = textures.get(&image.texture) {
|
if let Some(texture) = textures.get(&image.texture) {
|
||||||
let size = Vec2::new(
|
let size = Vec2::new(
|
||||||
texture.texture_descriptor.size.width as f32,
|
texture.texture_descriptor.size.width as f32,
|
||||||
texture.texture_descriptor.size.height as f32,
|
texture.texture_descriptor.size.height as f32,
|
||||||
);
|
);
|
||||||
// Update only if size has changed to avoid needless layout calculations
|
// Update only if size or scale factor has changed to avoid needless layout calculations
|
||||||
if size != image_size.size {
|
if size != image_size.size || combined_scale_factor != *previous_combined_scale_factor {
|
||||||
image_size.size = size;
|
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
|
/// Updates content size of the node based on the texture atlas sprite
|
||||||
pub fn update_atlas_content_size_system(
|
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>>,
|
atlases: Res<Assets<TextureAtlas>>,
|
||||||
#[cfg(feature = "bevy_text")] mut atlas_query: Query<
|
#[cfg(feature = "bevy_text")] mut atlas_query: Query<
|
||||||
(
|
(
|
||||||
@ -115,18 +139,25 @@ pub fn update_atlas_content_size_system(
|
|||||||
(With<Node>, Without<UiImage>),
|
(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 {
|
for (mut content_size, atlas, atlas_image, mut image_size) in &mut atlas_query {
|
||||||
if let Some(atlas) = atlases.get(atlas) {
|
if let Some(atlas) = atlases.get(atlas) {
|
||||||
let texture_rect = atlas.textures[atlas_image.index];
|
let size = atlas.textures[atlas_image.index].size();
|
||||||
let size = Vec2::new(
|
// Update only if size or scale factor has changed to avoid needless layout calculations
|
||||||
texture_rect.max.x - texture_rect.min.x,
|
if size != image_size.size || combined_scale_factor != *previous_combined_scale_factor {
|
||||||
texture_rect.max.y - texture_rect.min.y,
|
|
||||||
);
|
|
||||||
// Update only if size has changed to avoid needless layout calculations
|
|
||||||
if size != image_size.size {
|
|
||||||
image_size.size = size;
|
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 New Issue
Block a user