Fix image measure function to apply inherent aspect ratio to style sizes (#13555)
# Objective - Fixes https://github.com/bevyengine/bevy/issues/13155 - fixes https://github.com/bevyengine/bevy/issues/13517 - Supercedes https://github.com/bevyengine/bevy/pull/13381 - Requires https://github.com/DioxusLabs/taffy/pull/661 ## Solution - Taffy has been updated to: - Apply size styles to absolutely positioned children - Pass the node's `Style` through to the measure function - Bevy's image measure function has been updated to make use of this style information ## Notes - This is currently using a git version of Taffy. If this is tested as fixing the issue then we can turn that into a Taffy 0.5 release (this would be the only change between Taffy 0.4 and Taffy 0.5 so upgrading is not expected to be an issue) - This implementation may not be completely correct. I would have preferred to extend Taffy's gentest infrastructure to handle images and used that to nail down the correct behaviour. But I don't have time for that atm so we'll have to iterate on this in future. This PR at least puts that under Bevy's control. ## Testing - I manually tested the game_menu_example (from https://github.com/bevyengine/bevy/issues/13155) - More testing is probably merited --- ## Changelog No changelog should be required as it fixes a regression on `main` that was not present in bevy 0.13. The changelog for "Taffy upgrade" may want to be changed from 0.4 to 0.5 if this change gets merged. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: François Mockers <francois.mockers@vleue.com>
This commit is contained in:
parent
5cfb063d4a
commit
a3e60d39b7
@ -31,7 +31,7 @@ bevy_window = { path = "../bevy_window", version = "0.14.0-dev" }
|
|||||||
bevy_utils = { path = "../bevy_utils", version = "0.14.0-dev" }
|
bevy_utils = { path = "../bevy_utils", version = "0.14.0-dev" }
|
||||||
|
|
||||||
# other
|
# other
|
||||||
taffy = { version = "0.4" }
|
taffy = { version = "0.5" }
|
||||||
serde = { version = "1", features = ["derive"], optional = true }
|
serde = { version = "1", features = ["derive"], optional = true }
|
||||||
bytemuck = { version = "1.5", features = ["derive"] }
|
bytemuck = { version = "1.5", features = ["derive"] }
|
||||||
thiserror = "1.0.0"
|
thiserror = "1.0.0"
|
||||||
|
@ -213,7 +213,8 @@ without UI components as a child of an entity with UI components, results may be
|
|||||||
|known_dimensions: taffy::Size<Option<f32>>,
|
|known_dimensions: taffy::Size<Option<f32>>,
|
||||||
available_space: taffy::Size<taffy::AvailableSpace>,
|
available_space: taffy::Size<taffy::AvailableSpace>,
|
||||||
_node_id: taffy::NodeId,
|
_node_id: taffy::NodeId,
|
||||||
context: Option<&mut NodeMeasure>|
|
context: Option<&mut NodeMeasure>,
|
||||||
|
style: &taffy::Style|
|
||||||
-> taffy::Size<f32> {
|
-> taffy::Size<f32> {
|
||||||
context
|
context
|
||||||
.map(|ctx| {
|
.map(|ctx| {
|
||||||
@ -222,6 +223,7 @@ without UI components as a child of an entity with UI components, results may be
|
|||||||
known_dimensions.height,
|
known_dimensions.height,
|
||||||
available_space.width,
|
available_space.width,
|
||||||
available_space.height,
|
available_space.height,
|
||||||
|
style,
|
||||||
);
|
);
|
||||||
taffy::Size {
|
taffy::Size {
|
||||||
width: size.x,
|
width: size.x,
|
||||||
|
@ -26,6 +26,7 @@ pub trait Measure: Send + Sync + 'static {
|
|||||||
height: Option<f32>,
|
height: Option<f32>,
|
||||||
available_width: AvailableSpace,
|
available_width: AvailableSpace,
|
||||||
available_height: AvailableSpace,
|
available_height: AvailableSpace,
|
||||||
|
style: &taffy::Style,
|
||||||
) -> Vec2;
|
) -> Vec2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,20 +49,21 @@ impl Measure for NodeMeasure {
|
|||||||
height: Option<f32>,
|
height: Option<f32>,
|
||||||
available_width: AvailableSpace,
|
available_width: AvailableSpace,
|
||||||
available_height: AvailableSpace,
|
available_height: AvailableSpace,
|
||||||
|
style: &taffy::Style,
|
||||||
) -> Vec2 {
|
) -> Vec2 {
|
||||||
match self {
|
match self {
|
||||||
NodeMeasure::Fixed(fixed) => {
|
NodeMeasure::Fixed(fixed) => {
|
||||||
fixed.measure(width, height, available_width, available_height)
|
fixed.measure(width, height, available_width, available_height, style)
|
||||||
}
|
}
|
||||||
#[cfg(feature = "bevy_text")]
|
#[cfg(feature = "bevy_text")]
|
||||||
NodeMeasure::Text(text) => {
|
NodeMeasure::Text(text) => {
|
||||||
text.measure(width, height, available_width, available_height)
|
text.measure(width, height, available_width, available_height, style)
|
||||||
}
|
}
|
||||||
NodeMeasure::Image(image) => {
|
NodeMeasure::Image(image) => {
|
||||||
image.measure(width, height, available_width, available_height)
|
image.measure(width, height, available_width, available_height, style)
|
||||||
}
|
}
|
||||||
NodeMeasure::Custom(custom) => {
|
NodeMeasure::Custom(custom) => {
|
||||||
custom.measure(width, height, available_width, available_height)
|
custom.measure(width, height, available_width, available_height, style)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,6 +83,7 @@ impl Measure for FixedMeasure {
|
|||||||
_: Option<f32>,
|
_: Option<f32>,
|
||||||
_: AvailableSpace,
|
_: AvailableSpace,
|
||||||
_: AvailableSpace,
|
_: AvailableSpace,
|
||||||
|
_: &taffy::Style,
|
||||||
) -> Vec2 {
|
) -> Vec2 {
|
||||||
self.size
|
self.size
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
|||||||
use bevy_render::texture::Image;
|
use bevy_render::texture::Image;
|
||||||
use bevy_sprite::{TextureAtlas, TextureAtlasLayout};
|
use bevy_sprite::{TextureAtlas, TextureAtlasLayout};
|
||||||
use bevy_window::{PrimaryWindow, Window};
|
use bevy_window::{PrimaryWindow, Window};
|
||||||
|
use taffy::{MaybeMath, MaybeResolve};
|
||||||
|
|
||||||
/// The size of the image's texture
|
/// The size of the image's texture
|
||||||
///
|
///
|
||||||
@ -40,26 +41,50 @@ impl Measure for ImageMeasure {
|
|||||||
&self,
|
&self,
|
||||||
width: Option<f32>,
|
width: Option<f32>,
|
||||||
height: Option<f32>,
|
height: Option<f32>,
|
||||||
_: AvailableSpace,
|
available_width: AvailableSpace,
|
||||||
_: AvailableSpace,
|
available_height: AvailableSpace,
|
||||||
|
style: &taffy::Style,
|
||||||
) -> Vec2 {
|
) -> Vec2 {
|
||||||
let mut size = self.size;
|
// Convert available width/height into an option
|
||||||
match (width, height) {
|
let parent_width = available_width.into_option();
|
||||||
(None, None) => {}
|
let parent_height = available_height.into_option();
|
||||||
(Some(width), None) => {
|
|
||||||
size.y = width * size.y / size.x;
|
// Resolve styles
|
||||||
size.x = width;
|
let s_aspect_ratio = style.aspect_ratio;
|
||||||
}
|
let s_width = style.size.width.maybe_resolve(parent_width);
|
||||||
(None, Some(height)) => {
|
let s_min_width = style.min_size.width.maybe_resolve(parent_width);
|
||||||
size.x = height * size.x / size.y;
|
let s_max_width = style.max_size.width.maybe_resolve(parent_width);
|
||||||
size.y = height;
|
let s_height = style.size.height.maybe_resolve(parent_height);
|
||||||
}
|
let s_min_height = style.min_size.height.maybe_resolve(parent_height);
|
||||||
(Some(width), Some(height)) => {
|
let s_max_height = style.max_size.height.maybe_resolve(parent_height);
|
||||||
size.x = width;
|
|
||||||
size.y = height;
|
// Determine width and height from styles and known_sizes (if a size is available
|
||||||
}
|
// from any of these sources)
|
||||||
|
let width = width.or(s_width
|
||||||
|
.or(s_min_width)
|
||||||
|
.maybe_clamp(s_min_width, s_max_width));
|
||||||
|
let height = height.or(s_height
|
||||||
|
.or(s_min_height)
|
||||||
|
.maybe_clamp(s_min_height, s_max_height));
|
||||||
|
|
||||||
|
// Use aspect_ratio from style, fall back to inherent aspect ratio
|
||||||
|
let aspect_ratio = s_aspect_ratio.unwrap_or_else(|| self.size.x / self.size.y);
|
||||||
|
|
||||||
|
// Apply aspect ratio
|
||||||
|
// If only one of width or height was determined at this point, then the other is set beyond this point using the aspect ratio.
|
||||||
|
let taffy_size = taffy::Size { width, height }.maybe_apply_aspect_ratio(Some(aspect_ratio));
|
||||||
|
|
||||||
|
// Use computed sizes or fall back to image's inherent size
|
||||||
|
Vec2 {
|
||||||
|
x: taffy_size
|
||||||
|
.width
|
||||||
|
.unwrap_or(self.size.x)
|
||||||
|
.maybe_clamp(s_min_width, s_max_width),
|
||||||
|
y: taffy_size
|
||||||
|
.height
|
||||||
|
.unwrap_or(self.size.y)
|
||||||
|
.maybe_clamp(s_min_height, s_max_height),
|
||||||
}
|
}
|
||||||
size
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ impl Measure for TextMeasure {
|
|||||||
height: Option<f32>,
|
height: Option<f32>,
|
||||||
available_width: AvailableSpace,
|
available_width: AvailableSpace,
|
||||||
_available_height: AvailableSpace,
|
_available_height: AvailableSpace,
|
||||||
|
_style: &taffy::Style,
|
||||||
) -> Vec2 {
|
) -> Vec2 {
|
||||||
let x = width.unwrap_or_else(|| match available_width {
|
let x = width.unwrap_or_else(|| match available_width {
|
||||||
AvailableSpace::Definite(x) => {
|
AvailableSpace::Definite(x) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user