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.
//! `AnyElement`: nodo del árbol UI alojado en la arena del frame.
//!
//! Los nodos viven en un `bumpalo::Bump` por hilo y se liberan en bloque con
//! `reset_frame_arena()`, evitando una asignación de heap por cada widget.

use crate::core::FrameArena;
use crate::view::context::ElementContext;
use crate::view::element::Element;
use std::cell::{Cell, RefCell};
use std::ptr::NonNull;
use taffy::NodeId;

thread_local! {
    static ARENA: RefCell<FrameArena> = RefCell::new(FrameArena::new());
    static LIVE_ELEMENTS: Cell<usize> = const { Cell::new(0) };
}

pub struct AnyElement {
    pointer: NonNull<dyn Element>,
}

impl AnyElement {
    pub fn new<E: Element>(element: E) -> Self {
        let pointer = ARENA.with(|arena| {
            let allocated: *mut E = arena.borrow().alloc(element);
            allocated as *mut dyn Element
        });
        LIVE_ELEMENTS.with(|live| live.set(live.get() + 1));
        // SAFETY: `alloc` nunca devuelve un puntero nulo.
        Self { pointer: unsafe { NonNull::new_unchecked(pointer) } }
    }

    fn element(&mut self) -> &mut dyn Element {
        // SAFETY: el puntero apunta a memoria de la arena, que solo se recicla
        // cuando no queda ningún `AnyElement` vivo (ver `reset_frame_arena`).
        unsafe { self.pointer.as_mut() }
    }

    pub fn request_layout(&mut self, cx: &mut ElementContext) -> NodeId {
        self.element().request_layout(cx)
    }

    pub fn prepaint(&mut self, cx: &mut ElementContext) {
        self.element().prepaint(cx);
    }

    pub fn paint(&mut self, cx: &mut ElementContext) {
        self.element().paint(cx);
    }

    pub fn z_index(&mut self) -> i16 {
        self.element().z_index()
    }
}

impl Drop for AnyElement {
    fn drop(&mut self) {
        // SAFETY: el valor se inicializó en `new` y se destruye una sola vez aquí.
        unsafe { std::ptr::drop_in_place(self.pointer.as_ptr()) };
        LIVE_ELEMENTS.with(|live| live.set(live.get().saturating_sub(1)));
    }
}

/// Recicla la memoria del frame. Si todavía hay elementos vivos no hace nada,
/// lo que mantiene válidos los punteros de esos elementos.
pub fn reset_frame_arena() {
    if LIVE_ELEMENTS.with(Cell::get) == 0 {
        ARENA.with(|arena| arena.borrow_mut().reset());
    }
}