sql_cli/widgets/
tab_bar_widget.rs

1use ratatui::{
2    layout::Rect,
3    style::{Color, Modifier, Style},
4    text::{Line, Span},
5    widgets::{Block, Borders, Tabs},
6    Frame,
7};
8
9/// Renders a tab bar showing all open buffers
10pub struct TabBarWidget {
11    /// Current buffer index
12    current_index: usize,
13    /// List of buffer names  
14    buffer_names: Vec<String>,
15    /// Whether to show Alt+N shortcuts
16    show_shortcuts: bool,
17}
18
19impl TabBarWidget {
20    #[must_use]
21    pub fn new(current_index: usize, buffer_names: Vec<String>) -> Self {
22        Self {
23            current_index,
24            buffer_names,
25            show_shortcuts: true,
26        }
27    }
28
29    /// Set whether to show Alt+N shortcuts
30    #[must_use]
31    pub fn with_shortcuts(mut self, show: bool) -> Self {
32        self.show_shortcuts = show;
33        self
34    }
35
36    /// Render the tab bar
37    pub fn render(&self, f: &mut Frame, area: Rect) {
38        // Always render tab bar to maintain consistent layout
39        // Even with one buffer, showing the tab provides visual consistency
40
41        // Create tab titles with optional shortcuts
42        let titles: Vec<Line> = self
43            .buffer_names
44            .iter()
45            .enumerate()
46            .map(|(i, name)| {
47                let mut spans = vec![];
48
49                // Add shortcut indicator if enabled and within Alt+1-9 range
50                if self.show_shortcuts && i < 9 {
51                    spans.push(Span::styled(
52                        format!("{}:", i + 1),
53                        Style::default()
54                            .fg(Color::DarkGray)
55                            .add_modifier(Modifier::DIM),
56                    ));
57                }
58
59                // Truncate long buffer names
60                let display_name = if name.len() > 20 {
61                    format!("{}...", &name[..17])
62                } else {
63                    name.clone()
64                };
65
66                spans.push(Span::raw(display_name));
67                Line::from(spans)
68            })
69            .collect();
70
71        let tabs = Tabs::new(titles)
72            .block(
73                Block::default()
74                    .borders(Borders::BOTTOM)
75                    .border_style(Style::default().fg(Color::DarkGray)),
76            )
77            .select(self.current_index)
78            .style(Style::default().fg(Color::Gray))
79            .highlight_style(
80                Style::default()
81                    .fg(Color::White)
82                    .add_modifier(Modifier::BOLD)
83                    .bg(Color::DarkGray),
84            )
85            .divider(Span::styled(" │ ", Style::default().fg(Color::DarkGray)));
86
87        f.render_widget(tabs, area);
88    }
89
90    /// Calculate the height needed for the tab bar
91    #[must_use]
92    pub fn height(&self) -> u16 {
93        // Always reserve space for tab bar to maintain consistent layout
94        2 // Tab bar with border
95    }
96}
97
98/// Helper function to create and render a tab bar in one call
99pub fn render_tab_bar(f: &mut Frame, area: Rect, current_index: usize, buffer_names: Vec<String>) {
100    let widget = TabBarWidget::new(current_index, buffer_names);
101    widget.render(f, area);
102}