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::backend::ratatui_backend::common::{
    border_tabs_title_line, richtext_to_spans, to_ratatui_style_with_terminal_bg, truncate_spans,
};
use crate::style::{Align, Style};
use crate::widgets::internal::FrameProps;
use ratatui::text::{Line, Span};

pub(crate) fn build_header_line<'a>(
    props: &'a FrameProps,
    block_style: Style,
    active: bool,
    width: u16,
    h_char: &str,
    terminal_bg: Option<crate::style::Color>,
) -> Option<Line<'a>> {
    if props.has_header {
        return None;
    }

    let mut title_line: Option<Line> = None;
    let max_title_w = width.saturating_sub(2);

    if !props.tab_titles.is_empty() {
        let mut active_tab_style = block_style.patch(props.active_tab_style);
        let mut title_style = block_style.patch(props.title_style);
        if active && let Some(fts) = props.focus_title_style() {
            title_style = block_style.patch(fts);
        }
        if active && let Some(fts) = props.focus_active_tab_style() {
            active_tab_style = block_style.patch(fts);
        }
        let mut inactive_tab_style = block_style.patch(props.inactive_tab_style);
        if active && let Some(ifts) = props.focus_inactive_tab_style() {
            inactive_tab_style = block_style.patch(ifts);
        }
        title_line = Some(border_tabs_title_line(
            &props.tab_titles,
            props.active_tab,
            active_tab_style,
            inactive_tab_style,
            props.tab_variant,
            block_style,
            title_style,
        ));
    } else if let Some(t) = &props.title {
        let mut title_style = block_style.patch(props.title_style);
        if active && let Some(fts) = props.focus_title_style() {
            title_style = block_style.patch(fts);
        }
        let spans = richtext_to_spans(t, title_style);
        let spans = truncate_spans(spans, max_title_w);
        title_line = Some(Line::from(spans));
    }

    if let Some(prefix) = &props.title_prefix {
        let mut title_style = block_style.patch(props.title_style);
        if active && let Some(fts) = props.focus_title_style() {
            title_style = block_style.patch(fts);
        }
        let prefix_spans = richtext_to_spans(prefix, title_style);

        if let Some(mut line) = title_line {
            let sep_span = Span::styled(
                h_char.to_string(),
                to_ratatui_style_with_terminal_bg(block_style, terminal_bg),
            );

            for span in prefix_spans.into_iter().rev() {
                line.spans.insert(0, span);
            }
            line.spans.insert(prefix.spans.len(), sep_span);
            title_line = Some(line);
        } else {
            title_line = Some(Line::from(prefix_spans));
        }
    }

    if let Some(suffix) = &props.title_suffix {
        let mut title_style = block_style.patch(props.title_style);
        if active && let Some(fts) = props.focus_title_style() {
            title_style = block_style.patch(fts);
        }
        let suffix_spans = richtext_to_spans(suffix, title_style);

        if let Some(mut line) = title_line {
            let sep_span = Span::styled(
                h_char.to_string(),
                to_ratatui_style_with_terminal_bg(block_style, terminal_bg),
            );

            line.spans.push(sep_span);
            line.spans.extend(suffix_spans);
            title_line = Some(line);
        } else {
            title_line = Some(Line::from(suffix_spans));
        }
    }

    if let Some(mut line) = title_line {
        let p = props.header_padding;
        if p.left > 0 || p.right > 0 {
            let left_span = Span::styled(
                h_char.repeat(p.left as usize),
                to_ratatui_style_with_terminal_bg(block_style, terminal_bg),
            );
            let right_span = Span::styled(
                h_char.repeat(p.right as usize),
                to_ratatui_style_with_terminal_bg(block_style, terminal_bg),
            );
            if p.left > 0 {
                line.spans.insert(0, left_span);
            }
            if p.right > 0 {
                line.spans.push(right_span);
            }
        }

        let aligned = match props.title_alignment {
            Align::Start | Align::Stretch => line.left_aligned(),
            Align::Center => line.centered(),
            Align::End => line.right_aligned(),
        };
        Some(aligned)
    } else {
        None
    }
}