use ratatui::buffer::Buffer as Surface;
use ratatui::layout::Rect;
use ratatui::text::{Line, Span};
use ratatui::widgets::Widget;
use unicode_width::UnicodeWidthStr;
use crate::theme::Theme;
#[derive(Debug, Clone, Copy)]
pub struct Tab<'a> {
pub name: &'a str,
pub dirty: bool,
}
pub struct TabBar<'a> {
pub tabs: &'a [Tab<'a>],
pub active: usize,
pub theme: &'a Theme,
}
impl TabBar<'_> {
fn label(tab: Tab<'_>) -> String {
if tab.dirty {
format!(" {} ● ", tab.name)
} else {
format!(" {} ", tab.name)
}
}
fn first_visible(&self, width: usize) -> usize {
let mut used = 0;
let mut first = self.active;
for index in (0..=self.active).rev() {
let needed = Self::label(self.tabs[index]).width();
if used + needed > width && index != self.active {
break;
}
used += needed;
first = index;
}
first
}
}
impl Widget for TabBar<'_> {
fn render(self, area: Rect, surface: &mut Surface) {
if area.is_empty() || self.tabs.is_empty() {
return;
}
surface.set_style(area, self.theme.tab_inactive);
let first = self.first_visible(area.width as usize);
let spans: Vec<Span<'_>> = self.tabs[first..]
.iter()
.enumerate()
.map(|(offset, tab)| {
let style = if first + offset == self.active {
self.theme.tab_active
} else {
self.theme.tab_inactive
};
Span::styled(Self::label(*tab), style)
})
.collect();
Line::from(spans).render(area, surface);
}
}