use bevy_math::{MismatchedUnitsError, StableInterpolate as _, TryStableInterpolate, 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}419420impl TryStableInterpolate for Val {421type Error = MismatchedUnitsError;422423/// # Example424///425/// ```426/// # use bevy_ui::Val;427/// # use bevy_math::TryStableInterpolate;428/// assert!(matches!(Val::Px(0.0).try_interpolate_stable(&Val::Px(10.0), 0.5), Ok(Val::Px(5.0))));429/// ```430fn try_interpolate_stable(&self, other: &Self, t: f32) -> Result<Self, Self::Error> {431match (self, other) {432(Val::Px(a), Val::Px(b)) => Ok(Val::Px(a.interpolate_stable(b, t))),433(Val::Percent(a), Val::Percent(b)) => Ok(Val::Percent(a.interpolate_stable(b, t))),434(Val::Vw(a), Val::Vw(b)) => Ok(Val::Vw(a.interpolate_stable(b, t))),435(Val::Vh(a), Val::Vh(b)) => Ok(Val::Vh(a.interpolate_stable(b, t))),436(Val::VMin(a), Val::VMin(b)) => Ok(Val::VMin(a.interpolate_stable(b, t))),437(Val::VMax(a), Val::VMax(b)) => Ok(Val::VMax(a.interpolate_stable(b, t))),438(Val::Auto, Val::Auto) => Ok(Val::Auto),439_ => Err(MismatchedUnitsError),440}441}442}443444/// All the types that should be able to be used in the [`Val`] enum should implement this trait.445///446/// Instead of just implementing `Into<Val>` a custom trait is added.447/// This is done in order to prevent having to define a default unit, which could lead to confusion especially for newcomers.448pub trait ValNum {449/// Called by the [`Val`] helper functions to convert the implementing type to an `f32` that can450/// be used by [`Val`].451fn val_num_f32(self) -> f32;452}453454macro_rules! impl_to_val_num {455($($impl_type:ty),*$(,)?) => {456$(457impl ValNum for $impl_type {458fn val_num_f32(self) -> f32 {459self as f32460}461}462)*463};464}465466impl_to_val_num!(f32, f64, i8, i16, i32, i64, u8, u16, u32, u64, usize, isize);467468/// Returns a [`Val::Auto`] where the value is automatically determined469/// based on the context and other [`Node`](crate::Node) properties.470pub const fn auto() -> Val {471Val::Auto472}473474/// Returns a [`Val::Px`] representing a value in logical pixels.475pub fn px<T: ValNum>(value: T) -> Val {476Val::Px(value.val_num_f32())477}478479/// Returns a [`Val::Percent`] representing a percentage of the parent node's length480/// along a specific axis.481///482/// If the UI node has no parent, the percentage is based on the window's length483/// along that axis.484///485/// Axis rules:486/// * For `flex_basis`, the percentage is relative to the main-axis length determined by the `flex_direction`.487/// * For `gap`, `min_size`, `size`, and `max_size`:488/// - `width` is relative to the parent's width.489/// - `height` is relative to the parent's height.490/// * For `margin`, `padding`, and `border` values: the percentage is relative to the parent's width.491/// * For positions, `left` and `right` are relative to the parent's width, while `bottom` and `top` are relative to the parent's height.492pub fn percent<T: ValNum>(value: T) -> Val {493Val::Percent(value.val_num_f32())494}495496/// Returns a [`Val::Vw`] representing a percentage of the viewport width.497pub fn vw<T: ValNum>(value: T) -> Val {498Val::Vw(value.val_num_f32())499}500501/// Returns a [`Val::Vh`] representing a percentage of the viewport height.502pub fn vh<T: ValNum>(value: T) -> Val {503Val::Vh(value.val_num_f32())504}505506/// Returns a [`Val::VMin`] representing a percentage of the viewport's smaller dimension.507pub fn vmin<T: ValNum>(value: T) -> Val {508Val::VMin(value.val_num_f32())509}510511/// Returns a [`Val::VMax`] representing a percentage of the viewport's larger dimension.512pub fn vmax<T: ValNum>(value: T) -> Val {513Val::VMax(value.val_num_f32())514}515516/// A type which is commonly used to define margins, paddings and borders.517///518/// # Examples519///520/// ## Margin521///522/// A margin is used to create space around UI elements, outside of any defined borders.523///524/// ```525/// # use bevy_ui::{UiRect, Val};526/// #527/// let margin = UiRect::all(Val::Auto); // Centers the UI element528/// ```529///530/// ## Padding531///532/// A padding is used to create space around UI elements, inside of any defined borders.533///534/// ```535/// # use bevy_ui::{UiRect, Val};536/// #537/// let padding = UiRect {538/// left: Val::Px(10.0),539/// right: Val::Px(20.0),540/// top: Val::Px(30.0),541/// bottom: Val::Px(40.0),542/// };543/// ```544///545/// ## Borders546///547/// A border is used to define the width of the border of a UI element.548///549/// ```550/// # use bevy_ui::{UiRect, Val};551/// #552/// let border = UiRect {553/// left: Val::Px(10.0),554/// right: Val::Px(20.0),555/// top: Val::Px(30.0),556/// bottom: Val::Px(40.0),557/// };558/// ```559#[derive(Copy, Clone, PartialEq, Debug, Reflect)]560#[reflect(Default, PartialEq, Debug, Clone)]561#[cfg_attr(562feature = "serialize",563derive(serde::Serialize, serde::Deserialize),564reflect(Serialize, Deserialize)565)]566pub struct UiRect {567/// The value corresponding to the left side of the UI rect.568pub left: Val,569/// The value corresponding to the right side of the UI rect.570pub right: Val,571/// The value corresponding to the top side of the UI rect.572pub top: Val,573/// The value corresponding to the bottom side of the UI rect.574pub bottom: Val,575}576577impl UiRect {578pub const DEFAULT: Self = Self::all(Val::ZERO);579pub const ZERO: Self = Self::all(Val::ZERO);580pub const AUTO: Self = Self::all(Val::Auto);581582/// Creates a new [`UiRect`] from the values specified.583///584/// # Example585///586/// ```587/// # use bevy_ui::{UiRect, Val};588/// #589/// let ui_rect = UiRect::new(590/// Val::Px(10.0),591/// Val::Px(20.0),592/// Val::Px(30.0),593/// Val::Px(40.0),594/// );595///596/// assert_eq!(ui_rect.left, Val::Px(10.0));597/// assert_eq!(ui_rect.right, Val::Px(20.0));598/// assert_eq!(ui_rect.top, Val::Px(30.0));599/// assert_eq!(ui_rect.bottom, Val::Px(40.0));600/// ```601pub const fn new(left: Val, right: Val, top: Val, bottom: Val) -> Self {602UiRect {603left,604right,605top,606bottom,607}608}609610/// Creates a new [`UiRect`] where all sides have the same value.611///612/// # Example613///614/// ```615/// # use bevy_ui::{UiRect, Val};616/// #617/// let ui_rect = UiRect::all(Val::Px(10.0));618///619/// assert_eq!(ui_rect.left, Val::Px(10.0));620/// assert_eq!(ui_rect.right, Val::Px(10.0));621/// assert_eq!(ui_rect.top, Val::Px(10.0));622/// assert_eq!(ui_rect.bottom, Val::Px(10.0));623/// ```624pub const fn all(value: Val) -> Self {625UiRect {626left: value,627right: value,628top: value,629bottom: value,630}631}632633/// Creates a new [`UiRect`] from the values specified in logical pixels.634///635/// This is a shortcut for [`UiRect::new()`], applying [`Val::Px`] to all arguments.636///637/// # Example638///639/// ```640/// # use bevy_ui::{UiRect, Val};641/// #642/// let ui_rect = UiRect::px(10., 20., 30., 40.);643/// assert_eq!(ui_rect.left, Val::Px(10.));644/// assert_eq!(ui_rect.right, Val::Px(20.));645/// assert_eq!(ui_rect.top, Val::Px(30.));646/// assert_eq!(ui_rect.bottom, Val::Px(40.));647/// ```648pub const fn px(left: f32, right: f32, top: f32, bottom: f32) -> Self {649UiRect {650left: Val::Px(left),651right: Val::Px(right),652top: Val::Px(top),653bottom: Val::Px(bottom),654}655}656657/// Creates a new [`UiRect`] from the values specified in percentages.658///659/// This is a shortcut for [`UiRect::new()`], applying [`Val::Percent`] to all arguments.660///661/// # Example662///663/// ```664/// # use bevy_ui::{UiRect, Val};665/// #666/// let ui_rect = UiRect::percent(5., 10., 2., 1.);667/// assert_eq!(ui_rect.left, Val::Percent(5.));668/// assert_eq!(ui_rect.right, Val::Percent(10.));669/// assert_eq!(ui_rect.top, Val::Percent(2.));670/// assert_eq!(ui_rect.bottom, Val::Percent(1.));671/// ```672pub const fn percent(left: f32, right: f32, top: f32, bottom: f32) -> Self {673UiRect {674left: Val::Percent(left),675right: Val::Percent(right),676top: Val::Percent(top),677bottom: Val::Percent(bottom),678}679}680681/// Creates a new [`UiRect`] where `left` and `right` take the given value,682/// and `top` and `bottom` set to zero `Val::ZERO`.683///684/// # Example685///686/// ```687/// # use bevy_ui::{UiRect, Val};688/// #689/// let ui_rect = UiRect::horizontal(Val::Px(10.0));690///691/// assert_eq!(ui_rect.left, Val::Px(10.0));692/// assert_eq!(ui_rect.right, Val::Px(10.0));693/// assert_eq!(ui_rect.top, Val::ZERO);694/// assert_eq!(ui_rect.bottom, Val::ZERO);695/// ```696pub const fn horizontal(value: Val) -> Self {697Self {698left: value,699right: value,700..Self::DEFAULT701}702}703704/// Creates a new [`UiRect`] where `top` and `bottom` take the given value,705/// and `left` and `right` are set to `Val::ZERO`.706///707/// # Example708///709/// ```710/// # use bevy_ui::{UiRect, Val};711/// #712/// let ui_rect = UiRect::vertical(Val::Px(10.0));713///714/// assert_eq!(ui_rect.left, Val::ZERO);715/// assert_eq!(ui_rect.right, Val::ZERO);716/// assert_eq!(ui_rect.top, Val::Px(10.0));717/// assert_eq!(ui_rect.bottom, Val::Px(10.0));718/// ```719pub const fn vertical(value: Val) -> Self {720Self {721top: value,722bottom: value,723..Self::DEFAULT724}725}726727/// Creates a new [`UiRect`] where both `left` and `right` take the value of `horizontal`, and both `top` and `bottom` take the value of `vertical`.728///729/// # Example730///731/// ```732/// # use bevy_ui::{UiRect, Val};733/// #734/// let ui_rect = UiRect::axes(Val::Px(10.0), Val::Percent(15.0));735///736/// assert_eq!(ui_rect.left, Val::Px(10.0));737/// assert_eq!(ui_rect.right, Val::Px(10.0));738/// assert_eq!(ui_rect.top, Val::Percent(15.0));739/// assert_eq!(ui_rect.bottom, Val::Percent(15.0));740/// ```741pub const fn axes(horizontal: Val, vertical: Val) -> Self {742Self {743left: horizontal,744right: horizontal,745top: vertical,746bottom: vertical,747}748}749750/// Creates a new [`UiRect`] where `left` takes the given value, and751/// the other fields are set to `Val::ZERO`.752///753/// # Example754///755/// ```756/// # use bevy_ui::{UiRect, Val};757/// #758/// let ui_rect = UiRect::left(Val::Px(10.0));759///760/// assert_eq!(ui_rect.left, Val::Px(10.0));761/// assert_eq!(ui_rect.right, Val::ZERO);762/// assert_eq!(ui_rect.top, Val::ZERO);763/// assert_eq!(ui_rect.bottom, Val::ZERO);764/// ```765pub const fn left(left: Val) -> Self {766Self {767left,768..Self::DEFAULT769}770}771772/// Creates a new [`UiRect`] where `right` takes the given value,773/// and the other fields are set to `Val::ZERO`.774///775/// # Example776///777/// ```778/// # use bevy_ui::{UiRect, Val};779/// #780/// let ui_rect = UiRect::right(Val::Px(10.0));781///782/// assert_eq!(ui_rect.left, Val::ZERO);783/// assert_eq!(ui_rect.right, Val::Px(10.0));784/// assert_eq!(ui_rect.top, Val::ZERO);785/// assert_eq!(ui_rect.bottom, Val::ZERO);786/// ```787pub const fn right(right: Val) -> Self {788Self {789right,790..Self::DEFAULT791}792}793794/// Creates a new [`UiRect`] where `top` takes the given value,795/// and the other fields are set to `Val::ZERO`.796///797/// # Example798///799/// ```800/// # use bevy_ui::{UiRect, Val};801/// #802/// let ui_rect = UiRect::top(Val::Px(10.0));803///804/// assert_eq!(ui_rect.left, Val::ZERO);805/// assert_eq!(ui_rect.right, Val::ZERO);806/// assert_eq!(ui_rect.top, Val::Px(10.0));807/// assert_eq!(ui_rect.bottom, Val::ZERO);808/// ```809pub const fn top(top: Val) -> Self {810Self {811top,812..Self::DEFAULT813}814}815816/// Creates a new [`UiRect`] where `bottom` takes the given value,817/// and the other fields are set to `Val::ZERO`.818///819/// # Example820///821/// ```822/// # use bevy_ui::{UiRect, Val};823/// #824/// let ui_rect = UiRect::bottom(Val::Px(10.0));825///826/// assert_eq!(ui_rect.left, Val::ZERO);827/// assert_eq!(ui_rect.right, Val::ZERO);828/// assert_eq!(ui_rect.top, Val::ZERO);829/// assert_eq!(ui_rect.bottom, Val::Px(10.0));830/// ```831pub const fn bottom(bottom: Val) -> Self {832Self {833bottom,834..Self::DEFAULT835}836}837838/// Returns the [`UiRect`] with its `left` field set to the given value.839///840/// # Example841///842/// ```843/// # use bevy_ui::{UiRect, Val};844/// #845/// let ui_rect = UiRect::all(Val::Px(20.0)).with_left(Val::Px(10.0));846/// assert_eq!(ui_rect.left, Val::Px(10.0));847/// assert_eq!(ui_rect.right, Val::Px(20.0));848/// assert_eq!(ui_rect.top, Val::Px(20.0));849/// assert_eq!(ui_rect.bottom, Val::Px(20.0));850/// ```851#[inline]852pub const fn with_left(mut self, left: Val) -> Self {853self.left = left;854self855}856857/// Returns the [`UiRect`] with its `right` field set to the given value.858///859/// # Example860///861/// ```862/// # use bevy_ui::{UiRect, Val};863/// #864/// let ui_rect = UiRect::all(Val::Px(20.0)).with_right(Val::Px(10.0));865/// assert_eq!(ui_rect.left, Val::Px(20.0));866/// assert_eq!(ui_rect.right, Val::Px(10.0));867/// assert_eq!(ui_rect.top, Val::Px(20.0));868/// assert_eq!(ui_rect.bottom, Val::Px(20.0));869/// ```870#[inline]871pub const fn with_right(mut self, right: Val) -> Self {872self.right = right;873self874}875876/// Returns the [`UiRect`] with its `top` field set to the given value.877///878/// # Example879///880/// ```881/// # use bevy_ui::{UiRect, Val};882/// #883/// let ui_rect = UiRect::all(Val::Px(20.0)).with_top(Val::Px(10.0));884/// assert_eq!(ui_rect.left, Val::Px(20.0));885/// assert_eq!(ui_rect.right, Val::Px(20.0));886/// assert_eq!(ui_rect.top, Val::Px(10.0));887/// assert_eq!(ui_rect.bottom, Val::Px(20.0));888/// ```889#[inline]890pub const fn with_top(mut self, top: Val) -> Self {891self.top = top;892self893}894895/// Returns the [`UiRect`] with its `bottom` field set to the given value.896///897/// # Example898///899/// ```900/// # use bevy_ui::{UiRect, Val};901/// #902/// let ui_rect = UiRect::all(Val::Px(20.0)).with_bottom(Val::Px(10.0));903/// assert_eq!(ui_rect.left, Val::Px(20.0));904/// assert_eq!(ui_rect.right, Val::Px(20.0));905/// assert_eq!(ui_rect.top, Val::Px(20.0));906/// assert_eq!(ui_rect.bottom, Val::Px(10.0));907/// ```908#[inline]909pub const fn with_bottom(mut self, bottom: Val) -> Self {910self.bottom = bottom;911self912}913}914915impl Default for UiRect {916fn default() -> Self {917Self::DEFAULT918}919}920921impl From<Val> for UiRect {922fn from(value: Val) -> Self {923UiRect::all(value)924}925}926927#[derive(Debug, Clone, Copy, PartialEq, Reflect)]928#[reflect(Default, Debug, PartialEq)]929#[cfg_attr(930feature = "serialize",931derive(serde::Serialize, serde::Deserialize),932reflect(Serialize, Deserialize)933)]934/// Responsive position relative to a UI node.935pub struct UiPosition {936/// Normalized anchor point937pub anchor: Vec2,938/// Responsive horizontal position relative to the anchor point939pub x: Val,940/// Responsive vertical position relative to the anchor point941pub y: Val,942}943944impl Default for UiPosition {945fn default() -> Self {946Self::CENTER947}948}949950impl UiPosition {951/// Position at the given normalized anchor point952pub const fn anchor(anchor: Vec2) -> Self {953Self {954anchor,955x: Val::ZERO,956y: Val::ZERO,957}958}959960/// Position at the top-left corner961pub const TOP_LEFT: Self = Self::anchor(Vec2::new(-0.5, -0.5));962963/// Position at the center of the left edge964pub const LEFT: Self = Self::anchor(Vec2::new(-0.5, 0.0));965966/// Position at the bottom-left corner967pub const BOTTOM_LEFT: Self = Self::anchor(Vec2::new(-0.5, 0.5));968969/// Position at the center of the top edge970pub const TOP: Self = Self::anchor(Vec2::new(0.0, -0.5));971972/// Position at the center of the element973pub const CENTER: Self = Self::anchor(Vec2::new(0.0, 0.0));974975/// Position at the center of the bottom edge976pub const BOTTOM: Self = Self::anchor(Vec2::new(0.0, 0.5));977978/// Position at the top-right corner979pub const TOP_RIGHT: Self = Self::anchor(Vec2::new(0.5, -0.5));980981/// Position at the center of the right edge982pub const RIGHT: Self = Self::anchor(Vec2::new(0.5, 0.0));983984/// Position at the bottom-right corner985pub const BOTTOM_RIGHT: Self = Self::anchor(Vec2::new(0.5, 0.5));986987/// Create a new position988pub const fn new(anchor: Vec2, x: Val, y: Val) -> Self {989Self { anchor, x, y }990}991992/// Creates a position from self with the given `x` and `y` coordinates993pub const fn at(self, x: Val, y: Val) -> Self {994Self { x, y, ..self }995}996997/// Creates a position from self with the given `x` coordinate998pub const fn at_x(self, x: Val) -> Self {999Self { x, ..self }1000}10011002/// Creates a position from self with the given `y` coordinate1003pub const fn at_y(self, y: Val) -> Self {1004Self { y, ..self }1005}10061007/// Creates a position in logical pixels from self with the given `x` and `y` coordinates1008pub const fn at_px(self, x: f32, y: f32) -> Self {1009self.at(Val::Px(x), Val::Px(y))1010}10111012/// Creates a percentage position from self with the given `x` and `y` coordinates1013pub const fn at_percent(self, x: f32, y: f32) -> Self {1014self.at(Val::Percent(x), Val::Percent(y))1015}10161017/// Creates a position from self with the given `anchor` point1018pub const fn with_anchor(self, anchor: Vec2) -> Self {1019Self { anchor, ..self }1020}10211022/// Position relative to the top-left corner1023pub const fn top_left(x: Val, y: Val) -> Self {1024Self::TOP_LEFT.at(x, y)1025}10261027/// Position relative to the left edge1028pub const fn left(x: Val, y: Val) -> Self {1029Self::LEFT.at(x, y)1030}10311032/// Position relative to the bottom-left corner1033pub const fn bottom_left(x: Val, y: Val) -> Self {1034Self::BOTTOM_LEFT.at(x, y)1035}10361037/// Position relative to the top edge1038pub const fn top(x: Val, y: Val) -> Self {1039Self::TOP.at(x, y)1040}10411042/// Position relative to the center1043pub const fn center(x: Val, y: Val) -> Self {1044Self::CENTER.at(x, y)1045}10461047/// Position relative to the bottom edge1048pub const fn bottom(x: Val, y: Val) -> Self {1049Self::BOTTOM.at(x, y)1050}10511052/// Position relative to the top-right corner1053pub const fn top_right(x: Val, y: Val) -> Self {1054Self::TOP_RIGHT.at(x, y)1055}10561057/// Position relative to the right edge1058pub const fn right(x: Val, y: Val) -> Self {1059Self::RIGHT.at(x, y)1060}10611062/// Position relative to the bottom-right corner1063pub const fn bottom_right(x: Val, y: Val) -> Self {1064Self::BOTTOM_RIGHT.at(x, y)1065}10661067/// Resolves the `Position` into physical coordinates.1068pub fn resolve(1069self,1070scale_factor: f32,1071physical_size: Vec2,1072physical_target_size: Vec2,1073) -> Vec2 {1074let d = self.anchor.map(|p| if 0. < p { -1. } else { 1. });10751076physical_size * self.anchor1077+ d * Vec2::new(1078self.x1079.resolve(scale_factor, physical_size.x, physical_target_size)1080.unwrap_or(0.),1081self.y1082.resolve(scale_factor, physical_size.y, physical_target_size)1083.unwrap_or(0.),1084)1085}1086}10871088impl From<Val> for UiPosition {1089fn from(x: Val) -> Self {1090Self { x, ..default() }1091}1092}10931094impl From<(Val, Val)> for UiPosition {1095fn from((x, y): (Val, Val)) -> Self {1096Self { x, y, ..default() }1097}1098}10991100#[cfg(test)]1101mod tests {1102use crate::geometry::*;1103use bevy_math::vec2;11041105#[test]1106fn val_evaluate() {1107let size = 250.;1108let viewport_size = vec2(1000., 500.);1109let result = Val::Percent(80.).resolve(1., size, viewport_size).unwrap();11101111assert_eq!(result, size * 0.8);1112}11131114#[test]1115fn val_resolve_px() {1116let size = 250.;1117let viewport_size = vec2(1000., 500.);1118let result = Val::Px(10.).resolve(1., size, viewport_size).unwrap();11191120assert_eq!(result, 10.);11211122let result = Val::Px(10.).resolve(3., size, viewport_size).unwrap();1123assert_eq!(result, 30.);1124let result = Val::Px(10.).resolve(0.25, size, viewport_size).unwrap();1125assert_eq!(result, 2.5);1126}11271128#[test]1129fn val_resolve_viewport_coords() {1130let size = 250.;1131let viewport_size = vec2(500., 500.);11321133for value in (-10..10).map(|value| value as f32) {1134// for a square viewport there should be no difference between `Vw` and `Vh` and between `Vmin` and `Vmax`.1135assert_eq!(1136Val::Vw(value).resolve(1., size, viewport_size),1137Val::Vh(value).resolve(1., size, viewport_size)1138);1139assert_eq!(1140Val::VMin(value).resolve(1., size, viewport_size),1141Val::VMax(value).resolve(1., size, viewport_size)1142);1143assert_eq!(1144Val::VMin(value).resolve(1., size, viewport_size),1145Val::Vw(value).resolve(1., size, viewport_size)1146);1147}11481149let viewport_size = vec2(1000., 500.);1150assert_eq!(1151Val::Vw(100.).resolve(1., size, viewport_size).unwrap(),11521000.1153);1154assert_eq!(1155Val::Vh(100.).resolve(1., size, viewport_size).unwrap(),1156500.1157);1158assert_eq!(Val::Vw(60.).resolve(1., size, viewport_size).unwrap(), 600.);1159assert_eq!(Val::Vh(40.).resolve(1., size, viewport_size).unwrap(), 200.);1160assert_eq!(1161Val::VMin(50.).resolve(1., size, viewport_size).unwrap(),1162250.1163);1164assert_eq!(1165Val::VMax(75.).resolve(1., size, viewport_size).unwrap(),1166750.1167);1168}11691170#[test]1171fn val_auto_is_non_evaluable() {1172let size = 250.;1173let viewport_size = vec2(1000., 500.);1174let resolve_auto = Val::Auto.resolve(1., size, viewport_size);11751176assert_eq!(resolve_auto, Err(ValArithmeticError::NonEvaluable));1177}11781179#[test]1180fn val_arithmetic_error_messages() {1181assert_eq!(1182format!("{}", ValArithmeticError::NonEvaluable),1183"the given variant of Val is not evaluable (non-numeric)"1184);1185}11861187#[test]1188fn val_str_parse() {1189assert_eq!("auto".parse::<Val>(), Ok(Val::Auto));1190assert_eq!("Auto".parse::<Val>(), Ok(Val::Auto));1191assert_eq!("AUTO".parse::<Val>(), Ok(Val::Auto));11921193assert_eq!("3px".parse::<Val>(), Ok(Val::Px(3.)));1194assert_eq!("3 px".parse::<Val>(), Ok(Val::Px(3.)));1195assert_eq!("3.5px".parse::<Val>(), Ok(Val::Px(3.5)));1196assert_eq!("-3px".parse::<Val>(), Ok(Val::Px(-3.)));1197assert_eq!("3.5 PX".parse::<Val>(), Ok(Val::Px(3.5)));11981199assert_eq!("3%".parse::<Val>(), Ok(Val::Percent(3.)));1200assert_eq!("3 %".parse::<Val>(), Ok(Val::Percent(3.)));1201assert_eq!("3.5%".parse::<Val>(), Ok(Val::Percent(3.5)));1202assert_eq!("-3%".parse::<Val>(), Ok(Val::Percent(-3.)));12031204assert_eq!("3vw".parse::<Val>(), Ok(Val::Vw(3.)));1205assert_eq!("3 vw".parse::<Val>(), Ok(Val::Vw(3.)));1206assert_eq!("3.5vw".parse::<Val>(), Ok(Val::Vw(3.5)));1207assert_eq!("-3vw".parse::<Val>(), Ok(Val::Vw(-3.)));1208assert_eq!("3.5 VW".parse::<Val>(), Ok(Val::Vw(3.5)));12091210assert_eq!("3vh".parse::<Val>(), Ok(Val::Vh(3.)));1211assert_eq!("3 vh".parse::<Val>(), Ok(Val::Vh(3.)));1212assert_eq!("3.5vh".parse::<Val>(), Ok(Val::Vh(3.5)));1213assert_eq!("-3vh".parse::<Val>(), Ok(Val::Vh(-3.)));1214assert_eq!("3.5 VH".parse::<Val>(), Ok(Val::Vh(3.5)));12151216assert_eq!("3vmin".parse::<Val>(), Ok(Val::VMin(3.)));1217assert_eq!("3 vmin".parse::<Val>(), Ok(Val::VMin(3.)));1218assert_eq!("3.5vmin".parse::<Val>(), Ok(Val::VMin(3.5)));1219assert_eq!("-3vmin".parse::<Val>(), Ok(Val::VMin(-3.)));1220assert_eq!("3.5 VMIN".parse::<Val>(), Ok(Val::VMin(3.5)));12211222assert_eq!("3vmax".parse::<Val>(), Ok(Val::VMax(3.)));1223assert_eq!("3 vmax".parse::<Val>(), Ok(Val::VMax(3.)));1224assert_eq!("3.5vmax".parse::<Val>(), Ok(Val::VMax(3.5)));1225assert_eq!("-3vmax".parse::<Val>(), Ok(Val::VMax(-3.)));1226assert_eq!("3.5 VMAX".parse::<Val>(), Ok(Val::VMax(3.5)));12271228assert_eq!("".parse::<Val>(), Err(ValParseError::UnitMissing));1229assert_eq!(1230"hello world".parse::<Val>(),1231Err(ValParseError::ValueMissing)1232);1233assert_eq!("3".parse::<Val>(), Err(ValParseError::UnitMissing));1234assert_eq!("3.5".parse::<Val>(), Err(ValParseError::UnitMissing));1235assert_eq!("3pxx".parse::<Val>(), Err(ValParseError::InvalidUnit));1236assert_eq!("3.5pxx".parse::<Val>(), Err(ValParseError::InvalidUnit));1237assert_eq!("3-3px".parse::<Val>(), Err(ValParseError::InvalidValue));1238assert_eq!("3.5-3px".parse::<Val>(), Err(ValParseError::InvalidValue));1239}12401241#[test]1242fn default_val_equals_const_default_val() {1243assert_eq!(Val::default(), Val::DEFAULT);1244}12451246#[test]1247fn uirect_default_equals_const_default() {1248assert_eq!(UiRect::default(), UiRect::all(Val::ZERO));1249assert_eq!(UiRect::default(), UiRect::DEFAULT);1250}12511252#[test]1253fn test_uirect_axes() {1254let x = Val::Px(1.);1255let y = Val::Vw(4.);1256let r = UiRect::axes(x, y);1257let h = UiRect::horizontal(x);1258let v = UiRect::vertical(y);12591260assert_eq!(r.top, v.top);1261assert_eq!(r.bottom, v.bottom);1262assert_eq!(r.left, h.left);1263assert_eq!(r.right, h.right);1264}12651266#[test]1267fn uirect_px() {1268let r = UiRect::px(3., 5., 20., 999.);1269assert_eq!(r.left, Val::Px(3.));1270assert_eq!(r.right, Val::Px(5.));1271assert_eq!(r.top, Val::Px(20.));1272assert_eq!(r.bottom, Val::Px(999.));1273}12741275#[test]1276fn uirect_percent() {1277let r = UiRect::percent(3., 5., 20., 99.);1278assert_eq!(r.left, Val::Percent(3.));1279assert_eq!(r.right, Val::Percent(5.));1280assert_eq!(r.top, Val::Percent(20.));1281assert_eq!(r.bottom, Val::Percent(99.));1282}12831284#[test]1285fn val_constructor_fns_return_correct_val_variant() {1286assert_eq!(auto(), Val::Auto);1287assert_eq!(px(0.0), Val::Px(0.0));1288assert_eq!(percent(0.0), Val::Percent(0.0));1289assert_eq!(vw(0.0), Val::Vw(0.0));1290assert_eq!(vh(0.0), Val::Vh(0.0));1291assert_eq!(vmin(0.0), Val::VMin(0.0));1292assert_eq!(vmax(0.0), Val::VMax(0.0));1293}1294}129512961297