diff --git a/src/render/mod.rs b/src/render/mod.rs index 1b1797a..45204c1 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -1,8 +1,10 @@ mod context; mod itemize; +mod model_clip_iterator; pub use self::context::Context; pub use self::context::CellMetrics; +use self::model_clip_iterator::ModelClipIteratorFactory; use mode; use color; @@ -36,10 +38,10 @@ pub fn render( ascent, .. } = font_ctx.cell_metrics(); - let mut line_y = 0.0; let (cursor_row, cursor_col) = ui_model.get_cursor(); - for (row, line) in ui_model.model().iter().enumerate() { + for (row, line) in ui_model.get_clip_iterator(ctx, font_ctx.cell_metrics()) { + let line_y = row as f64 * line_height; let mut line_x = 0.0; for col in 0..line.line.len() { @@ -78,7 +80,7 @@ pub fn render( if cell.attrs.underline || cell.attrs.undercurl { if cell.attrs.undercurl { - //FIXME: don't repaint all lines on changes + //FIXME: sometime we don't repaint all undercurl lines let sp = color_model.actual_cell_sp(cell); ctx.set_source_rgba(sp.0, sp.1, sp.2, 0.7); @@ -115,7 +117,6 @@ pub fn render( line_x += char_width; } - line_y += line_height; } } diff --git a/src/render/model_clip_iterator.rs b/src/render/model_clip_iterator.rs new file mode 100644 index 0000000..997da2c --- /dev/null +++ b/src/render/model_clip_iterator.rs @@ -0,0 +1,64 @@ +use std::slice::Iter; +use cairo; + +use super::context::CellMetrics; +use ui_model; + +pub struct ModelClipIterator<'a> { + model_idx: usize, + model_iter: Iter<'a, ui_model::Line>, +} + +pub trait ModelClipIteratorFactory { + fn get_clip_iterator( + &self, + ctx: &cairo::Context, + cell_metrics: &CellMetrics, + ) -> ModelClipIterator; +} + +impl<'a> Iterator for ModelClipIterator<'a> { + type Item = (usize, &'a ui_model::Line); + + fn next(&mut self) -> Option<(usize, &'a ui_model::Line)> { + let next = if let Some(line) = self.model_iter.next() { + Some((self.model_idx, line)) + } else { + None + }; + self.model_idx += 1; + + next + } +} + +/// Clip implemented as top - 1/bot + 1 +/// this is because in some cases(like 'g' character) drawing character does not fit to calculated bounds +/// and if one line must be repainted - also previous and next line must be repainted to +impl ModelClipIteratorFactory for ui_model::UiModel { + fn get_clip_iterator( + &self, + ctx: &cairo::Context, + cell_metrics: &CellMetrics, + ) -> ModelClipIterator { + let model = self.model(); + + let (x1, y1, x2, y2) = ctx.clip_extents(); + let model_clip = ui_model::ModelRect::from_area(cell_metrics, x1, y1, x2, y2); + let model_clip_top = if model_clip.top <= 0 { + 0 + } else { + model_clip.top - 1 + }; + let model_clip_bot = if model_clip.bot >= model.len() - 1 { + model.len() - 1 + } else { + model_clip.bot + 1 + }; + + ModelClipIterator { + model_idx: model_clip_top, + model_iter: model[model_clip_top..model_clip_bot + 1].iter(), + } + } +} diff --git a/src/ui_model/mod.rs b/src/ui_model/mod.rs index 26e20d2..e5cc799 100644 --- a/src/ui_model/mod.rs +++ b/src/ui_model/mod.rs @@ -4,10 +4,9 @@ mod item; mod model_rect; pub use self::cell::{Cell, Attrs}; -pub use self::line::StyledLine; +pub use self::line::{Line, StyledLine}; pub use self::item::Item; pub use self::model_rect::{ModelRect, ModelRectVec}; -use self::line::Line; pub struct UiModel { @@ -42,10 +41,12 @@ impl UiModel { } } + #[inline] pub fn model(&self) -> &[Line] { &self.model } + #[inline] pub fn model_mut(&mut self) -> &mut [Line] { &mut self.model } diff --git a/src/ui_model/model_rect.rs b/src/ui_model/model_rect.rs index 886d995..9ce5ac5 100644 --- a/src/ui_model/model_rect.rs +++ b/src/ui_model/model_rect.rs @@ -240,13 +240,18 @@ impl ModelRect { } pub fn from_area( - line_height: f64, - char_width: f64, + cell_metrics: &CellMetrics, x1: f64, y1: f64, x2: f64, y2: f64, ) -> ModelRect { + let &CellMetrics { + char_width, + line_height, + .. + } = cell_metrics; + let x2 = if x2 > 0.0 { x2 - 1.0 } else { x2 }; let y2 = if y2 > 0.0 { y2 - 1.0 } else { y2 }; let left = (x1 / char_width) as usize;