aura-gui 0.3.5

UI nativa acelerada por GPU para escritorio, Android e iOS, en un solo crate: estilo tipo Tailwind, componentes, sombras, animaciones y agentes de IA. Reposo a cero. Licencia AGPL-3.0 o comercial.
//! Estilo declarativo. Todos los campos son opcionales para poder "refinar":
//! `hover` y `active` aplican un `Style` parcial encima del estilo base.

use crate::core::Pixels;
pub use crate::view::shadow::{BoxShadow, ShadowPreset, Shadows};
use crate::view::style_extra::StyleExtra;
use crate::view::theme::ColorSpec;

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Length {
    Px(Pixels),
    Percent(f32),
    Auto,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Direction {
    Row,
    Column,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Align {
    Start,
    Center,
    End,
    Stretch,
    Baseline,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Justify {
    Start,
    Center,
    End,
    SpaceBetween,
    SpaceAround,
    SpaceEvenly,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Overflow {
    Visible,
    Hidden,
    Scroll,
}

/// Valor opcional por lado, usado para padding, margen e inset.
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Sides {
    pub top: Option<Pixels>,
    pub right: Option<Pixels>,
    pub bottom: Option<Pixels>,
    pub left: Option<Pixels>,
}

impl Sides {
    pub(crate) fn refine(&mut self, other: &Sides) {
        self.top = other.top.or(self.top);
        self.right = other.right.or(self.right);
        self.bottom = other.bottom.or(self.bottom);
        self.left = other.left.or(self.left);
    }

    pub fn set_all(&mut self, value: Pixels) {
        *self = Sides { top: Some(value), right: Some(value), bottom: Some(value), left: Some(value) };
    }

    pub fn set_horizontal(&mut self, value: Pixels) {
        self.left = Some(value);
        self.right = Some(value);
    }

    pub fn set_vertical(&mut self, value: Pixels) {
        self.top = Some(value);
        self.bottom = Some(value);
    }
}

#[derive(Clone, Debug, Default, PartialEq)]
pub struct Style {
    pub direction: Option<Direction>,
    pub wrap: Option<bool>,
    pub align_items: Option<Align>,
    pub align_self: Option<Align>,
    pub justify_content: Option<Justify>,
    pub flex_grow: Option<f32>,
    pub flex_shrink: Option<f32>,
    pub flex_basis: Option<Length>,
    pub width: Option<Length>,
    pub height: Option<Length>,
    pub min_width: Option<Length>,
    pub min_height: Option<Length>,
    pub max_width: Option<Length>,
    pub max_height: Option<Length>,
    /// Ancho entre alto: con solo el ancho fijado, el alto sale de aquí (imágenes, diagramas).
    pub aspect_ratio: Option<f32>,
    pub padding: Sides,
    pub margin: Sides,
    pub inset: Sides,
    pub gap_x: Option<Pixels>,
    pub gap_y: Option<Pixels>,
    pub absolute: Option<bool>,
    pub hidden: Option<bool>,
    pub overflow_x: Option<Overflow>,
    pub overflow_y: Option<Overflow>,
    pub background: Option<ColorSpec>,
    pub border_color: Option<ColorSpec>,
    pub border_width: Option<Pixels>,
    pub radius: Option<Pixels>,
    pub text_color: Option<ColorSpec>,
    pub font_size: Option<Pixels>,
    pub font_bold: Option<bool>,
    pub italic: Option<bool>,
    pub underline: Option<bool>,
    /// `Some(false)` evita el ajuste de línea (`whitespace-nowrap`).
    pub text_wrap: Option<bool>,
    pub opacity: Option<f32>,
    /// Propiedades poco frecuentes (sombras, degradados, rejilla, transformaciones); solo ocupa memoria si se usa.
    pub extra: Option<Box<StyleExtra>>,
}

macro_rules! refine_options {
    ($self:ident, $other:ident, $($field:ident),* $(,)?) => {
        $( $self.$field = $other.$field.or($self.$field); )*
    };
}

pub(crate) use refine_options;

impl Style {
    /// Aplica encima solo los campos definidos en `other`.
    pub fn refine(&mut self, other: &Style) {
        refine_options!(
            self,
            other,
            direction,
            wrap,
            align_items,
            align_self,
            justify_content,
            flex_grow,
            flex_shrink,
            flex_basis,
            width,
            height,
            min_width,
            min_height,
            max_width,
            max_height,
            aspect_ratio,
            gap_x,
            gap_y,
            absolute,
            hidden,
            overflow_x,
            overflow_y,
            background,
            border_color,
            border_width,
            radius,
            text_color,
            font_size,
            font_bold,
            italic,
            underline,
            text_wrap,
            opacity,
        );
        match (&mut self.extra, &other.extra) {
            (_, None) => {}
            (Some(own), Some(refinement)) => own.refine(refinement),
            (own @ None, Some(refinement)) => *own = Some(refinement.clone()),
        }
        self.padding.refine(&other.padding);
        self.margin.refine(&other.margin);
        self.inset.refine(&other.inset);
    }

    /// Las propiedades poco frecuentes, creándolas la primera vez que se tocan.
    pub fn extra_mut(&mut self) -> &mut StyleExtra {
        self.extra.get_or_insert_with(Default::default)
    }

    pub fn shadows(&self) -> Option<&Shadows> {
        self.extra.as_deref()?.shadow.as_ref()
    }

    pub fn gradient(&self) -> Option<(f32, ColorSpec)> {
        self.extra.as_deref()?.gradient
    }

    pub fn is_scrollable_y(&self) -> bool {
        self.overflow_y == Some(Overflow::Scroll)
    }
}