use bevy_math::Vec2;1use bevy_reflect::{std_traits::ReflectDefault, Reflect};2use bevy_utils::default;3use core::ops::{Div, DivAssign, Mul, MulAssign, Neg};4use thiserror::Error;56#[cfg(feature = "serialize")]7use bevy_reflect::{ReflectDeserialize, ReflectSerialize};89/// Represents the possible value types for layout properties.10///11/// This enum allows specifying values for various [`Node`](crate::Node) properties in different units,12/// such as logical pixels, percentages, or automatically determined values.13///14/// `Val` also implements [`core::str::FromStr`] to allow parsing values from strings in the format `#.#px`. Whitespaces between the value and unit is allowed. The following units are supported:15/// * `px`: logical pixels16/// * `%`: percentage17/// * `vw`: percentage of the viewport width18/// * `vh`: percentage of the viewport height19/// * `vmin`: percentage of the viewport's smaller dimension20/// * `vmax`: percentage of the viewport's larger dimension21///22/// Additionally, `auto` will be parsed as [`Val::Auto`].23#[derive(Copy, Clone, Debug, Reflect)]24#[reflect(Default, PartialEq, Debug, Clone)]25#[cfg_attr(26feature = "serialize",27derive(serde::Serialize, serde::Deserialize),28reflect(Serialize, Deserialize)29)]30pub enum Val {31/// Automatically determine the value based on the context and other [`Node`](crate::Node) properties.32Auto,33/// Set this value in logical pixels.34Px(f32),35/// Set the value as a percentage of its parent node's length along a specific axis.36///37/// If the UI node has no parent, the percentage is calculated based on the window's length38/// along the corresponding axis.39///40/// The chosen axis depends on the [`Node`](crate::Node) field set:41/// * For `flex_basis`, the percentage is relative to the main-axis length determined by the `flex_direction`.42/// * For `gap`, `min_size`, `size`, and `max_size`:43/// - `width` is relative to the parent's width.44/// - `height` is relative to the parent's height.45/// * For `margin`, `padding`, and `border` values: the percentage is relative to the parent node's width.46/// * For positions, `left` and `right` are relative to the parent's width, while `bottom` and `top` are relative to the parent's height.47Percent(f32),48/// Set this value in percent of the viewport width49Vw(f32),50/// Set this value in percent of the viewport height51Vh(f32),52/// Set this value in percent of the viewport's smaller dimension.53VMin(f32),54/// Set this value in percent of the viewport's larger dimension.55VMax(f32),56}5758#[derive(Debug, Error, PartialEq, Eq)]59pub enum ValParseError {60UnitMissing,61ValueMissing,62InvalidValue,63InvalidUnit,64}6566impl core::fmt::Display for ValParseError {67fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {68match self {69ValParseError::UnitMissing => write!(f, "unit missing"),70ValParseError::ValueMissing => write!(f, "value missing"),71ValParseError::InvalidValue => write!(f, "invalid value"),72ValParseError::InvalidUnit => write!(f, "invalid unit"),73}74}75}7677impl core::str::FromStr for Val {78type Err = ValParseError;7980fn from_str(s: &str) -> Result<Self, Self::Err> {81let s = s.trim();8283if s.eq_ignore_ascii_case("auto") {84return Ok(Val::Auto);85}8687let Some(end_of_number) = s88.bytes()89.position(|c| !(c.is_ascii_digit() || c == b'.' || c == b'-' || c == b'+'))90else {91return Err(ValParseError::UnitMissing);92};9394if end_of_number == 0 {95return Err(ValParseError::ValueMissing);96}9798let (value, unit) = s.split_at(end_of_number);99100let value: f32 = value.parse().map_err(|_| ValParseError::InvalidValue)?;101102let unit = unit.trim();103104if unit.eq_ignore_ascii_case("px") {105Ok(Val::Px(value))106} else if unit.eq_ignore_ascii_case("%") {107Ok(Val::Percent(value))108} else if unit.eq_ignore_ascii_case("vw") {109Ok(Val::Vw(value))110} else if unit.eq_ignore_ascii_case("vh") {111Ok(Val::Vh(value))112} else if unit.eq_ignore_ascii_case("vmin") {113Ok(Val::VMin(value))114} else if unit.eq_ignore_ascii_case("vmax") {115Ok(Val::VMax(value))116} else {117Err(ValParseError::InvalidUnit)118}119}120}121122impl PartialEq for Val {123fn eq(&self, other: &Self) -> bool {124let same_unit = matches!(125(self, other),126(Self::Auto, Self::Auto)127| (Self::Px(_), Self::Px(_))128| (Self::Percent(_), Self::Percent(_))129| (Self::Vw(_), Self::Vw(_))130| (Self::Vh(_), Self::Vh(_))131| (Self::VMin(_), Self::VMin(_))132| (Self::VMax(_), Self::VMax(_))133);134135let left = match self {136Self::Auto => None,137Self::Px(v)138| Self::Percent(v)139| Self::Vw(v)140| Self::Vh(v)141| Self::VMin(v)142| Self::VMax(v) => Some(v),143};144145let right = match other {146Self::Auto => None,147Self::Px(v)148| Self::Percent(v)149| Self::Vw(v)150| Self::Vh(v)151| Self::VMin(v)152| Self::VMax(v) => Some(v),153};154155match (same_unit, left, right) {156(true, a, b) => a == b,157// All zero-value variants are considered equal.158(false, Some(&a), Some(&b)) => a == 0. && b == 0.,159_ => false,160}161}162}163164impl Val {165pub const DEFAULT: Self = Self::Auto;166pub const ZERO: Self = Self::Px(0.0);167168/// Returns a [`UiRect`] with its `left` equal to this value,169/// and all other fields set to `Val::ZERO`.170///171///172/// # Example173///174/// ```175/// # use bevy_ui::{UiRect, Val};176/// #177/// let ui_rect = Val::Px(1.).left();178///179/// assert_eq!(ui_rect.left, Val::Px(1.));180/// assert_eq!(ui_rect.right, Val::ZERO);181/// assert_eq!(ui_rect.top, Val::ZERO);182/// assert_eq!(ui_rect.bottom, Val::ZERO);183/// ```184pub const fn left(self) -> UiRect {185UiRect::left(self)186}187188/// Returns a [`UiRect`] with its `right` equal to this value,189/// and all other fields set to `Val::ZERO`.190///191///192/// # Example193///194/// ```195/// # use bevy_ui::{UiRect, Val};196/// #197/// let ui_rect = Val::Px(1.).right();198///199/// assert_eq!(ui_rect.left, Val::ZERO);200/// assert_eq!(ui_rect.right, Val::Px(1.));201/// assert_eq!(ui_rect.top, Val::ZERO);202/// assert_eq!(ui_rect.bottom, Val::ZERO);203/// ```204pub const fn right(self) -> UiRect {205UiRect::right(self)206}207208/// Returns a [`UiRect`] with its `top` equal to this value,209/// and all other fields set to `Val::ZERO`.210///211///212/// # Example213///214/// ```215/// # use bevy_ui::{UiRect, Val};216/// #217/// let ui_rect = Val::Px(1.).top();218///219/// assert_eq!(ui_rect.left, Val::ZERO);220/// assert_eq!(ui_rect.right, Val::ZERO);221/// assert_eq!(ui_rect.top, Val::Px(1.));222/// assert_eq!(ui_rect.bottom, Val::ZERO);223/// ```224pub const fn top(self) -> UiRect {225UiRect::top(self)226}227228/// Returns a [`UiRect`] with its `bottom` equal to this value,229/// and all other fields set to `Val::ZERO`.230///231///232/// # Example233///234/// ```235/// # use bevy_ui::{UiRect, Val};236/// #237/// let ui_rect = Val::Px(1.).bottom();238///239/// assert_eq!(ui_rect.left, Val::ZERO);240/// assert_eq!(ui_rect.right, Val::ZERO);241/// assert_eq!(ui_rect.top, Val::ZERO);242/// assert_eq!(ui_rect.bottom, Val::Px(1.));243/// ```244pub const fn bottom(self) -> UiRect {245UiRect::bottom(self)246}247248/// Returns a [`UiRect`] with all its fields equal to this value.249///250/// # Example251///252/// ```253/// # use bevy_ui::{UiRect, Val};254/// #255/// let ui_rect = Val::Px(1.).all();256///257/// assert_eq!(ui_rect.left, Val::Px(1.));258/// assert_eq!(ui_rect.right, Val::Px(1.));259/// assert_eq!(ui_rect.top, Val::Px(1.));260/// assert_eq!(ui_rect.bottom, Val::Px(1.));261/// ```262pub const fn all(self) -> UiRect {263UiRect::all(self)264}265266/// Returns a [`UiRect`] with all its `left` and `right` equal to this value,267/// and its `top` and `bottom` set to `Val::ZERO`.268///269/// # Example270///271/// ```272/// # use bevy_ui::{UiRect, Val};273/// #274/// let ui_rect = Val::Px(1.).horizontal();275///276/// assert_eq!(ui_rect.left, Val::Px(1.));277/// assert_eq!(ui_rect.right, Val::Px(1.));278/// assert_eq!(ui_rect.top, Val::ZERO);279/// assert_eq!(ui_rect.bottom, Val::ZERO);280/// ```281pub const fn horizontal(self) -> UiRect {282UiRect::horizontal(self)283}284285/// Returns a [`UiRect`] with all its `top` and `bottom` equal to this value,286/// and its `left` and `right` set to `Val::ZERO`.287///288/// # Example289///290/// ```291/// # use bevy_ui::{UiRect, Val};292/// #293/// let ui_rect = Val::Px(1.).vertical();294///295/// assert_eq!(ui_rect.left, Val::ZERO);296/// assert_eq!(ui_rect.right, Val::ZERO);297/// assert_eq!(ui_rect.top, Val::Px(1.));298/// assert_eq!(ui_rect.bottom, Val::Px(1.));299/// ```300pub const fn vertical(self) -> UiRect {301UiRect::vertical(self)302}303}304305impl Default for Val {306fn default() -> Self {307Self::DEFAULT308}309}310311impl Mul<f32> for Val {312type Output = Val;313314fn mul(self, rhs: f32) -> Self::Output {315match self {316Val::Auto => Val::Auto,317Val::Px(value) => Val::Px(value * rhs),318Val::Percent(value) => Val::Percent(value * rhs),319Val::Vw(value) => Val::Vw(value * rhs),320Val::Vh(value) => Val::Vh(value * rhs),321Val::VMin(value) => Val::VMin(value * rhs),322Val::VMax(value) => Val::VMax(value * rhs),323}324}325}326327impl MulAssign<f32> for Val {328fn mul_assign(&mut self, rhs: f32) {329match self {330Val::Auto => {}331Val::Px(value)332| Val::Percent(value)333| Val::Vw(value)334| Val::Vh(value)335| Val::VMin(value)336| Val::VMax(value) => *value *= rhs,337}338}339}340341impl Div<f32> for Val {342type Output = Val;343344fn div(self, rhs: f32) -> Self::Output {345match self {346Val::Auto => Val::Auto,347Val::Px(value) => Val::Px(value / rhs),348Val::Percent(value) => Val::Percent(value / rhs),349Val::Vw(value) => Val::Vw(value / rhs),350Val::Vh(value) => Val::Vh(value / rhs),351Val::VMin(value) => Val::VMin(value / rhs),352Val::VMax(value) => Val::VMax(value / rhs),353}354}355}356357impl DivAssign<f32> for Val {358fn div_assign(&mut self, rhs: f32) {359match self {360Val::Auto => {}361Val::Px(value)362| Val::Percent(value)363| Val::Vw(value)364| Val::Vh(value)365| Val::VMin(value)366| Val::VMax(value) => *value /= rhs,367}368}369}370371impl Neg for Val {372type Output = Val;373374fn neg(self) -> Self::Output {375match self {376Val::Px(value) => Val::Px(-value),377Val::Percent(value) => Val::Percent(-value),378Val::Vw(value) => Val::Vw(-value),379Val::Vh(value) => Val::Vh(-value),380Val::VMin(value) => Val::VMin(-value),381Val::VMax(value) => Val::VMax(-value),382_ => self,383}384}385}386387#[derive(Debug, Eq, PartialEq, Clone, Copy, Error)]388pub enum ValArithmeticError {389#[error("the given variant of Val is not evaluable (non-numeric)")]390NonEvaluable,391}392393impl Val {394/// Resolves this [`Val`] to a value in physical pixels from the given `scale_factor`, `physical_base_value`,395/// and `physical_target_size` context values.396///397/// Returns a [`ValArithmeticError::NonEvaluable`] if the [`Val`] is impossible to resolve into a concrete value.398pub const fn resolve(399self,400scale_factor: f32,401physical_base_value: f32,402physical_target_size: Vec2,403) -> Result<f32, ValArithmeticError> {404match self {405Val::Percent(value) => Ok(physical_base_value * value / 100.0),406Val::Px(value) => Ok(value * scale_factor),407Val::Vw(value) => Ok(physical_target_size.x * value / 100.0),408Val::Vh(value) => Ok(physical_target_size.y * value / 100.0),409Val::VMin(value) => {410Ok(physical_target_size.x.min(physical_target_size.y) * value / 100.0)411}412Val::VMax(value) => {413Ok(physical_target_size.x.max(physical_target_size.y) * value / 100.0)414}415Val::Auto => Err(ValArithmeticError::NonEvaluable),416}417}418}419420/// All the types that should be able to be used in the [`Val`] enum should implement this trait.421///422/// Instead of just implementing `Into<Val>` a custom trait is added.423/// This is done in order to prevent having to define a default unit, which could lead to confusion especially for newcomers.424pub trait ValNum {425/// Called by the [`Val`] helper functions to convert the implementing type to an `f32` that can426/// be used by [`Val`].427fn val_num_f32(self) -> f32;428}429430macro_rules! impl_to_val_num {431($($impl_type:ty),*$(,)?) => {432$(433impl ValNum for $impl_type {434fn val_num_f32(self) -> f32 {435self as f32436}437}438)*439};440}441442impl_to_val_num!(f32, f64, i8, i16, i32, i64, u8, u16, u32, u64, usize, isize);443444/// Returns a [`Val::Auto`] where the value is automatically determined445/// based on the context and other [`Node`](crate::Node) properties.446pub const fn auto() -> Val {447Val::Auto448}449450/// Returns a [`Val::Px`] representing a value in logical pixels.451pub fn px<T: ValNum>(value: T) -> Val {452Val::Px(value.val_num_f32())453}454455/// Returns a [`Val::Percent`] representing a percentage of the parent node's length456/// along a specific axis.457///458/// If the UI node has no parent, the percentage is based on the window's length459/// along that axis.460///461/// Axis rules:462/// * For `flex_basis`, the percentage is relative to the main-axis length determined by the `flex_direction`.463/// * For `gap`, `min_size`, `size`, and `max_size`:464/// - `width` is relative to the parent's width.465/// - `height` is relative to the parent's height.466/// * For `margin`, `padding`, and `border` values: the percentage is relative to the parent's width.467/// * For positions, `left` and `right` are relative to the parent's width, while `bottom` and `top` are relative to the parent's height.468pub fn percent<T: ValNum>(value: T) -> Val {469Val::Percent(value.val_num_f32())470}471472/// Returns a [`Val::Vw`] representing a percentage of the viewport width.473pub fn vw<T: ValNum>(value: T) -> Val {474Val::Vw(value.val_num_f32())475}476477/// Returns a [`Val::Vh`] representing a percentage of the viewport height.478pub fn vh<T: ValNum>(value: T) -> Val {479Val::Vh(value.val_num_f32())480}481482/// Returns a [`Val::VMin`] representing a percentage of the viewport's smaller dimension.483pub fn vmin<T: ValNum>(value: T) -> Val {484Val::VMin(value.val_num_f32())485}486487/// Returns a [`Val::VMax`] representing a percentage of the viewport's larger dimension.488pub fn vmax<T: ValNum>(value: T) -> Val {489Val::VMax(value.val_num_f32())490}491492/// A type which is commonly used to define margins, paddings and borders.493///494/// # Examples495///496/// ## Margin497///498/// A margin is used to create space around UI elements, outside of any defined borders.499///500/// ```501/// # use bevy_ui::{UiRect, Val};502/// #503/// let margin = UiRect::all(Val::Auto); // Centers the UI element504/// ```505///506/// ## Padding507///508/// A padding is used to create space around UI elements, inside of any defined borders.509///510/// ```511/// # use bevy_ui::{UiRect, Val};512/// #513/// let padding = UiRect {514/// left: Val::Px(10.0),515/// right: Val::Px(20.0),516/// top: Val::Px(30.0),517/// bottom: Val::Px(40.0),518/// };519/// ```520///521/// ## Borders522///523/// A border is used to define the width of the border of a UI element.524///525/// ```526/// # use bevy_ui::{UiRect, Val};527/// #528/// let border = UiRect {529/// left: Val::Px(10.0),530/// right: Val::Px(20.0),531/// top: Val::Px(30.0),532/// bottom: Val::Px(40.0),533/// };534/// ```535#[derive(Copy, Clone, PartialEq, Debug, Reflect)]536#[reflect(Default, PartialEq, Debug, Clone)]537#[cfg_attr(538feature = "serialize",539derive(serde::Serialize, serde::Deserialize),540reflect(Serialize, Deserialize)541)]542pub struct UiRect {543/// The value corresponding to the left side of the UI rect.544pub left: Val,545/// The value corresponding to the right side of the UI rect.546pub right: Val,547/// The value corresponding to the top side of the UI rect.548pub top: Val,549/// The value corresponding to the bottom side of the UI rect.550pub bottom: Val,551}552553impl UiRect {554pub const DEFAULT: Self = Self::all(Val::ZERO);555pub const ZERO: Self = Self::all(Val::ZERO);556pub const AUTO: Self = Self::all(Val::Auto);557558/// Creates a new [`UiRect`] from the values specified.559///560/// # Example561///562/// ```563/// # use bevy_ui::{UiRect, Val};564/// #565/// let ui_rect = UiRect::new(566/// Val::Px(10.0),567/// Val::Px(20.0),568/// Val::Px(30.0),569/// Val::Px(40.0),570/// );571///572/// assert_eq!(ui_rect.left, Val::Px(10.0));573/// assert_eq!(ui_rect.right, Val::Px(20.0));574/// assert_eq!(ui_rect.top, Val::Px(30.0));575/// assert_eq!(ui_rect.bottom, Val::Px(40.0));576/// ```577pub const fn new(left: Val, right: Val, top: Val, bottom: Val) -> Self {578UiRect {579left,580right,581top,582bottom,583}584}585586/// Creates a new [`UiRect`] where all sides have the same value.587///588/// # Example589///590/// ```591/// # use bevy_ui::{UiRect, Val};592/// #593/// let ui_rect = UiRect::all(Val::Px(10.0));594///595/// assert_eq!(ui_rect.left, Val::Px(10.0));596/// assert_eq!(ui_rect.right, Val::Px(10.0));597/// assert_eq!(ui_rect.top, Val::Px(10.0));598/// assert_eq!(ui_rect.bottom, Val::Px(10.0));599/// ```600pub const fn all(value: Val) -> Self {601UiRect {602left: value,603right: value,604top: value,605bottom: value,606}607}608609/// Creates a new [`UiRect`] from the values specified in logical pixels.610///611/// This is a shortcut for [`UiRect::new()`], applying [`Val::Px`] to all arguments.612///613/// # Example614///615/// ```616/// # use bevy_ui::{UiRect, Val};617/// #618/// let ui_rect = UiRect::px(10., 20., 30., 40.);619/// assert_eq!(ui_rect.left, Val::Px(10.));620/// assert_eq!(ui_rect.right, Val::Px(20.));621/// assert_eq!(ui_rect.top, Val::Px(30.));622/// assert_eq!(ui_rect.bottom, Val::Px(40.));623/// ```624pub const fn px(left: f32, right: f32, top: f32, bottom: f32) -> Self {625UiRect {626left: Val::Px(left),627right: Val::Px(right),628top: Val::Px(top),629bottom: Val::Px(bottom),630}631}632633/// Creates a new [`UiRect`] from the values specified in percentages.634///635/// This is a shortcut for [`UiRect::new()`], applying [`Val::Percent`] to all arguments.636///637/// # Example638///639/// ```640/// # use bevy_ui::{UiRect, Val};641/// #642/// let ui_rect = UiRect::percent(5., 10., 2., 1.);643/// assert_eq!(ui_rect.left, Val::Percent(5.));644/// assert_eq!(ui_rect.right, Val::Percent(10.));645/// assert_eq!(ui_rect.top, Val::Percent(2.));646/// assert_eq!(ui_rect.bottom, Val::Percent(1.));647/// ```648pub const fn percent(left: f32, right: f32, top: f32, bottom: f32) -> Self {649UiRect {650left: Val::Percent(left),651right: Val::Percent(right),652top: Val::Percent(top),653bottom: Val::Percent(bottom),654}655}656657/// Creates a new [`UiRect`] where `left` and `right` take the given value,658/// and `top` and `bottom` set to zero `Val::ZERO`.659///660/// # Example661///662/// ```663/// # use bevy_ui::{UiRect, Val};664/// #665/// let ui_rect = UiRect::horizontal(Val::Px(10.0));666///667/// assert_eq!(ui_rect.left, Val::Px(10.0));668/// assert_eq!(ui_rect.right, Val::Px(10.0));669/// assert_eq!(ui_rect.top, Val::ZERO);670/// assert_eq!(ui_rect.bottom, Val::ZERO);671/// ```672pub const fn horizontal(value: Val) -> Self {673Self {674left: value,675right: value,676..Self::DEFAULT677}678}679680/// Creates a new [`UiRect`] where `top` and `bottom` take the given value,681/// and `left` and `right` are set to `Val::ZERO`.682///683/// # Example684///685/// ```686/// # use bevy_ui::{UiRect, Val};687/// #688/// let ui_rect = UiRect::vertical(Val::Px(10.0));689///690/// assert_eq!(ui_rect.left, Val::ZERO);691/// assert_eq!(ui_rect.right, Val::ZERO);692/// assert_eq!(ui_rect.top, Val::Px(10.0));693/// assert_eq!(ui_rect.bottom, Val::Px(10.0));694/// ```695pub const fn vertical(value: Val) -> Self {696Self {697top: value,698bottom: value,699..Self::DEFAULT700}701}702703/// Creates a new [`UiRect`] where both `left` and `right` take the value of `horizontal`, and both `top` and `bottom` take the value of `vertical`.704///705/// # Example706///707/// ```708/// # use bevy_ui::{UiRect, Val};709/// #710/// let ui_rect = UiRect::axes(Val::Px(10.0), Val::Percent(15.0));711///712/// assert_eq!(ui_rect.left, Val::Px(10.0));713/// assert_eq!(ui_rect.right, Val::Px(10.0));714/// assert_eq!(ui_rect.top, Val::Percent(15.0));715/// assert_eq!(ui_rect.bottom, Val::Percent(15.0));716/// ```717pub const fn axes(horizontal: Val, vertical: Val) -> Self {718Self {719left: horizontal,720right: horizontal,721top: vertical,722bottom: vertical,723}724}725726/// Creates a new [`UiRect`] where `left` takes the given value, and727/// the other fields are set to `Val::ZERO`.728///729/// # Example730///731/// ```732/// # use bevy_ui::{UiRect, Val};733/// #734/// let ui_rect = UiRect::left(Val::Px(10.0));735///736/// assert_eq!(ui_rect.left, Val::Px(10.0));737/// assert_eq!(ui_rect.right, Val::ZERO);738/// assert_eq!(ui_rect.top, Val::ZERO);739/// assert_eq!(ui_rect.bottom, Val::ZERO);740/// ```741pub const fn left(left: Val) -> Self {742Self {743left,744..Self::DEFAULT745}746}747748/// Creates a new [`UiRect`] where `right` takes the given value,749/// and the other fields are set to `Val::ZERO`.750///751/// # Example752///753/// ```754/// # use bevy_ui::{UiRect, Val};755/// #756/// let ui_rect = UiRect::right(Val::Px(10.0));757///758/// assert_eq!(ui_rect.left, Val::ZERO);759/// assert_eq!(ui_rect.right, Val::Px(10.0));760/// assert_eq!(ui_rect.top, Val::ZERO);761/// assert_eq!(ui_rect.bottom, Val::ZERO);762/// ```763pub const fn right(right: Val) -> Self {764Self {765right,766..Self::DEFAULT767}768}769770/// Creates a new [`UiRect`] where `top` takes the given value,771/// and the other fields are set to `Val::ZERO`.772///773/// # Example774///775/// ```776/// # use bevy_ui::{UiRect, Val};777/// #778/// let ui_rect = UiRect::top(Val::Px(10.0));779///780/// assert_eq!(ui_rect.left, Val::ZERO);781/// assert_eq!(ui_rect.right, Val::ZERO);782/// assert_eq!(ui_rect.top, Val::Px(10.0));783/// assert_eq!(ui_rect.bottom, Val::ZERO);784/// ```785pub const fn top(top: Val) -> Self {786Self {787top,788..Self::DEFAULT789}790}791792/// Creates a new [`UiRect`] where `bottom` takes the given value,793/// and the other fields are set to `Val::ZERO`.794///795/// # Example796///797/// ```798/// # use bevy_ui::{UiRect, Val};799/// #800/// let ui_rect = UiRect::bottom(Val::Px(10.0));801///802/// assert_eq!(ui_rect.left, Val::ZERO);803/// assert_eq!(ui_rect.right, Val::ZERO);804/// assert_eq!(ui_rect.top, Val::ZERO);805/// assert_eq!(ui_rect.bottom, Val::Px(10.0));806/// ```807pub const fn bottom(bottom: Val) -> Self {808Self {809bottom,810..Self::DEFAULT811}812}813814/// Returns the [`UiRect`] with its `left` field set to the given value.815///816/// # Example817///818/// ```819/// # use bevy_ui::{UiRect, Val};820/// #821/// let ui_rect = UiRect::all(Val::Px(20.0)).with_left(Val::Px(10.0));822/// assert_eq!(ui_rect.left, Val::Px(10.0));823/// assert_eq!(ui_rect.right, Val::Px(20.0));824/// assert_eq!(ui_rect.top, Val::Px(20.0));825/// assert_eq!(ui_rect.bottom, Val::Px(20.0));826/// ```827#[inline]828pub const fn with_left(mut self, left: Val) -> Self {829self.left = left;830self831}832833/// Returns the [`UiRect`] with its `right` field set to the given value.834///835/// # Example836///837/// ```838/// # use bevy_ui::{UiRect, Val};839/// #840/// let ui_rect = UiRect::all(Val::Px(20.0)).with_right(Val::Px(10.0));841/// assert_eq!(ui_rect.left, Val::Px(20.0));842/// assert_eq!(ui_rect.right, Val::Px(10.0));843/// assert_eq!(ui_rect.top, Val::Px(20.0));844/// assert_eq!(ui_rect.bottom, Val::Px(20.0));845/// ```846#[inline]847pub const fn with_right(mut self, right: Val) -> Self {848self.right = right;849self850}851852/// Returns the [`UiRect`] with its `top` field set to the given value.853///854/// # Example855///856/// ```857/// # use bevy_ui::{UiRect, Val};858/// #859/// let ui_rect = UiRect::all(Val::Px(20.0)).with_top(Val::Px(10.0));860/// assert_eq!(ui_rect.left, Val::Px(20.0));861/// assert_eq!(ui_rect.right, Val::Px(20.0));862/// assert_eq!(ui_rect.top, Val::Px(10.0));863/// assert_eq!(ui_rect.bottom, Val::Px(20.0));864/// ```865#[inline]866pub const fn with_top(mut self, top: Val) -> Self {867self.top = top;868self869}870871/// Returns the [`UiRect`] with its `bottom` field set to the given value.872///873/// # Example874///875/// ```876/// # use bevy_ui::{UiRect, Val};877/// #878/// let ui_rect = UiRect::all(Val::Px(20.0)).with_bottom(Val::Px(10.0));879/// assert_eq!(ui_rect.left, Val::Px(20.0));880/// assert_eq!(ui_rect.right, Val::Px(20.0));881/// assert_eq!(ui_rect.top, Val::Px(20.0));882/// assert_eq!(ui_rect.bottom, Val::Px(10.0));883/// ```884#[inline]885pub const fn with_bottom(mut self, bottom: Val) -> Self {886self.bottom = bottom;887self888}889}890891impl Default for UiRect {892fn default() -> Self {893Self::DEFAULT894}895}896897impl From<Val> for UiRect {898fn from(value: Val) -> Self {899UiRect::all(value)900}901}902903#[derive(Debug, Clone, Copy, PartialEq, Reflect)]904#[reflect(Default, Debug, PartialEq)]905#[cfg_attr(906feature = "serialize",907derive(serde::Serialize, serde::Deserialize),908reflect(Serialize, Deserialize)909)]910/// Responsive position relative to a UI node.911pub struct UiPosition {912/// Normalized anchor point913pub anchor: Vec2,914/// Responsive horizontal position relative to the anchor point915pub x: Val,916/// Responsive vertical position relative to the anchor point917pub y: Val,918}919920impl Default for UiPosition {921fn default() -> Self {922Self::CENTER923}924}925926impl UiPosition {927/// Position at the given normalized anchor point928pub const fn anchor(anchor: Vec2) -> Self {929Self {930anchor,931x: Val::ZERO,932y: Val::ZERO,933}934}935936/// Position at the top-left corner937pub const TOP_LEFT: Self = Self::anchor(Vec2::new(-0.5, -0.5));938939/// Position at the center of the left edge940pub const LEFT: Self = Self::anchor(Vec2::new(-0.5, 0.0));941942/// Position at the bottom-left corner943pub const BOTTOM_LEFT: Self = Self::anchor(Vec2::new(-0.5, 0.5));944945/// Position at the center of the top edge946pub const TOP: Self = Self::anchor(Vec2::new(0.0, -0.5));947948/// Position at the center of the element949pub const CENTER: Self = Self::anchor(Vec2::new(0.0, 0.0));950951/// Position at the center of the bottom edge952pub const BOTTOM: Self = Self::anchor(Vec2::new(0.0, 0.5));953954/// Position at the top-right corner955pub const TOP_RIGHT: Self = Self::anchor(Vec2::new(0.5, -0.5));956957/// Position at the center of the right edge958pub const RIGHT: Self = Self::anchor(Vec2::new(0.5, 0.0));959960/// Position at the bottom-right corner961pub const BOTTOM_RIGHT: Self = Self::anchor(Vec2::new(0.5, 0.5));962963/// Create a new position964pub const fn new(anchor: Vec2, x: Val, y: Val) -> Self {965Self { anchor, x, y }966}967968/// Creates a position from self with the given `x` and `y` coordinates969pub const fn at(self, x: Val, y: Val) -> Self {970Self { x, y, ..self }971}972973/// Creates a position from self with the given `x` coordinate974pub const fn at_x(self, x: Val) -> Self {975Self { x, ..self }976}977978/// Creates a position from self with the given `y` coordinate979pub const fn at_y(self, y: Val) -> Self {980Self { y, ..self }981}982983/// Creates a position in logical pixels from self with the given `x` and `y` coordinates984pub const fn at_px(self, x: f32, y: f32) -> Self {985self.at(Val::Px(x), Val::Px(y))986}987988/// Creates a percentage position from self with the given `x` and `y` coordinates989pub const fn at_percent(self, x: f32, y: f32) -> Self {990self.at(Val::Percent(x), Val::Percent(y))991}992993/// Creates a position from self with the given `anchor` point994pub const fn with_anchor(self, anchor: Vec2) -> Self {995Self { anchor, ..self }996}997998/// Position relative to the top-left corner999pub const fn top_left(x: Val, y: Val) -> Self {1000Self::TOP_LEFT.at(x, y)1001}10021003/// Position relative to the left edge1004pub const fn left(x: Val, y: Val) -> Self {1005Self::LEFT.at(x, y)1006}10071008/// Position relative to the bottom-left corner1009pub const fn bottom_left(x: Val, y: Val) -> Self {1010Self::BOTTOM_LEFT.at(x, y)1011}10121013/// Position relative to the top edge1014pub const fn top(x: Val, y: Val) -> Self {1015Self::TOP.at(x, y)1016}10171018/// Position relative to the center1019pub const fn center(x: Val, y: Val) -> Self {1020Self::CENTER.at(x, y)1021}10221023/// Position relative to the bottom edge1024pub const fn bottom(x: Val, y: Val) -> Self {1025Self::BOTTOM.at(x, y)1026}10271028/// Position relative to the top-right corner1029pub const fn top_right(x: Val, y: Val) -> Self {1030Self::TOP_RIGHT.at(x, y)1031}10321033/// Position relative to the right edge1034pub const fn right(x: Val, y: Val) -> Self {1035Self::RIGHT.at(x, y)1036}10371038/// Position relative to the bottom-right corner1039pub const fn bottom_right(x: Val, y: Val) -> Self {1040Self::BOTTOM_RIGHT.at(x, y)1041}10421043/// Resolves the `Position` into physical coordinates.1044pub fn resolve(1045self,1046scale_factor: f32,1047physical_size: Vec2,1048physical_target_size: Vec2,1049) -> Vec2 {1050let d = self.anchor.map(|p| if 0. < p { -1. } else { 1. });10511052physical_size * self.anchor1053+ d * Vec2::new(1054self.x1055.resolve(scale_factor, physical_size.x, physical_target_size)1056.unwrap_or(0.),1057self.y1058.resolve(scale_factor, physical_size.y, physical_target_size)1059.unwrap_or(0.),1060)1061}1062}10631064impl From<Val> for UiPosition {1065fn from(x: Val) -> Self {1066Self { x, ..default() }1067}1068}10691070impl From<(Val, Val)> for UiPosition {1071fn from((x, y): (Val, Val)) -> Self {1072Self { x, y, ..default() }1073}1074}10751076#[cfg(test)]1077mod tests {1078use crate::geometry::*;1079use bevy_math::vec2;10801081#[test]1082fn val_evaluate() {1083let size = 250.;1084let viewport_size = vec2(1000., 500.);1085let result = Val::Percent(80.).resolve(1., size, viewport_size).unwrap();10861087assert_eq!(result, size * 0.8);1088}10891090#[test]1091fn val_resolve_px() {1092let size = 250.;1093let viewport_size = vec2(1000., 500.);1094let result = Val::Px(10.).resolve(1., size, viewport_size).unwrap();10951096assert_eq!(result, 10.);1097}10981099#[test]1100fn val_resolve_viewport_coords() {1101let size = 250.;1102let viewport_size = vec2(500., 500.);11031104for value in (-10..10).map(|value| value as f32) {1105// for a square viewport there should be no difference between `Vw` and `Vh` and between `Vmin` and `Vmax`.1106assert_eq!(1107Val::Vw(value).resolve(1., size, viewport_size),1108Val::Vh(value).resolve(1., size, viewport_size)1109);1110assert_eq!(1111Val::VMin(value).resolve(1., size, viewport_size),1112Val::VMax(value).resolve(1., size, viewport_size)1113);1114assert_eq!(1115Val::VMin(value).resolve(1., size, viewport_size),1116Val::Vw(value).resolve(1., size, viewport_size)1117);1118}11191120let viewport_size = vec2(1000., 500.);1121assert_eq!(1122Val::Vw(100.).resolve(1., size, viewport_size).unwrap(),11231000.1124);1125assert_eq!(1126Val::Vh(100.).resolve(1., size, viewport_size).unwrap(),1127500.1128);1129assert_eq!(Val::Vw(60.).resolve(1., size, viewport_size).unwrap(), 600.);1130assert_eq!(Val::Vh(40.).resolve(1., size, viewport_size).unwrap(), 200.);1131assert_eq!(1132Val::VMin(50.).resolve(1., size, viewport_size).unwrap(),1133250.1134);1135assert_eq!(1136Val::VMax(75.).resolve(1., size, viewport_size).unwrap(),1137750.1138);1139}11401141#[test]1142fn val_auto_is_non_evaluable() {1143let size = 250.;1144let viewport_size = vec2(1000., 500.);1145let resolve_auto = Val::Auto.resolve(1., size, viewport_size);11461147assert_eq!(resolve_auto, Err(ValArithmeticError::NonEvaluable));1148}11491150#[test]1151fn val_arithmetic_error_messages() {1152assert_eq!(1153format!("{}", ValArithmeticError::NonEvaluable),1154"the given variant of Val is not evaluable (non-numeric)"1155);1156}11571158#[test]1159fn val_str_parse() {1160assert_eq!("auto".parse::<Val>(), Ok(Val::Auto));1161assert_eq!("Auto".parse::<Val>(), Ok(Val::Auto));1162assert_eq!("AUTO".parse::<Val>(), Ok(Val::Auto));11631164assert_eq!("3px".parse::<Val>(), Ok(Val::Px(3.)));1165assert_eq!("3 px".parse::<Val>(), Ok(Val::Px(3.)));1166assert_eq!("3.5px".parse::<Val>(), Ok(Val::Px(3.5)));1167assert_eq!("-3px".parse::<Val>(), Ok(Val::Px(-3.)));1168assert_eq!("3.5 PX".parse::<Val>(), Ok(Val::Px(3.5)));11691170assert_eq!("3%".parse::<Val>(), Ok(Val::Percent(3.)));1171assert_eq!("3 %".parse::<Val>(), Ok(Val::Percent(3.)));1172assert_eq!("3.5%".parse::<Val>(), Ok(Val::Percent(3.5)));1173assert_eq!("-3%".parse::<Val>(), Ok(Val::Percent(-3.)));11741175assert_eq!("3vw".parse::<Val>(), Ok(Val::Vw(3.)));1176assert_eq!("3 vw".parse::<Val>(), Ok(Val::Vw(3.)));1177assert_eq!("3.5vw".parse::<Val>(), Ok(Val::Vw(3.5)));1178assert_eq!("-3vw".parse::<Val>(), Ok(Val::Vw(-3.)));1179assert_eq!("3.5 VW".parse::<Val>(), Ok(Val::Vw(3.5)));11801181assert_eq!("3vh".parse::<Val>(), Ok(Val::Vh(3.)));1182assert_eq!("3 vh".parse::<Val>(), Ok(Val::Vh(3.)));1183assert_eq!("3.5vh".parse::<Val>(), Ok(Val::Vh(3.5)));1184assert_eq!("-3vh".parse::<Val>(), Ok(Val::Vh(-3.)));1185assert_eq!("3.5 VH".parse::<Val>(), Ok(Val::Vh(3.5)));11861187assert_eq!("3vmin".parse::<Val>(), Ok(Val::VMin(3.)));1188assert_eq!("3 vmin".parse::<Val>(), Ok(Val::VMin(3.)));1189assert_eq!("3.5vmin".parse::<Val>(), Ok(Val::VMin(3.5)));1190assert_eq!("-3vmin".parse::<Val>(), Ok(Val::VMin(-3.)));1191assert_eq!("3.5 VMIN".parse::<Val>(), Ok(Val::VMin(3.5)));11921193assert_eq!("3vmax".parse::<Val>(), Ok(Val::VMax(3.)));1194assert_eq!("3 vmax".parse::<Val>(), Ok(Val::VMax(3.)));1195assert_eq!("3.5vmax".parse::<Val>(), Ok(Val::VMax(3.5)));1196assert_eq!("-3vmax".parse::<Val>(), Ok(Val::VMax(-3.)));1197assert_eq!("3.5 VMAX".parse::<Val>(), Ok(Val::VMax(3.5)));11981199assert_eq!("".parse::<Val>(), Err(ValParseError::UnitMissing));1200assert_eq!(1201"hello world".parse::<Val>(),1202Err(ValParseError::ValueMissing)1203);1204assert_eq!("3".parse::<Val>(), Err(ValParseError::UnitMissing));1205assert_eq!("3.5".parse::<Val>(), Err(ValParseError::UnitMissing));1206assert_eq!("3pxx".parse::<Val>(), Err(ValParseError::InvalidUnit));1207assert_eq!("3.5pxx".parse::<Val>(), Err(ValParseError::InvalidUnit));1208assert_eq!("3-3px".parse::<Val>(), Err(ValParseError::InvalidValue));1209assert_eq!("3.5-3px".parse::<Val>(), Err(ValParseError::InvalidValue));1210}12111212#[test]1213fn default_val_equals_const_default_val() {1214assert_eq!(Val::default(), Val::DEFAULT);1215}12161217#[test]1218fn uirect_default_equals_const_default() {1219assert_eq!(UiRect::default(), UiRect::all(Val::ZERO));1220assert_eq!(UiRect::default(), UiRect::DEFAULT);1221}12221223#[test]1224fn test_uirect_axes() {1225let x = Val::Px(1.);1226let y = Val::Vw(4.);1227let r = UiRect::axes(x, y);1228let h = UiRect::horizontal(x);1229let v = UiRect::vertical(y);12301231assert_eq!(r.top, v.top);1232assert_eq!(r.bottom, v.bottom);1233assert_eq!(r.left, h.left);1234assert_eq!(r.right, h.right);1235}12361237#[test]1238fn uirect_px() {1239let r = UiRect::px(3., 5., 20., 999.);1240assert_eq!(r.left, Val::Px(3.));1241assert_eq!(r.right, Val::Px(5.));1242assert_eq!(r.top, Val::Px(20.));1243assert_eq!(r.bottom, Val::Px(999.));1244}12451246#[test]1247fn uirect_percent() {1248let r = UiRect::percent(3., 5., 20., 99.);1249assert_eq!(r.left, Val::Percent(3.));1250assert_eq!(r.right, Val::Percent(5.));1251assert_eq!(r.top, Val::Percent(20.));1252assert_eq!(r.bottom, Val::Percent(99.));1253}12541255#[test]1256fn val_constructor_fns_return_correct_val_variant() {1257assert_eq!(auto(), Val::Auto);1258assert_eq!(px(0.0), Val::Px(0.0));1259assert_eq!(percent(0.0), Val::Percent(0.0));1260assert_eq!(vw(0.0), Val::Vw(0.0));1261assert_eq!(vh(0.0), Val::Vh(0.0));1262assert_eq!(vmin(0.0), Val::VMin(0.0));1263assert_eq!(vmax(0.0), Val::VMax(0.0));1264}1265}126612671268