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::component::FocusContext;
use crate::core::element::{Element, ElementKind};
use crate::core::node::{NodeId, NodeKind, NodeTree};
use crate::layout::reconcile::{
    OverlayState, ReconcileCtx, SingleChildReconcile, reconcile_single_child,
};
use crate::style::Rect;

use super::{EffectScope, EffectScopeNode};

pub(crate) fn reconcile_effect_scope(
    tree: &mut NodeTree,
    epoch: u32,
    id: NodeId,
    scope: &EffectScope,
    rect: Rect,
    focus: Option<&FocusContext>,
    overlay_state: &mut OverlayState,
) -> NodeId {
    let scoped_portal_child = effect_scoped_portal_child(scope);
    let child = scoped_portal_child.as_ref().or(scope.child.as_deref());
    let effects = if scoped_portal_child.is_some() {
        Vec::new()
    } else {
        scope.effects.clone()
    };

    let old_children = {
        let node = tree.node_mut(id);
        node.rect = rect;
        node.kind = NodeKind::EffectScope(EffectScopeNode { effects });
        std::mem::take(&mut node.children)
    };

    let new_children = reconcile_single_child(
        &mut ReconcileCtx {
            tree,
            epoch,
            focus,
            overlay_state,
        },
        SingleChildReconcile {
            parent_id: id,
            child,
            rect,
            old_children,
        },
    );

    let node = tree.node_mut(id);
    node.children = new_children;

    id
}

fn effect_scoped_portal_child(scope: &EffectScope) -> Option<Element> {
    if scope.effects.is_empty() {
        return None;
    }

    let child = scope.child.as_deref()?;
    effect_scoped_portal_element(child, scope)
}

fn effect_scoped_portal_element(element: &Element, scope: &EffectScope) -> Option<Element> {
    match &element.kind {
        ElementKind::Portal(portal) => {
            let mut portal = portal.clone();
            portal.content = Box::new(
                EffectScope {
                    child: Some(portal.content.clone()),
                    effects: scope.effects.clone(),
                }
                .into(),
            );

            Some(copy_element_metadata(
                Element::new(ElementKind::Portal(portal)),
                element,
            ))
        }
        ElementKind::Group(group) => {
            let scoped_child = effect_scoped_portal_element(group.child.as_ref(), scope)?;
            let mut group = group.clone();
            group.child = Box::new(scoped_child);

            Some(copy_element_metadata(
                Element::new(ElementKind::Group(group)),
                element,
            ))
        }
        _ => None,
    }
}

fn copy_element_metadata(mut element: Element, source: &Element) -> Element {
    element.layout = source.layout;
    element.key = source.key.clone();
    element
}