Add rect field to UI image (#15095)
# Objective
Fixes #14424
## Solution
Add a rect field to UiImage, and update the extraction of ui images and
slices.
## Testing
I tested all possible combinations of having a rect, using a texture
atlas, setting image scale mode to sliced and image scale mode to tiled.
See the showcase section.
---
## Showcase
<img width="1279" alt="Screenshot 2024-09-08 at 16 23 05"
src="https://github.com/user-attachments/assets/183e53eb-f27c-4c8e-9fd5-4678825db3b6">
<details>
<summary>Click to view showcase</summary>
```rust
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
.add_systems(Startup, create_ui)
.run();
}
fn create_ui(
mut commands: Commands,
assets: Res<AssetServer>,
mut texture_atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
) {
let texture = assets.load("textures/fantasy_ui_borders/numbered_slices.png");
let layout = TextureAtlasLayout::from_grid(UVec2::splat(16), 3, 3, None, None);
let texture_atlas_layout = texture_atlas_layouts.add(layout);
commands.spawn(Camera2dBundle::default());
let style = Style {
width: Val::Px(96.),
height: Val::Px(96.),
..default()
};
commands
.spawn(NodeBundle { ..default() })
.with_children(|parent| {
// nothing
parent.spawn(ImageBundle {
image: UiImage::new(texture.clone()),
style: style.clone(),
..default()
});
// with rect
parent.spawn(ImageBundle {
image: UiImage::new(texture.clone()).with_rect(Rect::new(0., 0., 16., 16.)),
style: style.clone(),
..default()
});
// with rect and texture atlas
parent.spawn((
ImageBundle {
image: UiImage::new(texture.clone()).with_rect(Rect::new(0., 0., 8., 8.)),
style: style.clone(),
..default()
},
TextureAtlas {
layout: texture_atlas_layout.clone(),
index: 1,
},
));
// with texture atlas
parent.spawn((
ImageBundle {
image: UiImage::new(texture.clone()),
style: style.clone(),
..default()
},
TextureAtlas {
layout: texture_atlas_layout.clone(),
index: 2,
},
));
// with texture slicer
parent.spawn((
ImageBundle {
image: UiImage::new(texture.clone()),
style: style.clone(),
..default()
},
ImageScaleMode::Sliced(TextureSlicer {
border: BorderRect::square(16.),
center_scale_mode: SliceScaleMode::Stretch,
sides_scale_mode: SliceScaleMode::Stretch,
max_corner_scale: 1.,
}),
));
// with rect and texture slicer
parent.spawn((
ImageBundle {
image: UiImage::new(texture.clone()).with_rect(Rect::new(0., 0., 16., 16.)),
style: style.clone(),
..default()
},
ImageScaleMode::Sliced(TextureSlicer {
border: BorderRect::square(2.),
center_scale_mode: SliceScaleMode::Stretch,
sides_scale_mode: SliceScaleMode::Stretch,
max_corner_scale: 1.,
}),
));
// with rect, texture atlas and texture slicer
parent.spawn((
ImageBundle {
image: UiImage::new(texture.clone()).with_rect(Rect::new(0., 0., 8., 8.)),
style: style.clone(),
..default()
},
TextureAtlas {
layout: texture_atlas_layout.clone(),
index: 1,
},
ImageScaleMode::Sliced(TextureSlicer {
border: BorderRect::square(1.),
center_scale_mode: SliceScaleMode::Stretch,
sides_scale_mode: SliceScaleMode::Stretch,
max_corner_scale: 1.,
}),
));
// with texture atlas and texture slicer
parent.spawn((
ImageBundle {
image: UiImage::new(texture.clone()),
style: style.clone(),
..default()
},
TextureAtlas {
layout: texture_atlas_layout.clone(),
index: 2,
},
ImageScaleMode::Sliced(TextureSlicer {
border: BorderRect::square(2.),
center_scale_mode: SliceScaleMode::Stretch,
sides_scale_mode: SliceScaleMode::Stretch,
max_corner_scale: 1.,
}),
));
// with tiled
parent.spawn((
ImageBundle {
image: UiImage::new(texture.clone()),
style: style.clone(),
..default()
},
ImageScaleMode::Tiled {
tile_x: true,
tile_y: true,
stretch_value: 1.,
},
));
// with rect and tiled
parent.spawn((
ImageBundle {
image: UiImage::new(texture.clone()).with_rect(Rect::new(0., 0., 16., 16.)),
style: style.clone(),
..default()
},
ImageScaleMode::Tiled {
tile_x: true,
tile_y: true,
stretch_value: 1.,
},
));
// with rect, texture atlas and tiled
parent.spawn((
ImageBundle {
image: UiImage::new(texture.clone()).with_rect(Rect::new(0., 0., 8., 8.)),
style: style.clone(),
..default()
},
TextureAtlas {
layout: texture_atlas_layout.clone(),
index: 1,
},
ImageScaleMode::Tiled {
tile_x: true,
tile_y: true,
stretch_value: 1.,
},
));
// with texture atlas and tiled
parent.spawn((
ImageBundle {
image: UiImage::new(texture.clone()),
style: style.clone(),
..default()
},
TextureAtlas {
layout: texture_atlas_layout.clone(),
index: 2,
},
ImageScaleMode::Tiled {
tile_x: true,
tile_y: true,
stretch_value: 1.,
},
));
});
}
```
</details>