diff --git a/src/nvim.rs b/src/nvim.rs index b15e9de..4c8f457 100644 --- a/src/nvim.rs +++ b/src/nvim.rs @@ -233,3 +233,24 @@ impl RepaintMode { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_mode() { + let mode = RepaintMode::Area(ModelRect::point(1, 1)); + mode.join(&RepaintMode::Nothing); + + match mode { + RepaintMode::Area(ref rect) => { + assert_eq!(1, rect.top); + assert_eq!(1, rect.bot); + assert_eq!(1, rect.left); + assert_eq!(1, rect.right); + } + _ => panic!("mode is worng") + } + } +} diff --git a/src/shell.rs b/src/shell.rs index ef2a700..cb7e827 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -14,7 +14,7 @@ use gtk::DrawingArea; use neovim_lib::{Neovim, NeovimApi, Value, Integer}; use settings; -use ui_model::{UiModel, Cell, Attrs, Color, COLOR_BLACK, COLOR_WHITE, COLOR_RED}; +use ui_model::{UiModel, Cell, Attrs, Color, ModelRect, COLOR_BLACK, COLOR_WHITE, COLOR_RED}; use nvim::{RedrawEvents, GuiApi, RepaintMode}; use input::{convert_key, keyval_to_input_string}; use ui::{UI, Ui, SET}; @@ -276,17 +276,24 @@ fn draw(shell: &Shell, ctx: &cairo::Context) { let line_height = shell.line_height.unwrap(); let char_width = shell.char_width.unwrap(); + let clip = ctx.clip_extents(); + let model_clip = ModelRect::from_area(line_height, char_width, + clip.0, clip.1, clip.2, clip.3); + + let line_x = model_clip.left as f64 * char_width; + let mut line_y: f64 = model_clip.top as f64 * line_height; + let (row, col) = shell.model.get_cursor(); let mut buf = String::with_capacity(4); - let mut line_y: f64 = 0.0; let layout = pc::create_layout(ctx); let mut desc = shell.create_pango_font(); - for (line_idx, line) in shell.model.model().iter().enumerate() { - ctx.move_to(0.0, line_y); + // FIXME: col_idx is wrong + for (line_idx, line) in shell.model.clip_model(&model_clip) { + ctx.move_to(line_x, line_y); // first draw background // here we join same bg color for given line @@ -319,7 +326,7 @@ fn draw(shell: &Shell, ctx: &cairo::Context) { line_height, from_bg.take().unwrap()); - ctx.move_to(0.0, line_y); + ctx.move_to(line_x, line_y); for (col_idx, cell) in line.iter().enumerate() { let double_width = line.get(col_idx + 1).map(|c| c.attrs.double_width).unwrap_or(false); @@ -508,12 +515,13 @@ impl RedrawEvents for Shell { match mode { &RepaintMode::All => self.drawing_area.queue_draw(), &RepaintMode::Area(ref rect) => { - if let Some(line_height) = self.line_height { - if let Some(char_width) = self.char_width { + match (&self.line_height, &self.char_width) { + (&Some(line_height), &Some(char_width)) => { let (x, y, width, height) = - rect.to_area(line_height as i32, char_width as i32); + rect.to_area(line_height, char_width); self.drawing_area.queue_draw_area(x, y, width, height); } + _ => self.drawing_area.queue_draw(), } } &RepaintMode::Nothing => (), diff --git a/src/ui_model.rs b/src/ui_model.rs index 4e29fb2..5b1e330 100644 --- a/src/ui_model.rs +++ b/src/ui_model.rs @@ -1,3 +1,4 @@ +use std::slice::Iter; #[derive(Clone, PartialEq)] pub struct Color(pub f64, pub f64, pub f64); @@ -120,6 +121,10 @@ impl UiModel { &self.model } + pub fn clip_model<'a> (&'a self, clip: &'a ModelRect) -> ClipRowIterator<'a> { + ClipRowIterator::new(self, clip) + } + pub fn cur_point(&self) -> ModelRect { ModelRect::point(self.cur_row, self.cur_col) } @@ -218,10 +223,10 @@ impl UiModel { #[derive(Clone)] pub struct ModelRect { - top: usize, - bot: usize, - left: usize, - right: usize, + pub top: usize, + pub bot: usize, + pub left: usize, + pub right: usize, } impl ModelRect { @@ -267,30 +272,105 @@ impl ModelRect { }; } - pub fn to_area(&self, line_height: i32, char_width: i32) -> (i32, i32, i32, i32) { - (self.left as i32 * char_width, - self.top as i32 * line_height, - (self.right - self.left + 1) as i32 * char_width, - (self.bot - self.top + 1) as i32 * line_height) + pub fn to_area(&self, line_height: f64, char_width: f64) -> (i32, i32, i32, i32) { + (self.left as i32 * char_width as i32, + self.top as i32 * line_height as i32, + (self.right - self.left + 1) as i32 * char_width as i32, + (self.bot - self.top + 1) as i32 * line_height as i32) } - pub fn from_area(line_height: i32, char_width: i32, x1: f64, y1: f64, x2: f64, y2: f64) -> ModelRect { - let left = (x1 / char_width as f64) as usize; - let right = (x2 / char_width as f64) as usize; - let top = (y1 / line_height as f64) as usize; - let bot = (y2 / line_height as f64) as usize; + pub fn from_area(line_height: f64, char_width: f64, x1: f64, y1: f64, x2: f64, y2: f64) -> ModelRect { + let left = (x1 / char_width) as usize; + let right = (x2 / char_width) as usize; + let top = (y1 / line_height) as usize; + let bot = (y2 / line_height) as usize; ModelRect::new(top, bot, left, right) } } +pub struct ClipRowIterator<'a> { + rect: &'a ModelRect, + pos: usize, + iter: Iter<'a, Vec>, +} + +impl <'a> ClipRowIterator<'a> { + pub fn new(model: &'a UiModel, rect: &'a ModelRect) -> ClipRowIterator<'a> { + ClipRowIterator { + rect: rect, + pos: 0, + iter: model.model()[rect.top..rect.bot + 1].iter(), + } + } +} + +impl <'a> Iterator for ClipRowIterator<'a> { + type Item = (usize, ClipColIterator<'a>); + + fn next(&mut self) -> Option<(usize, ClipColIterator<'a>)> { + self.pos += 1; + self.iter.next().map(|line| { + (self.rect.top + self.pos, ClipColIterator::new(line, self.rect)) + }) + } +} + +pub struct ClipColIterator<'a> { + rect: &'a ModelRect, + len: usize, + line: &'a Vec, + pos: usize, + iter: Iter<'a, Cell>, +} + +impl <'a> ClipColIterator<'a> { + pub fn new(model: &'a Vec, rect: &'a ModelRect) -> ClipColIterator<'a> { + ClipColIterator { + len: rect.right - rect.left + 1, + line: model, + rect: rect, + pos: 0, + iter: model[rect.left..rect.right + 1].iter(), + } + } + + pub fn len(&self) -> usize { + self.len + } + + pub fn get(&self, idx: usize) -> Option<&Cell> { + self.line.get(idx) + } +} + +impl <'a>Iterator for ClipColIterator<'a> { + type Item = (usize, &'a Cell); + + fn next(&mut self) -> Option<(usize, &'a Cell)> { + self.pos += 1; + self.iter.next().map(|line| { + (self.rect.left + self.pos, line) + }) + } +} + #[cfg(test)] mod tests { use super::*; + #[test] + fn test_iterator() { + let model = UiModel::new(10, 20); + let rect = ModelRect::new(1, 2, 1, 2); + + assert_eq!(2, model.clip_model(&rect).count()); + assert_eq!(2, model.clip_model(&rect).nth(0).unwrap().count()); + } + #[test] fn test_from_area() { - let rect = ModelRect::from_area(10, 5, 3.0, 3.0, 9.0, 17.0); + let rect = ModelRect::from_area(10.0, 5.0, 3.0, 3.0, 9.0, 17.0); assert_eq!(0, rect.top); assert_eq!(0, rect.left);