use crate::{FocusPolicy, UiRect, Val};
use bevy_color::Color;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{prelude::*, system::SystemParam};
use bevy_math::{vec4, Rect, UVec2, Vec2, Vec4Swizzles};
use bevy_reflect::prelude::*;
use bevy_render::{
camera::{Camera, RenderTarget},
view::Visibility,
view::VisibilityClass,
};
use bevy_sprite::BorderRect;
use bevy_transform::components::Transform;
use bevy_utils::once;
use bevy_window::{PrimaryWindow, WindowRef};
use core::num::NonZero;
use derive_more::derive::From;
use smallvec::SmallVec;
use thiserror::Error;
use tracing::warn;
#[derive(Component, Debug, Copy, Clone, PartialEq, Reflect)]
#[reflect(Component, Default, Debug, Clone)]
pub struct ComputedNode {
pub stack_index: u32,
pub size: Vec2,
pub content_size: Vec2,
pub outline_width: f32,
pub outline_offset: f32,
pub unrounded_size: Vec2,
pub border: BorderRect,
pub border_radius: ResolvedBorderRadius,
pub padding: BorderRect,
pub inverse_scale_factor: f32,
}
impl ComputedNode {
#[inline]
pub const fn size(&self) -> Vec2 {
self.size
}
#[inline]
pub const fn content_size(&self) -> Vec2 {
self.content_size
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.size.x <= 0. || self.size.y <= 0.
}
pub const fn stack_index(&self) -> u32 {
self.stack_index
}
#[inline]
pub const fn unrounded_size(&self) -> Vec2 {
self.unrounded_size
}
#[inline]
pub const fn outline_width(&self) -> f32 {
self.outline_width
}
#[inline]
pub const fn outline_offset(&self) -> f32 {
self.outline_offset
}
#[inline]
pub const fn outlined_node_size(&self) -> Vec2 {
let offset = 2. * (self.outline_offset + self.outline_width);
Vec2::new(self.size.x + offset, self.size.y + offset)
}
#[inline]
pub const fn outline_radius(&self) -> ResolvedBorderRadius {
let outer_distance = self.outline_width + self.outline_offset;
const fn compute_radius(radius: f32, outer_distance: f32) -> f32 {
if radius > 0. {
radius + outer_distance
} else {
0.
}
}
ResolvedBorderRadius {
top_left: compute_radius(self.border_radius.top_left, outer_distance),
top_right: compute_radius(self.border_radius.top_right, outer_distance),
bottom_left: compute_radius(self.border_radius.bottom_left, outer_distance),
bottom_right: compute_radius(self.border_radius.bottom_right, outer_distance),
}
}
#[inline]
pub const fn border(&self) -> BorderRect {
self.border
}
#[inline]
pub const fn border_radius(&self) -> ResolvedBorderRadius {
self.border_radius
}
pub fn inner_radius(&self) -> ResolvedBorderRadius {
fn clamp_corner(r: f32, size: Vec2, offset: Vec2) -> f32 {
let s = 0.5 * size + offset;
let sm = s.x.min(s.y);
r.min(sm)
}
let b = vec4(
self.border.left,
self.border.top,
self.border.right,
self.border.bottom,
);
let s = self.size() - b.xy() - b.zw();
ResolvedBorderRadius {
top_left: clamp_corner(self.border_radius.top_left, s, b.xy()),
top_right: clamp_corner(self.border_radius.top_right, s, b.zy()),
bottom_left: clamp_corner(self.border_radius.bottom_right, s, b.zw()),
bottom_right: clamp_corner(self.border_radius.bottom_left, s, b.xw()),
}
}
#[inline]
pub const fn padding(&self) -> BorderRect {
self.padding
}
#[inline]
pub const fn content_inset(&self) -> BorderRect {
BorderRect {
left: self.border.left + self.padding.left,
right: self.border.right + self.padding.right,
top: self.border.top + self.padding.top,
bottom: self.border.bottom + self.padding.bottom,
}
}
#[inline]
pub const fn inverse_scale_factor(&self) -> f32 {
self.inverse_scale_factor
}
}
impl ComputedNode {
pub const DEFAULT: Self = Self {
stack_index: 0,
size: Vec2::ZERO,
content_size: Vec2::ZERO,
outline_width: 0.,
outline_offset: 0.,
unrounded_size: Vec2::ZERO,
border_radius: ResolvedBorderRadius::ZERO,
border: BorderRect::ZERO,
padding: BorderRect::ZERO,
inverse_scale_factor: 1.,
};
}
impl Default for ComputedNode {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component, Default, Clone)]
pub struct ScrollPosition {
pub offset_x: f32,
pub offset_y: f32,
}
impl ScrollPosition {
pub const DEFAULT: Self = Self {
offset_x: 0.0,
offset_y: 0.0,
};
}
impl Default for ScrollPosition {
fn default() -> Self {
Self::DEFAULT
}
}
impl From<&ScrollPosition> for Vec2 {
fn from(scroll_pos: &ScrollPosition) -> Self {
Vec2::new(scroll_pos.offset_x, scroll_pos.offset_y)
}
}
impl From<Vec2> for ScrollPosition {
fn from(vec: Vec2) -> Self {
ScrollPosition {
offset_x: vec.x,
offset_y: vec.y,
}
}
}
#[derive(Component, Clone, PartialEq, Debug, Reflect)]
#[require(
ComputedNode,
ComputedNodeTarget,
BackgroundColor,
BorderColor,
BorderRadius,
FocusPolicy,
ScrollPosition,
Transform,
Visibility,
VisibilityClass,
ZIndex
)]
#[reflect(Component, Default, PartialEq, Debug, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct Node {
pub display: Display,
pub box_sizing: BoxSizing,
pub position_type: PositionType,
pub overflow: Overflow,
pub overflow_clip_margin: OverflowClipMargin,
pub left: Val,
pub right: Val,
pub top: Val,
pub bottom: Val,
pub width: Val,
pub height: Val,
pub min_width: Val,
pub min_height: Val,
pub max_width: Val,
pub max_height: Val,
pub aspect_ratio: Option<f32>,
pub align_items: AlignItems,
pub justify_items: JustifyItems,
pub align_self: AlignSelf,
pub justify_self: JustifySelf,
pub align_content: AlignContent,
pub justify_content: JustifyContent,
pub margin: UiRect,
pub padding: UiRect,
pub border: UiRect,
pub flex_direction: FlexDirection,
pub flex_wrap: FlexWrap,
pub flex_grow: f32,
pub flex_shrink: f32,
pub flex_basis: Val,
pub row_gap: Val,
pub column_gap: Val,
pub grid_auto_flow: GridAutoFlow,
pub grid_template_rows: Vec<RepeatedGridTrack>,
pub grid_template_columns: Vec<RepeatedGridTrack>,
pub grid_auto_rows: Vec<GridTrack>,
pub grid_auto_columns: Vec<GridTrack>,
pub grid_row: GridPlacement,
pub grid_column: GridPlacement,
}
impl Node {
pub const DEFAULT: Self = Self {
display: Display::DEFAULT,
box_sizing: BoxSizing::DEFAULT,
position_type: PositionType::DEFAULT,
left: Val::Auto,
right: Val::Auto,
top: Val::Auto,
bottom: Val::Auto,
flex_direction: FlexDirection::DEFAULT,
flex_wrap: FlexWrap::DEFAULT,
align_items: AlignItems::DEFAULT,
justify_items: JustifyItems::DEFAULT,
align_self: AlignSelf::DEFAULT,
justify_self: JustifySelf::DEFAULT,
align_content: AlignContent::DEFAULT,
justify_content: JustifyContent::DEFAULT,
margin: UiRect::DEFAULT,
padding: UiRect::DEFAULT,
border: UiRect::DEFAULT,
flex_grow: 0.0,
flex_shrink: 1.0,
flex_basis: Val::Auto,
width: Val::Auto,
height: Val::Auto,
min_width: Val::Auto,
min_height: Val::Auto,
max_width: Val::Auto,
max_height: Val::Auto,
aspect_ratio: None,
overflow: Overflow::DEFAULT,
overflow_clip_margin: OverflowClipMargin::DEFAULT,
row_gap: Val::ZERO,
column_gap: Val::ZERO,
grid_auto_flow: GridAutoFlow::DEFAULT,
grid_template_rows: Vec::new(),
grid_template_columns: Vec::new(),
grid_auto_rows: Vec::new(),
grid_auto_columns: Vec::new(),
grid_column: GridPlacement::DEFAULT,
grid_row: GridPlacement::DEFAULT,
};
}
impl Default for Node {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum AlignItems {
Default,
Start,
End,
FlexStart,
FlexEnd,
Center,
Baseline,
Stretch,
}
impl AlignItems {
pub const DEFAULT: Self = Self::Default;
}
impl Default for AlignItems {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum JustifyItems {
Default,
Start,
End,
Center,
Baseline,
Stretch,
}
impl JustifyItems {
pub const DEFAULT: Self = Self::Default;
}
impl Default for JustifyItems {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum AlignSelf {
Auto,
Start,
End,
FlexStart,
FlexEnd,
Center,
Baseline,
Stretch,
}
impl AlignSelf {
pub const DEFAULT: Self = Self::Auto;
}
impl Default for AlignSelf {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum JustifySelf {
Auto,
Start,
End,
Center,
Baseline,
Stretch,
}
impl JustifySelf {
pub const DEFAULT: Self = Self::Auto;
}
impl Default for JustifySelf {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum AlignContent {
Default,
Start,
End,
FlexStart,
FlexEnd,
Center,
Stretch,
SpaceBetween,
SpaceEvenly,
SpaceAround,
}
impl AlignContent {
pub const DEFAULT: Self = Self::Default;
}
impl Default for AlignContent {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum JustifyContent {
Default,
Start,
End,
FlexStart,
FlexEnd,
Center,
Stretch,
SpaceBetween,
SpaceEvenly,
SpaceAround,
}
impl JustifyContent {
pub const DEFAULT: Self = Self::Default;
}
impl Default for JustifyContent {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum Display {
Flex,
Grid,
Block,
None,
}
impl Display {
pub const DEFAULT: Self = Self::Flex;
}
impl Default for Display {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum BoxSizing {
BorderBox,
ContentBox,
}
impl BoxSizing {
pub const DEFAULT: Self = Self::BorderBox;
}
impl Default for BoxSizing {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum FlexDirection {
Row,
Column,
RowReverse,
ColumnReverse,
}
impl FlexDirection {
pub const DEFAULT: Self = Self::Row;
}
impl Default for FlexDirection {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct Overflow {
pub x: OverflowAxis,
pub y: OverflowAxis,
}
impl Overflow {
pub const DEFAULT: Self = Self {
x: OverflowAxis::DEFAULT,
y: OverflowAxis::DEFAULT,
};
pub const fn visible() -> Self {
Self {
x: OverflowAxis::Visible,
y: OverflowAxis::Visible,
}
}
pub const fn clip() -> Self {
Self {
x: OverflowAxis::Clip,
y: OverflowAxis::Clip,
}
}
pub const fn clip_x() -> Self {
Self {
x: OverflowAxis::Clip,
y: OverflowAxis::Visible,
}
}
pub const fn clip_y() -> Self {
Self {
x: OverflowAxis::Visible,
y: OverflowAxis::Clip,
}
}
pub const fn hidden() -> Self {
Self {
x: OverflowAxis::Hidden,
y: OverflowAxis::Hidden,
}
}
pub const fn hidden_x() -> Self {
Self {
x: OverflowAxis::Hidden,
y: OverflowAxis::Visible,
}
}
pub const fn hidden_y() -> Self {
Self {
x: OverflowAxis::Visible,
y: OverflowAxis::Hidden,
}
}
pub const fn is_visible(&self) -> bool {
self.x.is_visible() && self.y.is_visible()
}
pub const fn scroll() -> Self {
Self {
x: OverflowAxis::Scroll,
y: OverflowAxis::Scroll,
}
}
pub const fn scroll_x() -> Self {
Self {
x: OverflowAxis::Scroll,
y: OverflowAxis::Visible,
}
}
pub const fn scroll_y() -> Self {
Self {
x: OverflowAxis::Visible,
y: OverflowAxis::Scroll,
}
}
}
impl Default for Overflow {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum OverflowAxis {
Visible,
Clip,
Hidden,
Scroll,
}
impl OverflowAxis {
pub const DEFAULT: Self = Self::Visible;
pub const fn is_visible(&self) -> bool {
matches!(self, Self::Visible)
}
}
impl Default for OverflowAxis {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Default, Copy, Clone, PartialEq, Debug, Reflect)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct OverflowClipMargin {
pub visual_box: OverflowClipBox,
pub margin: f32,
}
impl OverflowClipMargin {
pub const DEFAULT: Self = Self {
visual_box: OverflowClipBox::ContentBox,
margin: 0.,
};
pub const fn content_box() -> Self {
Self {
visual_box: OverflowClipBox::ContentBox,
..Self::DEFAULT
}
}
pub const fn padding_box() -> Self {
Self {
visual_box: OverflowClipBox::PaddingBox,
..Self::DEFAULT
}
}
pub const fn border_box() -> Self {
Self {
visual_box: OverflowClipBox::BorderBox,
..Self::DEFAULT
}
}
pub const fn with_margin(mut self, margin: f32) -> Self {
self.margin = margin;
self
}
}
#[derive(Default, Copy, Clone, PartialEq, Eq, Debug, Reflect)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum OverflowClipBox {
#[default]
ContentBox,
PaddingBox,
BorderBox,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum PositionType {
Relative,
Absolute,
}
impl PositionType {
pub const DEFAULT: Self = Self::Relative;
}
impl Default for PositionType {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum FlexWrap {
NoWrap,
Wrap,
WrapReverse,
}
impl FlexWrap {
pub const DEFAULT: Self = Self::NoWrap;
}
impl Default for FlexWrap {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum GridAutoFlow {
Row,
Column,
RowDense,
ColumnDense,
}
impl GridAutoFlow {
pub const DEFAULT: Self = Self::Row;
}
impl Default for GridAutoFlow {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Default, Copy, Clone, PartialEq, Debug, Reflect)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum MinTrackSizingFunction {
Px(f32),
Percent(f32),
MinContent,
MaxContent,
#[default]
Auto,
VMin(f32),
VMax(f32),
Vh(f32),
Vw(f32),
}
#[derive(Default, Copy, Clone, PartialEq, Debug, Reflect)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum MaxTrackSizingFunction {
Px(f32),
Percent(f32),
MinContent,
MaxContent,
FitContentPx(f32),
FitContentPercent(f32),
#[default]
Auto,
Fraction(f32),
VMin(f32),
VMax(f32),
Vh(f32),
Vw(f32),
}
#[derive(Copy, Clone, PartialEq, Debug, Reflect)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct GridTrack {
pub(crate) min_sizing_function: MinTrackSizingFunction,
pub(crate) max_sizing_function: MaxTrackSizingFunction,
}
impl GridTrack {
pub const DEFAULT: Self = Self {
min_sizing_function: MinTrackSizingFunction::Auto,
max_sizing_function: MaxTrackSizingFunction::Auto,
};
pub fn px<T: From<Self>>(value: f32) -> T {
Self {
min_sizing_function: MinTrackSizingFunction::Px(value),
max_sizing_function: MaxTrackSizingFunction::Px(value),
}
.into()
}
pub fn percent<T: From<Self>>(value: f32) -> T {
Self {
min_sizing_function: MinTrackSizingFunction::Percent(value),
max_sizing_function: MaxTrackSizingFunction::Percent(value),
}
.into()
}
pub fn fr<T: From<Self>>(value: f32) -> T {
Self {
min_sizing_function: MinTrackSizingFunction::Auto,
max_sizing_function: MaxTrackSizingFunction::Fraction(value),
}
.into()
}
pub fn flex<T: From<Self>>(value: f32) -> T {
Self {
min_sizing_function: MinTrackSizingFunction::Px(0.0),
max_sizing_function: MaxTrackSizingFunction::Fraction(value),
}
.into()
}
pub fn auto<T: From<Self>>() -> T {
Self {
min_sizing_function: MinTrackSizingFunction::Auto,
max_sizing_function: MaxTrackSizingFunction::Auto,
}
.into()
}
pub fn min_content<T: From<Self>>() -> T {
Self {
min_sizing_function: MinTrackSizingFunction::MinContent,
max_sizing_function: MaxTrackSizingFunction::MinContent,
}
.into()
}
pub fn max_content<T: From<Self>>() -> T {
Self {
min_sizing_function: MinTrackSizingFunction::MaxContent,
max_sizing_function: MaxTrackSizingFunction::MaxContent,
}
.into()
}
pub fn fit_content_px<T: From<Self>>(limit: f32) -> T {
Self {
min_sizing_function: MinTrackSizingFunction::Auto,
max_sizing_function: MaxTrackSizingFunction::FitContentPx(limit),
}
.into()
}
pub fn fit_content_percent<T: From<Self>>(limit: f32) -> T {
Self {
min_sizing_function: MinTrackSizingFunction::Auto,
max_sizing_function: MaxTrackSizingFunction::FitContentPercent(limit),
}
.into()
}
pub fn minmax<T: From<Self>>(min: MinTrackSizingFunction, max: MaxTrackSizingFunction) -> T {
Self {
min_sizing_function: min,
max_sizing_function: max,
}
.into()
}
pub fn vmin<T: From<Self>>(value: f32) -> T {
Self {
min_sizing_function: MinTrackSizingFunction::VMin(value),
max_sizing_function: MaxTrackSizingFunction::VMin(value),
}
.into()
}
pub fn vmax<T: From<Self>>(value: f32) -> T {
Self {
min_sizing_function: MinTrackSizingFunction::VMax(value),
max_sizing_function: MaxTrackSizingFunction::VMax(value),
}
.into()
}
pub fn vh<T: From<Self>>(value: f32) -> T {
Self {
min_sizing_function: MinTrackSizingFunction::Vh(value),
max_sizing_function: MaxTrackSizingFunction::Vh(value),
}
.into()
}
pub fn vw<T: From<Self>>(value: f32) -> T {
Self {
min_sizing_function: MinTrackSizingFunction::Vw(value),
max_sizing_function: MaxTrackSizingFunction::Vw(value),
}
.into()
}
}
impl Default for GridTrack {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Copy, Clone, PartialEq, Debug, Reflect, From)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum GridTrackRepetition {
Count(u16),
AutoFill,
AutoFit,
}
impl Default for GridTrackRepetition {
fn default() -> Self {
Self::Count(1)
}
}
impl From<i32> for GridTrackRepetition {
fn from(count: i32) -> Self {
Self::Count(count as u16)
}
}
impl From<usize> for GridTrackRepetition {
fn from(count: usize) -> Self {
Self::Count(count as u16)
}
}
#[derive(Clone, PartialEq, Debug, Reflect)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct RepeatedGridTrack {
pub(crate) repetition: GridTrackRepetition,
pub(crate) tracks: SmallVec<[GridTrack; 1]>,
}
impl RepeatedGridTrack {
pub fn px<T: From<Self>>(repetition: impl Into<GridTrackRepetition>, value: f32) -> T {
Self {
repetition: repetition.into(),
tracks: SmallVec::from_buf([GridTrack::px(value)]),
}
.into()
}
pub fn percent<T: From<Self>>(repetition: impl Into<GridTrackRepetition>, value: f32) -> T {
Self {
repetition: repetition.into(),
tracks: SmallVec::from_buf([GridTrack::percent(value)]),
}
.into()
}
pub fn auto<T: From<Self>>(repetition: u16) -> T {
Self {
repetition: GridTrackRepetition::Count(repetition),
tracks: SmallVec::from_buf([GridTrack::auto()]),
}
.into()
}
pub fn fr<T: From<Self>>(repetition: u16, value: f32) -> T {
Self {
repetition: GridTrackRepetition::Count(repetition),
tracks: SmallVec::from_buf([GridTrack::fr(value)]),
}
.into()
}
pub fn flex<T: From<Self>>(repetition: u16, value: f32) -> T {
Self {
repetition: GridTrackRepetition::Count(repetition),
tracks: SmallVec::from_buf([GridTrack::flex(value)]),
}
.into()
}
pub fn min_content<T: From<Self>>(repetition: u16) -> T {
Self {
repetition: GridTrackRepetition::Count(repetition),
tracks: SmallVec::from_buf([GridTrack::min_content()]),
}
.into()
}
pub fn max_content<T: From<Self>>(repetition: u16) -> T {
Self {
repetition: GridTrackRepetition::Count(repetition),
tracks: SmallVec::from_buf([GridTrack::max_content()]),
}
.into()
}
pub fn fit_content_px<T: From<Self>>(repetition: u16, limit: f32) -> T {
Self {
repetition: GridTrackRepetition::Count(repetition),
tracks: SmallVec::from_buf([GridTrack::fit_content_px(limit)]),
}
.into()
}
pub fn fit_content_percent<T: From<Self>>(repetition: u16, limit: f32) -> T {
Self {
repetition: GridTrackRepetition::Count(repetition),
tracks: SmallVec::from_buf([GridTrack::fit_content_percent(limit)]),
}
.into()
}
pub fn minmax<T: From<Self>>(
repetition: impl Into<GridTrackRepetition>,
min: MinTrackSizingFunction,
max: MaxTrackSizingFunction,
) -> T {
Self {
repetition: repetition.into(),
tracks: SmallVec::from_buf([GridTrack::minmax(min, max)]),
}
.into()
}
pub fn vmin<T: From<Self>>(repetition: impl Into<GridTrackRepetition>, value: f32) -> T {
Self {
repetition: repetition.into(),
tracks: SmallVec::from_buf([GridTrack::vmin(value)]),
}
.into()
}
pub fn vmax<T: From<Self>>(repetition: impl Into<GridTrackRepetition>, value: f32) -> T {
Self {
repetition: repetition.into(),
tracks: SmallVec::from_buf([GridTrack::vmax(value)]),
}
.into()
}
pub fn vh<T: From<Self>>(repetition: impl Into<GridTrackRepetition>, value: f32) -> T {
Self {
repetition: repetition.into(),
tracks: SmallVec::from_buf([GridTrack::vh(value)]),
}
.into()
}
pub fn vw<T: From<Self>>(repetition: impl Into<GridTrackRepetition>, value: f32) -> T {
Self {
repetition: repetition.into(),
tracks: SmallVec::from_buf([GridTrack::vw(value)]),
}
.into()
}
pub fn repeat_many<T: From<Self>>(
repetition: impl Into<GridTrackRepetition>,
tracks: impl Into<Vec<GridTrack>>,
) -> T {
Self {
repetition: repetition.into(),
tracks: SmallVec::from_vec(tracks.into()),
}
.into()
}
}
impl Default for RepeatedGridTrack {
fn default() -> Self {
Self {
repetition: Default::default(),
tracks: SmallVec::from_buf([GridTrack::default()]),
}
}
}
impl From<GridTrack> for RepeatedGridTrack {
fn from(track: GridTrack) -> Self {
Self {
repetition: GridTrackRepetition::Count(1),
tracks: SmallVec::from_buf([track]),
}
}
}
impl From<GridTrack> for Vec<GridTrack> {
fn from(track: GridTrack) -> Self {
vec![track]
}
}
impl From<GridTrack> for Vec<RepeatedGridTrack> {
fn from(track: GridTrack) -> Self {
vec![RepeatedGridTrack {
repetition: GridTrackRepetition::Count(1),
tracks: SmallVec::from_buf([track]),
}]
}
}
impl From<RepeatedGridTrack> for Vec<RepeatedGridTrack> {
fn from(track: RepeatedGridTrack) -> Self {
vec![track]
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
#[reflect(Default, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct GridPlacement {
pub(crate) start: Option<NonZero<i16>>,
pub(crate) span: Option<NonZero<u16>>,
pub(crate) end: Option<NonZero<i16>>,
}
impl GridPlacement {
pub const DEFAULT: Self = Self {
start: None,
span: NonZero::<u16>::new(1),
end: None,
};
pub fn auto() -> Self {
Self::DEFAULT
}
pub fn span(span: u16) -> Self {
Self {
start: None,
end: None,
span: try_into_grid_span(span).expect("Invalid span value of 0."),
}
}
pub fn start(start: i16) -> Self {
Self {
start: try_into_grid_index(start).expect("Invalid start value of 0."),
..Self::DEFAULT
}
}
pub fn end(end: i16) -> Self {
Self {
end: try_into_grid_index(end).expect("Invalid end value of 0."),
..Self::DEFAULT
}
}
pub fn start_span(start: i16, span: u16) -> Self {
Self {
start: try_into_grid_index(start).expect("Invalid start value of 0."),
end: None,
span: try_into_grid_span(span).expect("Invalid span value of 0."),
}
}
pub fn start_end(start: i16, end: i16) -> Self {
Self {
start: try_into_grid_index(start).expect("Invalid start value of 0."),
end: try_into_grid_index(end).expect("Invalid end value of 0."),
span: None,
}
}
pub fn end_span(end: i16, span: u16) -> Self {
Self {
start: None,
end: try_into_grid_index(end).expect("Invalid end value of 0."),
span: try_into_grid_span(span).expect("Invalid span value of 0."),
}
}
pub fn set_start(mut self, start: i16) -> Self {
self.start = try_into_grid_index(start).expect("Invalid start value of 0.");
self
}
pub fn set_end(mut self, end: i16) -> Self {
self.end = try_into_grid_index(end).expect("Invalid end value of 0.");
self
}
pub fn set_span(mut self, span: u16) -> Self {
self.span = try_into_grid_span(span).expect("Invalid span value of 0.");
self
}
pub fn get_start(self) -> Option<i16> {
self.start.map(NonZero::<i16>::get)
}
pub fn get_end(self) -> Option<i16> {
self.end.map(NonZero::<i16>::get)
}
pub fn get_span(self) -> Option<u16> {
self.span.map(NonZero::<u16>::get)
}
}
impl Default for GridPlacement {
fn default() -> Self {
Self::DEFAULT
}
}
fn try_into_grid_index(index: i16) -> Result<Option<NonZero<i16>>, GridPlacementError> {
Ok(Some(
NonZero::<i16>::new(index).ok_or(GridPlacementError::InvalidZeroIndex)?,
))
}
fn try_into_grid_span(span: u16) -> Result<Option<NonZero<u16>>, GridPlacementError> {
Ok(Some(
NonZero::<u16>::new(span).ok_or(GridPlacementError::InvalidZeroSpan)?,
))
}
#[derive(Debug, Eq, PartialEq, Clone, Copy, Error)]
pub enum GridPlacementError {
#[error("Zero is not a valid grid position")]
InvalidZeroIndex,
#[error("Spans cannot be zero length")]
InvalidZeroSpan,
}
#[derive(Component, Copy, Clone, Debug, PartialEq, Reflect)]
#[reflect(Component, Default, Debug, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct BackgroundColor(pub Color);
impl BackgroundColor {
pub const DEFAULT: Self = Self(Color::NONE);
}
impl Default for BackgroundColor {
fn default() -> Self {
Self::DEFAULT
}
}
impl<T: Into<Color>> From<T> for BackgroundColor {
fn from(color: T) -> Self {
Self(color.into())
}
}
#[derive(Component, Copy, Clone, Debug, PartialEq, Reflect)]
#[reflect(Component, Default, Debug, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct BorderColor(pub Color);
impl<T: Into<Color>> From<T> for BorderColor {
fn from(color: T) -> Self {
Self(color.into())
}
}
impl BorderColor {
pub const DEFAULT: Self = BorderColor(Color::NONE);
}
impl Default for BorderColor {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Component, Copy, Clone, Default, Debug, PartialEq, Reflect)]
#[reflect(Component, Default, Debug, PartialEq, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct Outline {
pub width: Val,
pub offset: Val,
pub color: Color,
}
impl Outline {
pub const fn new(width: Val, offset: Val, color: Color) -> Self {
Self {
width,
offset,
color,
}
}
}
#[derive(Component, Default, Copy, Clone, Debug, Reflect)]
#[reflect(Component, Default, Debug, Clone)]
pub struct CalculatedClip {
pub clip: Rect,
}
#[derive(Component, Copy, Clone, Debug, Default, PartialEq, Eq, Reflect)]
#[reflect(Component, Default, Debug, PartialEq, Clone)]
pub struct ZIndex(pub i32);
#[derive(Component, Copy, Clone, Debug, Default, PartialEq, Eq, Reflect)]
#[reflect(Component, Default, Debug, PartialEq, Clone)]
pub struct GlobalZIndex(pub i32);
#[derive(Component, Copy, Clone, Debug, PartialEq, Reflect)]
#[reflect(Component, PartialEq, Default, Debug, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct BorderRadius {
pub top_left: Val,
pub top_right: Val,
pub bottom_left: Val,
pub bottom_right: Val,
}
impl Default for BorderRadius {
fn default() -> Self {
Self::DEFAULT
}
}
impl BorderRadius {
pub const DEFAULT: Self = Self::ZERO;
pub const ZERO: Self = Self::all(Val::Px(0.));
pub const MAX: Self = Self::all(Val::Px(f32::MAX));
#[inline]
pub const fn all(radius: Val) -> Self {
Self {
top_left: radius,
top_right: radius,
bottom_left: radius,
bottom_right: radius,
}
}
#[inline]
pub const fn new(top_left: Val, top_right: Val, bottom_right: Val, bottom_left: Val) -> Self {
Self {
top_left,
top_right,
bottom_right,
bottom_left,
}
}
#[inline]
pub const fn px(top_left: f32, top_right: f32, bottom_right: f32, bottom_left: f32) -> Self {
Self {
top_left: Val::Px(top_left),
top_right: Val::Px(top_right),
bottom_right: Val::Px(bottom_right),
bottom_left: Val::Px(bottom_left),
}
}
#[inline]
pub const fn percent(
top_left: f32,
top_right: f32,
bottom_right: f32,
bottom_left: f32,
) -> Self {
Self {
top_left: Val::Percent(top_left),
top_right: Val::Percent(top_right),
bottom_right: Val::Percent(bottom_right),
bottom_left: Val::Percent(bottom_left),
}
}
#[inline]
pub const fn top_left(radius: Val) -> Self {
Self {
top_left: radius,
..Self::DEFAULT
}
}
#[inline]
pub const fn top_right(radius: Val) -> Self {
Self {
top_right: radius,
..Self::DEFAULT
}
}
#[inline]
pub const fn bottom_right(radius: Val) -> Self {
Self {
bottom_right: radius,
..Self::DEFAULT
}
}
#[inline]
pub const fn bottom_left(radius: Val) -> Self {
Self {
bottom_left: radius,
..Self::DEFAULT
}
}
#[inline]
pub const fn left(radius: Val) -> Self {
Self {
top_left: radius,
bottom_left: radius,
..Self::DEFAULT
}
}
#[inline]
pub const fn right(radius: Val) -> Self {
Self {
top_right: radius,
bottom_right: radius,
..Self::DEFAULT
}
}
#[inline]
pub const fn top(radius: Val) -> Self {
Self {
top_left: radius,
top_right: radius,
..Self::DEFAULT
}
}
#[inline]
pub const fn bottom(radius: Val) -> Self {
Self {
bottom_left: radius,
bottom_right: radius,
..Self::DEFAULT
}
}
#[inline]
pub const fn with_top_left(mut self, radius: Val) -> Self {
self.top_left = radius;
self
}
#[inline]
pub const fn with_top_right(mut self, radius: Val) -> Self {
self.top_right = radius;
self
}
#[inline]
pub const fn with_bottom_right(mut self, radius: Val) -> Self {
self.bottom_right = radius;
self
}
#[inline]
pub const fn with_bottom_left(mut self, radius: Val) -> Self {
self.bottom_left = radius;
self
}
#[inline]
pub const fn with_left(mut self, radius: Val) -> Self {
self.top_left = radius;
self.bottom_left = radius;
self
}
#[inline]
pub const fn with_right(mut self, radius: Val) -> Self {
self.top_right = radius;
self.bottom_right = radius;
self
}
#[inline]
pub const fn with_top(mut self, radius: Val) -> Self {
self.top_left = radius;
self.top_right = radius;
self
}
#[inline]
pub const fn with_bottom(mut self, radius: Val) -> Self {
self.bottom_left = radius;
self.bottom_right = radius;
self
}
pub fn resolve_single_corner(
radius: Val,
node_size: Vec2,
viewport_size: Vec2,
scale_factor: f32,
) -> f32 {
match radius {
Val::Auto => 0.,
Val::Px(px) => px * scale_factor,
Val::Percent(percent) => node_size.min_element() * percent / 100.,
Val::Vw(percent) => viewport_size.x * percent / 100.,
Val::Vh(percent) => viewport_size.y * percent / 100.,
Val::VMin(percent) => viewport_size.min_element() * percent / 100.,
Val::VMax(percent) => viewport_size.max_element() * percent / 100.,
}
.clamp(0., 0.5 * node_size.min_element())
}
pub fn resolve(
&self,
node_size: Vec2,
viewport_size: Vec2,
scale_factor: f32,
) -> ResolvedBorderRadius {
ResolvedBorderRadius {
top_left: Self::resolve_single_corner(
self.top_left,
node_size,
viewport_size,
scale_factor,
),
top_right: Self::resolve_single_corner(
self.top_right,
node_size,
viewport_size,
scale_factor,
),
bottom_left: Self::resolve_single_corner(
self.bottom_left,
node_size,
viewport_size,
scale_factor,
),
bottom_right: Self::resolve_single_corner(
self.bottom_right,
node_size,
viewport_size,
scale_factor,
),
}
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Reflect)]
#[reflect(Clone, PartialEq, Default)]
pub struct ResolvedBorderRadius {
pub top_left: f32,
pub top_right: f32,
pub bottom_left: f32,
pub bottom_right: f32,
}
impl ResolvedBorderRadius {
pub const ZERO: Self = Self {
top_left: 0.,
top_right: 0.,
bottom_left: 0.,
bottom_right: 0.,
};
}
#[derive(Component, Clone, Debug, Default, PartialEq, Reflect, Deref, DerefMut)]
#[reflect(Component, PartialEq, Default, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct BoxShadow(pub Vec<ShadowStyle>);
impl BoxShadow {
pub fn new(
color: Color,
x_offset: Val,
y_offset: Val,
spread_radius: Val,
blur_radius: Val,
) -> Self {
Self(vec![ShadowStyle {
color,
x_offset,
y_offset,
spread_radius,
blur_radius,
}])
}
}
impl From<ShadowStyle> for BoxShadow {
fn from(value: ShadowStyle) -> Self {
Self(vec![value])
}
}
#[derive(Copy, Clone, Debug, PartialEq, Reflect)]
#[reflect(PartialEq, Default, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct ShadowStyle {
pub color: Color,
pub x_offset: Val,
pub y_offset: Val,
pub spread_radius: Val,
pub blur_radius: Val,
}
impl Default for ShadowStyle {
fn default() -> Self {
Self {
color: Color::BLACK,
x_offset: Val::Percent(20.),
y_offset: Val::Percent(20.),
spread_radius: Val::ZERO,
blur_radius: Val::Percent(10.),
}
}
}
#[derive(Component, Copy, Clone, Debug, PartialEq, Reflect)]
#[reflect(Component, Debug, PartialEq, Default, Clone)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct LayoutConfig {
pub use_rounding: bool,
}
impl Default for LayoutConfig {
fn default() -> Self {
Self { use_rounding: true }
}
}
#[cfg(test)]
mod tests {
use crate::GridPlacement;
#[test]
fn invalid_grid_placement_values() {
assert!(std::panic::catch_unwind(|| GridPlacement::span(0)).is_err());
assert!(std::panic::catch_unwind(|| GridPlacement::start(0)).is_err());
assert!(std::panic::catch_unwind(|| GridPlacement::end(0)).is_err());
assert!(std::panic::catch_unwind(|| GridPlacement::start_end(0, 1)).is_err());
assert!(std::panic::catch_unwind(|| GridPlacement::start_end(-1, 0)).is_err());
assert!(std::panic::catch_unwind(|| GridPlacement::start_span(1, 0)).is_err());
assert!(std::panic::catch_unwind(|| GridPlacement::start_span(0, 1)).is_err());
assert!(std::panic::catch_unwind(|| GridPlacement::end_span(0, 1)).is_err());
assert!(std::panic::catch_unwind(|| GridPlacement::end_span(1, 0)).is_err());
assert!(std::panic::catch_unwind(|| GridPlacement::default().set_start(0)).is_err());
assert!(std::panic::catch_unwind(|| GridPlacement::default().set_end(0)).is_err());
assert!(std::panic::catch_unwind(|| GridPlacement::default().set_span(0)).is_err());
}
#[test]
fn grid_placement_accessors() {
assert_eq!(GridPlacement::start(5).get_start(), Some(5));
assert_eq!(GridPlacement::end(-4).get_end(), Some(-4));
assert_eq!(GridPlacement::span(2).get_span(), Some(2));
assert_eq!(GridPlacement::start_end(11, 21).get_span(), None);
assert_eq!(GridPlacement::start_span(3, 5).get_end(), None);
assert_eq!(GridPlacement::end_span(-4, 12).get_start(), None);
}
}
#[derive(Component, Clone, Debug, Reflect, Eq, PartialEq)]
#[reflect(Component, Debug, PartialEq, Clone)]
pub struct UiTargetCamera(pub Entity);
impl UiTargetCamera {
pub fn entity(&self) -> Entity {
self.0
}
}
#[derive(Component, Default)]
pub struct IsDefaultUiCamera;
#[derive(SystemParam)]
pub struct DefaultUiCamera<'w, 's> {
cameras: Query<'w, 's, (Entity, &'static Camera)>,
default_cameras: Query<'w, 's, Entity, (With<Camera>, With<IsDefaultUiCamera>)>,
primary_window: Query<'w, 's, Entity, With<PrimaryWindow>>,
}
impl<'w, 's> DefaultUiCamera<'w, 's> {
pub fn get(&self) -> Option<Entity> {
self.default_cameras.single().ok().or_else(|| {
if !self.default_cameras.is_empty() {
once!(warn!("Two or more Entities with IsDefaultUiCamera found when only one Camera with this marker is allowed."));
}
self.cameras
.iter()
.filter(|(_, c)| match c.target {
RenderTarget::Window(WindowRef::Primary) => true,
RenderTarget::Window(WindowRef::Entity(w)) => {
self.primary_window.get(w).is_ok()
}
_ => false,
})
.max_by_key(|(e, c)| (c.order, *e))
.map(|(e, _)| e)
})
}
}
#[derive(Component, Clone, Copy, Default, Debug, Reflect, Eq, PartialEq)]
#[reflect(Component, Default, PartialEq, Clone)]
pub enum UiAntiAlias {
#[default]
On,
Off,
}
#[derive(Component, Clone, Copy, Debug, Reflect, Eq, PartialEq)]
#[reflect(Component, Default, PartialEq, Clone)]
pub struct BoxShadowSamples(pub u32);
impl Default for BoxShadowSamples {
fn default() -> Self {
Self(4)
}
}
#[derive(Component, Clone, Copy, Debug, Reflect, PartialEq)]
#[reflect(Component, Default, PartialEq, Clone)]
pub struct ComputedNodeTarget {
pub(crate) camera: Entity,
pub(crate) scale_factor: f32,
pub(crate) physical_size: UVec2,
}
impl Default for ComputedNodeTarget {
fn default() -> Self {
Self {
camera: Entity::PLACEHOLDER,
scale_factor: 1.,
physical_size: UVec2::ZERO,
}
}
}
impl ComputedNodeTarget {
pub fn camera(&self) -> Option<Entity> {
Some(self.camera).filter(|&entity| entity != Entity::PLACEHOLDER)
}
pub const fn scale_factor(&self) -> f32 {
self.scale_factor
}
pub const fn physical_size(&self) -> UVec2 {
self.physical_size
}
pub fn logical_size(&self) -> Vec2 {
self.physical_size.as_vec2() / self.scale_factor
}
}
#[derive(Component, Copy, Clone, Debug, Reflect)]
#[reflect(Component, Default, Debug, Clone)]
pub struct TextShadow {
pub offset: Vec2,
pub color: Color,
}
impl Default for TextShadow {
fn default() -> Self {
Self {
offset: Vec2::splat(4.),
color: Color::linear_rgba(0., 0., 0., 0.75),
}
}
}