diff --git a/examples/prototype.rs b/examples/prototype.rs index 81c0af2..36b0b0d 100644 --- a/examples/prototype.rs +++ b/examples/prototype.rs @@ -82,7 +82,7 @@ struct App { progress: u16, data: Vec, data2: Vec<(f64, f64)>, - window: [f64; 2], + data3: Vec<(f64, f64)>, colors: [Color; 2], color_index: usize, } @@ -109,6 +109,7 @@ fn main() { let mut rand_signal = RandomSignal::new(Range::new(0, 100)); let mut sin_signal = SinSignal::new(4.0, 20.0); + let mut sin_signal2 = SinSignal::new(2.0, 10.0); let mut app = App { name: String::from("Test app"), @@ -119,7 +120,7 @@ fn main() { progress: 0, data: rand_signal.clone().take(100).collect(), data2: sin_signal.clone().take(100).collect(), - window: [0.0, 100.0], + data3: sin_signal2.clone().take(100).collect(), colors: [Color::Magenta, Color::Red], color_index: 0, }; @@ -141,7 +142,7 @@ fn main() { let tx = tx.clone(); loop { tx.send(Event::Tick).unwrap(); - thread::sleep(time::Duration::from_millis(500)); + thread::sleep(time::Duration::from_millis(1000)); } }); @@ -184,8 +185,8 @@ fn main() { app.data.pop(); app.data2.remove(0); app.data2.push(sin_signal.next().unwrap()); - app.window[0] += 1.0; - app.window[1] += 1.0; + app.data3.remove(0); + app.data3.push(sin_signal2.next().unwrap()); app.selected += 1; if app.selected >= app.items.len() { app.selected = 0; @@ -206,14 +207,14 @@ fn draw(t: &mut Terminal, app: &App) { .direction(Direction::Vertical) .alignment(Alignment::Left) .chunks(&[Size::Fixed(7), Size::Min(5), Size::Fixed(3)]) - .render(&Terminal::size().unwrap(), |chunks| { + .render(t, &Terminal::size().unwrap(), |t, 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| { + .render(t, &chunks[0], |t, chunks| { Gauge::default() .block(Block::default().title("Gauge:")) .bg(Color::Yellow) @@ -234,7 +235,7 @@ fn draw(t: &mut Terminal, app: &App) { .direction(Direction::Horizontal) .alignment(Alignment::Left) .chunks(&sizes) - .render(&chunks[1], |chunks| { + .render(t, &chunks[1], |t, chunks| { List::default() .block(Block::default().borders(border::ALL).title("List")) .render(&chunks[0], t); @@ -243,9 +244,10 @@ fn draw(t: &mut Terminal, app: &App) { .block(Block::default() .borders(border::ALL) .title("Chart")) - .x_axis(Axis::default().title("X").bounds(app.window)) + .x_axis(Axis::default().title("X").bounds([0.0, 100.0])) .y_axis(Axis::default().title("Y").bounds([0.0, 40.0])) - .datasets(&[Dataset::default().color(Color::Cyan).data(&app.data2)]) + .datasets(&[Dataset::default().color(Color::Cyan).data(&app.data2), + Dataset::default().color(Color::Yellow).data(&app.data3)]) .render(&chunks[1], t); } }); diff --git a/src/layout.rs b/src/layout.rs index ff6d6e3..89b051f 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -6,7 +6,10 @@ use cassowary::WeightedRelation::*; use cassowary::strength::{WEAK, REQUIRED}; use buffer::Buffer; +use terminal::Terminal; +use util::hash; +#[derive(Hash)] pub enum Alignment { Top, Left, @@ -15,12 +18,13 @@ pub enum Alignment { Right, } +#[derive(Hash)] pub enum Direction { Horizontal, Vertical, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] pub struct Rect { pub x: u16, pub y: u16, @@ -235,6 +239,7 @@ impl Element { } } +#[derive(Hash)] pub struct Group { direction: Direction, alignment: Alignment, @@ -273,14 +278,26 @@ impl Group { self.chunks = Vec::from(chunks); self } - pub fn render(&self, area: &Rect, mut f: F) - where F: FnMut(&[Rect]) + pub fn render(&self, t: &mut Terminal, area: &Rect, mut f: F) + where F: FnMut(&mut Terminal, &[Rect]) { - let chunks = split(area, - &self.direction, - &self.alignment, - self.margin, - &self.chunks); - f(&chunks); + let hash = hash(self, area); + let (cache_update, chunks) = match t.get_layout(hash) { + Some(chs) => (false, chs.to_vec()), + None => { + (true, + split(area, + &self.direction, + &self.alignment, + self.margin, + &self.chunks)) + } + }; + + f(t, &chunks); + + if cache_update { + t.set_layout(hash, chunks); + } } } diff --git a/src/terminal.rs b/src/terminal.rs index 6367b27..a53edc3 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -8,16 +8,19 @@ use termion::raw::{IntoRawMode, RawTerminal}; use buffer::Buffer; use widgets::Widget; use layout::Rect; -use util::hash; pub struct Terminal { stdout: RawTerminal, + layout_cache: HashMap>, } impl Terminal { pub fn new() -> Result { let stdout = try!(io::stdout().into_raw_mode()); - Ok(Terminal { stdout: stdout }) + Ok(Terminal { + stdout: stdout, + layout_cache: HashMap::new(), + }) } pub fn size() -> Result { @@ -30,20 +33,29 @@ impl Terminal { }) } + // FIXME: Clean cache to prevent memory leak + pub fn get_layout(&self, hash: u64) -> Option<&Vec> { + self.layout_cache.get(&hash) + } + + pub fn set_layout(&mut self, hash: u64, chunks: Vec) { + self.layout_cache.insert(hash, chunks); + } + pub fn render_buffer(&mut self, buffer: Buffer) { + let mut string = String::with_capacity(buffer.area().area() as usize); for (i, cell) in buffer.content().iter().enumerate() { let (lx, ly) = buffer.pos_of(i).unwrap(); let (x, y) = (lx + buffer.area().x, ly + buffer.area().y); if cell.symbol != "" { - write!(self.stdout, - "{}{}{}{}", - termion::cursor::Goto(x + 1, y + 1), - cell.fg.fg(), - cell.bg.bg(), - cell.symbol) - .unwrap(); + string.push_str(&format!("{}{}{}{}", + termion::cursor::Goto(x + 1, y + 1), + cell.fg.fg(), + cell.bg.bg(), + cell.symbol)) } } + write!(self.stdout, "{}", string); self.stdout.flush().unwrap(); } pub fn clear(&mut self) { diff --git a/src/util.rs b/src/util.rs index 5ab4da3..cbf0e81 100644 --- a/src/util.rs +++ b/src/util.rs @@ -3,9 +3,10 @@ use std::hash::{Hash, Hasher, BuildHasher}; use layout::Rect; -pub fn hash(t: &T) -> u64 { +pub fn hash(t: &T, area: &Rect) -> u64 { let state = RandomState::new(); let mut hasher = state.build_hasher(); t.hash(&mut hasher); + area.hash(&mut hasher); hasher.finish() } diff --git a/src/widgets/chart.rs b/src/widgets/chart.rs index 97a5fa1..eb4137d 100644 --- a/src/widgets/chart.rs +++ b/src/widgets/chart.rs @@ -138,7 +138,6 @@ impl<'a> Widget<'a> for Chart<'a> { let margin_x = chart_area.x - area.x; let margin_y = chart_area.y - area.y; - // info!("{:?}", self.datasets[0].data[0]); for dataset in self.datasets { for &(x, y) in dataset.data.iter() { @@ -150,7 +149,6 @@ impl<'a> Widget<'a> for Chart<'a> { (self.y_axis.bounds[1] - self.y_axis.bounds[0]); let dx = (self.x_axis.bounds[1] - x) * (chart_area.width - 1) as f64 / (self.x_axis.bounds[1] - self.x_axis.bounds[0]); - info!("{} {}", dx, dy); buf.update_cell(dx as u16 + margin_x, dy as u16 + margin_y, |c| { c.symbol = symbols::DOT; c.fg = dataset.color;