ratatui/widgets/barchart/
bar_group.rs

1use crate::{
2    buffer::Buffer,
3    layout::{Alignment, Rect},
4    style::Style,
5    text::Line,
6    widgets::{barchart::Bar, Widget},
7};
8
9/// A group of bars to be shown by the Barchart.
10///
11/// # Examples
12///
13/// ```
14/// use ratatui::widgets::{Bar, BarGroup};
15///
16/// BarGroup::default()
17///     .label("Group 1".into())
18///     .bars(&[Bar::default().value(200), Bar::default().value(150)]);
19/// ```
20#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
21pub struct BarGroup<'a> {
22    /// label of the group. It will be printed centered under this group of bars
23    pub(super) label: Option<Line<'a>>,
24    /// list of bars to be shown
25    pub(super) bars: Vec<Bar<'a>>,
26}
27
28impl<'a> BarGroup<'a> {
29    /// Set the group label
30    #[must_use = "method moves the value of self and returns the modified value"]
31    pub fn label(mut self, label: Line<'a>) -> Self {
32        self.label = Some(label);
33        self
34    }
35
36    /// Set the bars of the group to be shown
37    #[must_use = "method moves the value of self and returns the modified value"]
38    pub fn bars(mut self, bars: &[Bar<'a>]) -> Self {
39        self.bars = bars.to_vec();
40        self
41    }
42
43    /// The maximum bar value of this group
44    pub(super) fn max(&self) -> Option<u64> {
45        self.bars.iter().max_by_key(|v| v.value).map(|v| v.value)
46    }
47
48    pub(super) fn render_label(&self, buf: &mut Buffer, area: Rect, default_label_style: Style) {
49        if let Some(label) = &self.label {
50            // align the label. Necessary to do it this way as we don't want to set the style
51            // of the whole area, just the label area
52            let width = label.width() as u16;
53            let area = match label.alignment {
54                Some(Alignment::Center) => Rect {
55                    x: area.x + (area.width.saturating_sub(width)) / 2,
56                    width,
57                    ..area
58                },
59                Some(Alignment::Right) => Rect {
60                    x: area.x + area.width.saturating_sub(width),
61                    width,
62                    ..area
63                },
64                _ => Rect { width, ..area },
65            };
66            buf.set_style(area, default_label_style);
67            label.render(area, buf);
68        }
69    }
70}
71
72impl<'a> From<&[(&'a str, u64)]> for BarGroup<'a> {
73    fn from(value: &[(&'a str, u64)]) -> Self {
74        Self {
75            label: None,
76            bars: value
77                .iter()
78                .map(|&(text, v)| Bar::default().value(v).label(text.into()))
79                .collect(),
80        }
81    }
82}
83
84impl<'a, const N: usize> From<&[(&'a str, u64); N]> for BarGroup<'a> {
85    fn from(value: &[(&'a str, u64); N]) -> Self {
86        let value: &[(&'a str, u64)] = value.as_ref();
87        Self::from(value)
88    }
89}
90
91impl<'a> From<&Vec<(&'a str, u64)>> for BarGroup<'a> {
92    fn from(value: &Vec<(&'a str, u64)>) -> Self {
93        let array: &[(&str, u64)] = value;
94        Self::from(array)
95    }
96}