2020-04-13 22:57:50 +00:00
|
|
|
use crate::{
|
|
|
|
buffer::Buffer,
|
|
|
|
layout::Rect,
|
|
|
|
style::Style,
|
|
|
|
symbols,
|
|
|
|
widgets::{Block, Widget},
|
|
|
|
};
|
2016-10-12 17:43:39 +00:00
|
|
|
use std::cmp::min;
|
|
|
|
|
2016-11-04 18:27:19 +00:00
|
|
|
/// Widget to render a sparkline over one or more lines.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
2017-12-26 15:55:13 +00:00
|
|
|
/// # use tui::widgets::{Block, Borders, Sparkline};
|
2016-11-06 20:41:32 +00:00
|
|
|
/// # use tui::style::{Style, Color};
|
2016-11-04 18:27:19 +00:00
|
|
|
/// Sparkline::default()
|
2017-12-26 15:55:13 +00:00
|
|
|
/// .block(Block::default().title("Sparkline").borders(Borders::ALL))
|
2016-11-04 18:27:19 +00:00
|
|
|
/// .data(&[0, 2, 3, 4, 1, 4, 10])
|
|
|
|
/// .max(5)
|
2016-11-06 20:41:32 +00:00
|
|
|
/// .style(Style::default().fg(Color::Red).bg(Color::White));
|
2016-11-04 18:27:19 +00:00
|
|
|
/// ```
|
2020-04-14 16:56:00 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2016-10-12 17:43:39 +00:00
|
|
|
pub struct Sparkline<'a> {
|
2016-11-04 18:27:19 +00:00
|
|
|
/// A block to wrap the widget in
|
2016-10-12 17:43:39 +00:00
|
|
|
block: Option<Block<'a>>,
|
2016-11-06 17:49:57 +00:00
|
|
|
/// Widget style
|
|
|
|
style: Style,
|
2016-11-04 18:27:19 +00:00
|
|
|
/// A slice of the data to display
|
2016-10-23 12:14:43 +00:00
|
|
|
data: &'a [u64],
|
2016-11-04 18:27:19 +00:00
|
|
|
/// The maximum value to take to compute the maximum bar height (if nothing is specified, the
|
|
|
|
/// widget uses the max of the dataset)
|
2016-10-13 15:30:35 +00:00
|
|
|
max: Option<u64>,
|
2020-04-13 22:57:50 +00:00
|
|
|
/// A set of bar symbols used to represent the give data
|
|
|
|
bar_set: symbols::bar::Set,
|
2016-10-12 17:43:39 +00:00
|
|
|
}
|
|
|
|
|
2016-10-14 17:44:52 +00:00
|
|
|
impl<'a> Default for Sparkline<'a> {
|
|
|
|
fn default() -> Sparkline<'a> {
|
2016-10-12 17:43:39 +00:00
|
|
|
Sparkline {
|
|
|
|
block: None,
|
2016-11-06 17:49:57 +00:00
|
|
|
style: Default::default(),
|
2016-10-23 12:14:43 +00:00
|
|
|
data: &[],
|
2016-10-13 15:30:35 +00:00
|
|
|
max: None,
|
2020-04-13 22:57:50 +00:00
|
|
|
bar_set: symbols::bar::NINE_LEVELS,
|
2016-10-12 17:43:39 +00:00
|
|
|
}
|
|
|
|
}
|
2016-10-14 17:44:52 +00:00
|
|
|
}
|
2016-10-12 17:43:39 +00:00
|
|
|
|
2016-10-14 17:44:52 +00:00
|
|
|
impl<'a> Sparkline<'a> {
|
2018-09-01 12:05:33 +00:00
|
|
|
pub fn block(mut self, block: Block<'a>) -> Sparkline<'a> {
|
2016-10-12 17:43:39 +00:00
|
|
|
self.block = Some(block);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2018-09-01 12:05:33 +00:00
|
|
|
pub fn style(mut self, style: Style) -> Sparkline<'a> {
|
2016-11-06 17:49:57 +00:00
|
|
|
self.style = style;
|
2016-10-12 17:43:39 +00:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2018-09-01 12:05:33 +00:00
|
|
|
pub fn data(mut self, data: &'a [u64]) -> Sparkline<'a> {
|
2016-10-23 12:14:43 +00:00
|
|
|
self.data = data;
|
2016-10-12 17:43:39 +00:00
|
|
|
self
|
|
|
|
}
|
2016-10-13 15:30:35 +00:00
|
|
|
|
2018-09-01 12:05:33 +00:00
|
|
|
pub fn max(mut self, max: u64) -> Sparkline<'a> {
|
2016-10-13 15:30:35 +00:00
|
|
|
self.max = Some(max);
|
|
|
|
self
|
|
|
|
}
|
2020-04-13 22:57:50 +00:00
|
|
|
|
|
|
|
pub fn bar_set(mut self, bar_set: symbols::bar::Set) -> Sparkline<'a> {
|
|
|
|
self.bar_set = bar_set;
|
|
|
|
self
|
|
|
|
}
|
2016-10-12 17:43:39 +00:00
|
|
|
}
|
|
|
|
|
2016-10-26 12:32:45 +00:00
|
|
|
impl<'a> Widget for Sparkline<'a> {
|
2019-12-15 20:38:18 +00:00
|
|
|
fn render(mut self, area: Rect, buf: &mut Buffer) {
|
2020-05-10 13:44:30 +00:00
|
|
|
let spark_area = match self.block.take() {
|
|
|
|
Some(b) => {
|
|
|
|
let inner_area = b.inner(area);
|
2019-12-15 20:38:18 +00:00
|
|
|
b.render(area, buf);
|
2020-05-10 13:44:30 +00:00
|
|
|
inner_area
|
2016-10-26 12:32:45 +00:00
|
|
|
}
|
2018-08-12 22:27:56 +00:00
|
|
|
None => area,
|
2016-10-12 17:43:39 +00:00
|
|
|
};
|
2016-11-02 16:08:52 +00:00
|
|
|
|
2016-10-12 17:43:39 +00:00
|
|
|
if spark_area.height < 1 {
|
2016-10-26 12:32:45 +00:00
|
|
|
return;
|
2016-11-02 16:08:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let max = match self.max {
|
|
|
|
Some(v) => v,
|
|
|
|
None => *self.data.iter().max().unwrap_or(&1u64),
|
|
|
|
};
|
|
|
|
let max_index = min(spark_area.width as usize, self.data.len());
|
2018-08-12 17:44:52 +00:00
|
|
|
let mut data = self
|
|
|
|
.data
|
2016-11-02 16:08:52 +00:00
|
|
|
.iter()
|
|
|
|
.take(max_index)
|
2019-04-14 09:33:13 +00:00
|
|
|
.map(|e| {
|
|
|
|
if max != 0 {
|
|
|
|
e * u64::from(spark_area.height) * 8 / max
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
}
|
|
|
|
})
|
2016-11-02 16:08:52 +00:00
|
|
|
.collect::<Vec<u64>>();
|
|
|
|
for j in (0..spark_area.height).rev() {
|
|
|
|
for (i, d) in data.iter_mut().enumerate() {
|
|
|
|
let symbol = match *d {
|
2020-04-13 22:57:50 +00:00
|
|
|
0 => self.bar_set.empty,
|
|
|
|
1 => self.bar_set.one_eighth,
|
|
|
|
2 => self.bar_set.one_quarter,
|
|
|
|
3 => self.bar_set.three_eighths,
|
|
|
|
4 => self.bar_set.half,
|
|
|
|
5 => self.bar_set.five_eighths,
|
|
|
|
6 => self.bar_set.three_quarters,
|
|
|
|
7 => self.bar_set.seven_eighths,
|
|
|
|
_ => self.bar_set.full,
|
2016-11-02 16:08:52 +00:00
|
|
|
};
|
2016-11-06 17:49:57 +00:00
|
|
|
buf.get_mut(spark_area.left() + i as u16, spark_area.top() + j)
|
|
|
|
.set_symbol(symbol)
|
2020-07-11 17:08:28 +00:00
|
|
|
.set_style(self.style);
|
2016-10-21 17:02:19 +00:00
|
|
|
|
2016-11-02 16:08:52 +00:00
|
|
|
if *d > 8 {
|
|
|
|
*d -= 8;
|
|
|
|
} else {
|
|
|
|
*d = 0;
|
2016-10-12 17:43:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-04-14 09:33:13 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn it_does_not_panic_if_max_is_zero() {
|
2019-12-15 20:38:18 +00:00
|
|
|
let widget = Sparkline::default().data(&[0, 0, 0]);
|
2019-04-14 09:33:13 +00:00
|
|
|
let area = Rect::new(0, 0, 3, 1);
|
|
|
|
let mut buffer = Buffer::empty(area);
|
2019-12-15 20:38:18 +00:00
|
|
|
widget.render(area, &mut buffer);
|
2019-04-14 09:33:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn it_does_not_panic_if_max_is_set_to_zero() {
|
2019-12-15 20:38:18 +00:00
|
|
|
let widget = Sparkline::default().data(&[0, 1, 2]).max(0);
|
2019-04-14 09:33:13 +00:00
|
|
|
let area = Rect::new(0, 0, 3, 1);
|
|
|
|
let mut buffer = Buffer::empty(area);
|
2019-12-15 20:38:18 +00:00
|
|
|
widget.render(area, &mut buffer);
|
2019-04-14 09:33:13 +00:00
|
|
|
}
|
|
|
|
}
|