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}