Skip to main content

gitkit_cli/tui/
ui.rs

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)]) // 3 chars is enough to render the nav text
32        .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
59// keybind code from binsider
60// https://github.com/orhun/binsider/blob/ebbb36aeff178b42fb81911cc697173ec6d21972/src/tui/ui.rs#L174
61fn 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), // adds a space
72                    "→ ".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}