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.
//! Estado por frame de la ventana: árbol de layout, escena, hitboxes y despacho,
//! más el ratón y el texto heredado que consultan los elementos.

use crate::core::{Bounds, HitboxId, HitboxRegistry, Pixels, Point};
use crate::gpu::{Scene, TextTraits};
use crate::view::dispatch::DispatchTree;
use crate::view::interactivity::HandlerEntry;
use crate::view::overlay::DeferredOverlay;
use crate::view::style_extra::TextRefine;
use crate::view::text_layout::TextLeaf;
use crate::view::theme::{ColorSpec, Token};
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use std::rc::Rc;
use taffy::TaffyTree;

/// Datos que se conservan entre fases y se recrean cada frame.
pub struct FrameState {
    pub taffy: TaffyTree<TextLeaf>,
    pub scene: Scene,
    pub hitboxes: HitboxRegistry,
    pub handlers: Vec<HandlerEntry>,
    pub dispatch: DispatchTree,
    pub deferred: Vec<DeferredOverlay>,
    /// Rectángulo del cursor de texto enfocado, para colocar la ventana del IME.
    pub ime_area: Option<Bounds>,
    pub wants_animation_frame: bool,
    /// Elementos visibles para lectores de pantalla (solo se llena si `access_enabled`).
    pub access: Vec<crate::view::accessibility::AccessNode>,
    pub access_enabled: bool,
    /// Hitboxes de cada grupo con nombre, para `group_hover`.
    pub groups: FxHashMap<Rc<str>, SmallVec<[HitboxId; 2]>>,
}

impl Default for FrameState {
    fn default() -> Self {
        Self {
            taffy: TaffyTree::new(),
            scene: Scene::default(),
            hitboxes: HitboxRegistry::default(),
            handlers: Vec::new(),
            dispatch: DispatchTree::default(),
            deferred: Vec::new(),
            ime_area: None,
            wants_animation_frame: false,
            access: Vec::new(),
            access_enabled: false,
            groups: FxHashMap::default(),
        }
    }
}

impl FrameState {
    pub fn begin(&mut self) {
        self.taffy.clear();
        self.scene.clear();
        self.hitboxes.clear();
        self.handlers.clear();
        self.dispatch.clear();
        self.deferred.clear();
        self.ime_area = None;
        self.wants_animation_frame = false;
        self.access.clear();
        self.groups.clear();
    }
}

#[derive(Clone, Copy, Debug)]
pub struct MouseState {
    pub position: Point,
    pub pressed: Option<HitboxId>,
}

impl MouseState {
    /// Antes del primer evento el puntero no está sobre ningún elemento.
    pub const OUTSIDE: MouseState = MouseState { position: Point::new(Pixels(-1.0e6), Pixels(-1.0e6)), pressed: None };
}

/// Propiedades de texto que los descendientes heredan, como en CSS.
#[derive(Clone, Copy)]
pub struct InheritedText {
    pub color: ColorSpec,
    pub size: Pixels,
    pub bold: bool,
    pub italic: bool,
    pub underline: bool,
    pub wrap: bool,
    pub strike: bool,
    pub mono: bool,
    /// Interlineado como múltiplo del tamaño de letra.
    pub leading: f32,
    /// Peso, alineación, espaciado, recorte y capitalización.
    pub traits: TextTraits,
}

impl Default for InheritedText {
    fn default() -> Self {
        Self {
            color: Token::Text.into(),
            size: Pixels(14.0),
            bold: false,
            italic: false,
            underline: false,
            wrap: true,
            strike: false,
            mono: false,
            leading: DEFAULT_LEADING,
            traits: TextTraits::default(),
        }
    }
}

/// Interlineado por defecto (1,4 veces el tamaño de letra).
pub const DEFAULT_LEADING: f32 = 1.4;

impl InheritedText {
    /// Aplica encima los rasgos de texto de un estilo; un `truncate` o `line-clamp` obliga a ajustar al ancho.
    pub fn refine(&mut self, text: &TextRefine) {
        self.traits.weight = text.weight.unwrap_or(self.traits.weight);
        self.traits.align = text.align.or(self.traits.align);
        self.traits.tracking = text.tracking.unwrap_or(self.traits.tracking);
        self.traits.lines = text.lines.unwrap_or(self.traits.lines);
        self.traits.transform = text.transform.or(self.traits.transform);
        self.leading = text.leading.unwrap_or(self.leading);
        self.strike = text.strike.unwrap_or(self.strike);
        self.mono = text.mono.unwrap_or(self.mono);
        self.wrap |= self.traits.lines > 0;
    }

    /// Alto de línea en px lógicos.
    pub fn line_height(&self) -> f32 {
        (self.size.0 * self.leading).round()
    }
}