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.
//! Hitboxes, foco, grupos (`group_hover`), accesibilidad y árbol de despacho en `ElementContext`.

use crate::core::{Bounds, FocusId, HitboxId, Pixels, Point, Size};
use crate::gpu::Affine;
use crate::view::context::ElementContext;
use crate::view::dispatch::DispatchNode;
use crate::view::interactivity::HandlerEntry;
use std::any::TypeId;
use std::rc::Rc;

impl ElementContext<'_> {
    pub fn insert_hitbox(&mut self, bounds: Bounds, blocks: bool) -> HitboxId {
        let Some(transform) = self.hit_transforms.last().filter(|transform| !transform.is_identity()) else {
            let clipped = self.mask().intersect(&bounds);
            return self.frame.hitboxes.insert(clipped, blocks);
        };
        // Dentro de un elemento transformado el hitbox es el rectángulo que envuelve a la figura transformada.
        let [x, y, width, height] =
            transform.bounds_of([bounds.origin.x.0, bounds.origin.y.0, bounds.size.width.0, bounds.size.height.0]);
        self.frame
            .hitboxes
            .insert(Bounds::new(Point::new(Pixels(x), Pixels(y)), Size::new(Pixels(width), Pixels(height))), blocks)
    }

    /// Hasta `pop_hit_transform`, los hitboxes se llevan a pantalla con `transform` (dentro de la vigente).
    pub fn push_hit_transform(&mut self, transform: Affine) {
        let current = self.hit_transforms.last().copied().unwrap_or_default();
        self.hit_transforms.push(transform.then(current));
    }

    pub fn pop_hit_transform(&mut self) {
        self.hit_transforms.pop();
    }

    // ---------- Hitboxes, foco y despacho ----------

    pub fn register_handlers(&mut self, entry: HandlerEntry) {
        self.frame.handlers.push(entry);
    }

    pub fn push_dispatch_node(&mut self, node: DispatchNode) {
        self.frame.dispatch.push(node);
    }

    pub fn pop_dispatch_node(&mut self) {
        self.frame.dispatch.pop();
    }

    pub fn is_hovered(&self, hitbox: HitboxId) -> bool {
        self.frame.hitboxes.is_hovered(hitbox, self.mouse.position)
    }

    /// `true` si hay un lector de pantalla pidiendo el árbol de accesibilidad.
    pub fn accessibility_enabled(&self) -> bool {
        self.frame.access_enabled
    }

    pub fn register_access(&mut self, node: crate::view::accessibility::AccessNode) {
        if self.frame.access_enabled {
            self.frame.access.push(node);
        }
    }

    pub fn register_group(&mut self, group: Rc<str>, hitbox: HitboxId) {
        self.frame.groups.entry(group).or_default().push(hitbox);
    }

    /// `true` si el puntero está sobre algún elemento del grupo (para `group_hover`).
    pub fn is_group_hovered(&self, group: &str) -> bool {
        self.frame.groups.get(group).is_some_and(|hitboxes| hitboxes.iter().any(|hitbox| self.is_hovered(*hitbox)))
    }

    pub fn active_drag_type(&self) -> Option<TypeId> {
        self.active_drag
    }

    pub fn is_pressed(&self, hitbox: HitboxId) -> bool {
        self.mouse.pressed == Some(hitbox) && self.is_hovered(hitbox)
    }

    pub fn is_focused(&self, focus: FocusId) -> bool {
        self.focused == Some(focus)
    }

    pub fn mouse_position(&self) -> Point {
        self.mouse.position
    }
}