zui_widgets/widgets/
barchart.rs1use crate::{
2 buffer::Buffer,
3 layout::Rect,
4 style::Style,
5 symbols,
6 widgets::{Block, Widget},
7};
8use std::cmp::min;
9use unicode_width::UnicodeWidthStr;
10
11#[derive(Debug, Clone)]
29pub struct BarChart<'a> {
30 block: Option<Block<'a>>,
32 bar_width: u16,
34 bar_gap: u16,
36 bar_set: symbols::bar::Set,
38 bar_style: Style,
40 value_style: Style,
42 label_style: Style,
44 style: Style,
46 data: &'a [(&'a str, u64)],
48 max: Option<u64>,
51 values: Vec<String>,
53}
54
55impl<'a> Default for BarChart<'a> {
56 fn default() -> BarChart<'a> {
57 BarChart {
58 block: None,
59 max: None,
60 data: &[],
61 values: Vec::new(),
62 bar_style: Style::default(),
63 bar_width: 1,
64 bar_gap: 1,
65 bar_set: symbols::bar::NINE_LEVELS,
66 value_style: Default::default(),
67 label_style: Default::default(),
68 style: Default::default(),
69 }
70 }
71}
72
73impl<'a> BarChart<'a> {
74 pub fn data(mut self, data: &'a [(&'a str, u64)]) -> BarChart<'a> {
75 self.data = data;
76 self.values = Vec::with_capacity(self.data.len());
77 for &(_, v) in self.data {
78 self.values.push(format!("{}", v));
79 }
80 self
81 }
82
83 pub fn block(mut self, block: Block<'a>) -> BarChart<'a> {
84 self.block = Some(block);
85 self
86 }
87
88 pub fn max(mut self, max: u64) -> BarChart<'a> {
89 self.max = Some(max);
90 self
91 }
92
93 pub fn bar_style(mut self, style: Style) -> BarChart<'a> {
94 self.bar_style = style;
95 self
96 }
97
98 pub fn bar_width(mut self, width: u16) -> BarChart<'a> {
99 self.bar_width = width;
100 self
101 }
102
103 pub fn bar_gap(mut self, gap: u16) -> BarChart<'a> {
104 self.bar_gap = gap;
105 self
106 }
107
108 pub fn bar_set(mut self, bar_set: symbols::bar::Set) -> BarChart<'a> {
109 self.bar_set = bar_set;
110 self
111 }
112
113 pub fn value_style(mut self, style: Style) -> BarChart<'a> {
114 self.value_style = style;
115 self
116 }
117
118 pub fn label_style(mut self, style: Style) -> BarChart<'a> {
119 self.label_style = style;
120 self
121 }
122
123 pub fn style(mut self, style: Style) -> BarChart<'a> {
124 self.style = style;
125 self
126 }
127}
128
129impl<'a> Widget for BarChart<'a> {
130 fn render(mut self, area: Rect, buf: &mut Buffer) {
131 buf.set_style(area, self.style);
132
133 let chart_area = match self.block.take() {
134 Some(b) => {
135 let inner_area = b.inner(area);
136 b.render(area, buf);
137 inner_area
138 }
139 None => area,
140 };
141
142 if chart_area.height < 2 {
143 return;
144 }
145
146 let max = self
147 .max
148 .unwrap_or_else(|| self.data.iter().map(|t| t.1).max().unwrap_or_default());
149 let max_index = min(
150 (chart_area.width / (self.bar_width + self.bar_gap)) as usize,
151 self.data.len(),
152 );
153 let mut data = self
154 .data
155 .iter()
156 .take(max_index)
157 .map(|&(l, v)| {
158 (
159 l,
160 v * u64::from(chart_area.height - 1) * 8 / std::cmp::max(max, 1),
161 )
162 })
163 .collect::<Vec<(&str, u64)>>();
164 for j in (0..chart_area.height - 1).rev() {
165 for (i, d) in data.iter_mut().enumerate() {
166 let symbol = match d.1 {
167 0 => self.bar_set.empty,
168 1 => self.bar_set.one_eighth,
169 2 => self.bar_set.one_quarter,
170 3 => self.bar_set.three_eighths,
171 4 => self.bar_set.half,
172 5 => self.bar_set.five_eighths,
173 6 => self.bar_set.three_quarters,
174 7 => self.bar_set.seven_eighths,
175 _ => self.bar_set.full,
176 };
177
178 for x in 0..self.bar_width {
179 buf.get_mut(
180 chart_area.left() + i as u16 * (self.bar_width + self.bar_gap) + x,
181 chart_area.top() + j,
182 )
183 .set_symbol(symbol)
184 .set_style(self.bar_style);
185 }
186
187 if d.1 > 8 {
188 d.1 -= 8;
189 } else {
190 d.1 = 0;
191 }
192 }
193 }
194
195 for (i, &(label, value)) in self.data.iter().take(max_index).enumerate() {
196 if value != 0 {
197 let value_label = &self.values[i];
198 let width = value_label.width() as u16;
199 if width < self.bar_width {
200 buf.set_string(
201 chart_area.left()
202 + i as u16 * (self.bar_width + self.bar_gap)
203 + (self.bar_width - width) / 2,
204 chart_area.bottom() - 2,
205 value_label,
206 self.value_style,
207 );
208 }
209 }
210 buf.set_stringn(
211 chart_area.left() + i as u16 * (self.bar_width + self.bar_gap),
212 chart_area.bottom() - 1,
213 label,
214 self.bar_width as usize,
215 self.label_style,
216 );
217 }
218 }
219}