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 std::rc::Rc;

use crate::core::node::{NodeId, NodeKind};
use crate::layout::hash::grid_layout_hash;
use crate::layout::reconcile::{ElementReconcile, ReconcileCtx, reconcile_element};
use crate::style::Rect;
use crate::widgets::containers::reconcile::stack_reuse_plan;

use super::layout::layout_grid;
use super::node::GridLayoutCache;
use crate::widgets::Grid;

pub(crate) struct GridReconcile<'a> {
    pub parent: NodeId,
    pub old_children: Vec<NodeId>,
    pub grid: &'a Grid,
    pub bounds: Rect,
}

pub(crate) fn reconcile_grid(ctx: &mut ReconcileCtx<'_>, args: GridReconcile<'_>) -> Vec<NodeId> {
    let GridReconcile {
        parent,
        mut old_children,
        grid,
        bounds,
    } = args;
    let focus = ctx.focus;
    let child_refs: Vec<&crate::core::element::Element> =
        grid.items.iter().map(|i| &i.element).collect();
    let plan = stack_reuse_plan(ctx.tree, &old_children, &child_refs);
    old_children.clear();

    let layout_hash = grid_layout_hash(grid, bounds, focus);
    let rects = match layout_hash {
        Some(hash) => {
            let cached = {
                let node = ctx.tree.node(parent);
                if let NodeKind::Grid(node) = &node.kind {
                    node.layout_cache
                        .as_ref()
                        .filter(|cache| {
                            cache.bounds == bounds
                                && cache.layout_hash == hash
                                && cache.child_rects.len() == grid.items.len()
                        })
                        .map(|cache| Rc::clone(&cache.child_rects))
                } else {
                    None
                }
            };
            cached.unwrap_or_else(|| Rc::new(layout_grid(&grid.props, &grid.items, bounds)))
        }
        None => Rc::new(layout_grid(&grid.props, &grid.items, bounds)),
    };

    for (item, (reuse_id, rect)) in grid
        .items
        .iter()
        .zip(plan.into_iter().zip(rects.iter().copied()))
    {
        let child_id = reconcile_element(
            ctx,
            ElementReconcile {
                reuse: reuse_id,
                parent: Some(parent),
                el: &item.element,
                rect,
            },
        );
        old_children.push(child_id);
    }

    {
        let node = ctx.tree.node_mut(parent);
        if let NodeKind::Grid(node) = &mut node.kind {
            node.layout_cache = layout_hash.map(|hash| GridLayoutCache {
                bounds,
                layout_hash: hash,
                child_rects: Rc::clone(&rects),
            });
        }
    }

    old_children
}