Decouple BackgroundColor
from UiImage
(#11165)
# Objective Fixes https://github.com/bevyengine/bevy/issues/11157. ## Solution Stop using `BackgroundColor` as a color tint for `UiImage`. Add a `UiImage::color` field for color tint instead. Allow a UI node to simultaneously include a solid-color background and an image, with the image rendered on top of the background (this is already how it works for e.g. text).  --- ## Changelog - The `BackgroundColor` component now renders a solid-color background behind `UiImage` instead of tinting its color. - Removed `BackgroundColor` from `ImageBundle`, `AtlasImageBundle`, and `ButtonBundle`. - Added `UiImage::color`. - Expanded `RenderUiSystem` variants. - Renamed `bevy_ui::extract_text_uinodes` to `extract_uinodes_text` for consistency. ## Migration Guide - `BackgroundColor` no longer tints the color of UI images. Use `UiImage::color` for that instead. - For solid color buttons, replace `ButtonBundle { background_color: my_color.into(), ... }` with `ButtonBundle { image: UiImage::default().with_color(my_color), ... }`, and update button interaction systems to use `UiImage::color` instead of `BackgroundColor` as well. - `bevy_ui::RenderUiSystem::ExtractNode` has been split into `ExtractBackgrounds`, `ExtractImages`, `ExtractBorders`, and `ExtractText`. - `bevy_ui::extract_uinodes` has been split into `bevy_ui::extract_uinode_background_colors` and `bevy_ui::extract_uinode_images`. - `bevy_ui::extract_text_uinodes` has been renamed to `extract_uinode_text`.
This commit is contained in:
parent
6e83439a06
commit
e8ae0d6c49
@ -93,10 +93,6 @@ pub struct ImageBundle {
|
|||||||
pub style: Style,
|
pub style: Style,
|
||||||
/// The calculated size based on the given image
|
/// The calculated size based on the given image
|
||||||
pub calculated_size: ContentSize,
|
pub calculated_size: ContentSize,
|
||||||
/// The background color, which serves as a "fill" for this node
|
|
||||||
///
|
|
||||||
/// Combines with `UiImage` to tint the provided image.
|
|
||||||
pub background_color: BackgroundColor,
|
|
||||||
/// The image of the node
|
/// The image of the node
|
||||||
pub image: UiImage,
|
pub image: UiImage,
|
||||||
/// The size of the image in pixels
|
/// The size of the image in pixels
|
||||||
@ -140,10 +136,6 @@ pub struct AtlasImageBundle {
|
|||||||
pub style: Style,
|
pub style: Style,
|
||||||
/// The calculated size based on the given image
|
/// The calculated size based on the given image
|
||||||
pub calculated_size: ContentSize,
|
pub calculated_size: ContentSize,
|
||||||
/// The background color, which serves as a "fill" for this node
|
|
||||||
///
|
|
||||||
/// Combines with `UiImage` to tint the provided image.
|
|
||||||
pub background_color: BackgroundColor,
|
|
||||||
/// The image of the node
|
/// The image of the node
|
||||||
pub image: UiImage,
|
pub image: UiImage,
|
||||||
/// A handle to the texture atlas to use for this Ui Node
|
/// A handle to the texture atlas to use for this Ui Node
|
||||||
@ -319,10 +311,6 @@ pub struct ButtonBundle {
|
|||||||
pub interaction: Interaction,
|
pub interaction: Interaction,
|
||||||
/// Whether this node should block interaction with lower nodes
|
/// Whether this node should block interaction with lower nodes
|
||||||
pub focus_policy: FocusPolicy,
|
pub focus_policy: FocusPolicy,
|
||||||
/// The background color, which serves as a "fill" for this node
|
|
||||||
///
|
|
||||||
/// When combined with `UiImage`, tints the provided image.
|
|
||||||
pub background_color: BackgroundColor,
|
|
||||||
/// The color of the Node's border
|
/// The color of the Node's border
|
||||||
pub border_color: BorderColor,
|
pub border_color: BorderColor,
|
||||||
/// The image of the node
|
/// The image of the node
|
||||||
@ -349,13 +337,12 @@ pub struct ButtonBundle {
|
|||||||
impl Default for ButtonBundle {
|
impl Default for ButtonBundle {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
focus_policy: FocusPolicy::Block,
|
|
||||||
node: Default::default(),
|
node: Default::default(),
|
||||||
button: Default::default(),
|
button: Default::default(),
|
||||||
style: Default::default(),
|
style: Default::default(),
|
||||||
border_color: BorderColor(Color::NONE),
|
|
||||||
interaction: Default::default(),
|
interaction: Default::default(),
|
||||||
background_color: Default::default(),
|
focus_policy: FocusPolicy::Block,
|
||||||
|
border_color: BorderColor(Color::NONE),
|
||||||
image: Default::default(),
|
image: Default::default(),
|
||||||
transform: Default::default(),
|
transform: Default::default(),
|
||||||
global_transform: Default::default(),
|
global_transform: Default::default(),
|
||||||
|
@ -59,7 +59,10 @@ pub const UI_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(130128470471
|
|||||||
|
|
||||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
|
||||||
pub enum RenderUiSystem {
|
pub enum RenderUiSystem {
|
||||||
ExtractNode,
|
ExtractBackgrounds,
|
||||||
|
ExtractImages,
|
||||||
|
ExtractBorders,
|
||||||
|
ExtractText,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_ui_render(app: &mut App) {
|
pub fn build_ui_render(app: &mut App) {
|
||||||
@ -77,16 +80,27 @@ pub fn build_ui_render(app: &mut App) {
|
|||||||
.allow_ambiguous_resource::<ExtractedUiNodes>()
|
.allow_ambiguous_resource::<ExtractedUiNodes>()
|
||||||
.init_resource::<DrawFunctions<TransparentUi>>()
|
.init_resource::<DrawFunctions<TransparentUi>>()
|
||||||
.add_render_command::<TransparentUi, DrawUi>()
|
.add_render_command::<TransparentUi, DrawUi>()
|
||||||
|
.configure_sets(
|
||||||
|
ExtractSchedule,
|
||||||
|
(
|
||||||
|
RenderUiSystem::ExtractBackgrounds,
|
||||||
|
RenderUiSystem::ExtractImages,
|
||||||
|
RenderUiSystem::ExtractBorders,
|
||||||
|
RenderUiSystem::ExtractText,
|
||||||
|
)
|
||||||
|
.chain(),
|
||||||
|
)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
ExtractSchedule,
|
ExtractSchedule,
|
||||||
(
|
(
|
||||||
extract_default_ui_camera_view::<Camera2d>,
|
extract_default_ui_camera_view::<Camera2d>,
|
||||||
extract_default_ui_camera_view::<Camera3d>,
|
extract_default_ui_camera_view::<Camera3d>,
|
||||||
extract_uinodes.in_set(RenderUiSystem::ExtractNode),
|
extract_uinode_background_colors.in_set(RenderUiSystem::ExtractBackgrounds),
|
||||||
extract_uinode_borders,
|
extract_uinode_images.in_set(RenderUiSystem::ExtractImages),
|
||||||
|
extract_uinode_borders.in_set(RenderUiSystem::ExtractBorders),
|
||||||
|
extract_uinode_outlines.in_set(RenderUiSystem::ExtractBorders),
|
||||||
#[cfg(feature = "bevy_text")]
|
#[cfg(feature = "bevy_text")]
|
||||||
extract_text_uinodes,
|
extract_uinode_text.in_set(RenderUiSystem::ExtractText),
|
||||||
extract_uinode_outlines,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
@ -148,6 +162,134 @@ pub struct ExtractedUiNodes {
|
|||||||
pub uinodes: EntityHashMap<ExtractedUiNode>,
|
pub uinodes: EntityHashMap<ExtractedUiNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn extract_uinode_background_colors(
|
||||||
|
mut extracted_uinodes: ResMut<ExtractedUiNodes>,
|
||||||
|
default_ui_camera: Extract<DefaultUiCamera>,
|
||||||
|
uinode_query: Extract<
|
||||||
|
Query<(
|
||||||
|
Entity,
|
||||||
|
&Node,
|
||||||
|
&GlobalTransform,
|
||||||
|
&ViewVisibility,
|
||||||
|
Option<&CalculatedClip>,
|
||||||
|
Option<&TargetCamera>,
|
||||||
|
&BackgroundColor,
|
||||||
|
)>,
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
for (entity, uinode, transform, view_visibility, clip, camera, background_color) in
|
||||||
|
&uinode_query
|
||||||
|
{
|
||||||
|
let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get())
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Skip invisible backgrounds
|
||||||
|
if !view_visibility.get() || background_color.0.is_fully_transparent() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
extracted_uinodes.uinodes.insert(
|
||||||
|
entity,
|
||||||
|
ExtractedUiNode {
|
||||||
|
stack_index: uinode.stack_index,
|
||||||
|
transform: transform.compute_matrix(),
|
||||||
|
color: background_color.0.into(),
|
||||||
|
rect: Rect {
|
||||||
|
min: Vec2::ZERO,
|
||||||
|
max: uinode.calculated_size,
|
||||||
|
},
|
||||||
|
clip: clip.map(|clip| clip.clip),
|
||||||
|
image: AssetId::default(),
|
||||||
|
atlas_size: None,
|
||||||
|
flip_x: false,
|
||||||
|
flip_y: false,
|
||||||
|
camera_entity,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extract_uinode_images(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut extracted_uinodes: ResMut<ExtractedUiNodes>,
|
||||||
|
texture_atlases: Extract<Res<Assets<TextureAtlasLayout>>>,
|
||||||
|
default_ui_camera: Extract<DefaultUiCamera>,
|
||||||
|
uinode_query: Extract<
|
||||||
|
Query<(
|
||||||
|
&Node,
|
||||||
|
&GlobalTransform,
|
||||||
|
&ViewVisibility,
|
||||||
|
Option<&CalculatedClip>,
|
||||||
|
Option<&TargetCamera>,
|
||||||
|
&UiImage,
|
||||||
|
Option<&TextureAtlas>,
|
||||||
|
Option<&ComputedTextureSlices>,
|
||||||
|
)>,
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
for (uinode, transform, view_visibility, clip, camera, image, atlas, slices) in &uinode_query {
|
||||||
|
let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get())
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Skip invisible images
|
||||||
|
if !view_visibility.get() || image.color.is_fully_transparent() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(slices) = slices {
|
||||||
|
extracted_uinodes.uinodes.extend(
|
||||||
|
slices
|
||||||
|
.extract_ui_nodes(transform, uinode, image, clip, camera_entity)
|
||||||
|
.map(|e| (commands.spawn_empty().id(), e)),
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (rect, atlas_size) = match atlas {
|
||||||
|
Some(atlas) => {
|
||||||
|
let Some(layout) = texture_atlases.get(&atlas.layout) else {
|
||||||
|
// Atlas not present in assets resource (should this warn the user?)
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let mut atlas_rect = layout.textures[atlas.index].as_rect();
|
||||||
|
let mut atlas_size = layout.size.as_vec2();
|
||||||
|
let scale = uinode.size() / atlas_rect.size();
|
||||||
|
atlas_rect.min *= scale;
|
||||||
|
atlas_rect.max *= scale;
|
||||||
|
atlas_size *= scale;
|
||||||
|
(atlas_rect, Some(atlas_size))
|
||||||
|
}
|
||||||
|
None => (
|
||||||
|
Rect {
|
||||||
|
min: Vec2::ZERO,
|
||||||
|
max: uinode.calculated_size,
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
extracted_uinodes.uinodes.insert(
|
||||||
|
commands.spawn_empty().id(),
|
||||||
|
ExtractedUiNode {
|
||||||
|
stack_index: uinode.stack_index,
|
||||||
|
transform: transform.compute_matrix(),
|
||||||
|
color: image.color.into(),
|
||||||
|
rect,
|
||||||
|
clip: clip.map(|clip| clip.clip),
|
||||||
|
image: image.texture.id(),
|
||||||
|
atlas_size,
|
||||||
|
flip_x: image.flip_x,
|
||||||
|
flip_y: image.flip_y,
|
||||||
|
camera_entity,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn resolve_border_thickness(value: Val, parent_width: f32, viewport_size: Vec2) -> f32 {
|
pub(crate) fn resolve_border_thickness(value: Val, parent_width: f32, viewport_size: Vec2) -> f32 {
|
||||||
match value {
|
match value {
|
||||||
Val::Auto => 0.,
|
Val::Auto => 0.,
|
||||||
@ -171,12 +313,12 @@ pub fn extract_uinode_borders(
|
|||||||
(
|
(
|
||||||
&Node,
|
&Node,
|
||||||
&GlobalTransform,
|
&GlobalTransform,
|
||||||
&Style,
|
|
||||||
&BorderColor,
|
|
||||||
Option<&Parent>,
|
|
||||||
&ViewVisibility,
|
&ViewVisibility,
|
||||||
Option<&CalculatedClip>,
|
Option<&CalculatedClip>,
|
||||||
Option<&TargetCamera>,
|
Option<&TargetCamera>,
|
||||||
|
Option<&Parent>,
|
||||||
|
&Style,
|
||||||
|
&BorderColor,
|
||||||
),
|
),
|
||||||
Without<ContentSize>,
|
Without<ContentSize>,
|
||||||
>,
|
>,
|
||||||
@ -185,13 +327,14 @@ pub fn extract_uinode_borders(
|
|||||||
) {
|
) {
|
||||||
let image = AssetId::<Image>::default();
|
let image = AssetId::<Image>::default();
|
||||||
|
|
||||||
for (node, global_transform, style, border_color, parent, view_visibility, clip, camera) in
|
for (node, global_transform, view_visibility, clip, camera, parent, style, border_color) in
|
||||||
&uinode_query
|
&uinode_query
|
||||||
{
|
{
|
||||||
let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get())
|
let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get())
|
||||||
else {
|
else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Skip invisible borders
|
// Skip invisible borders
|
||||||
if !view_visibility.get()
|
if !view_visibility.get()
|
||||||
|| border_color.0.is_fully_transparent()
|
|| border_color.0.is_fully_transparent()
|
||||||
@ -290,19 +433,20 @@ pub fn extract_uinode_outlines(
|
|||||||
Query<(
|
Query<(
|
||||||
&Node,
|
&Node,
|
||||||
&GlobalTransform,
|
&GlobalTransform,
|
||||||
&Outline,
|
|
||||||
&ViewVisibility,
|
&ViewVisibility,
|
||||||
Option<&CalculatedClip>,
|
Option<&CalculatedClip>,
|
||||||
Option<&TargetCamera>,
|
Option<&TargetCamera>,
|
||||||
|
&Outline,
|
||||||
)>,
|
)>,
|
||||||
>,
|
>,
|
||||||
) {
|
) {
|
||||||
let image = AssetId::<Image>::default();
|
let image = AssetId::<Image>::default();
|
||||||
for (node, global_transform, outline, view_visibility, maybe_clip, camera) in &uinode_query {
|
for (node, global_transform, view_visibility, maybe_clip, camera, outline) in &uinode_query {
|
||||||
let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get())
|
let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get())
|
||||||
else {
|
else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Skip invisible outlines
|
// Skip invisible outlines
|
||||||
if !view_visibility.get()
|
if !view_visibility.get()
|
||||||
|| outline.color.is_fully_transparent()
|
|| outline.color.is_fully_transparent()
|
||||||
@ -373,104 +517,6 @@ pub fn extract_uinode_outlines(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_uinodes(
|
|
||||||
mut commands: Commands,
|
|
||||||
mut extracted_uinodes: ResMut<ExtractedUiNodes>,
|
|
||||||
texture_atlases: Extract<Res<Assets<TextureAtlasLayout>>>,
|
|
||||||
default_ui_camera: Extract<DefaultUiCamera>,
|
|
||||||
uinode_query: Extract<
|
|
||||||
Query<(
|
|
||||||
Entity,
|
|
||||||
&Node,
|
|
||||||
&GlobalTransform,
|
|
||||||
&BackgroundColor,
|
|
||||||
Option<&UiImage>,
|
|
||||||
&ViewVisibility,
|
|
||||||
Option<&CalculatedClip>,
|
|
||||||
Option<&TextureAtlas>,
|
|
||||||
Option<&TargetCamera>,
|
|
||||||
Option<&ComputedTextureSlices>,
|
|
||||||
)>,
|
|
||||||
>,
|
|
||||||
) {
|
|
||||||
for (
|
|
||||||
entity,
|
|
||||||
uinode,
|
|
||||||
transform,
|
|
||||||
color,
|
|
||||||
maybe_image,
|
|
||||||
view_visibility,
|
|
||||||
clip,
|
|
||||||
atlas,
|
|
||||||
camera,
|
|
||||||
slices,
|
|
||||||
) in uinode_query.iter()
|
|
||||||
{
|
|
||||||
let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get())
|
|
||||||
else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
// Skip invisible and completely transparent nodes
|
|
||||||
if !view_visibility.get() || color.0.is_fully_transparent() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some((image, slices)) = maybe_image.zip(slices) {
|
|
||||||
extracted_uinodes.uinodes.extend(
|
|
||||||
slices
|
|
||||||
.extract_ui_nodes(transform, uinode, color, image, clip, camera_entity)
|
|
||||||
.map(|e| (commands.spawn_empty().id(), e)),
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (image, flip_x, flip_y) = if let Some(image) = maybe_image {
|
|
||||||
(image.texture.id(), image.flip_x, image.flip_y)
|
|
||||||
} else {
|
|
||||||
(AssetId::default(), false, false)
|
|
||||||
};
|
|
||||||
|
|
||||||
let (rect, atlas_size) = match atlas {
|
|
||||||
Some(atlas) => {
|
|
||||||
let Some(layout) = texture_atlases.get(&atlas.layout) else {
|
|
||||||
// Atlas not present in assets resource (should this warn the user?)
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let mut atlas_rect = layout.textures[atlas.index].as_rect();
|
|
||||||
let mut atlas_size = layout.size.as_vec2();
|
|
||||||
let scale = uinode.size() / atlas_rect.size();
|
|
||||||
atlas_rect.min *= scale;
|
|
||||||
atlas_rect.max *= scale;
|
|
||||||
atlas_size *= scale;
|
|
||||||
(atlas_rect, Some(atlas_size))
|
|
||||||
}
|
|
||||||
None => (
|
|
||||||
Rect {
|
|
||||||
min: Vec2::ZERO,
|
|
||||||
max: uinode.calculated_size,
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
extracted_uinodes.uinodes.insert(
|
|
||||||
entity,
|
|
||||||
ExtractedUiNode {
|
|
||||||
stack_index: uinode.stack_index,
|
|
||||||
transform: transform.compute_matrix(),
|
|
||||||
color: color.0.into(),
|
|
||||||
rect,
|
|
||||||
clip: clip.map(|clip| clip.clip),
|
|
||||||
image,
|
|
||||||
atlas_size,
|
|
||||||
flip_x,
|
|
||||||
flip_y,
|
|
||||||
camera_entity,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The UI camera is "moved back" by this many units (plus the [`UI_CAMERA_TRANSFORM_OFFSET`]) and also has a view
|
/// The UI camera is "moved back" by this many units (plus the [`UI_CAMERA_TRANSFORM_OFFSET`]) and also has a view
|
||||||
/// distance of this many units. This ensures that with a left-handed projection,
|
/// distance of this many units. This ensures that with a left-handed projection,
|
||||||
/// as ui elements are "stacked on top of each other", they are within the camera's view
|
/// as ui elements are "stacked on top of each other", they are within the camera's view
|
||||||
@ -546,7 +592,7 @@ pub fn extract_default_ui_camera_view<T: Component>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "bevy_text")]
|
#[cfg(feature = "bevy_text")]
|
||||||
pub fn extract_text_uinodes(
|
pub fn extract_uinode_text(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut extracted_uinodes: ResMut<ExtractedUiNodes>,
|
mut extracted_uinodes: ResMut<ExtractedUiNodes>,
|
||||||
camera_query: Extract<Query<(Entity, &Camera)>>,
|
camera_query: Extract<Query<(Entity, &Camera)>>,
|
||||||
@ -557,21 +603,22 @@ pub fn extract_text_uinodes(
|
|||||||
Query<(
|
Query<(
|
||||||
&Node,
|
&Node,
|
||||||
&GlobalTransform,
|
&GlobalTransform,
|
||||||
&Text,
|
|
||||||
&TextLayoutInfo,
|
|
||||||
&ViewVisibility,
|
&ViewVisibility,
|
||||||
Option<&CalculatedClip>,
|
Option<&CalculatedClip>,
|
||||||
Option<&TargetCamera>,
|
Option<&TargetCamera>,
|
||||||
|
&Text,
|
||||||
|
&TextLayoutInfo,
|
||||||
)>,
|
)>,
|
||||||
>,
|
>,
|
||||||
) {
|
) {
|
||||||
for (uinode, global_transform, text, text_layout_info, view_visibility, clip, camera) in
|
for (uinode, global_transform, view_visibility, clip, camera, text, text_layout_info) in
|
||||||
uinode_query.iter()
|
&uinode_query
|
||||||
{
|
{
|
||||||
let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get())
|
let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get())
|
||||||
else {
|
else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Skip if not visible or if size is set to zero (e.g. when a parent is set to `Display::None`)
|
// Skip if not visible or if size is set to zero (e.g. when a parent is set to `Display::None`)
|
||||||
if !view_visibility.get() || uinode.size().x == 0. || uinode.size().y == 0. {
|
if !view_visibility.get() || uinode.size().x == 0. || uinode.size().y == 0. {
|
||||||
continue;
|
continue;
|
||||||
|
@ -74,7 +74,7 @@ where
|
|||||||
ExtractSchedule,
|
ExtractSchedule,
|
||||||
(
|
(
|
||||||
extract_ui_materials::<M>,
|
extract_ui_materials::<M>,
|
||||||
extract_ui_material_nodes::<M>.in_set(RenderUiSystem::ExtractNode),
|
extract_ui_material_nodes::<M>.in_set(RenderUiSystem::ExtractBackgrounds),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
|
@ -10,7 +10,7 @@ use bevy_sprite::{ImageScaleMode, TextureSlice};
|
|||||||
use bevy_transform::prelude::*;
|
use bevy_transform::prelude::*;
|
||||||
use bevy_utils::HashSet;
|
use bevy_utils::HashSet;
|
||||||
|
|
||||||
use crate::{BackgroundColor, CalculatedClip, ExtractedUiNode, Node, UiImage};
|
use crate::{CalculatedClip, ExtractedUiNode, Node, UiImage};
|
||||||
|
|
||||||
/// Component storing texture slices for image nodes entities with a tiled or sliced [`ImageScaleMode`]
|
/// Component storing texture slices for image nodes entities with a tiled or sliced [`ImageScaleMode`]
|
||||||
///
|
///
|
||||||
@ -35,7 +35,6 @@ impl ComputedTextureSlices {
|
|||||||
&'a self,
|
&'a self,
|
||||||
transform: &'a GlobalTransform,
|
transform: &'a GlobalTransform,
|
||||||
node: &'a Node,
|
node: &'a Node,
|
||||||
background_color: &'a BackgroundColor,
|
|
||||||
image: &'a UiImage,
|
image: &'a UiImage,
|
||||||
clip: Option<&'a CalculatedClip>,
|
clip: Option<&'a CalculatedClip>,
|
||||||
camera_entity: Entity,
|
camera_entity: Entity,
|
||||||
@ -60,7 +59,7 @@ impl ComputedTextureSlices {
|
|||||||
let atlas_size = Some(self.image_size * scale);
|
let atlas_size = Some(self.image_size * scale);
|
||||||
ExtractedUiNode {
|
ExtractedUiNode {
|
||||||
stack_index: node.stack_index,
|
stack_index: node.stack_index,
|
||||||
color: background_color.0.into(),
|
color: image.color.into(),
|
||||||
transform: transform.compute_matrix(),
|
transform: transform.compute_matrix(),
|
||||||
rect,
|
rect,
|
||||||
flip_x,
|
flip_x,
|
||||||
|
@ -1589,7 +1589,6 @@ pub enum GridPlacementError {
|
|||||||
/// The background color of the node
|
/// The background color of the node
|
||||||
///
|
///
|
||||||
/// This serves as the "fill" color.
|
/// This serves as the "fill" color.
|
||||||
/// When combined with [`UiImage`], tints the provided texture.
|
|
||||||
#[derive(Component, Copy, Clone, Debug, Reflect)]
|
#[derive(Component, Copy, Clone, Debug, Reflect)]
|
||||||
#[reflect(Component, Default)]
|
#[reflect(Component, Default)]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
@ -1729,6 +1728,8 @@ impl Outline {
|
|||||||
#[derive(Component, Clone, Debug, Reflect, Default)]
|
#[derive(Component, Clone, Debug, Reflect, Default)]
|
||||||
#[reflect(Component, Default)]
|
#[reflect(Component, Default)]
|
||||||
pub struct UiImage {
|
pub struct UiImage {
|
||||||
|
/// The tint color used to draw the image
|
||||||
|
pub color: Color,
|
||||||
/// Handle to the texture
|
/// Handle to the texture
|
||||||
pub texture: Handle<Image>,
|
pub texture: Handle<Image>,
|
||||||
/// Whether the image should be flipped along its x-axis
|
/// Whether the image should be flipped along its x-axis
|
||||||
@ -1745,6 +1746,13 @@ impl UiImage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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
|
/// Flip the image along its x-axis
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn with_flip_x(mut self) -> Self {
|
pub const fn with_flip_x(mut self) -> Self {
|
||||||
|
@ -147,7 +147,7 @@ fn setup(
|
|||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
border_color: Color::WHITE.into(),
|
border_color: Color::WHITE.into(),
|
||||||
background_color: DARK_GRAY.into(),
|
image: UiImage::default().with_color(DARK_GRAY.into()),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
|
@ -74,7 +74,7 @@ fn setup_menu(mut commands: Commands) {
|
|||||||
align_items: AlignItems::Center,
|
align_items: AlignItems::Center,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
background_color: NORMAL_BUTTON.into(),
|
image: UiImage::default().with_color(NORMAL_BUTTON),
|
||||||
..default()
|
..default()
|
||||||
})
|
})
|
||||||
.with_children(|parent| {
|
.with_children(|parent| {
|
||||||
@ -95,21 +95,21 @@ fn setup_menu(mut commands: Commands) {
|
|||||||
fn menu(
|
fn menu(
|
||||||
mut next_state: ResMut<NextState<AppState>>,
|
mut next_state: ResMut<NextState<AppState>>,
|
||||||
mut interaction_query: Query<
|
mut interaction_query: Query<
|
||||||
(&Interaction, &mut BackgroundColor),
|
(&Interaction, &mut UiImage),
|
||||||
(Changed<Interaction>, With<Button>),
|
(Changed<Interaction>, With<Button>),
|
||||||
>,
|
>,
|
||||||
) {
|
) {
|
||||||
for (interaction, mut color) in &mut interaction_query {
|
for (interaction, mut image) in &mut interaction_query {
|
||||||
match *interaction {
|
match *interaction {
|
||||||
Interaction::Pressed => {
|
Interaction::Pressed => {
|
||||||
*color = PRESSED_BUTTON.into();
|
image.color = PRESSED_BUTTON;
|
||||||
next_state.set(AppState::InGame);
|
next_state.set(AppState::InGame);
|
||||||
}
|
}
|
||||||
Interaction::Hovered => {
|
Interaction::Hovered => {
|
||||||
*color = HOVERED_BUTTON.into();
|
image.color = HOVERED_BUTTON;
|
||||||
}
|
}
|
||||||
Interaction::None => {
|
Interaction::None => {
|
||||||
*color = NORMAL_BUTTON.into();
|
image.color = NORMAL_BUTTON;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,16 +345,16 @@ mod menu {
|
|||||||
// This system handles changing all buttons color based on mouse interaction
|
// This system handles changing all buttons color based on mouse interaction
|
||||||
fn button_system(
|
fn button_system(
|
||||||
mut interaction_query: Query<
|
mut interaction_query: Query<
|
||||||
(&Interaction, &mut BackgroundColor, Option<&SelectedOption>),
|
(&Interaction, &mut UiImage, Option<&SelectedOption>),
|
||||||
(Changed<Interaction>, With<Button>),
|
(Changed<Interaction>, With<Button>),
|
||||||
>,
|
>,
|
||||||
) {
|
) {
|
||||||
for (interaction, mut color, selected) in &mut interaction_query {
|
for (interaction, mut image, selected) in &mut interaction_query {
|
||||||
*color = match (*interaction, selected) {
|
image.color = match (*interaction, selected) {
|
||||||
(Interaction::Pressed, _) | (Interaction::None, Some(_)) => PRESSED_BUTTON.into(),
|
(Interaction::Pressed, _) | (Interaction::None, Some(_)) => PRESSED_BUTTON,
|
||||||
(Interaction::Hovered, Some(_)) => HOVERED_PRESSED_BUTTON.into(),
|
(Interaction::Hovered, Some(_)) => HOVERED_PRESSED_BUTTON,
|
||||||
(Interaction::Hovered, None) => HOVERED_BUTTON.into(),
|
(Interaction::Hovered, None) => HOVERED_BUTTON,
|
||||||
(Interaction::None, None) => NORMAL_BUTTON.into(),
|
(Interaction::None, None) => NORMAL_BUTTON,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -363,14 +363,14 @@ mod menu {
|
|||||||
// the button as the one currently selected
|
// the button as the one currently selected
|
||||||
fn setting_button<T: Resource + Component + PartialEq + Copy>(
|
fn setting_button<T: Resource + Component + PartialEq + Copy>(
|
||||||
interaction_query: Query<(&Interaction, &T, Entity), (Changed<Interaction>, With<Button>)>,
|
interaction_query: Query<(&Interaction, &T, Entity), (Changed<Interaction>, With<Button>)>,
|
||||||
mut selected_query: Query<(Entity, &mut BackgroundColor), With<SelectedOption>>,
|
mut selected_query: Query<(Entity, &mut UiImage), With<SelectedOption>>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut setting: ResMut<T>,
|
mut setting: ResMut<T>,
|
||||||
) {
|
) {
|
||||||
for (interaction, button_setting, entity) in &interaction_query {
|
for (interaction, button_setting, entity) in &interaction_query {
|
||||||
if *interaction == Interaction::Pressed && *setting != *button_setting {
|
if *interaction == Interaction::Pressed && *setting != *button_setting {
|
||||||
let (previous_button, mut previous_color) = selected_query.single_mut();
|
let (previous_button, mut previous_image) = selected_query.single_mut();
|
||||||
*previous_color = NORMAL_BUTTON.into();
|
previous_image.color = NORMAL_BUTTON;
|
||||||
commands.entity(previous_button).remove::<SelectedOption>();
|
commands.entity(previous_button).remove::<SelectedOption>();
|
||||||
commands.entity(entity).insert(SelectedOption);
|
commands.entity(entity).insert(SelectedOption);
|
||||||
*setting = *button_setting;
|
*setting = *button_setting;
|
||||||
@ -456,7 +456,7 @@ mod menu {
|
|||||||
.spawn((
|
.spawn((
|
||||||
ButtonBundle {
|
ButtonBundle {
|
||||||
style: button_style.clone(),
|
style: button_style.clone(),
|
||||||
background_color: NORMAL_BUTTON.into(),
|
image: UiImage::default().with_color(NORMAL_BUTTON),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
MenuButtonAction::Play,
|
MenuButtonAction::Play,
|
||||||
@ -477,7 +477,7 @@ mod menu {
|
|||||||
.spawn((
|
.spawn((
|
||||||
ButtonBundle {
|
ButtonBundle {
|
||||||
style: button_style.clone(),
|
style: button_style.clone(),
|
||||||
background_color: NORMAL_BUTTON.into(),
|
image: UiImage::default().with_color(NORMAL_BUTTON),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
MenuButtonAction::Settings,
|
MenuButtonAction::Settings,
|
||||||
@ -498,7 +498,7 @@ mod menu {
|
|||||||
.spawn((
|
.spawn((
|
||||||
ButtonBundle {
|
ButtonBundle {
|
||||||
style: button_style,
|
style: button_style,
|
||||||
background_color: NORMAL_BUTTON.into(),
|
image: UiImage::default().with_color(NORMAL_BUTTON),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
MenuButtonAction::Quit,
|
MenuButtonAction::Quit,
|
||||||
@ -567,7 +567,7 @@ mod menu {
|
|||||||
.spawn((
|
.spawn((
|
||||||
ButtonBundle {
|
ButtonBundle {
|
||||||
style: button_style.clone(),
|
style: button_style.clone(),
|
||||||
background_color: NORMAL_BUTTON.into(),
|
image: UiImage::default().with_color(NORMAL_BUTTON),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
action,
|
action,
|
||||||
@ -654,7 +654,7 @@ mod menu {
|
|||||||
height: Val::Px(65.0),
|
height: Val::Px(65.0),
|
||||||
..button_style.clone()
|
..button_style.clone()
|
||||||
},
|
},
|
||||||
background_color: NORMAL_BUTTON.into(),
|
image: UiImage::default().with_color(NORMAL_BUTTON),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
quality_setting,
|
quality_setting,
|
||||||
@ -675,7 +675,7 @@ mod menu {
|
|||||||
.spawn((
|
.spawn((
|
||||||
ButtonBundle {
|
ButtonBundle {
|
||||||
style: button_style,
|
style: button_style,
|
||||||
background_color: NORMAL_BUTTON.into(),
|
image: UiImage::default().with_color(NORMAL_BUTTON),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
MenuButtonAction::BackToSettings,
|
MenuButtonAction::BackToSettings,
|
||||||
@ -750,7 +750,7 @@ mod menu {
|
|||||||
height: Val::Px(65.0),
|
height: Val::Px(65.0),
|
||||||
..button_style.clone()
|
..button_style.clone()
|
||||||
},
|
},
|
||||||
background_color: NORMAL_BUTTON.into(),
|
image: UiImage::default().with_color(NORMAL_BUTTON),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
Volume(volume_setting),
|
Volume(volume_setting),
|
||||||
@ -764,7 +764,7 @@ mod menu {
|
|||||||
.spawn((
|
.spawn((
|
||||||
ButtonBundle {
|
ButtonBundle {
|
||||||
style: button_style,
|
style: button_style,
|
||||||
background_color: NORMAL_BUTTON.into(),
|
image: UiImage::default().with_color(NORMAL_BUTTON),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
MenuButtonAction::BackToSettings,
|
MenuButtonAction::BackToSettings,
|
||||||
|
@ -97,18 +97,15 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct IdleColor(BackgroundColor);
|
struct IdleColor(Color);
|
||||||
|
|
||||||
fn button_system(
|
fn button_system(
|
||||||
mut interaction_query: Query<
|
mut interaction_query: Query<(&Interaction, &mut UiImage, &IdleColor), Changed<Interaction>>,
|
||||||
(&Interaction, &mut BackgroundColor, &IdleColor),
|
|
||||||
Changed<Interaction>,
|
|
||||||
>,
|
|
||||||
) {
|
) {
|
||||||
for (interaction, mut button_color, IdleColor(idle_color)) in interaction_query.iter_mut() {
|
for (interaction, mut image, &IdleColor(idle_color)) in interaction_query.iter_mut() {
|
||||||
*button_color = match interaction {
|
image.color = match interaction {
|
||||||
Interaction::Hovered => ORANGE_RED.into(),
|
Interaction::Hovered => ORANGE_RED.into(),
|
||||||
_ => *idle_color,
|
_ => idle_color,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,7 +145,7 @@ fn setup_flex(mut commands: Commands, asset_server: Res<AssetServer>, args: Res<
|
|||||||
.spawn(NodeBundle::default())
|
.spawn(NodeBundle::default())
|
||||||
.with_children(|commands| {
|
.with_children(|commands| {
|
||||||
for row in 0..args.buttons {
|
for row in 0..args.buttons {
|
||||||
let color = as_rainbow(row % column.max(1)).into();
|
let color = as_rainbow(row % column.max(1));
|
||||||
let border_color = Color::WHITE.with_alpha(0.5).into();
|
let border_color = Color::WHITE.with_alpha(0.5).into();
|
||||||
spawn_button(
|
spawn_button(
|
||||||
commands,
|
commands,
|
||||||
@ -202,7 +199,7 @@ fn setup_grid(mut commands: Commands, asset_server: Res<AssetServer>, args: Res<
|
|||||||
.with_children(|commands| {
|
.with_children(|commands| {
|
||||||
for column in 0..args.buttons {
|
for column in 0..args.buttons {
|
||||||
for row in 0..args.buttons {
|
for row in 0..args.buttons {
|
||||||
let color = as_rainbow(row % column.max(1)).into();
|
let color = as_rainbow(row % column.max(1));
|
||||||
let border_color = Color::WHITE.with_alpha(0.5).into();
|
let border_color = Color::WHITE.with_alpha(0.5).into();
|
||||||
spawn_button(
|
spawn_button(
|
||||||
commands,
|
commands,
|
||||||
@ -226,7 +223,7 @@ fn setup_grid(mut commands: Commands, asset_server: Res<AssetServer>, args: Res<
|
|||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn spawn_button(
|
fn spawn_button(
|
||||||
commands: &mut ChildBuilder,
|
commands: &mut ChildBuilder,
|
||||||
background_color: BackgroundColor,
|
background_color: Color,
|
||||||
buttons: f32,
|
buttons: f32,
|
||||||
column: usize,
|
column: usize,
|
||||||
row: usize,
|
row: usize,
|
||||||
@ -249,7 +246,7 @@ fn spawn_button(
|
|||||||
border,
|
border,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
background_color,
|
image: UiImage::default().with_color(background_color),
|
||||||
border_color,
|
border_color,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
|
@ -19,33 +19,28 @@ const PRESSED_BUTTON: Color = Color::srgb(0.35, 0.75, 0.35);
|
|||||||
|
|
||||||
fn button_system(
|
fn button_system(
|
||||||
mut interaction_query: Query<
|
mut interaction_query: Query<
|
||||||
(
|
(&Interaction, &mut UiImage, &mut BorderColor, &Children),
|
||||||
&Interaction,
|
|
||||||
&mut BackgroundColor,
|
|
||||||
&mut BorderColor,
|
|
||||||
&Children,
|
|
||||||
),
|
|
||||||
(Changed<Interaction>, With<Button>),
|
(Changed<Interaction>, With<Button>),
|
||||||
>,
|
>,
|
||||||
mut text_query: Query<&mut Text>,
|
mut text_query: Query<&mut Text>,
|
||||||
) {
|
) {
|
||||||
for (interaction, mut color, mut border_color, children) in &mut interaction_query {
|
for (interaction, mut image, mut border_color, children) in &mut interaction_query {
|
||||||
let mut text = text_query.get_mut(children[0]).unwrap();
|
let mut text = text_query.get_mut(children[0]).unwrap();
|
||||||
match *interaction {
|
match *interaction {
|
||||||
Interaction::Pressed => {
|
Interaction::Pressed => {
|
||||||
text.sections[0].value = "Press".to_string();
|
text.sections[0].value = "Press".to_string();
|
||||||
*color = PRESSED_BUTTON.into();
|
image.color = PRESSED_BUTTON;
|
||||||
border_color.0 = RED.into();
|
border_color.0 = RED.into();
|
||||||
}
|
}
|
||||||
Interaction::Hovered => {
|
Interaction::Hovered => {
|
||||||
text.sections[0].value = "Hover".to_string();
|
text.sections[0].value = "Hover".to_string();
|
||||||
*color = HOVERED_BUTTON.into();
|
image.color = HOVERED_BUTTON;
|
||||||
border_color.0 = WHITE.into();
|
border_color.0 = Color::WHITE;
|
||||||
}
|
}
|
||||||
Interaction::None => {
|
Interaction::None => {
|
||||||
text.sections[0].value = "Button".to_string();
|
text.sections[0].value = "Button".to_string();
|
||||||
*color = NORMAL_BUTTON.into();
|
image.color = NORMAL_BUTTON;
|
||||||
border_color.0 = BLACK.into();
|
border_color.0 = Color::BLACK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,7 +74,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
border_color: BorderColor(Color::BLACK),
|
border_color: BorderColor(Color::BLACK),
|
||||||
background_color: NORMAL_BUTTON.into(),
|
image: UiImage::default().with_color(NORMAL_BUTTON),
|
||||||
..default()
|
..default()
|
||||||
})
|
})
|
||||||
.with_children(|parent| {
|
.with_children(|parent| {
|
||||||
|
@ -417,7 +417,7 @@ where
|
|||||||
padding: UiRect::axes(Val::Px(5.), Val::Px(1.)),
|
padding: UiRect::axes(Val::Px(5.), Val::Px(1.)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
background_color: BackgroundColor(Color::BLACK.with_alpha(0.5)),
|
image: UiImage::default().with_color(Color::BLACK.with_alpha(0.5)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Target::<T>::new(target),
|
Target::<T>::new(target),
|
||||||
@ -461,13 +461,13 @@ fn buttons_handler<T>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn text_hover(
|
fn text_hover(
|
||||||
mut button_query: Query<(&Interaction, &mut BackgroundColor, &Children), Changed<Interaction>>,
|
mut button_query: Query<(&Interaction, &mut UiImage, &Children), Changed<Interaction>>,
|
||||||
mut text_query: Query<&mut Text>,
|
mut text_query: Query<&mut Text>,
|
||||||
) {
|
) {
|
||||||
for (interaction, mut background_color, children) in button_query.iter_mut() {
|
for (interaction, mut image, children) in button_query.iter_mut() {
|
||||||
match interaction {
|
match interaction {
|
||||||
Interaction::Hovered => {
|
Interaction::Hovered => {
|
||||||
*background_color = BackgroundColor(Color::BLACK.with_alpha(0.6));
|
image.color = Color::BLACK.with_alpha(0.6);
|
||||||
for &child in children {
|
for &child in children {
|
||||||
if let Ok(mut text) = text_query.get_mut(child) {
|
if let Ok(mut text) = text_query.get_mut(child) {
|
||||||
// Bypass change detection to avoid recomputation of the text when only changing the color
|
// Bypass change detection to avoid recomputation of the text when only changing the color
|
||||||
@ -476,7 +476,7 @@ fn text_hover(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
*background_color = BackgroundColor(Color::BLACK.with_alpha(0.5));
|
image.color = Color::BLACK.with_alpha(0.5);
|
||||||
for &child in children {
|
for &child in children {
|
||||||
if let Ok(mut text) = text_query.get_mut(child) {
|
if let Ok(mut text) = text_query.get_mut(child) {
|
||||||
text.bypass_change_detection().sections[0].style.color =
|
text.bypass_change_detection().sections[0].style.color =
|
||||||
|
@ -95,8 +95,6 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||||||
min_height: Val::Px(100.),
|
min_height: Val::Px(100.),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
background_color: Color::WHITE.into(),
|
|
||||||
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Interaction::default(),
|
Interaction::default(),
|
||||||
|
@ -245,12 +245,11 @@ fn spawn_button(
|
|||||||
margin: UiRect::horizontal(Val::Px(2.)),
|
margin: UiRect::horizontal(Val::Px(2.)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
background_color: if active {
|
image: UiImage::default().with_color(if active {
|
||||||
ACTIVE_BORDER_COLOR
|
ACTIVE_BORDER_COLOR
|
||||||
} else {
|
} else {
|
||||||
INACTIVE_BORDER_COLOR
|
INACTIVE_BORDER_COLOR
|
||||||
}
|
}),
|
||||||
.into(),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
constraint,
|
constraint,
|
||||||
@ -359,6 +358,7 @@ fn update_buttons(
|
|||||||
fn update_radio_buttons_colors(
|
fn update_radio_buttons_colors(
|
||||||
mut event_reader: EventReader<ButtonActivatedEvent>,
|
mut event_reader: EventReader<ButtonActivatedEvent>,
|
||||||
button_query: Query<(Entity, &Constraint, &Interaction)>,
|
button_query: Query<(Entity, &Constraint, &Interaction)>,
|
||||||
|
mut image_query: Query<&mut UiImage>,
|
||||||
mut color_query: Query<&mut BackgroundColor>,
|
mut color_query: Query<&mut BackgroundColor>,
|
||||||
mut text_query: Query<&mut Text>,
|
mut text_query: Query<&mut Text>,
|
||||||
children_query: Query<&Children>,
|
children_query: Query<&Children>,
|
||||||
@ -381,16 +381,12 @@ fn update_radio_buttons_colors(
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
color_query.get_mut(id).unwrap().0 = border_color;
|
image_query.get_mut(id).unwrap().color = border_color;
|
||||||
if let Ok(children) = children_query.get(id) {
|
for &child in children_query.get(id).into_iter().flatten() {
|
||||||
for &child in children {
|
color_query.get_mut(child).unwrap().0 = inner_color;
|
||||||
color_query.get_mut(child).unwrap().0 = inner_color;
|
for &grandchild in children_query.get(child).into_iter().flatten() {
|
||||||
if let Ok(grand_children) = children_query.get(child) {
|
if let Ok(mut text) = text_query.get_mut(grandchild) {
|
||||||
for &grandchild in grand_children {
|
text.sections[0].style.color = text_color;
|
||||||
if let Ok(mut text) = text_query.get_mut(grandchild) {
|
|
||||||
text.sections[0].style.color = text_color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||||||
align_items: AlignItems::Center,
|
align_items: AlignItems::Center,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
background_color: Color::srgb(0.1, 0.5, 0.1).into(),
|
image: UiImage::default().with_color(Color::srgb(0.1, 0.5, 0.1)),
|
||||||
..default()
|
..default()
|
||||||
})
|
})
|
||||||
.with_children(|parent| {
|
.with_children(|parent| {
|
||||||
@ -63,7 +63,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||||||
align_items: AlignItems::Center,
|
align_items: AlignItems::Center,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
background_color: Color::srgb(0.5, 0.1, 0.5).into(),
|
image: UiImage::default().with_color(Color::srgb(0.5, 0.1, 0.5)),
|
||||||
..default()
|
..default()
|
||||||
})
|
})
|
||||||
.with_children(|parent| {
|
.with_children(|parent| {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! This example illustrates how to use `TextureAtlases` within ui
|
//! This example illustrates how to use `TextureAtlases` within ui
|
||||||
|
|
||||||
use bevy::{color::palettes::basic::YELLOW, prelude::*, winit::WinitSettings};
|
use bevy::{color::palettes::css::*, prelude::*, winit::WinitSettings};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
@ -60,6 +60,8 @@ fn setup(
|
|||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
TextureAtlas::from(texture_atlas_handle),
|
TextureAtlas::from(texture_atlas_handle),
|
||||||
|
BackgroundColor(ANTIQUE_WHITE.into()),
|
||||||
|
Outline::new(Val::Px(8.0), Val::ZERO, CRIMSON.into()),
|
||||||
));
|
));
|
||||||
parent.spawn(TextBundle::from_sections([
|
parent.spawn(TextBundle::from_sections([
|
||||||
TextSection::new("press ".to_string(), text_style.clone()),
|
TextSection::new("press ".to_string(), text_style.clone()),
|
||||||
|
@ -19,25 +19,25 @@ fn main() {
|
|||||||
|
|
||||||
fn button_system(
|
fn button_system(
|
||||||
mut interaction_query: Query<
|
mut interaction_query: Query<
|
||||||
(&Interaction, &Children, &mut BackgroundColor),
|
(&Interaction, &Children, &mut UiImage),
|
||||||
(Changed<Interaction>, With<Button>),
|
(Changed<Interaction>, With<Button>),
|
||||||
>,
|
>,
|
||||||
mut text_query: Query<&mut Text>,
|
mut text_query: Query<&mut Text>,
|
||||||
) {
|
) {
|
||||||
for (interaction, children, mut color) in &mut interaction_query {
|
for (interaction, children, mut image) in &mut interaction_query {
|
||||||
let mut text = text_query.get_mut(children[0]).unwrap();
|
let mut text = text_query.get_mut(children[0]).unwrap();
|
||||||
match *interaction {
|
match *interaction {
|
||||||
Interaction::Pressed => {
|
Interaction::Pressed => {
|
||||||
text.sections[0].value = "Press".to_string();
|
text.sections[0].value = "Press".to_string();
|
||||||
color.0 = GOLD.into();
|
image.color = GOLD.into();
|
||||||
}
|
}
|
||||||
Interaction::Hovered => {
|
Interaction::Hovered => {
|
||||||
text.sections[0].value = "Hover".to_string();
|
text.sections[0].value = "Hover".to_string();
|
||||||
color.0 = ORANGE.into();
|
image.color = ORANGE.into();
|
||||||
}
|
}
|
||||||
Interaction::None => {
|
Interaction::None => {
|
||||||
text.sections[0].value = "Button".to_string();
|
text.sections[0].value = "Button".to_string();
|
||||||
color.0 = Color::WHITE;
|
image.color = Color::WHITE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,8 +81,6 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
image: image.clone().into(),
|
image: image.clone().into(),
|
||||||
// When combined with an image, this tints the image.
|
|
||||||
background_color: Color::WHITE.into(),
|
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
ImageScaleMode::Sliced(slicer.clone()),
|
ImageScaleMode::Sliced(slicer.clone()),
|
||||||
|
Loading…
Reference in New Issue
Block a user