Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ui/src/ui_node.rs
6598 views
1
use crate::{
2
ui_transform::{UiGlobalTransform, UiTransform},
3
FocusPolicy, UiRect, Val,
4
};
5
use bevy_camera::{visibility::Visibility, Camera, RenderTarget};
6
use bevy_color::{Alpha, Color};
7
use bevy_derive::{Deref, DerefMut};
8
use bevy_ecs::{prelude::*, system::SystemParam};
9
use bevy_math::{vec4, Rect, UVec2, Vec2, Vec4Swizzles};
10
use bevy_reflect::prelude::*;
11
use bevy_sprite::BorderRect;
12
use bevy_utils::once;
13
use bevy_window::{PrimaryWindow, WindowRef};
14
use core::{f32, num::NonZero};
15
use derive_more::derive::From;
16
use smallvec::SmallVec;
17
use thiserror::Error;
18
use tracing::warn;
19
20
/// Provides the computed size and layout properties of the node.
21
///
22
/// Fields in this struct are public but should not be modified under most circumstances.
23
/// For example, in a scrollbar you may want to derive the handle's size from the proportion of
24
/// scrollable content in-view. You can directly modify `ComputedNode` after layout to set the
25
/// handle size without any delays.
26
#[derive(Component, Debug, Copy, Clone, PartialEq, Reflect)]
27
#[reflect(Component, Default, Debug, Clone)]
28
pub struct ComputedNode {
29
/// The order of the node in the UI layout.
30
/// Nodes with a higher stack index are drawn on top of and receive interactions before nodes with lower stack indices.
31
///
32
/// Automatically calculated in [`UiSystems::Stack`](`super::UiSystems::Stack`).
33
pub stack_index: u32,
34
/// The size of the node as width and height in physical pixels.
35
///
36
/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).
37
pub size: Vec2,
38
/// Size of this node's content.
39
///
40
/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).
41
pub content_size: Vec2,
42
/// Space allocated for scrollbars.
43
///
44
/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).
45
pub scrollbar_size: Vec2,
46
/// Resolved offset of scrolled content
47
///
48
/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).
49
pub scroll_position: Vec2,
50
/// The width of this node's outline.
51
/// If this value is `Auto`, negative or `0.` then no outline will be rendered.
52
/// Outline updates bypass change detection.
53
///
54
/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).
55
pub outline_width: f32,
56
/// The amount of space between the outline and the edge of the node.
57
/// Outline updates bypass change detection.
58
///
59
/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).
60
pub outline_offset: f32,
61
/// The unrounded size of the node as width and height in physical pixels.
62
///
63
/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).
64
pub unrounded_size: Vec2,
65
/// Resolved border values in physical pixels.
66
/// Border updates bypass change detection.
67
///
68
/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).
69
pub border: BorderRect,
70
/// Resolved border radius values in physical pixels.
71
/// Border radius updates bypass change detection.
72
///
73
/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).
74
pub border_radius: ResolvedBorderRadius,
75
/// Resolved padding values in physical pixels.
76
/// Padding updates bypass change detection.
77
///
78
/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).
79
pub padding: BorderRect,
80
/// Inverse scale factor for this Node.
81
/// Multiply physical coordinates by the inverse scale factor to give logical coordinates.
82
///
83
/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).
84
pub inverse_scale_factor: f32,
85
}
86
87
impl ComputedNode {
88
/// The calculated node size as width and height in physical pixels.
89
///
90
/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).
91
#[inline]
92
pub const fn size(&self) -> Vec2 {
93
self.size
94
}
95
96
/// The calculated node content size as width and height in physical pixels.
97
///
98
/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).
99
#[inline]
100
pub const fn content_size(&self) -> Vec2 {
101
self.content_size
102
}
103
104
/// Check if the node is empty.
105
/// A node is considered empty if it has a zero or negative extent along either of its axes.
106
#[inline]
107
pub const fn is_empty(&self) -> bool {
108
self.size.x <= 0. || self.size.y <= 0.
109
}
110
111
/// The order of the node in the UI layout.
112
/// Nodes with a higher stack index are drawn on top of and receive interactions before nodes with lower stack indices.
113
///
114
/// Automatically calculated in [`UiSystems::Stack`](super::UiSystems::Stack).
115
pub const fn stack_index(&self) -> u32 {
116
self.stack_index
117
}
118
119
/// The calculated node size as width and height in physical pixels before rounding.
120
///
121
/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).
122
#[inline]
123
pub const fn unrounded_size(&self) -> Vec2 {
124
self.unrounded_size
125
}
126
127
/// Returns the thickness of the UI node's outline in physical pixels.
128
/// If this value is negative or `0.` then no outline will be rendered.
129
///
130
/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).
131
#[inline]
132
pub const fn outline_width(&self) -> f32 {
133
self.outline_width
134
}
135
136
/// Returns the amount of space between the outline and the edge of the node in physical pixels.
137
///
138
/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).
139
#[inline]
140
pub const fn outline_offset(&self) -> f32 {
141
self.outline_offset
142
}
143
144
/// Returns the size of the node when including its outline.
145
///
146
/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).
147
#[inline]
148
pub const fn outlined_node_size(&self) -> Vec2 {
149
let offset = 2. * (self.outline_offset + self.outline_width);
150
Vec2::new(self.size.x + offset, self.size.y + offset)
151
}
152
153
/// Returns the border radius for each corner of the outline
154
/// An outline's border radius is derived from the node's border-radius
155
/// so that the outline wraps the border equally at all points.
156
///
157
/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).
158
#[inline]
159
pub const fn outline_radius(&self) -> ResolvedBorderRadius {
160
let outer_distance = self.outline_width + self.outline_offset;
161
const fn compute_radius(radius: f32, outer_distance: f32) -> f32 {
162
if radius > 0. {
163
radius + outer_distance
164
} else {
165
0.
166
}
167
}
168
ResolvedBorderRadius {
169
top_left: compute_radius(self.border_radius.top_left, outer_distance),
170
top_right: compute_radius(self.border_radius.top_right, outer_distance),
171
bottom_right: compute_radius(self.border_radius.bottom_right, outer_distance),
172
bottom_left: compute_radius(self.border_radius.bottom_left, outer_distance),
173
}
174
}
175
176
/// Returns the thickness of the node's border on each edge in physical pixels.
177
///
178
/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).
179
#[inline]
180
pub const fn border(&self) -> BorderRect {
181
self.border
182
}
183
184
/// Returns the border radius for each of the node's corners in physical pixels.
185
///
186
/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).
187
#[inline]
188
pub const fn border_radius(&self) -> ResolvedBorderRadius {
189
self.border_radius
190
}
191
192
/// Returns the inner border radius for each of the node's corners in physical pixels.
193
pub fn inner_radius(&self) -> ResolvedBorderRadius {
194
fn clamp_corner(r: f32, size: Vec2, offset: Vec2) -> f32 {
195
let s = 0.5 * size + offset;
196
let sm = s.x.min(s.y);
197
r.min(sm)
198
}
199
let b = vec4(
200
self.border.left,
201
self.border.top,
202
self.border.right,
203
self.border.bottom,
204
);
205
let s = self.size() - b.xy() - b.zw();
206
ResolvedBorderRadius {
207
top_left: clamp_corner(self.border_radius.top_left, s, b.xy()),
208
top_right: clamp_corner(self.border_radius.top_right, s, b.zy()),
209
bottom_right: clamp_corner(self.border_radius.bottom_left, s, b.xw()),
210
bottom_left: clamp_corner(self.border_radius.bottom_right, s, b.zw()),
211
}
212
}
213
214
/// Returns the thickness of the node's padding on each edge in physical pixels.
215
///
216
/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).
217
#[inline]
218
pub const fn padding(&self) -> BorderRect {
219
self.padding
220
}
221
222
/// Returns the combined inset on each edge including both padding and border thickness in physical pixels.
223
#[inline]
224
pub const fn content_inset(&self) -> BorderRect {
225
BorderRect {
226
left: self.border.left + self.padding.left,
227
right: self.border.right + self.padding.right,
228
top: self.border.top + self.padding.top,
229
bottom: self.border.bottom + self.padding.bottom,
230
}
231
}
232
233
/// Returns the inverse of the scale factor for this node.
234
/// To convert from physical coordinates to logical coordinates multiply by this value.
235
#[inline]
236
pub const fn inverse_scale_factor(&self) -> f32 {
237
self.inverse_scale_factor
238
}
239
240
// Returns true if `point` within the node.
241
//
242
// Matches the sdf function in `ui.wgsl` that is used by the UI renderer to draw rounded rectangles.
243
pub fn contains_point(&self, transform: UiGlobalTransform, point: Vec2) -> bool {
244
let Some(local_point) = transform
245
.try_inverse()
246
.map(|transform| transform.transform_point2(point))
247
else {
248
return false;
249
};
250
let [top, bottom] = if local_point.x < 0. {
251
[self.border_radius.top_left, self.border_radius.bottom_left]
252
} else {
253
[
254
self.border_radius.top_right,
255
self.border_radius.bottom_right,
256
]
257
};
258
let r = if local_point.y < 0. { top } else { bottom };
259
let corner_to_point = local_point.abs() - 0.5 * self.size;
260
let q = corner_to_point + r;
261
let l = q.max(Vec2::ZERO).length();
262
let m = q.max_element().min(0.);
263
l + m - r < 0.
264
}
265
266
/// Transform a point to normalized node space with the center of the node at the origin and the corners at [+/-0.5, +/-0.5]
267
pub fn normalize_point(&self, transform: UiGlobalTransform, point: Vec2) -> Option<Vec2> {
268
self.size
269
.cmpgt(Vec2::ZERO)
270
.all()
271
.then(|| transform.try_inverse())
272
.flatten()
273
.map(|transform| transform.transform_point2(point) / self.size)
274
}
275
276
/// Resolve the node's clipping rect in local space
277
pub fn resolve_clip_rect(
278
&self,
279
overflow: Overflow,
280
overflow_clip_margin: OverflowClipMargin,
281
) -> Rect {
282
let mut clip_rect = Rect::from_center_size(Vec2::ZERO, self.size);
283
284
let clip_inset = match overflow_clip_margin.visual_box {
285
OverflowClipBox::BorderBox => BorderRect::ZERO,
286
OverflowClipBox::ContentBox => self.content_inset(),
287
OverflowClipBox::PaddingBox => self.border(),
288
};
289
290
clip_rect.min.x += clip_inset.left;
291
clip_rect.min.y += clip_inset.top;
292
clip_rect.max.x -= clip_inset.right;
293
clip_rect.max.y -= clip_inset.bottom;
294
295
if overflow.x == OverflowAxis::Visible {
296
clip_rect.min.x = -f32::INFINITY;
297
clip_rect.max.x = f32::INFINITY;
298
}
299
if overflow.y == OverflowAxis::Visible {
300
clip_rect.min.y = -f32::INFINITY;
301
clip_rect.max.y = f32::INFINITY;
302
}
303
304
clip_rect
305
}
306
}
307
308
impl ComputedNode {
309
pub const DEFAULT: Self = Self {
310
stack_index: 0,
311
size: Vec2::ZERO,
312
content_size: Vec2::ZERO,
313
scrollbar_size: Vec2::ZERO,
314
scroll_position: Vec2::ZERO,
315
outline_width: 0.,
316
outline_offset: 0.,
317
unrounded_size: Vec2::ZERO,
318
border_radius: ResolvedBorderRadius::ZERO,
319
border: BorderRect::ZERO,
320
padding: BorderRect::ZERO,
321
inverse_scale_factor: 1.,
322
};
323
}
324
325
impl Default for ComputedNode {
326
fn default() -> Self {
327
Self::DEFAULT
328
}
329
}
330
331
/// The scroll position of the node. Values are in logical pixels, increasing from top-left to bottom-right.
332
///
333
/// Increasing the x-coordinate causes the scrolled content to visibly move left on the screen, while increasing the y-coordinate causes the scrolled content to move up.
334
/// This might seem backwards, however what's really happening is that
335
/// the scroll position is moving the visible "window" in the local coordinate system of the scrolled content -
336
/// moving the window down causes the content to move up.
337
///
338
/// Updating the values of `ScrollPosition` will reposition the children of the node by the offset amount in logical pixels.
339
/// `ScrollPosition` may be updated by the layout system when a layout change makes a previously valid `ScrollPosition` invalid.
340
/// Changing this does nothing on a `Node` without setting at least one `OverflowAxis` to `OverflowAxis::Scroll`.
341
#[derive(Component, Debug, Clone, Default, Deref, DerefMut, Reflect)]
342
#[reflect(Component, Default, Clone)]
343
pub struct ScrollPosition(pub Vec2);
344
345
impl ScrollPosition {
346
pub const DEFAULT: Self = Self(Vec2::ZERO);
347
}
348
349
impl From<Vec2> for ScrollPosition {
350
fn from(value: Vec2) -> Self {
351
Self(value)
352
}
353
}
354
355
/// The base component for UI entities. It describes UI layout and style properties.
356
///
357
/// When defining new types of UI entities, require [`Node`] to make them behave like UI nodes.
358
///
359
/// Nodes can be laid out using either Flexbox or CSS Grid Layout.
360
///
361
/// See below for general learning resources and for documentation on the individual style properties.
362
///
363
/// ### Flexbox
364
///
365
/// - [MDN: Basic Concepts of Flexbox](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox)
366
/// - [A Complete Guide To Flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) by CSS Tricks. This is detailed guide with illustrations and comprehensive written explanation of the different Flexbox properties and how they work.
367
/// - [Flexbox Froggy](https://flexboxfroggy.com/). An interactive tutorial/game that teaches the essential parts of Flexbox in a fun engaging way.
368
///
369
/// ### CSS Grid
370
///
371
/// - [MDN: Basic Concepts of Grid Layout](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Basic_Concepts_of_Grid_Layout)
372
/// - [A Complete Guide To CSS Grid](https://css-tricks.com/snippets/css/complete-guide-grid/) by CSS Tricks. This is detailed guide with illustrations and comprehensive written explanation of the different CSS Grid properties and how they work.
373
/// - [CSS Grid Garden](https://cssgridgarden.com/). An interactive tutorial/game that teaches the essential parts of CSS Grid in a fun engaging way.
374
///
375
/// # See also
376
///
377
/// - [`RelativeCursorPosition`](crate::RelativeCursorPosition) to obtain the cursor position relative to this node
378
/// - [`Interaction`](crate::Interaction) to obtain the interaction state of this node
379
380
#[derive(Component, Clone, PartialEq, Debug, Reflect)]
381
#[require(
382
ComputedNode,
383
ComputedUiTargetCamera,
384
ComputedUiRenderTargetInfo,
385
UiTransform,
386
BackgroundColor,
387
BorderColor,
388
BorderRadius,
389
FocusPolicy,
390
ScrollPosition,
391
Visibility,
392
ZIndex
393
)]
394
#[reflect(Component, Default, PartialEq, Debug, Clone)]
395
#[cfg_attr(
396
feature = "serialize",
397
derive(serde::Serialize, serde::Deserialize),
398
reflect(Serialize, Deserialize)
399
)]
400
pub struct Node {
401
/// Which layout algorithm to use when laying out this node's contents:
402
/// - [`Display::Flex`]: Use the Flexbox layout algorithm
403
/// - [`Display::Grid`]: Use the CSS Grid layout algorithm
404
/// - [`Display::None`]: Hide this node and perform layout as if it does not exist.
405
///
406
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/display>
407
pub display: Display,
408
409
/// Which part of a Node's box length styles like width and height control
410
/// - [`BoxSizing::BorderBox`]: They refer to the "border box" size (size including padding and border)
411
/// - [`BoxSizing::ContentBox`]: They refer to the "content box" size (size excluding padding and border)
412
///
413
/// `BoxSizing::BorderBox` is generally considered more intuitive and is the default in Bevy even though it is not on the web.
414
///
415
/// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing>
416
pub box_sizing: BoxSizing,
417
418
/// Whether a node should be laid out in-flow with, or independently of its siblings:
419
/// - [`PositionType::Relative`]: Layout this node in-flow with other nodes using the usual (flexbox/grid) layout algorithm.
420
/// - [`PositionType::Absolute`]: Layout this node on top and independently of other nodes.
421
///
422
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/position>
423
pub position_type: PositionType,
424
425
/// Whether overflowing content should be displayed or clipped.
426
///
427
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/overflow>
428
pub overflow: Overflow,
429
430
/// How much space in logical pixels should be reserved for scrollbars when overflow is set to scroll or auto on an axis.
431
pub scrollbar_width: f32,
432
433
/// How the bounds of clipped content should be determined
434
///
435
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-margin>
436
pub overflow_clip_margin: OverflowClipMargin,
437
438
/// The horizontal position of the left edge of the node.
439
/// - For relatively positioned nodes, this is relative to the node's position as computed during regular layout.
440
/// - For absolutely positioned nodes, this is relative to the *parent* node's bounding box.
441
///
442
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/left>
443
pub left: Val,
444
445
/// The horizontal position of the right edge of the node.
446
/// - For relatively positioned nodes, this is relative to the node's position as computed during regular layout.
447
/// - For absolutely positioned nodes, this is relative to the *parent* node's bounding box.
448
///
449
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/right>
450
pub right: Val,
451
452
/// The vertical position of the top edge of the node.
453
/// - For relatively positioned nodes, this is relative to the node's position as computed during regular layout.
454
/// - For absolutely positioned nodes, this is relative to the *parent* node's bounding box.
455
///
456
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/top>
457
pub top: Val,
458
459
/// The vertical position of the bottom edge of the node.
460
/// - For relatively positioned nodes, this is relative to the node's position as computed during regular layout.
461
/// - For absolutely positioned nodes, this is relative to the *parent* node's bounding box.
462
///
463
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/bottom>
464
pub bottom: Val,
465
466
/// The ideal width of the node. `width` is used when it is within the bounds defined by `min_width` and `max_width`.
467
///
468
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/width>
469
pub width: Val,
470
471
/// The ideal height of the node. `height` is used when it is within the bounds defined by `min_height` and `max_height`.
472
///
473
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/height>
474
pub height: Val,
475
476
/// The minimum width of the node. `min_width` is used if it is greater than `width` and/or `max_width`.
477
///
478
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/min-width>
479
pub min_width: Val,
480
481
/// The minimum height of the node. `min_height` is used if it is greater than `height` and/or `max_height`.
482
///
483
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/min-height>
484
pub min_height: Val,
485
486
/// The maximum width of the node. `max_width` is used if it is within the bounds defined by `min_width` and `width`.
487
///
488
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/max-width>
489
pub max_width: Val,
490
491
/// The maximum height of the node. `max_height` is used if it is within the bounds defined by `min_height` and `height`.
492
///
493
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/max-height>
494
pub max_height: Val,
495
496
/// The aspect ratio of the node (defined as `width / height`)
497
///
498
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio>
499
pub aspect_ratio: Option<f32>,
500
501
/// Used to control how each individual item is aligned by default within the space they're given.
502
/// - For Flexbox containers, sets default cross axis alignment of the child items.
503
/// - For CSS Grid containers, controls block (vertical) axis alignment of children of this grid container within their grid areas.
504
///
505
/// This value is overridden if [`AlignSelf`] on the child node is set.
506
///
507
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/align-items>
508
pub align_items: AlignItems,
509
510
/// Used to control how each individual item is aligned by default within the space they're given.
511
/// - For Flexbox containers, this property has no effect. See `justify_content` for main axis alignment of flex items.
512
/// - For CSS Grid containers, sets default inline (horizontal) axis alignment of child items within their grid areas.
513
///
514
/// This value is overridden if [`JustifySelf`] on the child node is set.
515
///
516
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/justify-items>
517
pub justify_items: JustifyItems,
518
519
/// Used to control how the specified item is aligned within the space it's given.
520
/// - For Flexbox items, controls cross axis alignment of the item.
521
/// - For CSS Grid items, controls block (vertical) axis alignment of a grid item within its grid area.
522
///
523
/// If set to `Auto`, alignment is inherited from the value of [`AlignItems`] set on the parent node.
524
///
525
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/align-self>
526
pub align_self: AlignSelf,
527
528
/// Used to control how the specified item is aligned within the space it's given.
529
/// - For Flexbox items, this property has no effect. See `justify_content` for main axis alignment of flex items.
530
/// - For CSS Grid items, controls inline (horizontal) axis alignment of a grid item within its grid area.
531
///
532
/// If set to `Auto`, alignment is inherited from the value of [`JustifyItems`] set on the parent node.
533
///
534
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/justify-self>
535
pub justify_self: JustifySelf,
536
537
/// Used to control how items are distributed.
538
/// - For Flexbox containers, controls alignment of lines if `flex_wrap` is set to [`FlexWrap::Wrap`] and there are multiple lines of items.
539
/// - For CSS Grid containers, controls alignment of grid rows.
540
///
541
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/align-content>
542
pub align_content: AlignContent,
543
544
/// Used to control how items are distributed.
545
/// - For Flexbox containers, controls alignment of items in the main axis.
546
/// - For CSS Grid containers, controls alignment of grid columns.
547
///
548
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content>
549
pub justify_content: JustifyContent,
550
551
/// The amount of space around a node outside its border.
552
///
553
/// If a percentage value is used, the percentage is calculated based on the width of the parent node.
554
///
555
/// # Example
556
/// ```
557
/// # use bevy_ui::{Node, UiRect, Val};
558
/// let node = Node {
559
/// margin: UiRect {
560
/// left: Val::Percent(10.),
561
/// right: Val::Percent(10.),
562
/// top: Val::Percent(15.),
563
/// bottom: Val::Percent(15.)
564
/// },
565
/// ..Default::default()
566
/// };
567
/// ```
568
/// A node with this style and a parent with dimensions of 100px by 300px will have calculated margins of 10px on both left and right edges, and 15px on both top and bottom edges.
569
///
570
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/margin>
571
pub margin: UiRect,
572
573
/// The amount of space between the edges of a node and its contents.
574
///
575
/// If a percentage value is used, the percentage is calculated based on the width of the parent node.
576
///
577
/// # Example
578
/// ```
579
/// # use bevy_ui::{Node, UiRect, Val};
580
/// let node = Node {
581
/// padding: UiRect {
582
/// left: Val::Percent(1.),
583
/// right: Val::Percent(2.),
584
/// top: Val::Percent(3.),
585
/// bottom: Val::Percent(4.)
586
/// },
587
/// ..Default::default()
588
/// };
589
/// ```
590
/// A node with this style and a parent with dimensions of 300px by 100px will have calculated padding of 3px on the left, 6px on the right, 9px on the top and 12px on the bottom.
591
///
592
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/padding>
593
pub padding: UiRect,
594
595
/// The amount of space between the margins of a node and its padding.
596
///
597
/// If a percentage value is used, the percentage is calculated based on the width of the parent node.
598
///
599
/// The size of the node will be expanded if there are constraints that prevent the layout algorithm from placing the border within the existing node boundary.
600
///
601
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/border-width>
602
pub border: UiRect,
603
604
/// Whether a Flexbox container should be a row or a column. This property has no effect on Grid nodes.
605
///
606
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/flex-direction>
607
pub flex_direction: FlexDirection,
608
609
/// Whether a Flexbox container should wrap its contents onto multiple lines if they overflow. This property has no effect on Grid nodes.
610
///
611
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/flex-wrap>
612
pub flex_wrap: FlexWrap,
613
614
/// Defines how much a flexbox item should grow if there's space available. Defaults to 0 (don't grow at all).
615
///
616
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/flex-grow>
617
pub flex_grow: f32,
618
619
/// Defines how much a flexbox item should shrink if there's not enough space available. Defaults to 1.
620
///
621
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/flex-shrink>
622
pub flex_shrink: f32,
623
624
/// The initial length of a flexbox in the main axis, before flex growing/shrinking properties are applied.
625
///
626
/// `flex_basis` overrides `width` (if the main axis is horizontal) or `height` (if the main axis is vertical) when both are set, but it obeys the constraints defined by `min_width`/`min_height` and `max_width`/`max_height`.
627
///
628
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/flex-basis>
629
pub flex_basis: Val,
630
631
/// The size of the gutters between items in a vertical flexbox layout or between rows in a grid layout.
632
///
633
/// Note: Values of `Val::Auto` are not valid and are treated as zero.
634
///
635
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/row-gap>
636
pub row_gap: Val,
637
638
/// The size of the gutters between items in a horizontal flexbox layout or between column in a grid layout.
639
///
640
/// Note: Values of `Val::Auto` are not valid and are treated as zero.
641
///
642
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/column-gap>
643
pub column_gap: Val,
644
645
/// Controls whether automatically placed grid items are placed row-wise or column-wise as well as whether the sparse or dense packing algorithm is used.
646
/// Only affects Grid layouts.
647
///
648
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow>
649
pub grid_auto_flow: GridAutoFlow,
650
651
/// Defines the number of rows a grid has and the sizes of those rows. If grid items are given explicit placements then more rows may
652
/// be implicitly generated by items that are placed out of bounds. The sizes of those rows are controlled by `grid_auto_rows` property.
653
///
654
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-rows>
655
pub grid_template_rows: Vec<RepeatedGridTrack>,
656
657
/// Defines the number of columns a grid has and the sizes of those columns. If grid items are given explicit placements then more columns may
658
/// be implicitly generated by items that are placed out of bounds. The sizes of those columns are controlled by `grid_auto_columns` property.
659
///
660
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns>
661
pub grid_template_columns: Vec<RepeatedGridTrack>,
662
663
/// Defines the size of implicitly created rows. Rows are created implicitly when grid items are given explicit placements that are out of bounds
664
/// of the rows explicitly created using `grid_template_rows`.
665
///
666
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-rows>
667
pub grid_auto_rows: Vec<GridTrack>,
668
/// Defines the size of implicitly created columns. Columns are created implicitly when grid items are given explicit placements that are out of bounds
669
/// of the columns explicitly created using `grid_template_columns`.
670
///
671
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-columns>
672
pub grid_auto_columns: Vec<GridTrack>,
673
674
/// The row in which a grid item starts and how many rows it spans.
675
///
676
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-row>
677
pub grid_row: GridPlacement,
678
679
/// The column in which a grid item starts and how many columns it spans.
680
///
681
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-column>
682
pub grid_column: GridPlacement,
683
}
684
685
impl Node {
686
pub const DEFAULT: Self = Self {
687
display: Display::DEFAULT,
688
box_sizing: BoxSizing::DEFAULT,
689
position_type: PositionType::DEFAULT,
690
left: Val::Auto,
691
right: Val::Auto,
692
top: Val::Auto,
693
bottom: Val::Auto,
694
flex_direction: FlexDirection::DEFAULT,
695
flex_wrap: FlexWrap::DEFAULT,
696
align_items: AlignItems::DEFAULT,
697
justify_items: JustifyItems::DEFAULT,
698
align_self: AlignSelf::DEFAULT,
699
justify_self: JustifySelf::DEFAULT,
700
align_content: AlignContent::DEFAULT,
701
justify_content: JustifyContent::DEFAULT,
702
margin: UiRect::DEFAULT,
703
padding: UiRect::DEFAULT,
704
border: UiRect::DEFAULT,
705
flex_grow: 0.0,
706
flex_shrink: 1.0,
707
flex_basis: Val::Auto,
708
width: Val::Auto,
709
height: Val::Auto,
710
min_width: Val::Auto,
711
min_height: Val::Auto,
712
max_width: Val::Auto,
713
max_height: Val::Auto,
714
aspect_ratio: None,
715
overflow: Overflow::DEFAULT,
716
overflow_clip_margin: OverflowClipMargin::DEFAULT,
717
scrollbar_width: 0.,
718
row_gap: Val::ZERO,
719
column_gap: Val::ZERO,
720
grid_auto_flow: GridAutoFlow::DEFAULT,
721
grid_template_rows: Vec::new(),
722
grid_template_columns: Vec::new(),
723
grid_auto_rows: Vec::new(),
724
grid_auto_columns: Vec::new(),
725
grid_column: GridPlacement::DEFAULT,
726
grid_row: GridPlacement::DEFAULT,
727
};
728
}
729
730
impl Default for Node {
731
fn default() -> Self {
732
Self::DEFAULT
733
}
734
}
735
736
/// Used to control how each individual item is aligned by default within the space they're given.
737
/// - For Flexbox containers, sets default cross axis alignment of the child items.
738
/// - For CSS Grid containers, controls block (vertical) axis alignment of children of this grid container within their grid areas.
739
///
740
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/align-items>
741
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
742
#[reflect(Default, PartialEq, Clone)]
743
#[cfg_attr(
744
feature = "serialize",
745
derive(serde::Serialize, serde::Deserialize),
746
reflect(Serialize, Deserialize)
747
)]
748
pub enum AlignItems {
749
/// The items are packed in their default position as if no alignment was applied.
750
Default,
751
/// The items are packed towards the start of the axis.
752
Start,
753
/// The items are packed towards the end of the axis.
754
End,
755
/// The items are packed towards the start of the axis, unless the flex direction is reversed;
756
/// then they are packed towards the end of the axis.
757
FlexStart,
758
/// The items are packed towards the end of the axis, unless the flex direction is reversed;
759
/// then they are packed towards the start of the axis.
760
FlexEnd,
761
/// The items are packed along the center of the axis.
762
Center,
763
/// The items are packed such that their baselines align.
764
Baseline,
765
/// The items are stretched to fill the space they're given.
766
Stretch,
767
}
768
769
impl AlignItems {
770
pub const DEFAULT: Self = Self::Default;
771
}
772
773
impl Default for AlignItems {
774
fn default() -> Self {
775
Self::DEFAULT
776
}
777
}
778
779
/// Used to control how each individual item is aligned by default within the space they're given.
780
/// - For Flexbox containers, this property has no effect. See `justify_content` for main axis alignment of flex items.
781
/// - For CSS Grid containers, sets default inline (horizontal) axis alignment of child items within their grid areas.
782
///
783
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/justify-items>
784
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
785
#[reflect(Default, PartialEq, Clone)]
786
#[cfg_attr(
787
feature = "serialize",
788
derive(serde::Serialize, serde::Deserialize),
789
reflect(Serialize, Deserialize)
790
)]
791
pub enum JustifyItems {
792
/// The items are packed in their default position as if no alignment was applied.
793
Default,
794
/// The items are packed towards the start of the axis.
795
Start,
796
/// The items are packed towards the end of the axis.
797
End,
798
/// The items are packed along the center of the axis
799
Center,
800
/// The items are packed such that their baselines align.
801
Baseline,
802
/// The items are stretched to fill the space they're given.
803
Stretch,
804
}
805
806
impl JustifyItems {
807
pub const DEFAULT: Self = Self::Default;
808
}
809
810
impl Default for JustifyItems {
811
fn default() -> Self {
812
Self::DEFAULT
813
}
814
}
815
816
/// Used to control how the specified item is aligned within the space it's given.
817
/// - For Flexbox items, controls cross axis alignment of the item.
818
/// - For CSS Grid items, controls block (vertical) axis alignment of a grid item within its grid area.
819
///
820
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/align-self>
821
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
822
#[reflect(Default, PartialEq, Clone)]
823
#[cfg_attr(
824
feature = "serialize",
825
derive(serde::Serialize, serde::Deserialize),
826
reflect(Serialize, Deserialize)
827
)]
828
pub enum AlignSelf {
829
/// Use the parent node's [`AlignItems`] value to determine how this item should be aligned.
830
Auto,
831
/// This item will be aligned with the start of the axis.
832
Start,
833
/// This item will be aligned with the end of the axis.
834
End,
835
/// This item will be aligned with the start of the axis, unless the flex direction is reversed;
836
/// then it will be aligned with the end of the axis.
837
FlexStart,
838
/// This item will be aligned with the end of the axis, unless the flex direction is reversed;
839
/// then it will be aligned with the start of the axis.
840
FlexEnd,
841
/// This item will be aligned along the center of the axis.
842
Center,
843
/// This item will be aligned at the baseline.
844
Baseline,
845
/// This item will be stretched to fill the container.
846
Stretch,
847
}
848
849
impl AlignSelf {
850
pub const DEFAULT: Self = Self::Auto;
851
}
852
853
impl Default for AlignSelf {
854
fn default() -> Self {
855
Self::DEFAULT
856
}
857
}
858
859
/// Used to control how the specified item is aligned within the space it's given.
860
/// - For Flexbox items, this property has no effect. See `justify_content` for main axis alignment of flex items.
861
/// - For CSS Grid items, controls inline (horizontal) axis alignment of a grid item within its grid area.
862
///
863
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/justify-self>
864
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
865
#[reflect(Default, PartialEq, Clone)]
866
#[cfg_attr(
867
feature = "serialize",
868
derive(serde::Serialize, serde::Deserialize),
869
reflect(Serialize, Deserialize)
870
)]
871
pub enum JustifySelf {
872
/// Use the parent node's [`JustifyItems`] value to determine how this item should be aligned.
873
Auto,
874
/// This item will be aligned with the start of the axis.
875
Start,
876
/// This item will be aligned with the end of the axis.
877
End,
878
/// This item will be aligned along the center of the axis.
879
Center,
880
/// This item will be aligned at the baseline.
881
Baseline,
882
/// This item will be stretched to fill the space it's given.
883
Stretch,
884
}
885
886
impl JustifySelf {
887
pub const DEFAULT: Self = Self::Auto;
888
}
889
890
impl Default for JustifySelf {
891
fn default() -> Self {
892
Self::DEFAULT
893
}
894
}
895
896
/// Used to control how items are distributed.
897
/// - For Flexbox containers, controls alignment of lines if `flex_wrap` is set to [`FlexWrap::Wrap`] and there are multiple lines of items.
898
/// - For CSS Grid containers, controls alignment of grid rows.
899
///
900
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/align-content>
901
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
902
#[reflect(Default, PartialEq, Clone)]
903
#[cfg_attr(
904
feature = "serialize",
905
derive(serde::Serialize, serde::Deserialize),
906
reflect(Serialize, Deserialize)
907
)]
908
pub enum AlignContent {
909
/// The items are packed in their default position as if no alignment was applied.
910
Default,
911
/// The items are packed towards the start of the axis.
912
Start,
913
/// The items are packed towards the end of the axis.
914
End,
915
/// The items are packed towards the start of the axis, unless the flex direction is reversed;
916
/// then the items are packed towards the end of the axis.
917
FlexStart,
918
/// The items are packed towards the end of the axis, unless the flex direction is reversed;
919
/// then the items are packed towards the start of the axis.
920
FlexEnd,
921
/// The items are packed along the center of the axis.
922
Center,
923
/// The items are stretched to fill the container along the axis.
924
Stretch,
925
/// The items are distributed such that the gap between any two items is equal.
926
SpaceBetween,
927
/// The items are distributed such that the gap between and around any two items is equal.
928
SpaceEvenly,
929
/// The items are distributed such that the gap between and around any two items is equal, with half-size gaps on either end.
930
SpaceAround,
931
}
932
933
impl AlignContent {
934
pub const DEFAULT: Self = Self::Default;
935
}
936
937
impl Default for AlignContent {
938
fn default() -> Self {
939
Self::DEFAULT
940
}
941
}
942
943
/// Used to control how items are distributed.
944
/// - For Flexbox containers, controls alignment of items in the main axis.
945
/// - For CSS Grid containers, controls alignment of grid columns.
946
///
947
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content>
948
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
949
#[reflect(Default, PartialEq, Clone)]
950
#[cfg_attr(
951
feature = "serialize",
952
derive(serde::Serialize, serde::Deserialize),
953
reflect(Serialize, Deserialize)
954
)]
955
pub enum JustifyContent {
956
/// The items are packed in their default position as if no alignment was applied.
957
Default,
958
/// The items are packed towards the start of the axis.
959
Start,
960
/// The items are packed towards the end of the axis.
961
End,
962
/// The items are packed towards the start of the axis, unless the flex direction is reversed;
963
/// then the items are packed towards the end of the axis.
964
FlexStart,
965
/// The items are packed towards the end of the axis, unless the flex direction is reversed;
966
/// then the items are packed towards the start of the axis.
967
FlexEnd,
968
/// The items are packed along the center of the axis.
969
Center,
970
/// The items are stretched to fill the container along the axis.
971
Stretch,
972
/// The items are distributed such that the gap between any two items is equal.
973
SpaceBetween,
974
/// The items are distributed such that the gap between and around any two items is equal.
975
SpaceEvenly,
976
/// The items are distributed such that the gap between and around any two items is equal, with half-size gaps on either end.
977
SpaceAround,
978
}
979
980
impl JustifyContent {
981
pub const DEFAULT: Self = Self::Default;
982
}
983
984
impl Default for JustifyContent {
985
fn default() -> Self {
986
Self::DEFAULT
987
}
988
}
989
990
/// Defines the layout model used by this node.
991
///
992
/// Part of the [`Node`] component.
993
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
994
#[reflect(Default, PartialEq, Clone)]
995
#[cfg_attr(
996
feature = "serialize",
997
derive(serde::Serialize, serde::Deserialize),
998
reflect(Serialize, Deserialize)
999
)]
1000
pub enum Display {
1001
/// Use Flexbox layout model to determine the position of this [`Node`]'s children.
1002
Flex,
1003
/// Use CSS Grid layout model to determine the position of this [`Node`]'s children.
1004
Grid,
1005
/// Use CSS Block layout model to determine the position of this [`Node`]'s children.
1006
Block,
1007
/// Use no layout, don't render this node and its children.
1008
///
1009
/// If you want to hide a node and its children,
1010
/// but keep its layout in place, set its [`Visibility`] component instead.
1011
None,
1012
}
1013
1014
impl Display {
1015
pub const DEFAULT: Self = Self::Flex;
1016
}
1017
1018
impl Default for Display {
1019
fn default() -> Self {
1020
Self::DEFAULT
1021
}
1022
}
1023
1024
/// Which part of a Node's box length styles like width and height control
1025
///
1026
/// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing>
1027
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
1028
#[reflect(Default, PartialEq, Clone)]
1029
#[cfg_attr(
1030
feature = "serialize",
1031
derive(serde::Serialize, serde::Deserialize),
1032
reflect(Serialize, Deserialize)
1033
)]
1034
pub enum BoxSizing {
1035
/// Length styles like width and height refer to the "border box" size (size including padding and border)
1036
BorderBox,
1037
/// Length styles like width and height refer to the "content box" size (size excluding padding and border)
1038
ContentBox,
1039
}
1040
1041
impl BoxSizing {
1042
pub const DEFAULT: Self = Self::BorderBox;
1043
}
1044
1045
impl Default for BoxSizing {
1046
fn default() -> Self {
1047
Self::DEFAULT
1048
}
1049
}
1050
1051
/// Defines how flexbox items are ordered within a flexbox
1052
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
1053
#[reflect(Default, PartialEq, Clone)]
1054
#[cfg_attr(
1055
feature = "serialize",
1056
derive(serde::Serialize, serde::Deserialize),
1057
reflect(Serialize, Deserialize)
1058
)]
1059
pub enum FlexDirection {
1060
/// Same way as text direction along the main axis.
1061
Row,
1062
/// Flex from top to bottom.
1063
Column,
1064
/// Opposite way as text direction along the main axis.
1065
RowReverse,
1066
/// Flex from bottom to top.
1067
ColumnReverse,
1068
}
1069
1070
impl FlexDirection {
1071
pub const DEFAULT: Self = Self::Row;
1072
}
1073
1074
impl Default for FlexDirection {
1075
fn default() -> Self {
1076
Self::DEFAULT
1077
}
1078
}
1079
1080
/// Whether to show or hide overflowing items
1081
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
1082
#[reflect(Default, PartialEq, Clone)]
1083
#[cfg_attr(
1084
feature = "serialize",
1085
derive(serde::Serialize, serde::Deserialize),
1086
reflect(Serialize, Deserialize)
1087
)]
1088
pub struct Overflow {
1089
/// Whether to show or clip overflowing items on the x axis
1090
pub x: OverflowAxis,
1091
/// Whether to show or clip overflowing items on the y axis
1092
pub y: OverflowAxis,
1093
}
1094
1095
impl Overflow {
1096
pub const DEFAULT: Self = Self {
1097
x: OverflowAxis::DEFAULT,
1098
y: OverflowAxis::DEFAULT,
1099
};
1100
1101
/// Show overflowing items on both axes
1102
pub const fn visible() -> Self {
1103
Self {
1104
x: OverflowAxis::Visible,
1105
y: OverflowAxis::Visible,
1106
}
1107
}
1108
1109
/// Clip overflowing items on both axes
1110
pub const fn clip() -> Self {
1111
Self {
1112
x: OverflowAxis::Clip,
1113
y: OverflowAxis::Clip,
1114
}
1115
}
1116
1117
/// Clip overflowing items on the x axis
1118
pub const fn clip_x() -> Self {
1119
Self {
1120
x: OverflowAxis::Clip,
1121
y: OverflowAxis::Visible,
1122
}
1123
}
1124
1125
/// Clip overflowing items on the y axis
1126
pub const fn clip_y() -> Self {
1127
Self {
1128
x: OverflowAxis::Visible,
1129
y: OverflowAxis::Clip,
1130
}
1131
}
1132
1133
/// Hide overflowing items on both axes by influencing layout and then clipping
1134
pub const fn hidden() -> Self {
1135
Self {
1136
x: OverflowAxis::Hidden,
1137
y: OverflowAxis::Hidden,
1138
}
1139
}
1140
1141
/// Hide overflowing items on the x axis by influencing layout and then clipping
1142
pub const fn hidden_x() -> Self {
1143
Self {
1144
x: OverflowAxis::Hidden,
1145
y: OverflowAxis::Visible,
1146
}
1147
}
1148
1149
/// Hide overflowing items on the y axis by influencing layout and then clipping
1150
pub const fn hidden_y() -> Self {
1151
Self {
1152
x: OverflowAxis::Visible,
1153
y: OverflowAxis::Hidden,
1154
}
1155
}
1156
1157
/// Overflow is visible on both axes
1158
pub const fn is_visible(&self) -> bool {
1159
self.x.is_visible() && self.y.is_visible()
1160
}
1161
1162
pub const fn scroll() -> Self {
1163
Self {
1164
x: OverflowAxis::Scroll,
1165
y: OverflowAxis::Scroll,
1166
}
1167
}
1168
1169
/// Scroll overflowing items on the x axis
1170
pub const fn scroll_x() -> Self {
1171
Self {
1172
x: OverflowAxis::Scroll,
1173
y: OverflowAxis::Visible,
1174
}
1175
}
1176
1177
/// Scroll overflowing items on the y axis
1178
pub const fn scroll_y() -> Self {
1179
Self {
1180
x: OverflowAxis::Visible,
1181
y: OverflowAxis::Scroll,
1182
}
1183
}
1184
}
1185
1186
impl Default for Overflow {
1187
fn default() -> Self {
1188
Self::DEFAULT
1189
}
1190
}
1191
1192
/// Whether to show or hide overflowing items
1193
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
1194
#[reflect(Default, PartialEq, Clone)]
1195
#[cfg_attr(
1196
feature = "serialize",
1197
derive(serde::Serialize, serde::Deserialize),
1198
reflect(Serialize, Deserialize)
1199
)]
1200
pub enum OverflowAxis {
1201
/// Show overflowing items.
1202
Visible,
1203
/// Hide overflowing items by clipping.
1204
Clip,
1205
/// Hide overflowing items by influencing layout and then clipping.
1206
Hidden,
1207
/// Scroll overflowing items.
1208
Scroll,
1209
}
1210
1211
impl OverflowAxis {
1212
pub const DEFAULT: Self = Self::Visible;
1213
1214
/// Overflow is visible on this axis
1215
pub const fn is_visible(&self) -> bool {
1216
matches!(self, Self::Visible)
1217
}
1218
}
1219
1220
impl Default for OverflowAxis {
1221
fn default() -> Self {
1222
Self::DEFAULT
1223
}
1224
}
1225
1226
/// The bounds of the visible area when a UI node is clipped.
1227
#[derive(Default, Copy, Clone, PartialEq, Debug, Reflect)]
1228
#[reflect(Default, PartialEq, Clone)]
1229
#[cfg_attr(
1230
feature = "serialize",
1231
derive(serde::Serialize, serde::Deserialize),
1232
reflect(Serialize, Deserialize)
1233
)]
1234
pub struct OverflowClipMargin {
1235
/// Visible unclipped area
1236
pub visual_box: OverflowClipBox,
1237
/// Width of the margin on each edge of the visual box in logical pixels.
1238
/// The width of the margin will be zero if a negative value is set.
1239
pub margin: f32,
1240
}
1241
1242
impl OverflowClipMargin {
1243
pub const DEFAULT: Self = Self {
1244
visual_box: OverflowClipBox::PaddingBox,
1245
margin: 0.,
1246
};
1247
1248
/// Clip any content that overflows outside the content box
1249
pub const fn content_box() -> Self {
1250
Self {
1251
visual_box: OverflowClipBox::ContentBox,
1252
..Self::DEFAULT
1253
}
1254
}
1255
1256
/// Clip any content that overflows outside the padding box
1257
pub const fn padding_box() -> Self {
1258
Self {
1259
visual_box: OverflowClipBox::PaddingBox,
1260
..Self::DEFAULT
1261
}
1262
}
1263
1264
/// Clip any content that overflows outside the border box
1265
pub const fn border_box() -> Self {
1266
Self {
1267
visual_box: OverflowClipBox::BorderBox,
1268
..Self::DEFAULT
1269
}
1270
}
1271
1272
/// Add a margin on each edge of the visual box in logical pixels.
1273
/// The width of the margin will be zero if a negative value is set.
1274
pub const fn with_margin(mut self, margin: f32) -> Self {
1275
self.margin = margin;
1276
self
1277
}
1278
}
1279
1280
/// Used to determine the bounds of the visible area when a UI node is clipped.
1281
#[derive(Default, Copy, Clone, PartialEq, Eq, Debug, Reflect)]
1282
#[reflect(Default, PartialEq, Clone)]
1283
#[cfg_attr(
1284
feature = "serialize",
1285
derive(serde::Serialize, serde::Deserialize),
1286
reflect(Serialize, Deserialize)
1287
)]
1288
pub enum OverflowClipBox {
1289
/// Clip any content that overflows outside the content box
1290
ContentBox,
1291
/// Clip any content that overflows outside the padding box
1292
#[default]
1293
PaddingBox,
1294
/// Clip any content that overflows outside the border box
1295
BorderBox,
1296
}
1297
1298
/// The strategy used to position this node
1299
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
1300
#[reflect(Default, PartialEq, Clone)]
1301
#[cfg_attr(
1302
feature = "serialize",
1303
derive(serde::Serialize, serde::Deserialize),
1304
reflect(Serialize, Deserialize)
1305
)]
1306
pub enum PositionType {
1307
/// Relative to all other nodes with the [`PositionType::Relative`] value.
1308
Relative,
1309
/// Independent of all other nodes, but relative to its parent node.
1310
Absolute,
1311
}
1312
1313
impl PositionType {
1314
pub const DEFAULT: Self = Self::Relative;
1315
}
1316
1317
impl Default for PositionType {
1318
fn default() -> Self {
1319
Self::DEFAULT
1320
}
1321
}
1322
1323
/// Defines if flexbox items appear on a single line or on multiple lines
1324
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
1325
#[reflect(Default, PartialEq, Clone)]
1326
#[cfg_attr(
1327
feature = "serialize",
1328
derive(serde::Serialize, serde::Deserialize),
1329
reflect(Serialize, Deserialize)
1330
)]
1331
pub enum FlexWrap {
1332
/// Single line, will overflow if needed.
1333
NoWrap,
1334
/// Multiple lines, if needed.
1335
Wrap,
1336
/// Same as [`FlexWrap::Wrap`] but new lines will appear before the previous one.
1337
WrapReverse,
1338
}
1339
1340
impl FlexWrap {
1341
pub const DEFAULT: Self = Self::NoWrap;
1342
}
1343
1344
impl Default for FlexWrap {
1345
fn default() -> Self {
1346
Self::DEFAULT
1347
}
1348
}
1349
1350
/// Controls whether grid items are placed row-wise or column-wise as well as whether the sparse or dense packing algorithm is used.
1351
///
1352
/// The "dense" packing algorithm attempts to fill in holes earlier in the grid, if smaller items come up later.
1353
/// This may cause items to appear out-of-order when doing so would fill in holes left by larger items.
1354
///
1355
/// Defaults to [`GridAutoFlow::Row`].
1356
///
1357
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow>
1358
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
1359
#[reflect(Default, PartialEq, Clone)]
1360
#[cfg_attr(
1361
feature = "serialize",
1362
derive(serde::Serialize, serde::Deserialize),
1363
reflect(Serialize, Deserialize)
1364
)]
1365
pub enum GridAutoFlow {
1366
/// Items are placed by filling each row in turn, adding new rows as necessary.
1367
Row,
1368
/// Items are placed by filling each column in turn, adding new columns as necessary.
1369
Column,
1370
/// Combines `Row` with the dense packing algorithm.
1371
RowDense,
1372
/// Combines `Column` with the dense packing algorithm.
1373
ColumnDense,
1374
}
1375
1376
impl GridAutoFlow {
1377
pub const DEFAULT: Self = Self::Row;
1378
}
1379
1380
impl Default for GridAutoFlow {
1381
fn default() -> Self {
1382
Self::DEFAULT
1383
}
1384
}
1385
1386
#[derive(Default, Copy, Clone, PartialEq, Debug, Reflect)]
1387
#[reflect(Default, PartialEq, Clone)]
1388
#[cfg_attr(
1389
feature = "serialize",
1390
derive(serde::Serialize, serde::Deserialize),
1391
reflect(Serialize, Deserialize)
1392
)]
1393
pub enum MinTrackSizingFunction {
1394
/// Track minimum size should be a fixed pixel value
1395
Px(f32),
1396
/// Track minimum size should be a percentage value
1397
Percent(f32),
1398
/// Track minimum size should be content sized under a min-content constraint
1399
MinContent,
1400
/// Track minimum size should be content sized under a max-content constraint
1401
MaxContent,
1402
/// Track minimum size should be automatically sized
1403
#[default]
1404
Auto,
1405
/// Track minimum size should be a percent of the viewport's smaller dimension.
1406
VMin(f32),
1407
/// Track minimum size should be a percent of the viewport's larger dimension.
1408
VMax(f32),
1409
/// Track minimum size should be a percent of the viewport's height dimension.
1410
Vh(f32),
1411
/// Track minimum size should be a percent of the viewport's width dimension.
1412
Vw(f32),
1413
}
1414
1415
#[derive(Default, Copy, Clone, PartialEq, Debug, Reflect)]
1416
#[reflect(Default, PartialEq, Clone)]
1417
#[cfg_attr(
1418
feature = "serialize",
1419
derive(serde::Serialize, serde::Deserialize),
1420
reflect(Serialize, Deserialize)
1421
)]
1422
pub enum MaxTrackSizingFunction {
1423
/// Track maximum size should be a fixed pixel value
1424
Px(f32),
1425
/// Track maximum size should be a percentage value
1426
Percent(f32),
1427
/// Track maximum size should be content sized under a min-content constraint
1428
MinContent,
1429
/// Track maximum size should be content sized under a max-content constraint
1430
MaxContent,
1431
/// Track maximum size should be sized according to the fit-content formula with a fixed pixel limit
1432
FitContentPx(f32),
1433
/// Track maximum size should be sized according to the fit-content formula with a percentage limit
1434
FitContentPercent(f32),
1435
/// Track maximum size should be automatically sized
1436
#[default]
1437
Auto,
1438
/// The dimension as a fraction of the total available grid space (`fr` units in CSS)
1439
/// Specified value is the numerator of the fraction. Denominator is the sum of all fractions specified in that grid dimension.
1440
///
1441
/// Spec: <https://www.w3.org/TR/css3-grid-layout/#fr-unit>
1442
Fraction(f32),
1443
/// Track maximum size should be a percent of the viewport's smaller dimension.
1444
VMin(f32),
1445
/// Track maximum size should be a percent of the viewport's smaller dimension.
1446
VMax(f32),
1447
/// Track maximum size should be a percent of the viewport's height dimension.
1448
Vh(f32),
1449
/// Track maximum size should be a percent of the viewport's width dimension.
1450
Vw(f32),
1451
}
1452
1453
/// A [`GridTrack`] is a Row or Column of a CSS Grid. This struct specifies what size the track should be.
1454
/// See below for the different "track sizing functions" you can specify.
1455
#[derive(Copy, Clone, PartialEq, Debug, Reflect)]
1456
#[reflect(Default, PartialEq, Clone)]
1457
#[cfg_attr(
1458
feature = "serialize",
1459
derive(serde::Serialize, serde::Deserialize),
1460
reflect(Serialize, Deserialize)
1461
)]
1462
pub struct GridTrack {
1463
pub(crate) min_sizing_function: MinTrackSizingFunction,
1464
pub(crate) max_sizing_function: MaxTrackSizingFunction,
1465
}
1466
1467
impl GridTrack {
1468
pub const DEFAULT: Self = Self {
1469
min_sizing_function: MinTrackSizingFunction::Auto,
1470
max_sizing_function: MaxTrackSizingFunction::Auto,
1471
};
1472
1473
/// Create a grid track with a fixed pixel size
1474
pub fn px<T: From<Self>>(value: f32) -> T {
1475
Self {
1476
min_sizing_function: MinTrackSizingFunction::Px(value),
1477
max_sizing_function: MaxTrackSizingFunction::Px(value),
1478
}
1479
.into()
1480
}
1481
1482
/// Create a grid track with a percentage size
1483
pub fn percent<T: From<Self>>(value: f32) -> T {
1484
Self {
1485
min_sizing_function: MinTrackSizingFunction::Percent(value),
1486
max_sizing_function: MaxTrackSizingFunction::Percent(value),
1487
}
1488
.into()
1489
}
1490
1491
/// Create a grid track with an `fr` size.
1492
/// Note that this will give the track a content-based minimum size.
1493
/// Usually you are best off using `GridTrack::flex` instead which uses a zero minimum size.
1494
pub fn fr<T: From<Self>>(value: f32) -> T {
1495
Self {
1496
min_sizing_function: MinTrackSizingFunction::Auto,
1497
max_sizing_function: MaxTrackSizingFunction::Fraction(value),
1498
}
1499
.into()
1500
}
1501
1502
/// Create a grid track with a `minmax(0, Nfr)` size.
1503
pub fn flex<T: From<Self>>(value: f32) -> T {
1504
Self {
1505
min_sizing_function: MinTrackSizingFunction::Px(0.0),
1506
max_sizing_function: MaxTrackSizingFunction::Fraction(value),
1507
}
1508
.into()
1509
}
1510
1511
/// Create a grid track which is automatically sized to fit its contents.
1512
pub fn auto<T: From<Self>>() -> T {
1513
Self {
1514
min_sizing_function: MinTrackSizingFunction::Auto,
1515
max_sizing_function: MaxTrackSizingFunction::Auto,
1516
}
1517
.into()
1518
}
1519
1520
/// Create a grid track which is automatically sized to fit its contents when sized at their "min-content" sizes
1521
pub fn min_content<T: From<Self>>() -> T {
1522
Self {
1523
min_sizing_function: MinTrackSizingFunction::MinContent,
1524
max_sizing_function: MaxTrackSizingFunction::MinContent,
1525
}
1526
.into()
1527
}
1528
1529
/// Create a grid track which is automatically sized to fit its contents when sized at their "max-content" sizes
1530
pub fn max_content<T: From<Self>>() -> T {
1531
Self {
1532
min_sizing_function: MinTrackSizingFunction::MaxContent,
1533
max_sizing_function: MaxTrackSizingFunction::MaxContent,
1534
}
1535
.into()
1536
}
1537
1538
/// Create a `fit-content()` grid track with fixed pixel limit.
1539
///
1540
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/fit-content_function>
1541
pub fn fit_content_px<T: From<Self>>(limit: f32) -> T {
1542
Self {
1543
min_sizing_function: MinTrackSizingFunction::Auto,
1544
max_sizing_function: MaxTrackSizingFunction::FitContentPx(limit),
1545
}
1546
.into()
1547
}
1548
1549
/// Create a `fit-content()` grid track with percentage limit.
1550
///
1551
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/fit-content_function>
1552
pub fn fit_content_percent<T: From<Self>>(limit: f32) -> T {
1553
Self {
1554
min_sizing_function: MinTrackSizingFunction::Auto,
1555
max_sizing_function: MaxTrackSizingFunction::FitContentPercent(limit),
1556
}
1557
.into()
1558
}
1559
1560
/// Create a `minmax()` grid track.
1561
///
1562
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/minmax>
1563
pub fn minmax<T: From<Self>>(min: MinTrackSizingFunction, max: MaxTrackSizingFunction) -> T {
1564
Self {
1565
min_sizing_function: min,
1566
max_sizing_function: max,
1567
}
1568
.into()
1569
}
1570
1571
/// Create a grid track with a percentage of the viewport's smaller dimension
1572
pub fn vmin<T: From<Self>>(value: f32) -> T {
1573
Self {
1574
min_sizing_function: MinTrackSizingFunction::VMin(value),
1575
max_sizing_function: MaxTrackSizingFunction::VMin(value),
1576
}
1577
.into()
1578
}
1579
1580
/// Create a grid track with a percentage of the viewport's larger dimension
1581
pub fn vmax<T: From<Self>>(value: f32) -> T {
1582
Self {
1583
min_sizing_function: MinTrackSizingFunction::VMax(value),
1584
max_sizing_function: MaxTrackSizingFunction::VMax(value),
1585
}
1586
.into()
1587
}
1588
1589
/// Create a grid track with a percentage of the viewport's height dimension
1590
pub fn vh<T: From<Self>>(value: f32) -> T {
1591
Self {
1592
min_sizing_function: MinTrackSizingFunction::Vh(value),
1593
max_sizing_function: MaxTrackSizingFunction::Vh(value),
1594
}
1595
.into()
1596
}
1597
1598
/// Create a grid track with a percentage of the viewport's width dimension
1599
pub fn vw<T: From<Self>>(value: f32) -> T {
1600
Self {
1601
min_sizing_function: MinTrackSizingFunction::Vw(value),
1602
max_sizing_function: MaxTrackSizingFunction::Vw(value),
1603
}
1604
.into()
1605
}
1606
}
1607
1608
impl Default for GridTrack {
1609
fn default() -> Self {
1610
Self::DEFAULT
1611
}
1612
}
1613
1614
#[derive(Copy, Clone, PartialEq, Debug, Reflect, From)]
1615
#[reflect(Default, PartialEq, Clone)]
1616
#[cfg_attr(
1617
feature = "serialize",
1618
derive(serde::Serialize, serde::Deserialize),
1619
reflect(Serialize, Deserialize)
1620
)]
1621
/// How many times to repeat a repeated grid track
1622
///
1623
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/repeat>
1624
pub enum GridTrackRepetition {
1625
/// Repeat the track fixed number of times
1626
Count(u16),
1627
/// Repeat the track to fill available space
1628
///
1629
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/repeat#auto-fill>
1630
AutoFill,
1631
/// Repeat the track to fill available space but collapse any tracks that do not end up with
1632
/// an item placed in them.
1633
///
1634
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/repeat#auto-fit>
1635
AutoFit,
1636
}
1637
1638
impl Default for GridTrackRepetition {
1639
fn default() -> Self {
1640
Self::Count(1)
1641
}
1642
}
1643
1644
impl From<i32> for GridTrackRepetition {
1645
fn from(count: i32) -> Self {
1646
Self::Count(count as u16)
1647
}
1648
}
1649
1650
impl From<usize> for GridTrackRepetition {
1651
fn from(count: usize) -> Self {
1652
Self::Count(count as u16)
1653
}
1654
}
1655
1656
/// Represents a *possibly* repeated [`GridTrack`].
1657
///
1658
/// The repetition parameter can either be:
1659
/// - The integer `1`, in which case the track is non-repeated.
1660
/// - a `u16` count to repeat the track N times.
1661
/// - A `GridTrackRepetition::AutoFit` or `GridTrackRepetition::AutoFill`.
1662
///
1663
/// Note: that in the common case you want a non-repeating track (repetition count 1), you may use the constructor methods on [`GridTrack`]
1664
/// to create a `RepeatedGridTrack`. i.e. `GridTrack::px(10.0)` is equivalent to `RepeatedGridTrack::px(1, 10.0)`.
1665
///
1666
/// You may only use one auto-repetition per track list. And if your track list contains an auto repetition
1667
/// then all tracks (in and outside of the repetition) must be fixed size (px or percent). Integer repetitions are just shorthand for writing out
1668
/// N tracks longhand and are not subject to the same limitations.
1669
#[derive(Clone, PartialEq, Debug, Reflect)]
1670
#[reflect(Default, PartialEq, Clone)]
1671
#[cfg_attr(
1672
feature = "serialize",
1673
derive(serde::Serialize, serde::Deserialize),
1674
reflect(Serialize, Deserialize)
1675
)]
1676
pub struct RepeatedGridTrack {
1677
pub(crate) repetition: GridTrackRepetition,
1678
pub(crate) tracks: SmallVec<[GridTrack; 1]>,
1679
}
1680
1681
impl RepeatedGridTrack {
1682
/// Create a repeating set of grid tracks with a fixed pixel size
1683
pub fn px<T: From<Self>>(repetition: impl Into<GridTrackRepetition>, value: f32) -> T {
1684
Self {
1685
repetition: repetition.into(),
1686
tracks: SmallVec::from_buf([GridTrack::px(value)]),
1687
}
1688
.into()
1689
}
1690
1691
/// Create a repeating set of grid tracks with a percentage size
1692
pub fn percent<T: From<Self>>(repetition: impl Into<GridTrackRepetition>, value: f32) -> T {
1693
Self {
1694
repetition: repetition.into(),
1695
tracks: SmallVec::from_buf([GridTrack::percent(value)]),
1696
}
1697
.into()
1698
}
1699
1700
/// Create a repeating set of grid tracks with automatic size
1701
pub fn auto<T: From<Self>>(repetition: u16) -> T {
1702
Self {
1703
repetition: GridTrackRepetition::Count(repetition),
1704
tracks: SmallVec::from_buf([GridTrack::auto()]),
1705
}
1706
.into()
1707
}
1708
1709
/// Create a repeating set of grid tracks with an `fr` size.
1710
/// Note that this will give the track a content-based minimum size.
1711
/// Usually you are best off using `GridTrack::flex` instead which uses a zero minimum size.
1712
pub fn fr<T: From<Self>>(repetition: u16, value: f32) -> T {
1713
Self {
1714
repetition: GridTrackRepetition::Count(repetition),
1715
tracks: SmallVec::from_buf([GridTrack::fr(value)]),
1716
}
1717
.into()
1718
}
1719
1720
/// Create a repeating set of grid tracks with a `minmax(0, Nfr)` size.
1721
pub fn flex<T: From<Self>>(repetition: u16, value: f32) -> T {
1722
Self {
1723
repetition: GridTrackRepetition::Count(repetition),
1724
tracks: SmallVec::from_buf([GridTrack::flex(value)]),
1725
}
1726
.into()
1727
}
1728
1729
/// Create a repeating set of grid tracks with min-content size
1730
pub fn min_content<T: From<Self>>(repetition: u16) -> T {
1731
Self {
1732
repetition: GridTrackRepetition::Count(repetition),
1733
tracks: SmallVec::from_buf([GridTrack::min_content()]),
1734
}
1735
.into()
1736
}
1737
1738
/// Create a repeating set of grid tracks with max-content size
1739
pub fn max_content<T: From<Self>>(repetition: u16) -> T {
1740
Self {
1741
repetition: GridTrackRepetition::Count(repetition),
1742
tracks: SmallVec::from_buf([GridTrack::max_content()]),
1743
}
1744
.into()
1745
}
1746
1747
/// Create a repeating set of `fit-content()` grid tracks with fixed pixel limit
1748
pub fn fit_content_px<T: From<Self>>(repetition: u16, limit: f32) -> T {
1749
Self {
1750
repetition: GridTrackRepetition::Count(repetition),
1751
tracks: SmallVec::from_buf([GridTrack::fit_content_px(limit)]),
1752
}
1753
.into()
1754
}
1755
1756
/// Create a repeating set of `fit-content()` grid tracks with percentage limit
1757
pub fn fit_content_percent<T: From<Self>>(repetition: u16, limit: f32) -> T {
1758
Self {
1759
repetition: GridTrackRepetition::Count(repetition),
1760
tracks: SmallVec::from_buf([GridTrack::fit_content_percent(limit)]),
1761
}
1762
.into()
1763
}
1764
1765
/// Create a repeating set of `minmax()` grid track
1766
pub fn minmax<T: From<Self>>(
1767
repetition: impl Into<GridTrackRepetition>,
1768
min: MinTrackSizingFunction,
1769
max: MaxTrackSizingFunction,
1770
) -> T {
1771
Self {
1772
repetition: repetition.into(),
1773
tracks: SmallVec::from_buf([GridTrack::minmax(min, max)]),
1774
}
1775
.into()
1776
}
1777
1778
/// Create a repeating set of grid tracks with the percentage size of the viewport's smaller dimension
1779
pub fn vmin<T: From<Self>>(repetition: impl Into<GridTrackRepetition>, value: f32) -> T {
1780
Self {
1781
repetition: repetition.into(),
1782
tracks: SmallVec::from_buf([GridTrack::vmin(value)]),
1783
}
1784
.into()
1785
}
1786
1787
/// Create a repeating set of grid tracks with the percentage size of the viewport's larger dimension
1788
pub fn vmax<T: From<Self>>(repetition: impl Into<GridTrackRepetition>, value: f32) -> T {
1789
Self {
1790
repetition: repetition.into(),
1791
tracks: SmallVec::from_buf([GridTrack::vmax(value)]),
1792
}
1793
.into()
1794
}
1795
1796
/// Create a repeating set of grid tracks with the percentage size of the viewport's height dimension
1797
pub fn vh<T: From<Self>>(repetition: impl Into<GridTrackRepetition>, value: f32) -> T {
1798
Self {
1799
repetition: repetition.into(),
1800
tracks: SmallVec::from_buf([GridTrack::vh(value)]),
1801
}
1802
.into()
1803
}
1804
1805
/// Create a repeating set of grid tracks with the percentage size of the viewport's width dimension
1806
pub fn vw<T: From<Self>>(repetition: impl Into<GridTrackRepetition>, value: f32) -> T {
1807
Self {
1808
repetition: repetition.into(),
1809
tracks: SmallVec::from_buf([GridTrack::vw(value)]),
1810
}
1811
.into()
1812
}
1813
1814
/// Create a repetition of a set of tracks
1815
pub fn repeat_many<T: From<Self>>(
1816
repetition: impl Into<GridTrackRepetition>,
1817
tracks: impl Into<Vec<GridTrack>>,
1818
) -> T {
1819
Self {
1820
repetition: repetition.into(),
1821
tracks: SmallVec::from_vec(tracks.into()),
1822
}
1823
.into()
1824
}
1825
}
1826
1827
impl Default for RepeatedGridTrack {
1828
fn default() -> Self {
1829
Self {
1830
repetition: Default::default(),
1831
tracks: SmallVec::from_buf([GridTrack::default()]),
1832
}
1833
}
1834
}
1835
1836
impl From<GridTrack> for RepeatedGridTrack {
1837
fn from(track: GridTrack) -> Self {
1838
Self {
1839
repetition: GridTrackRepetition::Count(1),
1840
tracks: SmallVec::from_buf([track]),
1841
}
1842
}
1843
}
1844
1845
impl From<GridTrack> for Vec<GridTrack> {
1846
fn from(track: GridTrack) -> Self {
1847
vec![track]
1848
}
1849
}
1850
1851
impl From<GridTrack> for Vec<RepeatedGridTrack> {
1852
fn from(track: GridTrack) -> Self {
1853
vec![RepeatedGridTrack {
1854
repetition: GridTrackRepetition::Count(1),
1855
tracks: SmallVec::from_buf([track]),
1856
}]
1857
}
1858
}
1859
1860
impl From<RepeatedGridTrack> for Vec<RepeatedGridTrack> {
1861
fn from(track: RepeatedGridTrack) -> Self {
1862
vec![track]
1863
}
1864
}
1865
1866
#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
1867
#[reflect(Default, PartialEq, Clone)]
1868
#[cfg_attr(
1869
feature = "serialize",
1870
derive(serde::Serialize, serde::Deserialize),
1871
reflect(Serialize, Deserialize)
1872
)]
1873
/// Represents the position of a grid item in a single axis.
1874
///
1875
/// There are 3 fields which may be set:
1876
/// - `start`: which grid line the item should start at
1877
/// - `end`: which grid line the item should end at
1878
/// - `span`: how many tracks the item should span
1879
///
1880
/// The default `span` is 1. If neither `start` or `end` is set then the item will be placed automatically.
1881
///
1882
/// Generally, at most two fields should be set. If all three fields are specified then `span` will be ignored. If `end` specifies an earlier
1883
/// grid line than `start` then `end` will be ignored and the item will have a span of 1.
1884
///
1885
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Line-based_Placement_with_CSS_Grid>
1886
pub struct GridPlacement {
1887
/// The grid line at which the item should start.
1888
/// Lines are 1-indexed.
1889
/// Negative indexes count backwards from the end of the grid.
1890
/// Zero is not a valid index.
1891
pub(crate) start: Option<NonZero<i16>>,
1892
/// How many grid tracks the item should span.
1893
/// Defaults to 1.
1894
pub(crate) span: Option<NonZero<u16>>,
1895
/// The grid line at which the item should end.
1896
/// Lines are 1-indexed.
1897
/// Negative indexes count backwards from the end of the grid.
1898
/// Zero is not a valid index.
1899
pub(crate) end: Option<NonZero<i16>>,
1900
}
1901
1902
impl GridPlacement {
1903
pub const DEFAULT: Self = Self {
1904
start: None,
1905
span: NonZero::<u16>::new(1),
1906
end: None,
1907
};
1908
1909
/// Place the grid item automatically (letting the `span` default to `1`).
1910
pub fn auto() -> Self {
1911
Self::DEFAULT
1912
}
1913
1914
/// Place the grid item automatically, specifying how many tracks it should `span`.
1915
///
1916
/// # Panics
1917
///
1918
/// Panics if `span` is `0`.
1919
pub fn span(span: u16) -> Self {
1920
Self {
1921
start: None,
1922
end: None,
1923
span: try_into_grid_span(span).expect("Invalid span value of 0."),
1924
}
1925
}
1926
1927
/// Place the grid item specifying the `start` grid line (letting the `span` default to `1`).
1928
///
1929
/// # Panics
1930
///
1931
/// Panics if `start` is `0`.
1932
pub fn start(start: i16) -> Self {
1933
Self {
1934
start: try_into_grid_index(start).expect("Invalid start value of 0."),
1935
..Self::DEFAULT
1936
}
1937
}
1938
1939
/// Place the grid item specifying the `end` grid line (letting the `span` default to `1`).
1940
///
1941
/// # Panics
1942
///
1943
/// Panics if `end` is `0`.
1944
pub fn end(end: i16) -> Self {
1945
Self {
1946
end: try_into_grid_index(end).expect("Invalid end value of 0."),
1947
..Self::DEFAULT
1948
}
1949
}
1950
1951
/// Place the grid item specifying the `start` grid line and how many tracks it should `span`.
1952
///
1953
/// # Panics
1954
///
1955
/// Panics if `start` or `span` is `0`.
1956
pub fn start_span(start: i16, span: u16) -> Self {
1957
Self {
1958
start: try_into_grid_index(start).expect("Invalid start value of 0."),
1959
end: None,
1960
span: try_into_grid_span(span).expect("Invalid span value of 0."),
1961
}
1962
}
1963
1964
/// Place the grid item specifying `start` and `end` grid lines (`span` will be inferred)
1965
///
1966
/// # Panics
1967
///
1968
/// Panics if `start` or `end` is `0`.
1969
pub fn start_end(start: i16, end: i16) -> Self {
1970
Self {
1971
start: try_into_grid_index(start).expect("Invalid start value of 0."),
1972
end: try_into_grid_index(end).expect("Invalid end value of 0."),
1973
span: None,
1974
}
1975
}
1976
1977
/// Place the grid item specifying the `end` grid line and how many tracks it should `span`.
1978
///
1979
/// # Panics
1980
///
1981
/// Panics if `end` or `span` is `0`.
1982
pub fn end_span(end: i16, span: u16) -> Self {
1983
Self {
1984
start: None,
1985
end: try_into_grid_index(end).expect("Invalid end value of 0."),
1986
span: try_into_grid_span(span).expect("Invalid span value of 0."),
1987
}
1988
}
1989
1990
/// Mutate the item, setting the `start` grid line
1991
///
1992
/// # Panics
1993
///
1994
/// Panics if `start` is `0`.
1995
pub fn set_start(mut self, start: i16) -> Self {
1996
self.start = try_into_grid_index(start).expect("Invalid start value of 0.");
1997
self
1998
}
1999
2000
/// Mutate the item, setting the `end` grid line
2001
///
2002
/// # Panics
2003
///
2004
/// Panics if `end` is `0`.
2005
pub fn set_end(mut self, end: i16) -> Self {
2006
self.end = try_into_grid_index(end).expect("Invalid end value of 0.");
2007
self
2008
}
2009
2010
/// Mutate the item, setting the number of tracks the item should `span`
2011
///
2012
/// # Panics
2013
///
2014
/// Panics if `span` is `0`.
2015
pub fn set_span(mut self, span: u16) -> Self {
2016
self.span = try_into_grid_span(span).expect("Invalid span value of 0.");
2017
self
2018
}
2019
2020
/// Returns the grid line at which the item should start, or `None` if not set.
2021
pub fn get_start(self) -> Option<i16> {
2022
self.start.map(NonZero::<i16>::get)
2023
}
2024
2025
/// Returns the grid line at which the item should end, or `None` if not set.
2026
pub fn get_end(self) -> Option<i16> {
2027
self.end.map(NonZero::<i16>::get)
2028
}
2029
2030
/// Returns span for this grid item, or `None` if not set.
2031
pub fn get_span(self) -> Option<u16> {
2032
self.span.map(NonZero::<u16>::get)
2033
}
2034
}
2035
2036
impl Default for GridPlacement {
2037
fn default() -> Self {
2038
Self::DEFAULT
2039
}
2040
}
2041
2042
/// Convert an `i16` to `NonZero<i16>`, fails on `0` and returns the `InvalidZeroIndex` error.
2043
fn try_into_grid_index(index: i16) -> Result<Option<NonZero<i16>>, GridPlacementError> {
2044
Ok(Some(
2045
NonZero::<i16>::new(index).ok_or(GridPlacementError::InvalidZeroIndex)?,
2046
))
2047
}
2048
2049
/// Convert a `u16` to `NonZero<u16>`, fails on `0` and returns the `InvalidZeroSpan` error.
2050
fn try_into_grid_span(span: u16) -> Result<Option<NonZero<u16>>, GridPlacementError> {
2051
Ok(Some(
2052
NonZero::<u16>::new(span).ok_or(GridPlacementError::InvalidZeroSpan)?,
2053
))
2054
}
2055
2056
/// Errors that occur when setting constraints for a `GridPlacement`
2057
#[derive(Debug, Eq, PartialEq, Clone, Copy, Error)]
2058
pub enum GridPlacementError {
2059
#[error("Zero is not a valid grid position")]
2060
InvalidZeroIndex,
2061
#[error("Spans cannot be zero length")]
2062
InvalidZeroSpan,
2063
}
2064
2065
/// The background color of the node
2066
///
2067
/// This serves as the "fill" color.
2068
#[derive(Component, Copy, Clone, Debug, PartialEq, Reflect)]
2069
#[reflect(Component, Default, Debug, PartialEq, Clone)]
2070
#[cfg_attr(
2071
feature = "serialize",
2072
derive(serde::Serialize, serde::Deserialize),
2073
reflect(Serialize, Deserialize)
2074
)]
2075
pub struct BackgroundColor(pub Color);
2076
2077
impl BackgroundColor {
2078
/// Background color is transparent by default.
2079
pub const DEFAULT: Self = Self(Color::NONE);
2080
}
2081
2082
impl Default for BackgroundColor {
2083
fn default() -> Self {
2084
Self::DEFAULT
2085
}
2086
}
2087
2088
impl<T: Into<Color>> From<T> for BackgroundColor {
2089
fn from(color: T) -> Self {
2090
Self(color.into())
2091
}
2092
}
2093
2094
/// The border color of the UI node.
2095
#[derive(Component, Copy, Clone, Debug, PartialEq, Reflect)]
2096
#[reflect(Component, Default, Debug, PartialEq, Clone)]
2097
#[cfg_attr(
2098
feature = "serialize",
2099
derive(serde::Serialize, serde::Deserialize),
2100
reflect(Serialize, Deserialize)
2101
)]
2102
pub struct BorderColor {
2103
pub top: Color,
2104
pub right: Color,
2105
pub bottom: Color,
2106
pub left: Color,
2107
}
2108
2109
impl<T: Into<Color>> From<T> for BorderColor {
2110
fn from(color: T) -> Self {
2111
Self::all(color.into())
2112
}
2113
}
2114
2115
impl BorderColor {
2116
/// Border color is transparent by default.
2117
pub const DEFAULT: Self = BorderColor {
2118
top: Color::NONE,
2119
right: Color::NONE,
2120
bottom: Color::NONE,
2121
left: Color::NONE,
2122
};
2123
2124
/// Helper to create a `BorderColor` struct with all borders set to the given color
2125
#[inline]
2126
pub fn all(color: impl Into<Color>) -> Self {
2127
let color = color.into();
2128
Self {
2129
top: color,
2130
bottom: color,
2131
left: color,
2132
right: color,
2133
}
2134
}
2135
2136
/// Helper to set all border colors to a given color.
2137
pub fn set_all(&mut self, color: impl Into<Color>) -> &mut Self {
2138
let color: Color = color.into();
2139
self.top = color;
2140
self.bottom = color;
2141
self.left = color;
2142
self.right = color;
2143
self
2144
}
2145
2146
/// Check if all contained border colors are transparent
2147
pub fn is_fully_transparent(&self) -> bool {
2148
self.top.is_fully_transparent()
2149
&& self.bottom.is_fully_transparent()
2150
&& self.left.is_fully_transparent()
2151
&& self.right.is_fully_transparent()
2152
}
2153
}
2154
2155
impl Default for BorderColor {
2156
fn default() -> Self {
2157
Self::DEFAULT
2158
}
2159
}
2160
2161
#[derive(Component, Copy, Clone, Default, Debug, PartialEq, Reflect)]
2162
#[reflect(Component, Default, Debug, PartialEq, Clone)]
2163
#[cfg_attr(
2164
feature = "serialize",
2165
derive(serde::Serialize, serde::Deserialize),
2166
reflect(Serialize, Deserialize)
2167
)]
2168
/// The [`Outline`] component adds an outline outside the edge of a UI node.
2169
/// Outlines do not take up space in the layout.
2170
///
2171
/// To add an [`Outline`] to a ui node you can spawn a `(Node, Outline)` tuple bundle:
2172
/// ```
2173
/// # use bevy_ecs::prelude::*;
2174
/// # use bevy_ui::prelude::*;
2175
/// # use bevy_color::palettes::basic::{RED, BLUE};
2176
/// fn setup_ui(mut commands: Commands) {
2177
/// commands.spawn((
2178
/// Node {
2179
/// width: Val::Px(100.),
2180
/// height: Val::Px(100.),
2181
/// ..Default::default()
2182
/// },
2183
/// BackgroundColor(BLUE.into()),
2184
/// Outline::new(Val::Px(10.), Val::ZERO, RED.into())
2185
/// ));
2186
/// }
2187
/// ```
2188
///
2189
/// [`Outline`] components can also be added later to existing UI nodes:
2190
/// ```
2191
/// # use bevy_ecs::prelude::*;
2192
/// # use bevy_ui::prelude::*;
2193
/// # use bevy_color::Color;
2194
/// fn outline_hovered_button_system(
2195
/// mut commands: Commands,
2196
/// mut node_query: Query<(Entity, &Interaction, Option<&mut Outline>), Changed<Interaction>>,
2197
/// ) {
2198
/// for (entity, interaction, mut maybe_outline) in node_query.iter_mut() {
2199
/// let outline_color =
2200
/// if matches!(*interaction, Interaction::Hovered) {
2201
/// Color::WHITE
2202
/// } else {
2203
/// Color::NONE
2204
/// };
2205
/// if let Some(mut outline) = maybe_outline {
2206
/// outline.color = outline_color;
2207
/// } else {
2208
/// commands.entity(entity).insert(Outline::new(Val::Px(10.), Val::ZERO, outline_color));
2209
/// }
2210
/// }
2211
/// }
2212
/// ```
2213
/// Inserting and removing an [`Outline`] component repeatedly will result in table moves, so it is generally preferable to
2214
/// set `Outline::color` to [`Color::NONE`] to hide an outline.
2215
pub struct Outline {
2216
/// The width of the outline.
2217
///
2218
/// Percentage `Val` values are resolved based on the width of the outlined [`Node`].
2219
pub width: Val,
2220
/// The amount of space between a node's outline the edge of the node.
2221
///
2222
/// Percentage `Val` values are resolved based on the width of the outlined [`Node`].
2223
pub offset: Val,
2224
/// The color of the outline.
2225
///
2226
/// If you are frequently toggling outlines for a UI node on and off it is recommended to set [`Color::NONE`] to hide the outline.
2227
/// This avoids the table moves that would occur from the repeated insertion and removal of the `Outline` component.
2228
pub color: Color,
2229
}
2230
2231
impl Outline {
2232
/// Create a new outline
2233
pub const fn new(width: Val, offset: Val, color: Color) -> Self {
2234
Self {
2235
width,
2236
offset,
2237
color,
2238
}
2239
}
2240
}
2241
2242
/// The calculated clip of the node
2243
#[derive(Component, Default, Copy, Clone, Debug, Reflect)]
2244
#[reflect(Component, Default, Debug, Clone)]
2245
pub struct CalculatedClip {
2246
/// The rect of the clip
2247
pub clip: Rect,
2248
}
2249
2250
/// UI node entities with this component will ignore any clipping rect they inherit,
2251
/// the node will not be clipped regardless of its ancestors' `Overflow` setting.
2252
#[derive(Component)]
2253
pub struct OverrideClip;
2254
2255
#[expect(
2256
rustdoc::redundant_explicit_links,
2257
reason = "To go around the `<code>` limitations, we put the link twice so we're \
2258
sure it's recognized as a markdown link."
2259
)]
2260
/// Indicates that this [`Node`] entity's front-to-back ordering is not controlled solely
2261
/// by its location in the UI hierarchy. A node with a higher z-index will appear on top
2262
/// of sibling nodes with a lower z-index.
2263
///
2264
/// UI nodes that have the same z-index will appear according to the order in which they
2265
/// appear in the UI hierarchy. In such a case, the last node to be added to its parent
2266
/// will appear in front of its siblings.
2267
///
2268
/// Nodes without this component will be treated as if they had a value of
2269
/// <code>[ZIndex][ZIndex]\(0\)</code>.
2270
///
2271
/// Use [`GlobalZIndex`] if you need to order separate UI hierarchies or nodes that are
2272
/// not siblings in a given UI hierarchy.
2273
#[derive(Component, Copy, Clone, Debug, Default, PartialEq, Eq, Reflect)]
2274
#[reflect(Component, Default, Debug, PartialEq, Clone)]
2275
pub struct ZIndex(pub i32);
2276
2277
/// `GlobalZIndex` allows a [`Node`] entity anywhere in the UI hierarchy to escape the implicit draw ordering of the UI's layout tree and
2278
/// be rendered above or below other UI nodes.
2279
/// Nodes with a `GlobalZIndex` of greater than 0 will be drawn on top of nodes without a `GlobalZIndex` or nodes with a lower `GlobalZIndex`.
2280
/// Nodes with a `GlobalZIndex` of less than 0 will be drawn below nodes without a `GlobalZIndex` or nodes with a greater `GlobalZIndex`.
2281
///
2282
/// If two Nodes have the same `GlobalZIndex`, the node with the greater [`ZIndex`] will be drawn on top.
2283
#[derive(Component, Copy, Clone, Debug, Default, PartialEq, Eq, Reflect)]
2284
#[reflect(Component, Default, Debug, PartialEq, Clone)]
2285
pub struct GlobalZIndex(pub i32);
2286
2287
/// Used to add rounded corners to a UI node. You can set a UI node to have uniformly
2288
/// rounded corners or specify different radii for each corner. If a given radius exceeds half
2289
/// the length of the smallest dimension between the node's height or width, the radius will
2290
/// calculated as half the smallest dimension.
2291
///
2292
/// Elliptical nodes are not supported yet. Percentage values are based on the node's smallest
2293
/// dimension, either width or height.
2294
///
2295
/// # Example
2296
/// ```rust
2297
/// # use bevy_ecs::prelude::*;
2298
/// # use bevy_ui::prelude::*;
2299
/// # use bevy_color::palettes::basic::{BLUE};
2300
/// fn setup_ui(mut commands: Commands) {
2301
/// commands.spawn((
2302
/// Node {
2303
/// width: Val::Px(100.),
2304
/// height: Val::Px(100.),
2305
/// border: UiRect::all(Val::Px(2.)),
2306
/// ..Default::default()
2307
/// },
2308
/// BackgroundColor(BLUE.into()),
2309
/// BorderRadius::new(
2310
/// // top left
2311
/// Val::Px(10.),
2312
/// // top right
2313
/// Val::Px(20.),
2314
/// // bottom right
2315
/// Val::Px(30.),
2316
/// // bottom left
2317
/// Val::Px(40.),
2318
/// ),
2319
/// ));
2320
/// }
2321
/// ```
2322
///
2323
/// <https://developer.mozilla.org/en-US/docs/Web/CSS/border-radius>
2324
#[derive(Component, Copy, Clone, Debug, PartialEq, Reflect)]
2325
#[reflect(Component, PartialEq, Default, Debug, Clone)]
2326
#[cfg_attr(
2327
feature = "serialize",
2328
derive(serde::Serialize, serde::Deserialize),
2329
reflect(Serialize, Deserialize)
2330
)]
2331
pub struct BorderRadius {
2332
pub top_left: Val,
2333
pub top_right: Val,
2334
pub bottom_right: Val,
2335
pub bottom_left: Val,
2336
}
2337
2338
impl Default for BorderRadius {
2339
fn default() -> Self {
2340
Self::DEFAULT
2341
}
2342
}
2343
2344
impl BorderRadius {
2345
pub const DEFAULT: Self = Self::ZERO;
2346
2347
/// Zero curvature. All the corners will be right-angled.
2348
pub const ZERO: Self = Self::all(Val::Px(0.));
2349
2350
/// Maximum curvature. The UI Node will take a capsule shape or circular if width and height are equal.
2351
pub const MAX: Self = Self::all(Val::Px(f32::MAX));
2352
2353
#[inline]
2354
/// Set all four corners to the same curvature.
2355
pub const fn all(radius: Val) -> Self {
2356
Self {
2357
top_left: radius,
2358
top_right: radius,
2359
bottom_left: radius,
2360
bottom_right: radius,
2361
}
2362
}
2363
2364
#[inline]
2365
pub const fn new(top_left: Val, top_right: Val, bottom_right: Val, bottom_left: Val) -> Self {
2366
Self {
2367
top_left,
2368
top_right,
2369
bottom_right,
2370
bottom_left,
2371
}
2372
}
2373
2374
#[inline]
2375
/// Sets the radii to logical pixel values.
2376
pub const fn px(top_left: f32, top_right: f32, bottom_right: f32, bottom_left: f32) -> Self {
2377
Self {
2378
top_left: Val::Px(top_left),
2379
top_right: Val::Px(top_right),
2380
bottom_right: Val::Px(bottom_right),
2381
bottom_left: Val::Px(bottom_left),
2382
}
2383
}
2384
2385
#[inline]
2386
/// Sets the radii to percentage values.
2387
pub const fn percent(
2388
top_left: f32,
2389
top_right: f32,
2390
bottom_right: f32,
2391
bottom_left: f32,
2392
) -> Self {
2393
Self {
2394
top_left: Val::Percent(top_left),
2395
top_right: Val::Percent(top_right),
2396
bottom_right: Val::Percent(bottom_right),
2397
bottom_left: Val::Percent(bottom_left),
2398
}
2399
}
2400
2401
#[inline]
2402
/// Sets the radius for the top left corner.
2403
/// Remaining corners will be right-angled.
2404
pub const fn top_left(radius: Val) -> Self {
2405
Self {
2406
top_left: radius,
2407
..Self::DEFAULT
2408
}
2409
}
2410
2411
#[inline]
2412
/// Sets the radius for the top right corner.
2413
/// Remaining corners will be right-angled.
2414
pub const fn top_right(radius: Val) -> Self {
2415
Self {
2416
top_right: radius,
2417
..Self::DEFAULT
2418
}
2419
}
2420
2421
#[inline]
2422
/// Sets the radius for the bottom right corner.
2423
/// Remaining corners will be right-angled.
2424
pub const fn bottom_right(radius: Val) -> Self {
2425
Self {
2426
bottom_right: radius,
2427
..Self::DEFAULT
2428
}
2429
}
2430
2431
#[inline]
2432
/// Sets the radius for the bottom left corner.
2433
/// Remaining corners will be right-angled.
2434
pub const fn bottom_left(radius: Val) -> Self {
2435
Self {
2436
bottom_left: radius,
2437
..Self::DEFAULT
2438
}
2439
}
2440
2441
#[inline]
2442
/// Sets the radii for the top left and bottom left corners.
2443
/// Remaining corners will be right-angled.
2444
pub const fn left(radius: Val) -> Self {
2445
Self {
2446
top_left: radius,
2447
bottom_left: radius,
2448
..Self::DEFAULT
2449
}
2450
}
2451
2452
#[inline]
2453
/// Sets the radii for the top right and bottom right corners.
2454
/// Remaining corners will be right-angled.
2455
pub const fn right(radius: Val) -> Self {
2456
Self {
2457
top_right: radius,
2458
bottom_right: radius,
2459
..Self::DEFAULT
2460
}
2461
}
2462
2463
#[inline]
2464
/// Sets the radii for the top left and top right corners.
2465
/// Remaining corners will be right-angled.
2466
pub const fn top(radius: Val) -> Self {
2467
Self {
2468
top_left: radius,
2469
top_right: radius,
2470
..Self::DEFAULT
2471
}
2472
}
2473
2474
#[inline]
2475
/// Sets the radii for the bottom left and bottom right corners.
2476
/// Remaining corners will be right-angled.
2477
pub const fn bottom(radius: Val) -> Self {
2478
Self {
2479
bottom_left: radius,
2480
bottom_right: radius,
2481
..Self::DEFAULT
2482
}
2483
}
2484
2485
/// Returns the [`BorderRadius`] with its `top_left` field set to the given value.
2486
#[inline]
2487
pub const fn with_top_left(mut self, radius: Val) -> Self {
2488
self.top_left = radius;
2489
self
2490
}
2491
2492
/// Returns the [`BorderRadius`] with its `top_right` field set to the given value.
2493
#[inline]
2494
pub const fn with_top_right(mut self, radius: Val) -> Self {
2495
self.top_right = radius;
2496
self
2497
}
2498
2499
/// Returns the [`BorderRadius`] with its `bottom_right` field set to the given value.
2500
#[inline]
2501
pub const fn with_bottom_right(mut self, radius: Val) -> Self {
2502
self.bottom_right = radius;
2503
self
2504
}
2505
2506
/// Returns the [`BorderRadius`] with its `bottom_left` field set to the given value.
2507
#[inline]
2508
pub const fn with_bottom_left(mut self, radius: Val) -> Self {
2509
self.bottom_left = radius;
2510
self
2511
}
2512
2513
/// Returns the [`BorderRadius`] with its `top_left` and `bottom_left` fields set to the given value.
2514
#[inline]
2515
pub const fn with_left(mut self, radius: Val) -> Self {
2516
self.top_left = radius;
2517
self.bottom_left = radius;
2518
self
2519
}
2520
2521
/// Returns the [`BorderRadius`] with its `top_right` and `bottom_right` fields set to the given value.
2522
#[inline]
2523
pub const fn with_right(mut self, radius: Val) -> Self {
2524
self.top_right = radius;
2525
self.bottom_right = radius;
2526
self
2527
}
2528
2529
/// Returns the [`BorderRadius`] with its `top_left` and `top_right` fields set to the given value.
2530
#[inline]
2531
pub const fn with_top(mut self, radius: Val) -> Self {
2532
self.top_left = radius;
2533
self.top_right = radius;
2534
self
2535
}
2536
2537
/// Returns the [`BorderRadius`] with its `bottom_left` and `bottom_right` fields set to the given value.
2538
#[inline]
2539
pub const fn with_bottom(mut self, radius: Val) -> Self {
2540
self.bottom_left = radius;
2541
self.bottom_right = radius;
2542
self
2543
}
2544
2545
/// Resolve the border radius for a single corner from the given context values.
2546
/// Returns the radius of the corner in physical pixels.
2547
pub const fn resolve_single_corner(
2548
radius: Val,
2549
scale_factor: f32,
2550
min_length: f32,
2551
viewport_size: Vec2,
2552
) -> f32 {
2553
if let Ok(radius) = radius.resolve(scale_factor, min_length, viewport_size) {
2554
radius.clamp(0., 0.5 * min_length)
2555
} else {
2556
0.
2557
}
2558
}
2559
2560
/// Resolve the border radii for the corners from the given context values.
2561
/// Returns the radii of the each corner in physical pixels.
2562
pub const fn resolve(
2563
&self,
2564
scale_factor: f32,
2565
node_size: Vec2,
2566
viewport_size: Vec2,
2567
) -> ResolvedBorderRadius {
2568
let length = node_size.x.min(node_size.y);
2569
ResolvedBorderRadius {
2570
top_left: Self::resolve_single_corner(
2571
self.top_left,
2572
scale_factor,
2573
length,
2574
viewport_size,
2575
),
2576
top_right: Self::resolve_single_corner(
2577
self.top_right,
2578
scale_factor,
2579
length,
2580
viewport_size,
2581
),
2582
bottom_left: Self::resolve_single_corner(
2583
self.bottom_left,
2584
scale_factor,
2585
length,
2586
viewport_size,
2587
),
2588
bottom_right: Self::resolve_single_corner(
2589
self.bottom_right,
2590
scale_factor,
2591
length,
2592
viewport_size,
2593
),
2594
}
2595
}
2596
}
2597
2598
/// Represents the resolved border radius values for a UI node.
2599
///
2600
/// The values are in physical pixels.
2601
#[derive(Copy, Clone, Debug, Default, PartialEq, Reflect)]
2602
#[reflect(Clone, PartialEq, Default)]
2603
pub struct ResolvedBorderRadius {
2604
pub top_left: f32,
2605
pub top_right: f32,
2606
pub bottom_right: f32,
2607
pub bottom_left: f32,
2608
}
2609
2610
impl ResolvedBorderRadius {
2611
pub const ZERO: Self = Self {
2612
top_left: 0.,
2613
top_right: 0.,
2614
bottom_right: 0.,
2615
bottom_left: 0.,
2616
};
2617
}
2618
2619
impl From<ResolvedBorderRadius> for [f32; 4] {
2620
fn from(radius: ResolvedBorderRadius) -> Self {
2621
[
2622
radius.top_left,
2623
radius.top_right,
2624
radius.bottom_right,
2625
radius.bottom_left,
2626
]
2627
}
2628
}
2629
2630
#[derive(Component, Clone, Debug, Default, PartialEq, Reflect, Deref, DerefMut)]
2631
#[reflect(Component, PartialEq, Default, Clone)]
2632
#[cfg_attr(
2633
feature = "serialize",
2634
derive(serde::Serialize, serde::Deserialize),
2635
reflect(Serialize, Deserialize)
2636
)]
2637
/// List of shadows to draw for a [`Node`].
2638
///
2639
/// Draw order is determined implicitly from the vector of [`ShadowStyle`]s, back-to-front.
2640
pub struct BoxShadow(pub Vec<ShadowStyle>);
2641
2642
impl BoxShadow {
2643
/// A single drop shadow
2644
pub fn new(
2645
color: Color,
2646
x_offset: Val,
2647
y_offset: Val,
2648
spread_radius: Val,
2649
blur_radius: Val,
2650
) -> Self {
2651
Self(vec![ShadowStyle {
2652
color,
2653
x_offset,
2654
y_offset,
2655
spread_radius,
2656
blur_radius,
2657
}])
2658
}
2659
}
2660
2661
impl From<ShadowStyle> for BoxShadow {
2662
fn from(value: ShadowStyle) -> Self {
2663
Self(vec![value])
2664
}
2665
}
2666
2667
#[derive(Copy, Clone, Debug, PartialEq, Reflect)]
2668
#[reflect(PartialEq, Default, Clone)]
2669
#[cfg_attr(
2670
feature = "serialize",
2671
derive(serde::Serialize, serde::Deserialize),
2672
reflect(Serialize, Deserialize)
2673
)]
2674
pub struct ShadowStyle {
2675
/// The shadow's color
2676
pub color: Color,
2677
/// Horizontal offset
2678
pub x_offset: Val,
2679
/// Vertical offset
2680
pub y_offset: Val,
2681
/// How much the shadow should spread outward.
2682
///
2683
/// Negative values will make the shadow shrink inwards.
2684
/// Percentage values are based on the width of the UI node.
2685
pub spread_radius: Val,
2686
/// Blurriness of the shadow
2687
pub blur_radius: Val,
2688
}
2689
2690
impl Default for ShadowStyle {
2691
fn default() -> Self {
2692
Self {
2693
color: Color::BLACK,
2694
x_offset: Val::Percent(20.),
2695
y_offset: Val::Percent(20.),
2696
spread_radius: Val::ZERO,
2697
blur_radius: Val::Percent(10.),
2698
}
2699
}
2700
}
2701
2702
#[derive(Component, Copy, Clone, Debug, PartialEq, Reflect)]
2703
#[reflect(Component, Debug, PartialEq, Default, Clone)]
2704
#[cfg_attr(
2705
feature = "serialize",
2706
derive(serde::Serialize, serde::Deserialize),
2707
reflect(Serialize, Deserialize)
2708
)]
2709
/// This component can be added to any UI node to modify its layout behavior.
2710
pub struct LayoutConfig {
2711
/// If set to true the coordinates for this node and its descendents will be rounded to the nearest physical pixel.
2712
/// This can help prevent visual artifacts like blurry images or semi-transparent edges that can occur with sub-pixel positioning.
2713
///
2714
/// Defaults to true.
2715
pub use_rounding: bool,
2716
}
2717
2718
impl Default for LayoutConfig {
2719
fn default() -> Self {
2720
Self { use_rounding: true }
2721
}
2722
}
2723
2724
/// Indicates that this root [`Node`] entity should be rendered to a specific camera.
2725
///
2726
/// UI then will be laid out respecting the camera's viewport and scale factor, and
2727
/// rendered to this camera's [`bevy_camera::RenderTarget`].
2728
///
2729
/// Setting this component on a non-root node will have no effect. It will be overridden
2730
/// by the root node's component.
2731
///
2732
/// Root node's without an explicit [`UiTargetCamera`] will be rendered to the default UI camera,
2733
/// which is either a single camera with the [`IsDefaultUiCamera`] marker component or the highest
2734
/// order camera targeting the primary window.
2735
#[derive(Component, Clone, Debug, Reflect, Eq, PartialEq)]
2736
#[reflect(Component, Debug, PartialEq, Clone)]
2737
pub struct UiTargetCamera(pub Entity);
2738
2739
impl UiTargetCamera {
2740
pub fn entity(&self) -> Entity {
2741
self.0
2742
}
2743
}
2744
2745
/// Marker used to identify default cameras, they will have priority over the [`PrimaryWindow`] camera.
2746
///
2747
/// This is useful if the [`PrimaryWindow`] has two cameras, one of them used
2748
/// just for debug purposes and the user wants a way to choose the default [`Camera`]
2749
/// without having to add a [`UiTargetCamera`] to the root node.
2750
///
2751
/// Another use is when the user wants the Ui to be in another window by default,
2752
/// all that is needed is to place this component on the camera
2753
///
2754
/// ```
2755
/// # use bevy_ui::prelude::*;
2756
/// # use bevy_ecs::prelude::Commands;
2757
/// # use bevy_camera::{Camera, Camera2d, RenderTarget};
2758
/// # use bevy_window::{Window, WindowRef};
2759
///
2760
/// fn spawn_camera(mut commands: Commands) {
2761
/// let another_window = commands.spawn(Window {
2762
/// title: String::from("Another window"),
2763
/// ..Default::default()
2764
/// }).id();
2765
/// commands.spawn((
2766
/// Camera2d,
2767
/// Camera {
2768
/// target: RenderTarget::Window(WindowRef::Entity(another_window)),
2769
/// ..Default::default()
2770
/// },
2771
/// // We add the Marker here so all Ui will spawn in
2772
/// // another window if no UiTargetCamera is specified
2773
/// IsDefaultUiCamera
2774
/// ));
2775
/// }
2776
/// ```
2777
#[derive(Component, Default)]
2778
pub struct IsDefaultUiCamera;
2779
2780
#[derive(SystemParam)]
2781
pub struct DefaultUiCamera<'w, 's> {
2782
cameras: Query<'w, 's, (Entity, &'static Camera)>,
2783
default_cameras: Query<'w, 's, Entity, (With<Camera>, With<IsDefaultUiCamera>)>,
2784
primary_window: Query<'w, 's, Entity, With<PrimaryWindow>>,
2785
}
2786
2787
impl<'w, 's> DefaultUiCamera<'w, 's> {
2788
pub fn get(&self) -> Option<Entity> {
2789
self.default_cameras.single().ok().or_else(|| {
2790
// If there isn't a single camera and the query isn't empty, there is two or more cameras queried.
2791
if !self.default_cameras.is_empty() {
2792
once!(warn!("Two or more Entities with IsDefaultUiCamera found when only one Camera with this marker is allowed."));
2793
}
2794
self.cameras
2795
.iter()
2796
.filter(|(_, c)| match c.target {
2797
RenderTarget::Window(WindowRef::Primary) => true,
2798
RenderTarget::Window(WindowRef::Entity(w)) => {
2799
self.primary_window.get(w).is_ok()
2800
}
2801
_ => false,
2802
})
2803
.max_by_key(|(e, c)| (c.order, *e))
2804
.map(|(e, _)| e)
2805
})
2806
}
2807
}
2808
2809
/// Derived information about the camera target for this UI node.
2810
///
2811
/// Updated in [`UiSystems::Prepare`](crate::UiSystems::Prepare) by [`propagate_ui_target_cameras`](crate::update::propagate_ui_target_cameras)
2812
#[derive(Component, Clone, Copy, Debug, Reflect, PartialEq)]
2813
#[reflect(Component, Default, PartialEq, Clone)]
2814
pub struct ComputedUiTargetCamera {
2815
pub(crate) camera: Entity,
2816
}
2817
2818
impl Default for ComputedUiTargetCamera {
2819
fn default() -> Self {
2820
Self {
2821
camera: Entity::PLACEHOLDER,
2822
}
2823
}
2824
}
2825
2826
impl ComputedUiTargetCamera {
2827
/// Returns the id of the target camera for this UI node.
2828
pub fn get(&self) -> Option<Entity> {
2829
Some(self.camera).filter(|&entity| entity != Entity::PLACEHOLDER)
2830
}
2831
}
2832
2833
/// Derived information about the render target for this UI node.
2834
#[derive(Component, Clone, Copy, Debug, Reflect, PartialEq)]
2835
#[reflect(Component, Default, PartialEq, Clone)]
2836
pub struct ComputedUiRenderTargetInfo {
2837
/// The scale factor of the target camera's render target.
2838
pub(crate) scale_factor: f32,
2839
/// The size of the target camera's viewport in physical pixels.
2840
pub(crate) physical_size: UVec2,
2841
}
2842
2843
impl Default for ComputedUiRenderTargetInfo {
2844
fn default() -> Self {
2845
Self {
2846
scale_factor: 1.,
2847
physical_size: UVec2::ZERO,
2848
}
2849
}
2850
}
2851
2852
impl ComputedUiRenderTargetInfo {
2853
pub const fn scale_factor(&self) -> f32 {
2854
self.scale_factor
2855
}
2856
2857
/// Returns the size of the target camera's viewport in physical pixels.
2858
pub const fn physical_size(&self) -> UVec2 {
2859
self.physical_size
2860
}
2861
2862
/// Returns the size of the target camera's viewport in logical pixels.
2863
pub fn logical_size(&self) -> Vec2 {
2864
self.physical_size.as_vec2() / self.scale_factor
2865
}
2866
}
2867
2868
#[cfg(test)]
2869
mod tests {
2870
use crate::GridPlacement;
2871
2872
#[test]
2873
fn invalid_grid_placement_values() {
2874
assert!(std::panic::catch_unwind(|| GridPlacement::span(0)).is_err());
2875
assert!(std::panic::catch_unwind(|| GridPlacement::start(0)).is_err());
2876
assert!(std::panic::catch_unwind(|| GridPlacement::end(0)).is_err());
2877
assert!(std::panic::catch_unwind(|| GridPlacement::start_end(0, 1)).is_err());
2878
assert!(std::panic::catch_unwind(|| GridPlacement::start_end(-1, 0)).is_err());
2879
assert!(std::panic::catch_unwind(|| GridPlacement::start_span(1, 0)).is_err());
2880
assert!(std::panic::catch_unwind(|| GridPlacement::start_span(0, 1)).is_err());
2881
assert!(std::panic::catch_unwind(|| GridPlacement::end_span(0, 1)).is_err());
2882
assert!(std::panic::catch_unwind(|| GridPlacement::end_span(1, 0)).is_err());
2883
assert!(std::panic::catch_unwind(|| GridPlacement::default().set_start(0)).is_err());
2884
assert!(std::panic::catch_unwind(|| GridPlacement::default().set_end(0)).is_err());
2885
assert!(std::panic::catch_unwind(|| GridPlacement::default().set_span(0)).is_err());
2886
}
2887
2888
#[test]
2889
fn grid_placement_accessors() {
2890
assert_eq!(GridPlacement::start(5).get_start(), Some(5));
2891
assert_eq!(GridPlacement::end(-4).get_end(), Some(-4));
2892
assert_eq!(GridPlacement::span(2).get_span(), Some(2));
2893
assert_eq!(GridPlacement::start_end(11, 21).get_span(), None);
2894
assert_eq!(GridPlacement::start_span(3, 5).get_end(), None);
2895
assert_eq!(GridPlacement::end_span(-4, 12).get_start(), None);
2896
}
2897
}
2898
2899