Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ui/src/geometry.rs
6598 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
170
impl Default for Val {
171
fn default() -> Self {
172
Self::DEFAULT
173
}
174
}
175
176
impl Mul<f32> for Val {
177
type Output = Val;
178
179
fn mul(self, rhs: f32) -> Self::Output {
180
match self {
181
Val::Auto => Val::Auto,
182
Val::Px(value) => Val::Px(value * rhs),
183
Val::Percent(value) => Val::Percent(value * rhs),
184
Val::Vw(value) => Val::Vw(value * rhs),
185
Val::Vh(value) => Val::Vh(value * rhs),
186
Val::VMin(value) => Val::VMin(value * rhs),
187
Val::VMax(value) => Val::VMax(value * rhs),
188
}
189
}
190
}
191
192
impl MulAssign<f32> for Val {
193
fn mul_assign(&mut self, rhs: f32) {
194
match self {
195
Val::Auto => {}
196
Val::Px(value)
197
| Val::Percent(value)
198
| Val::Vw(value)
199
| Val::Vh(value)
200
| Val::VMin(value)
201
| Val::VMax(value) => *value *= rhs,
202
}
203
}
204
}
205
206
impl Div<f32> for Val {
207
type Output = Val;
208
209
fn div(self, rhs: f32) -> Self::Output {
210
match self {
211
Val::Auto => Val::Auto,
212
Val::Px(value) => Val::Px(value / rhs),
213
Val::Percent(value) => Val::Percent(value / rhs),
214
Val::Vw(value) => Val::Vw(value / rhs),
215
Val::Vh(value) => Val::Vh(value / rhs),
216
Val::VMin(value) => Val::VMin(value / rhs),
217
Val::VMax(value) => Val::VMax(value / rhs),
218
}
219
}
220
}
221
222
impl DivAssign<f32> for Val {
223
fn div_assign(&mut self, rhs: f32) {
224
match self {
225
Val::Auto => {}
226
Val::Px(value)
227
| Val::Percent(value)
228
| Val::Vw(value)
229
| Val::Vh(value)
230
| Val::VMin(value)
231
| Val::VMax(value) => *value /= rhs,
232
}
233
}
234
}
235
236
impl Neg for Val {
237
type Output = Val;
238
239
fn neg(self) -> Self::Output {
240
match self {
241
Val::Px(value) => Val::Px(-value),
242
Val::Percent(value) => Val::Percent(-value),
243
Val::Vw(value) => Val::Vw(-value),
244
Val::Vh(value) => Val::Vh(-value),
245
Val::VMin(value) => Val::VMin(-value),
246
Val::VMax(value) => Val::VMax(-value),
247
_ => self,
248
}
249
}
250
}
251
252
#[derive(Debug, Eq, PartialEq, Clone, Copy, Error)]
253
pub enum ValArithmeticError {
254
#[error("the given variant of Val is not evaluable (non-numeric)")]
255
NonEvaluable,
256
}
257
258
impl Val {
259
/// Resolves this [`Val`] to a value in physical pixels from the given `scale_factor`, `physical_base_value`,
260
/// and `physical_target_size` context values.
261
///
262
/// Returns a [`ValArithmeticError::NonEvaluable`] if the [`Val`] is impossible to resolve into a concrete value.
263
pub const fn resolve(
264
self,
265
scale_factor: f32,
266
physical_base_value: f32,
267
physical_target_size: Vec2,
268
) -> Result<f32, ValArithmeticError> {
269
match self {
270
Val::Percent(value) => Ok(physical_base_value * value / 100.0),
271
Val::Px(value) => Ok(value * scale_factor),
272
Val::Vw(value) => Ok(physical_target_size.x * value / 100.0),
273
Val::Vh(value) => Ok(physical_target_size.y * value / 100.0),
274
Val::VMin(value) => {
275
Ok(physical_target_size.x.min(physical_target_size.y) * value / 100.0)
276
}
277
Val::VMax(value) => {
278
Ok(physical_target_size.x.max(physical_target_size.y) * value / 100.0)
279
}
280
Val::Auto => Err(ValArithmeticError::NonEvaluable),
281
}
282
}
283
}
284
285
/// All the types that should be able to be used in the [`Val`] enum should implement this trait.
286
///
287
/// Instead of just implementing `Into<Val>` a custom trait is added.
288
/// This is done in order to prevent having to define a default unit, which could lead to confusion especially for newcomers.
289
pub trait ValNum {
290
/// Called by the [`Val`] helper functions to convert the implementing type to an `f32` that can
291
/// be used by [`Val`].
292
fn val_num_f32(self) -> f32;
293
}
294
295
macro_rules! impl_to_val_num {
296
($($impl_type:ty),*$(,)?) => {
297
$(
298
impl ValNum for $impl_type {
299
fn val_num_f32(self) -> f32 {
300
self as f32
301
}
302
}
303
)*
304
};
305
}
306
307
impl_to_val_num!(f32, f64, i8, i16, i32, i64, u8, u16, u32, u64, usize, isize);
308
309
/// Returns a [`Val::Auto`] where the value is automatically determined
310
/// based on the context and other [`Node`](crate::Node) properties.
311
pub const fn auto() -> Val {
312
Val::Auto
313
}
314
315
/// Returns a [`Val::Px`] representing a value in logical pixels.
316
pub fn px<T: ValNum>(value: T) -> Val {
317
Val::Px(value.val_num_f32())
318
}
319
320
/// Returns a [`Val::Percent`] representing a percentage of the parent node's length
321
/// along a specific axis.
322
///
323
/// If the UI node has no parent, the percentage is based on the window's length
324
/// along that axis.
325
///
326
/// Axis rules:
327
/// * For `flex_basis`, the percentage is relative to the main-axis length determined by the `flex_direction`.
328
/// * For `gap`, `min_size`, `size`, and `max_size`:
329
/// - `width` is relative to the parent's width.
330
/// - `height` is relative to the parent's height.
331
/// * For `margin`, `padding`, and `border` values: the percentage is relative to the parent's width.
332
/// * For positions, `left` and `right` are relative to the parent's width, while `bottom` and `top` are relative to the parent's height.
333
pub fn percent<T: ValNum>(value: T) -> Val {
334
Val::Percent(value.val_num_f32())
335
}
336
337
/// Returns a [`Val::Vw`] representing a percentage of the viewport width.
338
pub fn vw<T: ValNum>(value: T) -> Val {
339
Val::Vw(value.val_num_f32())
340
}
341
342
/// Returns a [`Val::Vh`] representing a percentage of the viewport height.
343
pub fn vh<T: ValNum>(value: T) -> Val {
344
Val::Vh(value.val_num_f32())
345
}
346
347
/// Returns a [`Val::VMin`] representing a percentage of the viewport's smaller dimension.
348
pub fn vmin<T: ValNum>(value: T) -> Val {
349
Val::VMin(value.val_num_f32())
350
}
351
352
/// Returns a [`Val::VMax`] representing a percentage of the viewport's larger dimension.
353
pub fn vmax<T: ValNum>(value: T) -> Val {
354
Val::VMax(value.val_num_f32())
355
}
356
357
/// A type which is commonly used to define margins, paddings and borders.
358
///
359
/// # Examples
360
///
361
/// ## Margin
362
///
363
/// A margin is used to create space around UI elements, outside of any defined borders.
364
///
365
/// ```
366
/// # use bevy_ui::{UiRect, Val};
367
/// #
368
/// let margin = UiRect::all(Val::Auto); // Centers the UI element
369
/// ```
370
///
371
/// ## Padding
372
///
373
/// A padding is used to create space around UI elements, inside of any defined borders.
374
///
375
/// ```
376
/// # use bevy_ui::{UiRect, Val};
377
/// #
378
/// let padding = UiRect {
379
/// left: Val::Px(10.0),
380
/// right: Val::Px(20.0),
381
/// top: Val::Px(30.0),
382
/// bottom: Val::Px(40.0),
383
/// };
384
/// ```
385
///
386
/// ## Borders
387
///
388
/// A border is used to define the width of the border of a UI element.
389
///
390
/// ```
391
/// # use bevy_ui::{UiRect, Val};
392
/// #
393
/// let border = UiRect {
394
/// left: Val::Px(10.0),
395
/// right: Val::Px(20.0),
396
/// top: Val::Px(30.0),
397
/// bottom: Val::Px(40.0),
398
/// };
399
/// ```
400
#[derive(Copy, Clone, PartialEq, Debug, Reflect)]
401
#[reflect(Default, PartialEq, Debug, Clone)]
402
#[cfg_attr(
403
feature = "serialize",
404
derive(serde::Serialize, serde::Deserialize),
405
reflect(Serialize, Deserialize)
406
)]
407
pub struct UiRect {
408
/// The value corresponding to the left side of the UI rect.
409
pub left: Val,
410
/// The value corresponding to the right side of the UI rect.
411
pub right: Val,
412
/// The value corresponding to the top side of the UI rect.
413
pub top: Val,
414
/// The value corresponding to the bottom side of the UI rect.
415
pub bottom: Val,
416
}
417
418
impl UiRect {
419
pub const DEFAULT: Self = Self::all(Val::ZERO);
420
pub const ZERO: Self = Self::all(Val::ZERO);
421
pub const AUTO: Self = Self::all(Val::Auto);
422
423
/// Creates a new [`UiRect`] from the values specified.
424
///
425
/// # Example
426
///
427
/// ```
428
/// # use bevy_ui::{UiRect, Val};
429
/// #
430
/// let ui_rect = UiRect::new(
431
/// Val::Px(10.0),
432
/// Val::Px(20.0),
433
/// Val::Px(30.0),
434
/// Val::Px(40.0),
435
/// );
436
///
437
/// assert_eq!(ui_rect.left, Val::Px(10.0));
438
/// assert_eq!(ui_rect.right, Val::Px(20.0));
439
/// assert_eq!(ui_rect.top, Val::Px(30.0));
440
/// assert_eq!(ui_rect.bottom, Val::Px(40.0));
441
/// ```
442
pub const fn new(left: Val, right: Val, top: Val, bottom: Val) -> Self {
443
UiRect {
444
left,
445
right,
446
top,
447
bottom,
448
}
449
}
450
451
/// Creates a new [`UiRect`] where all sides have the same value.
452
///
453
/// # Example
454
///
455
/// ```
456
/// # use bevy_ui::{UiRect, Val};
457
/// #
458
/// let ui_rect = UiRect::all(Val::Px(10.0));
459
///
460
/// assert_eq!(ui_rect.left, Val::Px(10.0));
461
/// assert_eq!(ui_rect.right, Val::Px(10.0));
462
/// assert_eq!(ui_rect.top, Val::Px(10.0));
463
/// assert_eq!(ui_rect.bottom, Val::Px(10.0));
464
/// ```
465
pub const fn all(value: Val) -> Self {
466
UiRect {
467
left: value,
468
right: value,
469
top: value,
470
bottom: value,
471
}
472
}
473
474
/// Creates a new [`UiRect`] from the values specified in logical pixels.
475
///
476
/// This is a shortcut for [`UiRect::new()`], applying [`Val::Px`] to all arguments.
477
///
478
/// # Example
479
///
480
/// ```
481
/// # use bevy_ui::{UiRect, Val};
482
/// #
483
/// let ui_rect = UiRect::px(10., 20., 30., 40.);
484
/// assert_eq!(ui_rect.left, Val::Px(10.));
485
/// assert_eq!(ui_rect.right, Val::Px(20.));
486
/// assert_eq!(ui_rect.top, Val::Px(30.));
487
/// assert_eq!(ui_rect.bottom, Val::Px(40.));
488
/// ```
489
pub const fn px(left: f32, right: f32, top: f32, bottom: f32) -> Self {
490
UiRect {
491
left: Val::Px(left),
492
right: Val::Px(right),
493
top: Val::Px(top),
494
bottom: Val::Px(bottom),
495
}
496
}
497
498
/// Creates a new [`UiRect`] from the values specified in percentages.
499
///
500
/// This is a shortcut for [`UiRect::new()`], applying [`Val::Percent`] to all arguments.
501
///
502
/// # Example
503
///
504
/// ```
505
/// # use bevy_ui::{UiRect, Val};
506
/// #
507
/// let ui_rect = UiRect::percent(5., 10., 2., 1.);
508
/// assert_eq!(ui_rect.left, Val::Percent(5.));
509
/// assert_eq!(ui_rect.right, Val::Percent(10.));
510
/// assert_eq!(ui_rect.top, Val::Percent(2.));
511
/// assert_eq!(ui_rect.bottom, Val::Percent(1.));
512
/// ```
513
pub const fn percent(left: f32, right: f32, top: f32, bottom: f32) -> Self {
514
UiRect {
515
left: Val::Percent(left),
516
right: Val::Percent(right),
517
top: Val::Percent(top),
518
bottom: Val::Percent(bottom),
519
}
520
}
521
522
/// Creates a new [`UiRect`] where `left` and `right` take the given value,
523
/// and `top` and `bottom` set to zero `Val::ZERO`.
524
///
525
/// # Example
526
///
527
/// ```
528
/// # use bevy_ui::{UiRect, Val};
529
/// #
530
/// let ui_rect = UiRect::horizontal(Val::Px(10.0));
531
///
532
/// assert_eq!(ui_rect.left, Val::Px(10.0));
533
/// assert_eq!(ui_rect.right, Val::Px(10.0));
534
/// assert_eq!(ui_rect.top, Val::ZERO);
535
/// assert_eq!(ui_rect.bottom, Val::ZERO);
536
/// ```
537
pub const fn horizontal(value: Val) -> Self {
538
Self {
539
left: value,
540
right: value,
541
..Self::DEFAULT
542
}
543
}
544
545
/// Creates a new [`UiRect`] where `top` and `bottom` take the given value,
546
/// and `left` and `right` are set to `Val::ZERO`.
547
///
548
/// # Example
549
///
550
/// ```
551
/// # use bevy_ui::{UiRect, Val};
552
/// #
553
/// let ui_rect = UiRect::vertical(Val::Px(10.0));
554
///
555
/// assert_eq!(ui_rect.left, Val::ZERO);
556
/// assert_eq!(ui_rect.right, Val::ZERO);
557
/// assert_eq!(ui_rect.top, Val::Px(10.0));
558
/// assert_eq!(ui_rect.bottom, Val::Px(10.0));
559
/// ```
560
pub const fn vertical(value: Val) -> Self {
561
Self {
562
top: value,
563
bottom: value,
564
..Self::DEFAULT
565
}
566
}
567
568
/// Creates a new [`UiRect`] where both `left` and `right` take the value of `horizontal`, and both `top` and `bottom` take the value of `vertical`.
569
///
570
/// # Example
571
///
572
/// ```
573
/// # use bevy_ui::{UiRect, Val};
574
/// #
575
/// let ui_rect = UiRect::axes(Val::Px(10.0), Val::Percent(15.0));
576
///
577
/// assert_eq!(ui_rect.left, Val::Px(10.0));
578
/// assert_eq!(ui_rect.right, Val::Px(10.0));
579
/// assert_eq!(ui_rect.top, Val::Percent(15.0));
580
/// assert_eq!(ui_rect.bottom, Val::Percent(15.0));
581
/// ```
582
pub const fn axes(horizontal: Val, vertical: Val) -> Self {
583
Self {
584
left: horizontal,
585
right: horizontal,
586
top: vertical,
587
bottom: vertical,
588
}
589
}
590
591
/// Creates a new [`UiRect`] where `left` takes the given value, and
592
/// the other fields are set to `Val::ZERO`.
593
///
594
/// # Example
595
///
596
/// ```
597
/// # use bevy_ui::{UiRect, Val};
598
/// #
599
/// let ui_rect = UiRect::left(Val::Px(10.0));
600
///
601
/// assert_eq!(ui_rect.left, Val::Px(10.0));
602
/// assert_eq!(ui_rect.right, Val::ZERO);
603
/// assert_eq!(ui_rect.top, Val::ZERO);
604
/// assert_eq!(ui_rect.bottom, Val::ZERO);
605
/// ```
606
pub const fn left(left: Val) -> Self {
607
Self {
608
left,
609
..Self::DEFAULT
610
}
611
}
612
613
/// Creates a new [`UiRect`] where `right` takes the given value,
614
/// and the other fields are set to `Val::ZERO`.
615
///
616
/// # Example
617
///
618
/// ```
619
/// # use bevy_ui::{UiRect, Val};
620
/// #
621
/// let ui_rect = UiRect::right(Val::Px(10.0));
622
///
623
/// assert_eq!(ui_rect.left, Val::ZERO);
624
/// assert_eq!(ui_rect.right, Val::Px(10.0));
625
/// assert_eq!(ui_rect.top, Val::ZERO);
626
/// assert_eq!(ui_rect.bottom, Val::ZERO);
627
/// ```
628
pub const fn right(right: Val) -> Self {
629
Self {
630
right,
631
..Self::DEFAULT
632
}
633
}
634
635
/// Creates a new [`UiRect`] where `top` takes the given value,
636
/// and the other fields are set to `Val::ZERO`.
637
///
638
/// # Example
639
///
640
/// ```
641
/// # use bevy_ui::{UiRect, Val};
642
/// #
643
/// let ui_rect = UiRect::top(Val::Px(10.0));
644
///
645
/// assert_eq!(ui_rect.left, Val::ZERO);
646
/// assert_eq!(ui_rect.right, Val::ZERO);
647
/// assert_eq!(ui_rect.top, Val::Px(10.0));
648
/// assert_eq!(ui_rect.bottom, Val::ZERO);
649
/// ```
650
pub const fn top(top: Val) -> Self {
651
Self {
652
top,
653
..Self::DEFAULT
654
}
655
}
656
657
/// Creates a new [`UiRect`] where `bottom` takes the given value,
658
/// and the other fields are set to `Val::ZERO`.
659
///
660
/// # Example
661
///
662
/// ```
663
/// # use bevy_ui::{UiRect, Val};
664
/// #
665
/// let ui_rect = UiRect::bottom(Val::Px(10.0));
666
///
667
/// assert_eq!(ui_rect.left, Val::ZERO);
668
/// assert_eq!(ui_rect.right, Val::ZERO);
669
/// assert_eq!(ui_rect.top, Val::ZERO);
670
/// assert_eq!(ui_rect.bottom, Val::Px(10.0));
671
/// ```
672
pub const fn bottom(bottom: Val) -> Self {
673
Self {
674
bottom,
675
..Self::DEFAULT
676
}
677
}
678
679
/// Returns the [`UiRect`] with its `left` field set to the given value.
680
///
681
/// # Example
682
///
683
/// ```
684
/// # use bevy_ui::{UiRect, Val};
685
/// #
686
/// let ui_rect = UiRect::all(Val::Px(20.0)).with_left(Val::Px(10.0));
687
/// assert_eq!(ui_rect.left, Val::Px(10.0));
688
/// assert_eq!(ui_rect.right, Val::Px(20.0));
689
/// assert_eq!(ui_rect.top, Val::Px(20.0));
690
/// assert_eq!(ui_rect.bottom, Val::Px(20.0));
691
/// ```
692
#[inline]
693
pub const fn with_left(mut self, left: Val) -> Self {
694
self.left = left;
695
self
696
}
697
698
/// Returns the [`UiRect`] with its `right` field set to the given value.
699
///
700
/// # Example
701
///
702
/// ```
703
/// # use bevy_ui::{UiRect, Val};
704
/// #
705
/// let ui_rect = UiRect::all(Val::Px(20.0)).with_right(Val::Px(10.0));
706
/// assert_eq!(ui_rect.left, Val::Px(20.0));
707
/// assert_eq!(ui_rect.right, Val::Px(10.0));
708
/// assert_eq!(ui_rect.top, Val::Px(20.0));
709
/// assert_eq!(ui_rect.bottom, Val::Px(20.0));
710
/// ```
711
#[inline]
712
pub const fn with_right(mut self, right: Val) -> Self {
713
self.right = right;
714
self
715
}
716
717
/// Returns the [`UiRect`] with its `top` field set to the given value.
718
///
719
/// # Example
720
///
721
/// ```
722
/// # use bevy_ui::{UiRect, Val};
723
/// #
724
/// let ui_rect = UiRect::all(Val::Px(20.0)).with_top(Val::Px(10.0));
725
/// assert_eq!(ui_rect.left, Val::Px(20.0));
726
/// assert_eq!(ui_rect.right, Val::Px(20.0));
727
/// assert_eq!(ui_rect.top, Val::Px(10.0));
728
/// assert_eq!(ui_rect.bottom, Val::Px(20.0));
729
/// ```
730
#[inline]
731
pub const fn with_top(mut self, top: Val) -> Self {
732
self.top = top;
733
self
734
}
735
736
/// Returns the [`UiRect`] with its `bottom` field set to the given value.
737
///
738
/// # Example
739
///
740
/// ```
741
/// # use bevy_ui::{UiRect, Val};
742
/// #
743
/// let ui_rect = UiRect::all(Val::Px(20.0)).with_bottom(Val::Px(10.0));
744
/// assert_eq!(ui_rect.left, Val::Px(20.0));
745
/// assert_eq!(ui_rect.right, Val::Px(20.0));
746
/// assert_eq!(ui_rect.top, Val::Px(20.0));
747
/// assert_eq!(ui_rect.bottom, Val::Px(10.0));
748
/// ```
749
#[inline]
750
pub const fn with_bottom(mut self, bottom: Val) -> Self {
751
self.bottom = bottom;
752
self
753
}
754
}
755
756
impl Default for UiRect {
757
fn default() -> Self {
758
Self::DEFAULT
759
}
760
}
761
762
impl From<Val> for UiRect {
763
fn from(value: Val) -> Self {
764
UiRect::all(value)
765
}
766
}
767
768
#[derive(Debug, Clone, Copy, PartialEq, Reflect)]
769
#[reflect(Default, Debug, PartialEq)]
770
#[cfg_attr(
771
feature = "serialize",
772
derive(serde::Serialize, serde::Deserialize),
773
reflect(Serialize, Deserialize)
774
)]
775
/// Responsive position relative to a UI node.
776
pub struct UiPosition {
777
/// Normalized anchor point
778
pub anchor: Vec2,
779
/// Responsive horizontal position relative to the anchor point
780
pub x: Val,
781
/// Responsive vertical position relative to the anchor point
782
pub y: Val,
783
}
784
785
impl Default for UiPosition {
786
fn default() -> Self {
787
Self::CENTER
788
}
789
}
790
791
impl UiPosition {
792
/// Position at the given normalized anchor point
793
pub const fn anchor(anchor: Vec2) -> Self {
794
Self {
795
anchor,
796
x: Val::ZERO,
797
y: Val::ZERO,
798
}
799
}
800
801
/// Position at the top-left corner
802
pub const TOP_LEFT: Self = Self::anchor(Vec2::new(-0.5, -0.5));
803
804
/// Position at the center of the left edge
805
pub const LEFT: Self = Self::anchor(Vec2::new(-0.5, 0.0));
806
807
/// Position at the bottom-left corner
808
pub const BOTTOM_LEFT: Self = Self::anchor(Vec2::new(-0.5, 0.5));
809
810
/// Position at the center of the top edge
811
pub const TOP: Self = Self::anchor(Vec2::new(0.0, -0.5));
812
813
/// Position at the center of the element
814
pub const CENTER: Self = Self::anchor(Vec2::new(0.0, 0.0));
815
816
/// Position at the center of the bottom edge
817
pub const BOTTOM: Self = Self::anchor(Vec2::new(0.0, 0.5));
818
819
/// Position at the top-right corner
820
pub const TOP_RIGHT: Self = Self::anchor(Vec2::new(0.5, -0.5));
821
822
/// Position at the center of the right edge
823
pub const RIGHT: Self = Self::anchor(Vec2::new(0.5, 0.0));
824
825
/// Position at the bottom-right corner
826
pub const BOTTOM_RIGHT: Self = Self::anchor(Vec2::new(0.5, 0.5));
827
828
/// Create a new position
829
pub const fn new(anchor: Vec2, x: Val, y: Val) -> Self {
830
Self { anchor, x, y }
831
}
832
833
/// Creates a position from self with the given `x` and `y` coordinates
834
pub const fn at(self, x: Val, y: Val) -> Self {
835
Self { x, y, ..self }
836
}
837
838
/// Creates a position from self with the given `x` coordinate
839
pub const fn at_x(self, x: Val) -> Self {
840
Self { x, ..self }
841
}
842
843
/// Creates a position from self with the given `y` coordinate
844
pub const fn at_y(self, y: Val) -> Self {
845
Self { y, ..self }
846
}
847
848
/// Creates a position in logical pixels from self with the given `x` and `y` coordinates
849
pub const fn at_px(self, x: f32, y: f32) -> Self {
850
self.at(Val::Px(x), Val::Px(y))
851
}
852
853
/// Creates a percentage position from self with the given `x` and `y` coordinates
854
pub const fn at_percent(self, x: f32, y: f32) -> Self {
855
self.at(Val::Percent(x), Val::Percent(y))
856
}
857
858
/// Creates a position from self with the given `anchor` point
859
pub const fn with_anchor(self, anchor: Vec2) -> Self {
860
Self { anchor, ..self }
861
}
862
863
/// Position relative to the top-left corner
864
pub const fn top_left(x: Val, y: Val) -> Self {
865
Self::TOP_LEFT.at(x, y)
866
}
867
868
/// Position relative to the left edge
869
pub const fn left(x: Val, y: Val) -> Self {
870
Self::LEFT.at(x, y)
871
}
872
873
/// Position relative to the bottom-left corner
874
pub const fn bottom_left(x: Val, y: Val) -> Self {
875
Self::BOTTOM_LEFT.at(x, y)
876
}
877
878
/// Position relative to the top edge
879
pub const fn top(x: Val, y: Val) -> Self {
880
Self::TOP.at(x, y)
881
}
882
883
/// Position relative to the center
884
pub const fn center(x: Val, y: Val) -> Self {
885
Self::CENTER.at(x, y)
886
}
887
888
/// Position relative to the bottom edge
889
pub const fn bottom(x: Val, y: Val) -> Self {
890
Self::BOTTOM.at(x, y)
891
}
892
893
/// Position relative to the top-right corner
894
pub const fn top_right(x: Val, y: Val) -> Self {
895
Self::TOP_RIGHT.at(x, y)
896
}
897
898
/// Position relative to the right edge
899
pub const fn right(x: Val, y: Val) -> Self {
900
Self::RIGHT.at(x, y)
901
}
902
903
/// Position relative to the bottom-right corner
904
pub const fn bottom_right(x: Val, y: Val) -> Self {
905
Self::BOTTOM_RIGHT.at(x, y)
906
}
907
908
/// Resolves the `Position` into physical coordinates.
909
pub fn resolve(
910
self,
911
scale_factor: f32,
912
physical_size: Vec2,
913
physical_target_size: Vec2,
914
) -> Vec2 {
915
let d = self.anchor.map(|p| if 0. < p { -1. } else { 1. });
916
917
physical_size * self.anchor
918
+ d * Vec2::new(
919
self.x
920
.resolve(scale_factor, physical_size.x, physical_target_size)
921
.unwrap_or(0.),
922
self.y
923
.resolve(scale_factor, physical_size.y, physical_target_size)
924
.unwrap_or(0.),
925
)
926
}
927
}
928
929
impl From<Val> for UiPosition {
930
fn from(x: Val) -> Self {
931
Self { x, ..default() }
932
}
933
}
934
935
impl From<(Val, Val)> for UiPosition {
936
fn from((x, y): (Val, Val)) -> Self {
937
Self { x, y, ..default() }
938
}
939
}
940
941
#[cfg(test)]
942
mod tests {
943
use crate::geometry::*;
944
use bevy_math::vec2;
945
946
#[test]
947
fn val_evaluate() {
948
let size = 250.;
949
let viewport_size = vec2(1000., 500.);
950
let result = Val::Percent(80.).resolve(1., size, viewport_size).unwrap();
951
952
assert_eq!(result, size * 0.8);
953
}
954
955
#[test]
956
fn val_resolve_px() {
957
let size = 250.;
958
let viewport_size = vec2(1000., 500.);
959
let result = Val::Px(10.).resolve(1., size, viewport_size).unwrap();
960
961
assert_eq!(result, 10.);
962
}
963
964
#[test]
965
fn val_resolve_viewport_coords() {
966
let size = 250.;
967
let viewport_size = vec2(500., 500.);
968
969
for value in (-10..10).map(|value| value as f32) {
970
// for a square viewport there should be no difference between `Vw` and `Vh` and between `Vmin` and `Vmax`.
971
assert_eq!(
972
Val::Vw(value).resolve(1., size, viewport_size),
973
Val::Vh(value).resolve(1., size, viewport_size)
974
);
975
assert_eq!(
976
Val::VMin(value).resolve(1., size, viewport_size),
977
Val::VMax(value).resolve(1., size, viewport_size)
978
);
979
assert_eq!(
980
Val::VMin(value).resolve(1., size, viewport_size),
981
Val::Vw(value).resolve(1., size, viewport_size)
982
);
983
}
984
985
let viewport_size = vec2(1000., 500.);
986
assert_eq!(
987
Val::Vw(100.).resolve(1., size, viewport_size).unwrap(),
988
1000.
989
);
990
assert_eq!(
991
Val::Vh(100.).resolve(1., size, viewport_size).unwrap(),
992
500.
993
);
994
assert_eq!(Val::Vw(60.).resolve(1., size, viewport_size).unwrap(), 600.);
995
assert_eq!(Val::Vh(40.).resolve(1., size, viewport_size).unwrap(), 200.);
996
assert_eq!(
997
Val::VMin(50.).resolve(1., size, viewport_size).unwrap(),
998
250.
999
);
1000
assert_eq!(
1001
Val::VMax(75.).resolve(1., size, viewport_size).unwrap(),
1002
750.
1003
);
1004
}
1005
1006
#[test]
1007
fn val_auto_is_non_evaluable() {
1008
let size = 250.;
1009
let viewport_size = vec2(1000., 500.);
1010
let resolve_auto = Val::Auto.resolve(1., size, viewport_size);
1011
1012
assert_eq!(resolve_auto, Err(ValArithmeticError::NonEvaluable));
1013
}
1014
1015
#[test]
1016
fn val_arithmetic_error_messages() {
1017
assert_eq!(
1018
format!("{}", ValArithmeticError::NonEvaluable),
1019
"the given variant of Val is not evaluable (non-numeric)"
1020
);
1021
}
1022
1023
#[test]
1024
fn val_str_parse() {
1025
assert_eq!("auto".parse::<Val>(), Ok(Val::Auto));
1026
assert_eq!("Auto".parse::<Val>(), Ok(Val::Auto));
1027
assert_eq!("AUTO".parse::<Val>(), Ok(Val::Auto));
1028
1029
assert_eq!("3px".parse::<Val>(), Ok(Val::Px(3.)));
1030
assert_eq!("3 px".parse::<Val>(), Ok(Val::Px(3.)));
1031
assert_eq!("3.5px".parse::<Val>(), Ok(Val::Px(3.5)));
1032
assert_eq!("-3px".parse::<Val>(), Ok(Val::Px(-3.)));
1033
assert_eq!("3.5 PX".parse::<Val>(), Ok(Val::Px(3.5)));
1034
1035
assert_eq!("3%".parse::<Val>(), Ok(Val::Percent(3.)));
1036
assert_eq!("3 %".parse::<Val>(), Ok(Val::Percent(3.)));
1037
assert_eq!("3.5%".parse::<Val>(), Ok(Val::Percent(3.5)));
1038
assert_eq!("-3%".parse::<Val>(), Ok(Val::Percent(-3.)));
1039
1040
assert_eq!("3vw".parse::<Val>(), Ok(Val::Vw(3.)));
1041
assert_eq!("3 vw".parse::<Val>(), Ok(Val::Vw(3.)));
1042
assert_eq!("3.5vw".parse::<Val>(), Ok(Val::Vw(3.5)));
1043
assert_eq!("-3vw".parse::<Val>(), Ok(Val::Vw(-3.)));
1044
assert_eq!("3.5 VW".parse::<Val>(), Ok(Val::Vw(3.5)));
1045
1046
assert_eq!("3vh".parse::<Val>(), Ok(Val::Vh(3.)));
1047
assert_eq!("3 vh".parse::<Val>(), Ok(Val::Vh(3.)));
1048
assert_eq!("3.5vh".parse::<Val>(), Ok(Val::Vh(3.5)));
1049
assert_eq!("-3vh".parse::<Val>(), Ok(Val::Vh(-3.)));
1050
assert_eq!("3.5 VH".parse::<Val>(), Ok(Val::Vh(3.5)));
1051
1052
assert_eq!("3vmin".parse::<Val>(), Ok(Val::VMin(3.)));
1053
assert_eq!("3 vmin".parse::<Val>(), Ok(Val::VMin(3.)));
1054
assert_eq!("3.5vmin".parse::<Val>(), Ok(Val::VMin(3.5)));
1055
assert_eq!("-3vmin".parse::<Val>(), Ok(Val::VMin(-3.)));
1056
assert_eq!("3.5 VMIN".parse::<Val>(), Ok(Val::VMin(3.5)));
1057
1058
assert_eq!("3vmax".parse::<Val>(), Ok(Val::VMax(3.)));
1059
assert_eq!("3 vmax".parse::<Val>(), Ok(Val::VMax(3.)));
1060
assert_eq!("3.5vmax".parse::<Val>(), Ok(Val::VMax(3.5)));
1061
assert_eq!("-3vmax".parse::<Val>(), Ok(Val::VMax(-3.)));
1062
assert_eq!("3.5 VMAX".parse::<Val>(), Ok(Val::VMax(3.5)));
1063
1064
assert_eq!("".parse::<Val>(), Err(ValParseError::UnitMissing));
1065
assert_eq!(
1066
"hello world".parse::<Val>(),
1067
Err(ValParseError::ValueMissing)
1068
);
1069
assert_eq!("3".parse::<Val>(), Err(ValParseError::UnitMissing));
1070
assert_eq!("3.5".parse::<Val>(), Err(ValParseError::UnitMissing));
1071
assert_eq!("3pxx".parse::<Val>(), Err(ValParseError::InvalidUnit));
1072
assert_eq!("3.5pxx".parse::<Val>(), Err(ValParseError::InvalidUnit));
1073
assert_eq!("3-3px".parse::<Val>(), Err(ValParseError::InvalidValue));
1074
assert_eq!("3.5-3px".parse::<Val>(), Err(ValParseError::InvalidValue));
1075
}
1076
1077
#[test]
1078
fn default_val_equals_const_default_val() {
1079
assert_eq!(Val::default(), Val::DEFAULT);
1080
}
1081
1082
#[test]
1083
fn uirect_default_equals_const_default() {
1084
assert_eq!(UiRect::default(), UiRect::all(Val::ZERO));
1085
assert_eq!(UiRect::default(), UiRect::DEFAULT);
1086
}
1087
1088
#[test]
1089
fn test_uirect_axes() {
1090
let x = Val::Px(1.);
1091
let y = Val::Vw(4.);
1092
let r = UiRect::axes(x, y);
1093
let h = UiRect::horizontal(x);
1094
let v = UiRect::vertical(y);
1095
1096
assert_eq!(r.top, v.top);
1097
assert_eq!(r.bottom, v.bottom);
1098
assert_eq!(r.left, h.left);
1099
assert_eq!(r.right, h.right);
1100
}
1101
1102
#[test]
1103
fn uirect_px() {
1104
let r = UiRect::px(3., 5., 20., 999.);
1105
assert_eq!(r.left, Val::Px(3.));
1106
assert_eq!(r.right, Val::Px(5.));
1107
assert_eq!(r.top, Val::Px(20.));
1108
assert_eq!(r.bottom, Val::Px(999.));
1109
}
1110
1111
#[test]
1112
fn uirect_percent() {
1113
let r = UiRect::percent(3., 5., 20., 99.);
1114
assert_eq!(r.left, Val::Percent(3.));
1115
assert_eq!(r.right, Val::Percent(5.));
1116
assert_eq!(r.top, Val::Percent(20.));
1117
assert_eq!(r.bottom, Val::Percent(99.));
1118
}
1119
}
1120
1121