synd-term 0.3.2

terminal feed viewer
Documentation
use ratatui::{
    prelude::{Buffer, Constraint, Layout, Rect},
    text::Span,
    widgets::{Paragraph, Tabs as TuiTabs, Widget},
};

use crate::{
    application::{Direction, Features, IndexOutOfRange},
    ui::{icon, Context},
};

#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum Tab {
    Entries,
    Feeds,
    GitHub,
}

impl Tab {
    fn width(self) -> u16 {
        match self {
            Tab::Entries => 7,
            Tab::Feeds => 5,
            Tab::GitHub => 6,
        }
    }
}

pub struct Tabs {
    pub selected: usize,
    pub tabs: Vec<Tab>,
}

impl Tabs {
    const PADDING: &'static str = "    ";

    pub fn new(features: &'_ Features) -> Self {
        let mut tabs = vec![Tab::Entries, Tab::Feeds];
        if features.enable_github_notification {
            tabs.insert(0, Tab::GitHub);
        }
        Self { selected: 0, tabs }
    }

    pub fn current(&self) -> Tab {
        self.tabs[self.selected]
    }

    pub fn move_selection(&mut self, direction: Direction) -> Tab {
        self.selected = direction.apply(self.selected, self.tabs.len(), IndexOutOfRange::Wrapping);
        self.current()
    }

    fn width(&self) -> u16 {
        #[allow(clippy::cast_possible_truncation)]
        self.tabs.iter().fold(0, |width, tab| {
            width + tab.width() + (Self::PADDING.len() as u16) + 2
        })
    }
}

impl Tabs {
    pub fn render(&self, area: Rect, buf: &mut Buffer, cx: &Context<'_>) {
        let area = Rect {
            x: area.x + 2,
            width: area.width.saturating_sub(3),
            ..area
        };

        let horizontal = Layout::horizontal([Constraint::Min(0), Constraint::Length(self.width())]);
        let [title, tabs] = horizontal.areas(area);

        Paragraph::new(Span::styled("Syndicationd", cx.theme.application_title)).render(title, buf);

        TuiTabs::new(self.tabs.iter().map(|tab| match tab {
            Tab::Entries => concat!(icon!(entries), " Entries"),
            Tab::Feeds => concat!(icon!(feeds), " Feeds"),
            Tab::GitHub => concat!(icon!(github), " GitHub"),
        }))
        .style(cx.theme.tabs)
        .divider("")
        .padding(Self::PADDING, "")
        .select(self.selected)
        .highlight_style(cx.theme.tabs_selected)
        .render(tabs, buf);
    }
}