use crate::{Size, UiRect}; use bevy_asset::Handle; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{prelude::Component, reflect::ReflectComponent}; use bevy_math::Vec2; use bevy_reflect::prelude::*; use bevy_render::{ color::Color, texture::{Image, DEFAULT_IMAGE_HANDLE}, }; use serde::{Deserialize, Serialize}; use std::ops::{Add, AddAssign}; /// Describes the size of a UI node #[derive(Component, Debug, Clone, Default, Reflect)] #[reflect(Component, Default)] pub struct Node { /// The size of the node as width and height in pixels pub size: Vec2, } /// An enum that describes possible types of value in flexbox layout options #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)] #[reflect_value(PartialEq, Serialize, Deserialize)] pub enum Val { /// No value defined Undefined, /// Automatically determine this value Auto, /// Set this value in pixels Px(f32), /// Set this value in percent Percent(f32), } impl Default for Val { fn default() -> Self { Val::Undefined } } impl Add for Val { type Output = Val; fn add(self, rhs: f32) -> Self::Output { match self { Val::Undefined => Val::Undefined, Val::Auto => Val::Auto, Val::Px(value) => Val::Px(value + rhs), Val::Percent(value) => Val::Percent(value + rhs), } } } impl AddAssign for Val { fn add_assign(&mut self, rhs: f32) { match self { Val::Undefined | Val::Auto => {} Val::Px(value) | Val::Percent(value) => *value += rhs, } } } /// Describes the style of a UI node /// /// It uses the [Flexbox](https://cssreference.io/flexbox/) system. /// /// **Note:** Bevy's UI is upside down compared to how Flexbox normally works, to stay consistent with engine paradigms about layouting from /// the upper left corner of the display #[derive(Component, Clone, PartialEq, Debug, Reflect)] #[reflect(Component, Default, PartialEq)] pub struct Style { /// Whether to arrange this node and its children with flexbox layout pub display: Display, /// Whether to arrange this node relative to other nodes, or positioned absolutely pub position_type: PositionType, /// Which direction the content of this node should go pub direction: Direction, /// Whether to use column or row layout pub flex_direction: FlexDirection, /// How to wrap nodes pub flex_wrap: FlexWrap, /// How items are aligned according to the cross axis pub align_items: AlignItems, /// Like align_items but for only this item pub align_self: AlignSelf, /// How to align each line, only applies if flex_wrap is set to /// [`FlexWrap::Wrap`] and there are multiple lines of items pub align_content: AlignContent, /// How items align according to the main axis pub justify_content: JustifyContent, /// The position of the node as descrided by its Rect pub position: UiRect, /// The margin of the node pub margin: UiRect, /// The padding of the node pub padding: UiRect, /// The border of the node pub border: UiRect, /// Defines how much a flexbox item should grow if there's space available pub flex_grow: f32, /// How to shrink if there's not enough space available pub flex_shrink: f32, /// The initial size of the item pub flex_basis: Val, /// The size of the flexbox pub size: Size, /// The minimum size of the flexbox pub min_size: Size, /// The maximum size of the flexbox pub max_size: Size, /// The aspect ratio of the flexbox pub aspect_ratio: Option, /// How to handle overflow pub overflow: Overflow, } impl Default for Style { fn default() -> Self { Self { display: Default::default(), position_type: Default::default(), direction: Default::default(), flex_direction: Default::default(), flex_wrap: Default::default(), align_items: Default::default(), align_self: Default::default(), align_content: Default::default(), justify_content: Default::default(), position: Default::default(), margin: Default::default(), padding: Default::default(), border: Default::default(), flex_grow: 0.0, flex_shrink: 1.0, flex_basis: Val::Auto, size: Size::new(Val::Auto, Val::Auto), min_size: Size::new(Val::Auto, Val::Auto), max_size: Size::new(Val::Auto, Val::Auto), aspect_ratio: Default::default(), overflow: Default::default(), } } } /// How items are aligned according to the cross axis #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)] #[reflect_value(PartialEq, Serialize, Deserialize)] pub enum AlignItems { /// Items are aligned at the start FlexStart, /// Items are aligned at the end FlexEnd, /// Items are aligned at the center Center, /// Items are aligned at the baseline Baseline, /// Items are stretched across the whole cross axis Stretch, } impl Default for AlignItems { fn default() -> AlignItems { AlignItems::Stretch } } /// Works like [`AlignItems`] but applies only to a single item #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)] #[reflect_value(PartialEq, Serialize, Deserialize)] pub enum AlignSelf { /// Use the value of [`AlignItems`] Auto, /// If the parent has [`AlignItems::Center`] only this item will be at the start FlexStart, /// If the parent has [`AlignItems::Center`] only this item will be at the end FlexEnd, /// If the parent has [`AlignItems::FlexStart`] only this item will be at the center Center, /// If the parent has [`AlignItems::Center`] only this item will be at the baseline Baseline, /// If the parent has [`AlignItems::Center`] only this item will stretch along the whole cross axis Stretch, } impl Default for AlignSelf { fn default() -> AlignSelf { AlignSelf::Auto } } /// Defines how each line is aligned within the flexbox. /// /// It only applies if [`FlexWrap::Wrap`] is present and if there are multiple lines of items. #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)] #[reflect_value(PartialEq, Serialize, Deserialize)] pub enum AlignContent { /// Each line moves towards the start of the cross axis FlexStart, /// Each line moves towards the end of the cross axis FlexEnd, /// Each line moves towards the center of the cross axis Center, /// Each line will stretch to fill the remaining space Stretch, /// Each line fills the space it needs, putting the remaining space, if any /// inbetween the lines SpaceBetween, /// Each line fills the space it needs, putting the remaining space, if any /// around the lines SpaceAround, } impl Default for AlignContent { fn default() -> AlignContent { AlignContent::Stretch } } /// Defines the text direction /// /// For example English is written LTR (left-to-right) while Arabic is written RTL (right-to-left). #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)] #[reflect_value(PartialEq, Serialize, Deserialize)] pub enum Direction { /// Inherit from parent node Inherit, /// Text is written left to right LeftToRight, /// Text is written right to left RightToLeft, } impl Default for Direction { fn default() -> Direction { Direction::Inherit } } /// Whether to use Flexbox layout #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)] #[reflect_value(PartialEq, Serialize, Deserialize)] pub enum Display { /// Use flexbox Flex, /// Use no layout, don't render this node and its children None, } impl Default for Display { fn default() -> Display { Display::Flex } } /// Defines how flexbox items are ordered within a flexbox #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)] #[reflect_value(PartialEq, Serialize, Deserialize)] pub enum FlexDirection { /// Same way as text direction along the main axis Row, /// Flex from bottom to top Column, /// Opposite way as text direction along the main axis RowReverse, /// Flex from top to bottom ColumnReverse, } impl Default for FlexDirection { fn default() -> FlexDirection { FlexDirection::Row } } /// Defines how items are aligned according to the main axis #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)] #[reflect_value(PartialEq, Serialize, Deserialize)] pub enum JustifyContent { /// Pushed towards the start FlexStart, /// Pushed towards the end FlexEnd, /// Centered along the main axis Center, /// Remaining space is distributed between the items SpaceBetween, /// Remaining space is distributed around the items SpaceAround, /// Like [`JustifyContent::SpaceAround`] but with even spacing between items SpaceEvenly, } impl Default for JustifyContent { fn default() -> JustifyContent { JustifyContent::FlexStart } } /// Whether to show or hide overflowing items #[derive(Copy, Clone, PartialEq, Debug, Reflect, Serialize, Deserialize)] #[reflect_value(PartialEq, Serialize, Deserialize)] pub enum Overflow { /// Show overflowing items Visible, /// Hide overflowing items Hidden, } impl Default for Overflow { fn default() -> Overflow { Overflow::Visible } } /// The strategy used to position this node #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)] #[reflect_value(PartialEq, Serialize, Deserialize)] pub enum PositionType { /// Relative to all other nodes with the [`PositionType::Relative`] value Relative, /// Independent of all other nodes /// /// As usual, the `Style.position` field of this node is specified relative to its parent node Absolute, } impl Default for PositionType { fn default() -> PositionType { PositionType::Relative } } /// Defines if flexbox items appear on a single line or on multiple lines #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize, Reflect)] #[reflect_value(PartialEq, Serialize, Deserialize)] pub enum FlexWrap { /// Single line, will overflow if needed NoWrap, /// Multiple lines, if needed Wrap, /// Same as [`FlexWrap::Wrap`] but new lines will appear before the previous one WrapReverse, } impl Default for FlexWrap { fn default() -> FlexWrap { FlexWrap::NoWrap } } /// The calculated size of the node #[derive(Component, Default, Copy, Clone, Debug, Reflect)] #[reflect(Component)] pub struct CalculatedSize { /// The size of the node pub size: Size, } /// The color of the node #[derive(Component, Default, Copy, Clone, Debug, Reflect)] #[reflect(Component, Default)] pub struct UiColor(pub Color); impl From for UiColor { fn from(color: Color) -> Self { Self(color) } } /// The image of the node #[derive(Component, Clone, Debug, Reflect, Deref, DerefMut)] #[reflect(Component, Default)] pub struct UiImage(pub Handle); impl Default for UiImage { fn default() -> Self { Self(DEFAULT_IMAGE_HANDLE.typed()) } } impl From> for UiImage { fn from(handle: Handle) -> Self { Self(handle) } } /// The calculated clip of the node #[derive(Component, Default, Copy, Clone, Debug, Reflect)] #[reflect(Component)] pub struct CalculatedClip { /// The rect of the clip pub clip: bevy_sprite::Rect, }