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