tui-rs/src/widgets/barchart.rs

185 lines
5.8 KiB
Rust
Raw Normal View History

2016-10-22 10:51:41 +00:00
use std::cmp::{min, max};
use unicode_width::UnicodeWidthStr;
use widgets::{Widget, Block};
use buffer::Buffer;
use layout::Rect;
2016-11-06 17:49:57 +00:00
use style::Style;
2016-10-22 10:51:41 +00:00
use symbols::bar;
2016-11-04 18:27:19 +00:00
/// Display multiple bars in a single widgets
///
/// # Examples
///
/// ```
/// # extern crate tui;
/// # use tui::widgets::{Block, border, 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()
/// .block(Block::default().title("BarChart").borders(border::ALL))
/// .bar_width(3)
/// .bar_gap(1)
2016-11-06 20:41:32 +00:00
/// .style(Style::default().fg(Color::Yellow).bg(Color::Red))
/// .value_style(Style::default().fg(Color::Red).modifier(Modifier::Bold))
/// .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,
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> {
pub fn data(&'a mut self, data: &'a [(&'a str, u64)]) -> &mut BarChart<'a> {
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
}
pub fn block(&'a mut self, block: Block<'a>) -> &mut BarChart<'a> {
self.block = Some(block);
self
}
pub fn max(&'a mut self, max: u64) -> &mut BarChart<'a> {
self.max = Some(max);
self
}
pub fn bar_width(&'a mut self, width: u16) -> &mut BarChart<'a> {
self.bar_width = width;
self
}
pub fn bar_gap(&'a mut self, gap: u16) -> &mut BarChart<'a> {
self.bar_gap = gap;
self
}
2016-11-06 17:49:57 +00:00
pub fn value_style(&'a mut self, style: Style) -> &mut BarChart<'a> {
self.value_style = style;
2016-10-22 10:51:41 +00:00
self
}
2016-11-06 17:49:57 +00:00
pub fn label_style(&'a mut self, style: Style) -> &mut BarChart<'a> {
self.label_style = style;
2016-10-22 10:51:41 +00:00
self
}
2016-11-06 17:49:57 +00:00
pub fn style(&'a mut self, style: Style) -> &mut BarChart<'a> {
self.style = style;
self
}
2016-10-22 10:51:41 +00:00
}
impl<'a> Widget for BarChart<'a> {
fn draw(&mut self, area: &Rect, buf: &mut Buffer) {
let chart_area = match self.block {
Some(ref mut b) => {
2016-11-02 18:16:53 +00:00
b.draw(area, buf);
b.inner(area)
}
None => *area,
2016-10-22 10:51:41 +00:00
};
2016-10-31 17:04:10 +00:00
if chart_area.height < 2 {
return;
2016-10-22 10:51:41 +00:00
}
2016-11-06 17:49:57 +00:00
self.background(&chart_area, buf, self.style.bg);
2017-05-21 09:13:24 +00:00
let max = self.max
.unwrap_or_else(|| self.data.iter().fold(0, |acc, &(_, v)| max(v, acc)));
2016-10-22 10:51:41 +00:00
let max_index = min((chart_area.width / (self.bar_width + self.bar_gap)) as usize,
self.data.len());
let mut data = self.data
.iter()
.take(max_index)
.map(|&(l, v)| (l, v * chart_area.height as u64 * 8 / max))
.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-05-21 09:13:24 +00:00
buf.get_mut(chart_area.left() + i as u16 * (self.bar_width + self.bar_gap) + x,
2016-11-06 17:49:57 +00:00
chart_area.top() + j)
.set_symbol(symbol)
.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 {
buf.set_string(chart_area.left() + i as u16 * (self.bar_width + self.bar_gap) +
2016-10-22 10:51:41 +00:00
(self.bar_width - width) / 2,
chart_area.bottom() - 2,
2016-10-22 10:51:41 +00:00
value_label,
2016-11-06 17:49:57 +00:00
&self.value_style);
2016-10-22 10:51:41 +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,
2016-11-06 17:49:57 +00:00
&self.label_style);
2016-10-22 10:51:41 +00:00
}
}
}