Skip to content

Commit

Permalink
Fix image measure function to apply inherent aspect ratio to style si…
Browse files Browse the repository at this point in the history
…zes (#13555)

# Objective

- Fixes #13155
- fixes #13517
- Supercedes #13381
- Requires DioxusLabs/taffy#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
#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 <[email protected]>
Co-authored-by: François Mockers <[email protected]>
  • Loading branch information
3 people authored May 30, 2024
1 parent 5cfb063 commit a3e60d3
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 24 deletions.
2 changes: 1 addition & 1 deletion crates/bevy_ui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ bevy_window = { path = "../bevy_window", version = "0.14.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.14.0-dev" }

# other
taffy = { version = "0.4" }
taffy = { version = "0.5" }
serde = { version = "1", features = ["derive"], optional = true }
bytemuck = { version = "1.5", features = ["derive"] }
thiserror = "1.0.0"
Expand Down
4 changes: 3 additions & 1 deletion crates/bevy_ui/src/layout/ui_surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>>,
available_space: taffy::Size<taffy::AvailableSpace>,
_node_id: taffy::NodeId,
context: Option<&mut NodeMeasure>|
context: Option<&mut NodeMeasure>,
style: &taffy::Style|
-> taffy::Size<f32> {
context
.map(|ctx| {
Expand All @@ -222,6 +223,7 @@ without UI components as a child of an entity with UI components, results may be
known_dimensions.height,
available_space.width,
available_space.height,
style,
);
taffy::Size {
width: size.x,
Expand Down
11 changes: 7 additions & 4 deletions crates/bevy_ui/src/measurement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub trait Measure: Send + Sync + 'static {
height: Option<f32>,
available_width: AvailableSpace,
available_height: AvailableSpace,
style: &taffy::Style,
) -> Vec2;
}

Expand All @@ -48,20 +49,21 @@ impl Measure for NodeMeasure {
height: Option<f32>,
available_width: AvailableSpace,
available_height: AvailableSpace,
style: &taffy::Style,
) -> Vec2 {
match self {
NodeMeasure::Fixed(fixed) => {
fixed.measure(width, height, available_width, available_height)
fixed.measure(width, height, available_width, available_height, style)
}
#[cfg(feature = "bevy_text")]
NodeMeasure::Text(text) => {
text.measure(width, height, available_width, available_height)
text.measure(width, height, available_width, available_height, style)
}
NodeMeasure::Image(image) => {
image.measure(width, height, available_width, available_height)
image.measure(width, height, available_width, available_height, style)
}
NodeMeasure::Custom(custom) => {
custom.measure(width, height, available_width, available_height)
custom.measure(width, height, available_width, available_height, style)
}
}
}
Expand All @@ -81,6 +83,7 @@ impl Measure for FixedMeasure {
_: Option<f32>,
_: AvailableSpace,
_: AvailableSpace,
_: &taffy::Style,
) -> Vec2 {
self.size
}
Expand Down
61 changes: 43 additions & 18 deletions crates/bevy_ui/src/widget/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::texture::Image;
use bevy_sprite::{TextureAtlas, TextureAtlasLayout};
use bevy_window::{PrimaryWindow, Window};
use taffy::{MaybeMath, MaybeResolve};

/// The size of the image's texture
///
Expand Down Expand Up @@ -40,26 +41,50 @@ impl Measure for ImageMeasure {
&self,
width: Option<f32>,
height: Option<f32>,
_: AvailableSpace,
_: AvailableSpace,
available_width: AvailableSpace,
available_height: AvailableSpace,
style: &taffy::Style,
) -> Vec2 {
let mut size = self.size;
match (width, height) {
(None, None) => {}
(Some(width), None) => {
size.y = width * size.y / size.x;
size.x = width;
}
(None, Some(height)) => {
size.x = height * size.x / size.y;
size.y = height;
}
(Some(width), Some(height)) => {
size.x = width;
size.y = height;
}
// Convert available width/height into an option
let parent_width = available_width.into_option();
let parent_height = available_height.into_option();

// Resolve styles
let s_aspect_ratio = style.aspect_ratio;
let s_width = style.size.width.maybe_resolve(parent_width);
let s_min_width = style.min_size.width.maybe_resolve(parent_width);
let s_max_width = style.max_size.width.maybe_resolve(parent_width);
let s_height = style.size.height.maybe_resolve(parent_height);
let s_min_height = style.min_size.height.maybe_resolve(parent_height);
let s_max_height = style.max_size.height.maybe_resolve(parent_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
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/bevy_ui/src/widget/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ impl Measure for TextMeasure {
height: Option<f32>,
available_width: AvailableSpace,
_available_height: AvailableSpace,
_style: &taffy::Style,
) -> Vec2 {
let x = width.unwrap_or_else(|| match available_width {
AvailableSpace::Definite(x) => {
Expand Down

0 comments on commit a3e60d3

Please sign in to comment.