tui_temp_fork/widgets/
barchart.rs1use std::cmp::{max, min};
2
3use unicode_width::UnicodeWidthStr;
4
5use crate::buffer::Buffer;
6use crate::layout::Rect;
7use crate::style::Style;
8use crate::symbols::bar;
9use crate::widgets::{Block, Widget};
10
11pub struct BarChart<'a> {
31 block: Option<Block<'a>>,
33 bar_width: u16,
35 bar_gap: u16,
37 value_style: Style,
39 label_style: Style,
41 style: Style,
43 data: &'a [(&'a str, u64)],
45 max: Option<u64>,
48 values: Vec<String>,
50}
51
52impl<'a> Default for BarChart<'a> {
53 fn default() -> BarChart<'a> {
54 BarChart {
55 block: None,
56 max: None,
57 data: &[],
58 values: Vec::new(),
59 bar_width: 1,
60 bar_gap: 1,
61 value_style: Default::default(),
62 label_style: Default::default(),
63 style: Default::default(),
64 }
65 }
66}
67
68impl<'a> BarChart<'a> {
69 pub fn data(mut self, data: &'a [(&'a str, u64)]) -> BarChart<'a> {
70 self.data = data;
71 self.values = Vec::with_capacity(self.data.len());
72 for &(_, v) in self.data {
73 self.values.push(format!("{}", v));
74 }
75 self
76 }
77
78 pub fn block(mut self, block: Block<'a>) -> BarChart<'a> {
79 self.block = Some(block);
80 self
81 }
82 pub fn max(mut self, max: u64) -> BarChart<'a> {
83 self.max = Some(max);
84 self
85 }
86
87 pub fn bar_width(mut self, width: u16) -> BarChart<'a> {
88 self.bar_width = width;
89 self
90 }
91 pub fn bar_gap(mut self, gap: u16) -> BarChart<'a> {
92 self.bar_gap = gap;
93 self
94 }
95 pub fn value_style(mut self, style: Style) -> BarChart<'a> {
96 self.value_style = style;
97 self
98 }
99 pub fn label_style(mut self, style: Style) -> BarChart<'a> {
100 self.label_style = style;
101 self
102 }
103 pub fn style(mut self, style: Style) -> BarChart<'a> {
104 self.style = style;
105 self
106 }
107}
108
109impl<'a> Widget for BarChart<'a> {
110 fn draw(&mut self, area: Rect, buf: &mut Buffer) {
111 let chart_area = match self.block {
112 Some(ref mut b) => {
113 b.draw(area, buf);
114 b.inner(area)
115 }
116 None => area,
117 };
118
119 if chart_area.height < 2 {
120 return;
121 }
122
123 self.background(chart_area, buf, self.style.bg);
124
125 let max = self
126 .max
127 .unwrap_or_else(|| self.data.iter().fold(0, |acc, &(_, v)| max(v, acc)));
128 let max_index = min(
129 (chart_area.width / (self.bar_width + self.bar_gap)) as usize,
130 self.data.len(),
131 );
132 let mut data = self
133 .data
134 .iter()
135 .take(max_index)
136 .map(|&(l, v)| {
137 (
138 l,
139 v * u64::from(chart_area.height) * 8 / std::cmp::max(max, 1),
140 )
141 })
142 .collect::<Vec<(&str, u64)>>();
143 for j in (0..chart_area.height - 1).rev() {
144 for (i, d) in data.iter_mut().enumerate() {
145 let symbol = match d.1 {
146 0 => " ",
147 1 => bar::ONE_EIGHTH,
148 2 => bar::ONE_QUARTER,
149 3 => bar::THREE_EIGHTHS,
150 4 => bar::HALF,
151 5 => bar::FIVE_EIGHTHS,
152 6 => bar::THREE_QUARTERS,
153 7 => bar::SEVEN_EIGHTHS,
154 _ => bar::FULL,
155 };
156
157 for x in 0..self.bar_width {
158 buf.get_mut(
159 chart_area.left() + i as u16 * (self.bar_width + self.bar_gap) + x,
160 chart_area.top() + j,
161 )
162 .set_symbol(symbol)
163 .set_style(self.style);
164 }
165
166 if d.1 > 8 {
167 d.1 -= 8;
168 } else {
169 d.1 = 0;
170 }
171 }
172 }
173
174 for (i, &(label, value)) in self.data.iter().take(max_index).enumerate() {
175 if value != 0 {
176 let value_label = &self.values[i];
177 let width = value_label.width() as u16;
178 if width < self.bar_width {
179 buf.set_string(
180 chart_area.left()
181 + i as u16 * (self.bar_width + self.bar_gap)
182 + (self.bar_width - width) / 2,
183 chart_area.bottom() - 2,
184 value_label,
185 self.value_style,
186 );
187 }
188 }
189 buf.set_stringn(
190 chart_area.left() + i as u16 * (self.bar_width + self.bar_gap),
191 chart_area.bottom() - 1,
192 label,
193 self.bar_width as usize,
194 self.label_style,
195 );
196 }
197 }
198}