bevy_ui/
geometry.rs

1use bevy_math::Vec2;
2use bevy_reflect::{std_traits::ReflectDefault, Reflect};
3use bevy_utils::default;
4use core::ops::{Div, DivAssign, Mul, MulAssign, Neg};
5use thiserror::Error;
6
7#[cfg(feature = "serialize")]
8use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
9
10/// Represents the possible value types for layout properties.
11///
12/// This enum allows specifying values for various [`Node`](crate::Node) properties in different units,
13/// such as logical pixels, percentages, or automatically determined values.
14///
15/// `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:
16/// * `px`: logical pixels
17/// * `%`: percentage
18/// * `vw`: percentage of the viewport width
19/// * `vh`: percentage of the viewport height
20/// * `vmin`: percentage of the viewport's smaller dimension
21/// * `vmax`: percentage of the viewport's larger dimension
22///
23/// Additionally, `auto` will be parsed as [`Val::Auto`].
24#[derive(Copy, Clone, Debug, Reflect)]
25#[reflect(Default, PartialEq, Debug, Clone)]
26#[cfg_attr(
27    feature = "serialize",
28    derive(serde::Serialize, serde::Deserialize),
29    reflect(Serialize, Deserialize)
30)]
31pub enum Val {
32    /// Automatically determine the value based on the context and other [`Node`](crate::Node) properties.
33    Auto,
34    /// Set this value in logical pixels.
35    Px(f32),
36    /// Set the value as a percentage of its parent node's length along a specific axis.
37    ///
38    /// If the UI node has no parent, the percentage is calculated based on the window's length
39    /// along the corresponding axis.
40    ///
41    /// The chosen axis depends on the [`Node`](crate::Node) field set:
42    /// * For `flex_basis`, the percentage is relative to the main-axis length determined by the `flex_direction`.
43    /// * For `gap`, `min_size`, `size`, and `max_size`:
44    ///   - `width` is relative to the parent's width.
45    ///   - `height` is relative to the parent's height.
46    /// * For `margin`, `padding`, and `border` values: the percentage is relative to the parent node's width.
47    /// * For positions, `left` and `right` are relative to the parent's width, while `bottom` and `top` are relative to the parent's height.
48    Percent(f32),
49    /// Set this value in percent of the viewport width
50    Vw(f32),
51    /// Set this value in percent of the viewport height
52    Vh(f32),
53    /// Set this value in percent of the viewport's smaller dimension.
54    VMin(f32),
55    /// Set this value in percent of the viewport's larger dimension.
56    VMax(f32),
57}
58
59#[derive(Debug, Error, PartialEq, Eq)]
60pub enum ValParseError {
61    UnitMissing,
62    ValueMissing,
63    InvalidValue,
64    InvalidUnit,
65}
66
67impl core::fmt::Display for ValParseError {
68    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
69        match self {
70            ValParseError::UnitMissing => write!(f, "unit missing"),
71            ValParseError::ValueMissing => write!(f, "value missing"),
72            ValParseError::InvalidValue => write!(f, "invalid value"),
73            ValParseError::InvalidUnit => write!(f, "invalid unit"),
74        }
75    }
76}
77
78impl core::str::FromStr for Val {
79    type Err = ValParseError;
80
81    fn from_str(s: &str) -> Result<Self, Self::Err> {
82        let s = s.trim();
83
84        if s.eq_ignore_ascii_case("auto") {
85            return Ok(Val::Auto);
86        }
87
88        let Some(end_of_number) = s
89            .bytes()
90            .position(|c| !(c.is_ascii_digit() || c == b'.' || c == b'-' || c == b'+'))
91        else {
92            return Err(ValParseError::UnitMissing);
93        };
94
95        if end_of_number == 0 {
96            return Err(ValParseError::ValueMissing);
97        }
98
99        let (value, unit) = s.split_at(end_of_number);
100
101        let value: f32 = value.parse().map_err(|_| ValParseError::InvalidValue)?;
102
103        let unit = unit.trim();
104
105        if unit.eq_ignore_ascii_case("px") {
106            Ok(Val::Px(value))
107        } else if unit.eq_ignore_ascii_case("%") {
108            Ok(Val::Percent(value))
109        } else if unit.eq_ignore_ascii_case("vw") {
110            Ok(Val::Vw(value))
111        } else if unit.eq_ignore_ascii_case("vh") {
112            Ok(Val::Vh(value))
113        } else if unit.eq_ignore_ascii_case("vmin") {
114            Ok(Val::VMin(value))
115        } else if unit.eq_ignore_ascii_case("vmax") {
116            Ok(Val::VMax(value))
117        } else {
118            Err(ValParseError::InvalidUnit)
119        }
120    }
121}
122
123impl PartialEq for Val {
124    fn eq(&self, other: &Self) -> bool {
125        let same_unit = matches!(
126            (self, other),
127            (Self::Auto, Self::Auto)
128                | (Self::Px(_), Self::Px(_))
129                | (Self::Percent(_), Self::Percent(_))
130                | (Self::Vw(_), Self::Vw(_))
131                | (Self::Vh(_), Self::Vh(_))
132                | (Self::VMin(_), Self::VMin(_))
133                | (Self::VMax(_), Self::VMax(_))
134        );
135
136        let left = match self {
137            Self::Auto => None,
138            Self::Px(v)
139            | Self::Percent(v)
140            | Self::Vw(v)
141            | Self::Vh(v)
142            | Self::VMin(v)
143            | Self::VMax(v) => Some(v),
144        };
145
146        let right = match other {
147            Self::Auto => None,
148            Self::Px(v)
149            | Self::Percent(v)
150            | Self::Vw(v)
151            | Self::Vh(v)
152            | Self::VMin(v)
153            | Self::VMax(v) => Some(v),
154        };
155
156        match (same_unit, left, right) {
157            (true, a, b) => a == b,
158            // All zero-value variants are considered equal.
159            (false, Some(&a), Some(&b)) => a == 0. && b == 0.,
160            _ => false,
161        }
162    }
163}
164
165impl Val {
166    pub const DEFAULT: Self = Self::Auto;
167    pub const ZERO: Self = Self::Px(0.0);
168
169    /// Returns a [`UiRect`] with its `left` equal to this value,
170    /// and all other fields set to `Val::ZERO`.
171    ///
172    ///
173    /// # Example
174    ///
175    /// ```
176    /// # use bevy_ui::{UiRect, Val};
177    /// #
178    /// let ui_rect = Val::Px(1.).left();
179    ///
180    /// assert_eq!(ui_rect.left, Val::Px(1.));
181    /// assert_eq!(ui_rect.right, Val::ZERO);
182    /// assert_eq!(ui_rect.top, Val::ZERO);
183    /// assert_eq!(ui_rect.bottom, Val::ZERO);
184    /// ```
185    pub const fn left(self) -> UiRect {
186        UiRect::left(self)
187    }
188
189    /// Returns a [`UiRect`] with its `right` equal to this value,
190    /// and all other fields set to `Val::ZERO`.
191    ///
192    ///
193    /// # Example
194    ///
195    /// ```
196    /// # use bevy_ui::{UiRect, Val};
197    /// #
198    /// let ui_rect = Val::Px(1.).right();
199    ///
200    /// assert_eq!(ui_rect.left, Val::ZERO);
201    /// assert_eq!(ui_rect.right, Val::Px(1.));
202    /// assert_eq!(ui_rect.top, Val::ZERO);
203    /// assert_eq!(ui_rect.bottom, Val::ZERO);
204    /// ```
205    pub const fn right(self) -> UiRect {
206        UiRect::right(self)
207    }
208
209    /// Returns a [`UiRect`] with its `top` equal to this value,
210    /// and all other fields set to `Val::ZERO`.
211    ///
212    ///
213    /// # Example
214    ///
215    /// ```
216    /// # use bevy_ui::{UiRect, Val};
217    /// #
218    /// let ui_rect = Val::Px(1.).top();
219    ///
220    /// assert_eq!(ui_rect.left, Val::ZERO);
221    /// assert_eq!(ui_rect.right, Val::ZERO);
222    /// assert_eq!(ui_rect.top, Val::Px(1.));
223    /// assert_eq!(ui_rect.bottom, Val::ZERO);
224    /// ```
225    pub const fn top(self) -> UiRect {
226        UiRect::top(self)
227    }
228
229    /// Returns a [`UiRect`] with its `bottom` equal to this value,
230    /// and all other fields set to `Val::ZERO`.
231    ///
232    ///
233    /// # Example
234    ///
235    /// ```
236    /// # use bevy_ui::{UiRect, Val};
237    /// #
238    /// let ui_rect = Val::Px(1.).bottom();
239    ///
240    /// assert_eq!(ui_rect.left, Val::ZERO);
241    /// assert_eq!(ui_rect.right, Val::ZERO);
242    /// assert_eq!(ui_rect.top, Val::ZERO);
243    /// assert_eq!(ui_rect.bottom, Val::Px(1.));
244    /// ```
245    pub const fn bottom(self) -> UiRect {
246        UiRect::bottom(self)
247    }
248
249    /// Returns a [`UiRect`] with all its fields equal to this value.
250    ///
251    /// # Example
252    ///
253    /// ```
254    /// # use bevy_ui::{UiRect, Val};
255    /// #
256    /// let ui_rect = Val::Px(1.).all();
257    ///
258    /// assert_eq!(ui_rect.left, Val::Px(1.));
259    /// assert_eq!(ui_rect.right, Val::Px(1.));
260    /// assert_eq!(ui_rect.top, Val::Px(1.));
261    /// assert_eq!(ui_rect.bottom, Val::Px(1.));
262    /// ```
263    pub const fn all(self) -> UiRect {
264        UiRect::all(self)
265    }
266
267    /// Returns a [`UiRect`] with all its `left` and `right` equal to this value,
268    /// and its `top` and `bottom` set to `Val::ZERO`.
269    ///
270    /// # Example
271    ///
272    /// ```
273    /// # use bevy_ui::{UiRect, Val};
274    /// #
275    /// let ui_rect = Val::Px(1.).horizontal();
276    ///
277    /// assert_eq!(ui_rect.left, Val::Px(1.));
278    /// assert_eq!(ui_rect.right, Val::Px(1.));
279    /// assert_eq!(ui_rect.top, Val::ZERO);
280    /// assert_eq!(ui_rect.bottom, Val::ZERO);
281    /// ```
282    pub const fn horizontal(self) -> UiRect {
283        UiRect::horizontal(self)
284    }
285
286    /// Returns a [`UiRect`] with all its `top` and `bottom` equal to this value,
287    /// and its `left` and `right` set to `Val::ZERO`.
288    ///
289    /// # Example
290    ///
291    /// ```
292    /// # use bevy_ui::{UiRect, Val};
293    /// #
294    /// let ui_rect = Val::Px(1.).vertical();
295    ///
296    /// assert_eq!(ui_rect.left, Val::ZERO);
297    /// assert_eq!(ui_rect.right, Val::ZERO);
298    /// assert_eq!(ui_rect.top, Val::Px(1.));
299    /// assert_eq!(ui_rect.bottom, Val::Px(1.));
300    /// ```
301    pub const fn vertical(self) -> UiRect {
302        UiRect::vertical(self)
303    }
304}
305
306impl Default for Val {
307    fn default() -> Self {
308        Self::DEFAULT
309    }
310}
311
312impl Mul<f32> for Val {
313    type Output = Val;
314
315    fn mul(self, rhs: f32) -> Self::Output {
316        match self {
317            Val::Auto => Val::Auto,
318            Val::Px(value) => Val::Px(value * rhs),
319            Val::Percent(value) => Val::Percent(value * rhs),
320            Val::Vw(value) => Val::Vw(value * rhs),
321            Val::Vh(value) => Val::Vh(value * rhs),
322            Val::VMin(value) => Val::VMin(value * rhs),
323            Val::VMax(value) => Val::VMax(value * rhs),
324        }
325    }
326}
327
328impl MulAssign<f32> for Val {
329    fn mul_assign(&mut self, rhs: f32) {
330        match self {
331            Val::Auto => {}
332            Val::Px(value)
333            | Val::Percent(value)
334            | Val::Vw(value)
335            | Val::Vh(value)
336            | Val::VMin(value)
337            | Val::VMax(value) => *value *= rhs,
338        }
339    }
340}
341
342impl Div<f32> for Val {
343    type Output = Val;
344
345    fn div(self, rhs: f32) -> Self::Output {
346        match self {
347            Val::Auto => Val::Auto,
348            Val::Px(value) => Val::Px(value / rhs),
349            Val::Percent(value) => Val::Percent(value / rhs),
350            Val::Vw(value) => Val::Vw(value / rhs),
351            Val::Vh(value) => Val::Vh(value / rhs),
352            Val::VMin(value) => Val::VMin(value / rhs),
353            Val::VMax(value) => Val::VMax(value / rhs),
354        }
355    }
356}
357
358impl DivAssign<f32> for Val {
359    fn div_assign(&mut self, rhs: f32) {
360        match self {
361            Val::Auto => {}
362            Val::Px(value)
363            | Val::Percent(value)
364            | Val::Vw(value)
365            | Val::Vh(value)
366            | Val::VMin(value)
367            | Val::VMax(value) => *value /= rhs,
368        }
369    }
370}
371
372impl Neg for Val {
373    type Output = Val;
374
375    fn neg(self) -> Self::Output {
376        match self {
377            Val::Px(value) => Val::Px(-value),
378            Val::Percent(value) => Val::Percent(-value),
379            Val::Vw(value) => Val::Vw(-value),
380            Val::Vh(value) => Val::Vh(-value),
381            Val::VMin(value) => Val::VMin(-value),
382            Val::VMax(value) => Val::VMax(-value),
383            _ => self,
384        }
385    }
386}
387
388#[derive(Debug, Eq, PartialEq, Clone, Copy, Error)]
389pub enum ValArithmeticError {
390    #[error("the given variant of Val is not evaluable (non-numeric)")]
391    NonEvaluable,
392}
393
394impl Val {
395    /// Resolves this [`Val`] to a value in physical pixels from the given `scale_factor`, `physical_base_value`,
396    /// and `physical_target_size` context values.
397    ///
398    /// Returns a [`ValArithmeticError::NonEvaluable`] if the [`Val`] is impossible to resolve into a concrete value.
399    pub const fn resolve(
400        self,
401        scale_factor: f32,
402        physical_base_value: f32,
403        physical_target_size: Vec2,
404    ) -> Result<f32, ValArithmeticError> {
405        match self {
406            Val::Percent(value) => Ok(physical_base_value * value / 100.0),
407            Val::Px(value) => Ok(value * scale_factor),
408            Val::Vw(value) => Ok(physical_target_size.x * value / 100.0),
409            Val::Vh(value) => Ok(physical_target_size.y * value / 100.0),
410            Val::VMin(value) => {
411                Ok(physical_target_size.x.min(physical_target_size.y) * value / 100.0)
412            }
413            Val::VMax(value) => {
414                Ok(physical_target_size.x.max(physical_target_size.y) * value / 100.0)
415            }
416            Val::Auto => Err(ValArithmeticError::NonEvaluable),
417        }
418    }
419}
420
421/// All the types that should be able to be used in the [`Val`] enum should implement this trait.
422///
423/// Instead of just implementing `Into<Val>` a custom trait is added.
424/// This is done in order to prevent having to define a default unit, which could lead to confusion especially for newcomers.
425pub trait ValNum {
426    /// Called by the [`Val`] helper functions to convert the implementing type to an `f32` that can
427    /// be used by [`Val`].
428    fn val_num_f32(self) -> f32;
429}
430
431macro_rules! impl_to_val_num {
432    ($($impl_type:ty),*$(,)?) => {
433        $(
434            impl ValNum for $impl_type {
435                fn val_num_f32(self) -> f32 {
436                    self as f32
437                }
438            }
439        )*
440    };
441}
442
443impl_to_val_num!(f32, f64, i8, i16, i32, i64, u8, u16, u32, u64, usize, isize);
444
445/// Returns a [`Val::Auto`] where the value is automatically determined
446/// based on the context and other [`Node`](crate::Node) properties.
447pub const fn auto() -> Val {
448    Val::Auto
449}
450
451/// Returns a [`Val::Px`] representing a value in logical pixels.
452pub fn px<T: ValNum>(value: T) -> Val {
453    Val::Px(value.val_num_f32())
454}
455
456/// Returns a [`Val::Percent`] representing a percentage of the parent node's length
457/// along a specific axis.
458///
459/// If the UI node has no parent, the percentage is based on the window's length
460/// along that axis.
461///
462/// Axis rules:
463/// * For `flex_basis`, the percentage is relative to the main-axis length determined by the `flex_direction`.
464/// * For `gap`, `min_size`, `size`, and `max_size`:
465///   - `width` is relative to the parent's width.
466///   - `height` is relative to the parent's height.
467/// * For `margin`, `padding`, and `border` values: the percentage is relative to the parent's width.
468/// * For positions, `left` and `right` are relative to the parent's width, while `bottom` and `top` are relative to the parent's height.
469pub fn percent<T: ValNum>(value: T) -> Val {
470    Val::Percent(value.val_num_f32())
471}
472
473/// Returns a [`Val::Vw`] representing a percentage of the viewport width.
474pub fn vw<T: ValNum>(value: T) -> Val {
475    Val::Vw(value.val_num_f32())
476}
477
478/// Returns a [`Val::Vh`] representing a percentage of the viewport height.
479pub fn vh<T: ValNum>(value: T) -> Val {
480    Val::Vh(value.val_num_f32())
481}
482
483/// Returns a [`Val::VMin`] representing a percentage of the viewport's smaller dimension.
484pub fn vmin<T: ValNum>(value: T) -> Val {
485    Val::VMin(value.val_num_f32())
486}
487
488/// Returns a [`Val::VMax`] representing a percentage of the viewport's larger dimension.
489pub fn vmax<T: ValNum>(value: T) -> Val {
490    Val::VMax(value.val_num_f32())
491}
492
493/// A type which is commonly used to define margins, paddings and borders.
494///
495/// # Examples
496///
497/// ## Margin
498///
499/// A margin is used to create space around UI elements, outside of any defined borders.
500///
501/// ```
502/// # use bevy_ui::{UiRect, Val};
503/// #
504/// let margin = UiRect::all(Val::Auto); // Centers the UI element
505/// ```
506///
507/// ## Padding
508///
509/// A padding is used to create space around UI elements, inside of any defined borders.
510///
511/// ```
512/// # use bevy_ui::{UiRect, Val};
513/// #
514/// let padding = UiRect {
515///     left: Val::Px(10.0),
516///     right: Val::Px(20.0),
517///     top: Val::Px(30.0),
518///     bottom: Val::Px(40.0),
519/// };
520/// ```
521///
522/// ## Borders
523///
524/// A border is used to define the width of the border of a UI element.
525///
526/// ```
527/// # use bevy_ui::{UiRect, Val};
528/// #
529/// let border = UiRect {
530///     left: Val::Px(10.0),
531///     right: Val::Px(20.0),
532///     top: Val::Px(30.0),
533///     bottom: Val::Px(40.0),
534/// };
535/// ```
536#[derive(Copy, Clone, PartialEq, Debug, Reflect)]
537#[reflect(Default, PartialEq, Debug, Clone)]
538#[cfg_attr(
539    feature = "serialize",
540    derive(serde::Serialize, serde::Deserialize),
541    reflect(Serialize, Deserialize)
542)]
543pub struct UiRect {
544    /// The value corresponding to the left side of the UI rect.
545    pub left: Val,
546    /// The value corresponding to the right side of the UI rect.
547    pub right: Val,
548    /// The value corresponding to the top side of the UI rect.
549    pub top: Val,
550    /// The value corresponding to the bottom side of the UI rect.
551    pub bottom: Val,
552}
553
554impl UiRect {
555    pub const DEFAULT: Self = Self::all(Val::ZERO);
556    pub const ZERO: Self = Self::all(Val::ZERO);
557    pub const AUTO: Self = Self::all(Val::Auto);
558
559    /// Creates a new [`UiRect`] from the values specified.
560    ///
561    /// # Example
562    ///
563    /// ```
564    /// # use bevy_ui::{UiRect, Val};
565    /// #
566    /// let ui_rect = UiRect::new(
567    ///     Val::Px(10.0),
568    ///     Val::Px(20.0),
569    ///     Val::Px(30.0),
570    ///     Val::Px(40.0),
571    /// );
572    ///
573    /// assert_eq!(ui_rect.left, Val::Px(10.0));
574    /// assert_eq!(ui_rect.right, Val::Px(20.0));
575    /// assert_eq!(ui_rect.top, Val::Px(30.0));
576    /// assert_eq!(ui_rect.bottom, Val::Px(40.0));
577    /// ```
578    pub const fn new(left: Val, right: Val, top: Val, bottom: Val) -> Self {
579        UiRect {
580            left,
581            right,
582            top,
583            bottom,
584        }
585    }
586
587    /// Creates a new [`UiRect`] where all sides have the same value.
588    ///
589    /// # Example
590    ///
591    /// ```
592    /// # use bevy_ui::{UiRect, Val};
593    /// #
594    /// let ui_rect = UiRect::all(Val::Px(10.0));
595    ///
596    /// assert_eq!(ui_rect.left, Val::Px(10.0));
597    /// assert_eq!(ui_rect.right, Val::Px(10.0));
598    /// assert_eq!(ui_rect.top, Val::Px(10.0));
599    /// assert_eq!(ui_rect.bottom, Val::Px(10.0));
600    /// ```
601    pub const fn all(value: Val) -> Self {
602        UiRect {
603            left: value,
604            right: value,
605            top: value,
606            bottom: value,
607        }
608    }
609
610    /// Creates a new [`UiRect`] from the values specified in logical pixels.
611    ///
612    /// This is a shortcut for [`UiRect::new()`], applying [`Val::Px`] to all arguments.
613    ///
614    /// # Example
615    ///
616    /// ```
617    /// # use bevy_ui::{UiRect, Val};
618    /// #
619    /// let ui_rect = UiRect::px(10., 20., 30., 40.);
620    /// assert_eq!(ui_rect.left, Val::Px(10.));
621    /// assert_eq!(ui_rect.right, Val::Px(20.));
622    /// assert_eq!(ui_rect.top, Val::Px(30.));
623    /// assert_eq!(ui_rect.bottom, Val::Px(40.));
624    /// ```
625    pub const fn px(left: f32, right: f32, top: f32, bottom: f32) -> Self {
626        UiRect {
627            left: Val::Px(left),
628            right: Val::Px(right),
629            top: Val::Px(top),
630            bottom: Val::Px(bottom),
631        }
632    }
633
634    /// Creates a new [`UiRect`] from the values specified in percentages.
635    ///
636    /// This is a shortcut for [`UiRect::new()`], applying [`Val::Percent`] to all arguments.
637    ///
638    /// # Example
639    ///
640    /// ```
641    /// # use bevy_ui::{UiRect, Val};
642    /// #
643    /// let ui_rect = UiRect::percent(5., 10., 2., 1.);
644    /// assert_eq!(ui_rect.left, Val::Percent(5.));
645    /// assert_eq!(ui_rect.right, Val::Percent(10.));
646    /// assert_eq!(ui_rect.top, Val::Percent(2.));
647    /// assert_eq!(ui_rect.bottom, Val::Percent(1.));
648    /// ```
649    pub const fn percent(left: f32, right: f32, top: f32, bottom: f32) -> Self {
650        UiRect {
651            left: Val::Percent(left),
652            right: Val::Percent(right),
653            top: Val::Percent(top),
654            bottom: Val::Percent(bottom),
655        }
656    }
657
658    /// Creates a new [`UiRect`] where `left` and `right` take the given value,
659    /// and `top` and `bottom` set to zero `Val::ZERO`.
660    ///
661    /// # Example
662    ///
663    /// ```
664    /// # use bevy_ui::{UiRect, Val};
665    /// #
666    /// let ui_rect = UiRect::horizontal(Val::Px(10.0));
667    ///
668    /// assert_eq!(ui_rect.left, Val::Px(10.0));
669    /// assert_eq!(ui_rect.right, Val::Px(10.0));
670    /// assert_eq!(ui_rect.top, Val::ZERO);
671    /// assert_eq!(ui_rect.bottom, Val::ZERO);
672    /// ```
673    pub const fn horizontal(value: Val) -> Self {
674        Self {
675            left: value,
676            right: value,
677            ..Self::DEFAULT
678        }
679    }
680
681    /// Creates a new [`UiRect`] where `top` and `bottom` take the given value,
682    /// and `left` and `right` are set to `Val::ZERO`.
683    ///
684    /// # Example
685    ///
686    /// ```
687    /// # use bevy_ui::{UiRect, Val};
688    /// #
689    /// let ui_rect = UiRect::vertical(Val::Px(10.0));
690    ///
691    /// assert_eq!(ui_rect.left, Val::ZERO);
692    /// assert_eq!(ui_rect.right, Val::ZERO);
693    /// assert_eq!(ui_rect.top, Val::Px(10.0));
694    /// assert_eq!(ui_rect.bottom, Val::Px(10.0));
695    /// ```
696    pub const fn vertical(value: Val) -> Self {
697        Self {
698            top: value,
699            bottom: value,
700            ..Self::DEFAULT
701        }
702    }
703
704    /// Creates a new [`UiRect`] where both `left` and `right` take the value of `horizontal`, and both `top` and `bottom` take the value of `vertical`.
705    ///
706    /// # Example
707    ///
708    /// ```
709    /// # use bevy_ui::{UiRect, Val};
710    /// #
711    /// let ui_rect = UiRect::axes(Val::Px(10.0), Val::Percent(15.0));
712    ///
713    /// assert_eq!(ui_rect.left, Val::Px(10.0));
714    /// assert_eq!(ui_rect.right, Val::Px(10.0));
715    /// assert_eq!(ui_rect.top, Val::Percent(15.0));
716    /// assert_eq!(ui_rect.bottom, Val::Percent(15.0));
717    /// ```
718    pub const fn axes(horizontal: Val, vertical: Val) -> Self {
719        Self {
720            left: horizontal,
721            right: horizontal,
722            top: vertical,
723            bottom: vertical,
724        }
725    }
726
727    /// Creates a new [`UiRect`] where `left` takes the given value, and
728    /// the other fields are set to `Val::ZERO`.
729    ///
730    /// # Example
731    ///
732    /// ```
733    /// # use bevy_ui::{UiRect, Val};
734    /// #
735    /// let ui_rect = UiRect::left(Val::Px(10.0));
736    ///
737    /// assert_eq!(ui_rect.left, Val::Px(10.0));
738    /// assert_eq!(ui_rect.right, Val::ZERO);
739    /// assert_eq!(ui_rect.top, Val::ZERO);
740    /// assert_eq!(ui_rect.bottom, Val::ZERO);
741    /// ```
742    pub const fn left(left: Val) -> Self {
743        Self {
744            left,
745            ..Self::DEFAULT
746        }
747    }
748
749    /// Creates a new [`UiRect`] where `right` takes the given value,
750    /// and the other fields are set to `Val::ZERO`.
751    ///
752    /// # Example
753    ///
754    /// ```
755    /// # use bevy_ui::{UiRect, Val};
756    /// #
757    /// let ui_rect = UiRect::right(Val::Px(10.0));
758    ///
759    /// assert_eq!(ui_rect.left, Val::ZERO);
760    /// assert_eq!(ui_rect.right, Val::Px(10.0));
761    /// assert_eq!(ui_rect.top, Val::ZERO);
762    /// assert_eq!(ui_rect.bottom, Val::ZERO);
763    /// ```
764    pub const fn right(right: Val) -> Self {
765        Self {
766            right,
767            ..Self::DEFAULT
768        }
769    }
770
771    /// Creates a new [`UiRect`] where `top` takes the given value,
772    /// and the other fields are set to `Val::ZERO`.
773    ///
774    /// # Example
775    ///
776    /// ```
777    /// # use bevy_ui::{UiRect, Val};
778    /// #
779    /// let ui_rect = UiRect::top(Val::Px(10.0));
780    ///
781    /// assert_eq!(ui_rect.left, Val::ZERO);
782    /// assert_eq!(ui_rect.right, Val::ZERO);
783    /// assert_eq!(ui_rect.top, Val::Px(10.0));
784    /// assert_eq!(ui_rect.bottom, Val::ZERO);
785    /// ```
786    pub const fn top(top: Val) -> Self {
787        Self {
788            top,
789            ..Self::DEFAULT
790        }
791    }
792
793    /// Creates a new [`UiRect`] where `bottom` takes the given value,
794    /// and the other fields are set to `Val::ZERO`.
795    ///
796    /// # Example
797    ///
798    /// ```
799    /// # use bevy_ui::{UiRect, Val};
800    /// #
801    /// let ui_rect = UiRect::bottom(Val::Px(10.0));
802    ///
803    /// assert_eq!(ui_rect.left, Val::ZERO);
804    /// assert_eq!(ui_rect.right, Val::ZERO);
805    /// assert_eq!(ui_rect.top, Val::ZERO);
806    /// assert_eq!(ui_rect.bottom, Val::Px(10.0));
807    /// ```
808    pub const fn bottom(bottom: Val) -> Self {
809        Self {
810            bottom,
811            ..Self::DEFAULT
812        }
813    }
814
815    /// Returns the [`UiRect`] with its `left` field set to the given value.
816    ///
817    /// # Example
818    ///
819    /// ```
820    /// # use bevy_ui::{UiRect, Val};
821    /// #
822    /// let ui_rect = UiRect::all(Val::Px(20.0)).with_left(Val::Px(10.0));
823    /// assert_eq!(ui_rect.left, Val::Px(10.0));
824    /// assert_eq!(ui_rect.right, Val::Px(20.0));
825    /// assert_eq!(ui_rect.top, Val::Px(20.0));
826    /// assert_eq!(ui_rect.bottom, Val::Px(20.0));
827    /// ```
828    #[inline]
829    pub const fn with_left(mut self, left: Val) -> Self {
830        self.left = left;
831        self
832    }
833
834    /// Returns the [`UiRect`] with its `right` field set to the given value.
835    ///
836    /// # Example
837    ///
838    /// ```
839    /// # use bevy_ui::{UiRect, Val};
840    /// #
841    /// let ui_rect = UiRect::all(Val::Px(20.0)).with_right(Val::Px(10.0));
842    /// assert_eq!(ui_rect.left, Val::Px(20.0));
843    /// assert_eq!(ui_rect.right, Val::Px(10.0));
844    /// assert_eq!(ui_rect.top, Val::Px(20.0));
845    /// assert_eq!(ui_rect.bottom, Val::Px(20.0));
846    /// ```
847    #[inline]
848    pub const fn with_right(mut self, right: Val) -> Self {
849        self.right = right;
850        self
851    }
852
853    /// Returns the [`UiRect`] with its `top` field set to the given value.
854    ///
855    /// # Example
856    ///
857    /// ```
858    /// # use bevy_ui::{UiRect, Val};
859    /// #
860    /// let ui_rect = UiRect::all(Val::Px(20.0)).with_top(Val::Px(10.0));
861    /// assert_eq!(ui_rect.left, Val::Px(20.0));
862    /// assert_eq!(ui_rect.right, Val::Px(20.0));
863    /// assert_eq!(ui_rect.top, Val::Px(10.0));
864    /// assert_eq!(ui_rect.bottom, Val::Px(20.0));
865    /// ```
866    #[inline]
867    pub const fn with_top(mut self, top: Val) -> Self {
868        self.top = top;
869        self
870    }
871
872    /// Returns the [`UiRect`] with its `bottom` field set to the given value.
873    ///
874    /// # Example
875    ///
876    /// ```
877    /// # use bevy_ui::{UiRect, Val};
878    /// #
879    /// let ui_rect = UiRect::all(Val::Px(20.0)).with_bottom(Val::Px(10.0));
880    /// assert_eq!(ui_rect.left, Val::Px(20.0));
881    /// assert_eq!(ui_rect.right, Val::Px(20.0));
882    /// assert_eq!(ui_rect.top, Val::Px(20.0));
883    /// assert_eq!(ui_rect.bottom, Val::Px(10.0));
884    /// ```
885    #[inline]
886    pub const fn with_bottom(mut self, bottom: Val) -> Self {
887        self.bottom = bottom;
888        self
889    }
890}
891
892impl Default for UiRect {
893    fn default() -> Self {
894        Self::DEFAULT
895    }
896}
897
898impl From<Val> for UiRect {
899    fn from(value: Val) -> Self {
900        UiRect::all(value)
901    }
902}
903
904#[derive(Debug, Clone, Copy, PartialEq, Reflect)]
905#[reflect(Default, Debug, PartialEq)]
906#[cfg_attr(
907    feature = "serialize",
908    derive(serde::Serialize, serde::Deserialize),
909    reflect(Serialize, Deserialize)
910)]
911/// Responsive position relative to a UI node.
912pub struct UiPosition {
913    /// Normalized anchor point
914    pub anchor: Vec2,
915    /// Responsive horizontal position relative to the anchor point
916    pub x: Val,
917    /// Responsive vertical position relative to the anchor point
918    pub y: Val,
919}
920
921impl Default for UiPosition {
922    fn default() -> Self {
923        Self::CENTER
924    }
925}
926
927impl UiPosition {
928    /// Position at the given normalized anchor point
929    pub const fn anchor(anchor: Vec2) -> Self {
930        Self {
931            anchor,
932            x: Val::ZERO,
933            y: Val::ZERO,
934        }
935    }
936
937    /// Position at the top-left corner
938    pub const TOP_LEFT: Self = Self::anchor(Vec2::new(-0.5, -0.5));
939
940    /// Position at the center of the left edge
941    pub const LEFT: Self = Self::anchor(Vec2::new(-0.5, 0.0));
942
943    /// Position at the bottom-left corner
944    pub const BOTTOM_LEFT: Self = Self::anchor(Vec2::new(-0.5, 0.5));
945
946    /// Position at the center of the top edge
947    pub const TOP: Self = Self::anchor(Vec2::new(0.0, -0.5));
948
949    /// Position at the center of the element
950    pub const CENTER: Self = Self::anchor(Vec2::new(0.0, 0.0));
951
952    /// Position at the center of the bottom edge
953    pub const BOTTOM: Self = Self::anchor(Vec2::new(0.0, 0.5));
954
955    /// Position at the top-right corner
956    pub const TOP_RIGHT: Self = Self::anchor(Vec2::new(0.5, -0.5));
957
958    /// Position at the center of the right edge
959    pub const RIGHT: Self = Self::anchor(Vec2::new(0.5, 0.0));
960
961    /// Position at the bottom-right corner
962    pub const BOTTOM_RIGHT: Self = Self::anchor(Vec2::new(0.5, 0.5));
963
964    /// Create a new position
965    pub const fn new(anchor: Vec2, x: Val, y: Val) -> Self {
966        Self { anchor, x, y }
967    }
968
969    /// Creates a position from self with the given `x` and `y` coordinates
970    pub const fn at(self, x: Val, y: Val) -> Self {
971        Self { x, y, ..self }
972    }
973
974    /// Creates a position from self with the given `x` coordinate
975    pub const fn at_x(self, x: Val) -> Self {
976        Self { x, ..self }
977    }
978
979    /// Creates a position from self with the given `y` coordinate
980    pub const fn at_y(self, y: Val) -> Self {
981        Self { y, ..self }
982    }
983
984    /// Creates a position in logical pixels from self with the given `x` and `y` coordinates
985    pub const fn at_px(self, x: f32, y: f32) -> Self {
986        self.at(Val::Px(x), Val::Px(y))
987    }
988
989    /// Creates a percentage position from self with the given `x` and `y` coordinates
990    pub const fn at_percent(self, x: f32, y: f32) -> Self {
991        self.at(Val::Percent(x), Val::Percent(y))
992    }
993
994    /// Creates a position from self with the given `anchor` point
995    pub const fn with_anchor(self, anchor: Vec2) -> Self {
996        Self { anchor, ..self }
997    }
998
999    /// Position relative to the top-left corner
1000    pub const fn top_left(x: Val, y: Val) -> Self {
1001        Self::TOP_LEFT.at(x, y)
1002    }
1003
1004    /// Position relative to the left edge
1005    pub const fn left(x: Val, y: Val) -> Self {
1006        Self::LEFT.at(x, y)
1007    }
1008
1009    /// Position relative to the bottom-left corner
1010    pub const fn bottom_left(x: Val, y: Val) -> Self {
1011        Self::BOTTOM_LEFT.at(x, y)
1012    }
1013
1014    /// Position relative to the top edge
1015    pub const fn top(x: Val, y: Val) -> Self {
1016        Self::TOP.at(x, y)
1017    }
1018
1019    /// Position relative to the center
1020    pub const fn center(x: Val, y: Val) -> Self {
1021        Self::CENTER.at(x, y)
1022    }
1023
1024    /// Position relative to the bottom edge
1025    pub const fn bottom(x: Val, y: Val) -> Self {
1026        Self::BOTTOM.at(x, y)
1027    }
1028
1029    /// Position relative to the top-right corner
1030    pub const fn top_right(x: Val, y: Val) -> Self {
1031        Self::TOP_RIGHT.at(x, y)
1032    }
1033
1034    /// Position relative to the right edge
1035    pub const fn right(x: Val, y: Val) -> Self {
1036        Self::RIGHT.at(x, y)
1037    }
1038
1039    /// Position relative to the bottom-right corner
1040    pub const fn bottom_right(x: Val, y: Val) -> Self {
1041        Self::BOTTOM_RIGHT.at(x, y)
1042    }
1043
1044    /// Resolves the `Position` into physical coordinates.
1045    pub fn resolve(
1046        self,
1047        scale_factor: f32,
1048        physical_size: Vec2,
1049        physical_target_size: Vec2,
1050    ) -> Vec2 {
1051        let d = self.anchor.map(|p| if 0. < p { -1. } else { 1. });
1052
1053        physical_size * self.anchor
1054            + d * Vec2::new(
1055                self.x
1056                    .resolve(scale_factor, physical_size.x, physical_target_size)
1057                    .unwrap_or(0.),
1058                self.y
1059                    .resolve(scale_factor, physical_size.y, physical_target_size)
1060                    .unwrap_or(0.),
1061            )
1062    }
1063}
1064
1065impl From<Val> for UiPosition {
1066    fn from(x: Val) -> Self {
1067        Self { x, ..default() }
1068    }
1069}
1070
1071impl From<(Val, Val)> for UiPosition {
1072    fn from((x, y): (Val, Val)) -> Self {
1073        Self { x, y, ..default() }
1074    }
1075}
1076
1077#[cfg(test)]
1078mod tests {
1079    use crate::geometry::*;
1080    use bevy_math::vec2;
1081
1082    #[test]
1083    fn val_evaluate() {
1084        let size = 250.;
1085        let viewport_size = vec2(1000., 500.);
1086        let result = Val::Percent(80.).resolve(1., size, viewport_size).unwrap();
1087
1088        assert_eq!(result, size * 0.8);
1089    }
1090
1091    #[test]
1092    fn val_resolve_px() {
1093        let size = 250.;
1094        let viewport_size = vec2(1000., 500.);
1095        let result = Val::Px(10.).resolve(1., size, viewport_size).unwrap();
1096
1097        assert_eq!(result, 10.);
1098    }
1099
1100    #[test]
1101    fn val_resolve_viewport_coords() {
1102        let size = 250.;
1103        let viewport_size = vec2(500., 500.);
1104
1105        for value in (-10..10).map(|value| value as f32) {
1106            // for a square viewport there should be no difference between `Vw` and `Vh` and between `Vmin` and `Vmax`.
1107            assert_eq!(
1108                Val::Vw(value).resolve(1., size, viewport_size),
1109                Val::Vh(value).resolve(1., size, viewport_size)
1110            );
1111            assert_eq!(
1112                Val::VMin(value).resolve(1., size, viewport_size),
1113                Val::VMax(value).resolve(1., size, viewport_size)
1114            );
1115            assert_eq!(
1116                Val::VMin(value).resolve(1., size, viewport_size),
1117                Val::Vw(value).resolve(1., size, viewport_size)
1118            );
1119        }
1120
1121        let viewport_size = vec2(1000., 500.);
1122        assert_eq!(
1123            Val::Vw(100.).resolve(1., size, viewport_size).unwrap(),
1124            1000.
1125        );
1126        assert_eq!(
1127            Val::Vh(100.).resolve(1., size, viewport_size).unwrap(),
1128            500.
1129        );
1130        assert_eq!(Val::Vw(60.).resolve(1., size, viewport_size).unwrap(), 600.);
1131        assert_eq!(Val::Vh(40.).resolve(1., size, viewport_size).unwrap(), 200.);
1132        assert_eq!(
1133            Val::VMin(50.).resolve(1., size, viewport_size).unwrap(),
1134            250.
1135        );
1136        assert_eq!(
1137            Val::VMax(75.).resolve(1., size, viewport_size).unwrap(),
1138            750.
1139        );
1140    }
1141
1142    #[test]
1143    fn val_auto_is_non_evaluable() {
1144        let size = 250.;
1145        let viewport_size = vec2(1000., 500.);
1146        let resolve_auto = Val::Auto.resolve(1., size, viewport_size);
1147
1148        assert_eq!(resolve_auto, Err(ValArithmeticError::NonEvaluable));
1149    }
1150
1151    #[test]
1152    fn val_arithmetic_error_messages() {
1153        assert_eq!(
1154            format!("{}", ValArithmeticError::NonEvaluable),
1155            "the given variant of Val is not evaluable (non-numeric)"
1156        );
1157    }
1158
1159    #[test]
1160    fn val_str_parse() {
1161        assert_eq!("auto".parse::<Val>(), Ok(Val::Auto));
1162        assert_eq!("Auto".parse::<Val>(), Ok(Val::Auto));
1163        assert_eq!("AUTO".parse::<Val>(), Ok(Val::Auto));
1164
1165        assert_eq!("3px".parse::<Val>(), Ok(Val::Px(3.)));
1166        assert_eq!("3 px".parse::<Val>(), Ok(Val::Px(3.)));
1167        assert_eq!("3.5px".parse::<Val>(), Ok(Val::Px(3.5)));
1168        assert_eq!("-3px".parse::<Val>(), Ok(Val::Px(-3.)));
1169        assert_eq!("3.5 PX".parse::<Val>(), Ok(Val::Px(3.5)));
1170
1171        assert_eq!("3%".parse::<Val>(), Ok(Val::Percent(3.)));
1172        assert_eq!("3 %".parse::<Val>(), Ok(Val::Percent(3.)));
1173        assert_eq!("3.5%".parse::<Val>(), Ok(Val::Percent(3.5)));
1174        assert_eq!("-3%".parse::<Val>(), Ok(Val::Percent(-3.)));
1175
1176        assert_eq!("3vw".parse::<Val>(), Ok(Val::Vw(3.)));
1177        assert_eq!("3 vw".parse::<Val>(), Ok(Val::Vw(3.)));
1178        assert_eq!("3.5vw".parse::<Val>(), Ok(Val::Vw(3.5)));
1179        assert_eq!("-3vw".parse::<Val>(), Ok(Val::Vw(-3.)));
1180        assert_eq!("3.5 VW".parse::<Val>(), Ok(Val::Vw(3.5)));
1181
1182        assert_eq!("3vh".parse::<Val>(), Ok(Val::Vh(3.)));
1183        assert_eq!("3 vh".parse::<Val>(), Ok(Val::Vh(3.)));
1184        assert_eq!("3.5vh".parse::<Val>(), Ok(Val::Vh(3.5)));
1185        assert_eq!("-3vh".parse::<Val>(), Ok(Val::Vh(-3.)));
1186        assert_eq!("3.5 VH".parse::<Val>(), Ok(Val::Vh(3.5)));
1187
1188        assert_eq!("3vmin".parse::<Val>(), Ok(Val::VMin(3.)));
1189        assert_eq!("3 vmin".parse::<Val>(), Ok(Val::VMin(3.)));
1190        assert_eq!("3.5vmin".parse::<Val>(), Ok(Val::VMin(3.5)));
1191        assert_eq!("-3vmin".parse::<Val>(), Ok(Val::VMin(-3.)));
1192        assert_eq!("3.5 VMIN".parse::<Val>(), Ok(Val::VMin(3.5)));
1193
1194        assert_eq!("3vmax".parse::<Val>(), Ok(Val::VMax(3.)));
1195        assert_eq!("3 vmax".parse::<Val>(), Ok(Val::VMax(3.)));
1196        assert_eq!("3.5vmax".parse::<Val>(), Ok(Val::VMax(3.5)));
1197        assert_eq!("-3vmax".parse::<Val>(), Ok(Val::VMax(-3.)));
1198        assert_eq!("3.5 VMAX".parse::<Val>(), Ok(Val::VMax(3.5)));
1199
1200        assert_eq!("".parse::<Val>(), Err(ValParseError::UnitMissing));
1201        assert_eq!(
1202            "hello world".parse::<Val>(),
1203            Err(ValParseError::ValueMissing)
1204        );
1205        assert_eq!("3".parse::<Val>(), Err(ValParseError::UnitMissing));
1206        assert_eq!("3.5".parse::<Val>(), Err(ValParseError::UnitMissing));
1207        assert_eq!("3pxx".parse::<Val>(), Err(ValParseError::InvalidUnit));
1208        assert_eq!("3.5pxx".parse::<Val>(), Err(ValParseError::InvalidUnit));
1209        assert_eq!("3-3px".parse::<Val>(), Err(ValParseError::InvalidValue));
1210        assert_eq!("3.5-3px".parse::<Val>(), Err(ValParseError::InvalidValue));
1211    }
1212
1213    #[test]
1214    fn default_val_equals_const_default_val() {
1215        assert_eq!(Val::default(), Val::DEFAULT);
1216    }
1217
1218    #[test]
1219    fn uirect_default_equals_const_default() {
1220        assert_eq!(UiRect::default(), UiRect::all(Val::ZERO));
1221        assert_eq!(UiRect::default(), UiRect::DEFAULT);
1222    }
1223
1224    #[test]
1225    fn test_uirect_axes() {
1226        let x = Val::Px(1.);
1227        let y = Val::Vw(4.);
1228        let r = UiRect::axes(x, y);
1229        let h = UiRect::horizontal(x);
1230        let v = UiRect::vertical(y);
1231
1232        assert_eq!(r.top, v.top);
1233        assert_eq!(r.bottom, v.bottom);
1234        assert_eq!(r.left, h.left);
1235        assert_eq!(r.right, h.right);
1236    }
1237
1238    #[test]
1239    fn uirect_px() {
1240        let r = UiRect::px(3., 5., 20., 999.);
1241        assert_eq!(r.left, Val::Px(3.));
1242        assert_eq!(r.right, Val::Px(5.));
1243        assert_eq!(r.top, Val::Px(20.));
1244        assert_eq!(r.bottom, Val::Px(999.));
1245    }
1246
1247    #[test]
1248    fn uirect_percent() {
1249        let r = UiRect::percent(3., 5., 20., 99.);
1250        assert_eq!(r.left, Val::Percent(3.));
1251        assert_eq!(r.right, Val::Percent(5.));
1252        assert_eq!(r.top, Val::Percent(20.));
1253        assert_eq!(r.bottom, Val::Percent(99.));
1254    }
1255}