Add Canvas widget

* Add a canvas widget to draw shapes using braille dots
* Use this widget in the chart widget when the braille mode is selected
This commit is contained in:
Florian Dehau 2016-10-27 19:36:55 +02:00
parent da67a679f7
commit 7bdb11c681
3 changed files with 123 additions and 40 deletions

112
src/widgets/canvas.rs Normal file
View file

@ -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<Block<'a>>,
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<u16> = 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;
});
}
}
}
}
}

View file

@ -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<u16> = 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);
}
}
}

View file

@ -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;