1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use std::cmp::min;
use std::slice::Iter;

use cairo;

use super::context::CellMetrics;
use crate::ui_model;

pub struct RowView<'a> {
    pub line: &'a ui_model::Line,
    pub cell_metrics: &'a CellMetrics,
    pub line_y: f64,
    pub ctx: &'a cairo::Context,
}

impl<'a> RowView<'a> {
    pub fn new(
        row: usize,
        ctx: &'a cairo::Context,
        cell_metrics: &'a CellMetrics,
        line: &'a ui_model::Line,
    ) -> Self {
        RowView {
            line,
            line_y: row as f64 * cell_metrics.line_height,
            cell_metrics,
            ctx,
        }
    }
}

pub struct ModelClipIterator<'a> {
    model_idx: usize,
    model_iter: Iter<'a, ui_model::Line>,
    cell_metrics: &'a CellMetrics,
    ctx: &'a cairo::Context,
}

pub trait ModelClipIteratorFactory {
    fn get_clip_iterator<'a>(
        &'a self,
        ctx: &'a cairo::Context,
        cell_metrics: &'a CellMetrics,
    ) -> ModelClipIterator;

    fn get_row_view<'a>(
        &'a self,
        ctx: &'a cairo::Context,
        cell_metrics: &'a CellMetrics,
        col: usize,
    ) -> RowView<'a>;
}

impl<'a> Iterator for ModelClipIterator<'a> {
    type Item = RowView<'a>;

    fn next(&mut self) -> Option<RowView<'a>> {
        let next = if let Some(line) = self.model_iter.next() {
            Some(RowView::new(
                self.model_idx,
                self.ctx,
                self.cell_metrics,
                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_row_view<'a>(
        &'a self,
        ctx: &'a cairo::Context,
        cell_metrics: &'a CellMetrics,
        col: usize,
    ) -> RowView<'a> {
        RowView::new(col, ctx, cell_metrics, &self.model()[col])
    }

    fn get_clip_iterator<'a>(
        &'a self,
        ctx: &'a cairo::Context,
        cell_metrics: &'a CellMetrics,
    ) -> ModelClipIterator<'a> {
        let model = self.model();

        let (x1, y1, x2, y2) = ctx.clip_extents();

        // in case ctx.translate is used y1 can be less then 0
        // in this case just use 0 as top value
        let model_clip = ui_model::ModelRect::from_area(cell_metrics, x1, y1.max(0.0), x2, y2);

        let model_clip_top = if model_clip.top == 0 {
            0
        } else {
            // looks like in some cases repaint can come from old model
            min(model.len() - 1, model_clip.top - 1)
        };
        let model_clip_bot = min(model.len() - 1, model_clip.bot + 1);

        debug_assert!(
            model_clip_top <= model_clip_bot,
            "model line index starts at {} but ends at {}. model.len = {}",
            model_clip_top,
            model_clip_bot,
            model.len()
        );

        ModelClipIterator {
            model_idx: model_clip_top,
            model_iter: model[model_clip_top..model_clip_bot + 1].iter(),
            ctx,
            cell_metrics,
        }
    }
}