aura-gui 0.2.2

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 registrados en la fase Prepaint para resolver hover y clics.

use crate::core::geometry::{Bounds, Point};

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct HitboxId(pub u32);

#[derive(Clone, Copy, Debug)]
pub struct Hitbox {
    pub id: HitboxId,
    pub bounds: Bounds,
    /// Un hitbox bloqueante (modal, paleta de comandos) oculta todo lo que tiene debajo.
    pub blocks: bool,
}

#[derive(Default)]
pub struct HitboxRegistry {
    hitboxes: Vec<Hitbox>,
}

impl HitboxRegistry {
    pub fn clear(&mut self) {
        self.hitboxes.clear();
    }

    pub fn insert(&mut self, bounds: Bounds, blocks: bool) -> HitboxId {
        let id = HitboxId(self.hitboxes.len() as u32);
        self.hitboxes.push(Hitbox { id, bounds, blocks });
        id
    }

    /// Hitboxes bajo `point`, del más alto al más bajo, hasta el primero bloqueante.
    pub fn hits_at(&self, point: Point) -> impl Iterator<Item = HitboxId> + '_ {
        let mut blocked = false;
        self.hitboxes.iter().rev().filter_map(move |hitbox| {
            if blocked || !hitbox.bounds.contains(point) {
                return None;
            }
            blocked = hitbox.blocks;
            Some(hitbox.id)
        })
    }

    pub fn bounds(&self, id: HitboxId) -> Option<Bounds> {
        self.hitboxes.get(id.0 as usize).map(|hitbox| hitbox.bounds)
    }

    pub fn topmost_at(&self, point: Point) -> Option<HitboxId> {
        self.hits_at(point).next()
    }

    /// Semántica de CSS `:hover`: también se activa el ancestro si el cursor está sobre un hijo.
    pub fn is_hovered(&self, id: HitboxId, point: Point) -> bool {
        self.hits_at(point).any(|hit| hit == id)
    }
}