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