Skip to main content

ez_tui/components/mock/
indicator.rs

1use crate::types::palette::EzPalette;
2use crate::{AttrValue, Attribute, MockComponent, MockProps, Props, Theme};
3use eztui_derive::MockProps;
4use ratatui::buffer::Buffer;
5use ratatui::layout::Rect;
6use ratatui::prelude::Alignment;
7use ratatui::style::Style;
8use ratatui::text::Line;
9use ratatui::widgets::{Paragraph, Widget};
10use std::fmt::Debug;
11
12/// A mock component to display a colored indicator inside the TUI. The indicator is a simple box alternating its style from a given set of [`Style`] when [`Self::cycle`] is called.
13#[derive(Debug, MockProps)]
14pub struct IndicatorMock {
15    props: Props,
16    styles: Vec<Style>,
17    current_style: usize,
18}
19
20impl IndicatorMock {
21    /// Create a new [`IndicatorMock`] component with  two styles derived from one [`Palette`].
22    #[must_use]
23    pub fn with_palette(name: String, palette: &EzPalette) -> Self {
24        let style1 = Style::new().fg(palette.c900).bg(palette.c100);
25        let style2 = Style::new().fg(palette.c100).bg(palette.c900);
26        Self::init(name, style1, style2)
27    }
28    /// Create a new [`IndicatorMock`] component with two styles derived from two [`Palette`].
29    #[must_use]
30    pub fn with_palettes(name: String, palette_a: &EzPalette, palette_b: &EzPalette) -> Self {
31        let style1 = Style::new().fg(palette_a.c100).bg(palette_a.c900);
32        let style2 = Style::new().fg(palette_b.c900).bg(palette_b.c100);
33        Self::init(name, style1, style2)
34    }
35
36    fn init(name: String, style1: Style, style2: Style) -> Self {
37        let mut cpt = IndicatorMock {
38            props: Props::default(),
39            styles: vec![style1, style2],
40            current_style: 0,
41        };
42        cpt.props_mut().expect("self has props").set(
43            Attribute::Title,
44            AttrValue::Title((name, Alignment::Center)),
45        );
46        let style = cpt.styles[0];
47        cpt.props_mut()
48            .expect("self has props")
49            .set(Attribute::Style, AttrValue::Style(style));
50        cpt
51    }
52
53    /// Cycle the indicator style.
54    pub fn cycle(&mut self) {
55        let new = self.current_style.wrapping_add(1);
56        self.current_style = if new >= self.styles.len() { 0 } else { new };
57        if let Some(style) = self.styles.get(self.current_style) {
58            let x = *style;
59            if let Some(props) = self.props_mut() {
60                props.set(Attribute::Style, AttrValue::Style(x));
61            }
62        }
63    }
64}
65
66impl MockComponent for IndicatorMock {
67    fn draw(&mut self, area: Rect, buf: &mut Buffer, _: &Theme) {
68        let (label, alignement) = self
69            .props()
70            .expect("self has props")
71            .get(Attribute::Title)
72            .and_then(|o| o.as_title().cloned())
73            .map_or((String::new(), Alignment::Center), |(title, align)| {
74                (format!("  {title}  "), align)
75            });
76        let style = self
77            .props()
78            .expect("self has props")
79            .get(Attribute::Style)
80            .and_then(|o| o.as_style())
81            .unwrap_or_default();
82        Paragraph::new(Line::from(label).alignment(alignement).style(style))
83            .centered()
84            .render(area, buf);
85    }
86}