nightshade 0.14.0

A cross-platform data-oriented game engine.
Documentation
use nalgebra_glm::{Vec2, Vec4};

use crate::ecs::text::components::{TextAlignment, VerticalAlignment};
use crate::ecs::ui::layout_types::{FlowLayout, GridLayout, UiLayoutType};
use crate::ecs::ui::state::STATE_COUNT;
use crate::ecs::ui::types::Rect;
use crate::ecs::ui::units::UiValue;
use crate::render::wgpu::passes::geometry::UiLayer;

use super::{AutoSizeMode, TextOverflow, UiDepthMode, UiResponsive};

#[derive(Clone, Debug)]
pub struct UiLayoutNode {
    pub base_layout: Option<UiLayoutType>,
    pub flow_layout: Option<FlowLayout>,
    pub responsive_flow: Option<crate::ecs::ui::layout_types::ResponsiveFlowOverride>,
    pub responsive: Option<UiResponsive>,
    pub grid_layout: Option<GridLayout>,
    pub depth: UiDepthMode,
    pub font_size: Option<f32>,
    pub clip_content: bool,
    pub pointer_events: bool,
    pub visible: bool,
    pub flow_child_size: Option<UiValue<Vec2>>,
    pub flex_grow: Option<f32>,
    pub flex_shrink: Option<f32>,
    pub min_size: Option<Vec2>,
    pub max_size: Option<Vec2>,
    pub z_index: Option<i32>,
    pub auto_size: AutoSizeMode,
    pub auto_size_padding: Vec2,
    pub computed_rect: Rect,
    pub computed_depth: f32,
    pub computed_clip_rect: Option<Rect>,
    pub layer: Option<UiLayer>,
    pub computed_layer: Option<UiLayer>,
    pub animation: Option<UiNodeAnimation>,
    pub scroll_offset: Vec2,
}

impl Default for UiLayoutNode {
    fn default() -> Self {
        Self {
            base_layout: None,
            flow_layout: None,
            responsive_flow: None,
            responsive: None,
            grid_layout: None,
            depth: UiDepthMode::default(),
            font_size: None,
            clip_content: false,
            pointer_events: false,
            visible: true,
            flow_child_size: None,
            flex_grow: None,
            flex_shrink: None,
            min_size: None,
            max_size: None,
            z_index: None,
            auto_size: AutoSizeMode::None,
            auto_size_padding: Vec2::new(0.0, 0.0),
            computed_rect: Rect::default(),
            computed_depth: 0.0,
            computed_clip_rect: None,
            layer: None,
            computed_layer: None,
            animation: None,
            scroll_offset: Vec2::new(0.0, 0.0),
        }
    }
}

impl UiLayoutNode {
    pub fn base_layout(&self) -> Option<&UiLayoutType> {
        self.base_layout.as_ref()
    }
}

#[derive(Clone, Copy, Debug)]
pub struct UiNodeColor {
    pub colors: [Option<Vec4>; STATE_COUNT],
    pub computed_color: Vec4,
}

impl Default for UiNodeColor {
    fn default() -> Self {
        Self {
            colors: [None; STATE_COUNT],
            computed_color: Vec4::new(1.0, 1.0, 1.0, 1.0),
        }
    }
}

#[derive(Clone, Copy, Debug, Default)]
pub enum UiNodeContent {
    #[default]
    None,
    Rect {
        corner_radius: f32,
        border_width: f32,
        border_color: Vec4,
    },
    Text {
        text_slot: usize,
        font_index: usize,
        font_size_override: Option<f32>,
        outline_color: Vec4,
        outline_width: f32,
        alignment: TextAlignment,
        vertical_alignment: VerticalAlignment,
        overflow: TextOverflow,
        monospace_width: Option<f32>,
        font_kind: crate::ecs::text::resources::font_engine::FontKind,
    },
    Image {
        texture_index: u32,
        uv_min: Vec2,
        uv_max: Vec2,
    },
}

/// Optional drop-shadow decorator on a node. Composes with any
/// [`UiNodeContent::Rect`]. The shadow is computed in the rect shader as a
/// second SDF pass behind the rect's fill.
#[derive(Clone, Copy, Debug, Default)]
pub struct UiNodeShadow {
    pub color: Vec4,
    pub offset: Vec2,
    pub blur: f32,
    pub spread: f32,
}

/// Optional fill effect applied to a [`UiNodeContent::Rect`]. Encoded as a
/// numeric kind plus four params consumed by the rect shader. Themes resolve
/// these from a [`crate::ecs::ui::components::ThemeEffect`] role each frame.
#[derive(Clone, Copy, Debug, Default)]
pub struct UiNodeEffect {
    pub kind: u32,
    pub params: [f32; 4],
}

/// Per-state shadow presets used to drive state-weight-based shadow blending.
/// Index by [`crate::ecs::ui::state::UiStateTrait::INDEX`]. Filled in by
/// [`crate::ecs::ui::systems::ui_theme_apply_system`] from a binding's
/// `shadow_roles`. The color-blend system reads this and writes the blended
/// result into the entity's [`UiNodeShadow`].
#[derive(Clone, Copy, Debug, Default)]
pub struct UiNodeShadowStates {
    pub shadows: [Option<UiNodeShadow>; crate::ecs::ui::state::STATE_COUNT],
}

/// Per-state geometric tweaks applied during render submission. None entries
/// contribute zero offset or unit scale. The transform-blend system folds
/// per-state values through `UiStateWeights` and writes the result into the
/// entity's [`UiNodeBlendedTransform`], which `render_sync` reads when
/// emitting rect instances.
#[derive(Clone, Copy, Debug, Default)]
pub struct UiNodeTransformStates {
    pub offsets: [Option<Vec2>; crate::ecs::ui::state::STATE_COUNT],
    pub scales: [Option<f32>; crate::ecs::ui::state::STATE_COUNT],
    pub radii: [Option<f32>; crate::ecs::ui::state::STATE_COUNT],
}

/// Frame-local blended transform produced by the transform-blend system from
/// [`UiNodeTransformStates`] and the entity's [`UiStateWeights`]. Read during
/// rect submission to apply hover-lift, press-compress, focus-grow, and
/// similar state-driven motion without invalidating layout.
#[derive(Clone, Copy, Debug)]
pub struct UiNodeBlendedTransform {
    pub offset: Vec2,
    pub scale: f32,
    pub radius_override: Option<f32>,
}

impl Default for UiNodeBlendedTransform {
    fn default() -> Self {
        Self {
            offset: Vec2::zeros(),
            scale: 1.0,
            radius_override: None,
        }
    }
}

/// Geometric transform applied during a node's intro or outro phase. The
/// alpha (Fade) effect is applied in `color_blend.rs`. Geometric transforms
/// (Slide*, Scale) are applied in `layout.rs`.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum UiAnimationType {
    Fade,
    SlideLeft,
    SlideRight,
    SlideUp,
    SlideDown,
    Scale,
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum UiAnimationPhase {
    Idle,
    IntroPlaying,
    OutroPlaying,
    OutroComplete,
}

/// One-shot intro/outro animation on a node. Cooperates with [`UiStateWeights`].
/// The `progress` field here drives the geometry transform, while node alpha
/// is multiplied at the color-blend stage.
///
/// For continuous state-driven blending (hover/pressed/etc.) use
/// `UiStateWeights` directly. For procedural animation, store state on
/// your own struct and write to node colors each frame.
#[derive(Clone, Copy, Debug)]
pub struct UiNodeAnimation {
    pub intro: Option<UiAnimationType>,
    pub outro: Option<UiAnimationType>,
    pub duration: f32,
    pub progress: f32,
    pub phase: UiAnimationPhase,
}