pub mod line;
use std::cmp::{max};
use std::mem;
use std::str;
use termbox_simple::Termbox;
use config::Colors;
pub use self::line::Line;
pub use self::line::SegStyle;
pub struct MsgArea {
lines: Vec<Line>,
width: i32,
height: i32,
scroll: i32,
line_buf: Line,
lines_height: Option<i32>
}
impl MsgArea {
pub fn new(width: i32, height: i32) -> MsgArea {
MsgArea {
lines: Vec::new(),
width: width,
height: height,
scroll: 0,
line_buf: Line::new(),
lines_height: Some(0),
}
}
pub fn resize(&mut self, width: i32, height: i32) {
self.width = width;
self.height = height;
self.lines_height = None;
}
pub fn draw(&self, tb: &mut Termbox, colors: &Colors, pos_x: i32, pos_y: i32) {
let mut row = pos_y + self.height - 1;
let mut skip = self.scroll;
let mut line_idx = (self.lines.len() as i32) - 1;
while line_idx >= 0 && row >= pos_y {
let line = unsafe { self.lines.get_unchecked(line_idx as usize) };
let line_height = line.rendered_height(self.width);
debug_assert!(line_height > 0);
if skip >= line_height {
line_idx -= 1;
skip -= line_height;
continue;
}
let height = line_height - skip;
let line_row = row - height + 1;
let render_from = max(0, pos_y - line_row);
line.draw(tb, colors, pos_x, line_row, render_from, height, self.width);
row = line_row - 1;
line_idx -= 1;
skip = 0;
if line_row < pos_y {
break;
}
}
}
}
impl MsgArea {
fn lines_height(&mut self) -> i32 {
match self.lines_height {
Some(height) =>
height,
None => {
let mut total_height = 0;
for line in &self.lines {
total_height += line.rendered_height(self.width);
}
self.lines_height = Some(total_height);
total_height
}
}
}
pub fn scroll_up(&mut self) {
if self.scroll < max(0, self.lines_height() - self.height) {
self.scroll += 1;
}
}
pub fn scroll_down(&mut self) {
if self.scroll > 0 {
self.scroll -= 1;
}
}
pub fn scroll_top(&mut self) {
self.scroll = max(0, self.lines_height() - self.height);
}
pub fn scroll_bottom(&mut self) {
self.scroll = 0;
}
pub fn page_up(&mut self) {
for _ in 0 .. 10 {
self.scroll_up();
}
}
pub fn page_down(&mut self) {
self.scroll = max(0, self.scroll - 10);
}
}
impl MsgArea {
pub fn set_style(&mut self, style: SegStyle) {
self.line_buf.set_style(style);
}
pub fn add_text(&mut self, str : &str) {
self.line_buf.add_text(str);
}
pub fn add_char(&mut self, char : char) {
self.line_buf.add_char(char);
}
pub fn flush_line(&mut self) -> usize {
let line_height = self.line_buf.rendered_height(self.width);
self.lines.push(mem::replace(&mut self.line_buf, Line::new()));
if self.scroll != 0 {
self.scroll += line_height;
}
if let Some(ref mut total_height) = self.lines_height {
*total_height = *total_height + line_height;
}
self.lines.len() - 1
}
#[inline]
pub fn modify_line<F>(&mut self, idx : usize, f : F) where F : Fn(&mut Line) {
f(&mut self.lines[idx]);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn newline_scrolling() {
let mut msg_area = MsgArea::new(100, 1);
assert_eq!(msg_area.scroll, 0);
msg_area.add_text("line1");
msg_area.flush_line();
assert_eq!(msg_area.scroll, 0);
msg_area.add_text("line2");
msg_area.flush_line();
assert_eq!(msg_area.scroll, 0);
msg_area.scroll_up();
assert_eq!(msg_area.scroll, 1);
msg_area.add_text("line3");
msg_area.flush_line();
assert_eq!(msg_area.scroll, 2);
}
}