nvim_gtk/ui_model/
model_rect.rs

1use super::item::Item;
2use super::UiModel;
3use crate::render::CellMetrics;
4
5#[derive(Clone, Debug)]
6pub struct ModelRectVec {
7    pub list: Vec<ModelRect>,
8}
9
10impl ModelRectVec {
11    pub fn empty() -> ModelRectVec {
12        ModelRectVec { list: vec![] }
13    }
14
15    pub fn new(first: ModelRect) -> ModelRectVec {
16        ModelRectVec { list: vec![first] }
17    }
18
19    fn find_neighbor(&self, neighbor: &ModelRect) -> Option<usize> {
20        for (i, rect) in self.list.iter().enumerate() {
21            if (neighbor.top > 0 && rect.top == neighbor.top - 1 || rect.bot == neighbor.bot + 1)
22                && neighbor.in_horizontal(rect)
23            {
24                return Some(i);
25            } else if (neighbor.left > 0 && rect.left == neighbor.left - 1
26                || rect.right == neighbor.right + 1)
27                && neighbor.in_vertical(rect)
28            {
29                return Some(i);
30            } else if rect.in_horizontal(neighbor) && rect.in_vertical(neighbor) {
31                return Some(i);
32            } else if rect.contains(neighbor) {
33                return Some(i);
34            }
35        }
36
37        None
38    }
39
40    pub fn join(&mut self, other: &ModelRect) {
41        match self.find_neighbor(other) {
42            Some(i) => self.list[i].join(other),
43            None => self.list.push(other.clone()),
44        }
45    }
46}
47
48#[derive(Clone, PartialEq, Debug)]
49pub struct ModelRect {
50    pub top: usize,
51    pub bot: usize,
52    pub left: usize,
53    pub right: usize,
54}
55
56impl ModelRect {
57    pub fn new(top: usize, bot: usize, left: usize, right: usize) -> ModelRect {
58        debug_assert!(top <= bot, "{} <= {}", top, bot);
59        debug_assert!(left <= right, "{} <= {}", left, right);
60
61        ModelRect {
62            top,
63            bot,
64            left,
65            right,
66        }
67    }
68
69    pub fn point(x: usize, y: usize) -> ModelRect {
70        ModelRect {
71            top: y,
72            bot: y,
73            left: x,
74            right: x,
75        }
76    }
77
78    #[inline]
79    fn in_horizontal(&self, other: &ModelRect) -> bool {
80        other.left >= self.left && other.left <= self.right
81            || other.right >= self.left && other.right >= self.right
82    }
83
84    #[inline]
85    fn in_vertical(&self, other: &ModelRect) -> bool {
86        other.top >= self.top && other.top <= self.bot
87            || other.bot >= self.top && other.bot <= self.bot
88    }
89
90    fn contains(&self, other: &ModelRect) -> bool {
91        self.top <= other.top
92            && self.bot >= other.bot
93            && self.left <= other.left
94            && self.right >= other.right
95    }
96
97    /// Extend rect to left and right to make changed Item rerendered
98    pub fn extend_by_items(&mut self, model: Option<&UiModel>) {
99        if model.is_none() {
100            return;
101        }
102        let model = model.unwrap();
103
104        let mut left = self.left;
105        let mut right = self.right;
106
107        for i in self.top..self.bot + 1 {
108            let line = &model.model[i];
109            let item_idx = line.cell_to_item(self.left);
110            if item_idx >= 0 {
111                let item_idx = item_idx as usize;
112                if item_idx < left {
113                    left = item_idx;
114                }
115            }
116
117            let len_since_right = line.item_len_from_idx(self.right) - 1;
118            if right < self.right + len_since_right {
119                right = self.right + len_since_right;
120            }
121
122            // extend also double_width chars
123            let cell = &line.line[self.left];
124            if self.left > 0 && cell.double_width {
125                let dw_char_idx = self.left - 1;
126                if dw_char_idx < left {
127                    left = dw_char_idx;
128                }
129            }
130
131            let dw_char_idx = self.right + 1;
132            if let Some(cell) = line.line.get(dw_char_idx) {
133                if cell.double_width {
134                    if right < dw_char_idx {
135                        right = dw_char_idx;
136                    }
137                }
138            }
139        }
140
141        self.left = left;
142        self.right = right;
143    }
144
145    pub fn to_area_extend_ink(
146        &self,
147        model: Option<&UiModel>,
148        cell_metrics: &CellMetrics,
149    ) -> (i32, i32, i32, i32) {
150        let (x, x2) = self.extend_left_right_area(model, cell_metrics);
151        let (y, y2) = self.extend_top_bottom_area(model, cell_metrics);
152
153        (x, y, x2 - x, y2 - y)
154    }
155
156    fn extend_left_right_area(&self, model: Option<&UiModel>, cell_metrics: &CellMetrics) -> (i32, i32) {
157        // when convert to i32 area must be bigger then original f64 version
158        let x = (self.left as f64 * cell_metrics.char_width).floor() as i32;
159        let x2 = ((self.right + 1) as f64 * cell_metrics.char_width).ceil() as i32;
160
161        if model.is_none() {
162            return (x, x2);
163        }
164        let model = model.unwrap();
165
166        let mut min_x_offset = 0.0;
167        let mut max_x_offset = 0.0;
168
169        for row in self.top..self.bot + 1 {
170            // left
171            let line = &model.model[row];
172            if let Some(&Item {
173                ink_overflow: Some(ref overflow),
174                ..
175            }) = line.item_line[self.left].as_ref()
176            {
177                if min_x_offset < overflow.left {
178                    min_x_offset = overflow.left;
179                }
180            }
181
182            // right
183            let line = &model.model[row];
184            // check if this item ends here
185            if self.right < model.columns - 1
186                && line.cell_to_item(self.right) != line.cell_to_item(self.right + 1)
187            {
188                if let Some(&Item {
189                    ink_overflow: Some(ref overflow),
190                    ..
191                }) = line.get_item(self.left)
192                {
193                    if max_x_offset < overflow.right {
194                        max_x_offset = overflow.right;
195                    }
196                }
197            }
198        }
199
200        (
201            x - min_x_offset.ceil() as i32,
202            x2 + max_x_offset.ceil() as i32,
203        )
204    }
205
206    fn extend_top_bottom_area(&self, model: Option<&UiModel>, cell_metrics: &CellMetrics) -> (i32, i32) {
207        let y = self.top as i32 * cell_metrics.line_height as i32;
208        let y2 = (self.bot + 1) as i32 * cell_metrics.line_height as i32;
209
210        if model.is_none() {
211            return (y, y2);
212        }
213        let model = model.unwrap();
214
215        let mut min_y_offset = 0.0;
216        let mut max_y_offset = 0.0;
217
218        for col in self.left..self.right + 1 {
219            // top
220            let line = &model.model[self.top];
221            if let Some(&Item {
222                ink_overflow: Some(ref overflow),
223                ..
224            }) = line.get_item(col)
225            {
226                if min_y_offset < overflow.top {
227                    min_y_offset = overflow.top;
228                }
229            }
230
231            // bottom
232            let line = &model.model[self.bot];
233            if let Some(&Item {
234                ink_overflow: Some(ref overflow),
235                ..
236            }) = line.get_item(col)
237            {
238                if max_y_offset < overflow.top {
239                    max_y_offset = overflow.top;
240                }
241            }
242        }
243
244        (
245            y - min_y_offset.ceil() as i32,
246            y2 + max_y_offset.ceil() as i32,
247        )
248    }
249
250    pub fn join(&mut self, rect: &ModelRect) {
251        self.top = if self.top < rect.top {
252            self.top
253        } else {
254            rect.top
255        };
256        self.left = if self.left < rect.left {
257            self.left
258        } else {
259            rect.left
260        };
261
262        self.bot = if self.bot > rect.bot {
263            self.bot
264        } else {
265            rect.bot
266        };
267        self.right = if self.right > rect.right {
268            self.right
269        } else {
270            rect.right
271        };
272
273        debug_assert!(self.top <= self.bot);
274        debug_assert!(self.left <= self.right);
275    }
276
277    pub fn to_area(&self, cell_metrics: &CellMetrics) -> (i32, i32, i32, i32) {
278        let &CellMetrics {
279            char_width,
280            line_height,
281            ..
282        } = cell_metrics;
283
284        // when convert to i32 area must be bigger then original f64 version
285        (
286            (self.left as f64 * char_width).floor() as i32,
287            (self.top as f64 * line_height).floor() as i32,
288            ((self.right - self.left + 1) as f64 * char_width).ceil() as i32,
289            ((self.bot - self.top + 1) as f64 * line_height).ceil() as i32,
290        )
291    }
292
293    pub fn from_area(cell_metrics: &CellMetrics, x1: f64, y1: f64, x2: f64, y2: f64) -> ModelRect {
294        let &CellMetrics {
295            char_width,
296            line_height,
297            ..
298        } = cell_metrics;
299
300        let x2 = if x2 > 0.0 { x2 - 1.0 } else { x2 };
301        let y2 = if y2 > 0.0 { y2 - 1.0 } else { y2 };
302        let left = (x1 / char_width) as usize;
303        let right = (x2 / char_width) as usize;
304        let top = (y1 / line_height) as usize;
305        let bot = (y2 / line_height) as usize;
306
307        ModelRect::new(top, bot, left, right)
308    }
309}
310
311impl AsRef<ModelRect> for ModelRect {
312    fn as_ref(&self) -> &ModelRect {
313        self
314    }
315}
316
317#[cfg(test)]
318mod tests {
319    use super::*;
320
321    #[test]
322    fn test_repaint_rect() {
323        let rect = ModelRect::point(1, 1);
324        let (x, y, width, height) = rect.to_area(&CellMetrics::new_hw(10.0, 5.0));
325
326        assert_eq!(5, x);
327        assert_eq!(10, y);
328        assert_eq!(5, width);
329        assert_eq!(10, height);
330    }
331
332    #[test]
333    fn test_from_area() {
334        let rect = ModelRect::from_area(&CellMetrics::new_hw(10.0, 5.0), 3.0, 3.0, 9.0, 17.0);
335
336        assert_eq!(0, rect.top);
337        assert_eq!(0, rect.left);
338        assert_eq!(1, rect.bot);
339        assert_eq!(1, rect.right);
340
341        let rect = ModelRect::from_area(&CellMetrics::new_hw(10.0, 5.0), 0.0, 0.0, 10.0, 20.0);
342
343        assert_eq!(0, rect.top);
344        assert_eq!(0, rect.left);
345        assert_eq!(1, rect.bot);
346        assert_eq!(1, rect.right);
347
348        let rect = ModelRect::from_area(&CellMetrics::new_hw(10.0, 5.0), 0.0, 0.0, 11.0, 21.0);
349
350        assert_eq!(0, rect.top);
351        assert_eq!(0, rect.left);
352        assert_eq!(2, rect.bot);
353        assert_eq!(2, rect.right);
354    }
355
356}