diff --git a/src/widgets/canvas.rs b/src/widgets/canvas.rs new file mode 100644 index 0000000..2ed19d4 --- /dev/null +++ b/src/widgets/canvas.rs @@ -0,0 +1,112 @@ +use style::Color; +use buffer::Buffer; +use widgets::{Block, Widget}; +use layout::Rect; + +pub const DOTS: [[u16; 2]; 4] = + [[0x0001, 0x0008], [0x0002, 0x0010], [0x0004, 0x0020], [0x0040, 0x0080]]; +pub const BRAILLE_OFFSET: u16 = 0x2800; +pub const BRAILLE_BLANK: char = '⠀'; + +pub struct Shape<'a> { + data: &'a [(f64, f64)], + color: Color, +} + +impl<'a> Default for Shape<'a> { + fn default() -> Shape<'a> { + Shape { + data: &[], + color: Color::Reset, + } + } +} + +impl<'a> Shape<'a> { + pub fn data(mut self, data: &'a [(f64, f64)]) -> Shape<'a> { + self.data = data; + self + } + + pub fn color(mut self, color: Color) -> Shape<'a> { + self.color = color; + self + } +} + +pub struct Canvas<'a> { + block: Option>, + x_bounds: [f64; 2], + y_bounds: [f64; 2], + shapes: &'a [Shape<'a>], +} + +impl<'a> Default for Canvas<'a> { + fn default() -> Canvas<'a> { + Canvas { + block: None, + x_bounds: [0.0, 0.0], + y_bounds: [0.0, 0.0], + shapes: &[], + } + } +} + +impl<'a> Canvas<'a> { + pub fn block(&mut self, block: Block<'a>) -> &mut Canvas<'a> { + self.block = Some(block); + self + } + pub fn x_bounds(&mut self, bounds: [f64; 2]) -> &mut Canvas<'a> { + self.x_bounds = bounds; + self + } + pub fn y_bounds(&mut self, bounds: [f64; 2]) -> &mut Canvas<'a> { + self.y_bounds = bounds; + self + } + pub fn shapes(&mut self, shapes: &'a [Shape<'a>]) -> &mut Canvas<'a> { + self.shapes = shapes; + self + } +} + +impl<'a> Widget for Canvas<'a> { + fn buffer(&self, area: &Rect, buf: &mut Buffer) { + let canvas_area = match self.block { + Some(ref b) => { + b.buffer(area, buf); + b.inner(area) + } + None => *area, + }; + let width = canvas_area.width as usize; + let height = canvas_area.height as usize; + let mut grid: Vec = vec![BRAILLE_OFFSET; width * height + 1]; + for shape in self.shapes { + for &(x, y) in shape.data.iter() { + if x < self.x_bounds[0] || x > self.x_bounds[1] || y < self.y_bounds[0] || + y > self.y_bounds[1] { + continue; + } + let dy = ((self.y_bounds[1] - y) * canvas_area.height as f64 * 4.0 / + (self.y_bounds[1] - self.y_bounds[0])) as usize; + let dx = ((self.x_bounds[1] - x) * canvas_area.width as f64 * 2.0 / + (self.x_bounds[1] - self.x_bounds[0])) as usize; + grid[dy / 4 * width + dx / 2] |= DOTS[dy % 4][dx % 2]; + } + let string = String::from_utf16(&grid).unwrap(); + for (i, ch) in string.chars().enumerate() { + if ch != BRAILLE_BLANK { + let (x, y) = (i % width, i / width); + buf.update_cell(x as u16 + canvas_area.left(), y as u16 + area.top(), |c| { + c.symbol.clear(); + c.symbol.push(ch); + c.fg = shape.color; + c.bg = Color::Reset; + }); + } + } + } + } +} diff --git a/src/widgets/chart.rs b/src/widgets/chart.rs index b561e85..547500c 100644 --- a/src/widgets/chart.rs +++ b/src/widgets/chart.rs @@ -2,18 +2,12 @@ use std::cmp::max; use unicode_width::UnicodeWidthStr; -use widgets::{Widget, Block}; +use widgets::{Widget, Block, Canvas, Shape}; use buffer::Buffer; use layout::Rect; use style::Color; use symbols; -pub const DOTS: [[u16; 2]; 4] = - [[0x0001, 0x0008], [0x0002, 0x0010], [0x0004, 0x0020], [0x0040, 0x0080]]; -pub const BRAILLE_OFFSET: u16 = 0x2800; -pub const BRAILLE_BLANK: char = '⠀'; - - pub struct Axis<'a> { title: Option<&'a str>, title_color: Color, @@ -327,39 +321,11 @@ impl<'a> Widget for Chart<'a> { } } Marker::Braille => { - let width = graph_area.width as usize; - let height = graph_area.height as usize; - let mut grid: Vec = vec![BRAILLE_OFFSET; width * height + 1]; - for &(x, y) in dataset.data.iter() { - if x < self.x_axis.bounds[0] || x > self.x_axis.bounds[1] || - y < self.y_axis.bounds[0] || - y > self.y_axis.bounds[1] { - continue; - } - let dy = - ((self.y_axis.bounds[1] - y) * graph_area.height as f64 * 4.0 / - (self.y_axis.bounds[1] - - self.y_axis.bounds[0])) as usize; - let dx = - ((self.x_axis.bounds[1] - x) * graph_area.width as f64 * 2.0 / - (self.x_axis.bounds[1] - - self.x_axis.bounds[0])) as usize; - grid[dy / 4 * width + dx / 2] |= DOTS[dy % 4][dx % 2]; - } - let string = String::from_utf16(&grid).unwrap(); - for (i, ch) in string.chars().enumerate() { - if ch != BRAILLE_BLANK { - let (x, y) = (i % width, i / width); - buf.update_cell(x as u16 + graph_area.left(), - y as u16 + graph_area.top(), - |c| { - c.symbol.clear(); - c.symbol.push(ch); - c.fg = dataset.color; - c.bg = self.bg; - }); - } - } + Canvas::default() + .x_bounds(self.x_axis.bounds) + .y_bounds(self.y_axis.bounds) + .shapes(&[Shape::default().data(dataset.data).color(dataset.color)]) + .buffer(&graph_area, buf); } } } diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index 07047f5..613730c 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -5,6 +5,9 @@ mod gauge; mod sparkline; mod chart; mod barchart; +mod tabs; +mod canvas; +mod map; pub use self::block::Block; pub use self::text::Text; @@ -13,6 +16,8 @@ pub use self::gauge::Gauge; pub use self::sparkline::Sparkline; pub use self::chart::{Chart, Axis, Dataset, Marker}; pub use self::barchart::BarChart; +pub use self::tabs::Tabs; +pub use self::canvas::{Canvas, Shape}; use buffer::Buffer; use layout::Rect;