2017-10-30 21:28:18 +00:00
|
|
|
use std::cmp::{max, min};
|
2016-10-22 10:51:41 +00:00
|
|
|
|
|
|
|
use unicode_width::UnicodeWidthStr;
|
|
|
|
|
2019-01-06 11:57:06 +00:00
|
|
|
use crate::buffer::Buffer;
|
|
|
|
use crate::layout::Rect;
|
|
|
|
use crate::style::Style;
|
|
|
|
use crate::symbols::bar;
|
|
|
|
use crate::widgets::{Block, Widget};
|
2016-10-22 10:51:41 +00:00
|
|
|
|
2016-11-04 18:27:19 +00:00
|
|
|
/// Display multiple bars in a single widgets
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
2017-12-26 15:55:13 +00:00
|
|
|
/// # use tui::widgets::{Block, Borders, BarChart};
|
2016-11-06 20:41:32 +00:00
|
|
|
/// # use tui::style::{Style, Color, Modifier};
|
2016-11-04 18:27:19 +00:00
|
|
|
/// # fn main() {
|
|
|
|
/// BarChart::default()
|
2017-12-26 15:55:13 +00:00
|
|
|
/// .block(Block::default().title("BarChart").borders(Borders::ALL))
|
2016-11-04 18:27:19 +00:00
|
|
|
/// .bar_width(3)
|
|
|
|
/// .bar_gap(1)
|
2016-11-06 20:41:32 +00:00
|
|
|
/// .style(Style::default().fg(Color::Yellow).bg(Color::Red))
|
2019-02-25 14:15:00 +00:00
|
|
|
/// .value_style(Style::default().fg(Color::Red).modifier(Modifier::BOLD))
|
2016-11-06 20:41:32 +00:00
|
|
|
/// .label_style(Style::default().fg(Color::White))
|
2016-11-04 18:27:19 +00:00
|
|
|
/// .data(&[("B0", 0), ("B1", 2), ("B2", 4), ("B3", 3)])
|
|
|
|
/// .max(4);
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2016-10-22 10:51:41 +00:00
|
|
|
pub struct BarChart<'a> {
|
2016-11-04 18:27:19 +00:00
|
|
|
/// Block to wrap the widget in
|
2016-10-22 10:51:41 +00:00
|
|
|
block: Option<Block<'a>>,
|
2016-11-04 18:27:19 +00:00
|
|
|
/// The width of each bar
|
2016-10-22 10:51:41 +00:00
|
|
|
bar_width: u16,
|
2016-11-04 18:27:19 +00:00
|
|
|
/// The gap between each bar
|
2016-10-22 10:51:41 +00:00
|
|
|
bar_gap: u16,
|
2016-11-06 17:49:57 +00:00
|
|
|
/// Style of the values printed at the bottom of each bar
|
|
|
|
value_style: Style,
|
|
|
|
/// Style of the labels printed under each bar
|
|
|
|
label_style: Style,
|
|
|
|
/// Style for the widget
|
|
|
|
style: Style,
|
2016-11-04 18:27:19 +00:00
|
|
|
/// Slice of (label, value) pair to plot on the chart
|
2016-10-22 10:51:41 +00:00
|
|
|
data: &'a [(&'a str, u64)],
|
2016-11-04 18:27:19 +00:00
|
|
|
/// Value necessary for a bar to reach the maximum height (if no value is specified,
|
|
|
|
/// the maximum value in the data is taken as reference)
|
|
|
|
max: Option<u64>,
|
|
|
|
/// Values to display on the bar (computed when the data is passed to the widget)
|
2016-10-22 10:51:41 +00:00
|
|
|
values: Vec<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Default for BarChart<'a> {
|
|
|
|
fn default() -> BarChart<'a> {
|
|
|
|
BarChart {
|
|
|
|
block: None,
|
|
|
|
max: None,
|
2016-11-02 16:08:52 +00:00
|
|
|
data: &[],
|
|
|
|
values: Vec::new(),
|
2016-10-22 10:51:41 +00:00
|
|
|
bar_width: 1,
|
|
|
|
bar_gap: 1,
|
2016-11-06 17:49:57 +00:00
|
|
|
value_style: Default::default(),
|
|
|
|
label_style: Default::default(),
|
|
|
|
style: Default::default(),
|
2016-10-22 10:51:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> BarChart<'a> {
|
2018-09-01 12:05:33 +00:00
|
|
|
pub fn data(mut self, data: &'a [(&'a str, u64)]) -> BarChart<'a> {
|
2016-10-22 10:51:41 +00:00
|
|
|
self.data = data;
|
|
|
|
self.values = Vec::with_capacity(self.data.len());
|
2016-10-22 17:24:35 +00:00
|
|
|
for &(_, v) in self.data {
|
2016-10-22 10:51:41 +00:00
|
|
|
self.values.push(format!("{}", v));
|
|
|
|
}
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2018-09-01 12:05:33 +00:00
|
|
|
pub fn block(mut self, block: Block<'a>) -> BarChart<'a> {
|
2016-10-22 10:51:41 +00:00
|
|
|
self.block = Some(block);
|
|
|
|
self
|
|
|
|
}
|
2018-09-01 12:05:33 +00:00
|
|
|
pub fn max(mut self, max: u64) -> BarChart<'a> {
|
2016-10-22 10:51:41 +00:00
|
|
|
self.max = Some(max);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2018-09-01 12:05:33 +00:00
|
|
|
pub fn bar_width(mut self, width: u16) -> BarChart<'a> {
|
2016-10-22 10:51:41 +00:00
|
|
|
self.bar_width = width;
|
|
|
|
self
|
|
|
|
}
|
2018-09-01 12:05:33 +00:00
|
|
|
pub fn bar_gap(mut self, gap: u16) -> BarChart<'a> {
|
2016-10-22 10:51:41 +00:00
|
|
|
self.bar_gap = gap;
|
|
|
|
self
|
|
|
|
}
|
2018-09-01 12:05:33 +00:00
|
|
|
pub fn value_style(mut self, style: Style) -> BarChart<'a> {
|
2016-11-06 17:49:57 +00:00
|
|
|
self.value_style = style;
|
2016-10-22 10:51:41 +00:00
|
|
|
self
|
|
|
|
}
|
2018-09-01 12:05:33 +00:00
|
|
|
pub fn label_style(mut self, style: Style) -> BarChart<'a> {
|
2016-11-06 17:49:57 +00:00
|
|
|
self.label_style = style;
|
2016-10-22 10:51:41 +00:00
|
|
|
self
|
|
|
|
}
|
2018-09-01 12:05:33 +00:00
|
|
|
pub fn style(mut self, style: Style) -> BarChart<'a> {
|
2016-11-06 17:49:57 +00:00
|
|
|
self.style = style;
|
2016-11-02 16:08:52 +00:00
|
|
|
self
|
|
|
|
}
|
2016-10-22 10:51:41 +00:00
|
|
|
}
|
|
|
|
|
2016-10-26 12:32:45 +00:00
|
|
|
impl<'a> Widget for BarChart<'a> {
|
2018-08-12 22:27:56 +00:00
|
|
|
fn draw(&mut self, area: Rect, buf: &mut Buffer) {
|
2016-10-26 12:32:45 +00:00
|
|
|
let chart_area = match self.block {
|
2017-09-03 09:58:39 +00:00
|
|
|
Some(ref mut b) => {
|
2016-11-02 18:16:53 +00:00
|
|
|
b.draw(area, buf);
|
2016-10-26 12:32:45 +00:00
|
|
|
b.inner(area)
|
|
|
|
}
|
2018-08-12 22:27:56 +00:00
|
|
|
None => area,
|
2016-10-22 10:51:41 +00:00
|
|
|
};
|
|
|
|
|
2016-10-31 17:04:10 +00:00
|
|
|
if chart_area.height < 2 {
|
2016-10-26 12:32:45 +00:00
|
|
|
return;
|
2016-10-22 10:51:41 +00:00
|
|
|
}
|
|
|
|
|
2019-02-03 21:07:58 +00:00
|
|
|
self.background(chart_area, buf, self.style.bg);
|
2016-11-02 16:08:52 +00:00
|
|
|
|
2018-08-12 17:44:52 +00:00
|
|
|
let max = self
|
|
|
|
.max
|
2017-10-30 21:28:18 +00:00
|
|
|
.unwrap_or_else(|| self.data.iter().fold(0, |acc, &(_, v)| max(v, acc)));
|
2017-09-11 05:58:37 +00:00
|
|
|
let max_index = min(
|
|
|
|
(chart_area.width / (self.bar_width + self.bar_gap)) as usize,
|
|
|
|
self.data.len(),
|
|
|
|
);
|
2018-08-12 17:44:52 +00:00
|
|
|
let mut data = self
|
|
|
|
.data
|
2016-10-22 10:51:41 +00:00
|
|
|
.iter()
|
|
|
|
.take(max_index)
|
2019-06-16 20:21:55 +00:00
|
|
|
.map(|&(l, v)| {
|
|
|
|
(
|
|
|
|
l,
|
|
|
|
v * u64::from(chart_area.height) * 8 / std::cmp::max(max, 1),
|
|
|
|
)
|
|
|
|
})
|
2016-10-22 10:51:41 +00:00
|
|
|
.collect::<Vec<(&str, u64)>>();
|
|
|
|
for j in (0..chart_area.height - 1).rev() {
|
|
|
|
for (i, d) in data.iter_mut().enumerate() {
|
|
|
|
let symbol = match d.1 {
|
|
|
|
0 => " ",
|
|
|
|
1 => bar::ONE_EIGHTH,
|
|
|
|
2 => bar::ONE_QUATER,
|
|
|
|
3 => bar::THREE_EIGHTHS,
|
|
|
|
4 => bar::HALF,
|
|
|
|
5 => bar::FIVE_EIGHTHS,
|
|
|
|
6 => bar::THREE_QUATERS,
|
|
|
|
7 => bar::SEVEN_EIGHTHS,
|
|
|
|
_ => bar::FULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
for x in 0..self.bar_width {
|
2017-09-11 05:58:37 +00:00
|
|
|
buf.get_mut(
|
|
|
|
chart_area.left() + i as u16 * (self.bar_width + self.bar_gap) + x,
|
|
|
|
chart_area.top() + j,
|
2018-12-07 15:17:33 +00:00
|
|
|
)
|
|
|
|
.set_symbol(symbol)
|
2018-09-23 19:00:36 +00:00
|
|
|
.set_style(self.style);
|
2016-10-22 10:51:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if d.1 > 8 {
|
|
|
|
d.1 -= 8;
|
|
|
|
} else {
|
|
|
|
d.1 = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i, &(label, value)) in self.data.iter().take(max_index).enumerate() {
|
|
|
|
if value != 0 {
|
|
|
|
let value_label = &self.values[i];
|
|
|
|
let width = value_label.width() as u16;
|
|
|
|
if width < self.bar_width {
|
2017-09-11 05:58:37 +00:00
|
|
|
buf.set_string(
|
2018-08-12 17:44:52 +00:00
|
|
|
chart_area.left()
|
|
|
|
+ i as u16 * (self.bar_width + self.bar_gap)
|
2017-10-30 21:28:18 +00:00
|
|
|
+ (self.bar_width - width) / 2,
|
2017-09-11 05:58:37 +00:00
|
|
|
chart_area.bottom() - 2,
|
|
|
|
value_label,
|
2018-09-07 20:24:52 +00:00
|
|
|
self.value_style,
|
2017-09-11 05:58:37 +00:00
|
|
|
);
|
2016-10-22 10:51:41 +00:00
|
|
|
}
|
|
|
|
}
|
2017-09-11 05:58:37 +00:00
|
|
|
buf.set_stringn(
|
|
|
|
chart_area.left() + i as u16 * (self.bar_width + self.bar_gap),
|
|
|
|
chart_area.bottom() - 1,
|
|
|
|
label,
|
|
|
|
self.bar_width as usize,
|
2018-09-07 20:24:52 +00:00
|
|
|
self.label_style,
|
2017-09-11 05:58:37 +00:00
|
|
|
);
|
2016-10-22 10:51:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|