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;

use super::{DraggableTabBar, measure_draggable_tab_bar};

pub fn reconcile_draggable_tab_bar(
    tree: &mut NodeTree,
    id: NodeId,
    bar: &DraggableTabBar,
    rect: Rect,
) -> NodeId {
    let (w, h) = measure_draggable_tab_bar(bar);

    let mut rect = rect;
    if matches!(bar.width, crate::style::Length::Auto) {
        rect.w = w.min(rect.w);
    }
    if matches!(bar.height, crate::style::Length::Auto) {
        rect.h = h.min(rect.h);
    }

    let (old_offset, old_override, old_previous_active, old_tabs) = match &tree.node(id).kind {
        NodeKind::DraggableTabBar(node) => (
            node.scroll_offset,
            node.scroll_override,
            node.previous_active,
            Some(node.tabs.clone()),
        ),
        _ => (bar.scroll_offset, None, bar.active, None),
    };

    let active_changed =
        !bar.tabs.is_empty() && bar.active < bar.tabs.len() && bar.active != old_previous_active;
    let viewport_w = rect.w.saturating_sub(bar.padding.horizontal()).max(1) as usize;
    let display_options = bar.display_options();
    let mut next_offset = old_override.unwrap_or(old_offset);
    if bar.tabs.is_empty() {
        next_offset = 0;
    } else if active_changed && bar.active < bar.tabs.len() {
        let still_visible = super::tab_fully_visible_at_offset(
            &bar.tabs,
            &display_options,
            bar.active,
            next_offset,
            viewport_w,
        );
        if !still_visible {
            next_offset = DraggableTabBar::scroll_offset_to_reveal_tab(
                &bar.tabs,
                &display_options,
                &super::TabViewportOptions {
                    scroll_offset: 0,
                    viewport_width: viewport_w,
                    show_overflow_controls: bar.show_overflow_controls,
                },
                bar.active,
            );
        }
    } else {
        let total_width =
            DraggableTabBar::content_width_for_viewport(&bar.tabs, &display_options, viewport_w);
        next_offset = next_offset.min(total_width.saturating_sub(1));
    }

    let mut next_node: crate::widgets::internal::DraggableTabBarNode = bar.clone().into();
    if let Some(old_tabs) = old_tabs.as_deref() {
        for (next_tab, old_tab) in std::sync::Arc::make_mut(&mut next_node.tabs)
            .iter_mut()
            .zip(old_tabs.iter())
        {
            let old_frame = old_tab
                .leading
                .as_ref()
                .and_then(|leading| leading.spinner_frame());
            if let Some(next_spinner) = next_tab
                .leading
                .as_mut()
                .and_then(|leading| leading.spinner_mut())
                && next_spinner.spinner.frame.is_none()
            {
                next_spinner.spinner.frame = old_frame;
            }
        }
    }
    next_node.previous_active = bar.active;
    next_node.scroll_offset = next_offset;
    next_node.scroll_override = old_override.map(|_| next_offset);

    let node = tree.node_mut(id);
    node.rect = rect;
    node.children.clear();
    node.kind = NodeKind::DraggableTabBar(next_node);
    id
}