1use crate::highlight::Highlight;
2use std::rc::Rc;
3
4mod cell;
5mod item;
6mod line;
7mod model_layout;
8mod model_rect;
9
10pub use self::cell::Cell;
11pub use self::item::Item;
12pub use self::line::{Line, StyledLine};
13pub use self::model_layout::ModelLayout;
14pub use self::model_rect::{ModelRect, ModelRectVec};
15
16pub struct UiModel {
17 pub columns: usize,
18 pub rows: usize,
19 cur_row: usize,
20 cur_col: usize,
21 model: Box<[Line]>,
22}
23
24impl UiModel {
25 pub fn new(rows: u64, columns: u64) -> UiModel {
26 let mut model = Vec::with_capacity(rows as usize);
27 for _ in 0..rows as usize {
28 model.push(Line::new(columns as usize));
29 }
30
31 UiModel {
32 columns: columns as usize,
33 rows: rows as usize,
34 cur_row: 0,
35 cur_col: 0,
36 model: model.into_boxed_slice(),
37 }
38 }
39
40 pub fn empty() -> UiModel {
41 UiModel {
42 columns: 0,
43 rows: 0,
44 cur_row: 0,
45 cur_col: 0,
46 model: Box::new([]),
47 }
48 }
49
50 #[inline]
51 pub fn model(&self) -> &[Line] {
52 &self.model
53 }
54
55 #[inline]
56 pub fn model_mut(&mut self) -> &mut [Line] {
57 &mut self.model
58 }
59
60 pub fn cur_point(&self) -> ModelRect {
61 ModelRect::point(self.cur_col, self.cur_row)
62 }
63
64 pub fn set_cursor(&mut self, row: usize, col: usize) -> ModelRectVec {
65 if row >= self.model.len() || col >= self.model[row].line.len() {
68 return ModelRectVec::empty();
69 }
70
71 let mut changed_region = ModelRectVec::new(self.cur_point());
72
73 self.cur_row = row;
74 self.cur_col = col;
75
76 changed_region.join(&self.cur_point());
77
78 changed_region
79 }
80
81 pub fn get_cursor(&self) -> (usize, usize) {
82 (self.cur_row, self.cur_col)
83 }
84
85 pub fn put_one(&mut self, row: usize, col: usize, ch: &str, double_width: bool, hl: Rc<Highlight>) {
86 self.put(row, col, ch, double_width, 1, hl);
87 }
88
89 pub fn put(
90 &mut self,
91 row: usize,
92 col: usize,
93 ch: &str,
94 double_width: bool,
95 repeat: usize,
96 hl: Rc<Highlight>,
97 ) {
98 let line = &mut self.model[row];
99 line.dirty_line = true;
100
101 for offset in 0..repeat {
102 let cell = &mut line[col + offset];
103 cell.ch.clear();
104 cell.ch.push_str(ch);
105 cell.hl = hl.clone();
106 cell.double_width = double_width;
107 cell.dirty = true;
108 }
109 }
110
111 pub fn swap_rows(&mut self, target: &mut UiModel, to_row: usize) {
115 for (row_idx, line) in self.model[0..to_row + 1].iter_mut().enumerate() {
116 let target_row = &mut target.model[row_idx];
117 line.swap_with(target_row, 0, self.columns - 1);
118 }
119 }
120
121 #[inline]
122 fn swap_row(&mut self, target_row: i64, offset: i64, left_col: usize, right_col: usize) {
123 debug_assert_ne!(0, offset);
124
125 let from_row = (target_row + offset) as usize;
126
127 let (left, right) = if offset > 0 {
128 self.model.split_at_mut(from_row)
129 } else {
130 self.model.split_at_mut(target_row as usize)
131 };
132
133 let (source_row, target_row) = if offset > 0 {
134 (&mut right[0], &mut left[target_row as usize])
135 } else {
136 (&mut left[from_row], &mut right[0])
137 };
138
139 source_row.swap_with(target_row, left_col, right_col);
140 }
141
142 pub fn scroll(&mut self, top: i64, bot: i64, left: usize, right: usize, count: i64, default_hl: &Rc<Highlight>) -> ModelRect {
143 if count > 0 {
144 for row in top..(bot - count + 1) {
145 self.swap_row(row, count, left, right);
146 }
147 } else {
148 for row in ((top - count)..(bot + 1)).rev() {
149 self.swap_row(row, count, left, right);
150 }
151 }
152
153 if count > 0 {
154 self.clear_region((bot - count + 1) as usize, bot as usize, left, right, default_hl);
155 } else {
156 self.clear_region(top as usize, (top - count - 1) as usize, left, right, default_hl);
157 }
158
159 ModelRect::new(top as usize, bot as usize, left, right)
160 }
161
162 pub fn clear(&mut self, default_hl: &Rc<Highlight>) {
163 let (rows, columns) = (self.rows, self.columns);
164 self.clear_region(0, rows - 1, 0, columns - 1, default_hl);
165 }
166
167 fn clear_region(&mut self, top: usize, bot: usize, left: usize, right: usize, default_hl: &Rc<Highlight>) {
168 for row in &mut self.model[top..bot + 1] {
169 row.clear(left, right, default_hl);
170 }
171 }
172
173 pub fn clear_glyphs(&mut self) {
174 for row in &mut self.model.iter_mut() {
175 row.clear_glyphs();
176 }
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 #[test]
185 fn test_vec_join_inside() {
186 let mut list = ModelRectVec::new(ModelRect::new(0, 23, 0, 69));
187
188 let inside = ModelRect::new(23, 23, 68, 69);
189
190 list.join(&inside);
191 assert_eq!(1, list.list.len());
192 }
193
194 #[test]
195 fn test_vec_join_top() {
196 let mut list = ModelRectVec::new(ModelRect::point(0, 0));
197
198 let neighbor = ModelRect::point(1, 0);
199
200 list.join(&neighbor);
201 assert_eq!(1, list.list.len());
202 }
203
204 #[test]
205 fn test_model_vec_join_right() {
206 let mut list = ModelRectVec::new(ModelRect::new(23, 23, 69, 69));
207
208 let neighbor = ModelRect::new(23, 23, 69, 70);
209
210 list.join(&neighbor);
211 assert_eq!(1, list.list.len());
212 }
213
214 #[test]
215 fn test_model_vec_join_right2() {
216 let mut list = ModelRectVec::new(ModelRect::new(0, 1, 0, 9));
217
218 let neighbor = ModelRect::new(1, 1, 9, 10);
219
220 list.join(&neighbor);
221 assert_eq!(1, list.list.len());
222 }
223
224 #[test]
225 fn test_model_vec_join() {
226 let mut list = ModelRectVec::new(ModelRect::point(5, 5));
227
228 let neighbor = ModelRect::point(6, 5);
229
230 list.join(&neighbor);
231 assert_eq!(1, list.list.len());
232 }
233
234 #[test]
235 fn test_model_vec_no_join() {
236 let mut list = ModelRectVec::new(ModelRect::point(5, 5));
237
238 let not_neighbor = ModelRect::point(6, 6);
239
240 list.join(¬_neighbor);
241 assert_eq!(2, list.list.len());
242 }
243
244 #[test]
245 fn test_cursor_area() {
246 let mut model = UiModel::new(10, 20);
247
248 model.set_cursor(1, 1);
249
250 let rect = model.set_cursor(5, 5);
251
252 assert_eq!(2, rect.list.len());
253
254 assert_eq!(1, rect.list[0].top);
255 assert_eq!(1, rect.list[0].left);
256 assert_eq!(1, rect.list[0].bot);
257 assert_eq!(1, rect.list[0].right);
258
259 assert_eq!(5, rect.list[1].top);
260 assert_eq!(5, rect.list[1].left);
261 assert_eq!(5, rect.list[1].bot);
262 assert_eq!(5, rect.list[1].right);
263 }
264
265 #[test]
266 fn test_scroll_area() {
267 let mut model = UiModel::new(10, 20);
268
269 let rect = model.scroll(1, 5, 1, 5, 3, &Rc::new(Highlight::new()));
270
271 assert_eq!(1, rect.top);
272 assert_eq!(1, rect.left);
273 assert_eq!(5, rect.bot);
274 assert_eq!(5, rect.right);
275 }
276}