Skip to main content

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