separate border colors (#18682)
# Objective allow specifying the left/top/right/bottom border colors separately for ui elements fixes #14773 ## Solution - change `BorderColor` to ```rs pub struct BorderColor { pub left: Color, pub top: Color, pub right: Color, pub bottom: Color, } ``` - generate one ui node per distinct border color, set flags for the active borders - render only the active borders i chose to do this rather than adding multiple colors to the ExtractedUiNode in order to minimize the impact for the common case where all border colors are the same. ## Testing modified the `borders` example to use separate colors:  the behaviour is a bit weird but it mirrors html/css border behaviour. --- ## Migration: To keep the existing behaviour, just change `BorderColor(color)` into `BorderColor::all(color)`. --------- Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
This commit is contained in:
parent
fb2d79ad60
commit
b641aa0ecf
@ -1,3 +1,4 @@
|
||||
use crate::shader_flags;
|
||||
use crate::ui_node::ComputedNodeTarget;
|
||||
use crate::CalculatedClip;
|
||||
use crate::ComputedNode;
|
||||
@ -106,7 +107,7 @@ pub fn extract_debug_overlay(
|
||||
flip_y: false,
|
||||
border: BorderRect::all(debug_options.line_width / uinode.inverse_scale_factor()),
|
||||
border_radius: uinode.border_radius(),
|
||||
node_type: NodeType::Border,
|
||||
node_type: NodeType::Border(shader_flags::BORDER_ALL),
|
||||
},
|
||||
main_entity: entity.into(),
|
||||
});
|
||||
|
@ -32,6 +32,8 @@ use bevy_sprite::BorderRect;
|
||||
use bevy_transform::prelude::GlobalTransform;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
use super::shader_flags::BORDER_ALL;
|
||||
|
||||
pub const UI_GRADIENT_SHADER_HANDLE: Handle<Shader> =
|
||||
weak_handle!("10116113-aac4-47fa-91c8-35cbe80dddcb");
|
||||
|
||||
@ -388,7 +390,7 @@ pub fn extract_gradients(
|
||||
|
||||
for (gradients, node_type) in [
|
||||
(gradient.map(|g| &g.0), NodeType::Rect),
|
||||
(gradient_border.map(|g| &g.0), NodeType::Border),
|
||||
(gradient_border.map(|g| &g.0), NodeType::Border(BORDER_ALL)),
|
||||
]
|
||||
.iter()
|
||||
.filter_map(|(g, n)| g.map(|g| (g, *n)))
|
||||
@ -742,8 +744,8 @@ pub fn prepare_gradient(
|
||||
|
||||
let uvs = { [Vec2::ZERO, Vec2::X, Vec2::ONE, Vec2::Y] };
|
||||
|
||||
let mut flags = if gradient.node_type == NodeType::Border {
|
||||
shader_flags::BORDER
|
||||
let mut flags = if let NodeType::Border(borders) = gradient.node_type {
|
||||
borders
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
@ -229,7 +229,7 @@ pub struct ExtractedUiNode {
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum NodeType {
|
||||
Rect,
|
||||
Border,
|
||||
Border(u32), // shader flags
|
||||
}
|
||||
|
||||
pub enum ExtractedUiItem {
|
||||
@ -522,30 +522,63 @@ pub fn extract_uinode_borders(
|
||||
|
||||
// Don't extract borders with zero width along all edges
|
||||
if computed_node.border() != BorderRect::ZERO {
|
||||
if let Some(border_color) = maybe_border_color.filter(|bc| !bc.0.is_fully_transparent())
|
||||
{
|
||||
extracted_uinodes.uinodes.push(ExtractedUiNode {
|
||||
stack_index: computed_node.stack_index,
|
||||
color: border_color.0.into(),
|
||||
rect: Rect {
|
||||
max: computed_node.size(),
|
||||
..Default::default()
|
||||
},
|
||||
image,
|
||||
clip: maybe_clip.map(|clip| clip.clip),
|
||||
extracted_camera_entity,
|
||||
item: ExtractedUiItem::Node {
|
||||
atlas_scaling: None,
|
||||
transform: global_transform.compute_matrix(),
|
||||
flip_x: false,
|
||||
flip_y: false,
|
||||
border: computed_node.border(),
|
||||
border_radius: computed_node.border_radius(),
|
||||
node_type: NodeType::Border,
|
||||
},
|
||||
main_entity: entity.into(),
|
||||
render_entity: commands.spawn(TemporaryRenderEntity).id(),
|
||||
});
|
||||
if let Some(border_color) = maybe_border_color {
|
||||
let border_colors = [
|
||||
border_color.left.to_linear(),
|
||||
border_color.top.to_linear(),
|
||||
border_color.right.to_linear(),
|
||||
border_color.bottom.to_linear(),
|
||||
];
|
||||
|
||||
const BORDER_FLAGS: [u32; 4] = [
|
||||
shader_flags::BORDER_LEFT,
|
||||
shader_flags::BORDER_TOP,
|
||||
shader_flags::BORDER_RIGHT,
|
||||
shader_flags::BORDER_BOTTOM,
|
||||
];
|
||||
let mut completed_flags = 0;
|
||||
|
||||
for (i, &color) in border_colors.iter().enumerate() {
|
||||
if color.is_fully_transparent() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut border_flags = BORDER_FLAGS[i];
|
||||
|
||||
if completed_flags & border_flags != 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
for j in i + 1..4 {
|
||||
if color == border_colors[j] {
|
||||
border_flags |= BORDER_FLAGS[j];
|
||||
}
|
||||
}
|
||||
completed_flags |= border_flags;
|
||||
|
||||
extracted_uinodes.uinodes.push(ExtractedUiNode {
|
||||
stack_index: computed_node.stack_index,
|
||||
color,
|
||||
rect: Rect {
|
||||
max: computed_node.size(),
|
||||
..Default::default()
|
||||
},
|
||||
image,
|
||||
clip: maybe_clip.map(|clip| clip.clip),
|
||||
extracted_camera_entity,
|
||||
item: ExtractedUiItem::Node {
|
||||
atlas_scaling: None,
|
||||
transform: global_transform.compute_matrix(),
|
||||
flip_x: false,
|
||||
flip_y: false,
|
||||
border: computed_node.border(),
|
||||
border_radius: computed_node.border_radius(),
|
||||
node_type: NodeType::Border(border_flags),
|
||||
},
|
||||
main_entity: entity.into(),
|
||||
render_entity: commands.spawn(TemporaryRenderEntity).id(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -574,7 +607,7 @@ pub fn extract_uinode_borders(
|
||||
flip_y: false,
|
||||
border: BorderRect::all(computed_node.outline_width()),
|
||||
border_radius: computed_node.outline_radius(),
|
||||
node_type: NodeType::Border,
|
||||
node_type: NodeType::Border(shader_flags::BORDER_ALL),
|
||||
},
|
||||
main_entity: entity.into(),
|
||||
});
|
||||
@ -1081,11 +1114,15 @@ pub mod shader_flags {
|
||||
pub const TEXTURED: u32 = 1;
|
||||
/// Ordering: top left, top right, bottom right, bottom left.
|
||||
pub const CORNERS: [u32; 4] = [0, 2, 2 | 4, 4];
|
||||
pub const BORDER: u32 = 8;
|
||||
pub const RADIAL: u32 = 16;
|
||||
pub const FILL_START: u32 = 32;
|
||||
pub const FILL_END: u32 = 64;
|
||||
pub const CONIC: u32 = 128;
|
||||
pub const BORDER_LEFT: u32 = 256;
|
||||
pub const BORDER_TOP: u32 = 512;
|
||||
pub const BORDER_RIGHT: u32 = 1024;
|
||||
pub const BORDER_BOTTOM: u32 = 2048;
|
||||
pub const BORDER_ALL: u32 = BORDER_LEFT + BORDER_TOP + BORDER_RIGHT + BORDER_BOTTOM;
|
||||
}
|
||||
|
||||
pub fn queue_uinodes(
|
||||
@ -1394,8 +1431,8 @@ pub fn prepare_uinodes(
|
||||
};
|
||||
|
||||
let color = extracted_uinode.color.to_f32_array();
|
||||
if *node_type == NodeType::Border {
|
||||
flags |= shader_flags::BORDER;
|
||||
if let NodeType::Border(border_flags) = *node_type {
|
||||
flags |= border_flags;
|
||||
}
|
||||
|
||||
for i in 0..4 {
|
||||
|
@ -5,7 +5,12 @@
|
||||
const TEXTURED = 1u;
|
||||
const RIGHT_VERTEX = 2u;
|
||||
const BOTTOM_VERTEX = 4u;
|
||||
const BORDER: u32 = 8u;
|
||||
// must align with BORDER_* shader_flags from bevy_ui/render/mod.rs
|
||||
const BORDER_LEFT: u32 = 256u;
|
||||
const BORDER_TOP: u32 = 512u;
|
||||
const BORDER_RIGHT: u32 = 1024u;
|
||||
const BORDER_BOTTOM: u32 = 2048u;
|
||||
const BORDER_ANY: u32 = BORDER_LEFT + BORDER_TOP + BORDER_RIGHT + BORDER_BOTTOM;
|
||||
|
||||
fn enabled(flags: u32, mask: u32) -> bool {
|
||||
return (flags & mask) != 0u;
|
||||
@ -116,6 +121,27 @@ fn sd_inset_rounded_box(point: vec2<f32>, size: vec2<f32>, radius: vec4<f32>, in
|
||||
return sd_rounded_box(inner_point, inner_size, r);
|
||||
}
|
||||
|
||||
fn nearest_border_active(point_vs_mid: vec2<f32>, size: vec2<f32>, width: vec4<f32>, flags: u32) -> bool {
|
||||
if (flags & BORDER_ANY) == BORDER_ANY {
|
||||
return true;
|
||||
}
|
||||
|
||||
// get point vs top left
|
||||
let point = clamp(point_vs_mid + size * 0.49999, vec2(0.0), size);
|
||||
|
||||
let left = point.x / width.x;
|
||||
let top = point.y / width.y;
|
||||
let right = (size.x - point.x) / width.z;
|
||||
let bottom = (size.y - point.y) / width.w;
|
||||
|
||||
let min_dist = min(min(left, top), min(right, bottom));
|
||||
|
||||
return (enabled(flags, BORDER_LEFT) && min_dist == left) ||
|
||||
(enabled(flags, BORDER_TOP) && min_dist == top) ||
|
||||
(enabled(flags, BORDER_RIGHT) && min_dist == right) ||
|
||||
(enabled(flags, BORDER_BOTTOM) && min_dist == bottom);
|
||||
}
|
||||
|
||||
// get alpha for antialiasing for sdf
|
||||
fn antialias(distance: f32) -> f32 {
|
||||
// Using the fwidth(distance) was causing artifacts, so just use the distance.
|
||||
@ -128,6 +154,7 @@ fn draw_uinode_border(
|
||||
size: vec2<f32>,
|
||||
radius: vec4<f32>,
|
||||
border: vec4<f32>,
|
||||
flags: u32,
|
||||
) -> vec4<f32> {
|
||||
// Signed distances. The magnitude is the distance of the point from the edge of the shape.
|
||||
// * Negative values indicate that the point is inside the shape.
|
||||
@ -147,6 +174,9 @@ fn draw_uinode_border(
|
||||
// outside the outside edge, or inside the inner edge have positive signed distance.
|
||||
let border_distance = max(external_distance, -internal_distance);
|
||||
|
||||
// check if this node should apply color for the nearest border
|
||||
let nearest_border = select(0.0, 1.0, nearest_border_active(point, size, border, flags));
|
||||
|
||||
#ifdef ANTI_ALIAS
|
||||
// At external edges with no border, `border_distance` is equal to zero.
|
||||
// This select statement ensures we only perform anti-aliasing where a non-zero width border
|
||||
@ -158,7 +188,7 @@ fn draw_uinode_border(
|
||||
#endif
|
||||
|
||||
// Blend mode ALPHA_BLENDING is used for UI elements, so we don't premultiply alpha here.
|
||||
return vec4(color.rgb, saturate(color.a * t));
|
||||
return vec4(color.rgb, saturate(color.a * t * nearest_border));
|
||||
}
|
||||
|
||||
fn draw_uinode_background(
|
||||
@ -188,8 +218,8 @@ fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
// This allows us to draw both textured and untextured shapes together in the same batch.
|
||||
let color = select(in.color, in.color * texture_color, enabled(in.flags, TEXTURED));
|
||||
|
||||
if enabled(in.flags, BORDER) {
|
||||
return draw_uinode_border(color, in.point, in.size, in.radius, in.border);
|
||||
if enabled(in.flags, BORDER_ANY) {
|
||||
return draw_uinode_border(color, in.point, in.size, in.radius, in.border, in.flags);
|
||||
} else {
|
||||
return draw_uinode_background(color, in.point, in.size, in.radius, in.border);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{FocusPolicy, UiRect, Val};
|
||||
use bevy_color::Color;
|
||||
use bevy_color::{Alpha, Color};
|
||||
use bevy_derive::{Deref, DerefMut};
|
||||
use bevy_ecs::{prelude::*, system::SystemParam};
|
||||
use bevy_math::{vec4, Rect, UVec2, Vec2, Vec4Swizzles};
|
||||
@ -2036,17 +2036,40 @@ impl<T: Into<Color>> From<T> for BackgroundColor {
|
||||
derive(serde::Serialize, serde::Deserialize),
|
||||
reflect(Serialize, Deserialize)
|
||||
)]
|
||||
pub struct BorderColor(pub Color);
|
||||
pub struct BorderColor {
|
||||
pub top: Color,
|
||||
pub right: Color,
|
||||
pub bottom: Color,
|
||||
pub left: Color,
|
||||
}
|
||||
|
||||
impl<T: Into<Color>> From<T> for BorderColor {
|
||||
fn from(color: T) -> Self {
|
||||
Self(color.into())
|
||||
Self::all(color.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl BorderColor {
|
||||
/// Border color is transparent by default.
|
||||
pub const DEFAULT: Self = BorderColor(Color::NONE);
|
||||
pub const DEFAULT: Self = BorderColor::all(Color::NONE);
|
||||
|
||||
/// Helper to create a `BorderColor` struct with all borders set to the given color
|
||||
pub const fn all(color: Color) -> Self {
|
||||
Self {
|
||||
top: color,
|
||||
bottom: color,
|
||||
left: color,
|
||||
right: color,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if all contained border colors are transparent
|
||||
pub fn is_fully_transparent(&self) -> bool {
|
||||
self.top.is_fully_transparent()
|
||||
&& self.bottom.is_fully_transparent()
|
||||
&& self.left.is_fully_transparent()
|
||||
&& self.right.is_fully_transparent()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for BorderColor {
|
||||
|
@ -252,7 +252,7 @@ fn add_button_for_value(
|
||||
margin: UiRect::right(Val::Px(12.0)),
|
||||
..default()
|
||||
},
|
||||
BorderColor(Color::WHITE),
|
||||
BorderColor::all(Color::WHITE),
|
||||
BorderRadius::MAX,
|
||||
BackgroundColor(Color::BLACK),
|
||||
))
|
||||
|
@ -137,7 +137,7 @@ fn setup(
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
},
|
||||
BorderColor(Color::WHITE),
|
||||
BorderColor::all(Color::WHITE),
|
||||
BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
|
||||
))
|
||||
.with_children(|parent| {
|
||||
|
@ -295,7 +295,7 @@ fn setup_node_rects(commands: &mut Commands) {
|
||||
justify_content: JustifyContent::Center,
|
||||
..default()
|
||||
},
|
||||
BorderColor(WHITE.into()),
|
||||
BorderColor::all(WHITE.into()),
|
||||
Outline::new(Val::Px(1.), Val::ZERO, Color::WHITE),
|
||||
));
|
||||
|
||||
@ -349,7 +349,7 @@ fn setup_node_lines(commands: &mut Commands) {
|
||||
border: UiRect::bottom(Val::Px(1.0)),
|
||||
..default()
|
||||
},
|
||||
BorderColor(WHITE.into()),
|
||||
BorderColor::all(WHITE.into()),
|
||||
));
|
||||
}
|
||||
|
||||
@ -364,7 +364,7 @@ fn setup_node_lines(commands: &mut Commands) {
|
||||
border: UiRect::left(Val::Px(1.0)),
|
||||
..default()
|
||||
},
|
||||
BorderColor(WHITE.into()),
|
||||
BorderColor::all(WHITE.into()),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -255,7 +255,7 @@ fn add_mask_group_control(
|
||||
margin: UiRect::ZERO,
|
||||
..default()
|
||||
},
|
||||
BorderColor(Color::WHITE),
|
||||
BorderColor::all(Color::WHITE),
|
||||
BorderRadius::all(Val::Px(3.0)),
|
||||
BackgroundColor(Color::BLACK),
|
||||
))
|
||||
@ -292,7 +292,7 @@ fn add_mask_group_control(
|
||||
border: UiRect::top(Val::Px(1.0)),
|
||||
..default()
|
||||
},
|
||||
BorderColor(Color::WHITE),
|
||||
BorderColor::all(Color::WHITE),
|
||||
))
|
||||
.with_children(|builder| {
|
||||
for (index, label) in [
|
||||
@ -321,7 +321,7 @@ fn add_mask_group_control(
|
||||
},
|
||||
..default()
|
||||
},
|
||||
BorderColor(Color::WHITE),
|
||||
BorderColor::all(Color::WHITE),
|
||||
AnimationControl {
|
||||
group_id: mask_group_id,
|
||||
label: *label,
|
||||
|
@ -28,7 +28,7 @@ pub struct RadioButtonText;
|
||||
pub const BUTTON_BORDER: UiRect = UiRect::all(Val::Px(1.0));
|
||||
|
||||
/// The color of the border that surrounds buttons.
|
||||
pub const BUTTON_BORDER_COLOR: BorderColor = BorderColor(Color::WHITE);
|
||||
pub const BUTTON_BORDER_COLOR: BorderColor = BorderColor::all(Color::WHITE);
|
||||
|
||||
/// The amount of rounding to apply to button corners.
|
||||
pub const BUTTON_BORDER_RADIUS_SIZE: Val = Val::Px(6.0);
|
||||
|
@ -221,7 +221,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
justify_content: JustifyContent::Center,
|
||||
..default()
|
||||
},
|
||||
BorderColor(LIME.into()),
|
||||
BorderColor::all(LIME.into()),
|
||||
BackgroundColor(Color::srgb(0.8, 0.8, 1.)),
|
||||
))
|
||||
.with_children(|parent| {
|
||||
|
@ -227,7 +227,7 @@ mod borders {
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(MAROON.into()),
|
||||
BorderColor(RED.into()),
|
||||
BorderColor::all(RED.into()),
|
||||
Outline {
|
||||
width: Val::Px(10.),
|
||||
offset: Val::Px(10.),
|
||||
@ -319,7 +319,7 @@ mod box_shadow {
|
||||
border: UiRect::all(Val::Px(2.)),
|
||||
..default()
|
||||
},
|
||||
BorderColor(WHITE.into()),
|
||||
BorderColor::all(WHITE.into()),
|
||||
border_radius,
|
||||
BackgroundColor(BLUE.into()),
|
||||
BoxShadow::new(
|
||||
@ -417,7 +417,7 @@ mod overflow {
|
||||
overflow,
|
||||
..default()
|
||||
},
|
||||
BorderColor(RED.into()),
|
||||
BorderColor::all(RED.into()),
|
||||
BackgroundColor(Color::WHITE),
|
||||
))
|
||||
.with_children(|parent| {
|
||||
@ -519,7 +519,7 @@ mod layout_rounding {
|
||||
..Default::default()
|
||||
},
|
||||
BackgroundColor(MAROON.into()),
|
||||
BorderColor(DARK_BLUE.into()),
|
||||
BorderColor::all(DARK_BLUE.into()),
|
||||
));
|
||||
}
|
||||
});
|
||||
|
@ -120,7 +120,12 @@ fn setup(mut commands: Commands) {
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(MAROON.into()),
|
||||
BorderColor(RED.into()),
|
||||
BorderColor {
|
||||
top: RED.into(),
|
||||
bottom: YELLOW.into(),
|
||||
left: GREEN.into(),
|
||||
right: BLUE.into(),
|
||||
},
|
||||
Outline {
|
||||
width: Val::Px(6.),
|
||||
offset: Val::Px(6.),
|
||||
@ -182,7 +187,12 @@ fn setup(mut commands: Commands) {
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(MAROON.into()),
|
||||
BorderColor(RED.into()),
|
||||
BorderColor {
|
||||
top: RED.into(),
|
||||
bottom: YELLOW.into(),
|
||||
left: GREEN.into(),
|
||||
right: BLUE.into(),
|
||||
},
|
||||
BorderRadius::px(
|
||||
border_size(border.left, border.top),
|
||||
border_size(border.right, border.top),
|
||||
|
@ -202,7 +202,7 @@ fn setup(mut commands: Commands) {
|
||||
border: UiRect::all(Val::Px(4.)),
|
||||
..default()
|
||||
},
|
||||
BorderColor(LIGHT_SKY_BLUE.into()),
|
||||
BorderColor::all(LIGHT_SKY_BLUE.into()),
|
||||
BorderRadius::all(Val::Px(20.)),
|
||||
BackgroundColor(DEEP_SKY_BLUE.into()),
|
||||
BoxShadow(vec![
|
||||
@ -253,7 +253,7 @@ fn box_shadow_node_bundle(
|
||||
border: UiRect::all(Val::Px(4.)),
|
||||
..default()
|
||||
},
|
||||
BorderColor(LIGHT_SKY_BLUE.into()),
|
||||
BorderColor::all(LIGHT_SKY_BLUE.into()),
|
||||
border_radius,
|
||||
BackgroundColor(DEEP_SKY_BLUE.into()),
|
||||
BoxShadow::new(
|
||||
|
@ -44,7 +44,7 @@ fn button_system(
|
||||
input_focus.set(entity);
|
||||
**text = "Press".to_string();
|
||||
*color = PRESSED_BUTTON.into();
|
||||
border_color.0 = RED.into();
|
||||
*border_color = BorderColor::all(RED.into());
|
||||
|
||||
// The accessibility system's only update the button's state when the `Button` component is marked as changed.
|
||||
button.set_changed();
|
||||
@ -53,14 +53,14 @@ fn button_system(
|
||||
input_focus.set(entity);
|
||||
**text = "Hover".to_string();
|
||||
*color = HOVERED_BUTTON.into();
|
||||
border_color.0 = Color::WHITE;
|
||||
*border_color = BorderColor::all(Color::WHITE);
|
||||
button.set_changed();
|
||||
}
|
||||
Interaction::None => {
|
||||
input_focus.clear();
|
||||
**text = "Button".to_string();
|
||||
*color = NORMAL_BUTTON.into();
|
||||
border_color.0 = Color::BLACK;
|
||||
*border_color = BorderColor::all(Color::BLACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -93,7 +93,7 @@ fn button(asset_server: &AssetServer) -> impl Bundle + use<> {
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
},
|
||||
BorderColor(Color::BLACK),
|
||||
BorderColor::all(Color::BLACK),
|
||||
BorderRadius::MAX,
|
||||
BackgroundColor(NORMAL_BUTTON),
|
||||
children![(
|
||||
|
@ -361,9 +361,9 @@ fn highlight_focused_element(
|
||||
if input_focus.0 == Some(entity) && input_focus_visible.0 {
|
||||
// Don't change the border size / radius here,
|
||||
// as it would result in wiggling buttons when they are focused
|
||||
border_color.0 = FOCUSED_BORDER.into();
|
||||
*border_color = BorderColor::all(FOCUSED_BORDER.into());
|
||||
} else {
|
||||
border_color.0 = Color::NONE;
|
||||
*border_color = BorderColor::DEFAULT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ fn create_button() -> impl Bundle {
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
},
|
||||
BorderColor(Color::BLACK),
|
||||
BorderColor::all(Color::BLACK),
|
||||
BorderRadius::MAX,
|
||||
BackgroundColor(Color::srgb(0.15, 0.15, 0.15)),
|
||||
)
|
||||
|
@ -72,7 +72,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
overflow,
|
||||
..default()
|
||||
},
|
||||
BorderColor(Color::BLACK),
|
||||
BorderColor::all(Color::BLACK),
|
||||
BackgroundColor(GRAY.into()),
|
||||
))
|
||||
.with_children(|parent| {
|
||||
|
@ -67,7 +67,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(GRAY.into()),
|
||||
BorderColor(Color::BLACK),
|
||||
BorderColor::all(Color::BLACK),
|
||||
))
|
||||
.with_children(|parent| {
|
||||
parent
|
||||
|
@ -221,7 +221,7 @@ fn spawn_button(
|
||||
margin: UiRect::horizontal(Val::Px(2.)),
|
||||
..Default::default()
|
||||
},
|
||||
BorderColor(if active {
|
||||
BorderColor::all(if active {
|
||||
ACTIVE_BORDER_COLOR
|
||||
} else {
|
||||
INACTIVE_BORDER_COLOR
|
||||
@ -345,7 +345,7 @@ fn update_radio_buttons_colors(
|
||||
)
|
||||
};
|
||||
|
||||
border_query.get_mut(id).unwrap().0 = border_color;
|
||||
*border_query.get_mut(id).unwrap() = BorderColor::all(border_color);
|
||||
for &child in children_query.get(id).into_iter().flatten() {
|
||||
color_query.get_mut(child).unwrap().0 = inner_color;
|
||||
for &grandchild in children_query.get(child).into_iter().flatten() {
|
||||
|
@ -42,17 +42,17 @@ fn button_system(
|
||||
Interaction::Pressed => {
|
||||
**text = "Press".to_string();
|
||||
*color = PRESSED_BUTTON.into();
|
||||
border_color.0 = RED.into();
|
||||
*border_color = BorderColor::all(RED.into());
|
||||
}
|
||||
Interaction::Hovered => {
|
||||
**text = "Hover".to_string();
|
||||
*color = HOVERED_BUTTON.into();
|
||||
border_color.0 = Color::WHITE;
|
||||
*border_color = BorderColor::all(Color::WHITE);
|
||||
}
|
||||
Interaction::None => {
|
||||
**text = "Button".to_string();
|
||||
*color = NORMAL_BUTTON.into();
|
||||
border_color.0 = Color::BLACK;
|
||||
*border_color = BorderColor::all(Color::BLACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -198,7 +198,7 @@ fn create_button(parent: &mut ChildSpawnerCommands<'_>, asset_server: &AssetServ
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
},
|
||||
BorderColor(Color::BLACK),
|
||||
BorderColor::all(Color::BLACK),
|
||||
BorderRadius::MAX,
|
||||
BackgroundColor(NORMAL_BUTTON),
|
||||
TabIndex(0),
|
||||
|
@ -75,7 +75,7 @@ fn spawn_with_viewport_coords(commands: &mut Commands) {
|
||||
flex_wrap: FlexWrap::Wrap,
|
||||
..default()
|
||||
},
|
||||
BorderColor(PALETTE[0].into()),
|
||||
BorderColor::all(PALETTE[0].into()),
|
||||
Coords::Viewport,
|
||||
))
|
||||
.with_children(|builder| {
|
||||
@ -87,7 +87,7 @@ fn spawn_with_viewport_coords(commands: &mut Commands) {
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(PALETTE[2].into()),
|
||||
BorderColor(PALETTE[9].into()),
|
||||
BorderColor::all(PALETTE[9].into()),
|
||||
));
|
||||
|
||||
builder.spawn((
|
||||
@ -107,7 +107,7 @@ fn spawn_with_viewport_coords(commands: &mut Commands) {
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(PALETTE[4].into()),
|
||||
BorderColor(PALETTE[8].into()),
|
||||
BorderColor::all(PALETTE[8].into()),
|
||||
));
|
||||
|
||||
builder.spawn((
|
||||
@ -118,7 +118,7 @@ fn spawn_with_viewport_coords(commands: &mut Commands) {
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(PALETTE[5].into()),
|
||||
BorderColor(PALETTE[8].into()),
|
||||
BorderColor::all(PALETTE[8].into()),
|
||||
));
|
||||
|
||||
builder.spawn((
|
||||
@ -138,7 +138,7 @@ fn spawn_with_viewport_coords(commands: &mut Commands) {
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(PALETTE[7].into()),
|
||||
BorderColor(PALETTE[9].into()),
|
||||
BorderColor::all(PALETTE[9].into()),
|
||||
));
|
||||
});
|
||||
}
|
||||
@ -153,7 +153,7 @@ fn spawn_with_pixel_coords(commands: &mut Commands) {
|
||||
flex_wrap: FlexWrap::Wrap,
|
||||
..default()
|
||||
},
|
||||
BorderColor(PALETTE[1].into()),
|
||||
BorderColor::all(PALETTE[1].into()),
|
||||
Coords::Pixel,
|
||||
))
|
||||
.with_children(|builder| {
|
||||
@ -165,7 +165,7 @@ fn spawn_with_pixel_coords(commands: &mut Commands) {
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(PALETTE[2].into()),
|
||||
BorderColor(PALETTE[9].into()),
|
||||
BorderColor::all(PALETTE[9].into()),
|
||||
));
|
||||
|
||||
builder.spawn((
|
||||
@ -185,7 +185,7 @@ fn spawn_with_pixel_coords(commands: &mut Commands) {
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(PALETTE[4].into()),
|
||||
BorderColor(PALETTE[8].into()),
|
||||
BorderColor::all(PALETTE[8].into()),
|
||||
));
|
||||
|
||||
builder.spawn((
|
||||
@ -196,7 +196,7 @@ fn spawn_with_pixel_coords(commands: &mut Commands) {
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(PALETTE[5].into()),
|
||||
BorderColor(PALETTE[8].into()),
|
||||
BorderColor::all(PALETTE[8].into()),
|
||||
));
|
||||
|
||||
builder.spawn((
|
||||
@ -216,7 +216,7 @@ fn spawn_with_pixel_coords(commands: &mut Commands) {
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(PALETTE[7].into()),
|
||||
BorderColor(PALETTE[9].into()),
|
||||
BorderColor::all(PALETTE[9].into()),
|
||||
));
|
||||
});
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ fn test(
|
||||
border: UiRect::all(Val::Px(5.0)),
|
||||
..default()
|
||||
},
|
||||
BorderColor(Color::WHITE),
|
||||
BorderColor::all(Color::WHITE),
|
||||
ViewportNode::new(camera),
|
||||
))
|
||||
.observe(on_drag_viewport);
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
title: Separate Border Colors
|
||||
pull_requests: [18682]
|
||||
---
|
||||
|
||||
The `BorderColor` struct now contains separate fields for each edge, `top`, `bottom`, `left`, `right`. To keep the existing behavior, replace `BorderColor(color)` with `BorderColor::all(color)`, and `border_color.0 = new_color` with `*border_color = BorderColor::all(new_color)`.
|
Loading…
Reference in New Issue
Block a user