Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ui/src/geometry.rs
6849 views
1
use bevy_math::Vec2;
2
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
3
use bevy_utils::default;
4
use core::ops::{Div, DivAssign, Mul, MulAssign, Neg};
5
use thiserror::Error;
6
7
#[cfg(feature = "serialize")]
8
use 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
)]
31
pub 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)]
60
pub enum ValParseError {
61
UnitMissing,
62
ValueMissing,
63
InvalidValue,
64
InvalidUnit,
65
}
66
67
impl 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
78
impl 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
123
impl 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
165
impl 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
306
impl Default for Val {
307
fn default() -> Self {
308
Self::DEFAULT
309
}
310
}
311
312
impl 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
328
impl 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
342
impl 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
358
impl 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
372
impl 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)]
389
pub enum ValArithmeticError {
390
#[error("the given variant of Val is not evaluable (non-numeric)")]
391
NonEvaluable,
392
}
393
394
impl 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.
425
pub 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
431
macro_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
443
impl_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.
447
pub const fn auto() -> Val {
448
Val::Auto
449
}
450
451
/// Returns a [`Val::Px`] representing a value in logical pixels.
452
pub 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.
469
pub 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.
474
pub 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.
479
pub 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.
484
pub 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.
489
pub 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
)]
543
pub 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
554
impl 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
892
impl Default for UiRect {
893
fn default() -> Self {
894
Self::DEFAULT
895
}
896
}
897
898
impl 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.
912
pub 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
921
impl Default for UiPosition {
922
fn default() -> Self {
923
Self::CENTER
924
}
925
}
926
927
impl 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
1065
impl From<Val> for UiPosition {
1066
fn from(x: Val) -> Self {
1067
Self { x, ..default() }
1068
}
1069
}
1070
1071
impl From<(Val, Val)> for UiPosition {
1072
fn from((x, y): (Val, Val)) -> Self {
1073
Self { x, y, ..default() }
1074
}
1075
}
1076
1077
#[cfg(test)]
1078
mod 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
1256
#[test]
1257
fn val_constructor_fns_return_correct_val_variant() {
1258
assert_eq!(auto(), Val::Auto);
1259
assert_eq!(px(0.0), Val::Px(0.0));
1260
assert_eq!(percent(0.0), Val::Percent(0.0));
1261
assert_eq!(vw(0.0), Val::Vw(0.0));
1262
assert_eq!(vh(0.0), Val::Vh(0.0));
1263
assert_eq!(vmin(0.0), Val::VMin(0.0));
1264
assert_eq!(vmax(0.0), Val::VMax(0.0));
1265
}
1266
}
1267
1268