bevy/crates/bevy_ui/src/widget/image.rs

141 lines
4.9 KiB
Rust
Raw Normal View History

use crate::{
measurement::AvailableSpace, ContentSize, Measure, Node, NodeMeasure, UiImage, UiScale,
};
Texture Atlas rework (#5103) # Objective > Old MR: #5072 > ~~Associated UI MR: #5070~~ > Adresses #1618 Unify sprite management ## Solution - Remove the `Handle<Image>` field in `TextureAtlas` which is the main cause for all the boilerplate - Remove the redundant `TextureAtlasSprite` component - Renamed `TextureAtlas` asset to `TextureAtlasLayout` ([suggestion](https://github.com/bevyengine/bevy/pull/5103#discussion_r917281844)) - Add a `TextureAtlas` component, containing the atlas layout handle and the section index The difference between this solution and #5072 is that instead of the `enum` approach is that we can more easily manipulate texture sheets without any breaking changes for classic `SpriteBundle`s (@mockersf [comment](https://github.com/bevyengine/bevy/pull/5072#issuecomment-1165836139)) Also, this approach is more *data oriented* extracting the `Handle<Image>` and avoiding complex texture atlas manipulations to retrieve the texture in both applicative and engine code. With this method, the only difference between a `SpriteBundle` and a `SpriteSheetBundle` is an **additional** component storing the atlas handle and the index. ~~This solution can be applied to `bevy_ui` as well (see #5070).~~ EDIT: I also applied this solution to Bevy UI ## Changelog - (**BREAKING**) Removed `TextureAtlasSprite` - (**BREAKING**) Renamed `TextureAtlas` to `TextureAtlasLayout` - (**BREAKING**) `SpriteSheetBundle`: - Uses a `Sprite` instead of a `TextureAtlasSprite` component - Has a `texture` field containing a `Handle<Image>` like the `SpriteBundle` - Has a new `TextureAtlas` component instead of a `Handle<TextureAtlasLayout>` - (**BREAKING**) `DynamicTextureAtlasBuilder::add_texture` takes an additional `&Handle<Image>` parameter - (**BREAKING**) `TextureAtlasLayout::from_grid` no longer takes a `Handle<Image>` parameter - (**BREAKING**) `TextureAtlasBuilder::finish` now returns a `Result<(TextureAtlasLayout, Handle<Image>), _>` - `bevy_text`: - `GlyphAtlasInfo` stores the texture `Handle<Image>` - `FontAtlas` stores the texture `Handle<Image>` - `bevy_ui`: - (**BREAKING**) Removed `UiAtlasImage` , the atlas bundle is now identical to the `ImageBundle` with an additional `TextureAtlas` ## Migration Guide * Sprites ```diff fn my_system( mut images: ResMut<Assets<Image>>, - mut atlases: ResMut<Assets<TextureAtlas>>, + mut atlases: ResMut<Assets<TextureAtlasLayout>>, asset_server: Res<AssetServer> ) { let texture_handle: asset_server.load("my_texture.png"); - let layout = TextureAtlas::from_grid(texture_handle, Vec2::new(25.0, 25.0), 5, 5, None, None); + let layout = TextureAtlasLayout::from_grid(Vec2::new(25.0, 25.0), 5, 5, None, None); let layout_handle = atlases.add(layout); commands.spawn(SpriteSheetBundle { - sprite: TextureAtlasSprite::new(0), - texture_atlas: atlas_handle, + atlas: TextureAtlas { + layout: layout_handle, + index: 0 + }, + texture: texture_handle, ..Default::default() }); } ``` * UI ```diff fn my_system( mut images: ResMut<Assets<Image>>, - mut atlases: ResMut<Assets<TextureAtlas>>, + mut atlases: ResMut<Assets<TextureAtlasLayout>>, asset_server: Res<AssetServer> ) { let texture_handle: asset_server.load("my_texture.png"); - let layout = TextureAtlas::from_grid(texture_handle, Vec2::new(25.0, 25.0), 5, 5, None, None); + let layout = TextureAtlasLayout::from_grid(Vec2::new(25.0, 25.0), 5, 5, None, None); let layout_handle = atlases.add(layout); commands.spawn(AtlasImageBundle { - texture_atlas_image: UiTextureAtlasImage { - index: 0, - flip_x: false, - flip_y: false, - }, - texture_atlas: atlas_handle, + atlas: TextureAtlas { + layout: layout_handle, + index: 0 + }, + image: UiImage { + texture: texture_handle, + flip_x: false, + flip_y: false, + }, ..Default::default() }); } ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: François <mockersf@gmail.com> Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
2024-01-16 13:59:08 +00:00
use bevy_asset::Assets;
use bevy_ecs::prelude::*;
use bevy_math::{UVec2, Vec2};
bevy_reflect: `FromReflect` Ergonomics Implementation (#6056) # Objective **This implementation is based on https://github.com/bevyengine/rfcs/pull/59.** --- Resolves #4597 Full details and motivation can be found in the RFC, but here's a brief summary. `FromReflect` is a very powerful and important trait within the reflection API. It allows Dynamic types (e.g., `DynamicList`, etc.) to be formed into Real ones (e.g., `Vec<i32>`, etc.). This mainly comes into play concerning deserialization, where the reflection deserializers both return a `Box<dyn Reflect>` that almost always contain one of these Dynamic representations of a Real type. To convert this to our Real type, we need to use `FromReflect`. It also sneaks up in other ways. For example, it's a required bound for `T` in `Vec<T>` so that `Vec<T>` as a whole can be made `FromReflect`. It's also required by all fields of an enum as it's used as part of the `Reflect::apply` implementation. So in other words, much like `GetTypeRegistration` and `Typed`, it is very much a core reflection trait. The problem is that it is not currently treated like a core trait and is not automatically derived alongside `Reflect`. This makes using it a bit cumbersome and easy to forget. ## Solution Automatically derive `FromReflect` when deriving `Reflect`. Users can then choose to opt-out if needed using the `#[reflect(from_reflect = false)]` attribute. ```rust #[derive(Reflect)] struct Foo; #[derive(Reflect)] #[reflect(from_reflect = false)] struct Bar; fn test<T: FromReflect>(value: T) {} test(Foo); // <-- OK test(Bar); // <-- Panic! Bar does not implement trait `FromReflect` ``` #### `ReflectFromReflect` This PR also automatically adds the `ReflectFromReflect` (introduced in #6245) registration to the derived `GetTypeRegistration` impl— if the type hasn't opted out of `FromReflect` of course. <details> <summary><h4>Improved Deserialization</h4></summary> > **Warning** > This section includes changes that have since been descoped from this PR. They will likely be implemented again in a followup PR. I am mainly leaving these details in for archival purposes, as well as for reference when implementing this logic again. And since we can do all the above, we might as well improve deserialization. We can now choose to deserialize into a Dynamic type or automatically convert it using `FromReflect` under the hood. `[Un]TypedReflectDeserializer::new` will now perform the conversion and return the `Box`'d Real type. `[Un]TypedReflectDeserializer::new_dynamic` will work like what we have now and simply return the `Box`'d Dynamic type. ```rust // Returns the Real type let reflect_deserializer = UntypedReflectDeserializer::new(&registry); let mut deserializer = ron::de::Deserializer::from_str(input)?; let output: SomeStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; // Returns the Dynamic type let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(&registry); let mut deserializer = ron::de::Deserializer::from_str(input)?; let output: DynamicStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; ``` </details> --- ## Changelog * `FromReflect` is now automatically derived within the `Reflect` derive macro * This includes auto-registering `ReflectFromReflect` in the derived `GetTypeRegistration` impl * ~~Renamed `TypedReflectDeserializer::new` and `UntypedReflectDeserializer::new` to `TypedReflectDeserializer::new_dynamic` and `UntypedReflectDeserializer::new_dynamic`, respectively~~ **Descoped** * ~~Changed `TypedReflectDeserializer::new` and `UntypedReflectDeserializer::new` to automatically convert the deserialized output using `FromReflect`~~ **Descoped** ## Migration Guide * `FromReflect` is now automatically derived within the `Reflect` derive macro. Items with both derives will need to remove the `FromReflect` one. ```rust // OLD #[derive(Reflect, FromReflect)] struct Foo; // NEW #[derive(Reflect)] struct Foo; ``` If using a manual implementation of `FromReflect` and the `Reflect` derive, users will need to opt-out of the automatic implementation. ```rust // OLD #[derive(Reflect)] struct Foo; impl FromReflect for Foo {/* ... */} // NEW #[derive(Reflect)] #[reflect(from_reflect = false)] struct Foo; impl FromReflect for Foo {/* ... */} ``` <details> <summary><h4>Removed Migrations</h4></summary> > **Warning** > This section includes changes that have since been descoped from this PR. They will likely be implemented again in a followup PR. I am mainly leaving these details in for archival purposes, as well as for reference when implementing this logic again. * The reflect deserializers now perform a `FromReflect` conversion internally. The expected output of `TypedReflectDeserializer::new` and `UntypedReflectDeserializer::new` is no longer a Dynamic (e.g., `DynamicList`), but its Real counterpart (e.g., `Vec<i32>`). ```rust let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(&registry); let mut deserializer = ron::de::Deserializer::from_str(input)?; // OLD let output: DynamicStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; // NEW let output: SomeStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; ``` Alternatively, if this behavior isn't desired, use the `TypedReflectDeserializer::new_dynamic` and `UntypedReflectDeserializer::new_dynamic` methods instead: ```rust // OLD let reflect_deserializer = UntypedReflectDeserializer::new(&registry); // NEW let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(&registry); ``` </details> --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2023-06-29 01:31:34 +00:00
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::texture::Image;
Texture Atlas rework (#5103) # Objective > Old MR: #5072 > ~~Associated UI MR: #5070~~ > Adresses #1618 Unify sprite management ## Solution - Remove the `Handle<Image>` field in `TextureAtlas` which is the main cause for all the boilerplate - Remove the redundant `TextureAtlasSprite` component - Renamed `TextureAtlas` asset to `TextureAtlasLayout` ([suggestion](https://github.com/bevyengine/bevy/pull/5103#discussion_r917281844)) - Add a `TextureAtlas` component, containing the atlas layout handle and the section index The difference between this solution and #5072 is that instead of the `enum` approach is that we can more easily manipulate texture sheets without any breaking changes for classic `SpriteBundle`s (@mockersf [comment](https://github.com/bevyengine/bevy/pull/5072#issuecomment-1165836139)) Also, this approach is more *data oriented* extracting the `Handle<Image>` and avoiding complex texture atlas manipulations to retrieve the texture in both applicative and engine code. With this method, the only difference between a `SpriteBundle` and a `SpriteSheetBundle` is an **additional** component storing the atlas handle and the index. ~~This solution can be applied to `bevy_ui` as well (see #5070).~~ EDIT: I also applied this solution to Bevy UI ## Changelog - (**BREAKING**) Removed `TextureAtlasSprite` - (**BREAKING**) Renamed `TextureAtlas` to `TextureAtlasLayout` - (**BREAKING**) `SpriteSheetBundle`: - Uses a `Sprite` instead of a `TextureAtlasSprite` component - Has a `texture` field containing a `Handle<Image>` like the `SpriteBundle` - Has a new `TextureAtlas` component instead of a `Handle<TextureAtlasLayout>` - (**BREAKING**) `DynamicTextureAtlasBuilder::add_texture` takes an additional `&Handle<Image>` parameter - (**BREAKING**) `TextureAtlasLayout::from_grid` no longer takes a `Handle<Image>` parameter - (**BREAKING**) `TextureAtlasBuilder::finish` now returns a `Result<(TextureAtlasLayout, Handle<Image>), _>` - `bevy_text`: - `GlyphAtlasInfo` stores the texture `Handle<Image>` - `FontAtlas` stores the texture `Handle<Image>` - `bevy_ui`: - (**BREAKING**) Removed `UiAtlasImage` , the atlas bundle is now identical to the `ImageBundle` with an additional `TextureAtlas` ## Migration Guide * Sprites ```diff fn my_system( mut images: ResMut<Assets<Image>>, - mut atlases: ResMut<Assets<TextureAtlas>>, + mut atlases: ResMut<Assets<TextureAtlasLayout>>, asset_server: Res<AssetServer> ) { let texture_handle: asset_server.load("my_texture.png"); - let layout = TextureAtlas::from_grid(texture_handle, Vec2::new(25.0, 25.0), 5, 5, None, None); + let layout = TextureAtlasLayout::from_grid(Vec2::new(25.0, 25.0), 5, 5, None, None); let layout_handle = atlases.add(layout); commands.spawn(SpriteSheetBundle { - sprite: TextureAtlasSprite::new(0), - texture_atlas: atlas_handle, + atlas: TextureAtlas { + layout: layout_handle, + index: 0 + }, + texture: texture_handle, ..Default::default() }); } ``` * UI ```diff fn my_system( mut images: ResMut<Assets<Image>>, - mut atlases: ResMut<Assets<TextureAtlas>>, + mut atlases: ResMut<Assets<TextureAtlasLayout>>, asset_server: Res<AssetServer> ) { let texture_handle: asset_server.load("my_texture.png"); - let layout = TextureAtlas::from_grid(texture_handle, Vec2::new(25.0, 25.0), 5, 5, None, None); + let layout = TextureAtlasLayout::from_grid(Vec2::new(25.0, 25.0), 5, 5, None, None); let layout_handle = atlases.add(layout); commands.spawn(AtlasImageBundle { - texture_atlas_image: UiTextureAtlasImage { - index: 0, - flip_x: false, - flip_y: false, - }, - texture_atlas: atlas_handle, + atlas: TextureAtlas { + layout: layout_handle, + index: 0 + }, + image: UiImage { + texture: texture_handle, + flip_x: false, + flip_y: false, + }, ..Default::default() }); } ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: François <mockersf@gmail.com> Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
2024-01-16 13:59:08 +00:00
use bevy_sprite::{TextureAtlas, TextureAtlasLayout};
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 bb59509d44d29: <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
2023-06-23 12:42:17 +00:00
use bevy_window::{PrimaryWindow, Window};
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>
2024-05-30 18:37:39 +00:00
use taffy::{MaybeMath, MaybeResolve};
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 bb59509d44d29: <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
2023-06-23 12:42:17 +00:00
/// The size of the image's texture
`text_system` split (#7779) # Objective `text_system` runs before the UI layout is calculated and the size of the text node is determined, so it cannot correctly shape the text to fit the layout, and has no way of determining if the text needs to be wrapped. The function `text_constraint` attempts to determine the size of the node from the local size constraints in the `Style` component. It can't be made to work, you have to compute the whole layout to get the correct size. A simple example of where this fails completely is a text node set to stretch to fill the empty space adjacent to a node with size constraints set to `Val::Percent(50.)`. The text node will take up half the space, even though its size constraints are `Val::Auto` Also because the `text_system` queries for changes to the `Style` component, when a style value is changed that doesn't affect the node's geometry the text is recomputed unnecessarily. Querying on changes to `Node` is not much better. The UI layout is changed to fit the `CalculatedSize` of the text, so the size of the node is changed and so the text and UI layout get recalculated multiple times from a single change to a `Text`. Also, the `MeasureFunc` doesn't work at all, it doesn't have enough information to fit the text correctly and makes no attempt. Fixes #7663, #6717, #5834, #1490, ## Solution Split the `text_system` into two functions: * `measure_text_system` which calculates the size constraints for the text node and runs before `UiSystem::Flex` * `text_system` which runs after `UiSystem::Flex` and generates the actual text. * Fix the `MeasureFunc` calculations. --- Text wrapping in main: <img width="961" alt="Capturemain" src="https://user-images.githubusercontent.com/27962798/220425740-4fe4bf46-24fb-4685-a1cf-bc01e139e72d.PNG"> With this PR: <img width="961" alt="captured_wrap" src="https://user-images.githubusercontent.com/27962798/220425807-949996b0-f127-4637-9f33-56a6da944fb0.PNG"> ## Changelog * Removed the previous fields from `CalculatedSize`. `CalculatedSize` now contains a boxed `Measure`. * Added `measurement` module to `bevy_ui`. * Added the method `create_text_measure` to `TextPipeline`. * Added a new system `measure_text_system` that runs before `UiSystem::Flex` that creates a `MeasureFunc` for the text. * Rescheduled `text_system` to run after `UiSystem::Flex`. * Added a trait `Measure`. A `Measure` is used to compute the size of a UI node when the size of that node is based on its content. * Added `ImageMeasure` and `TextMeasure` which implement `Measure`. * Added a new component `UiImageSize` which is used by `update_image_calculated_size_system` to track image size changes. * Added a `UiImageSize` component to `ImageBundle`. ## Migration Guide `ImageBundle` has a new component `UiImageSize` which contains the size of the image bundle's texture and is updated automatically by `update_image_calculated_size_system` --------- Co-authored-by: François <mockersf@gmail.com>
2023-04-17 15:23:21 +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 bb59509d44d29: <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
2023-06-23 12:42:17 +00:00
/// This component is updated automatically by [`update_image_content_size_system`]
bevy_reflect: `FromReflect` Ergonomics Implementation (#6056) # Objective **This implementation is based on https://github.com/bevyengine/rfcs/pull/59.** --- Resolves #4597 Full details and motivation can be found in the RFC, but here's a brief summary. `FromReflect` is a very powerful and important trait within the reflection API. It allows Dynamic types (e.g., `DynamicList`, etc.) to be formed into Real ones (e.g., `Vec<i32>`, etc.). This mainly comes into play concerning deserialization, where the reflection deserializers both return a `Box<dyn Reflect>` that almost always contain one of these Dynamic representations of a Real type. To convert this to our Real type, we need to use `FromReflect`. It also sneaks up in other ways. For example, it's a required bound for `T` in `Vec<T>` so that `Vec<T>` as a whole can be made `FromReflect`. It's also required by all fields of an enum as it's used as part of the `Reflect::apply` implementation. So in other words, much like `GetTypeRegistration` and `Typed`, it is very much a core reflection trait. The problem is that it is not currently treated like a core trait and is not automatically derived alongside `Reflect`. This makes using it a bit cumbersome and easy to forget. ## Solution Automatically derive `FromReflect` when deriving `Reflect`. Users can then choose to opt-out if needed using the `#[reflect(from_reflect = false)]` attribute. ```rust #[derive(Reflect)] struct Foo; #[derive(Reflect)] #[reflect(from_reflect = false)] struct Bar; fn test<T: FromReflect>(value: T) {} test(Foo); // <-- OK test(Bar); // <-- Panic! Bar does not implement trait `FromReflect` ``` #### `ReflectFromReflect` This PR also automatically adds the `ReflectFromReflect` (introduced in #6245) registration to the derived `GetTypeRegistration` impl— if the type hasn't opted out of `FromReflect` of course. <details> <summary><h4>Improved Deserialization</h4></summary> > **Warning** > This section includes changes that have since been descoped from this PR. They will likely be implemented again in a followup PR. I am mainly leaving these details in for archival purposes, as well as for reference when implementing this logic again. And since we can do all the above, we might as well improve deserialization. We can now choose to deserialize into a Dynamic type or automatically convert it using `FromReflect` under the hood. `[Un]TypedReflectDeserializer::new` will now perform the conversion and return the `Box`'d Real type. `[Un]TypedReflectDeserializer::new_dynamic` will work like what we have now and simply return the `Box`'d Dynamic type. ```rust // Returns the Real type let reflect_deserializer = UntypedReflectDeserializer::new(&registry); let mut deserializer = ron::de::Deserializer::from_str(input)?; let output: SomeStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; // Returns the Dynamic type let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(&registry); let mut deserializer = ron::de::Deserializer::from_str(input)?; let output: DynamicStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; ``` </details> --- ## Changelog * `FromReflect` is now automatically derived within the `Reflect` derive macro * This includes auto-registering `ReflectFromReflect` in the derived `GetTypeRegistration` impl * ~~Renamed `TypedReflectDeserializer::new` and `UntypedReflectDeserializer::new` to `TypedReflectDeserializer::new_dynamic` and `UntypedReflectDeserializer::new_dynamic`, respectively~~ **Descoped** * ~~Changed `TypedReflectDeserializer::new` and `UntypedReflectDeserializer::new` to automatically convert the deserialized output using `FromReflect`~~ **Descoped** ## Migration Guide * `FromReflect` is now automatically derived within the `Reflect` derive macro. Items with both derives will need to remove the `FromReflect` one. ```rust // OLD #[derive(Reflect, FromReflect)] struct Foo; // NEW #[derive(Reflect)] struct Foo; ``` If using a manual implementation of `FromReflect` and the `Reflect` derive, users will need to opt-out of the automatic implementation. ```rust // OLD #[derive(Reflect)] struct Foo; impl FromReflect for Foo {/* ... */} // NEW #[derive(Reflect)] #[reflect(from_reflect = false)] struct Foo; impl FromReflect for Foo {/* ... */} ``` <details> <summary><h4>Removed Migrations</h4></summary> > **Warning** > This section includes changes that have since been descoped from this PR. They will likely be implemented again in a followup PR. I am mainly leaving these details in for archival purposes, as well as for reference when implementing this logic again. * The reflect deserializers now perform a `FromReflect` conversion internally. The expected output of `TypedReflectDeserializer::new` and `UntypedReflectDeserializer::new` is no longer a Dynamic (e.g., `DynamicList`), but its Real counterpart (e.g., `Vec<i32>`). ```rust let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(&registry); let mut deserializer = ron::de::Deserializer::from_str(input)?; // OLD let output: DynamicStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; // NEW let output: SomeStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; ``` Alternatively, if this behavior isn't desired, use the `TypedReflectDeserializer::new_dynamic` and `UntypedReflectDeserializer::new_dynamic` methods instead: ```rust // OLD let reflect_deserializer = UntypedReflectDeserializer::new(&registry); // NEW let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(&registry); ``` </details> --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2023-06-29 01:31:34 +00:00
#[derive(Component, Debug, Copy, Clone, Default, Reflect)]
#[reflect(Component, Default)]
`text_system` split (#7779) # Objective `text_system` runs before the UI layout is calculated and the size of the text node is determined, so it cannot correctly shape the text to fit the layout, and has no way of determining if the text needs to be wrapped. The function `text_constraint` attempts to determine the size of the node from the local size constraints in the `Style` component. It can't be made to work, you have to compute the whole layout to get the correct size. A simple example of where this fails completely is a text node set to stretch to fill the empty space adjacent to a node with size constraints set to `Val::Percent(50.)`. The text node will take up half the space, even though its size constraints are `Val::Auto` Also because the `text_system` queries for changes to the `Style` component, when a style value is changed that doesn't affect the node's geometry the text is recomputed unnecessarily. Querying on changes to `Node` is not much better. The UI layout is changed to fit the `CalculatedSize` of the text, so the size of the node is changed and so the text and UI layout get recalculated multiple times from a single change to a `Text`. Also, the `MeasureFunc` doesn't work at all, it doesn't have enough information to fit the text correctly and makes no attempt. Fixes #7663, #6717, #5834, #1490, ## Solution Split the `text_system` into two functions: * `measure_text_system` which calculates the size constraints for the text node and runs before `UiSystem::Flex` * `text_system` which runs after `UiSystem::Flex` and generates the actual text. * Fix the `MeasureFunc` calculations. --- Text wrapping in main: <img width="961" alt="Capturemain" src="https://user-images.githubusercontent.com/27962798/220425740-4fe4bf46-24fb-4685-a1cf-bc01e139e72d.PNG"> With this PR: <img width="961" alt="captured_wrap" src="https://user-images.githubusercontent.com/27962798/220425807-949996b0-f127-4637-9f33-56a6da944fb0.PNG"> ## Changelog * Removed the previous fields from `CalculatedSize`. `CalculatedSize` now contains a boxed `Measure`. * Added `measurement` module to `bevy_ui`. * Added the method `create_text_measure` to `TextPipeline`. * Added a new system `measure_text_system` that runs before `UiSystem::Flex` that creates a `MeasureFunc` for the text. * Rescheduled `text_system` to run after `UiSystem::Flex`. * Added a trait `Measure`. A `Measure` is used to compute the size of a UI node when the size of that node is based on its content. * Added `ImageMeasure` and `TextMeasure` which implement `Measure`. * Added a new component `UiImageSize` which is used by `update_image_calculated_size_system` to track image size changes. * Added a `UiImageSize` component to `ImageBundle`. ## Migration Guide `ImageBundle` has a new component `UiImageSize` which contains the size of the image bundle's texture and is updated automatically by `update_image_calculated_size_system` --------- Co-authored-by: François <mockersf@gmail.com>
2023-04-17 15:23:21 +00:00
pub struct UiImageSize {
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 bb59509d44d29: <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
2023-06-23 12:42:17 +00:00
/// The size of the image's texture
///
/// This field is updated automatically by [`update_image_content_size_system`]
size: UVec2,
`text_system` split (#7779) # Objective `text_system` runs before the UI layout is calculated and the size of the text node is determined, so it cannot correctly shape the text to fit the layout, and has no way of determining if the text needs to be wrapped. The function `text_constraint` attempts to determine the size of the node from the local size constraints in the `Style` component. It can't be made to work, you have to compute the whole layout to get the correct size. A simple example of where this fails completely is a text node set to stretch to fill the empty space adjacent to a node with size constraints set to `Val::Percent(50.)`. The text node will take up half the space, even though its size constraints are `Val::Auto` Also because the `text_system` queries for changes to the `Style` component, when a style value is changed that doesn't affect the node's geometry the text is recomputed unnecessarily. Querying on changes to `Node` is not much better. The UI layout is changed to fit the `CalculatedSize` of the text, so the size of the node is changed and so the text and UI layout get recalculated multiple times from a single change to a `Text`. Also, the `MeasureFunc` doesn't work at all, it doesn't have enough information to fit the text correctly and makes no attempt. Fixes #7663, #6717, #5834, #1490, ## Solution Split the `text_system` into two functions: * `measure_text_system` which calculates the size constraints for the text node and runs before `UiSystem::Flex` * `text_system` which runs after `UiSystem::Flex` and generates the actual text. * Fix the `MeasureFunc` calculations. --- Text wrapping in main: <img width="961" alt="Capturemain" src="https://user-images.githubusercontent.com/27962798/220425740-4fe4bf46-24fb-4685-a1cf-bc01e139e72d.PNG"> With this PR: <img width="961" alt="captured_wrap" src="https://user-images.githubusercontent.com/27962798/220425807-949996b0-f127-4637-9f33-56a6da944fb0.PNG"> ## Changelog * Removed the previous fields from `CalculatedSize`. `CalculatedSize` now contains a boxed `Measure`. * Added `measurement` module to `bevy_ui`. * Added the method `create_text_measure` to `TextPipeline`. * Added a new system `measure_text_system` that runs before `UiSystem::Flex` that creates a `MeasureFunc` for the text. * Rescheduled `text_system` to run after `UiSystem::Flex`. * Added a trait `Measure`. A `Measure` is used to compute the size of a UI node when the size of that node is based on its content. * Added `ImageMeasure` and `TextMeasure` which implement `Measure`. * Added a new component `UiImageSize` which is used by `update_image_calculated_size_system` to track image size changes. * Added a `UiImageSize` component to `ImageBundle`. ## Migration Guide `ImageBundle` has a new component `UiImageSize` which contains the size of the image bundle's texture and is updated automatically by `update_image_calculated_size_system` --------- Co-authored-by: François <mockersf@gmail.com>
2023-04-17 15:23:21 +00:00
}
impl UiImageSize {
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 bb59509d44d29: <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
2023-06-23 12:42:17 +00:00
/// The size of the image's texture
pub fn size(&self) -> UVec2 {
`text_system` split (#7779) # Objective `text_system` runs before the UI layout is calculated and the size of the text node is determined, so it cannot correctly shape the text to fit the layout, and has no way of determining if the text needs to be wrapped. The function `text_constraint` attempts to determine the size of the node from the local size constraints in the `Style` component. It can't be made to work, you have to compute the whole layout to get the correct size. A simple example of where this fails completely is a text node set to stretch to fill the empty space adjacent to a node with size constraints set to `Val::Percent(50.)`. The text node will take up half the space, even though its size constraints are `Val::Auto` Also because the `text_system` queries for changes to the `Style` component, when a style value is changed that doesn't affect the node's geometry the text is recomputed unnecessarily. Querying on changes to `Node` is not much better. The UI layout is changed to fit the `CalculatedSize` of the text, so the size of the node is changed and so the text and UI layout get recalculated multiple times from a single change to a `Text`. Also, the `MeasureFunc` doesn't work at all, it doesn't have enough information to fit the text correctly and makes no attempt. Fixes #7663, #6717, #5834, #1490, ## Solution Split the `text_system` into two functions: * `measure_text_system` which calculates the size constraints for the text node and runs before `UiSystem::Flex` * `text_system` which runs after `UiSystem::Flex` and generates the actual text. * Fix the `MeasureFunc` calculations. --- Text wrapping in main: <img width="961" alt="Capturemain" src="https://user-images.githubusercontent.com/27962798/220425740-4fe4bf46-24fb-4685-a1cf-bc01e139e72d.PNG"> With this PR: <img width="961" alt="captured_wrap" src="https://user-images.githubusercontent.com/27962798/220425807-949996b0-f127-4637-9f33-56a6da944fb0.PNG"> ## Changelog * Removed the previous fields from `CalculatedSize`. `CalculatedSize` now contains a boxed `Measure`. * Added `measurement` module to `bevy_ui`. * Added the method `create_text_measure` to `TextPipeline`. * Added a new system `measure_text_system` that runs before `UiSystem::Flex` that creates a `MeasureFunc` for the text. * Rescheduled `text_system` to run after `UiSystem::Flex`. * Added a trait `Measure`. A `Measure` is used to compute the size of a UI node when the size of that node is based on its content. * Added `ImageMeasure` and `TextMeasure` which implement `Measure`. * Added a new component `UiImageSize` which is used by `update_image_calculated_size_system` to track image size changes. * Added a `UiImageSize` component to `ImageBundle`. ## Migration Guide `ImageBundle` has a new component `UiImageSize` which contains the size of the image bundle's texture and is updated automatically by `update_image_calculated_size_system` --------- Co-authored-by: François <mockersf@gmail.com>
2023-04-17 15:23:21 +00:00
self.size
}
}
#[derive(Clone)]
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 bb59509d44d29: <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
2023-06-23 12:42:17 +00:00
/// Used to calculate the size of UI image nodes
`text_system` split (#7779) # Objective `text_system` runs before the UI layout is calculated and the size of the text node is determined, so it cannot correctly shape the text to fit the layout, and has no way of determining if the text needs to be wrapped. The function `text_constraint` attempts to determine the size of the node from the local size constraints in the `Style` component. It can't be made to work, you have to compute the whole layout to get the correct size. A simple example of where this fails completely is a text node set to stretch to fill the empty space adjacent to a node with size constraints set to `Val::Percent(50.)`. The text node will take up half the space, even though its size constraints are `Val::Auto` Also because the `text_system` queries for changes to the `Style` component, when a style value is changed that doesn't affect the node's geometry the text is recomputed unnecessarily. Querying on changes to `Node` is not much better. The UI layout is changed to fit the `CalculatedSize` of the text, so the size of the node is changed and so the text and UI layout get recalculated multiple times from a single change to a `Text`. Also, the `MeasureFunc` doesn't work at all, it doesn't have enough information to fit the text correctly and makes no attempt. Fixes #7663, #6717, #5834, #1490, ## Solution Split the `text_system` into two functions: * `measure_text_system` which calculates the size constraints for the text node and runs before `UiSystem::Flex` * `text_system` which runs after `UiSystem::Flex` and generates the actual text. * Fix the `MeasureFunc` calculations. --- Text wrapping in main: <img width="961" alt="Capturemain" src="https://user-images.githubusercontent.com/27962798/220425740-4fe4bf46-24fb-4685-a1cf-bc01e139e72d.PNG"> With this PR: <img width="961" alt="captured_wrap" src="https://user-images.githubusercontent.com/27962798/220425807-949996b0-f127-4637-9f33-56a6da944fb0.PNG"> ## Changelog * Removed the previous fields from `CalculatedSize`. `CalculatedSize` now contains a boxed `Measure`. * Added `measurement` module to `bevy_ui`. * Added the method `create_text_measure` to `TextPipeline`. * Added a new system `measure_text_system` that runs before `UiSystem::Flex` that creates a `MeasureFunc` for the text. * Rescheduled `text_system` to run after `UiSystem::Flex`. * Added a trait `Measure`. A `Measure` is used to compute the size of a UI node when the size of that node is based on its content. * Added `ImageMeasure` and `TextMeasure` which implement `Measure`. * Added a new component `UiImageSize` which is used by `update_image_calculated_size_system` to track image size changes. * Added a `UiImageSize` component to `ImageBundle`. ## Migration Guide `ImageBundle` has a new component `UiImageSize` which contains the size of the image bundle's texture and is updated automatically by `update_image_calculated_size_system` --------- Co-authored-by: François <mockersf@gmail.com>
2023-04-17 15:23:21 +00:00
pub struct ImageMeasure {
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 bb59509d44d29: <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
2023-06-23 12:42:17 +00:00
/// The size of the image's texture
pub size: Vec2,
`text_system` split (#7779) # Objective `text_system` runs before the UI layout is calculated and the size of the text node is determined, so it cannot correctly shape the text to fit the layout, and has no way of determining if the text needs to be wrapped. The function `text_constraint` attempts to determine the size of the node from the local size constraints in the `Style` component. It can't be made to work, you have to compute the whole layout to get the correct size. A simple example of where this fails completely is a text node set to stretch to fill the empty space adjacent to a node with size constraints set to `Val::Percent(50.)`. The text node will take up half the space, even though its size constraints are `Val::Auto` Also because the `text_system` queries for changes to the `Style` component, when a style value is changed that doesn't affect the node's geometry the text is recomputed unnecessarily. Querying on changes to `Node` is not much better. The UI layout is changed to fit the `CalculatedSize` of the text, so the size of the node is changed and so the text and UI layout get recalculated multiple times from a single change to a `Text`. Also, the `MeasureFunc` doesn't work at all, it doesn't have enough information to fit the text correctly and makes no attempt. Fixes #7663, #6717, #5834, #1490, ## Solution Split the `text_system` into two functions: * `measure_text_system` which calculates the size constraints for the text node and runs before `UiSystem::Flex` * `text_system` which runs after `UiSystem::Flex` and generates the actual text. * Fix the `MeasureFunc` calculations. --- Text wrapping in main: <img width="961" alt="Capturemain" src="https://user-images.githubusercontent.com/27962798/220425740-4fe4bf46-24fb-4685-a1cf-bc01e139e72d.PNG"> With this PR: <img width="961" alt="captured_wrap" src="https://user-images.githubusercontent.com/27962798/220425807-949996b0-f127-4637-9f33-56a6da944fb0.PNG"> ## Changelog * Removed the previous fields from `CalculatedSize`. `CalculatedSize` now contains a boxed `Measure`. * Added `measurement` module to `bevy_ui`. * Added the method `create_text_measure` to `TextPipeline`. * Added a new system `measure_text_system` that runs before `UiSystem::Flex` that creates a `MeasureFunc` for the text. * Rescheduled `text_system` to run after `UiSystem::Flex`. * Added a trait `Measure`. A `Measure` is used to compute the size of a UI node when the size of that node is based on its content. * Added `ImageMeasure` and `TextMeasure` which implement `Measure`. * Added a new component `UiImageSize` which is used by `update_image_calculated_size_system` to track image size changes. * Added a `UiImageSize` component to `ImageBundle`. ## Migration Guide `ImageBundle` has a new component `UiImageSize` which contains the size of the image bundle's texture and is updated automatically by `update_image_calculated_size_system` --------- Co-authored-by: François <mockersf@gmail.com>
2023-04-17 15:23:21 +00:00
}
impl Measure for ImageMeasure {
fn measure(
&self,
width: Option<f32>,
height: Option<f32>,
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>
2024-05-30 18:37:39 +00:00
available_width: AvailableSpace,
available_height: AvailableSpace,
style: &taffy::Style,
`text_system` split (#7779) # Objective `text_system` runs before the UI layout is calculated and the size of the text node is determined, so it cannot correctly shape the text to fit the layout, and has no way of determining if the text needs to be wrapped. The function `text_constraint` attempts to determine the size of the node from the local size constraints in the `Style` component. It can't be made to work, you have to compute the whole layout to get the correct size. A simple example of where this fails completely is a text node set to stretch to fill the empty space adjacent to a node with size constraints set to `Val::Percent(50.)`. The text node will take up half the space, even though its size constraints are `Val::Auto` Also because the `text_system` queries for changes to the `Style` component, when a style value is changed that doesn't affect the node's geometry the text is recomputed unnecessarily. Querying on changes to `Node` is not much better. The UI layout is changed to fit the `CalculatedSize` of the text, so the size of the node is changed and so the text and UI layout get recalculated multiple times from a single change to a `Text`. Also, the `MeasureFunc` doesn't work at all, it doesn't have enough information to fit the text correctly and makes no attempt. Fixes #7663, #6717, #5834, #1490, ## Solution Split the `text_system` into two functions: * `measure_text_system` which calculates the size constraints for the text node and runs before `UiSystem::Flex` * `text_system` which runs after `UiSystem::Flex` and generates the actual text. * Fix the `MeasureFunc` calculations. --- Text wrapping in main: <img width="961" alt="Capturemain" src="https://user-images.githubusercontent.com/27962798/220425740-4fe4bf46-24fb-4685-a1cf-bc01e139e72d.PNG"> With this PR: <img width="961" alt="captured_wrap" src="https://user-images.githubusercontent.com/27962798/220425807-949996b0-f127-4637-9f33-56a6da944fb0.PNG"> ## Changelog * Removed the previous fields from `CalculatedSize`. `CalculatedSize` now contains a boxed `Measure`. * Added `measurement` module to `bevy_ui`. * Added the method `create_text_measure` to `TextPipeline`. * Added a new system `measure_text_system` that runs before `UiSystem::Flex` that creates a `MeasureFunc` for the text. * Rescheduled `text_system` to run after `UiSystem::Flex`. * Added a trait `Measure`. A `Measure` is used to compute the size of a UI node when the size of that node is based on its content. * Added `ImageMeasure` and `TextMeasure` which implement `Measure`. * Added a new component `UiImageSize` which is used by `update_image_calculated_size_system` to track image size changes. * Added a `UiImageSize` component to `ImageBundle`. ## Migration Guide `ImageBundle` has a new component `UiImageSize` which contains the size of the image bundle's texture and is updated automatically by `update_image_calculated_size_system` --------- Co-authored-by: François <mockersf@gmail.com>
2023-04-17 15:23:21 +00:00
) -> Vec2 {
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>
2024-05-30 18:37:39 +00:00
// 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),
`text_system` split (#7779) # Objective `text_system` runs before the UI layout is calculated and the size of the text node is determined, so it cannot correctly shape the text to fit the layout, and has no way of determining if the text needs to be wrapped. The function `text_constraint` attempts to determine the size of the node from the local size constraints in the `Style` component. It can't be made to work, you have to compute the whole layout to get the correct size. A simple example of where this fails completely is a text node set to stretch to fill the empty space adjacent to a node with size constraints set to `Val::Percent(50.)`. The text node will take up half the space, even though its size constraints are `Val::Auto` Also because the `text_system` queries for changes to the `Style` component, when a style value is changed that doesn't affect the node's geometry the text is recomputed unnecessarily. Querying on changes to `Node` is not much better. The UI layout is changed to fit the `CalculatedSize` of the text, so the size of the node is changed and so the text and UI layout get recalculated multiple times from a single change to a `Text`. Also, the `MeasureFunc` doesn't work at all, it doesn't have enough information to fit the text correctly and makes no attempt. Fixes #7663, #6717, #5834, #1490, ## Solution Split the `text_system` into two functions: * `measure_text_system` which calculates the size constraints for the text node and runs before `UiSystem::Flex` * `text_system` which runs after `UiSystem::Flex` and generates the actual text. * Fix the `MeasureFunc` calculations. --- Text wrapping in main: <img width="961" alt="Capturemain" src="https://user-images.githubusercontent.com/27962798/220425740-4fe4bf46-24fb-4685-a1cf-bc01e139e72d.PNG"> With this PR: <img width="961" alt="captured_wrap" src="https://user-images.githubusercontent.com/27962798/220425807-949996b0-f127-4637-9f33-56a6da944fb0.PNG"> ## Changelog * Removed the previous fields from `CalculatedSize`. `CalculatedSize` now contains a boxed `Measure`. * Added `measurement` module to `bevy_ui`. * Added the method `create_text_measure` to `TextPipeline`. * Added a new system `measure_text_system` that runs before `UiSystem::Flex` that creates a `MeasureFunc` for the text. * Rescheduled `text_system` to run after `UiSystem::Flex`. * Added a trait `Measure`. A `Measure` is used to compute the size of a UI node when the size of that node is based on its content. * Added `ImageMeasure` and `TextMeasure` which implement `Measure`. * Added a new component `UiImageSize` which is used by `update_image_calculated_size_system` to track image size changes. * Added a `UiImageSize` component to `ImageBundle`. ## Migration Guide `ImageBundle` has a new component `UiImageSize` which contains the size of the image bundle's texture and is updated automatically by `update_image_calculated_size_system` --------- Co-authored-by: François <mockersf@gmail.com>
2023-04-17 15:23:21 +00:00
}
}
}
#[cfg(feature = "bevy_text")]
type UpdateImageFilter = (With<Node>, Without<bevy_text::Text>);
#[cfg(not(feature = "bevy_text"))]
type UpdateImageFilter = With<Node>;
`MeasureFunc` improvements (#8402) # Objective fixes #8516 * Give `CalculatedSize` a more specific and intuitive name. * `MeasureFunc`s should only be updated when their `CalculatedSize` is modified by the systems managing their content. For example, suppose that you have a UI displaying an image using an `ImageNode`. When the window is resized, the node's `MeasureFunc` will be updated even though the dimensions of the texture contained by the node are unchanged. * Fix the `CalculatedSize` API so that it no longer requires the extra boxing and the `dyn_clone` method. ## Solution * Rename `CalculatedSize` to `ContentSize` * Only update `MeasureFunc`s on `CalculatedSize` changes. * Remove the `dyn_clone` method from `Measure` and move the `Measure` from the `ContentSize` component rather than cloning it. * Change the measure_func field of `ContentSize` to type `Option<taffy::node::MeasureFunc>`. Add a `set` method that wraps the given measure appropriately. --- ## Changelog * Renamed `CalculatedSize` to `ContentSize`. * Replaced `upsert_leaf` with a function `update_measure` that only updates the node's `MeasureFunc`. * `MeasureFunc`s are only updated when the `ContentSize` changes and not when the layout changes. * Scale factor is no longer applied to the size values passed to the `MeasureFunc`. * Remove the `ContentSize` scaling in `text_system`. * The `dyn_clone` method has been removed from the `Measure` trait. * `Measure`s are moved from the `ContentSize` component instead of cloning them. * Added `set` method to `ContentSize` that replaces the `new` function. ## Migration Guide * `CalculatedSize` has been renamed to `ContentSize`. * The `upsert_leaf` function has been removed from `UiSurface` and replaced with `update_measure` which updates the `MeasureFunc` without node insertion. * The `dyn_clone` method has been removed from the `Measure` trait. * The new function of `CalculatedSize` has been replaced with the method `set`.
2023-05-01 15:40:53 +00:00
/// Updates content size of the node based on the image provided
pub fn update_image_content_size_system(
mut previous_combined_scale_factor: Local<f32>,
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 bb59509d44d29: <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
2023-06-23 12:42:17 +00:00
windows: Query<&Window, With<PrimaryWindow>>,
ui_scale: Res<UiScale>,
textures: Res<Assets<Image>>,
Texture Atlas rework (#5103) # Objective > Old MR: #5072 > ~~Associated UI MR: #5070~~ > Adresses #1618 Unify sprite management ## Solution - Remove the `Handle<Image>` field in `TextureAtlas` which is the main cause for all the boilerplate - Remove the redundant `TextureAtlasSprite` component - Renamed `TextureAtlas` asset to `TextureAtlasLayout` ([suggestion](https://github.com/bevyengine/bevy/pull/5103#discussion_r917281844)) - Add a `TextureAtlas` component, containing the atlas layout handle and the section index The difference between this solution and #5072 is that instead of the `enum` approach is that we can more easily manipulate texture sheets without any breaking changes for classic `SpriteBundle`s (@mockersf [comment](https://github.com/bevyengine/bevy/pull/5072#issuecomment-1165836139)) Also, this approach is more *data oriented* extracting the `Handle<Image>` and avoiding complex texture atlas manipulations to retrieve the texture in both applicative and engine code. With this method, the only difference between a `SpriteBundle` and a `SpriteSheetBundle` is an **additional** component storing the atlas handle and the index. ~~This solution can be applied to `bevy_ui` as well (see #5070).~~ EDIT: I also applied this solution to Bevy UI ## Changelog - (**BREAKING**) Removed `TextureAtlasSprite` - (**BREAKING**) Renamed `TextureAtlas` to `TextureAtlasLayout` - (**BREAKING**) `SpriteSheetBundle`: - Uses a `Sprite` instead of a `TextureAtlasSprite` component - Has a `texture` field containing a `Handle<Image>` like the `SpriteBundle` - Has a new `TextureAtlas` component instead of a `Handle<TextureAtlasLayout>` - (**BREAKING**) `DynamicTextureAtlasBuilder::add_texture` takes an additional `&Handle<Image>` parameter - (**BREAKING**) `TextureAtlasLayout::from_grid` no longer takes a `Handle<Image>` parameter - (**BREAKING**) `TextureAtlasBuilder::finish` now returns a `Result<(TextureAtlasLayout, Handle<Image>), _>` - `bevy_text`: - `GlyphAtlasInfo` stores the texture `Handle<Image>` - `FontAtlas` stores the texture `Handle<Image>` - `bevy_ui`: - (**BREAKING**) Removed `UiAtlasImage` , the atlas bundle is now identical to the `ImageBundle` with an additional `TextureAtlas` ## Migration Guide * Sprites ```diff fn my_system( mut images: ResMut<Assets<Image>>, - mut atlases: ResMut<Assets<TextureAtlas>>, + mut atlases: ResMut<Assets<TextureAtlasLayout>>, asset_server: Res<AssetServer> ) { let texture_handle: asset_server.load("my_texture.png"); - let layout = TextureAtlas::from_grid(texture_handle, Vec2::new(25.0, 25.0), 5, 5, None, None); + let layout = TextureAtlasLayout::from_grid(Vec2::new(25.0, 25.0), 5, 5, None, None); let layout_handle = atlases.add(layout); commands.spawn(SpriteSheetBundle { - sprite: TextureAtlasSprite::new(0), - texture_atlas: atlas_handle, + atlas: TextureAtlas { + layout: layout_handle, + index: 0 + }, + texture: texture_handle, ..Default::default() }); } ``` * UI ```diff fn my_system( mut images: ResMut<Assets<Image>>, - mut atlases: ResMut<Assets<TextureAtlas>>, + mut atlases: ResMut<Assets<TextureAtlasLayout>>, asset_server: Res<AssetServer> ) { let texture_handle: asset_server.load("my_texture.png"); - let layout = TextureAtlas::from_grid(texture_handle, Vec2::new(25.0, 25.0), 5, 5, None, None); + let layout = TextureAtlasLayout::from_grid(Vec2::new(25.0, 25.0), 5, 5, None, None); let layout_handle = atlases.add(layout); commands.spawn(AtlasImageBundle { - texture_atlas_image: UiTextureAtlasImage { - index: 0, - flip_x: false, - flip_y: false, - }, - texture_atlas: atlas_handle, + atlas: TextureAtlas { + layout: layout_handle, + index: 0 + }, + image: UiImage { + texture: texture_handle, + flip_x: false, + flip_y: false, + }, ..Default::default() }); } ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: François <mockersf@gmail.com> Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
2024-01-16 13:59:08 +00:00
atlases: Res<Assets<TextureAtlasLayout>>,
mut query: Query<
(
&mut ContentSize,
Texture Atlas rework (#5103) # Objective > Old MR: #5072 > ~~Associated UI MR: #5070~~ > Adresses #1618 Unify sprite management ## Solution - Remove the `Handle<Image>` field in `TextureAtlas` which is the main cause for all the boilerplate - Remove the redundant `TextureAtlasSprite` component - Renamed `TextureAtlas` asset to `TextureAtlasLayout` ([suggestion](https://github.com/bevyengine/bevy/pull/5103#discussion_r917281844)) - Add a `TextureAtlas` component, containing the atlas layout handle and the section index The difference between this solution and #5072 is that instead of the `enum` approach is that we can more easily manipulate texture sheets without any breaking changes for classic `SpriteBundle`s (@mockersf [comment](https://github.com/bevyengine/bevy/pull/5072#issuecomment-1165836139)) Also, this approach is more *data oriented* extracting the `Handle<Image>` and avoiding complex texture atlas manipulations to retrieve the texture in both applicative and engine code. With this method, the only difference between a `SpriteBundle` and a `SpriteSheetBundle` is an **additional** component storing the atlas handle and the index. ~~This solution can be applied to `bevy_ui` as well (see #5070).~~ EDIT: I also applied this solution to Bevy UI ## Changelog - (**BREAKING**) Removed `TextureAtlasSprite` - (**BREAKING**) Renamed `TextureAtlas` to `TextureAtlasLayout` - (**BREAKING**) `SpriteSheetBundle`: - Uses a `Sprite` instead of a `TextureAtlasSprite` component - Has a `texture` field containing a `Handle<Image>` like the `SpriteBundle` - Has a new `TextureAtlas` component instead of a `Handle<TextureAtlasLayout>` - (**BREAKING**) `DynamicTextureAtlasBuilder::add_texture` takes an additional `&Handle<Image>` parameter - (**BREAKING**) `TextureAtlasLayout::from_grid` no longer takes a `Handle<Image>` parameter - (**BREAKING**) `TextureAtlasBuilder::finish` now returns a `Result<(TextureAtlasLayout, Handle<Image>), _>` - `bevy_text`: - `GlyphAtlasInfo` stores the texture `Handle<Image>` - `FontAtlas` stores the texture `Handle<Image>` - `bevy_ui`: - (**BREAKING**) Removed `UiAtlasImage` , the atlas bundle is now identical to the `ImageBundle` with an additional `TextureAtlas` ## Migration Guide * Sprites ```diff fn my_system( mut images: ResMut<Assets<Image>>, - mut atlases: ResMut<Assets<TextureAtlas>>, + mut atlases: ResMut<Assets<TextureAtlasLayout>>, asset_server: Res<AssetServer> ) { let texture_handle: asset_server.load("my_texture.png"); - let layout = TextureAtlas::from_grid(texture_handle, Vec2::new(25.0, 25.0), 5, 5, None, None); + let layout = TextureAtlasLayout::from_grid(Vec2::new(25.0, 25.0), 5, 5, None, None); let layout_handle = atlases.add(layout); commands.spawn(SpriteSheetBundle { - sprite: TextureAtlasSprite::new(0), - texture_atlas: atlas_handle, + atlas: TextureAtlas { + layout: layout_handle, + index: 0 + }, + texture: texture_handle, ..Default::default() }); } ``` * UI ```diff fn my_system( mut images: ResMut<Assets<Image>>, - mut atlases: ResMut<Assets<TextureAtlas>>, + mut atlases: ResMut<Assets<TextureAtlasLayout>>, asset_server: Res<AssetServer> ) { let texture_handle: asset_server.load("my_texture.png"); - let layout = TextureAtlas::from_grid(texture_handle, Vec2::new(25.0, 25.0), 5, 5, None, None); + let layout = TextureAtlasLayout::from_grid(Vec2::new(25.0, 25.0), 5, 5, None, None); let layout_handle = atlases.add(layout); commands.spawn(AtlasImageBundle { - texture_atlas_image: UiTextureAtlasImage { - index: 0, - flip_x: false, - flip_y: false, - }, - texture_atlas: atlas_handle, + atlas: TextureAtlas { + layout: layout_handle, + index: 0 + }, + image: UiImage { + texture: texture_handle, + flip_x: false, + flip_y: false, + }, ..Default::default() }); } ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: François <mockersf@gmail.com> Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
2024-01-16 13:59:08 +00:00
&UiImage,
&mut UiImageSize,
Texture Atlas rework (#5103) # Objective > Old MR: #5072 > ~~Associated UI MR: #5070~~ > Adresses #1618 Unify sprite management ## Solution - Remove the `Handle<Image>` field in `TextureAtlas` which is the main cause for all the boilerplate - Remove the redundant `TextureAtlasSprite` component - Renamed `TextureAtlas` asset to `TextureAtlasLayout` ([suggestion](https://github.com/bevyengine/bevy/pull/5103#discussion_r917281844)) - Add a `TextureAtlas` component, containing the atlas layout handle and the section index The difference between this solution and #5072 is that instead of the `enum` approach is that we can more easily manipulate texture sheets without any breaking changes for classic `SpriteBundle`s (@mockersf [comment](https://github.com/bevyengine/bevy/pull/5072#issuecomment-1165836139)) Also, this approach is more *data oriented* extracting the `Handle<Image>` and avoiding complex texture atlas manipulations to retrieve the texture in both applicative and engine code. With this method, the only difference between a `SpriteBundle` and a `SpriteSheetBundle` is an **additional** component storing the atlas handle and the index. ~~This solution can be applied to `bevy_ui` as well (see #5070).~~ EDIT: I also applied this solution to Bevy UI ## Changelog - (**BREAKING**) Removed `TextureAtlasSprite` - (**BREAKING**) Renamed `TextureAtlas` to `TextureAtlasLayout` - (**BREAKING**) `SpriteSheetBundle`: - Uses a `Sprite` instead of a `TextureAtlasSprite` component - Has a `texture` field containing a `Handle<Image>` like the `SpriteBundle` - Has a new `TextureAtlas` component instead of a `Handle<TextureAtlasLayout>` - (**BREAKING**) `DynamicTextureAtlasBuilder::add_texture` takes an additional `&Handle<Image>` parameter - (**BREAKING**) `TextureAtlasLayout::from_grid` no longer takes a `Handle<Image>` parameter - (**BREAKING**) `TextureAtlasBuilder::finish` now returns a `Result<(TextureAtlasLayout, Handle<Image>), _>` - `bevy_text`: - `GlyphAtlasInfo` stores the texture `Handle<Image>` - `FontAtlas` stores the texture `Handle<Image>` - `bevy_ui`: - (**BREAKING**) Removed `UiAtlasImage` , the atlas bundle is now identical to the `ImageBundle` with an additional `TextureAtlas` ## Migration Guide * Sprites ```diff fn my_system( mut images: ResMut<Assets<Image>>, - mut atlases: ResMut<Assets<TextureAtlas>>, + mut atlases: ResMut<Assets<TextureAtlasLayout>>, asset_server: Res<AssetServer> ) { let texture_handle: asset_server.load("my_texture.png"); - let layout = TextureAtlas::from_grid(texture_handle, Vec2::new(25.0, 25.0), 5, 5, None, None); + let layout = TextureAtlasLayout::from_grid(Vec2::new(25.0, 25.0), 5, 5, None, None); let layout_handle = atlases.add(layout); commands.spawn(SpriteSheetBundle { - sprite: TextureAtlasSprite::new(0), - texture_atlas: atlas_handle, + atlas: TextureAtlas { + layout: layout_handle, + index: 0 + }, + texture: texture_handle, ..Default::default() }); } ``` * UI ```diff fn my_system( mut images: ResMut<Assets<Image>>, - mut atlases: ResMut<Assets<TextureAtlas>>, + mut atlases: ResMut<Assets<TextureAtlasLayout>>, asset_server: Res<AssetServer> ) { let texture_handle: asset_server.load("my_texture.png"); - let layout = TextureAtlas::from_grid(texture_handle, Vec2::new(25.0, 25.0), 5, 5, None, None); + let layout = TextureAtlasLayout::from_grid(Vec2::new(25.0, 25.0), 5, 5, None, None); let layout_handle = atlases.add(layout); commands.spawn(AtlasImageBundle { - texture_atlas_image: UiTextureAtlasImage { - index: 0, - flip_x: false, - flip_y: false, - }, - texture_atlas: atlas_handle, + atlas: TextureAtlas { + layout: layout_handle, + index: 0 + }, + image: UiImage { + texture: texture_handle, + flip_x: false, + flip_y: false, + }, ..Default::default() }); } ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: François <mockersf@gmail.com> Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
2024-01-16 13:59:08 +00:00
Option<&TextureAtlas>,
),
Texture Atlas rework (#5103) # Objective > Old MR: #5072 > ~~Associated UI MR: #5070~~ > Adresses #1618 Unify sprite management ## Solution - Remove the `Handle<Image>` field in `TextureAtlas` which is the main cause for all the boilerplate - Remove the redundant `TextureAtlasSprite` component - Renamed `TextureAtlas` asset to `TextureAtlasLayout` ([suggestion](https://github.com/bevyengine/bevy/pull/5103#discussion_r917281844)) - Add a `TextureAtlas` component, containing the atlas layout handle and the section index The difference between this solution and #5072 is that instead of the `enum` approach is that we can more easily manipulate texture sheets without any breaking changes for classic `SpriteBundle`s (@mockersf [comment](https://github.com/bevyengine/bevy/pull/5072#issuecomment-1165836139)) Also, this approach is more *data oriented* extracting the `Handle<Image>` and avoiding complex texture atlas manipulations to retrieve the texture in both applicative and engine code. With this method, the only difference between a `SpriteBundle` and a `SpriteSheetBundle` is an **additional** component storing the atlas handle and the index. ~~This solution can be applied to `bevy_ui` as well (see #5070).~~ EDIT: I also applied this solution to Bevy UI ## Changelog - (**BREAKING**) Removed `TextureAtlasSprite` - (**BREAKING**) Renamed `TextureAtlas` to `TextureAtlasLayout` - (**BREAKING**) `SpriteSheetBundle`: - Uses a `Sprite` instead of a `TextureAtlasSprite` component - Has a `texture` field containing a `Handle<Image>` like the `SpriteBundle` - Has a new `TextureAtlas` component instead of a `Handle<TextureAtlasLayout>` - (**BREAKING**) `DynamicTextureAtlasBuilder::add_texture` takes an additional `&Handle<Image>` parameter - (**BREAKING**) `TextureAtlasLayout::from_grid` no longer takes a `Handle<Image>` parameter - (**BREAKING**) `TextureAtlasBuilder::finish` now returns a `Result<(TextureAtlasLayout, Handle<Image>), _>` - `bevy_text`: - `GlyphAtlasInfo` stores the texture `Handle<Image>` - `FontAtlas` stores the texture `Handle<Image>` - `bevy_ui`: - (**BREAKING**) Removed `UiAtlasImage` , the atlas bundle is now identical to the `ImageBundle` with an additional `TextureAtlas` ## Migration Guide * Sprites ```diff fn my_system( mut images: ResMut<Assets<Image>>, - mut atlases: ResMut<Assets<TextureAtlas>>, + mut atlases: ResMut<Assets<TextureAtlasLayout>>, asset_server: Res<AssetServer> ) { let texture_handle: asset_server.load("my_texture.png"); - let layout = TextureAtlas::from_grid(texture_handle, Vec2::new(25.0, 25.0), 5, 5, None, None); + let layout = TextureAtlasLayout::from_grid(Vec2::new(25.0, 25.0), 5, 5, None, None); let layout_handle = atlases.add(layout); commands.spawn(SpriteSheetBundle { - sprite: TextureAtlasSprite::new(0), - texture_atlas: atlas_handle, + atlas: TextureAtlas { + layout: layout_handle, + index: 0 + }, + texture: texture_handle, ..Default::default() }); } ``` * UI ```diff fn my_system( mut images: ResMut<Assets<Image>>, - mut atlases: ResMut<Assets<TextureAtlas>>, + mut atlases: ResMut<Assets<TextureAtlasLayout>>, asset_server: Res<AssetServer> ) { let texture_handle: asset_server.load("my_texture.png"); - let layout = TextureAtlas::from_grid(texture_handle, Vec2::new(25.0, 25.0), 5, 5, None, None); + let layout = TextureAtlasLayout::from_grid(Vec2::new(25.0, 25.0), 5, 5, None, None); let layout_handle = atlases.add(layout); commands.spawn(AtlasImageBundle { - texture_atlas_image: UiTextureAtlasImage { - index: 0, - flip_x: false, - flip_y: false, - }, - texture_atlas: atlas_handle, + atlas: TextureAtlas { + layout: layout_handle, + index: 0 + }, + image: UiImage { + texture: texture_handle, + flip_x: false, + flip_y: false, + }, ..Default::default() }); } ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: François <mockersf@gmail.com> Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
2024-01-16 13:59:08 +00:00
UpdateImageFilter,
>,
) {
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 bb59509d44d29: <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
2023-06-23 12:42:17 +00:00
let combined_scale_factor = windows
.get_single()
.map(|window| window.resolution.scale_factor())
.unwrap_or(1.)
* ui_scale.0;
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 bb59509d44d29: <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
2023-06-23 12:42:17 +00:00
Texture Atlas rework (#5103) # Objective > Old MR: #5072 > ~~Associated UI MR: #5070~~ > Adresses #1618 Unify sprite management ## Solution - Remove the `Handle<Image>` field in `TextureAtlas` which is the main cause for all the boilerplate - Remove the redundant `TextureAtlasSprite` component - Renamed `TextureAtlas` asset to `TextureAtlasLayout` ([suggestion](https://github.com/bevyengine/bevy/pull/5103#discussion_r917281844)) - Add a `TextureAtlas` component, containing the atlas layout handle and the section index The difference between this solution and #5072 is that instead of the `enum` approach is that we can more easily manipulate texture sheets without any breaking changes for classic `SpriteBundle`s (@mockersf [comment](https://github.com/bevyengine/bevy/pull/5072#issuecomment-1165836139)) Also, this approach is more *data oriented* extracting the `Handle<Image>` and avoiding complex texture atlas manipulations to retrieve the texture in both applicative and engine code. With this method, the only difference between a `SpriteBundle` and a `SpriteSheetBundle` is an **additional** component storing the atlas handle and the index. ~~This solution can be applied to `bevy_ui` as well (see #5070).~~ EDIT: I also applied this solution to Bevy UI ## Changelog - (**BREAKING**) Removed `TextureAtlasSprite` - (**BREAKING**) Renamed `TextureAtlas` to `TextureAtlasLayout` - (**BREAKING**) `SpriteSheetBundle`: - Uses a `Sprite` instead of a `TextureAtlasSprite` component - Has a `texture` field containing a `Handle<Image>` like the `SpriteBundle` - Has a new `TextureAtlas` component instead of a `Handle<TextureAtlasLayout>` - (**BREAKING**) `DynamicTextureAtlasBuilder::add_texture` takes an additional `&Handle<Image>` parameter - (**BREAKING**) `TextureAtlasLayout::from_grid` no longer takes a `Handle<Image>` parameter - (**BREAKING**) `TextureAtlasBuilder::finish` now returns a `Result<(TextureAtlasLayout, Handle<Image>), _>` - `bevy_text`: - `GlyphAtlasInfo` stores the texture `Handle<Image>` - `FontAtlas` stores the texture `Handle<Image>` - `bevy_ui`: - (**BREAKING**) Removed `UiAtlasImage` , the atlas bundle is now identical to the `ImageBundle` with an additional `TextureAtlas` ## Migration Guide * Sprites ```diff fn my_system( mut images: ResMut<Assets<Image>>, - mut atlases: ResMut<Assets<TextureAtlas>>, + mut atlases: ResMut<Assets<TextureAtlasLayout>>, asset_server: Res<AssetServer> ) { let texture_handle: asset_server.load("my_texture.png"); - let layout = TextureAtlas::from_grid(texture_handle, Vec2::new(25.0, 25.0), 5, 5, None, None); + let layout = TextureAtlasLayout::from_grid(Vec2::new(25.0, 25.0), 5, 5, None, None); let layout_handle = atlases.add(layout); commands.spawn(SpriteSheetBundle { - sprite: TextureAtlasSprite::new(0), - texture_atlas: atlas_handle, + atlas: TextureAtlas { + layout: layout_handle, + index: 0 + }, + texture: texture_handle, ..Default::default() }); } ``` * UI ```diff fn my_system( mut images: ResMut<Assets<Image>>, - mut atlases: ResMut<Assets<TextureAtlas>>, + mut atlases: ResMut<Assets<TextureAtlasLayout>>, asset_server: Res<AssetServer> ) { let texture_handle: asset_server.load("my_texture.png"); - let layout = TextureAtlas::from_grid(texture_handle, Vec2::new(25.0, 25.0), 5, 5, None, None); + let layout = TextureAtlasLayout::from_grid(Vec2::new(25.0, 25.0), 5, 5, None, None); let layout_handle = atlases.add(layout); commands.spawn(AtlasImageBundle { - texture_atlas_image: UiTextureAtlasImage { - index: 0, - flip_x: false, - flip_y: false, - }, - texture_atlas: atlas_handle, + atlas: TextureAtlas { + layout: layout_handle, + index: 0 + }, + image: UiImage { + texture: texture_handle, + flip_x: false, + flip_y: false, + }, ..Default::default() }); } ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: François <mockersf@gmail.com> Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
2024-01-16 13:59:08 +00:00
for (mut content_size, image, mut image_size, atlas_image) in &mut query {
if let Some(size) = match atlas_image {
Some(atlas) => atlas.texture_rect(&atlases).map(|t| t.size()),
None => textures.get(&image.texture).map(Image::size),
Texture Atlas rework (#5103) # Objective > Old MR: #5072 > ~~Associated UI MR: #5070~~ > Adresses #1618 Unify sprite management ## Solution - Remove the `Handle<Image>` field in `TextureAtlas` which is the main cause for all the boilerplate - Remove the redundant `TextureAtlasSprite` component - Renamed `TextureAtlas` asset to `TextureAtlasLayout` ([suggestion](https://github.com/bevyengine/bevy/pull/5103#discussion_r917281844)) - Add a `TextureAtlas` component, containing the atlas layout handle and the section index The difference between this solution and #5072 is that instead of the `enum` approach is that we can more easily manipulate texture sheets without any breaking changes for classic `SpriteBundle`s (@mockersf [comment](https://github.com/bevyengine/bevy/pull/5072#issuecomment-1165836139)) Also, this approach is more *data oriented* extracting the `Handle<Image>` and avoiding complex texture atlas manipulations to retrieve the texture in both applicative and engine code. With this method, the only difference between a `SpriteBundle` and a `SpriteSheetBundle` is an **additional** component storing the atlas handle and the index. ~~This solution can be applied to `bevy_ui` as well (see #5070).~~ EDIT: I also applied this solution to Bevy UI ## Changelog - (**BREAKING**) Removed `TextureAtlasSprite` - (**BREAKING**) Renamed `TextureAtlas` to `TextureAtlasLayout` - (**BREAKING**) `SpriteSheetBundle`: - Uses a `Sprite` instead of a `TextureAtlasSprite` component - Has a `texture` field containing a `Handle<Image>` like the `SpriteBundle` - Has a new `TextureAtlas` component instead of a `Handle<TextureAtlasLayout>` - (**BREAKING**) `DynamicTextureAtlasBuilder::add_texture` takes an additional `&Handle<Image>` parameter - (**BREAKING**) `TextureAtlasLayout::from_grid` no longer takes a `Handle<Image>` parameter - (**BREAKING**) `TextureAtlasBuilder::finish` now returns a `Result<(TextureAtlasLayout, Handle<Image>), _>` - `bevy_text`: - `GlyphAtlasInfo` stores the texture `Handle<Image>` - `FontAtlas` stores the texture `Handle<Image>` - `bevy_ui`: - (**BREAKING**) Removed `UiAtlasImage` , the atlas bundle is now identical to the `ImageBundle` with an additional `TextureAtlas` ## Migration Guide * Sprites ```diff fn my_system( mut images: ResMut<Assets<Image>>, - mut atlases: ResMut<Assets<TextureAtlas>>, + mut atlases: ResMut<Assets<TextureAtlasLayout>>, asset_server: Res<AssetServer> ) { let texture_handle: asset_server.load("my_texture.png"); - let layout = TextureAtlas::from_grid(texture_handle, Vec2::new(25.0, 25.0), 5, 5, None, None); + let layout = TextureAtlasLayout::from_grid(Vec2::new(25.0, 25.0), 5, 5, None, None); let layout_handle = atlases.add(layout); commands.spawn(SpriteSheetBundle { - sprite: TextureAtlasSprite::new(0), - texture_atlas: atlas_handle, + atlas: TextureAtlas { + layout: layout_handle, + index: 0 + }, + texture: texture_handle, ..Default::default() }); } ``` * UI ```diff fn my_system( mut images: ResMut<Assets<Image>>, - mut atlases: ResMut<Assets<TextureAtlas>>, + mut atlases: ResMut<Assets<TextureAtlasLayout>>, asset_server: Res<AssetServer> ) { let texture_handle: asset_server.load("my_texture.png"); - let layout = TextureAtlas::from_grid(texture_handle, Vec2::new(25.0, 25.0), 5, 5, None, None); + let layout = TextureAtlasLayout::from_grid(Vec2::new(25.0, 25.0), 5, 5, None, None); let layout_handle = atlases.add(layout); commands.spawn(AtlasImageBundle { - texture_atlas_image: UiTextureAtlasImage { - index: 0, - flip_x: false, - flip_y: false, - }, - texture_atlas: atlas_handle, + atlas: TextureAtlas { + layout: layout_handle, + index: 0 + }, + image: UiImage { + texture: texture_handle, + flip_x: false, + flip_y: false, + }, ..Default::default() }); } ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: François <mockersf@gmail.com> Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
2024-01-16 13:59:08 +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 bb59509d44d29: <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
2023-06-23 12:42:17 +00:00
// Update only if size or scale factor has changed to avoid needless layout calculations
`ContentSize` replacement fix (#9753) # Objective If you remove a `ContentSize` component from a Bevy UI entity and then replace it `ui_layout_system` will remove the measure func from the internal Taffy layout tree but no new measure func will be generated to replace it since it's the widget systems that are responsible for creating their respective measure funcs not `ui_layout_system`. The widget systems only perform a measure func update on changes to a widget entity's content. This means that until its content is changed in some way, no content will be displayed by the node. ### Example This example spawns a text node which disappears after a few moments once its `ContentSize` component is replaced. ```rust use bevy::prelude::*; use bevy::ui::ContentSize; fn main() { App::new() .add_plugins(DefaultPlugins) .add_systems(Startup, setup) .add_systems(Update, delayed_replacement) .run(); } fn setup(mut commands: Commands) { commands.spawn(Camera2dBundle::default()); commands.spawn( TextBundle::from_section( "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", TextStyle::default(), ) ); } // Waits a few frames to make sure the font is loaded and the text's glyph layout has been generated. fn delayed_replacement(mut commands: Commands, mut count: Local<usize>, query: Query<Entity, With<Style>>) { *count += 1; if *count == 10 { for item in query.iter() { commands .entity(item) .remove::<ContentSize>() .insert(ContentSize::default()); } } } ``` ## Solution Perform `ui_layout_system`'s `ContentSize` removal detection and resolution first, before the measure func updates. Then in the widget systems, generate a new `Measure` when a `ContentSize` component is added to a widget entity. ## Changelog * `measure_text_system`, `update_image_content_size_system` and `update_atlas_content_size_system` generate a new `Measure` when a `ContentSize` component is added.
2023-09-18 08:54:39 +00:00
if size != image_size.size
|| combined_scale_factor != *previous_combined_scale_factor
|| content_size.is_added()
{
image_size.size = size;
content_size.set(NodeMeasure::Image(ImageMeasure {
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 bb59509d44d29: <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
2023-06-23 12:42:17 +00:00
// multiply the image size by the scale factor to get the physical size
size: size.as_vec2() * combined_scale_factor,
}));
}
}
}
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 bb59509d44d29: <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
2023-06-23 12:42:17 +00:00
*previous_combined_scale_factor = combined_scale_factor;
}