bevy_ui 0.18.1

A custom ECS-driven UI framework built specifically for Bevy Engine
Documentation
use crate::Val;
use bevy_derive::Deref;
use bevy_ecs::component::Component;
use bevy_ecs::prelude::ReflectComponent;
use bevy_math::Affine2;
use bevy_math::Mat2;
use bevy_math::Rot2;
use bevy_math::Vec2;
use bevy_reflect::prelude::*;
use core::ops::Mul;

/// A pair of [`Val`]s used to represent a 2-dimensional size or offset.
#[derive(Debug, PartialEq, Clone, Copy, Reflect)]
#[reflect(Default, PartialEq, Debug, Clone)]
#[cfg_attr(
    feature = "serialize",
    derive(serde::Serialize, serde::Deserialize),
    reflect(Serialize, Deserialize)
)]
pub struct Val2 {
    /// Translate the node along the x-axis.
    /// `Val::Percent` values are resolved based on the computed width of the Ui Node.
    /// `Val::Auto` is resolved to `0.`.
    pub x: Val,
    /// Translate the node along the y-axis.
    /// `Val::Percent` values are resolved based on the computed height of the UI Node.
    /// `Val::Auto` is resolved to `0.`.
    pub y: Val,
}

impl Val2 {
    pub const ZERO: Self = Self {
        x: Val::ZERO,
        y: Val::ZERO,
    };

    /// Creates a new [`Val2`] where both components are in logical pixels
    pub const fn px(x: f32, y: f32) -> Self {
        Self {
            x: Val::Px(x),
            y: Val::Px(y),
        }
    }

    /// Creates a new [`Val2`] where both components are percentage values
    pub const fn percent(x: f32, y: f32) -> Self {
        Self {
            x: Val::Percent(x),
            y: Val::Percent(y),
        }
    }

    /// Creates a new [`Val2`]
    pub const fn new(x: Val, y: Val) -> Self {
        Self { x, y }
    }

    /// Resolves this [`Val2`] from the given `scale_factor`, `parent_size`,
    /// and `viewport_size`.
    ///
    /// Component values of [`Val::Auto`] are resolved to 0.
    pub fn resolve(&self, scale_factor: f32, base_size: Vec2, viewport_size: Vec2) -> Vec2 {
        Vec2::new(
            self.x
                .resolve(scale_factor, base_size.x, viewport_size)
                .unwrap_or(0.),
            self.y
                .resolve(scale_factor, base_size.y, viewport_size)
                .unwrap_or(0.),
        )
    }
}

impl Default for Val2 {
    fn default() -> Self {
        Self::ZERO
    }
}

/// Relative 2D transform for UI nodes
///
/// [`UiGlobalTransform`] is automatically inserted whenever [`UiTransform`] is inserted.
#[derive(Component, Debug, PartialEq, Clone, Copy, Reflect)]
#[reflect(Component, Default, PartialEq, Debug, Clone)]
#[cfg_attr(
    feature = "serialize",
    derive(serde::Serialize, serde::Deserialize),
    reflect(Serialize, Deserialize)
)]
#[require(UiGlobalTransform)]
pub struct UiTransform {
    /// Translate the node.
    pub translation: Val2,
    /// Scale the node. A negative value reflects the node in that axis.
    pub scale: Vec2,
    /// Rotate the node clockwise.
    pub rotation: Rot2,
}

impl UiTransform {
    pub const IDENTITY: Self = Self {
        translation: Val2::ZERO,
        scale: Vec2::ONE,
        rotation: Rot2::IDENTITY,
    };

    /// Creates a UI transform representing a rotation.
    pub const fn from_rotation(rotation: Rot2) -> Self {
        Self {
            rotation,
            ..Self::IDENTITY
        }
    }

    /// Creates a UI transform representing a responsive translation.
    pub const fn from_translation(translation: Val2) -> Self {
        Self {
            translation,
            ..Self::IDENTITY
        }
    }

    /// Creates a UI transform representing a scaling.
    pub const fn from_scale(scale: Vec2) -> Self {
        Self {
            scale,
            ..Self::IDENTITY
        }
    }

    /// Resolves the translation from the given `scale_factor`, `base_value`, and `target_size`
    /// and returns a 2d affine transform from the resolved translation, and the `UiTransform`'s rotation, and scale.
    pub fn compute_affine(&self, scale_factor: f32, base_size: Vec2, target_size: Vec2) -> Affine2 {
        Affine2::from_mat2_translation(
            Mat2::from(self.rotation) * Mat2::from_diagonal(self.scale),
            self.translation
                .resolve(scale_factor, base_size, target_size),
        )
    }
}

impl Default for UiTransform {
    fn default() -> Self {
        Self::IDENTITY
    }
}

/// Absolute 2D transform for UI nodes
///
/// [`UiGlobalTransform`]s are updated from [`UiTransform`] and [`Node`](crate::ui_node::Node)
///  in [`ui_layout_system`](crate::layout::ui_layout_system)
#[derive(Component, Debug, PartialEq, Clone, Copy, Reflect, Deref)]
#[reflect(Component, Default, PartialEq, Debug, Clone)]
#[cfg_attr(
    feature = "serialize",
    derive(serde::Serialize, serde::Deserialize),
    reflect(Serialize, Deserialize)
)]
pub struct UiGlobalTransform(Affine2);

impl Default for UiGlobalTransform {
    fn default() -> Self {
        Self(Affine2::IDENTITY)
    }
}

impl UiGlobalTransform {
    /// If the transform is invertible returns its inverse.
    /// Otherwise returns `None`.
    #[inline]
    pub fn try_inverse(&self) -> Option<Affine2> {
        (self.matrix2.determinant() != 0.).then_some(self.inverse())
    }

    /// Creates a `UiGlobalTransform` from the given 2D translation.
    #[inline]
    pub fn from_translation(translation: Vec2) -> Self {
        Self(Affine2::from_translation(translation))
    }

    /// Creates a `UiGlobalTransform` from the given 2D translation.
    #[inline]
    pub fn from_xy(x: f32, y: f32) -> Self {
        Self::from_translation(Vec2::new(x, y))
    }

    /// Creates a `UiGlobalTransform` from the given rotation.
    #[inline]
    pub fn from_rotation(rotation: Rot2) -> Self {
        Self(Affine2::from_mat2(rotation.into()))
    }

    /// Creates a `UiGlobalTransform` from the given scaling.
    #[inline]
    pub fn from_scale(scale: Vec2) -> Self {
        Self(Affine2::from_scale(scale))
    }

    /// Extracts scale, angle and translation from self.
    /// The transform is expected to be non-degenerate and without shearing, or the output will be invalid.
    #[inline]
    pub fn to_scale_angle_translation(&self) -> (Vec2, f32, Vec2) {
        self.0.to_scale_angle_translation()
    }

    /// Returns the transform as an [`Affine2`]
    #[inline]
    pub fn affine(&self) -> Affine2 {
        self.0
    }
}

impl From<Affine2> for UiGlobalTransform {
    fn from(value: Affine2) -> Self {
        Self(value)
    }
}

impl From<UiGlobalTransform> for Affine2 {
    fn from(value: UiGlobalTransform) -> Self {
        value.0
    }
}

impl From<&UiGlobalTransform> for Affine2 {
    fn from(value: &UiGlobalTransform) -> Self {
        value.0
    }
}

impl Mul for UiGlobalTransform {
    type Output = Self;

    #[inline]
    fn mul(self, value: Self) -> Self::Output {
        Self(self.0 * value.0)
    }
}

impl Mul<Affine2> for UiGlobalTransform {
    type Output = Affine2;

    #[inline]
    fn mul(self, affine2: Affine2) -> Self::Output {
        self.0 * affine2
    }
}

impl Mul<UiGlobalTransform> for Affine2 {
    type Output = Affine2;

    #[inline]
    fn mul(self, transform: UiGlobalTransform) -> Self::Output {
        self * transform.0
    }
}

impl Mul<Vec2> for UiGlobalTransform {
    type Output = Vec2;

    #[inline]
    fn mul(self, value: Vec2) -> Vec2 {
        self.transform_point2(value)
    }
}