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