use crate::{1ui_transform::{UiGlobalTransform, UiTransform},2FocusPolicy, UiRect, Val,3};4use bevy_camera::{visibility::Visibility, Camera, RenderTarget};5use bevy_color::{Alpha, Color};6use bevy_derive::{Deref, DerefMut};7use bevy_ecs::{prelude::*, system::SystemParam};8use bevy_math::{BVec2, Rect, UVec2, Vec2, Vec4, Vec4Swizzles};9use bevy_reflect::prelude::*;10use bevy_sprite::BorderRect;11use bevy_utils::once;12use bevy_window::{PrimaryWindow, WindowRef};13use core::{f32, num::NonZero};14use derive_more::derive::From;15use smallvec::SmallVec;16use thiserror::Error;17use tracing::warn;1819/// Provides the computed size and layout properties of the node.20///21/// Fields in this struct are public but should not be modified under most circumstances.22/// For example, in a scrollbar you may want to derive the handle's size from the proportion of23/// scrollable content in-view. You can directly modify `ComputedNode` after layout to set the24/// handle size without any delays.25#[derive(Component, Debug, Copy, Clone, PartialEq, Reflect)]26#[reflect(Component, Default, Debug, Clone)]27pub struct ComputedNode {28/// The order of the node in the UI layout.29/// Nodes with a higher stack index are drawn on top of and receive interactions before nodes with lower stack indices.30///31/// Automatically calculated in [`UiSystems::Stack`](`super::UiSystems::Stack`).32pub stack_index: u32,33/// The size of the node as width and height in physical pixels.34///35/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).36pub size: Vec2,37/// Size of this node's content.38///39/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).40pub content_size: Vec2,41/// Space allocated for scrollbars.42///43/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).44pub scrollbar_size: Vec2,45/// Resolved offset of scrolled content46///47/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).48pub scroll_position: Vec2,49/// The width of this node's outline.50/// If this value is `Auto`, negative or `0.` then no outline will be rendered.51/// Outline updates bypass change detection.52///53/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).54pub outline_width: f32,55/// The amount of space between the outline and the edge of the node.56/// Outline updates bypass change detection.57///58/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).59pub outline_offset: f32,60/// The unrounded size of the node as width and height in physical pixels.61///62/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).63pub unrounded_size: Vec2,64/// Resolved border values in physical pixels.65/// Border updates bypass change detection.66///67/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).68pub border: BorderRect,69/// Resolved border radius values in physical pixels.70/// Border radius updates bypass change detection.71///72/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).73pub border_radius: ResolvedBorderRadius,74/// Resolved padding values in physical pixels.75/// Padding updates bypass change detection.76///77/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).78pub padding: BorderRect,79/// Inverse scale factor for this Node.80/// Multiply physical coordinates by the inverse scale factor to give logical coordinates.81///82/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).83pub inverse_scale_factor: f32,84}8586impl ComputedNode {87/// The calculated node size as width and height in physical pixels.88///89/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).90#[inline]91pub const fn size(&self) -> Vec2 {92self.size93}9495/// The calculated node content size as width and height in physical pixels.96///97/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).98#[inline]99pub const fn content_size(&self) -> Vec2 {100self.content_size101}102103/// Check if the node is empty.104/// A node is considered empty if it has a zero or negative extent along either of its axes.105#[inline]106pub const fn is_empty(&self) -> bool {107self.size.x <= 0. || self.size.y <= 0.108}109110/// The order of the node in the UI layout.111/// Nodes with a higher stack index are drawn on top of and receive interactions before nodes with lower stack indices.112///113/// Automatically calculated in [`UiSystems::Stack`](super::UiSystems::Stack).114pub const fn stack_index(&self) -> u32 {115self.stack_index116}117118/// The calculated node size as width and height in physical pixels before rounding.119///120/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).121#[inline]122pub const fn unrounded_size(&self) -> Vec2 {123self.unrounded_size124}125126/// Returns the thickness of the UI node's outline in physical pixels.127/// If this value is negative or `0.` then no outline will be rendered.128///129/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).130#[inline]131pub const fn outline_width(&self) -> f32 {132self.outline_width133}134135/// Returns the amount of space between the outline and the edge of the node in physical pixels.136///137/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).138#[inline]139pub const fn outline_offset(&self) -> f32 {140self.outline_offset141}142143/// Returns the size of the node when including its outline.144///145/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).146#[inline]147pub const fn outlined_node_size(&self) -> Vec2 {148let offset = 2. * (self.outline_offset + self.outline_width);149Vec2::new(self.size.x + offset, self.size.y + offset)150}151152/// Returns the border radius for each corner of the outline153/// An outline's border radius is derived from the node's border-radius154/// so that the outline wraps the border equally at all points.155///156/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).157#[inline]158pub const fn outline_radius(&self) -> ResolvedBorderRadius {159let outer_distance = self.outline_width + self.outline_offset;160const fn compute_radius(radius: f32, outer_distance: f32) -> f32 {161if radius > 0. {162radius + outer_distance163} else {1640.165}166}167ResolvedBorderRadius {168top_left: compute_radius(self.border_radius.top_left, outer_distance),169top_right: compute_radius(self.border_radius.top_right, outer_distance),170bottom_right: compute_radius(self.border_radius.bottom_right, outer_distance),171bottom_left: compute_radius(self.border_radius.bottom_left, outer_distance),172}173}174175/// Returns the thickness of the node's border on each edge in physical pixels.176///177/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).178#[inline]179pub const fn border(&self) -> BorderRect {180self.border181}182183/// Returns the border radius for each of the node's corners in physical pixels.184///185/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).186#[inline]187pub const fn border_radius(&self) -> ResolvedBorderRadius {188self.border_radius189}190191/// Returns the inner border radius for each of the node's corners in physical pixels.192pub fn inner_radius(&self) -> ResolvedBorderRadius {193fn clamp_corner(r: f32, size: Vec2, offset: Vec2) -> f32 {194let s = 0.5 * size + offset;195let sm = s.x.min(s.y);196r.min(sm)197}198let b = Vec4::from((self.border.min_inset, self.border.max_inset));199let s = self.size() - b.xy() - b.zw();200ResolvedBorderRadius {201top_left: clamp_corner(self.border_radius.top_left, s, b.xy()),202top_right: clamp_corner(self.border_radius.top_right, s, b.zy()),203bottom_right: clamp_corner(self.border_radius.bottom_left, s, b.xw()),204bottom_left: clamp_corner(self.border_radius.bottom_right, s, b.zw()),205}206}207208/// Returns the thickness of the node's padding on each edge in physical pixels.209///210/// Automatically calculated by [`ui_layout_system`](`super::layout::ui_layout_system`).211#[inline]212pub const fn padding(&self) -> BorderRect {213self.padding214}215216/// Returns the combined inset on each edge including both padding and border thickness in physical pixels.217#[inline]218pub fn content_inset(&self) -> BorderRect {219let mut content_inset = self.border + self.padding;220content_inset.max_inset += self.scrollbar_size;221content_inset222}223224/// Returns the inverse of the scale factor for this node.225/// To convert from physical coordinates to logical coordinates multiply by this value.226#[inline]227pub const fn inverse_scale_factor(&self) -> f32 {228self.inverse_scale_factor229}230231// Returns true if `point` within the node.232//233// Matches the sdf function in `ui.wgsl` that is used by the UI renderer to draw rounded rectangles.234pub fn contains_point(&self, transform: UiGlobalTransform, point: Vec2) -> bool {235let Some(local_point) = transform236.try_inverse()237.map(|transform| transform.transform_point2(point))238else {239return false;240};241let [top, bottom] = if local_point.x < 0. {242[self.border_radius.top_left, self.border_radius.bottom_left]243} else {244[245self.border_radius.top_right,246self.border_radius.bottom_right,247]248};249let r = if local_point.y < 0. { top } else { bottom };250let corner_to_point = local_point.abs() - 0.5 * self.size;251let q = corner_to_point + r;252let l = q.max(Vec2::ZERO).length();253let m = q.max_element().min(0.);254l + m - r < 0.255}256257/// Transform a point to normalized node space with the center of the node at the origin and the corners at [+/-0.5, +/-0.5]258pub fn normalize_point(&self, transform: UiGlobalTransform, point: Vec2) -> Option<Vec2> {259self.size260.cmpgt(Vec2::ZERO)261.all()262.then(|| transform.try_inverse())263.flatten()264.map(|transform| transform.transform_point2(point) / self.size)265}266267/// Resolve the node's clipping rect in local space268pub fn resolve_clip_rect(269&self,270overflow: Overflow,271overflow_clip_margin: OverflowClipMargin,272) -> Rect {273let mut clip_rect = Rect::from_center_size(Vec2::ZERO, self.size);274275let clip_inset = match overflow_clip_margin.visual_box {276OverflowClipBox::BorderBox => BorderRect::ZERO,277OverflowClipBox::ContentBox => self.content_inset(),278OverflowClipBox::PaddingBox => self.border(),279};280281clip_rect.min += clip_inset.min_inset;282clip_rect.max -= clip_inset.max_inset;283284if overflow.x == OverflowAxis::Visible {285clip_rect.min.x = -f32::INFINITY;286clip_rect.max.x = f32::INFINITY;287}288if overflow.y == OverflowAxis::Visible {289clip_rect.min.y = -f32::INFINITY;290clip_rect.max.y = f32::INFINITY;291}292293clip_rect294}295296/// Returns the node's border-box in object-centered physical coordinates.297/// This is the full rectangle enclosing the node.298#[inline]299pub fn border_box(&self) -> Rect {300Rect::from_center_size(Vec2::ZERO, self.size)301}302303/// Returns the node's padding-box in object-centered physical coordinates.304/// This is the region inside the border containing the node's padding and content areas.305#[inline]306pub fn padding_box(&self) -> Rect {307let mut out = self.border_box();308out.min += self.border.min_inset;309out.max -= self.border.max_inset;310out311}312313/// Returns the node's content-box in object-centered physical coordinates.314/// This is the innermost region of the node, where its content is placed.315#[inline]316pub fn content_box(&self) -> Rect {317let mut out = self.border_box();318let content_inset = self.content_inset();319out.min += content_inset.min_inset;320out.max -= content_inset.max_inset;321out322}323324const fn compute_thumb(325gutter_min: f32,326content_length: f32,327gutter_length: f32,328scroll_position: f32,329) -> [f32; 2] {330if content_length <= gutter_length {331return [gutter_min, gutter_min + gutter_length];332}333let thumb_len = gutter_length * gutter_length / content_length;334let thumb_min = gutter_min + scroll_position * gutter_length / content_length;335[thumb_min, thumb_min + thumb_len]336}337338/// Compute the bounds of the horizontal scrollbar and the thumb339/// in object-centered coordinates.340pub fn horizontal_scrollbar(&self) -> Option<(Rect, [f32; 2])> {341if self.scrollbar_size.y <= 0. {342return None;343}344let content_inset = self.content_inset();345let half_size = 0.5 * self.size;346let min_x = -half_size.x + content_inset.min_inset.x;347let max_x = half_size.x - content_inset.max_inset.x;348let min_y = half_size.y - content_inset.max_inset.y;349let max_y = min_y + self.scrollbar_size.y;350let gutter = Rect {351min: Vec2::new(min_x, min_y),352max: Vec2::new(max_x, max_y),353};354Some((355gutter,356Self::compute_thumb(357gutter.min.x,358self.content_size.x,359gutter.size().x,360self.scroll_position.x,361),362))363}364365/// Compute the bounds of the vertical scrollbar and the thumb366/// in object-centered coordinates.367pub fn vertical_scrollbar(&self) -> Option<(Rect, [f32; 2])> {368if self.scrollbar_size.x <= 0. {369return None;370}371let content_inset = self.content_inset();372let half_size = 0.5 * self.size;373let min_x = half_size.x - content_inset.max_inset.x;374let max_x = min_x + self.scrollbar_size.x;375let min_y = -half_size.y + content_inset.min_inset.y;376let max_y = half_size.y - content_inset.max_inset.y;377let gutter = Rect {378min: Vec2::new(min_x, min_y),379max: Vec2::new(max_x, max_y),380};381Some((382gutter,383Self::compute_thumb(384gutter.min.y,385self.content_size.y,386gutter.size().y,387self.scroll_position.y,388),389))390}391}392393impl ComputedNode {394pub const DEFAULT: Self = Self {395stack_index: 0,396size: Vec2::ZERO,397content_size: Vec2::ZERO,398scrollbar_size: Vec2::ZERO,399scroll_position: Vec2::ZERO,400outline_width: 0.,401outline_offset: 0.,402unrounded_size: Vec2::ZERO,403border_radius: ResolvedBorderRadius::ZERO,404border: BorderRect::ZERO,405padding: BorderRect::ZERO,406inverse_scale_factor: 1.,407};408}409410impl Default for ComputedNode {411fn default() -> Self {412Self::DEFAULT413}414}415416/// The scroll position of the node. Values are in logical pixels, increasing from top-left to bottom-right.417///418/// 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.419/// This might seem backwards, however what's really happening is that420/// the scroll position is moving the visible "window" in the local coordinate system of the scrolled content -421/// moving the window down causes the content to move up.422///423/// Updating the values of `ScrollPosition` will reposition the children of the node by the offset amount in logical pixels.424/// `ScrollPosition` may be updated by the layout system when a layout change makes a previously valid `ScrollPosition` invalid.425/// Changing this does nothing on a `Node` without setting at least one `OverflowAxis` to `OverflowAxis::Scroll`.426#[derive(Component, Debug, Clone, Default, Deref, DerefMut, Reflect)]427#[reflect(Component, Default, Clone)]428pub struct ScrollPosition(pub Vec2);429430impl ScrollPosition {431pub const DEFAULT: Self = Self(Vec2::ZERO);432}433434impl From<Vec2> for ScrollPosition {435fn from(value: Vec2) -> Self {436Self(value)437}438}439440/// Controls whether a UI element ignores its parent's [`ScrollPosition`] along specific axes.441///442/// When an axis is set to `true`, the node will not have the parent’s scroll position applied443/// on that axis. This can be used to keep an element visually fixed along one or both axes444/// even when its parent UI element is scrolled.445#[derive(Component, Debug, Clone, Default, Deref, DerefMut, Reflect)]446#[reflect(Component, Default, Clone)]447pub struct IgnoreScroll(pub BVec2);448449impl From<BVec2> for IgnoreScroll {450fn from(value: BVec2) -> Self {451Self(value)452}453}454455/// The base component for UI entities. It describes UI layout and style properties.456///457/// When defining new types of UI entities, require [`Node`] to make them behave like UI nodes.458///459/// Nodes can be laid out using either Flexbox or CSS Grid Layout.460///461/// See below for general learning resources and for documentation on the individual style properties.462///463/// ### Flexbox464///465/// - [MDN: Basic Concepts of Flexbox](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox)466/// - [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.467/// - [Flexbox Froggy](https://flexboxfroggy.com/). An interactive tutorial/game that teaches the essential parts of Flexbox in a fun engaging way.468///469/// ### CSS Grid470///471/// - [MDN: Basic Concepts of Grid Layout](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Basic_Concepts_of_Grid_Layout)472/// - [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.473/// - [CSS Grid Garden](https://cssgridgarden.com/). An interactive tutorial/game that teaches the essential parts of CSS Grid in a fun engaging way.474///475/// # See also476///477/// - [`RelativeCursorPosition`](crate::RelativeCursorPosition) to obtain the cursor position relative to this node478/// - [`Interaction`](crate::Interaction) to obtain the interaction state of this node479480#[derive(Component, Clone, PartialEq, Debug, Reflect)]481#[require(482ComputedNode,483ComputedUiTargetCamera,484ComputedUiRenderTargetInfo,485UiTransform,486BackgroundColor,487BorderColor,488FocusPolicy,489ScrollPosition,490Visibility,491ZIndex492)]493#[reflect(Component, Default, PartialEq, Debug, Clone)]494#[cfg_attr(495feature = "serialize",496derive(serde::Serialize, serde::Deserialize),497reflect(Serialize, Deserialize)498)]499pub struct Node {500/// Which layout algorithm to use when laying out this node's contents:501/// - [`Display::Flex`]: Use the Flexbox layout algorithm502/// - [`Display::Grid`]: Use the CSS Grid layout algorithm503/// - [`Display::None`]: Hide this node and perform layout as if it does not exist.504///505/// <https://developer.mozilla.org/en-US/docs/Web/CSS/display>506pub display: Display,507508/// Which part of a Node's box length styles like width and height control509/// - [`BoxSizing::BorderBox`]: They refer to the "border box" size (size including padding and border)510/// - [`BoxSizing::ContentBox`]: They refer to the "content box" size (size excluding padding and border)511///512/// `BoxSizing::BorderBox` is generally considered more intuitive and is the default in Bevy even though it is not on the web.513///514/// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing>515pub box_sizing: BoxSizing,516517/// Whether a node should be laid out in-flow with, or independently of its siblings:518/// - [`PositionType::Relative`]: Layout this node in-flow with other nodes using the usual (flexbox/grid) layout algorithm.519/// - [`PositionType::Absolute`]: Layout this node on top and independently of other nodes.520///521/// <https://developer.mozilla.org/en-US/docs/Web/CSS/position>522pub position_type: PositionType,523524/// Whether overflowing content should be displayed or clipped.525///526/// <https://developer.mozilla.org/en-US/docs/Web/CSS/overflow>527pub overflow: Overflow,528529/// How much space in logical pixels should be reserved for scrollbars when overflow is set to scroll or auto on an axis.530pub scrollbar_width: f32,531532/// How the bounds of clipped content should be determined533///534/// <https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-margin>535pub overflow_clip_margin: OverflowClipMargin,536537/// The horizontal position of the left edge of the node.538/// - For relatively positioned nodes, this is relative to the node's position as computed during regular layout.539/// - For absolutely positioned nodes, this is relative to the *parent* node's bounding box.540///541/// <https://developer.mozilla.org/en-US/docs/Web/CSS/left>542pub left: Val,543544/// The horizontal position of the right edge of the node.545/// - For relatively positioned nodes, this is relative to the node's position as computed during regular layout.546/// - For absolutely positioned nodes, this is relative to the *parent* node's bounding box.547///548/// <https://developer.mozilla.org/en-US/docs/Web/CSS/right>549pub right: Val,550551/// The vertical position of the top edge of the node.552/// - For relatively positioned nodes, this is relative to the node's position as computed during regular layout.553/// - For absolutely positioned nodes, this is relative to the *parent* node's bounding box.554///555/// <https://developer.mozilla.org/en-US/docs/Web/CSS/top>556pub top: Val,557558/// The vertical position of the bottom edge of the node.559/// - For relatively positioned nodes, this is relative to the node's position as computed during regular layout.560/// - For absolutely positioned nodes, this is relative to the *parent* node's bounding box.561///562/// <https://developer.mozilla.org/en-US/docs/Web/CSS/bottom>563pub bottom: Val,564565/// The ideal width of the node. `width` is used when it is within the bounds defined by `min_width` and `max_width`.566///567/// <https://developer.mozilla.org/en-US/docs/Web/CSS/width>568pub width: Val,569570/// The ideal height of the node. `height` is used when it is within the bounds defined by `min_height` and `max_height`.571///572/// <https://developer.mozilla.org/en-US/docs/Web/CSS/height>573pub height: Val,574575/// The minimum width of the node. `min_width` is used if it is greater than `width` and/or `max_width`.576///577/// <https://developer.mozilla.org/en-US/docs/Web/CSS/min-width>578pub min_width: Val,579580/// The minimum height of the node. `min_height` is used if it is greater than `height` and/or `max_height`.581///582/// <https://developer.mozilla.org/en-US/docs/Web/CSS/min-height>583pub min_height: Val,584585/// The maximum width of the node. `max_width` is used if it is within the bounds defined by `min_width` and `width`.586///587/// <https://developer.mozilla.org/en-US/docs/Web/CSS/max-width>588pub max_width: Val,589590/// The maximum height of the node. `max_height` is used if it is within the bounds defined by `min_height` and `height`.591///592/// <https://developer.mozilla.org/en-US/docs/Web/CSS/max-height>593pub max_height: Val,594595/// The aspect ratio of the node (defined as `width / height`)596///597/// <https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio>598pub aspect_ratio: Option<f32>,599600/// Used to control how each individual item is aligned by default within the space they're given.601/// - For Flexbox containers, sets default cross axis alignment of the child items.602/// - For CSS Grid containers, controls block (vertical) axis alignment of children of this grid container within their grid areas.603///604/// This value is overridden if [`AlignSelf`] on the child node is set.605///606/// <https://developer.mozilla.org/en-US/docs/Web/CSS/align-items>607pub align_items: AlignItems,608609/// Used to control how each individual item is aligned by default within the space they're given.610/// - For Flexbox containers, this property has no effect. See `justify_content` for main axis alignment of flex items.611/// - For CSS Grid containers, sets default inline (horizontal) axis alignment of child items within their grid areas.612///613/// This value is overridden if [`JustifySelf`] on the child node is set.614///615/// <https://developer.mozilla.org/en-US/docs/Web/CSS/justify-items>616pub justify_items: JustifyItems,617618/// Used to control how the specified item is aligned within the space it's given.619/// - For Flexbox items, controls cross axis alignment of the item.620/// - For CSS Grid items, controls block (vertical) axis alignment of a grid item within its grid area.621///622/// If set to `Auto`, alignment is inherited from the value of [`AlignItems`] set on the parent node.623///624/// <https://developer.mozilla.org/en-US/docs/Web/CSS/align-self>625pub align_self: AlignSelf,626627/// Used to control how the specified item is aligned within the space it's given.628/// - For Flexbox items, this property has no effect. See `justify_content` for main axis alignment of flex items.629/// - For CSS Grid items, controls inline (horizontal) axis alignment of a grid item within its grid area.630///631/// If set to `Auto`, alignment is inherited from the value of [`JustifyItems`] set on the parent node.632///633/// <https://developer.mozilla.org/en-US/docs/Web/CSS/justify-self>634pub justify_self: JustifySelf,635636/// Used to control how items are distributed.637/// - For Flexbox containers, controls alignment of lines if `flex_wrap` is set to [`FlexWrap::Wrap`] and there are multiple lines of items.638/// - For CSS Grid containers, controls alignment of grid rows.639///640/// <https://developer.mozilla.org/en-US/docs/Web/CSS/align-content>641pub align_content: AlignContent,642643/// Used to control how items are distributed.644/// - For Flexbox containers, controls alignment of items in the main axis.645/// - For CSS Grid containers, controls alignment of grid columns.646///647/// <https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content>648pub justify_content: JustifyContent,649650/// The amount of space around a node outside its border.651///652/// If a percentage value is used, the percentage is calculated based on the width of the parent node.653///654/// # Example655/// ```656/// # use bevy_ui::{Node, UiRect, Val};657/// let node = Node {658/// margin: UiRect {659/// left: Val::Percent(10.),660/// right: Val::Percent(10.),661/// top: Val::Percent(15.),662/// bottom: Val::Percent(15.)663/// },664/// ..Default::default()665/// };666/// ```667/// 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.668///669/// <https://developer.mozilla.org/en-US/docs/Web/CSS/margin>670pub margin: UiRect,671672/// The amount of space between the edges of a node and its contents.673///674/// If a percentage value is used, the percentage is calculated based on the width of the parent node.675///676/// # Example677/// ```678/// # use bevy_ui::{Node, UiRect, Val};679/// let node = Node {680/// padding: UiRect {681/// left: Val::Percent(1.),682/// right: Val::Percent(2.),683/// top: Val::Percent(3.),684/// bottom: Val::Percent(4.)685/// },686/// ..Default::default()687/// };688/// ```689/// 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.690///691/// <https://developer.mozilla.org/en-US/docs/Web/CSS/padding>692pub padding: UiRect,693694/// The amount of space between the margins of a node and its padding.695///696/// If a percentage value is used, the percentage is calculated based on the width of the parent node.697///698/// 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.699///700/// <https://developer.mozilla.org/en-US/docs/Web/CSS/border-width>701pub border: UiRect,702703/// Used to add rounded corners to a UI node. You can set a UI node to have uniformly704/// rounded corners or specify different radii for each corner. If a given radius exceeds half705/// the length of the smallest dimension between the node's height or width, the radius will706/// calculated as half the smallest dimension.707///708/// Elliptical nodes are not supported yet. Percentage values are based on the node's smallest709/// dimension, either width or height.710///711/// # Example712/// ```rust713/// # use bevy_ecs::prelude::*;714/// # use bevy_ui::prelude::*;715/// # use bevy_color::palettes::basic::{BLUE};716/// fn setup_ui(mut commands: Commands) {717/// commands.spawn((718/// Node {719/// width: Val::Px(100.),720/// height: Val::Px(100.),721/// border: UiRect::all(Val::Px(2.)),722/// border_radius: BorderRadius::new(723/// // top left724/// Val::Px(10.),725/// // top right726/// Val::Px(20.),727/// // bottom right728/// Val::Px(30.),729/// // bottom left730/// Val::Px(40.),731/// ),732/// ..Default::default()733/// },734/// BackgroundColor(BLUE.into()),735/// ));736/// }737/// ```738///739/// <https://developer.mozilla.org/en-US/docs/Web/CSS/border-radius>740pub border_radius: BorderRadius,741742/// Whether a Flexbox container should be a row or a column. This property has no effect on Grid nodes.743///744/// <https://developer.mozilla.org/en-US/docs/Web/CSS/flex-direction>745pub flex_direction: FlexDirection,746747/// Whether a Flexbox container should wrap its contents onto multiple lines if they overflow. This property has no effect on Grid nodes.748///749/// <https://developer.mozilla.org/en-US/docs/Web/CSS/flex-wrap>750pub flex_wrap: FlexWrap,751752/// Defines how much a flexbox item should grow if there's space available. Defaults to 0 (don't grow at all).753///754/// <https://developer.mozilla.org/en-US/docs/Web/CSS/flex-grow>755pub flex_grow: f32,756757/// Defines how much a flexbox item should shrink if there's not enough space available. Defaults to 1.758///759/// <https://developer.mozilla.org/en-US/docs/Web/CSS/flex-shrink>760pub flex_shrink: f32,761762/// The initial length of a flexbox in the main axis, before flex growing/shrinking properties are applied.763///764/// `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`.765///766/// <https://developer.mozilla.org/en-US/docs/Web/CSS/flex-basis>767pub flex_basis: Val,768769/// The size of the gutters between items in a vertical flexbox layout or between rows in a grid layout.770///771/// Note: Values of `Val::Auto` are not valid and are treated as zero.772///773/// <https://developer.mozilla.org/en-US/docs/Web/CSS/row-gap>774pub row_gap: Val,775776/// The size of the gutters between items in a horizontal flexbox layout or between column in a grid layout.777///778/// Note: Values of `Val::Auto` are not valid and are treated as zero.779///780/// <https://developer.mozilla.org/en-US/docs/Web/CSS/column-gap>781pub column_gap: Val,782783/// 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.784/// Only affects Grid layouts.785///786/// <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow>787pub grid_auto_flow: GridAutoFlow,788789/// Defines the number of rows a grid has and the sizes of those rows. If grid items are given explicit placements then more rows may790/// be implicitly generated by items that are placed out of bounds. The sizes of those rows are controlled by `grid_auto_rows` property.791///792/// <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-rows>793pub grid_template_rows: Vec<RepeatedGridTrack>,794795/// Defines the number of columns a grid has and the sizes of those columns. If grid items are given explicit placements then more columns may796/// be implicitly generated by items that are placed out of bounds. The sizes of those columns are controlled by `grid_auto_columns` property.797///798/// <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns>799pub grid_template_columns: Vec<RepeatedGridTrack>,800801/// Defines the size of implicitly created rows. Rows are created implicitly when grid items are given explicit placements that are out of bounds802/// of the rows explicitly created using `grid_template_rows`.803///804/// <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-rows>805pub grid_auto_rows: Vec<GridTrack>,806/// Defines the size of implicitly created columns. Columns are created implicitly when grid items are given explicit placements that are out of bounds807/// of the columns explicitly created using `grid_template_columns`.808///809/// <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-columns>810pub grid_auto_columns: Vec<GridTrack>,811812/// The row in which a grid item starts and how many rows it spans.813///814/// <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-row>815pub grid_row: GridPlacement,816817/// The column in which a grid item starts and how many columns it spans.818///819/// <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-column>820pub grid_column: GridPlacement,821}822823impl Node {824pub const DEFAULT: Self = Self {825display: Display::DEFAULT,826box_sizing: BoxSizing::DEFAULT,827position_type: PositionType::DEFAULT,828left: Val::Auto,829right: Val::Auto,830top: Val::Auto,831bottom: Val::Auto,832flex_direction: FlexDirection::DEFAULT,833flex_wrap: FlexWrap::DEFAULT,834align_items: AlignItems::DEFAULT,835justify_items: JustifyItems::DEFAULT,836align_self: AlignSelf::DEFAULT,837justify_self: JustifySelf::DEFAULT,838align_content: AlignContent::DEFAULT,839justify_content: JustifyContent::DEFAULT,840margin: UiRect::DEFAULT,841padding: UiRect::DEFAULT,842border: UiRect::DEFAULT,843border_radius: BorderRadius::DEFAULT,844flex_grow: 0.0,845flex_shrink: 1.0,846flex_basis: Val::Auto,847width: Val::Auto,848height: Val::Auto,849min_width: Val::Auto,850min_height: Val::Auto,851max_width: Val::Auto,852max_height: Val::Auto,853aspect_ratio: None,854overflow: Overflow::DEFAULT,855overflow_clip_margin: OverflowClipMargin::DEFAULT,856scrollbar_width: 0.,857row_gap: Val::ZERO,858column_gap: Val::ZERO,859grid_auto_flow: GridAutoFlow::DEFAULT,860grid_template_rows: Vec::new(),861grid_template_columns: Vec::new(),862grid_auto_rows: Vec::new(),863grid_auto_columns: Vec::new(),864grid_column: GridPlacement::DEFAULT,865grid_row: GridPlacement::DEFAULT,866};867}868869impl Default for Node {870fn default() -> Self {871Self::DEFAULT872}873}874875/// Used to control how each individual item is aligned by default within the space they're given.876/// - For Flexbox containers, sets default cross axis alignment of the child items.877/// - For CSS Grid containers, controls block (vertical) axis alignment of children of this grid container within their grid areas.878///879/// <https://developer.mozilla.org/en-US/docs/Web/CSS/align-items>880#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]881#[reflect(Default, PartialEq, Clone)]882#[cfg_attr(883feature = "serialize",884derive(serde::Serialize, serde::Deserialize),885reflect(Serialize, Deserialize)886)]887pub enum AlignItems {888/// The items are packed in their default position as if no alignment was applied.889Default,890/// The items are packed towards the start of the axis.891Start,892/// The items are packed towards the end of the axis.893End,894/// The items are packed towards the start of the axis, unless the flex direction is reversed;895/// then they are packed towards the end of the axis.896FlexStart,897/// The items are packed towards the end of the axis, unless the flex direction is reversed;898/// then they are packed towards the start of the axis.899FlexEnd,900/// The items are packed along the center of the axis.901Center,902/// The items are packed such that their baselines align.903Baseline,904/// The items are stretched to fill the space they're given.905Stretch,906}907908impl AlignItems {909pub const DEFAULT: Self = Self::Default;910}911912impl Default for AlignItems {913fn default() -> Self {914Self::DEFAULT915}916}917918/// Used to control how each individual item is aligned by default within the space they're given.919/// - For Flexbox containers, this property has no effect. See `justify_content` for main axis alignment of flex items.920/// - For CSS Grid containers, sets default inline (horizontal) axis alignment of child items within their grid areas.921///922/// <https://developer.mozilla.org/en-US/docs/Web/CSS/justify-items>923#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]924#[reflect(Default, PartialEq, Clone)]925#[cfg_attr(926feature = "serialize",927derive(serde::Serialize, serde::Deserialize),928reflect(Serialize, Deserialize)929)]930pub enum JustifyItems {931/// The items are packed in their default position as if no alignment was applied.932Default,933/// The items are packed towards the start of the axis.934Start,935/// The items are packed towards the end of the axis.936End,937/// The items are packed along the center of the axis938Center,939/// The items are packed such that their baselines align.940Baseline,941/// The items are stretched to fill the space they're given.942Stretch,943}944945impl JustifyItems {946pub const DEFAULT: Self = Self::Default;947}948949impl Default for JustifyItems {950fn default() -> Self {951Self::DEFAULT952}953}954955/// Used to control how the specified item is aligned within the space it's given.956/// - For Flexbox items, controls cross axis alignment of the item.957/// - For CSS Grid items, controls block (vertical) axis alignment of a grid item within its grid area.958///959/// <https://developer.mozilla.org/en-US/docs/Web/CSS/align-self>960#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]961#[reflect(Default, PartialEq, Clone)]962#[cfg_attr(963feature = "serialize",964derive(serde::Serialize, serde::Deserialize),965reflect(Serialize, Deserialize)966)]967pub enum AlignSelf {968/// Use the parent node's [`AlignItems`] value to determine how this item should be aligned.969Auto,970/// This item will be aligned with the start of the axis.971Start,972/// This item will be aligned with the end of the axis.973End,974/// This item will be aligned with the start of the axis, unless the flex direction is reversed;975/// then it will be aligned with the end of the axis.976FlexStart,977/// This item will be aligned with the end of the axis, unless the flex direction is reversed;978/// then it will be aligned with the start of the axis.979FlexEnd,980/// This item will be aligned along the center of the axis.981Center,982/// This item will be aligned at the baseline.983Baseline,984/// This item will be stretched to fill the container.985Stretch,986}987988impl AlignSelf {989pub const DEFAULT: Self = Self::Auto;990}991992impl Default for AlignSelf {993fn default() -> Self {994Self::DEFAULT995}996}997998/// Used to control how the specified item is aligned within the space it's given.999/// - For children of flex nodes, this property has no effect. See `justify_content` for main axis alignment of flex items.1000/// - For CSS Grid items, controls inline (horizontal) axis alignment of a grid item within its grid area.1001///1002/// <https://developer.mozilla.org/en-US/docs/Web/CSS/justify-self>1003#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]1004#[reflect(Default, PartialEq, Clone)]1005#[cfg_attr(1006feature = "serialize",1007derive(serde::Serialize, serde::Deserialize),1008reflect(Serialize, Deserialize)1009)]1010pub enum JustifySelf {1011/// Use the parent node's [`JustifyItems`] value to determine how this item should be aligned.1012Auto,1013/// This item will be aligned with the start of the axis.1014Start,1015/// This item will be aligned with the end of the axis.1016End,1017/// This item will be aligned along the center of the axis.1018Center,1019/// This item will be aligned at the baseline.1020Baseline,1021/// This item will be stretched to fill the space it's given.1022Stretch,1023}10241025impl JustifySelf {1026pub const DEFAULT: Self = Self::Auto;1027}10281029impl Default for JustifySelf {1030fn default() -> Self {1031Self::DEFAULT1032}1033}10341035/// Used to control how items are distributed.1036/// - For Flexbox containers, controls alignment of lines if `flex_wrap` is set to [`FlexWrap::Wrap`] and there are multiple lines of items.1037/// - For CSS Grid containers, controls alignment of grid rows.1038///1039/// <https://developer.mozilla.org/en-US/docs/Web/CSS/align-content>1040#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]1041#[reflect(Default, PartialEq, Clone)]1042#[cfg_attr(1043feature = "serialize",1044derive(serde::Serialize, serde::Deserialize),1045reflect(Serialize, Deserialize)1046)]1047pub enum AlignContent {1048/// The items are packed in their default position as if no alignment was applied.1049Default,1050/// The items are packed towards the start of the axis.1051Start,1052/// The items are packed towards the end of the axis.1053End,1054/// The items are packed towards the start of the axis, unless the flex direction is reversed;1055/// then the items are packed towards the end of the axis.1056FlexStart,1057/// The items are packed towards the end of the axis, unless the flex direction is reversed;1058/// then the items are packed towards the start of the axis.1059FlexEnd,1060/// The items are packed along the center of the axis.1061Center,1062/// The items are stretched to fill the container along the axis.1063Stretch,1064/// The items are distributed such that the gap between any two items is equal.1065SpaceBetween,1066/// The items are distributed such that the gap between and around any two items is equal.1067SpaceEvenly,1068/// The items are distributed such that the gap between and around any two items is equal, with half-size gaps on either end.1069SpaceAround,1070}10711072impl AlignContent {1073pub const DEFAULT: Self = Self::Default;1074}10751076impl Default for AlignContent {1077fn default() -> Self {1078Self::DEFAULT1079}1080}10811082/// Used to control how items are distributed.1083/// - For Flexbox containers, controls alignment of items in the main axis.1084/// - For CSS Grid containers, controls alignment of grid columns.1085///1086/// <https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content>1087#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]1088#[reflect(Default, PartialEq, Clone)]1089#[cfg_attr(1090feature = "serialize",1091derive(serde::Serialize, serde::Deserialize),1092reflect(Serialize, Deserialize)1093)]1094pub enum JustifyContent {1095/// The items are packed in their default position as if no alignment was applied.1096Default,1097/// The items are packed towards the start of the axis.1098Start,1099/// The items are packed towards the end of the axis.1100End,1101/// The items are packed towards the start of the axis, unless the flex direction is reversed;1102/// then the items are packed towards the end of the axis.1103FlexStart,1104/// The items are packed towards the end of the axis, unless the flex direction is reversed;1105/// then the items are packed towards the start of the axis.1106FlexEnd,1107/// The items are packed along the center of the axis.1108Center,1109/// The items are stretched to fill the container along the axis.1110Stretch,1111/// The items are distributed such that the gap between any two items is equal.1112SpaceBetween,1113/// The items are distributed such that the gap between and around any two items is equal.1114SpaceEvenly,1115/// The items are distributed such that the gap between and around any two items is equal, with half-size gaps on either end.1116SpaceAround,1117}11181119impl JustifyContent {1120pub const DEFAULT: Self = Self::Default;1121}11221123impl Default for JustifyContent {1124fn default() -> Self {1125Self::DEFAULT1126}1127}11281129/// Defines the layout model used by this node.1130///1131/// Part of the [`Node`] component.1132#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]1133#[reflect(Default, PartialEq, Clone)]1134#[cfg_attr(1135feature = "serialize",1136derive(serde::Serialize, serde::Deserialize),1137reflect(Serialize, Deserialize)1138)]1139pub enum Display {1140/// Use Flexbox layout model to determine the position of this [`Node`]'s children.1141Flex,1142/// Use CSS Grid layout model to determine the position of this [`Node`]'s children.1143Grid,1144/// Use CSS Block layout model to determine the position of this [`Node`]'s children.1145Block,1146/// Use no layout, don't render this node and its children.1147///1148/// If you want to hide a node and its children,1149/// but keep its layout in place, set its [`Visibility`] component instead.1150None,1151}11521153impl Display {1154pub const DEFAULT: Self = Self::Flex;1155}11561157impl Default for Display {1158fn default() -> Self {1159Self::DEFAULT1160}1161}11621163/// Which part of a Node's box length styles like width and height control1164///1165/// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing>1166#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]1167#[reflect(Default, PartialEq, Clone)]1168#[cfg_attr(1169feature = "serialize",1170derive(serde::Serialize, serde::Deserialize),1171reflect(Serialize, Deserialize)1172)]1173pub enum BoxSizing {1174/// Length styles like width and height refer to the "border box" size (size including padding and border)1175BorderBox,1176/// Length styles like width and height refer to the "content box" size (size excluding padding and border)1177ContentBox,1178}11791180impl BoxSizing {1181pub const DEFAULT: Self = Self::BorderBox;1182}11831184impl Default for BoxSizing {1185fn default() -> Self {1186Self::DEFAULT1187}1188}11891190/// Defines how flexbox items are ordered within a flexbox1191#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]1192#[reflect(Default, PartialEq, Clone)]1193#[cfg_attr(1194feature = "serialize",1195derive(serde::Serialize, serde::Deserialize),1196reflect(Serialize, Deserialize)1197)]1198pub enum FlexDirection {1199/// Same way as text direction along the main axis.1200Row,1201/// Flex from top to bottom.1202Column,1203/// Opposite way as text direction along the main axis.1204RowReverse,1205/// Flex from bottom to top.1206ColumnReverse,1207}12081209impl FlexDirection {1210pub const DEFAULT: Self = Self::Row;1211}12121213impl Default for FlexDirection {1214fn default() -> Self {1215Self::DEFAULT1216}1217}12181219/// Whether to show or hide overflowing items1220#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]1221#[reflect(Default, PartialEq, Clone)]1222#[cfg_attr(1223feature = "serialize",1224derive(serde::Serialize, serde::Deserialize),1225reflect(Serialize, Deserialize)1226)]1227pub struct Overflow {1228/// Whether to show or clip overflowing items on the x axis1229pub x: OverflowAxis,1230/// Whether to show or clip overflowing items on the y axis1231pub y: OverflowAxis,1232}12331234impl Overflow {1235pub const DEFAULT: Self = Self {1236x: OverflowAxis::DEFAULT,1237y: OverflowAxis::DEFAULT,1238};12391240/// Show overflowing items on both axes1241pub const fn visible() -> Self {1242Self {1243x: OverflowAxis::Visible,1244y: OverflowAxis::Visible,1245}1246}12471248/// Clip overflowing items on both axes1249pub const fn clip() -> Self {1250Self {1251x: OverflowAxis::Clip,1252y: OverflowAxis::Clip,1253}1254}12551256/// Clip overflowing items on the x axis1257pub const fn clip_x() -> Self {1258Self {1259x: OverflowAxis::Clip,1260y: OverflowAxis::Visible,1261}1262}12631264/// Clip overflowing items on the y axis1265pub const fn clip_y() -> Self {1266Self {1267x: OverflowAxis::Visible,1268y: OverflowAxis::Clip,1269}1270}12711272/// Hide overflowing items on both axes by influencing layout and then clipping1273pub const fn hidden() -> Self {1274Self {1275x: OverflowAxis::Hidden,1276y: OverflowAxis::Hidden,1277}1278}12791280/// Hide overflowing items on the x axis by influencing layout and then clipping1281pub const fn hidden_x() -> Self {1282Self {1283x: OverflowAxis::Hidden,1284y: OverflowAxis::Visible,1285}1286}12871288/// Hide overflowing items on the y axis by influencing layout and then clipping1289pub const fn hidden_y() -> Self {1290Self {1291x: OverflowAxis::Visible,1292y: OverflowAxis::Hidden,1293}1294}12951296/// Overflow is visible on both axes1297pub const fn is_visible(&self) -> bool {1298self.x.is_visible() && self.y.is_visible()1299}13001301pub const fn scroll() -> Self {1302Self {1303x: OverflowAxis::Scroll,1304y: OverflowAxis::Scroll,1305}1306}13071308/// Scroll overflowing items on the x axis1309pub const fn scroll_x() -> Self {1310Self {1311x: OverflowAxis::Scroll,1312y: OverflowAxis::Visible,1313}1314}13151316/// Scroll overflowing items on the y axis1317pub const fn scroll_y() -> Self {1318Self {1319x: OverflowAxis::Visible,1320y: OverflowAxis::Scroll,1321}1322}1323}13241325impl Default for Overflow {1326fn default() -> Self {1327Self::DEFAULT1328}1329}13301331/// Whether to show or hide overflowing items1332#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]1333#[reflect(Default, PartialEq, Clone)]1334#[cfg_attr(1335feature = "serialize",1336derive(serde::Serialize, serde::Deserialize),1337reflect(Serialize, Deserialize)1338)]1339pub enum OverflowAxis {1340/// Show overflowing items.1341Visible,1342/// Hide overflowing items by clipping.1343Clip,1344/// Hide overflowing items by influencing layout and then clipping.1345Hidden,1346/// Scroll overflowing items.1347Scroll,1348}13491350impl OverflowAxis {1351pub const DEFAULT: Self = Self::Visible;13521353/// Overflow is visible on this axis1354pub const fn is_visible(&self) -> bool {1355matches!(self, Self::Visible)1356}1357}13581359impl Default for OverflowAxis {1360fn default() -> Self {1361Self::DEFAULT1362}1363}13641365/// The bounds of the visible area when a UI node is clipped.1366#[derive(Default, Copy, Clone, PartialEq, Debug, Reflect)]1367#[reflect(Default, PartialEq, Clone)]1368#[cfg_attr(1369feature = "serialize",1370derive(serde::Serialize, serde::Deserialize),1371reflect(Serialize, Deserialize)1372)]1373pub struct OverflowClipMargin {1374/// Visible unclipped area1375pub visual_box: OverflowClipBox,1376/// Width of the margin on each edge of the visual box in logical pixels.1377/// The width of the margin will be zero if a negative value is set.1378pub margin: f32,1379}13801381impl OverflowClipMargin {1382pub const DEFAULT: Self = Self {1383visual_box: OverflowClipBox::PaddingBox,1384margin: 0.,1385};13861387/// Clip any content that overflows outside the content box1388pub const fn content_box() -> Self {1389Self {1390visual_box: OverflowClipBox::ContentBox,1391..Self::DEFAULT1392}1393}13941395/// Clip any content that overflows outside the padding box1396pub const fn padding_box() -> Self {1397Self {1398visual_box: OverflowClipBox::PaddingBox,1399..Self::DEFAULT1400}1401}14021403/// Clip any content that overflows outside the border box1404pub const fn border_box() -> Self {1405Self {1406visual_box: OverflowClipBox::BorderBox,1407..Self::DEFAULT1408}1409}14101411/// Add a margin on each edge of the visual box in logical pixels.1412/// The width of the margin will be zero if a negative value is set.1413pub const fn with_margin(mut self, margin: f32) -> Self {1414self.margin = margin;1415self1416}1417}14181419/// Used to determine the bounds of the visible area when a UI node is clipped.1420#[derive(Default, Copy, Clone, PartialEq, Eq, Debug, Reflect)]1421#[reflect(Default, PartialEq, Clone)]1422#[cfg_attr(1423feature = "serialize",1424derive(serde::Serialize, serde::Deserialize),1425reflect(Serialize, Deserialize)1426)]1427pub enum OverflowClipBox {1428/// Clip any content that overflows outside the content box1429ContentBox,1430/// Clip any content that overflows outside the padding box1431#[default]1432PaddingBox,1433/// Clip any content that overflows outside the border box1434BorderBox,1435}14361437/// The strategy used to position this node1438#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]1439#[reflect(Default, PartialEq, Clone)]1440#[cfg_attr(1441feature = "serialize",1442derive(serde::Serialize, serde::Deserialize),1443reflect(Serialize, Deserialize)1444)]1445pub enum PositionType {1446/// Relative to all other nodes with the [`PositionType::Relative`] value.1447Relative,1448/// Independent of all other nodes, but relative to its parent node.1449Absolute,1450}14511452impl PositionType {1453pub const DEFAULT: Self = Self::Relative;1454}14551456impl Default for PositionType {1457fn default() -> Self {1458Self::DEFAULT1459}1460}14611462/// Defines if flexbox items appear on a single line or on multiple lines1463#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]1464#[reflect(Default, PartialEq, Clone)]1465#[cfg_attr(1466feature = "serialize",1467derive(serde::Serialize, serde::Deserialize),1468reflect(Serialize, Deserialize)1469)]1470pub enum FlexWrap {1471/// Single line, will overflow if needed.1472NoWrap,1473/// Multiple lines, if needed.1474Wrap,1475/// Same as [`FlexWrap::Wrap`] but new lines will appear before the previous one.1476WrapReverse,1477}14781479impl FlexWrap {1480pub const DEFAULT: Self = Self::NoWrap;1481}14821483impl Default for FlexWrap {1484fn default() -> Self {1485Self::DEFAULT1486}1487}14881489/// Controls whether grid items are placed row-wise or column-wise as well as whether the sparse or dense packing algorithm is used.1490///1491/// The "dense" packing algorithm attempts to fill in holes earlier in the grid, if smaller items come up later.1492/// This may cause items to appear out-of-order when doing so would fill in holes left by larger items.1493///1494/// Defaults to [`GridAutoFlow::Row`].1495///1496/// <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow>1497#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]1498#[reflect(Default, PartialEq, Clone)]1499#[cfg_attr(1500feature = "serialize",1501derive(serde::Serialize, serde::Deserialize),1502reflect(Serialize, Deserialize)1503)]1504pub enum GridAutoFlow {1505/// Items are placed by filling each row in turn, adding new rows as necessary.1506Row,1507/// Items are placed by filling each column in turn, adding new columns as necessary.1508Column,1509/// Combines `Row` with the dense packing algorithm.1510RowDense,1511/// Combines `Column` with the dense packing algorithm.1512ColumnDense,1513}15141515impl GridAutoFlow {1516pub const DEFAULT: Self = Self::Row;1517}15181519impl Default for GridAutoFlow {1520fn default() -> Self {1521Self::DEFAULT1522}1523}15241525#[derive(Default, Copy, Clone, PartialEq, Debug, Reflect)]1526#[reflect(Default, PartialEq, Clone)]1527#[cfg_attr(1528feature = "serialize",1529derive(serde::Serialize, serde::Deserialize),1530reflect(Serialize, Deserialize)1531)]1532pub enum MinTrackSizingFunction {1533/// Track minimum size should be a fixed pixel value1534Px(f32),1535/// Track minimum size should be a percentage value1536Percent(f32),1537/// Track minimum size should be content sized under a min-content constraint1538MinContent,1539/// Track minimum size should be content sized under a max-content constraint1540MaxContent,1541/// Track minimum size should be automatically sized1542#[default]1543Auto,1544/// Track minimum size should be a percent of the viewport's smaller dimension.1545VMin(f32),1546/// Track minimum size should be a percent of the viewport's larger dimension.1547VMax(f32),1548/// Track minimum size should be a percent of the viewport's height dimension.1549Vh(f32),1550/// Track minimum size should be a percent of the viewport's width dimension.1551Vw(f32),1552}15531554#[derive(Default, Copy, Clone, PartialEq, Debug, Reflect)]1555#[reflect(Default, PartialEq, Clone)]1556#[cfg_attr(1557feature = "serialize",1558derive(serde::Serialize, serde::Deserialize),1559reflect(Serialize, Deserialize)1560)]1561pub enum MaxTrackSizingFunction {1562/// Track maximum size should be a fixed pixel value1563Px(f32),1564/// Track maximum size should be a percentage value1565Percent(f32),1566/// Track maximum size should be content sized under a min-content constraint1567MinContent,1568/// Track maximum size should be content sized under a max-content constraint1569MaxContent,1570/// Track maximum size should be sized according to the fit-content formula with a fixed pixel limit1571FitContentPx(f32),1572/// Track maximum size should be sized according to the fit-content formula with a percentage limit1573FitContentPercent(f32),1574/// Track maximum size should be automatically sized1575#[default]1576Auto,1577/// The dimension as a fraction of the total available grid space (`fr` units in CSS)1578/// Specified value is the numerator of the fraction. Denominator is the sum of all fractions specified in that grid dimension.1579///1580/// Spec: <https://www.w3.org/TR/css3-grid-layout/#fr-unit>1581Fraction(f32),1582/// Track maximum size should be a percent of the viewport's smaller dimension.1583VMin(f32),1584/// Track maximum size should be a percent of the viewport's smaller dimension.1585VMax(f32),1586/// Track maximum size should be a percent of the viewport's height dimension.1587Vh(f32),1588/// Track maximum size should be a percent of the viewport's width dimension.1589Vw(f32),1590}15911592/// A [`GridTrack`] is a Row or Column of a CSS Grid. This struct specifies what size the track should be.1593/// See below for the different "track sizing functions" you can specify.1594#[derive(Copy, Clone, PartialEq, Debug, Reflect)]1595#[reflect(Default, PartialEq, Clone)]1596#[cfg_attr(1597feature = "serialize",1598derive(serde::Serialize, serde::Deserialize),1599reflect(Serialize, Deserialize)1600)]1601pub struct GridTrack {1602pub(crate) min_sizing_function: MinTrackSizingFunction,1603pub(crate) max_sizing_function: MaxTrackSizingFunction,1604}16051606impl GridTrack {1607pub const DEFAULT: Self = Self {1608min_sizing_function: MinTrackSizingFunction::Auto,1609max_sizing_function: MaxTrackSizingFunction::Auto,1610};16111612/// Create a grid track with a fixed pixel size1613pub fn px<T: From<Self>>(value: f32) -> T {1614Self {1615min_sizing_function: MinTrackSizingFunction::Px(value),1616max_sizing_function: MaxTrackSizingFunction::Px(value),1617}1618.into()1619}16201621/// Create a grid track with a percentage size1622pub fn percent<T: From<Self>>(value: f32) -> T {1623Self {1624min_sizing_function: MinTrackSizingFunction::Percent(value),1625max_sizing_function: MaxTrackSizingFunction::Percent(value),1626}1627.into()1628}16291630/// Create a grid track with an `fr` size.1631/// Note that this will give the track a content-based minimum size.1632/// Usually you are best off using `GridTrack::flex` instead which uses a zero minimum size.1633pub fn fr<T: From<Self>>(value: f32) -> T {1634Self {1635min_sizing_function: MinTrackSizingFunction::Auto,1636max_sizing_function: MaxTrackSizingFunction::Fraction(value),1637}1638.into()1639}16401641/// Create a grid track with a `minmax(0, Nfr)` size.1642pub fn flex<T: From<Self>>(value: f32) -> T {1643Self {1644min_sizing_function: MinTrackSizingFunction::Px(0.0),1645max_sizing_function: MaxTrackSizingFunction::Fraction(value),1646}1647.into()1648}16491650/// Create a grid track which is automatically sized to fit its contents.1651pub fn auto<T: From<Self>>() -> T {1652Self {1653min_sizing_function: MinTrackSizingFunction::Auto,1654max_sizing_function: MaxTrackSizingFunction::Auto,1655}1656.into()1657}16581659/// Create a grid track which is automatically sized to fit its contents when sized at their "min-content" sizes1660pub fn min_content<T: From<Self>>() -> T {1661Self {1662min_sizing_function: MinTrackSizingFunction::MinContent,1663max_sizing_function: MaxTrackSizingFunction::MinContent,1664}1665.into()1666}16671668/// Create a grid track which is automatically sized to fit its contents when sized at their "max-content" sizes1669pub fn max_content<T: From<Self>>() -> T {1670Self {1671min_sizing_function: MinTrackSizingFunction::MaxContent,1672max_sizing_function: MaxTrackSizingFunction::MaxContent,1673}1674.into()1675}16761677/// Create a `fit-content()` grid track with fixed pixel limit.1678///1679/// <https://developer.mozilla.org/en-US/docs/Web/CSS/fit-content_function>1680pub fn fit_content_px<T: From<Self>>(limit: f32) -> T {1681Self {1682min_sizing_function: MinTrackSizingFunction::Auto,1683max_sizing_function: MaxTrackSizingFunction::FitContentPx(limit),1684}1685.into()1686}16871688/// Create a `fit-content()` grid track with percentage limit.1689///1690/// <https://developer.mozilla.org/en-US/docs/Web/CSS/fit-content_function>1691pub fn fit_content_percent<T: From<Self>>(limit: f32) -> T {1692Self {1693min_sizing_function: MinTrackSizingFunction::Auto,1694max_sizing_function: MaxTrackSizingFunction::FitContentPercent(limit),1695}1696.into()1697}16981699/// Create a `minmax()` grid track.1700///1701/// <https://developer.mozilla.org/en-US/docs/Web/CSS/minmax>1702pub fn minmax<T: From<Self>>(min: MinTrackSizingFunction, max: MaxTrackSizingFunction) -> T {1703Self {1704min_sizing_function: min,1705max_sizing_function: max,1706}1707.into()1708}17091710/// Create a grid track with a percentage of the viewport's smaller dimension1711pub fn vmin<T: From<Self>>(value: f32) -> T {1712Self {1713min_sizing_function: MinTrackSizingFunction::VMin(value),1714max_sizing_function: MaxTrackSizingFunction::VMin(value),1715}1716.into()1717}17181719/// Create a grid track with a percentage of the viewport's larger dimension1720pub fn vmax<T: From<Self>>(value: f32) -> T {1721Self {1722min_sizing_function: MinTrackSizingFunction::VMax(value),1723max_sizing_function: MaxTrackSizingFunction::VMax(value),1724}1725.into()1726}17271728/// Create a grid track with a percentage of the viewport's height dimension1729pub fn vh<T: From<Self>>(value: f32) -> T {1730Self {1731min_sizing_function: MinTrackSizingFunction::Vh(value),1732max_sizing_function: MaxTrackSizingFunction::Vh(value),1733}1734.into()1735}17361737/// Create a grid track with a percentage of the viewport's width dimension1738pub fn vw<T: From<Self>>(value: f32) -> T {1739Self {1740min_sizing_function: MinTrackSizingFunction::Vw(value),1741max_sizing_function: MaxTrackSizingFunction::Vw(value),1742}1743.into()1744}1745}17461747impl Default for GridTrack {1748fn default() -> Self {1749Self::DEFAULT1750}1751}17521753#[derive(Copy, Clone, PartialEq, Debug, Reflect, From)]1754#[reflect(Default, PartialEq, Clone)]1755#[cfg_attr(1756feature = "serialize",1757derive(serde::Serialize, serde::Deserialize),1758reflect(Serialize, Deserialize)1759)]1760/// How many times to repeat a repeated grid track1761///1762/// <https://developer.mozilla.org/en-US/docs/Web/CSS/repeat>1763pub enum GridTrackRepetition {1764/// Repeat the track fixed number of times1765Count(u16),1766/// Repeat the track to fill available space1767///1768/// <https://developer.mozilla.org/en-US/docs/Web/CSS/repeat#auto-fill>1769AutoFill,1770/// Repeat the track to fill available space but collapse any tracks that do not end up with1771/// an item placed in them.1772///1773/// <https://developer.mozilla.org/en-US/docs/Web/CSS/repeat#auto-fit>1774AutoFit,1775}17761777impl Default for GridTrackRepetition {1778fn default() -> Self {1779Self::Count(1)1780}1781}17821783impl From<i32> for GridTrackRepetition {1784fn from(count: i32) -> Self {1785Self::Count(count as u16)1786}1787}17881789impl From<usize> for GridTrackRepetition {1790fn from(count: usize) -> Self {1791Self::Count(count as u16)1792}1793}17941795/// Represents a *possibly* repeated [`GridTrack`].1796///1797/// The repetition parameter can either be:1798/// - The integer `1`, in which case the track is non-repeated.1799/// - a `u16` count to repeat the track N times.1800/// - A `GridTrackRepetition::AutoFit` or `GridTrackRepetition::AutoFill`.1801///1802/// Note: that in the common case you want a non-repeating track (repetition count 1), you may use the constructor methods on [`GridTrack`]1803/// to create a `RepeatedGridTrack`. i.e. `GridTrack::px(10.0)` is equivalent to `RepeatedGridTrack::px(1, 10.0)`.1804///1805/// You may only use one auto-repetition per track list. And if your track list contains an auto repetition1806/// then all tracks (in and outside of the repetition) must be fixed size (px or percent). Integer repetitions are just shorthand for writing out1807/// N tracks longhand and are not subject to the same limitations.1808#[derive(Clone, PartialEq, Debug, Reflect)]1809#[reflect(Default, PartialEq, Clone)]1810#[cfg_attr(1811feature = "serialize",1812derive(serde::Serialize, serde::Deserialize),1813reflect(Serialize, Deserialize)1814)]1815pub struct RepeatedGridTrack {1816pub(crate) repetition: GridTrackRepetition,1817pub(crate) tracks: SmallVec<[GridTrack; 1]>,1818}18191820impl RepeatedGridTrack {1821/// Create a repeating set of grid tracks with a fixed pixel size1822pub fn px<T: From<Self>>(repetition: impl Into<GridTrackRepetition>, value: f32) -> T {1823Self {1824repetition: repetition.into(),1825tracks: SmallVec::from_buf([GridTrack::px(value)]),1826}1827.into()1828}18291830/// Create a repeating set of grid tracks with a percentage size1831pub fn percent<T: From<Self>>(repetition: impl Into<GridTrackRepetition>, value: f32) -> T {1832Self {1833repetition: repetition.into(),1834tracks: SmallVec::from_buf([GridTrack::percent(value)]),1835}1836.into()1837}18381839/// Create a repeating set of grid tracks with automatic size1840pub fn auto<T: From<Self>>(repetition: u16) -> T {1841Self {1842repetition: GridTrackRepetition::Count(repetition),1843tracks: SmallVec::from_buf([GridTrack::auto()]),1844}1845.into()1846}18471848/// Create a repeating set of grid tracks with an `fr` size.1849/// Note that this will give the track a content-based minimum size.1850/// Usually you are best off using `GridTrack::flex` instead which uses a zero minimum size.1851pub fn fr<T: From<Self>>(repetition: u16, value: f32) -> T {1852Self {1853repetition: GridTrackRepetition::Count(repetition),1854tracks: SmallVec::from_buf([GridTrack::fr(value)]),1855}1856.into()1857}18581859/// Create a repeating set of grid tracks with a `minmax(0, Nfr)` size.1860pub fn flex<T: From<Self>>(repetition: u16, value: f32) -> T {1861Self {1862repetition: GridTrackRepetition::Count(repetition),1863tracks: SmallVec::from_buf([GridTrack::flex(value)]),1864}1865.into()1866}18671868/// Create a repeating set of grid tracks with min-content size1869pub fn min_content<T: From<Self>>(repetition: u16) -> T {1870Self {1871repetition: GridTrackRepetition::Count(repetition),1872tracks: SmallVec::from_buf([GridTrack::min_content()]),1873}1874.into()1875}18761877/// Create a repeating set of grid tracks with max-content size1878pub fn max_content<T: From<Self>>(repetition: u16) -> T {1879Self {1880repetition: GridTrackRepetition::Count(repetition),1881tracks: SmallVec::from_buf([GridTrack::max_content()]),1882}1883.into()1884}18851886/// Create a repeating set of `fit-content()` grid tracks with fixed pixel limit1887pub fn fit_content_px<T: From<Self>>(repetition: u16, limit: f32) -> T {1888Self {1889repetition: GridTrackRepetition::Count(repetition),1890tracks: SmallVec::from_buf([GridTrack::fit_content_px(limit)]),1891}1892.into()1893}18941895/// Create a repeating set of `fit-content()` grid tracks with percentage limit1896pub fn fit_content_percent<T: From<Self>>(repetition: u16, limit: f32) -> T {1897Self {1898repetition: GridTrackRepetition::Count(repetition),1899tracks: SmallVec::from_buf([GridTrack::fit_content_percent(limit)]),1900}1901.into()1902}19031904/// Create a repeating set of `minmax()` grid track1905pub fn minmax<T: From<Self>>(1906repetition: impl Into<GridTrackRepetition>,1907min: MinTrackSizingFunction,1908max: MaxTrackSizingFunction,1909) -> T {1910Self {1911repetition: repetition.into(),1912tracks: SmallVec::from_buf([GridTrack::minmax(min, max)]),1913}1914.into()1915}19161917/// Create a repeating set of grid tracks with the percentage size of the viewport's smaller dimension1918pub fn vmin<T: From<Self>>(repetition: impl Into<GridTrackRepetition>, value: f32) -> T {1919Self {1920repetition: repetition.into(),1921tracks: SmallVec::from_buf([GridTrack::vmin(value)]),1922}1923.into()1924}19251926/// Create a repeating set of grid tracks with the percentage size of the viewport's larger dimension1927pub fn vmax<T: From<Self>>(repetition: impl Into<GridTrackRepetition>, value: f32) -> T {1928Self {1929repetition: repetition.into(),1930tracks: SmallVec::from_buf([GridTrack::vmax(value)]),1931}1932.into()1933}19341935/// Create a repeating set of grid tracks with the percentage size of the viewport's height dimension1936pub fn vh<T: From<Self>>(repetition: impl Into<GridTrackRepetition>, value: f32) -> T {1937Self {1938repetition: repetition.into(),1939tracks: SmallVec::from_buf([GridTrack::vh(value)]),1940}1941.into()1942}19431944/// Create a repeating set of grid tracks with the percentage size of the viewport's width dimension1945pub fn vw<T: From<Self>>(repetition: impl Into<GridTrackRepetition>, value: f32) -> T {1946Self {1947repetition: repetition.into(),1948tracks: SmallVec::from_buf([GridTrack::vw(value)]),1949}1950.into()1951}19521953/// Create a repetition of a set of tracks1954pub fn repeat_many<T: From<Self>>(1955repetition: impl Into<GridTrackRepetition>,1956tracks: impl Into<Vec<GridTrack>>,1957) -> T {1958Self {1959repetition: repetition.into(),1960tracks: SmallVec::from_vec(tracks.into()),1961}1962.into()1963}1964}19651966impl Default for RepeatedGridTrack {1967fn default() -> Self {1968Self {1969repetition: Default::default(),1970tracks: SmallVec::from_buf([GridTrack::default()]),1971}1972}1973}19741975impl From<GridTrack> for RepeatedGridTrack {1976fn from(track: GridTrack) -> Self {1977Self {1978repetition: GridTrackRepetition::Count(1),1979tracks: SmallVec::from_buf([track]),1980}1981}1982}19831984impl From<GridTrack> for Vec<GridTrack> {1985fn from(track: GridTrack) -> Self {1986vec![track]1987}1988}19891990impl From<GridTrack> for Vec<RepeatedGridTrack> {1991fn from(track: GridTrack) -> Self {1992vec![RepeatedGridTrack {1993repetition: GridTrackRepetition::Count(1),1994tracks: SmallVec::from_buf([track]),1995}]1996}1997}19981999impl From<RepeatedGridTrack> for Vec<RepeatedGridTrack> {2000fn from(track: RepeatedGridTrack) -> Self {2001vec![track]2002}2003}20042005#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]2006#[reflect(Default, PartialEq, Clone)]2007#[cfg_attr(2008feature = "serialize",2009derive(serde::Serialize, serde::Deserialize),2010reflect(Serialize, Deserialize)2011)]2012/// Represents the position of a grid item in a single axis.2013///2014/// There are 3 fields which may be set:2015/// - `start`: which grid line the item should start at2016/// - `end`: which grid line the item should end at2017/// - `span`: how many tracks the item should span2018///2019/// The default `span` is 1. If neither `start` or `end` is set then the item will be placed automatically.2020///2021/// Generally, at most two fields should be set. If all three fields are specified then `span` will be ignored. If `end` specifies an earlier2022/// grid line than `start` then `end` will be ignored and the item will have a span of 1.2023///2024/// <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Line-based_Placement_with_CSS_Grid>2025pub struct GridPlacement {2026/// The grid line at which the item should start.2027/// Lines are 1-indexed.2028/// Negative indexes count backwards from the end of the grid.2029/// Zero is not a valid index.2030pub(crate) start: Option<NonZero<i16>>,2031/// How many grid tracks the item should span.2032/// Defaults to 1.2033pub(crate) span: Option<NonZero<u16>>,2034/// The grid line at which the item should end.2035/// Lines are 1-indexed.2036/// Negative indexes count backwards from the end of the grid.2037/// Zero is not a valid index.2038pub(crate) end: Option<NonZero<i16>>,2039}20402041impl GridPlacement {2042pub const DEFAULT: Self = Self {2043start: None,2044span: NonZero::<u16>::new(1),2045end: None,2046};20472048/// Place the grid item automatically (letting the `span` default to `1`).2049pub fn auto() -> Self {2050Self::DEFAULT2051}20522053/// Place the grid item automatically, specifying how many tracks it should `span`.2054///2055/// # Panics2056///2057/// Panics if `span` is `0`.2058pub fn span(span: u16) -> Self {2059Self {2060start: None,2061end: None,2062span: try_into_grid_span(span).expect("Invalid span value of 0."),2063}2064}20652066/// Place the grid item specifying the `start` grid line (letting the `span` default to `1`).2067///2068/// # Panics2069///2070/// Panics if `start` is `0`.2071pub fn start(start: i16) -> Self {2072Self {2073start: try_into_grid_index(start).expect("Invalid start value of 0."),2074..Self::DEFAULT2075}2076}20772078/// Place the grid item specifying the `end` grid line (letting the `span` default to `1`).2079///2080/// # Panics2081///2082/// Panics if `end` is `0`.2083pub fn end(end: i16) -> Self {2084Self {2085end: try_into_grid_index(end).expect("Invalid end value of 0."),2086..Self::DEFAULT2087}2088}20892090/// Place the grid item specifying the `start` grid line and how many tracks it should `span`.2091///2092/// # Panics2093///2094/// Panics if `start` or `span` is `0`.2095pub fn start_span(start: i16, span: u16) -> Self {2096Self {2097start: try_into_grid_index(start).expect("Invalid start value of 0."),2098end: None,2099span: try_into_grid_span(span).expect("Invalid span value of 0."),2100}2101}21022103/// Place the grid item specifying `start` and `end` grid lines (`span` will be inferred)2104///2105/// # Panics2106///2107/// Panics if `start` or `end` is `0`.2108pub fn start_end(start: i16, end: i16) -> Self {2109Self {2110start: try_into_grid_index(start).expect("Invalid start value of 0."),2111end: try_into_grid_index(end).expect("Invalid end value of 0."),2112span: None,2113}2114}21152116/// Place the grid item specifying the `end` grid line and how many tracks it should `span`.2117///2118/// # Panics2119///2120/// Panics if `end` or `span` is `0`.2121pub fn end_span(end: i16, span: u16) -> Self {2122Self {2123start: None,2124end: try_into_grid_index(end).expect("Invalid end value of 0."),2125span: try_into_grid_span(span).expect("Invalid span value of 0."),2126}2127}21282129/// Mutate the item, setting the `start` grid line2130///2131/// # Panics2132///2133/// Panics if `start` is `0`.2134pub fn set_start(mut self, start: i16) -> Self {2135self.start = try_into_grid_index(start).expect("Invalid start value of 0.");2136self2137}21382139/// Mutate the item, setting the `end` grid line2140///2141/// # Panics2142///2143/// Panics if `end` is `0`.2144pub fn set_end(mut self, end: i16) -> Self {2145self.end = try_into_grid_index(end).expect("Invalid end value of 0.");2146self2147}21482149/// Mutate the item, setting the number of tracks the item should `span`2150///2151/// # Panics2152///2153/// Panics if `span` is `0`.2154pub fn set_span(mut self, span: u16) -> Self {2155self.span = try_into_grid_span(span).expect("Invalid span value of 0.");2156self2157}21582159/// Returns the grid line at which the item should start, or `None` if not set.2160pub fn get_start(self) -> Option<i16> {2161self.start.map(NonZero::<i16>::get)2162}21632164/// Returns the grid line at which the item should end, or `None` if not set.2165pub fn get_end(self) -> Option<i16> {2166self.end.map(NonZero::<i16>::get)2167}21682169/// Returns span for this grid item, or `None` if not set.2170pub fn get_span(self) -> Option<u16> {2171self.span.map(NonZero::<u16>::get)2172}2173}21742175impl Default for GridPlacement {2176fn default() -> Self {2177Self::DEFAULT2178}2179}21802181/// Convert an `i16` to `NonZero<i16>`, fails on `0` and returns the `InvalidZeroIndex` error.2182fn try_into_grid_index(index: i16) -> Result<Option<NonZero<i16>>, GridPlacementError> {2183Ok(Some(2184NonZero::<i16>::new(index).ok_or(GridPlacementError::InvalidZeroIndex)?,2185))2186}21872188/// Convert a `u16` to `NonZero<u16>`, fails on `0` and returns the `InvalidZeroSpan` error.2189fn try_into_grid_span(span: u16) -> Result<Option<NonZero<u16>>, GridPlacementError> {2190Ok(Some(2191NonZero::<u16>::new(span).ok_or(GridPlacementError::InvalidZeroSpan)?,2192))2193}21942195/// Errors that occur when setting constraints for a `GridPlacement`2196#[derive(Debug, Eq, PartialEq, Clone, Copy, Error)]2197pub enum GridPlacementError {2198#[error("Zero is not a valid grid position")]2199InvalidZeroIndex,2200#[error("Spans cannot be zero length")]2201InvalidZeroSpan,2202}22032204/// The background color of the node2205///2206/// This serves as the "fill" color.2207#[derive(Component, Copy, Clone, Debug, Deref, DerefMut, PartialEq, Reflect)]2208#[reflect(Component, Default, Debug, PartialEq, Clone)]2209#[cfg_attr(2210feature = "serialize",2211derive(serde::Serialize, serde::Deserialize),2212reflect(Serialize, Deserialize)2213)]2214pub struct BackgroundColor(pub Color);22152216impl BackgroundColor {2217/// Background color is transparent by default.2218pub const DEFAULT: Self = Self(Color::NONE);2219}22202221impl Default for BackgroundColor {2222fn default() -> Self {2223Self::DEFAULT2224}2225}22262227impl<T: Into<Color>> From<T> for BackgroundColor {2228fn from(color: T) -> Self {2229Self(color.into())2230}2231}22322233/// The border color of the UI node.2234#[derive(Component, Copy, Clone, Debug, PartialEq, Reflect)]2235#[reflect(Component, Default, Debug, PartialEq, Clone)]2236#[cfg_attr(2237feature = "serialize",2238derive(serde::Serialize, serde::Deserialize),2239reflect(Serialize, Deserialize)2240)]2241pub struct BorderColor {2242pub top: Color,2243pub right: Color,2244pub bottom: Color,2245pub left: Color,2246}22472248impl<T: Into<Color>> From<T> for BorderColor {2249fn from(color: T) -> Self {2250Self::all(color.into())2251}2252}22532254impl BorderColor {2255/// Border color is transparent by default.2256pub const DEFAULT: Self = BorderColor {2257top: Color::NONE,2258right: Color::NONE,2259bottom: Color::NONE,2260left: Color::NONE,2261};22622263/// Helper to create a `BorderColor` struct with all borders set to the given color2264#[inline]2265pub fn all(color: impl Into<Color>) -> Self {2266let color = color.into();2267Self {2268top: color,2269bottom: color,2270left: color,2271right: color,2272}2273}22742275/// Helper to set all border colors to a given color.2276pub fn set_all(&mut self, color: impl Into<Color>) -> &mut Self {2277let color: Color = color.into();2278self.top = color;2279self.bottom = color;2280self.left = color;2281self.right = color;2282self2283}22842285/// Check if all contained border colors are transparent2286pub fn is_fully_transparent(&self) -> bool {2287self.top.is_fully_transparent()2288&& self.bottom.is_fully_transparent()2289&& self.left.is_fully_transparent()2290&& self.right.is_fully_transparent()2291}2292}22932294impl Default for BorderColor {2295fn default() -> Self {2296Self::DEFAULT2297}2298}22992300#[derive(Component, Copy, Clone, Default, Debug, PartialEq, Reflect)]2301#[reflect(Component, Default, Debug, PartialEq, Clone)]2302#[cfg_attr(2303feature = "serialize",2304derive(serde::Serialize, serde::Deserialize),2305reflect(Serialize, Deserialize)2306)]2307/// The [`Outline`] component adds an outline outside the edge of a UI node.2308/// Outlines do not take up space in the layout.2309///2310/// To add an [`Outline`] to a ui node you can spawn a `(Node, Outline)` tuple bundle:2311/// ```2312/// # use bevy_ecs::prelude::*;2313/// # use bevy_ui::prelude::*;2314/// # use bevy_color::palettes::basic::{RED, BLUE};2315/// fn setup_ui(mut commands: Commands) {2316/// commands.spawn((2317/// Node {2318/// width: Val::Px(100.),2319/// height: Val::Px(100.),2320/// ..Default::default()2321/// },2322/// BackgroundColor(BLUE.into()),2323/// Outline::new(Val::Px(10.), Val::ZERO, RED.into())2324/// ));2325/// }2326/// ```2327///2328/// [`Outline`] components can also be added later to existing UI nodes:2329/// ```2330/// # use bevy_ecs::prelude::*;2331/// # use bevy_ui::prelude::*;2332/// # use bevy_color::Color;2333/// fn outline_hovered_button_system(2334/// mut commands: Commands,2335/// mut node_query: Query<(Entity, &Interaction, Option<&mut Outline>), Changed<Interaction>>,2336/// ) {2337/// for (entity, interaction, mut maybe_outline) in node_query.iter_mut() {2338/// let outline_color =2339/// if matches!(*interaction, Interaction::Hovered) {2340/// Color::WHITE2341/// } else {2342/// Color::NONE2343/// };2344/// if let Some(mut outline) = maybe_outline {2345/// outline.color = outline_color;2346/// } else {2347/// commands.entity(entity).insert(Outline::new(Val::Px(10.), Val::ZERO, outline_color));2348/// }2349/// }2350/// }2351/// ```2352/// Inserting and removing an [`Outline`] component repeatedly will result in table moves, so it is generally preferable to2353/// set `Outline::color` to [`Color::NONE`] to hide an outline.2354pub struct Outline {2355/// The width of the outline.2356///2357/// Percentage `Val` values are resolved based on the width of the outlined [`Node`].2358pub width: Val,2359/// The amount of space between a node's outline the edge of the node.2360///2361/// Percentage `Val` values are resolved based on the width of the outlined [`Node`].2362pub offset: Val,2363/// The color of the outline.2364///2365/// If you are frequently toggling outlines for a UI node on and off it is recommended to set [`Color::NONE`] to hide the outline.2366/// This avoids the table moves that would occur from the repeated insertion and removal of the `Outline` component.2367pub color: Color,2368}23692370impl Outline {2371/// Create a new outline2372pub const fn new(width: Val, offset: Val, color: Color) -> Self {2373Self {2374width,2375offset,2376color,2377}2378}2379}23802381/// The calculated clip of the node2382#[derive(Component, Default, Copy, Clone, Debug, Reflect)]2383#[reflect(Component, Default, Debug, Clone)]2384pub struct CalculatedClip {2385/// The rect of the clip2386pub clip: Rect,2387}23882389/// UI node entities with this component will ignore any clipping rect they inherit,2390/// the node will not be clipped regardless of its ancestors' `Overflow` setting.2391#[derive(Component)]2392pub struct OverrideClip;23932394#[expect(2395rustdoc::redundant_explicit_links,2396reason = "To go around the `<code>` limitations, we put the link twice so we're \2397sure it's recognized as a markdown link."2398)]2399/// Indicates that this [`Node`] entity's front-to-back ordering is not controlled solely2400/// by its location in the UI hierarchy. A node with a higher z-index will appear on top2401/// of sibling nodes with a lower z-index.2402///2403/// UI nodes that have the same z-index will appear according to the order in which they2404/// appear in the UI hierarchy. In such a case, the last node to be added to its parent2405/// will appear in front of its siblings.2406///2407/// Nodes without this component will be treated as if they had a value of2408/// <code>[ZIndex][ZIndex]\(0\)</code>.2409///2410/// Use [`GlobalZIndex`] if you need to order separate UI hierarchies or nodes that are2411/// not siblings in a given UI hierarchy.2412#[derive(Component, Copy, Clone, Debug, Default, PartialEq, Eq, Reflect)]2413#[reflect(Component, Default, Debug, PartialEq, Clone)]2414pub struct ZIndex(pub i32);24152416/// `GlobalZIndex` allows a [`Node`] entity anywhere in the UI hierarchy to escape the implicit draw ordering of the UI's layout tree and2417/// be rendered above or below other UI nodes.2418/// Nodes with a `GlobalZIndex` of greater than 0 will be drawn on top of nodes without a `GlobalZIndex` or nodes with a lower `GlobalZIndex`.2419/// Nodes with a `GlobalZIndex` of less than 0 will be drawn below nodes without a `GlobalZIndex` or nodes with a greater `GlobalZIndex`.2420///2421/// If two Nodes have the same `GlobalZIndex`, the node with the greater [`ZIndex`] will be drawn on top.2422#[derive(Component, Copy, Clone, Debug, Default, PartialEq, Eq, Reflect)]2423#[reflect(Component, Default, Debug, PartialEq, Clone)]2424pub struct GlobalZIndex(pub i32);24252426/// Used to add rounded corners to a UI node. You can set a UI node to have uniformly2427/// rounded corners or specify different radii for each corner. If a given radius exceeds half2428/// the length of the smallest dimension between the node's height or width, the radius will2429/// calculated as half the smallest dimension.2430///2431/// Elliptical nodes are not supported yet. Percentage values are based on the node's smallest2432/// dimension, either width or height.2433///2434/// # Example2435/// ```rust2436/// # use bevy_ecs::prelude::*;2437/// # use bevy_ui::prelude::*;2438/// # use bevy_color::palettes::basic::{BLUE};2439/// fn setup_ui(mut commands: Commands) {2440/// commands.spawn((2441/// Node {2442/// width: Val::Px(100.),2443/// height: Val::Px(100.),2444/// border: UiRect::all(Val::Px(2.)),2445/// border_radius: BorderRadius::new(2446/// // top left2447/// Val::Px(10.),2448/// // top right2449/// Val::Px(20.),2450/// // bottom right2451/// Val::Px(30.),2452/// // bottom left2453/// Val::Px(40.),2454/// ),2455/// ..Default::default()2456/// },2457/// BackgroundColor(BLUE.into()),2458/// ));2459/// }2460/// ```2461///2462/// <https://developer.mozilla.org/en-US/docs/Web/CSS/border-radius>2463#[derive(Copy, Clone, Debug, PartialEq, Reflect)]2464#[reflect(PartialEq, Default, Debug, Clone)]2465#[cfg_attr(2466feature = "serialize",2467derive(serde::Serialize, serde::Deserialize),2468reflect(Serialize, Deserialize)2469)]2470pub struct BorderRadius {2471pub top_left: Val,2472pub top_right: Val,2473pub bottom_right: Val,2474pub bottom_left: Val,2475}24762477impl Default for BorderRadius {2478fn default() -> Self {2479Self::DEFAULT2480}2481}24822483impl BorderRadius {2484pub const DEFAULT: Self = Self::ZERO;24852486/// Zero curvature. All the corners will be right-angled.2487pub const ZERO: Self = Self::all(Val::Px(0.));24882489/// Maximum curvature. The UI Node will take a capsule shape or circular if width and height are equal.2490pub const MAX: Self = Self::all(Val::Px(f32::MAX));24912492#[inline]2493/// Set all four corners to the same curvature.2494pub const fn all(radius: Val) -> Self {2495Self {2496top_left: radius,2497top_right: radius,2498bottom_left: radius,2499bottom_right: radius,2500}2501}25022503#[inline]2504pub const fn new(top_left: Val, top_right: Val, bottom_right: Val, bottom_left: Val) -> Self {2505Self {2506top_left,2507top_right,2508bottom_right,2509bottom_left,2510}2511}25122513#[inline]2514/// Sets the radii to logical pixel values.2515pub const fn px(top_left: f32, top_right: f32, bottom_right: f32, bottom_left: f32) -> Self {2516Self {2517top_left: Val::Px(top_left),2518top_right: Val::Px(top_right),2519bottom_right: Val::Px(bottom_right),2520bottom_left: Val::Px(bottom_left),2521}2522}25232524#[inline]2525/// Sets the radii to percentage values.2526pub const fn percent(2527top_left: f32,2528top_right: f32,2529bottom_right: f32,2530bottom_left: f32,2531) -> Self {2532Self {2533top_left: Val::Percent(top_left),2534top_right: Val::Percent(top_right),2535bottom_right: Val::Percent(bottom_right),2536bottom_left: Val::Percent(bottom_left),2537}2538}25392540#[inline]2541/// Sets the radius for the top left corner.2542/// Remaining corners will be right-angled.2543pub const fn top_left(radius: Val) -> Self {2544Self {2545top_left: radius,2546..Self::DEFAULT2547}2548}25492550#[inline]2551/// Sets the radius for the top right corner.2552/// Remaining corners will be right-angled.2553pub const fn top_right(radius: Val) -> Self {2554Self {2555top_right: radius,2556..Self::DEFAULT2557}2558}25592560#[inline]2561/// Sets the radius for the bottom right corner.2562/// Remaining corners will be right-angled.2563pub const fn bottom_right(radius: Val) -> Self {2564Self {2565bottom_right: radius,2566..Self::DEFAULT2567}2568}25692570#[inline]2571/// Sets the radius for the bottom left corner.2572/// Remaining corners will be right-angled.2573pub const fn bottom_left(radius: Val) -> Self {2574Self {2575bottom_left: radius,2576..Self::DEFAULT2577}2578}25792580#[inline]2581/// Sets the radii for the top left and bottom left corners.2582/// Remaining corners will be right-angled.2583pub const fn left(radius: Val) -> Self {2584Self {2585top_left: radius,2586bottom_left: radius,2587..Self::DEFAULT2588}2589}25902591#[inline]2592/// Sets the radii for the top right and bottom right corners.2593/// Remaining corners will be right-angled.2594pub const fn right(radius: Val) -> Self {2595Self {2596top_right: radius,2597bottom_right: radius,2598..Self::DEFAULT2599}2600}26012602#[inline]2603/// Sets the radii for the top left and top right corners.2604/// Remaining corners will be right-angled.2605pub const fn top(radius: Val) -> Self {2606Self {2607top_left: radius,2608top_right: radius,2609..Self::DEFAULT2610}2611}26122613#[inline]2614/// Sets the radii for the bottom left and bottom right corners.2615/// Remaining corners will be right-angled.2616pub const fn bottom(radius: Val) -> Self {2617Self {2618bottom_left: radius,2619bottom_right: radius,2620..Self::DEFAULT2621}2622}26232624/// Returns the [`BorderRadius`] with its `top_left` field set to the given value.2625#[inline]2626pub const fn with_top_left(mut self, radius: Val) -> Self {2627self.top_left = radius;2628self2629}26302631/// Returns the [`BorderRadius`] with its `top_right` field set to the given value.2632#[inline]2633pub const fn with_top_right(mut self, radius: Val) -> Self {2634self.top_right = radius;2635self2636}26372638/// Returns the [`BorderRadius`] with its `bottom_right` field set to the given value.2639#[inline]2640pub const fn with_bottom_right(mut self, radius: Val) -> Self {2641self.bottom_right = radius;2642self2643}26442645/// Returns the [`BorderRadius`] with its `bottom_left` field set to the given value.2646#[inline]2647pub const fn with_bottom_left(mut self, radius: Val) -> Self {2648self.bottom_left = radius;2649self2650}26512652/// Returns the [`BorderRadius`] with its `top_left` and `bottom_left` fields set to the given value.2653#[inline]2654pub const fn with_left(mut self, radius: Val) -> Self {2655self.top_left = radius;2656self.bottom_left = radius;2657self2658}26592660/// Returns the [`BorderRadius`] with its `top_right` and `bottom_right` fields set to the given value.2661#[inline]2662pub const fn with_right(mut self, radius: Val) -> Self {2663self.top_right = radius;2664self.bottom_right = radius;2665self2666}26672668/// Returns the [`BorderRadius`] with its `top_left` and `top_right` fields set to the given value.2669#[inline]2670pub const fn with_top(mut self, radius: Val) -> Self {2671self.top_left = radius;2672self.top_right = radius;2673self2674}26752676/// Returns the [`BorderRadius`] with its `bottom_left` and `bottom_right` fields set to the given value.2677#[inline]2678pub const fn with_bottom(mut self, radius: Val) -> Self {2679self.bottom_left = radius;2680self.bottom_right = radius;2681self2682}26832684/// Resolve the border radius for a single corner from the given context values.2685/// Returns the radius of the corner in physical pixels.2686pub const fn resolve_single_corner(2687radius: Val,2688scale_factor: f32,2689min_length: f32,2690viewport_size: Vec2,2691) -> f32 {2692if let Ok(radius) = radius.resolve(scale_factor, min_length, viewport_size) {2693radius.clamp(0., 0.5 * min_length)2694} else {26950.2696}2697}26982699/// Resolve the border radii for the corners from the given context values.2700/// Returns the radii of the each corner in physical pixels.2701pub const fn resolve(2702&self,2703scale_factor: f32,2704node_size: Vec2,2705viewport_size: Vec2,2706) -> ResolvedBorderRadius {2707let length = node_size.x.min(node_size.y);2708ResolvedBorderRadius {2709top_left: Self::resolve_single_corner(2710self.top_left,2711scale_factor,2712length,2713viewport_size,2714),2715top_right: Self::resolve_single_corner(2716self.top_right,2717scale_factor,2718length,2719viewport_size,2720),2721bottom_left: Self::resolve_single_corner(2722self.bottom_left,2723scale_factor,2724length,2725viewport_size,2726),2727bottom_right: Self::resolve_single_corner(2728self.bottom_right,2729scale_factor,2730length,2731viewport_size,2732),2733}2734}2735}27362737/// Represents the resolved border radius values for a UI node.2738///2739/// The values are in physical pixels.2740#[derive(Copy, Clone, Debug, Default, PartialEq, Reflect)]2741#[reflect(Clone, PartialEq, Default)]2742pub struct ResolvedBorderRadius {2743pub top_left: f32,2744pub top_right: f32,2745pub bottom_right: f32,2746pub bottom_left: f32,2747}27482749impl ResolvedBorderRadius {2750pub const ZERO: Self = Self {2751top_left: 0.,2752top_right: 0.,2753bottom_right: 0.,2754bottom_left: 0.,2755};2756}27572758impl From<ResolvedBorderRadius> for [f32; 4] {2759fn from(radius: ResolvedBorderRadius) -> Self {2760[2761radius.top_left,2762radius.top_right,2763radius.bottom_right,2764radius.bottom_left,2765]2766}2767}27682769#[derive(Component, Clone, Debug, Default, PartialEq, Reflect, Deref, DerefMut)]2770#[reflect(Component, PartialEq, Default, Clone)]2771#[cfg_attr(2772feature = "serialize",2773derive(serde::Serialize, serde::Deserialize),2774reflect(Serialize, Deserialize)2775)]2776/// List of shadows to draw for a [`Node`].2777///2778/// Draw order is determined implicitly from the vector of [`ShadowStyle`]s, back-to-front.2779pub struct BoxShadow(pub Vec<ShadowStyle>);27802781impl BoxShadow {2782/// A single drop shadow2783pub fn new(2784color: Color,2785x_offset: Val,2786y_offset: Val,2787spread_radius: Val,2788blur_radius: Val,2789) -> Self {2790Self(vec![ShadowStyle {2791color,2792x_offset,2793y_offset,2794spread_radius,2795blur_radius,2796}])2797}2798}27992800impl From<ShadowStyle> for BoxShadow {2801fn from(value: ShadowStyle) -> Self {2802Self(vec![value])2803}2804}28052806#[derive(Copy, Clone, Debug, PartialEq, Reflect)]2807#[reflect(PartialEq, Default, Clone)]2808#[cfg_attr(2809feature = "serialize",2810derive(serde::Serialize, serde::Deserialize),2811reflect(Serialize, Deserialize)2812)]2813pub struct ShadowStyle {2814/// The shadow's color2815pub color: Color,2816/// Horizontal offset2817pub x_offset: Val,2818/// Vertical offset2819pub y_offset: Val,2820/// How much the shadow should spread outward.2821///2822/// Negative values will make the shadow shrink inwards.2823/// Percentage values are based on the width of the UI node.2824pub spread_radius: Val,2825/// Blurriness of the shadow2826pub blur_radius: Val,2827}28282829impl Default for ShadowStyle {2830fn default() -> Self {2831Self {2832color: Color::BLACK,2833x_offset: Val::Percent(20.),2834y_offset: Val::Percent(20.),2835spread_radius: Val::ZERO,2836blur_radius: Val::Percent(10.),2837}2838}2839}28402841#[derive(Component, Copy, Clone, Debug, PartialEq, Reflect)]2842#[reflect(Component, Debug, PartialEq, Default, Clone)]2843#[cfg_attr(2844feature = "serialize",2845derive(serde::Serialize, serde::Deserialize),2846reflect(Serialize, Deserialize)2847)]2848/// This component can be added to any UI node to modify its layout behavior.2849pub struct LayoutConfig {2850/// If set to true the coordinates for this node and its descendents will be rounded to the nearest physical pixel.2851/// This can help prevent visual artifacts like blurry images or semi-transparent edges that can occur with sub-pixel positioning.2852///2853/// Defaults to true.2854pub use_rounding: bool,2855}28562857impl Default for LayoutConfig {2858fn default() -> Self {2859Self { use_rounding: true }2860}2861}28622863/// Indicates that this root [`Node`] entity should be rendered to a specific camera.2864///2865/// UI then will be laid out respecting the camera's viewport and scale factor, and2866/// rendered to this camera's [`bevy_camera::RenderTarget`].2867///2868/// Setting this component on a non-root node will have no effect. It will be overridden2869/// by the root node's component.2870///2871/// Root node's without an explicit [`UiTargetCamera`] will be rendered to the default UI camera,2872/// which is either a single camera with the [`IsDefaultUiCamera`] marker component or the highest2873/// order camera targeting the primary window.2874#[derive(Component, Clone, Debug, Reflect, Eq, PartialEq)]2875#[reflect(Component, Debug, PartialEq, Clone)]2876pub struct UiTargetCamera(pub Entity);28772878impl UiTargetCamera {2879pub fn entity(&self) -> Entity {2880self.02881}2882}28832884/// Marker used to identify default cameras, they will have priority over the [`PrimaryWindow`] camera.2885///2886/// This is useful if the [`PrimaryWindow`] has two cameras, one of them used2887/// just for debug purposes and the user wants a way to choose the default [`Camera`]2888/// without having to add a [`UiTargetCamera`] to the root node.2889///2890/// Another use is when the user wants the Ui to be in another window by default,2891/// all that is needed is to place this component on the camera2892///2893/// ```2894/// # use bevy_ui::prelude::*;2895/// # use bevy_ecs::prelude::Commands;2896/// # use bevy_camera::{Camera, Camera2d, RenderTarget};2897/// # use bevy_window::{Window, WindowRef};2898///2899/// fn spawn_camera(mut commands: Commands) {2900/// let another_window = commands.spawn(Window {2901/// title: String::from("Another window"),2902/// ..Default::default()2903/// }).id();2904/// commands.spawn((2905/// Camera2d,2906/// Camera {2907/// ..Default::default()2908/// },2909/// RenderTarget::Window(WindowRef::Entity(another_window)),2910/// // We add the Marker here so all Ui will spawn in2911/// // another window if no UiTargetCamera is specified2912/// IsDefaultUiCamera2913/// ));2914/// }2915/// ```2916#[derive(Component, Default)]2917pub struct IsDefaultUiCamera;29182919#[derive(SystemParam)]2920pub struct DefaultUiCamera<'w, 's> {2921cameras: Query<'w, 's, (Entity, &'static Camera, &'static RenderTarget)>,2922default_cameras: Query<'w, 's, Entity, (With<Camera>, With<IsDefaultUiCamera>)>,2923primary_window: Query<'w, 's, Entity, With<PrimaryWindow>>,2924}29252926impl<'w, 's> DefaultUiCamera<'w, 's> {2927pub fn get(&self) -> Option<Entity> {2928self.default_cameras.single().ok().or_else(|| {2929// If there isn't a single camera and the query isn't empty, there is two or more cameras queried.2930if !self.default_cameras.is_empty() {2931once!(warn!("Two or more Entities with IsDefaultUiCamera found when only one Camera with this marker is allowed."));2932}2933self.cameras2934.iter()2935.filter(|(_, _, render_target)| match render_target {2936RenderTarget::Window(WindowRef::Primary) => true,2937RenderTarget::Window(WindowRef::Entity(w)) => {2938self.primary_window.get(*w).is_ok()2939}2940_ => false,2941})2942.max_by_key(|(e, c, _)| (c.order, *e))2943.map(|(e, _, _)| e)2944})2945}2946}29472948/// Derived information about the camera target for this UI node.2949///2950/// Updated in [`UiSystems::Prepare`](crate::UiSystems::Prepare) by [`propagate_ui_target_cameras`](crate::update::propagate_ui_target_cameras)2951#[derive(Component, Clone, Copy, Debug, Reflect, PartialEq)]2952#[reflect(Component, Default, PartialEq, Clone)]2953pub struct ComputedUiTargetCamera {2954pub(crate) camera: Entity,2955}29562957impl Default for ComputedUiTargetCamera {2958fn default() -> Self {2959Self {2960camera: Entity::PLACEHOLDER,2961}2962}2963}29642965impl ComputedUiTargetCamera {2966/// Returns the id of the target camera for this UI node.2967pub fn get(&self) -> Option<Entity> {2968Some(self.camera).filter(|&entity| entity != Entity::PLACEHOLDER)2969}2970}29712972/// Derived information about the render target for this UI node.2973#[derive(Component, Clone, Copy, Debug, Reflect, PartialEq)]2974#[reflect(Component, Default, PartialEq, Clone)]2975pub struct ComputedUiRenderTargetInfo {2976/// The scale factor of the target camera's render target.2977pub(crate) scale_factor: f32,2978/// The size of the target camera's viewport in physical pixels.2979pub(crate) physical_size: UVec2,2980}29812982impl Default for ComputedUiRenderTargetInfo {2983fn default() -> Self {2984Self {2985scale_factor: 1.,2986physical_size: UVec2::ZERO,2987}2988}2989}29902991impl ComputedUiRenderTargetInfo {2992pub const fn scale_factor(&self) -> f32 {2993self.scale_factor2994}29952996/// Returns the size of the target camera's viewport in physical pixels.2997pub const fn physical_size(&self) -> UVec2 {2998self.physical_size2999}30003001/// Returns the size of the target camera's viewport in logical pixels.3002pub fn logical_size(&self) -> Vec2 {3003self.physical_size.as_vec2() / self.scale_factor3004}3005}30063007#[cfg(test)]3008mod tests {3009use crate::ComputedNode;3010use crate::GridPlacement;3011use bevy_math::{Rect, Vec2};3012use bevy_sprite::BorderRect;30133014#[test]3015fn invalid_grid_placement_values() {3016assert!(std::panic::catch_unwind(|| GridPlacement::span(0)).is_err());3017assert!(std::panic::catch_unwind(|| GridPlacement::start(0)).is_err());3018assert!(std::panic::catch_unwind(|| GridPlacement::end(0)).is_err());3019assert!(std::panic::catch_unwind(|| GridPlacement::start_end(0, 1)).is_err());3020assert!(std::panic::catch_unwind(|| GridPlacement::start_end(-1, 0)).is_err());3021assert!(std::panic::catch_unwind(|| GridPlacement::start_span(1, 0)).is_err());3022assert!(std::panic::catch_unwind(|| GridPlacement::start_span(0, 1)).is_err());3023assert!(std::panic::catch_unwind(|| GridPlacement::end_span(0, 1)).is_err());3024assert!(std::panic::catch_unwind(|| GridPlacement::end_span(1, 0)).is_err());3025assert!(std::panic::catch_unwind(|| GridPlacement::default().set_start(0)).is_err());3026assert!(std::panic::catch_unwind(|| GridPlacement::default().set_end(0)).is_err());3027assert!(std::panic::catch_unwind(|| GridPlacement::default().set_span(0)).is_err());3028}30293030#[test]3031fn grid_placement_accessors() {3032assert_eq!(GridPlacement::start(5).get_start(), Some(5));3033assert_eq!(GridPlacement::end(-4).get_end(), Some(-4));3034assert_eq!(GridPlacement::span(2).get_span(), Some(2));3035assert_eq!(GridPlacement::start_end(11, 21).get_span(), None);3036assert_eq!(GridPlacement::start_span(3, 5).get_end(), None);3037assert_eq!(GridPlacement::end_span(-4, 12).get_start(), None);3038}30393040#[test]3041fn computed_node_both_scrollbars() {3042let node = ComputedNode {3043size: Vec2::splat(100.),3044scrollbar_size: Vec2::splat(10.),3045content_size: Vec2::splat(100.),3046..Default::default()3047};30483049let (gutter, thumb) = node.horizontal_scrollbar().unwrap();3050assert_eq!(3051gutter,3052Rect {3053min: Vec2::new(-50., 40.),3054max: Vec2::new(40., 50.)3055}3056);3057assert_eq!(thumb, [-50., 31.]);30583059let (gutter, thumb) = node.vertical_scrollbar().unwrap();3060assert_eq!(3061gutter,3062Rect {3063min: Vec2::new(40., -50.),3064max: Vec2::new(50., 40.)3065}3066);3067assert_eq!(thumb, [-50., 31.]);3068}30693070#[test]3071fn computed_node_single_horizontal_scrollbar() {3072let mut node = ComputedNode {3073size: Vec2::splat(100.),3074scrollbar_size: Vec2::new(0., 10.),3075content_size: Vec2::new(200., 100.),3076scroll_position: Vec2::new(0., 0.),3077..Default::default()3078};30793080assert_eq!(None, node.vertical_scrollbar());30813082let (gutter, thumb) = node.horizontal_scrollbar().unwrap();3083assert_eq!(3084gutter,3085Rect {3086min: Vec2::new(-50., 40.),3087max: Vec2::new(50., 50.)3088}3089);3090assert_eq!(thumb, [-50., 0.]);30913092node.scroll_position.x += 100.;3093let (gutter, thumb) = node.horizontal_scrollbar().unwrap();3094assert_eq!(3095gutter,3096Rect {3097min: Vec2::new(-50., 40.),3098max: Vec2::new(50., 50.)3099}3100);3101assert_eq!(thumb, [0., 50.]);3102}31033104#[test]3105fn computed_node_single_vertical_scrollbar() {3106let mut node = ComputedNode {3107size: Vec2::splat(100.),3108scrollbar_size: Vec2::new(10., 0.),3109content_size: Vec2::new(100., 200.),3110scroll_position: Vec2::new(0., 0.),3111..Default::default()3112};31133114assert_eq!(None, node.horizontal_scrollbar());31153116let (gutter, thumb) = node.vertical_scrollbar().unwrap();3117assert_eq!(3118gutter,3119Rect {3120min: Vec2::new(40., -50.),3121max: Vec2::new(50., 50.)3122}3123);3124assert_eq!(thumb, [-50., 0.]);31253126node.scroll_position.y += 100.;3127let (gutter, thumb) = node.vertical_scrollbar().unwrap();3128assert_eq!(3129gutter,3130Rect {3131min: Vec2::new(40., -50.),3132max: Vec2::new(50., 50.)3133}3134);3135assert_eq!(thumb, [0., 50.]);3136}31373138#[test]3139fn border_box_is_centered_rect_of_node_size() {3140let node = ComputedNode {3141size: Vec2::new(100.0, 50.0),3142..Default::default()3143};3144let border_box = node.border_box();31453146assert_eq!(border_box.min, Vec2::new(-50.0, -25.0));3147assert_eq!(border_box.max, Vec2::new(50.0, 25.0));3148}31493150#[test]3151fn padding_box_subtracts_border_thickness() {3152let node = ComputedNode {3153size: Vec2::new(100.0, 60.0),3154border: BorderRect {3155min_inset: Vec2::new(5.0, 3.0),3156max_inset: Vec2::new(7.0, 9.0),3157},3158..Default::default()3159};3160let padding_box = node.padding_box();31613162assert_eq!(padding_box.min, Vec2::new(-50.0 + 5.0, -30.0 + 3.0));3163assert_eq!(padding_box.max, Vec2::new(50.0 - 7.0, 30.0 - 9.0));3164}31653166#[test]3167fn content_box_uses_content_inset() {3168let node = ComputedNode {3169size: Vec2::new(80.0, 40.0),3170padding: BorderRect {3171min_inset: Vec2::new(4.0, 2.0),3172max_inset: Vec2::new(6.0, 8.0),3173},3174..Default::default()3175};3176let content_box = node.content_box();31773178assert_eq!(content_box.min, Vec2::new(-40.0 + 4.0, -20.0 + 2.0));3179assert_eq!(content_box.max, Vec2::new(40.0 - 6.0, 20.0 - 8.0));3180}3181}318231833184