Add sparkline widget and fix warnings

pull/3/head
Florian Dehau 8 years ago
parent 5b5d37ee69
commit d11dedd864

@ -8,4 +8,7 @@ termion = "1.1.1"
bitflags = "0.7"
cassowary = "0.2.0"
log = "0.3"
[dev-dependencies]
log4rs = "*"
rand = "0.3"

@ -3,23 +3,23 @@ extern crate tui;
extern crate log;
extern crate log4rs;
extern crate termion;
extern crate rand;
use std::thread;
use std::time;
use std::sync::mpsc;
use std::io::{Write, stdin};
use std::io::stdin;
use termion::event;
use termion::input::TermRead;
use log::LogLevelFilter;
use log4rs::append::console::ConsoleAppender;
use log4rs::append::file::FileAppender;
use log4rs::encode::pattern::PatternEncoder;
use log4rs::config::{Appender, Config, Logger, Root};
use tui::Terminal;
use tui::widgets::{Widget, Block, List, Gauge, border};
use tui::widgets::{Widget, Block, List, Gauge, Sparkline, border};
use tui::layout::{Group, Direction, Alignment, Size};
struct App {
@ -29,6 +29,7 @@ struct App {
selected: usize,
show_episodes: bool,
progress: u16,
data: Vec<u64>,
}
enum Event {
@ -58,6 +59,7 @@ fn main() {
selected: 0,
show_episodes: false,
progress: 0,
data: (0..100).map(|_| rand::random::<u8>() as u64).collect(),
};
let (tx, rx) = mpsc::channel();
let input_tx = tx.clone();
@ -115,6 +117,8 @@ fn main() {
if app.progress > 100 {
app.progress = 0;
}
app.data.insert(0, rand::random::<u8>() as u64);
app.data.pop();
}
}
}
@ -128,6 +132,7 @@ fn draw(terminal: &mut Terminal, app: &App) {
.alignment(Alignment::Left)
.chunks(&[Size::Fixed(5), Size::Percent(80), Size::Fixed(10)])
.render(&terminal.area(), |chunks, tree| {
info!("{:?}", terminal.area());
tree.add(Block::default().borders(border::ALL).title("Gauges").render(&chunks[0]));
tree.add(Group::default()
.direction(Direction::Vertical)
@ -138,14 +143,14 @@ fn draw(terminal: &mut Terminal, app: &App) {
tree.add(Gauge::new()
.percent(app.progress)
.render(&chunks[0]));
tree.add(Gauge::new()
.percent(app.progress)
tree.add(Sparkline::new()
.data(&app.data)
.render(&chunks[2]));
}));
let sizes = if app.show_episodes {
vec![Size::Percent(50), Size::Percent(50)]
} else {
vec![Size::Percent(50), Size::Percent(50)]
vec![Size::Percent(100)]
};
tree.add(Group::default()
.direction(Direction::Horizontal)
@ -153,9 +158,7 @@ fn draw(terminal: &mut Terminal, app: &App) {
.chunks(&sizes)
.render(&chunks[1], |chunks, tree| {
tree.add(List::default()
.block(|b| {
b.borders(border::ALL).title("Podcasts");
})
.block(*Block::default().borders(border::ALL).title("Podcasts"))
.items(&app.items)
.select(app.selected)
.formatter(|i, s| {

@ -1,22 +0,0 @@
extern crate termion;
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate log;
extern crate cassowary;
mod buffer;
mod util;
pub mod symbols;
pub mod terminal;
pub mod widgets;
pub mod style;
pub mod layout;
pub use self::terminal::Terminal;
#[cfg(test)]
mod tests {
#[test]
fn it_works() {}
}

@ -121,6 +121,7 @@ pub enum Size {
/// }
///
/// ```
#[allow(unused_variables)]
pub fn split(area: &Rect,
dir: &Direction,
align: &Alignment,

@ -8,3 +8,14 @@ pub mod block {
pub const ONE_QUATER: char = '▎';
pub const ONE_EIGHTH: char = '▏';
}
pub mod bar {
pub const FULL: char = '█';
pub const SEVEN_EIGHTHS: char = '▇';
pub const THREE_QUATERS: char = '▆';
pub const FIVE_EIGHTHS: char = '▅';
pub const HALF: char = '▄';
pub const THREE_EIGHTHS: char = '▃';
pub const ONE_QUATER: char = '▂';
pub const ONE_EIGHTH: char = '▁';
}

@ -59,7 +59,7 @@ impl Terminal {
}
previous.insert((node.widget_type, area), node.hash);
}
for (&(t, a), h) in &self.previous {
for (&(t, a), _h) in &self.previous {
buffers.insert(0, Buffer::empty(a));
debug!("Erased {:?} at {:?}", t, a);
}

@ -1,10 +1,12 @@
use std::hash::{Hash, SipHasher, Hasher};
use std::collections::hash_map::RandomState;
use std::hash::{Hash, Hasher, BuildHasher};
use layout::Rect;
pub fn hash<T: Hash>(t: &T, area: &Rect) -> u64 {
let mut s = SipHasher::new();
t.hash(&mut s);
area.hash(&mut s);
s.finish()
let state = RandomState::new();
let mut hasher = state.build_hasher();
t.hash(&mut hasher);
area.hash(&mut hasher);
hasher.finish()
}

@ -36,7 +36,7 @@ impl<'a> Block<'a> {
}
impl<'a> Widget for Block<'a> {
fn _buffer(&self, area: &Rect) -> Buffer {
fn buffer(&self, area: &Rect) -> Buffer {
let mut buf = Buffer::empty(*area);

@ -47,9 +47,9 @@ impl<'a> Gauge<'a> {
}
impl<'a> Widget for Gauge<'a> {
fn _buffer(&self, area: &Rect) -> Buffer {
fn buffer(&self, area: &Rect) -> Buffer {
let (mut buf, gauge_area) = match self.block {
Some(ref b) => (b._buffer(area), area.inner(1)),
Some(ref b) => (b.buffer(area), area.inner(1)),
None => (Buffer::empty(*area), *area),
};
if gauge_area.height < 1 {

@ -7,7 +7,7 @@ use widgets::{Widget, WidgetType, Block};
use layout::Rect;
pub struct List<'a, T> {
block: Block<'a>,
block: Option<Block<'a>>,
selected: usize,
formatter: Box<Fn(&T, bool) -> String>,
items: Vec<T>,
@ -26,7 +26,7 @@ impl<'a, T> Hash for List<'a, T>
impl<'a, T> Default for List<'a, T> {
fn default() -> List<'a, T> {
List {
block: Block::default(),
block: None,
selected: 0,
formatter: Box::new(|_, _| String::from("")),
items: Vec::new(),
@ -37,10 +37,8 @@ impl<'a, T> Default for List<'a, T> {
impl<'a, T> List<'a, T>
where T: Clone
{
pub fn block<F>(&'a mut self, f: F) -> &mut List<'a, T>
where F: Fn(&mut Block)
{
f(&mut self.block);
pub fn block(&'a mut self, block: Block<'a>) -> &mut List<'a, T> {
self.block = Some(block);
self
}
@ -65,14 +63,14 @@ impl<'a, T> List<'a, T>
impl<'a, T> Widget for List<'a, T>
where T: Display + Hash
{
fn _buffer(&self, area: &Rect) -> Buffer {
let mut buf = self.block.buffer(area);
if area.area() == 0 {
return buf;
}
fn buffer(&self, area: &Rect) -> Buffer {
let (mut buf, list_area) = match self.block {
Some(ref b) => (b.buffer(area), area.inner(1)),
None => (Buffer::empty(*area), *area),
};
let list_length = self.items.len();
let list_area = area.inner(1);
let list_height = list_area.height as usize;
let bound = min(list_height, list_length);
let offset = if self.selected > list_height {

@ -1,10 +1,12 @@
mod block;
mod list;
mod gauge;
mod sparkline;
pub use self::block::Block;
pub use self::list::List;
pub use self::gauge::Gauge;
pub use self::sparkline::Sparkline;
use std::hash::Hash;
@ -13,6 +15,7 @@ use buffer::{Buffer, Cell};
use layout::{Rect, Tree, Leaf};
use style::Color;
#[allow(dead_code)]
enum Line {
Horizontal,
Vertical,
@ -88,16 +91,11 @@ pub enum WidgetType {
Block,
List,
Gauge,
Sparkline,
}
pub trait Widget: Hash {
fn _buffer(&self, area: &Rect) -> Buffer;
fn buffer(&self, area: &Rect) -> Buffer {
match area.area() {
0 => Buffer::empty(*area),
_ => self._buffer(area),
}
}
fn buffer(&self, area: &Rect) -> Buffer;
fn widget_type(&self) -> WidgetType;
fn render(&self, area: &Rect) -> Tree {
let widget_type = self.widget_type();

@ -0,0 +1,84 @@
use std::cmp::min;
use layout::Rect;
use buffer::Buffer;
use widgets::{Widget, WidgetType, Block};
use style::Color;
use symbols::bar;
#[derive(Hash)]
pub struct Sparkline<'a> {
block: Option<Block<'a>>,
color: Color,
data: Vec<u64>,
}
impl<'a> Sparkline<'a> {
pub fn new() -> Sparkline<'a> {
Sparkline {
block: None,
color: Color::White,
data: Vec::new(),
}
}
pub fn block(&mut self, block: Block<'a>) -> &mut Sparkline<'a> {
self.block = Some(block);
self
}
pub fn color(&mut self, color: Color) -> &mut Sparkline<'a> {
self.color = color;
self
}
pub fn data(&mut self, data: &[u64]) -> &mut Sparkline<'a> {
self.data = data.to_vec();
self
}
}
impl<'a> Widget for Sparkline<'a> {
fn buffer(&self, area: &Rect) -> Buffer {
let (mut buf, spark_area) = match self.block {
Some(ref b) => (b.buffer(area), area.inner(1)),
None => (Buffer::empty(*area), *area),
};
if spark_area.height < 1 {
return buf;
} else {
let margin = spark_area.x - area.x;
match self.data.iter().max() {
Some(max_value) => {
let max_index = min(spark_area.width as usize, self.data.len());
let line = self.data
.iter()
.take(max_index)
.filter_map(|e| {
let value = e * 8 / max_value;
match value {
0 => Some(' '),
1 => Some(bar::ONE_EIGHTH),
2 => Some(bar::ONE_QUATER),
3 => Some(bar::THREE_EIGHTHS),
4 => Some(bar::HALF),
5 => Some(bar::FIVE_EIGHTHS),
6 => Some(bar::THREE_EIGHTHS),
7 => Some(bar::THREE_QUATERS),
8 => Some(bar::FULL),
_ => None,
}
})
.collect::<String>();
buf.set_string(margin, margin, &line);
}
None => {}
}
}
buf
}
fn widget_type(&self) -> WidgetType {
WidgetType::Sparkline
}
}
Loading…
Cancel
Save