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}