Remove cache system and add unicode segmentation

pull/3/head
Florian Dehau 8 years ago
parent bd404f0238
commit fde0ba95dd

@ -8,6 +8,7 @@ termion = "1.1.1"
bitflags = "0.7"
cassowary = "0.2.0"
log = "0.3"
unicode-segmentation = "0.1.2"
[dev-dependencies]
log4rs = "*"

@ -0,0 +1,43 @@
extern crate tui;
extern crate termion;
use std::io;
use termion::event;
use termion::input::TermRead;
use tui::Terminal;
use tui::widgets::{Widget, Block};
use tui::layout::{Group, Direction, Alignment, Size};
fn main() {
let mut terminal = Terminal::new().unwrap();
let stdin = io::stdin();
terminal.clear();
terminal.hide_cursor();
for c in stdin.keys() {
let evt = c.unwrap();
if evt == event::Key::Char('q') {
break;
}
draw(&mut terminal);
}
terminal.show_cursor();
}
fn draw(t: &mut Terminal) {
Group::default()
.direction(Direction::Vertical)
.alignment(Alignment::Left)
.chunks(&[Size::Fixed(7), Size::Min(5), Size::Fixed(3)])
.render(&Terminal::size().unwrap(), |chunks| {
t.render(chunks[0], Block::default().title("Block"));
Group::default()
.direction(Direction::Vertical)
.alignment(Alignment::Left)
.chunks(&[Size::Fixed(7), Size::Min(5), Size::Fixed(3)])
.render(&chunks[1], |chunks| {
t.render(chunks[0], Block::default().title("Block"));
});
});
}

@ -148,6 +148,7 @@ fn main() {
terminal.hide_cursor();
loop {
terminal.clear();
draw(&mut terminal, &app);
let evt = rx.recv().unwrap();
match evt {
@ -195,70 +196,59 @@ fn main() {
terminal.show_cursor();
}
fn draw(terminal: &mut Terminal, app: &App) {
fn draw(t: &mut Terminal, app: &App) {
let ui = Group::default()
Group::default()
.direction(Direction::Vertical)
.alignment(Alignment::Left)
.chunks(&[Size::Fixed(7), Size::Min(5), Size::Fixed(3)])
.render(&Terminal::size().unwrap(), |chunks, tree| {
tree.add(Block::default().borders(border::ALL).title("Graphs").render(&chunks[0]));
tree.add(Group::default()
.render(&Terminal::size().unwrap(), |chunks| {
Block::default().borders(border::ALL).title("Graphs").render(&chunks[0], t);
Group::default()
.direction(Direction::Vertical)
.alignment(Alignment::Left)
.margin(1)
.chunks(&[Size::Fixed(2), Size::Fixed(3)])
.render(&chunks[0], |chunks, tree| {
tree.add(Gauge::default()
.render(&chunks[0], |chunks| {
Gauge::default()
.block(*Block::default().title("Gauge:"))
.bg(Color::Yellow)
.percent(app.progress)
.render(&chunks[0]));
tree.add(Sparkline::default()
.render(&chunks[0], t);
Sparkline::default()
.block(*Block::default().title("Sparkline:"))
.fg(Color::Green)
.data(&app.data)
.render(&chunks[1]));
}));
.render(&chunks[1], t);
});
let sizes = if app.show_chart {
vec![Size::Max(40), Size::Min(20)]
} else {
vec![Size::Max(40)]
};
tree.add(Group::default()
Group::default()
.direction(Direction::Horizontal)
.alignment(Alignment::Left)
.chunks(&sizes)
.render(&chunks[1], |chunks, tree| {
tree.add(List::default()
.render(&chunks[1], |chunks| {
List::default()
.block(*Block::default().borders(border::ALL).title("List"))
.items(&app.items)
.select(app.selected)
.formatter(|i, s| {
let (prefix, fg) = if s {
(">", Color::Cyan)
} else {
("*", Color::White)
};
(format!("{} {}", prefix, i), fg, Color::Black)
})
.render(&chunks[0]));
.render(&chunks[0], t);
if app.show_chart {
tree.add(Chart::default()
Chart::default()
.block(*Block::default()
.borders(border::ALL)
.title("Chart"))
.fg(Color::Cyan)
.axis([0, 40])
.data(&app.data2)
.render(&chunks[1]));
.render(&chunks[1], t);
}
}));
tree.add(Text::default()
});
Text::default()
.block(*Block::default().borders(border::ALL).title("Footer"))
.fg(app.colors[app.color_index])
.text("This is a footer")
.render(&chunks[2]));
.text("This żółw is a footer")
.render(&chunks[2], t);
});
terminal.render(ui);
}

@ -1,17 +1,19 @@
use unicode_segmentation::UnicodeSegmentation;
use layout::Rect;
use style::Color;
#[derive(Debug, Clone)]
pub struct Cell {
pub symbol: char,
pub struct Cell<'a> {
pub symbol: &'a str,
pub fg: Color,
pub bg: Color,
}
impl Default for Cell {
fn default() -> Cell {
impl<'a> Default for Cell<'a> {
fn default() -> Cell<'a> {
Cell {
symbol: ' ',
symbol: "",
fg: Color::White,
bg: Color::Black,
}
@ -19,13 +21,13 @@ impl Default for Cell {
}
#[derive(Debug, Clone)]
pub struct Buffer {
pub struct Buffer<'a> {
area: Rect,
content: Vec<Cell>,
content: Vec<Cell<'a>>,
}
impl Default for Buffer {
fn default() -> Buffer {
impl<'a> Default for Buffer<'a> {
fn default() -> Buffer<'a> {
Buffer {
area: Default::default(),
content: Vec::new(),
@ -33,13 +35,13 @@ impl Default for Buffer {
}
}
impl Buffer {
pub fn empty(area: Rect) -> Buffer {
impl<'a> Buffer<'a> {
pub fn empty(area: Rect) -> Buffer<'a> {
let cell: Cell = Default::default();
Buffer::filled(area, cell)
}
pub fn filled(area: Rect, cell: Cell) -> Buffer {
pub fn filled(area: Rect, cell: Cell<'a>) -> Buffer<'a> {
let size = area.area() as usize;
let mut content = Vec::with_capacity(size);
for _ in 0..size {
@ -51,7 +53,7 @@ impl Buffer {
}
}
pub fn content(&self) -> &[Cell] {
pub fn content(&'a self) -> &'a [Cell<'a>] {
&self.content
}
@ -83,12 +85,12 @@ impl Buffer {
}
}
pub fn set(&mut self, x: u16, y: u16, cell: Cell) {
pub fn set(&mut self, x: u16, y: u16, cell: Cell<'a>) {
let i = self.index_of(x, y);
self.content[i] = cell;
}
pub fn set_symbol(&mut self, x: u16, y: u16, symbol: char) {
pub fn set_symbol(&mut self, x: u16, y: u16, symbol: &'a str) {
let i = self.index_of(x, y);
self.content[i].symbol = symbol;
}
@ -102,11 +104,12 @@ impl Buffer {
self.content[i].bg = color;
}
pub fn set_string(&mut self, x: u16, y: u16, string: &str, fg: Color, bg: Color) {
pub fn set_string(&mut self, x: u16, y: u16, string: &'a str, fg: Color, bg: Color) {
let mut cursor = (x, y);
for c in string.chars() {
for s in UnicodeSegmentation::graphemes(string, true).collect::<Vec<&str>>() {
info!("{}", s);
let index = self.index_of(cursor.0, cursor.1);
self.content[index].symbol = c;
self.content[index].symbol = s;
self.content[index].fg = fg;
self.content[index].bg = bg;
match self.next_pos(cursor.0, cursor.1) {
@ -127,12 +130,7 @@ impl Buffer {
f(&mut self.content[i]);
}
pub fn get(&self, x: u16, y: u16) -> &Cell {
let i = self.index_of(x, y);
&self.content[i]
}
pub fn merge(&mut self, other: &Buffer) {
pub fn merge(&'a mut self, other: Buffer<'a>) {
let area = self.area.union(&other.area);
let cell: Cell = Default::default();
self.content.resize(area.area() as usize, cell.clone());

@ -6,9 +6,7 @@ use cassowary::WeightedRelation::*;
use cassowary::strength::{WEAK, REQUIRED};
use buffer::Buffer;
use widgets::WidgetType;
#[derive(Hash)]
pub enum Alignment {
Top,
Left,
@ -17,13 +15,12 @@ pub enum Alignment {
Right,
}
#[derive(Hash)]
pub enum Direction {
Horizontal,
Vertical,
}
#[derive(Hash, Debug, Clone, Copy, Eq, PartialEq)]
#[derive(Debug, Clone, Copy)]
pub struct Rect {
pub x: u16,
pub y: u16,
@ -238,67 +235,6 @@ impl Element {
}
}
pub enum Tree {
Node(Node),
Leaf(Leaf),
}
impl IntoIterator for Tree {
type Item = Leaf;
type IntoIter = WidgetIterator;
fn into_iter(self) -> WidgetIterator {
WidgetIterator::new(self)
}
}
pub struct WidgetIterator {
stack: Vec<Tree>,
}
impl WidgetIterator {
fn new(tree: Tree) -> WidgetIterator {
WidgetIterator { stack: vec![tree] }
}
}
impl Iterator for WidgetIterator {
type Item = Leaf;
fn next(&mut self) -> Option<Leaf> {
match self.stack.pop() {
Some(t) => {
match t {
Tree::Node(n) => {
let index = self.stack.len();
for c in n.children {
self.stack.insert(index, c);
}
self.next()
}
Tree::Leaf(l) => Some(l),
}
}
None => None,
}
}
}
pub struct Node {
pub children: Vec<Tree>,
}
impl Node {
pub fn add(&mut self, node: Tree) {
self.children.push(node);
}
}
pub struct Leaf {
pub widget_type: WidgetType,
pub hash: u64,
pub buffer: Buffer,
}
pub struct Group {
direction: Direction,
alignment: Alignment,
@ -337,16 +273,14 @@ impl Group {
self.chunks = Vec::from(chunks);
self
}
pub fn render<F>(&self, area: &Rect, f: F) -> Tree
where F: Fn(&[Rect], &mut Node)
pub fn render<F>(&self, area: &Rect, mut f: F)
where F: FnMut(&[Rect])
{
let chunks = split(area,
&self.direction,
&self.alignment,
self.margin,
&self.chunks);
let mut node = Node { children: Vec::with_capacity(chunks.len()) };
f(&chunks, &mut node);
Tree::Node(node)
f(&chunks);
}
}

@ -4,6 +4,7 @@ extern crate bitflags;
#[macro_use]
extern crate log;
extern crate cassowary;
extern crate unicode_segmentation;
mod buffer;
mod util;

@ -1,23 +1,36 @@
pub mod block {
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 = '▏';
pub const FULL: &'static str = "█";
pub const SEVEN_EIGHTHS: &'static str = "▉";
pub const THREE_QUATERS: &'static str = "▊";
pub const FIVE_EIGHTHS: &'static str = "▋";
pub const HALF: &'static str = "▌";
pub const THREE_EIGHTHS: &'static str = "▍";
pub const ONE_QUATER: &'static str = "▎";
pub const ONE_EIGHTH: &'static str = "▏";
}
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 = '▁';
pub const FULL: &'static str = "█";
pub const SEVEN_EIGHTHS: &'static str = "▇";
pub const THREE_QUATERS: &'static str = "▆";
pub const FIVE_EIGHTHS: &'static str = "▅";
pub const HALF: &'static str = "▄";
pub const THREE_EIGHTHS: &'static str = "▃";
pub const ONE_QUATER: &'static str = "▂";
pub const ONE_EIGHTH: &'static str = "▁";
}
pub const DOT: char = '•';
pub mod line {
pub const TOP_RIGHT: &'static str = "┐";
pub const VERTICAL: &'static str = "│";
pub const HORIZONTAL: &'static str = "─";
pub const TOP_LEFT: &'static str = "┌";
pub const BOTTOM_RIGHT: &'static str = "┘";
pub const BOTTOM_LEFT: &'static str = "└";
pub const VERTICAL_LEFT: &'static str = "┤";
pub const VERTICAL_RIGHT: &'static str = "├";
pub const HORIZONTAL_DOWN: &'static str = "┬";
pub const HORIZONTAL_UP: &'static str = "┴";
}
pub const DOT: &'static str = "•";

@ -6,21 +6,18 @@ use termion;
use termion::raw::{IntoRawMode, RawTerminal};
use buffer::Buffer;
use widgets::WidgetType;
use layout::{Rect, Tree};
use widgets::Widget;
use layout::Rect;
use util::hash;
pub struct Terminal {
stdout: RawTerminal<io::Stdout>,
cache: HashMap<(WidgetType, Rect), u64>,
}
impl Terminal {
pub fn new() -> Result<Terminal, io::Error> {
let stdout = try!(io::stdout().into_raw_mode());
Ok(Terminal {
stdout: stdout,
cache: HashMap::new(),
})
Ok(Terminal { stdout: stdout })
}
pub fn size() -> Result<Rect, io::Error> {
@ -33,49 +30,19 @@ impl Terminal {
})
}
pub fn render(&mut self, ui: Tree) {
debug!("Render Pass");
let mut buffers: Vec<Buffer> = Vec::new();
let mut cache: HashMap<(WidgetType, Rect), u64> = HashMap::new();
for node in ui {
let area = *node.buffer.area();
match self.cache.remove(&(node.widget_type, area)) {
Some(h) => {
if h == node.hash {
debug!("Skip {:?} at {:?}", node.widget_type, area);
} else {
debug!("Update {:?} at {:?}", node.widget_type, area);
buffers.push(node.buffer);
}
}
None => {
buffers.push(node.buffer);
debug!("Render {:?} at {:?}", node.widget_type, area);
}
}
cache.insert((node.widget_type, area), node.hash);
}
for &(t, a) in self.cache.keys() {
buffers.insert(0, Buffer::empty(a));
debug!("Erased {:?} at {:?}", t, a);
}
for buf in buffers {
self.render_buffer(&buf);
}
self.cache = cache;
}
pub fn render_buffer(&mut self, buffer: &Buffer) {
pub fn render_buffer(&mut self, buffer: Buffer) {
for (i, cell) in buffer.content().iter().enumerate() {
let (lx, ly) = buffer.pos_of(i);
let (x, y) = (lx + buffer.area().x, ly + buffer.area().y);
write!(self.stdout,
"{}{}{}{}",
termion::cursor::Goto(x + 1, y + 1),
cell.fg.fg(),
cell.bg.bg(),
cell.symbol)
.unwrap();
if cell.symbol != "" {
write!(self.stdout,
"{}{}{}{}",
termion::cursor::Goto(x + 1, y + 1),
cell.fg.fg(),
cell.bg.bg(),
cell.symbol)
.unwrap();
}
}
self.stdout.flush().unwrap();
}

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

@ -2,9 +2,10 @@
use buffer::Buffer;
use layout::Rect;
use style::Color;
use widgets::{Widget, WidgetType, border, Line, vline, hline};
use widgets::{Widget, border};
use symbols::line;
#[derive(Hash, Clone, Copy)]
#[derive(Clone, Copy)]
pub struct Block<'a> {
title: Option<&'a str>,
title_fg: Color,
@ -82,8 +83,8 @@ impl<'a> Block<'a> {
}
}
impl<'a> Widget for Block<'a> {
fn buffer(&self, area: &Rect) -> Buffer {
impl<'a> Widget<'a> for Block<'a> {
fn buffer(&'a self, area: &Rect) -> Buffer<'a> {
let mut buf = Buffer::empty(*area);
@ -93,55 +94,65 @@ impl<'a> Widget for Block<'a> {
// Sides
if self.borders.intersects(border::LEFT) {
let line = vline(area.x, area.y, area.height, self.border_fg, self.border_bg);
buf.merge(&line);
for y in 0..area.height {
let c = buf.update_cell(0, y, |c| {
c.symbol = line::VERTICAL;
c.fg = self.border_fg;
c.bg = self.border_bg;
});
}
}
if self.borders.intersects(border::TOP) {
let line = hline(area.x, area.y, area.width, self.border_fg, self.border_bg);
buf.merge(&line);
for x in 0..area.width {
let c = buf.update_cell(x, 0, |c| {
c.symbol = line::HORIZONTAL;
c.fg = self.border_fg;
c.bg = self.border_bg;
});
}
}
if self.borders.intersects(border::RIGHT) {
let line = vline(area.x + area.width - 1,
area.y,
area.height,
self.border_fg,
self.border_bg);
buf.merge(&line);
let x = area.width - 1;
for y in 0..area.height {
buf.update_cell(x, y, |c| {
c.symbol = line::VERTICAL;
c.fg = self.border_fg;
c.bg = self.border_bg;
});
}
}
if self.borders.intersects(border::BOTTOM) {
let line = hline(area.x,
area.y + area.height - 1,
area.width,
self.border_fg,
self.border_bg);
buf.merge(&line);
let y = area.height - 1;
for x in 0..area.width {
buf.update_cell(x, y, |c| {
c.symbol = line::HORIZONTAL;
c.fg = self.border_fg;
c.bg = self.border_bg;
});
}
}
// Corners
if self.borders.contains(border::LEFT | border::TOP) {
buf.set_symbol(0, 0, Line::TopLeft.get());
buf.set_symbol(0, 0, line::TOP_LEFT);
}
if self.borders.contains(border::RIGHT | border::TOP) {
buf.set_symbol(area.width - 1, 0, Line::TopRight.get());
buf.set_symbol(area.width - 1, 0, line::TOP_RIGHT);
}
if self.borders.contains(border::BOTTOM | border::LEFT) {
buf.set_symbol(0, area.height - 1, Line::BottomLeft.get());
buf.set_symbol(0, area.height - 1, line::BOTTOM_LEFT);
}
if self.borders.contains(border::BOTTOM | border::RIGHT) {
buf.set_symbol(area.width - 1, area.height - 1, Line::BottomRight.get());
buf.set_symbol(area.width - 1, area.height - 1, line::BOTTOM_RIGHT);
}
if let Some(title) = self.title {
let (margin_x, string) = if self.borders.intersects(border::LEFT) {
(1, format!(" {} ", title))
let margin_x = if self.borders.intersects(border::LEFT) {
1
} else {
(0, String::from(title))
0
};
buf.set_string(margin_x, 0, &string, self.title_fg, self.title_bg);
buf.set_string(margin_x, 0, &title, self.title_fg, self.title_bg);
}
buf
}
fn widget_type(&self) -> WidgetType {
WidgetType::Block
}
}

@ -1,12 +1,12 @@
use std::cmp::min;
use widgets::{Widget, WidgetType, Block};
use widgets::{Widget, Block};
use buffer::Buffer;
use layout::Rect;
use style::Color;
use util::hash;
use symbols;
#[derive(Hash)]
pub struct Chart<'a> {
block: Option<Block<'a>>,
fg: Color,
@ -55,8 +55,8 @@ impl<'a> Chart<'a> {
}
}
impl<'a> Widget for Chart<'a> {
fn buffer(&self, area: &Rect) -> Buffer {
impl<'a> Widget<'a> for Chart<'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),
@ -82,7 +82,4 @@ impl<'a> Widget for Chart<'a> {
}
buf
}
fn widget_type(&self) -> WidgetType {
WidgetType::Chart
}
}

@ -1,7 +1,8 @@
use widgets::{Widget, WidgetType, Block};
use widgets::{Widget, Block};
use buffer::Buffer;
use style::Color;
use layout::Rect;
use util::hash;
/// Progress bar widget
///
@ -17,10 +18,10 @@ use layout::Rect;
/// .percent(20);
/// }
/// ```
#[derive(Hash)]
pub struct Gauge<'a> {
block: Option<Block<'a>>,
percent: u16,
percent_string: String,
fg: Color,
bg: Color,
}
@ -30,6 +31,7 @@ impl<'a> Default for Gauge<'a> {
Gauge {
block: None,
percent: 0,
percent_string: String::from("0%"),
bg: Color::White,
fg: Color::Black,
}
@ -44,6 +46,7 @@ impl<'a> Gauge<'a> {
pub fn percent(&mut self, percent: u16) -> &mut Gauge<'a> {
self.percent = percent;
self.percent_string = format!("{}%", percent);
self
}
@ -58,8 +61,8 @@ impl<'a> Gauge<'a> {
}
}
impl<'a> Widget for Gauge<'a> {
fn buffer(&self, area: &Rect) -> Buffer {
impl<'a> Widget<'a> for Gauge<'a> {
fn buffer(&'a self, area: &Rect) -> Buffer<'a> {
let (mut buf, gauge_area) = match self.block {
Some(ref b) => (b.buffer(area), b.inner(*area)),
None => (Buffer::empty(*area), *area),
@ -69,22 +72,23 @@ impl<'a> Widget for Gauge<'a> {
} else {
let margin_x = gauge_area.x - area.x;
let margin_y = gauge_area.y - area.y;
// Label
let percent_string = format!("{}%", self.percent);
let len = percent_string.len() as u16;
let middle = gauge_area.width / 2 - len / 2;
buf.set_string(middle, margin_y, &percent_string, self.bg, self.fg);
// Gauge
let width = (gauge_area.width * self.percent) / 100;
info!("{}", width);
// Label
let len = self.percent_string.len() as u16;
let middle = gauge_area.width / 2 - len / 2;
buf.set_string(middle, margin_y, &self.percent_string, self.bg, self.fg);
for i in 0..width {
buf.set_bg(margin_x + i, margin_y, self.bg);
buf.set_fg(margin_x + i, margin_y, self.fg);
buf.update_cell(margin_x + i, margin_y, |c| {
if c.symbol == "" {
c.symbol = " "
};
c.fg = self.fg;
c.bg = self.bg;
})
}
}
buf
}
fn widget_type(&self) -> WidgetType {
WidgetType::Gauge
}
}

@ -2,71 +2,44 @@ use std::cmp::min;
use std::hash::{Hash, Hasher};
use buffer::Buffer;
use widgets::{Widget, WidgetType, Block};
use widgets::{Widget, Block};
use layout::Rect;
use style::Color;
use util::hash;
pub struct List<'a, T> {
pub struct List<'a> {
block: Option<Block<'a>>,
selected: usize,
formatter: Box<Fn(&T, bool) -> (String, Color, Color)>,
items: Vec<T>,
items: Vec<(&'a str, Color, Color)>,
}
impl<'a, T> Hash for List<'a, T>
where T: Hash
{
fn hash<H: Hasher>(&self, state: &mut H) {
self.block.hash(state);
self.selected.hash(state);
self.items.hash(state);
}
}
impl<'a, T> Default for List<'a, T> {
fn default() -> List<'a, T> {
impl<'a> Default for List<'a> {
fn default() -> List<'a> {
List {
block: None,
selected: 0,
formatter: Box::new(|_, _| (String::from(""), Color::White, Color::Black)),
items: Vec::new(),
}
}
}
impl<'a, T> List<'a, T>
where T: Clone
{
pub fn block(&'a mut self, block: Block<'a>) -> &mut List<'a, T> {
impl<'a> List<'a> {
pub fn block(&'a mut self, block: Block<'a>) -> &mut List<'a> {
self.block = Some(block);
self
}
pub fn formatter<F>(&'a mut self, f: F) -> &mut List<'a, T>
where F: 'static + Fn(&T, bool) -> (String, Color, Color)
{
self.formatter = Box::new(f);
self
}
pub fn items(&'a mut self, items: &'a [T]) -> &mut List<'a, T> {
pub fn items(&'a mut self, items: &[(&'a str, Color, Color)]) -> &mut List<'a> {
self.items = items.to_vec();
self
}
pub fn select(&'a mut self, index: usize) -> &mut List<'a, T> {
self.selected = index;
self
}
}
impl<'a, T> Widget for List<'a, T>
where T: Hash
{
fn buffer(&self, area: &Rect) -> Buffer {
impl<'a> Widget<'a> for List<'a> {
fn buffer(&'a self, area: &Rect) -> Buffer<'a> {
let (mut buf, list_area) = match self.block {
Some(ref b) => (b.buffer(area), area.inner(1)),
Some(ref b) => (b.buffer(area), b.inner(*area)),
None => (Buffer::empty(*area), *area),
};
@ -80,16 +53,9 @@ impl<'a, T> Widget for List<'a, T>
};
for i in 0..bound {
let index = i + offset;
let item = &self.items[index];
let formatter = &self.formatter;
let (mut string, fg, bg) = formatter(item, self.selected == index);
string.truncate(list_area.width as usize);
buf.set_string(1, 1 + i as u16, &string, fg, bg);
let (item, fg, bg) = self.items[index];
buf.set_string(1, 1 + i as u16, item, fg, bg);
}
buf
}
fn widget_type(&self) -> WidgetType {
WidgetType::List
}
}

@ -16,22 +16,9 @@ use std::hash::Hash;
use util::hash;
use buffer::{Buffer, Cell};
use layout::{Rect, Tree, Leaf};
use layout::Rect;
use style::Color;
#[allow(dead_code)]
enum Line {
Horizontal,
Vertical,
TopLeft,
TopRight,
BottomLeft,
BottomRight,
VerticalLeft,
VerticalRight,
HorizontalDown,
HorizontalUp,
}
use terminal::Terminal;
pub mod border {
bitflags! {
@ -46,71 +33,9 @@ pub mod border {
}
}
impl Line {
fn get(&self) -> char {
match *self {
Line::TopRight => '┐',
Line::Vertical => '│',
Line::Horizontal => '─',
Line::TopLeft => '┌',
Line::BottomRight => '┘',
Line::BottomLeft => '└',
Line::VerticalLeft => '┤',
Line::VerticalRight => '├',
Line::HorizontalDown => '┬',
Line::HorizontalUp => '┴',
}
}
}
fn hline(x: u16, y: u16, len: u16, fg: Color, bg: Color) -> Buffer {
Buffer::filled(Rect {
x: x,
y: y,
width: len,
height: 1,
},
Cell {
symbol: Line::Horizontal.get(),
fg: fg,
bg: bg,
})
}
fn vline(x: u16, y: u16, len: u16, fg: Color, bg: Color) -> Buffer {
Buffer::filled(Rect {
x: x,
y: y,
width: 1,
height: len,
},
Cell {
symbol: Line::Vertical.get(),
fg: fg,
bg: bg,
})
}
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
pub enum WidgetType {
Block,
Text,
List,
Gauge,
Sparkline,
Chart,
}
pub trait Widget: Hash {
fn buffer(&self, area: &Rect) -> Buffer;
fn widget_type(&self) -> WidgetType;
fn render(&self, area: &Rect) -> Tree {
let widget_type = self.widget_type();
let hash = hash(&self, area);
let buffer = self.buffer(area);
Tree::Leaf(Leaf {
widget_type: widget_type,
hash: hash,
buffer: buffer,
})
pub trait Widget<'a> {
fn buffer(&'a self, area: &Rect) -> Buffer<'a>;
fn render(&'a self, area: &Rect, t: &mut Terminal) {
t.render_buffer(self.buffer(area));
}
}

@ -2,11 +2,10 @@ use std::cmp::min;
use layout::Rect;
use buffer::Buffer;
use widgets::{Widget, WidgetType, Block};
use widgets::{Widget, Block};
use style::Color;
use symbols::bar;
#[derive(Hash)]
pub struct Sparkline<'a> {
block: Option<Block<'a>>,
fg: Color,
@ -55,8 +54,8 @@ impl<'a> Sparkline<'a> {
}
}
impl<'a> Widget for Sparkline<'a> {
fn buffer(&self, area: &Rect) -> Buffer {
impl<'a> Widget<'a> for Sparkline<'a> {
fn buffer(&'a self, area: &Rect) -> Buffer<'a> {
let (mut buf, spark_area) = match self.block {
Some(ref b) => (b.buffer(area), b.inner(*area)),
None => (Buffer::empty(*area), *area),
@ -76,18 +75,21 @@ impl<'a> Widget for Sparkline<'a> {
.map(|e| e * spark_area.height as u64 * 8 / max)
.collect::<Vec<u64>>();
for j in (0..spark_area.height).rev() {
let mut line = String::with_capacity(max_index);
for d in data.iter_mut().take(max_index) {
line.push(match *d {
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 (i, d) in data.iter_mut().take(max_index).enumerate() {
buf.update_cell(margin_x + i as u16, margin_y + j, |c| {
c.symbol = &match *d {
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,
};
c.fg = self.fg;
c.bg = self.bg;
});
if *d > 8 {
*d -= 8;
@ -95,13 +97,8 @@ impl<'a> Widget for Sparkline<'a> {
*d = 0;
}
}
buf.set_string(margin_x, margin_y + j, &line, self.fg, self.bg);
}
}
buf
}
fn widget_type(&self) -> WidgetType {
WidgetType::Sparkline
}
}

@ -1,16 +1,16 @@
use std::cmp::min;
use widgets::{Widget, WidgetType, Block};
use widgets::{Widget, Block};
use buffer::Buffer;
use layout::Rect;
use style::Color;
#[derive(Hash)]
pub struct Text<'a> {
block: Option<Block<'a>>,
fg: Color,
bg: Color,
text: &'a str,
colors: &'a [(u16, u16, u16, Color, Color)],
}
impl<'a> Default for Text<'a> {
@ -20,6 +20,7 @@ impl<'a> Default for Text<'a> {
fg: Color::White,
bg: Color::Black,
text: "",
colors: &[],
}
}
}
@ -44,26 +45,35 @@ impl<'a> Text<'a> {
self.fg = fg;
self
}
pub fn colors(&mut self, colors: &'a [(u16, u16, u16, Color, Color)]) -> &mut Text<'a> {
self.colors = colors;
self
}
}
impl<'a> Widget for Text<'a> {
fn buffer(&self, area: &Rect) -> Buffer {
impl<'a> Widget<'a> for Text<'a> {
fn buffer(&'a self, area: &Rect) -> Buffer<'a> {
let (mut buf, text_area) = match self.block {
Some(b) => (b.buffer(area), b.inner(*area)),
Some(ref b) => (b.buffer(area), b.inner(*area)),
None => (Buffer::empty(*area), *area),
};
let mut lines = self.text.lines().map(String::from).collect::<Vec<String>>();
let mut lines = self.text.lines().collect::<Vec<&str>>();
let margin_x = text_area.x - area.x;
let margin_y = text_area.y - area.y;
let height = min(lines.len(), text_area.height as usize);
let width = text_area.width as usize;
for line in lines.iter_mut().take(height) {
line.truncate(width);
buf.set_string(margin_x, margin_y, line, self.fg, self.bg);
buf.set_string(margin_x, margin_y, &line, self.fg, self.bg);
}
for &(x, y, width, fg, bg) in self.colors {
for i in 0..width {
buf.update_cell(x + i, y + margin_y, |c| {
c.fg = fg;
c.bg = bg;
})
}
}
buf
}
fn widget_type(&self) -> WidgetType {
WidgetType::Text
}
}

Loading…
Cancel
Save