tui-lipan 0.1.0

Opinionated, component-based TUI framework for Rust - declarative components, reconciliation, layout engine, focus, overlays, and rich widgets on top of ratatui.
Documentation
use crate::core::node::{NodeId, NodeKind, NodeTree};
use crate::style::Rect;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) struct RenderOffset {
    x: i32,
    y: i32,
}

impl RenderOffset {
    pub(crate) const ZERO: Self = Self { x: 0, y: 0 };

    pub(crate) fn add_cells(self, (x, y): (i16, i16)) -> Self {
        Self {
            x: self.x.saturating_add(i32::from(x)),
            y: self.y.saturating_add(i32::from(y)),
        }
    }

    pub(crate) fn apply_to_rect(self, mut rect: Rect) -> Rect {
        rect.x = (i32::from(rect.x).saturating_add(self.x)).clamp(i16::MIN as i32, i16::MAX as i32)
            as i16;
        rect.y = (i32::from(rect.y).saturating_add(self.y)).clamp(i16::MIN as i32, i16::MAX as i32)
            as i16;
        rect
    }
}

pub(crate) fn translate_clip(clip: Option<Rect>, (dx, dy): (i16, i16)) -> Option<Rect> {
    if dx == 0 && dy == 0 {
        return clip;
    }
    clip.map(|c| Rect {
        x: (i32::from(c.x).saturating_add(i32::from(dx))).clamp(i16::MIN as i32, i16::MAX as i32)
            as i16,
        y: (i32::from(c.y).saturating_add(i32::from(dy))).clamp(i16::MIN as i32, i16::MAX as i32)
            as i16,
        w: c.w,
        h: c.h,
    })
}

pub(crate) fn render_offset_for_node(tree: &NodeTree, id: NodeId) -> RenderOffset {
    let mut chain = Vec::new();
    let mut current = Some(id);

    while let Some(node_id) = current {
        if !tree.is_valid(node_id) {
            break;
        }

        chain.push(node_id);

        if node_id == tree.root || tree.overlay_roots().iter().any(|root| root.id == node_id) {
            break;
        }

        current = tree.node(node_id).parent;
    }

    let mut offset = RenderOffset::ZERO;
    for node_id in chain.into_iter().rev() {
        if let NodeKind::Animated(animated) = &tree.node(node_id).kind {
            offset = offset.add_cells(animated.visual_position_offset_cells());
        }
    }

    offset
}