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