basalt_tui/
statusbar.rs

1use std::marker::PhantomData;
2
3use ratatui::{
4    buffer::Buffer,
5    layout::{Constraint, Flex, Layout, Rect},
6    style::{Color, Stylize},
7    text::{Line, Span, Text},
8    widgets::{StatefulWidgetRef, Widget},
9};
10
11#[derive(Default, Clone, PartialEq)]
12pub struct StatusBarState<'a> {
13    mode: &'a str,
14    meta: Option<&'a str>,
15    word_count: usize,
16    char_count: usize,
17}
18
19impl<'a> StatusBarState<'a> {
20    pub fn new(mode: &'a str, meta: Option<&'a str>, word_count: usize, char_count: usize) -> Self {
21        Self {
22            mode,
23            meta,
24            word_count,
25            char_count,
26        }
27    }
28}
29
30#[derive(Default)]
31pub struct StatusBar<'a> {
32    _lifetime: PhantomData<&'a ()>,
33}
34
35impl<'a> StatefulWidgetRef for StatusBar<'a> {
36    type State = StatusBarState<'a>;
37
38    fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
39        let [left, right] = Layout::horizontal([Constraint::Fill(1), Constraint::Length(28)])
40            .flex(Flex::SpaceBetween)
41            .areas(area);
42
43        let meta = state
44            .meta
45            .map(|meta| {
46                [
47                    Span::from(" ").bg(Color::Black),
48                    Span::from(meta).bg(Color::Black).gray().bold(),
49                    Span::from(" ").bg(Color::Black),
50                    Span::from("").black(),
51                ]
52            })
53            .unwrap_or_default();
54
55        Text::from(Line::from(
56            [
57                Span::from("").magenta(),
58                Span::from(" ").bg(Color::Magenta),
59                Span::from(state.mode).magenta().reversed().bold(),
60                Span::from(" ").bg(Color::Magenta),
61                Span::from("")
62                    .bg(if state.meta.is_some() {
63                        Color::Black
64                    } else {
65                        Color::default()
66                    })
67                    .magenta(),
68            ]
69            .into_iter()
70            .chain(meta)
71            .collect::<Vec<Span>>(),
72        ))
73        .render(left, buf);
74
75        let [word_count, char_count] =
76            Layout::horizontal([Constraint::Fill(1), Constraint::Fill(1)])
77                .flex(Flex::End)
78                .areas(right);
79
80        Text::from(format!("{} words", state.word_count))
81            .right_aligned()
82            .render(word_count, buf);
83
84        Text::from(format!("{} chars", state.char_count))
85            .right_aligned()
86            .render(char_count, buf);
87    }
88}