1use std::env;
2
3use ratatui::{
4 Frame,
5 layout::{Constraint, Layout, Rect},
6 style::{Color, Modifier, Style, Stylize},
7 text::{Line, Span},
8 widgets::{Block, BorderType, Borders, Clear, Padding, Paragraph, Tabs, TitlePosition},
9};
10
11use crate::tui::{ACCENT, ACCENT_TEXT, GRAY_BORDER_COLOR, Renderable, page::Page, state::TuiState};
12
13fn loading(frame: &mut Frame) {
14 let loading_block = Block::bordered();
15
16 let loading_span = vec!["Reloading data...".into()];
17
18 frame.render_widget(Clear, frame.area());
19 frame.render_widget(
20 Paragraph::new(loading_span).block(loading_block),
21 frame.area(),
22 );
23}
24
25pub fn render(frame: &mut Frame, state: &mut TuiState) {
26 if state.loading {
27 loading(frame);
28 return;
29 }
30
31 let chunks = Layout::vertical([Constraint::Length(3), Constraint::Min(0)]) .margin(2)
33 .split(frame.area());
34
35 render_tabs(frame, &state, chunks[0]);
36
37 let content_area = render_outer_frame(frame, chunks[1], state);
38
39 match state.active_page {
40 Page::Home => state.home.render(frame, content_area),
41 Page::Cadence => state.cadence.render(frame, content_area),
42 Page::Silo => state.silo.render(frame, content_area),
43 }
44}
45
46fn render_outer_frame(frame: &mut Frame, chunk: Rect, state: &TuiState) -> Rect {
47 let border = Block::bordered()
48 .title(format_keybinds(state))
49 .title_alignment(ratatui::layout::HorizontalAlignment::Center)
50 .title_position(TitlePosition::Bottom)
51 .border_type(BorderType::Plain)
52 .border_style(GRAY_BORDER_COLOR)
53 .padding(Padding::ZERO);
54
55 frame.render_widget(&border, chunk);
56 border.inner(chunk)
57}
58
59fn format_keybinds<'repo>(state: &'repo TuiState) -> Line<'repo> {
62 let key_bindings = state.get_binds();
63
64 Line::from(
65 key_bindings
66 .iter()
67 .enumerate()
68 .flat_map(|(i, (keys, desc))| {
69 vec![
70 "[".fg(Color::Rgb(100, 100, 100)),
71 (format!("{} ", keys)).fg(ACCENT), "→ ".fg(Color::Rgb(100, 100, 100)),
73 Span::from(*desc).fg(ACCENT_TEXT),
74 "]".fg(Color::Rgb(100, 100, 100)),
75 if i != key_bindings.len() - 1 { " " } else { "" }.into(),
76 ]
77 })
78 .collect::<Vec<Span>>(),
79 )
80}
81
82fn render_tabs(frame: &mut Frame, state: &TuiState, chunk: Rect) {
83 let title = format!(
84 "|< {} (v{}) >|",
85 env!("CARGO_PKG_NAME"),
86 env!("CARGO_PKG_VERSION")
87 );
88 let nav_block = Block::bordered()
89 .border_style(GRAY_BORDER_COLOR)
90 .title(title)
91 .title_style(Color::White)
92 .title_alignment(ratatui::layout::HorizontalAlignment::Center);
93
94 let nav_tabs = nav(state).block(nav_block);
95
96 frame.render_widget(nav_tabs, chunk);
97}
98
99pub fn nav(state: &TuiState) -> Tabs<'static> {
100 let tab_titles = Page::ALL.iter().map(|page| page.to_str());
101 let tabs = Tabs::new(tab_titles)
102 .select(state.active_page as usize)
103 .style(Style::default().fg(ACCENT_TEXT))
104 .highlight_style(Style::default().add_modifier(Modifier::BOLD).fg(ACCENT));
105
106 tabs
107}
108
109pub fn draw_placeholder(frame: &mut Frame, area: Rect, label: &str, color: Color) {
110 let placeholder = Block::default()
111 .borders(Borders::ALL)
112 .border_type(BorderType::Rounded)
113 .border_style(Style::default().fg(color))
114 .title(format!(" {} ({}x{}) ", label, area.width, area.height))
115 .title_style(Style::default().fg(color));
116
117 frame.render_widget(placeholder, area);
118}