mirror of
https://github.com/bevyengine/bevy
synced 2025-02-16 14:08:32 +00:00
UiImage -> ImageNode, UiImageSize -> ImageNodeSize (#16271)
Align `UiImage` with the new `XNode` naming convention. - Rename `UiImage` to `ImageNode` - Rename `UiImageSize` to `ImageNodeSize` --- Before: ```rust commands.spawn(UiImage::new(image)); ```` After: ```rust commands.spawn(ImageNode::new(image)); ```
This commit is contained in:
parent
c53e73321f
commit
197e5a41db
21 changed files with 245 additions and 232 deletions
|
@ -1,8 +1,8 @@
|
|||
use crate::{
|
||||
experimental::UiChildren,
|
||||
prelude::{Button, Label},
|
||||
widget::TextUiReader,
|
||||
ComputedNode, UiImage,
|
||||
widget::{ImageNode, TextUiReader},
|
||||
ComputedNode,
|
||||
};
|
||||
use bevy_a11y::{
|
||||
accesskit::{NodeBuilder, Rect, Role},
|
||||
|
@ -92,7 +92,10 @@ fn button_changed(
|
|||
|
||||
fn image_changed(
|
||||
mut commands: Commands,
|
||||
mut query: Query<(Entity, Option<&mut AccessibilityNode>), (Changed<UiImage>, Without<Button>)>,
|
||||
mut query: Query<
|
||||
(Entity, Option<&mut AccessibilityNode>),
|
||||
(Changed<ImageNode>, Without<Button>),
|
||||
>,
|
||||
ui_children: UiChildren,
|
||||
mut text_reader: TextUiReader,
|
||||
) {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
//! This crate contains Bevy's UI system, which can be used to create UI for both 2D and 3D games
|
||||
//! # Basic usage
|
||||
//! Spawn UI elements with [`widget::Button`], [`UiImage`], [`Text`](prelude::Text) and [`Node`]
|
||||
//! Spawn UI elements with [`widget::Button`], [`ImageNode`], [`Text`](prelude::Text) and [`Node`]
|
||||
//! This UI is laid out with the Flexbox and CSS Grid layout models (see <https://cssreference.io/flexbox/>)
|
||||
|
||||
pub mod measurement;
|
||||
|
@ -40,7 +40,7 @@ pub use measurement::*;
|
|||
pub use render::*;
|
||||
pub use ui_material::*;
|
||||
pub use ui_node::*;
|
||||
use widget::UiImageSize;
|
||||
use widget::{ImageNode, ImageNodeSize};
|
||||
|
||||
/// The UI prelude.
|
||||
///
|
||||
|
@ -58,7 +58,7 @@ pub mod prelude {
|
|||
node_bundles::*,
|
||||
ui_material::*,
|
||||
ui_node::*,
|
||||
widget::{Button, Label},
|
||||
widget::{Button, ImageNode, Label},
|
||||
Interaction, MaterialNode, UiMaterialPlugin, UiScale,
|
||||
},
|
||||
// `bevy_sprite` re-exports for texture slicing
|
||||
|
@ -155,8 +155,8 @@ impl Plugin for UiPlugin {
|
|||
.register_type::<RelativeCursorPosition>()
|
||||
.register_type::<ScrollPosition>()
|
||||
.register_type::<TargetCamera>()
|
||||
.register_type::<UiImage>()
|
||||
.register_type::<UiImageSize>()
|
||||
.register_type::<ImageNode>()
|
||||
.register_type::<ImageNodeSize>()
|
||||
.register_type::<UiRect>()
|
||||
.register_type::<UiScale>()
|
||||
.register_type::<BorderColor>()
|
||||
|
@ -208,7 +208,7 @@ impl Plugin for UiPlugin {
|
|||
update_clipping_system.after(TransformSystem::TransformPropagate),
|
||||
// Potential conflicts: `Assets<Image>`
|
||||
// They run independently since `widget::image_node_system` will only ever observe
|
||||
// its own UiImage, and `widget::text_system` & `bevy_text::update_text2d_layout`
|
||||
// its own ImageNode, and `widget::text_system` & `bevy_text::update_text2d_layout`
|
||||
// will never modify a pre-existing `Image` asset.
|
||||
widget::update_image_content_size_system
|
||||
.in_set(UiSystem::Prepare)
|
||||
|
@ -265,7 +265,7 @@ fn build_text_interop(app: &mut App) {
|
|||
// Since both systems will only ever insert new [`Image`] assets,
|
||||
// they will never observe each other's effects.
|
||||
.ambiguous_with(bevy_text::update_text2d_layout)
|
||||
// We assume Text is on disjoint UI entities to UiImage and UiTextureAtlasImage
|
||||
// We assume Text is on disjoint UI entities to ImageNode and UiTextureAtlasImage
|
||||
// FIXME: Add an archetype invariant for this https://github.com/bevyengine/bevy/issues/1481.
|
||||
.ambiguous_with(widget::update_image_content_size_system),
|
||||
widget::text_system
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
#![expect(deprecated)]
|
||||
|
||||
use crate::{
|
||||
widget::{Button, UiImageSize},
|
||||
BackgroundColor, BorderColor, BorderRadius, ComputedNode, ContentSize, FocusPolicy,
|
||||
Interaction, MaterialNode, Node, ScrollPosition, UiImage, UiMaterial, ZIndex,
|
||||
widget::{Button, ImageNodeSize},
|
||||
BackgroundColor, BorderColor, BorderRadius, ComputedNode, ContentSize, FocusPolicy, ImageNode,
|
||||
Interaction, MaterialNode, Node, ScrollPosition, UiMaterial, ZIndex,
|
||||
};
|
||||
use bevy_ecs::bundle::Bundle;
|
||||
use bevy_render::view::{InheritedVisibility, ViewVisibility, Visibility};
|
||||
|
@ -60,7 +60,7 @@ pub struct NodeBundle {
|
|||
#[derive(Bundle, Debug, Default)]
|
||||
#[deprecated(
|
||||
since = "0.15.0",
|
||||
note = "Use the `UiImage` component instead. Inserting `UiImage` will also insert the other components required automatically."
|
||||
note = "Use the `ImageNode` component instead. Inserting `ImageNode` will also insert the other components required automatically."
|
||||
)]
|
||||
pub struct ImageBundle {
|
||||
/// Describes the logical size of the node
|
||||
|
@ -73,7 +73,7 @@ pub struct ImageBundle {
|
|||
/// The image of the node.
|
||||
///
|
||||
/// To tint the image, change the `color` field of this component.
|
||||
pub image: UiImage,
|
||||
pub image: ImageNode,
|
||||
/// The color of the background that will fill the containing node.
|
||||
pub background_color: BackgroundColor,
|
||||
/// The border radius of the node
|
||||
|
@ -81,7 +81,7 @@ pub struct ImageBundle {
|
|||
/// The size of the image in pixels
|
||||
///
|
||||
/// This component is set automatically
|
||||
pub image_size: UiImageSize,
|
||||
pub image_size: ImageNodeSize,
|
||||
/// Whether this node should block interaction with lower nodes
|
||||
pub focus_policy: FocusPolicy,
|
||||
/// The transform of the node
|
||||
|
@ -126,7 +126,7 @@ pub struct ButtonBundle {
|
|||
/// The border radius of the node
|
||||
pub border_radius: BorderRadius,
|
||||
/// The image of the node
|
||||
pub image: UiImage,
|
||||
pub image: ImageNode,
|
||||
/// The background color that will fill the containing node
|
||||
pub background_color: BackgroundColor,
|
||||
/// The transform of the node
|
||||
|
|
|
@ -6,8 +6,8 @@ pub mod ui_texture_slice_pipeline;
|
|||
|
||||
use crate::{
|
||||
experimental::UiChildren, BackgroundColor, BorderColor, CalculatedClip, ComputedNode,
|
||||
DefaultUiCamera, Outline, ResolvedBorderRadius, TargetCamera, UiAntiAlias, UiBoxShadowSamples,
|
||||
UiImage, UiScale,
|
||||
DefaultUiCamera, ImageNode, Outline, ResolvedBorderRadius, TargetCamera, UiAntiAlias,
|
||||
UiBoxShadowSamples, UiScale,
|
||||
};
|
||||
use bevy_app::prelude::*;
|
||||
use bevy_asset::{load_internal_asset, AssetEvent, AssetId, Assets, Handle};
|
||||
|
@ -107,7 +107,7 @@ pub fn build_ui_render(app: &mut App) {
|
|||
|
||||
render_app
|
||||
.init_resource::<SpecializedRenderPipelines<UiPipeline>>()
|
||||
.init_resource::<UiImageBindGroups>()
|
||||
.init_resource::<ImageNodeBindGroups>()
|
||||
.init_resource::<UiMeta>()
|
||||
.init_resource::<ExtractedUiNodes>()
|
||||
.allow_ambiguous_resource::<ExtractedUiNodes>()
|
||||
|
@ -315,7 +315,7 @@ pub fn extract_uinode_images(
|
|||
&ViewVisibility,
|
||||
Option<&CalculatedClip>,
|
||||
Option<&TargetCamera>,
|
||||
&UiImage,
|
||||
&ImageNode,
|
||||
)>,
|
||||
>,
|
||||
mapping: Extract<Query<RenderEntity>>,
|
||||
|
@ -871,7 +871,7 @@ pub fn queue_uinodes(
|
|||
}
|
||||
|
||||
#[derive(Resource, Default)]
|
||||
pub struct UiImageBindGroups {
|
||||
pub struct ImageNodeBindGroups {
|
||||
pub values: HashMap<AssetId<Image>, BindGroup>,
|
||||
}
|
||||
|
||||
|
@ -884,7 +884,7 @@ pub fn prepare_uinodes(
|
|||
mut extracted_uinodes: ResMut<ExtractedUiNodes>,
|
||||
view_uniforms: Res<ViewUniforms>,
|
||||
ui_pipeline: Res<UiPipeline>,
|
||||
mut image_bind_groups: ResMut<UiImageBindGroups>,
|
||||
mut image_bind_groups: ResMut<ImageNodeBindGroups>,
|
||||
gpu_images: Res<RenderAssets<GpuImage>>,
|
||||
mut phases: ResMut<ViewSortedRenderPhases<TransparentUi>>,
|
||||
events: Res<SpriteAssetEvents>,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use core::ops::Range;
|
||||
|
||||
use super::{UiBatch, UiImageBindGroups, UiMeta};
|
||||
use super::{ImageNodeBindGroups, UiBatch, UiMeta};
|
||||
use crate::DefaultCameraView;
|
||||
use bevy_ecs::{
|
||||
prelude::*,
|
||||
|
@ -185,7 +185,7 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetUiViewBindGroup<I> {
|
|||
}
|
||||
pub struct SetUiTextureBindGroup<const I: usize>;
|
||||
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetUiTextureBindGroup<I> {
|
||||
type Param = SRes<UiImageBindGroups>;
|
||||
type Param = SRes<ImageNodeBindGroups>;
|
||||
type ViewQuery = ();
|
||||
type ItemQuery = Read<UiBatch>;
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ use bevy_transform::prelude::GlobalTransform;
|
|||
use bevy_utils::HashMap;
|
||||
use binding_types::{sampler, texture_2d};
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use widget::{ImageNode, NodeImageMode};
|
||||
|
||||
pub const UI_SLICER_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(11156288772117983964);
|
||||
|
||||
|
@ -255,7 +256,7 @@ pub fn extract_ui_texture_slices(
|
|||
&ViewVisibility,
|
||||
Option<&CalculatedClip>,
|
||||
Option<&TargetCamera>,
|
||||
&UiImage,
|
||||
&ImageNode,
|
||||
)>,
|
||||
>,
|
||||
mapping: Extract<Query<RenderEntity>>,
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
use crate::{widget::UiImageSize, ContentSize, FocusPolicy, UiRect, Val};
|
||||
use bevy_asset::Handle;
|
||||
use crate::{FocusPolicy, UiRect, Val};
|
||||
use bevy_color::Color;
|
||||
use bevy_ecs::{prelude::*, system::SystemParam};
|
||||
use bevy_math::{vec4, Rect, Vec2, Vec4Swizzles};
|
||||
use bevy_reflect::prelude::*;
|
||||
use bevy_render::{
|
||||
camera::{Camera, RenderTarget},
|
||||
texture::{Image, TRANSPARENT_IMAGE_HANDLE},
|
||||
view::Visibility,
|
||||
};
|
||||
use bevy_sprite::{BorderRect, TextureAtlas, TextureSlicer};
|
||||
use bevy_sprite::BorderRect;
|
||||
use bevy_transform::components::Transform;
|
||||
use bevy_utils::warn_once;
|
||||
use bevy_window::{PrimaryWindow, WindowRef};
|
||||
|
@ -2041,168 +2039,6 @@ impl Outline {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The 2D texture displayed for this UI node
|
||||
#[derive(Component, Clone, Debug, Reflect)]
|
||||
#[reflect(Component, Default, Debug)]
|
||||
#[require(Node, UiImageSize, ContentSize)]
|
||||
pub struct UiImage {
|
||||
/// The tint color used to draw the image.
|
||||
///
|
||||
/// This is multiplied by the color of each pixel in the image.
|
||||
/// The field value defaults to solid white, which will pass the image through unmodified.
|
||||
pub color: Color,
|
||||
/// Handle to the texture.
|
||||
///
|
||||
/// This defaults to a [`TRANSPARENT_IMAGE_HANDLE`], which points to a fully transparent 1x1 texture.
|
||||
pub image: Handle<Image>,
|
||||
/// The (optional) texture atlas used to render the image
|
||||
pub texture_atlas: Option<TextureAtlas>,
|
||||
/// Whether the image should be flipped along its x-axis
|
||||
pub flip_x: bool,
|
||||
/// Whether the image should be flipped along its y-axis
|
||||
pub flip_y: bool,
|
||||
/// An optional rectangle representing the region of the image to render, instead of rendering
|
||||
/// the full image. This is an easy one-off alternative to using a [`TextureAtlas`].
|
||||
///
|
||||
/// When used with a [`TextureAtlas`], the rect
|
||||
/// is offset by the atlas's minimal (top-left) corner position.
|
||||
pub rect: Option<Rect>,
|
||||
/// Controls how the image is altered to fit within the layout and how the layout algorithm determines the space to allocate for the image.
|
||||
pub image_mode: NodeImageMode,
|
||||
}
|
||||
|
||||
impl Default for UiImage {
|
||||
/// A transparent 1x1 image with a solid white tint.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// This will be invisible by default.
|
||||
/// To set this to a visible image, you need to set the `texture` field to a valid image handle,
|
||||
/// or use [`Handle<Image>`]'s default 1x1 solid white texture (as is done in [`UiImage::solid_color`]).
|
||||
fn default() -> Self {
|
||||
UiImage {
|
||||
// This should be white because the tint is multiplied with the image,
|
||||
// so if you set an actual image with default tint you'd want its original colors
|
||||
color: Color::WHITE,
|
||||
texture_atlas: None,
|
||||
// This texture needs to be transparent by default, to avoid covering the background color
|
||||
image: TRANSPARENT_IMAGE_HANDLE,
|
||||
flip_x: false,
|
||||
flip_y: false,
|
||||
rect: None,
|
||||
image_mode: NodeImageMode::Auto,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UiImage {
|
||||
/// Create a new [`UiImage`] with the given texture.
|
||||
pub fn new(texture: Handle<Image>) -> Self {
|
||||
Self {
|
||||
image: texture,
|
||||
color: Color::WHITE,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a solid color [`UiImage`].
|
||||
///
|
||||
/// This is primarily useful for debugging / mocking the extents of your image.
|
||||
pub fn solid_color(color: Color) -> Self {
|
||||
Self {
|
||||
image: Handle::default(),
|
||||
color,
|
||||
flip_x: false,
|
||||
flip_y: false,
|
||||
texture_atlas: None,
|
||||
rect: None,
|
||||
image_mode: NodeImageMode::Auto,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a [`UiImage`] from an image, with an associated texture atlas
|
||||
pub fn from_atlas_image(image: Handle<Image>, atlas: TextureAtlas) -> Self {
|
||||
Self {
|
||||
image,
|
||||
texture_atlas: Some(atlas),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the color tint
|
||||
#[must_use]
|
||||
pub const fn with_color(mut self, color: Color) -> Self {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
|
||||
/// Flip the image along its x-axis
|
||||
#[must_use]
|
||||
pub const fn with_flip_x(mut self) -> Self {
|
||||
self.flip_x = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Flip the image along its y-axis
|
||||
#[must_use]
|
||||
pub const fn with_flip_y(mut self) -> Self {
|
||||
self.flip_y = true;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn with_rect(mut self, rect: Rect) -> Self {
|
||||
self.rect = Some(rect);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn with_mode(mut self, mode: NodeImageMode) -> Self {
|
||||
self.image_mode = mode;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Handle<Image>> for UiImage {
|
||||
fn from(texture: Handle<Image>) -> Self {
|
||||
Self::new(texture)
|
||||
}
|
||||
}
|
||||
|
||||
/// Controls how the image is altered to fit within the layout and how the layout algorithm determines the space in the layout for the image
|
||||
#[derive(Default, Debug, Clone, Reflect)]
|
||||
pub enum NodeImageMode {
|
||||
/// The image will be sized automatically by taking the size of the source image and applying any layout constraints.
|
||||
#[default]
|
||||
Auto,
|
||||
/// The image will be resized to match the size of the node. The image's original size and aspect ratio will be ignored.
|
||||
Stretch,
|
||||
/// The texture will be cut in 9 slices, keeping the texture in proportions on resize
|
||||
Sliced(TextureSlicer),
|
||||
/// The texture will be repeated if stretched beyond `stretched_value`
|
||||
Tiled {
|
||||
/// Should the image repeat horizontally
|
||||
tile_x: bool,
|
||||
/// Should the image repeat vertically
|
||||
tile_y: bool,
|
||||
/// The texture will repeat when the ratio between the *drawing dimensions* of texture and the
|
||||
/// *original texture size* are above this value.
|
||||
stretch_value: f32,
|
||||
},
|
||||
}
|
||||
|
||||
impl NodeImageMode {
|
||||
/// Returns true if this mode uses slices internally ([`NodeImageMode::Sliced`] or [`NodeImageMode::Tiled`])
|
||||
#[inline]
|
||||
pub fn uses_slices(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
NodeImageMode::Sliced(..) | NodeImageMode::Tiled { .. }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// The calculated clip of the node
|
||||
#[derive(Component, Default, Copy, Clone, Debug, Reflect)]
|
||||
#[reflect(Component, Default, Debug)]
|
||||
|
|
|
@ -1,28 +1,188 @@
|
|||
use crate::{
|
||||
ContentSize, Measure, MeasureArgs, Node, NodeImageMode, NodeMeasure, UiImage, UiScale,
|
||||
};
|
||||
use bevy_asset::Assets;
|
||||
use crate::{ContentSize, Measure, MeasureArgs, Node, NodeMeasure, UiScale};
|
||||
use bevy_asset::{Assets, Handle};
|
||||
use bevy_color::Color;
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_math::{UVec2, Vec2};
|
||||
use bevy_math::{Rect, UVec2, Vec2};
|
||||
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
||||
use bevy_render::texture::Image;
|
||||
use bevy_sprite::TextureAtlasLayout;
|
||||
use bevy_render::texture::{Image, TRANSPARENT_IMAGE_HANDLE};
|
||||
use bevy_sprite::{TextureAtlas, TextureAtlasLayout, TextureSlicer};
|
||||
use bevy_window::{PrimaryWindow, Window};
|
||||
use taffy::{MaybeMath, MaybeResolve};
|
||||
|
||||
/// A UI Node that renders an image.
|
||||
#[derive(Component, Clone, Debug, Reflect)]
|
||||
#[reflect(Component, Default, Debug)]
|
||||
#[require(Node, ImageNodeSize, ContentSize)]
|
||||
pub struct ImageNode {
|
||||
/// The tint color used to draw the image.
|
||||
///
|
||||
/// This is multiplied by the color of each pixel in the image.
|
||||
/// The field value defaults to solid white, which will pass the image through unmodified.
|
||||
pub color: Color,
|
||||
/// Handle to the texture.
|
||||
///
|
||||
/// This defaults to a [`TRANSPARENT_IMAGE_HANDLE`], which points to a fully transparent 1x1 texture.
|
||||
pub image: Handle<Image>,
|
||||
/// The (optional) texture atlas used to render the image.
|
||||
pub texture_atlas: Option<TextureAtlas>,
|
||||
/// Whether the image should be flipped along its x-axis.
|
||||
pub flip_x: bool,
|
||||
/// Whether the image should be flipped along its y-axis.
|
||||
pub flip_y: bool,
|
||||
/// An optional rectangle representing the region of the image to render, instead of rendering
|
||||
/// the full image. This is an easy one-off alternative to using a [`TextureAtlas`].
|
||||
///
|
||||
/// When used with a [`TextureAtlas`], the rect
|
||||
/// is offset by the atlas's minimal (top-left) corner position.
|
||||
pub rect: Option<Rect>,
|
||||
/// Controls how the image is altered to fit within the layout and how the layout algorithm determines the space to allocate for the image.
|
||||
pub image_mode: NodeImageMode,
|
||||
}
|
||||
|
||||
impl Default for ImageNode {
|
||||
/// A transparent 1x1 image with a solid white tint.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// This will be invisible by default.
|
||||
/// To set this to a visible image, you need to set the `texture` field to a valid image handle,
|
||||
/// or use [`Handle<Image>`]'s default 1x1 solid white texture (as is done in [`ImageNode::solid_color`]).
|
||||
fn default() -> Self {
|
||||
ImageNode {
|
||||
// This should be white because the tint is multiplied with the image,
|
||||
// so if you set an actual image with default tint you'd want its original colors
|
||||
color: Color::WHITE,
|
||||
texture_atlas: None,
|
||||
// This texture needs to be transparent by default, to avoid covering the background color
|
||||
image: TRANSPARENT_IMAGE_HANDLE,
|
||||
flip_x: false,
|
||||
flip_y: false,
|
||||
rect: None,
|
||||
image_mode: NodeImageMode::Auto,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ImageNode {
|
||||
/// Create a new [`ImageNode`] with the given texture.
|
||||
pub fn new(texture: Handle<Image>) -> Self {
|
||||
Self {
|
||||
image: texture,
|
||||
color: Color::WHITE,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a solid color [`ImageNode`].
|
||||
///
|
||||
/// This is primarily useful for debugging / mocking the extents of your image.
|
||||
pub fn solid_color(color: Color) -> Self {
|
||||
Self {
|
||||
image: Handle::default(),
|
||||
color,
|
||||
flip_x: false,
|
||||
flip_y: false,
|
||||
texture_atlas: None,
|
||||
rect: None,
|
||||
image_mode: NodeImageMode::Auto,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a [`ImageNode`] from an image, with an associated texture atlas
|
||||
pub fn from_atlas_image(image: Handle<Image>, atlas: TextureAtlas) -> Self {
|
||||
Self {
|
||||
image,
|
||||
texture_atlas: Some(atlas),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the color tint
|
||||
#[must_use]
|
||||
pub const fn with_color(mut self, color: Color) -> Self {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
|
||||
/// Flip the image along its x-axis
|
||||
#[must_use]
|
||||
pub const fn with_flip_x(mut self) -> Self {
|
||||
self.flip_x = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Flip the image along its y-axis
|
||||
#[must_use]
|
||||
pub const fn with_flip_y(mut self) -> Self {
|
||||
self.flip_y = true;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn with_rect(mut self, rect: Rect) -> Self {
|
||||
self.rect = Some(rect);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn with_mode(mut self, mode: NodeImageMode) -> Self {
|
||||
self.image_mode = mode;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Handle<Image>> for ImageNode {
|
||||
fn from(texture: Handle<Image>) -> Self {
|
||||
Self::new(texture)
|
||||
}
|
||||
}
|
||||
|
||||
/// Controls how the image is altered to fit within the layout and how the layout algorithm determines the space in the layout for the image
|
||||
#[derive(Default, Debug, Clone, Reflect)]
|
||||
pub enum NodeImageMode {
|
||||
/// The image will be sized automatically by taking the size of the source image and applying any layout constraints.
|
||||
#[default]
|
||||
Auto,
|
||||
/// The image will be resized to match the size of the node. The image's original size and aspect ratio will be ignored.
|
||||
Stretch,
|
||||
/// The texture will be cut in 9 slices, keeping the texture in proportions on resize
|
||||
Sliced(TextureSlicer),
|
||||
/// The texture will be repeated if stretched beyond `stretched_value`
|
||||
Tiled {
|
||||
/// Should the image repeat horizontally
|
||||
tile_x: bool,
|
||||
/// Should the image repeat vertically
|
||||
tile_y: bool,
|
||||
/// The texture will repeat when the ratio between the *drawing dimensions* of texture and the
|
||||
/// *original texture size* are above this value.
|
||||
stretch_value: f32,
|
||||
},
|
||||
}
|
||||
|
||||
impl NodeImageMode {
|
||||
/// Returns true if this mode uses slices internally ([`NodeImageMode::Sliced`] or [`NodeImageMode::Tiled`])
|
||||
#[inline]
|
||||
pub fn uses_slices(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
NodeImageMode::Sliced(..) | NodeImageMode::Tiled { .. }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// The size of the image's texture
|
||||
///
|
||||
/// This component is updated automatically by [`update_image_content_size_system`]
|
||||
#[derive(Component, Debug, Copy, Clone, Default, Reflect)]
|
||||
#[reflect(Component, Default, Debug)]
|
||||
pub struct UiImageSize {
|
||||
pub struct ImageNodeSize {
|
||||
/// The size of the image's texture
|
||||
///
|
||||
/// This field is updated automatically by [`update_image_content_size_system`]
|
||||
size: UVec2,
|
||||
}
|
||||
|
||||
impl UiImageSize {
|
||||
impl ImageNodeSize {
|
||||
/// The size of the image's texture
|
||||
pub fn size(&self) -> UVec2 {
|
||||
self.size
|
||||
|
@ -99,7 +259,7 @@ pub fn update_image_content_size_system(
|
|||
textures: Res<Assets<Image>>,
|
||||
|
||||
atlases: Res<Assets<TextureAtlasLayout>>,
|
||||
mut query: Query<(&mut ContentSize, Ref<UiImage>, &mut UiImageSize), UpdateImageFilter>,
|
||||
mut query: Query<(&mut ContentSize, Ref<ImageNode>, &mut ImageNodeSize), UpdateImageFilter>,
|
||||
) {
|
||||
let combined_scale_factor = windows
|
||||
.get_single()
|
||||
|
|
|
@ -115,7 +115,7 @@ fn setup(
|
|||
));
|
||||
|
||||
commands.spawn((
|
||||
UiImage {
|
||||
ImageNode {
|
||||
image: metering_mask,
|
||||
..default()
|
||||
},
|
||||
|
@ -162,7 +162,7 @@ struct ExampleResources {
|
|||
fn example_control_system(
|
||||
camera: Single<(&mut Transform, &mut AutoExposure), With<Camera3d>>,
|
||||
mut display: Single<&mut Text, With<ExampleDisplay>>,
|
||||
mut mask_image: Single<&mut Node, With<UiImage>>,
|
||||
mut mask_image: Single<&mut Node, With<ImageNode>>,
|
||||
time: Res<Time>,
|
||||
input: Res<ButtonInput<KeyCode>>,
|
||||
resources: Res<ExampleResources>,
|
||||
|
|
|
@ -86,7 +86,7 @@ mod splash {
|
|||
))
|
||||
.with_children(|parent| {
|
||||
parent.spawn((
|
||||
UiImage::new(icon),
|
||||
ImageNode::new(icon),
|
||||
Node {
|
||||
// This will set the logo to be 200px wide, and auto adjust its height
|
||||
width: Val::Px(200.0),
|
||||
|
@ -444,7 +444,7 @@ mod menu {
|
|||
))
|
||||
.with_children(|parent| {
|
||||
let icon = asset_server.load("textures/Game Icons/right.png");
|
||||
parent.spawn((UiImage::new(icon), button_icon_node.clone()));
|
||||
parent.spawn((ImageNode::new(icon), button_icon_node.clone()));
|
||||
parent.spawn((
|
||||
Text::new("New Game"),
|
||||
button_text_font.clone(),
|
||||
|
@ -460,7 +460,7 @@ mod menu {
|
|||
))
|
||||
.with_children(|parent| {
|
||||
let icon = asset_server.load("textures/Game Icons/wrench.png");
|
||||
parent.spawn((UiImage::new(icon), button_icon_node.clone()));
|
||||
parent.spawn((ImageNode::new(icon), button_icon_node.clone()));
|
||||
parent.spawn((
|
||||
Text::new("Settings"),
|
||||
button_text_font.clone(),
|
||||
|
@ -476,7 +476,7 @@ mod menu {
|
|||
))
|
||||
.with_children(|parent| {
|
||||
let icon = asset_server.load("textures/Game Icons/exitRight.png");
|
||||
parent.spawn((UiImage::new(icon), button_icon_node));
|
||||
parent.spawn((ImageNode::new(icon), button_icon_node));
|
||||
parent.spawn((
|
||||
Text::new("Quit"),
|
||||
button_text_font,
|
||||
|
|
|
@ -252,7 +252,7 @@ fn spawn_button(
|
|||
));
|
||||
|
||||
if let Some(image) = image {
|
||||
builder.insert(UiImage::new(image));
|
||||
builder.insert(ImageNode::new(image));
|
||||
}
|
||||
|
||||
if spawn_text {
|
||||
|
|
|
@ -49,7 +49,7 @@ fn atlas_render_system(
|
|||
let font_atlas = &font_atlas[state.atlas_count as usize];
|
||||
state.atlas_count += 1;
|
||||
commands.spawn((
|
||||
UiImage::new(font_atlas.texture.clone()),
|
||||
ImageNode::new(font_atlas.texture.clone()),
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
top: Val::ZERO,
|
||||
|
|
|
@ -77,7 +77,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||
))
|
||||
.with_children(|parent| {
|
||||
parent.spawn((
|
||||
UiImage::new(image.clone()),
|
||||
ImageNode::new(image.clone()),
|
||||
Node {
|
||||
min_width: Val::Px(100.),
|
||||
min_height: Val::Px(100.),
|
||||
|
|
|
@ -80,7 +80,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||
BackgroundColor(LIGHT_CYAN.into()),
|
||||
))
|
||||
.with_child((
|
||||
UiImage::new(image.clone()),
|
||||
ImageNode::new(image.clone()),
|
||||
Node {
|
||||
min_width: Val::Px(100.),
|
||||
min_height: Val::Px(100.),
|
||||
|
|
|
@ -141,7 +141,7 @@ fn spawn_image(
|
|||
) {
|
||||
spawn_container(parent, update_transform, |parent| {
|
||||
parent.spawn((
|
||||
UiImage::new(asset_server.load("branding/bevy_logo_dark_big.png")),
|
||||
ImageNode::new(asset_server.load("branding/bevy_logo_dark_big.png")),
|
||||
Node {
|
||||
height: Val::Px(100.),
|
||||
position_type: PositionType::Absolute,
|
||||
|
|
|
@ -11,6 +11,7 @@ use bevy::{
|
|||
input::mouse::{MouseScrollUnit, MouseWheel},
|
||||
picking::focus::HoverMap,
|
||||
prelude::*,
|
||||
ui::widget::NodeImageMode,
|
||||
winit::WinitSettings,
|
||||
};
|
||||
|
||||
|
@ -181,7 +182,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||
))
|
||||
.with_children(|parent| {
|
||||
parent.spawn((
|
||||
UiImage::new(asset_server.load("branding/bevy_logo_light.png")),
|
||||
ImageNode::new(asset_server.load("branding/bevy_logo_light.png")),
|
||||
// Uses the transform to rotate the logo image by 45 degrees
|
||||
Transform::from_rotation(Quat::from_rotation_z(0.25 * PI)),
|
||||
BorderRadius::all(Val::Px(10.)),
|
||||
|
@ -293,7 +294,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||
// bevy logo (image)
|
||||
parent
|
||||
.spawn((
|
||||
UiImage::new(asset_server.load("branding/bevy_logo_dark_big.png"))
|
||||
ImageNode::new(asset_server.load("branding/bevy_logo_dark_big.png"))
|
||||
.with_mode(NodeImageMode::Stretch),
|
||||
Node {
|
||||
width: Val::Px(500.0),
|
||||
|
@ -333,17 +334,17 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||
[(false, false), (false, true), (true, true), (true, false)]
|
||||
{
|
||||
parent.spawn((
|
||||
Node {
|
||||
// The height will be chosen automatically to preserve the image's aspect ratio
|
||||
width: Val::Px(75.),
|
||||
..default()
|
||||
},
|
||||
UiImage {
|
||||
ImageNode {
|
||||
image: asset_server.load("branding/icon.png"),
|
||||
flip_x,
|
||||
flip_y,
|
||||
..default()
|
||||
},
|
||||
Node {
|
||||
// The height will be chosen automatically to preserve the image's aspect ratio
|
||||
width: Val::Px(75.),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
}
|
||||
});
|
||||
|
|
|
@ -64,7 +64,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||
BackgroundColor(BLUE.into()),
|
||||
));
|
||||
parent.spawn((
|
||||
UiImage::new(asset_server.load("branding/icon.png")),
|
||||
ImageNode::new(asset_server.load("branding/icon.png")),
|
||||
Node {
|
||||
width: Val::Px(30.0),
|
||||
height: Val::Px(30.0),
|
||||
|
|
|
@ -44,7 +44,10 @@ fn setup(
|
|||
})
|
||||
.with_children(|parent| {
|
||||
parent.spawn((
|
||||
UiImage::from_atlas_image(texture_handle, TextureAtlas::from(texture_atlas_handle)),
|
||||
ImageNode::from_atlas_image(
|
||||
texture_handle,
|
||||
TextureAtlas::from(texture_atlas_handle),
|
||||
),
|
||||
Node {
|
||||
width: Val::Px(256.),
|
||||
height: Val::Px(256.),
|
||||
|
@ -64,10 +67,13 @@ fn setup(
|
|||
});
|
||||
}
|
||||
|
||||
fn increment_atlas_index(mut ui_images: Query<&mut UiImage>, keyboard: Res<ButtonInput<KeyCode>>) {
|
||||
fn increment_atlas_index(
|
||||
mut image_nodes: Query<&mut ImageNode>,
|
||||
keyboard: Res<ButtonInput<KeyCode>>,
|
||||
) {
|
||||
if keyboard.just_pressed(KeyCode::Space) {
|
||||
for mut ui_image in &mut ui_images {
|
||||
if let Some(atlas) = &mut ui_image.texture_atlas {
|
||||
for mut image_node in &mut image_nodes {
|
||||
if let Some(atlas) = &mut image_node.texture_atlas {
|
||||
atlas.index = (atlas.index + 1) % 6;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
use bevy::{
|
||||
color::palettes::css::{GOLD, ORANGE},
|
||||
prelude::*,
|
||||
ui::widget::NodeImageMode,
|
||||
winit::WinitSettings,
|
||||
};
|
||||
|
||||
|
@ -19,7 +20,7 @@ fn main() {
|
|||
|
||||
fn button_system(
|
||||
mut interaction_query: Query<
|
||||
(&Interaction, &Children, &mut UiImage),
|
||||
(&Interaction, &Children, &mut ImageNode),
|
||||
(Changed<Interaction>, With<Button>),
|
||||
>,
|
||||
mut text_query: Query<&mut Text>,
|
||||
|
@ -81,7 +82,7 @@ fn setup(
|
|||
parent
|
||||
.spawn((
|
||||
Button,
|
||||
UiImage::from_atlas_image(
|
||||
ImageNode::from_atlas_image(
|
||||
texture_handle.clone(),
|
||||
TextureAtlas {
|
||||
index: idx,
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
use bevy::{
|
||||
color::palettes::css::{GOLD, ORANGE},
|
||||
prelude::*,
|
||||
ui::widget::NodeImageMode,
|
||||
winit::WinitSettings,
|
||||
};
|
||||
|
||||
|
@ -19,7 +20,7 @@ fn main() {
|
|||
|
||||
fn button_system(
|
||||
mut interaction_query: Query<
|
||||
(&Interaction, &Children, &mut UiImage),
|
||||
(&Interaction, &Children, &mut ImageNode),
|
||||
(Changed<Interaction>, With<Button>),
|
||||
>,
|
||||
mut text_query: Query<&mut Text>,
|
||||
|
@ -67,6 +68,11 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||
parent
|
||||
.spawn((
|
||||
Button,
|
||||
ImageNode {
|
||||
image: image.clone(),
|
||||
image_mode: NodeImageMode::Sliced(slicer.clone()),
|
||||
..default()
|
||||
},
|
||||
Node {
|
||||
width: Val::Px(w),
|
||||
height: Val::Px(h),
|
||||
|
@ -77,8 +83,6 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||
margin: UiRect::all(Val::Px(20.0)),
|
||||
..default()
|
||||
},
|
||||
UiImage::new(image.clone())
|
||||
.with_mode(NodeImageMode::Sliced(slicer.clone())),
|
||||
))
|
||||
.with_child((
|
||||
Text::new("Button"),
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
use bevy::{
|
||||
prelude::*,
|
||||
render::texture::{ImageLoaderSettings, ImageSampler},
|
||||
ui::widget::NodeImageMode,
|
||||
winit::WinitSettings,
|
||||
};
|
||||
|
||||
|
@ -57,7 +58,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||
([160., 160.], true, true),
|
||||
] {
|
||||
parent.spawn((
|
||||
UiImage {
|
||||
ImageNode {
|
||||
image: image.clone(),
|
||||
flip_x,
|
||||
flip_y,
|
||||
|
|
Loading…
Add table
Reference in a new issue