Add bar chart prototype

pull/3/head
Florian Dehau 8 years ago
parent 85ca4f6b19
commit 063bde8764

@ -21,7 +21,8 @@ use log4rs::encode::pattern::PatternEncoder;
use log4rs::config::{Appender, Config, Root};
use tui::Terminal;
use tui::widgets::{Widget, Block, List, Gauge, Sparkline, Text, border, Chart, Axis, Dataset};
use tui::widgets::{Widget, Block, List, Gauge, Sparkline, Text, border, Chart, Axis, Dataset,
BarChart};
use tui::layout::{Group, Direction, Alignment, Size};
use tui::style::Color;
@ -82,6 +83,7 @@ struct App<'a> {
data: Vec<u64>,
data2: Vec<(f64, f64)>,
data3: Vec<(f64, f64)>,
data4: Vec<(&'a str, u64)>,
window: [f64; 2],
colors: [Color; 2],
color_index: usize,
@ -124,6 +126,18 @@ fn main() {
data: rand_signal.clone().take(200).collect(),
data2: sin_signal.clone().take(20).collect(),
data3: sin_signal2.clone().take(20).collect(),
data4: vec![("B1", 9),
("B2", 12),
("B3", 5),
("B4", 8),
("B5", 2),
("B6", 4),
("B7", 5),
("B8", 9),
("B9", 14),
("B10", 15),
("B11", 1),
("B12", 0)],
window: [0.0, 20.0],
colors: [Color::Magenta, Color::Red],
color_index: 0,
@ -151,7 +165,7 @@ fn main() {
let tx = tx.clone();
loop {
tx.send(Event::Tick).unwrap();
thread::sleep(time::Duration::from_millis(200));
thread::sleep(time::Duration::from_millis(500));
}
});
@ -196,6 +210,8 @@ fn main() {
app.data2.push(sin_signal.next().unwrap());
app.data3.remove(0);
app.data3.push(sin_signal2.next().unwrap());
let i = app.data4.pop().unwrap();
app.data4.insert(0, i);
app.window[0] += 1.0;
app.window[1] += 1.0;
app.selected += 1;
@ -270,7 +286,16 @@ fn draw(t: &mut Terminal, app: &App) {
.block(Block::default().borders(border::ALL).title("List"))
.items(&app.items2)
.render(&chunks[1], t);
})
});
BarChart::default()
.block(Block::default().borders(border::ALL).title("Bar chart"))
.data(&app.data4)
.bar_width(3)
.bar_gap(2)
.bar_color(Color::LightGreen)
.value_color(Color::Black)
.label_color(Color::LightYellow)
.render(&chunks[1], t);
});
if app.show_chart {
Chart::default()

@ -1,3 +1,6 @@
use std::cmp::min;
use std::usize;
use unicode_segmentation::UnicodeSegmentation;
use layout::Rect;
@ -115,13 +118,23 @@ impl<'a> Buffer<'a> {
}
pub fn set_string(&mut self, x: u16, y: u16, string: &'a str, fg: Color, bg: Color) {
self.set_characters(x, y, string, usize::MAX, fg, bg);
}
pub fn set_characters(&mut self,
x: u16,
y: u16,
string: &'a str,
limit: usize,
fg: Color,
bg: Color) {
let index = self.index_of(x, y);
if index.is_none() {
return;
}
let mut index = index.unwrap();
let graphemes = UnicodeSegmentation::graphemes(string, true).collect::<Vec<&str>>();
let max_index = (self.area.width - x) as usize;
let max_index = min((self.area.width - x) as usize, limit);
for s in graphemes.iter().take(max_index) {
self.content[index].symbol = s;
self.content[index].fg = fg;
@ -130,6 +143,7 @@ impl<'a> Buffer<'a> {
}
}
pub fn update_colors(&mut self, x: u16, y: u16, fg: Color, bg: Color) {
if let Some(i) = self.index_of(x, y) {
self.content[i].fg = fg;

@ -0,0 +1,155 @@
use std::cmp::{min, max};
use unicode_width::UnicodeWidthStr;
use widgets::{Widget, Block};
use buffer::Buffer;
use layout::Rect;
use style::Color;
use symbols::bar;
pub struct BarChart<'a> {
block: Option<Block<'a>>,
max: Option<u64>,
bar_width: u16,
bar_gap: u16,
bar_color: Color,
value_color: Color,
label_color: Color,
data: &'a [(&'a str, u64)],
values: Vec<String>,
}
impl<'a> Default for BarChart<'a> {
fn default() -> BarChart<'a> {
BarChart {
block: None,
max: None,
bar_width: 1,
bar_gap: 1,
bar_color: Color::Reset,
value_color: Color::Reset,
label_color: Color::Reset,
data: &[],
values: Vec::new(),
}
}
}
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());
for &(l, v) in self.data {
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
}
pub fn bar_color(&'a mut self, color: Color) -> &mut BarChart<'a> {
self.bar_color = color;
self
}
pub fn value_color(&'a mut self, color: Color) -> &mut BarChart<'a> {
self.value_color = color;
self
}
pub fn label_color(&'a mut self, color: Color) -> &mut BarChart<'a> {
self.label_color = color;
self
}
}
impl<'a> Widget<'a> for BarChart<'a> {
fn buffer(&'a self, area: &Rect) -> Buffer<'a> {
let (mut buf, chart_area) = match self.block {
Some(ref b) => (b.buffer(area), b.inner(*area)),
None => (Buffer::empty(*area), *area),
};
if chart_area.height < 1 {
return buf;
}
let margin_x = chart_area.x - area.x;
let margin_y = chart_area.y - area.y;
let max = self.max.unwrap_or(self.data.iter().fold(0, |acc, &(_, v)| max(v, acc)));
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 {
buf.update_cell(margin_x + i as u16 * (self.bar_width + self.bar_gap) + x,
margin_y + j,
symbol,
self.bar_color,
Color::Reset);
}
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(margin_x + i as u16 * (self.bar_width + self.bar_gap) +
(self.bar_width - width) / 2,
chart_area.height - 1,
value_label,
self.value_color,
self.bar_color);
}
}
buf.set_characters(margin_x + i as u16 * (self.bar_width + self.bar_gap),
chart_area.height,
label,
self.bar_width as usize,
self.label_color,
Color::Reset);
}
buf
}
}

@ -4,6 +4,7 @@ mod list;
mod gauge;
mod sparkline;
mod chart;
mod barchart;
pub use self::block::Block;
pub use self::text::Text;
@ -11,6 +12,7 @@ pub use self::list::List;
pub use self::gauge::Gauge;
pub use self::sparkline::Sparkline;
pub use self::chart::{Chart, Axis, Dataset};
pub use self::barchart::BarChart;
use buffer::Buffer;
use layout::Rect;

@ -72,10 +72,11 @@ impl<'a> Widget<'a> for Sparkline<'a> {
let max_index = min(spark_area.width as usize, self.data.len());
let mut data = self.data
.iter()
.take(max_index)
.map(|e| e * spark_area.height as u64 * 8 / max)
.collect::<Vec<u64>>();
for j in (0..spark_area.height).rev() {
for (i, d) in data.iter_mut().take(max_index).enumerate() {
for (i, d) in data.iter_mut().enumerate() {
let symbol = match *d {
0 => " ",
1 => bar::ONE_EIGHTH,

Loading…
Cancel
Save