1use std::cmp::max;
2use std::rc::Rc;
3
4use unicode_width::UnicodeWidthStr;
5
6use crate::highlight::Highlight;
7use crate::ui_model::UiModel;
8
9pub struct ModelLayout {
10 pub model: UiModel,
11 rows_filled: usize,
12 cols_filled: usize,
13 lines: Vec<Vec<(Rc<Highlight>, Vec<String>)>>,
14}
15
16impl ModelLayout {
17 const ROWS_STEP: usize = 10;
18
19 pub fn new(columns: u64) -> Self {
20 ModelLayout {
21 model: UiModel::new(ModelLayout::ROWS_STEP as u64, columns),
22 rows_filled: 0,
23 cols_filled: 0,
24 lines: Vec::new(),
25 }
26 }
27
28 pub fn layout_append(&mut self, mut lines: Vec<Vec<(Rc<Highlight>, Vec<String>)>>) {
29 let rows_filled = self.rows_filled;
30 let take_from = self.lines.len();
31
32 self.lines.append(&mut lines);
33
34 self.layout_replace(rows_filled, take_from);
35 }
36
37 pub fn layout(&mut self, lines: Vec<Vec<(Rc<Highlight>, Vec<String>)>>) {
38 self.lines = lines;
39 self.layout_replace(0, 0);
40 }
41
42 pub fn set_cursor(&mut self, col: usize) {
43 let row = if self.rows_filled > 0 {
44 self.rows_filled - 1
45 } else {
46 0
47 };
48
49 self.model.set_cursor(row, col);
50 }
51
52 pub fn size(&self) -> (usize, usize) {
53 (
54 max(self.cols_filled, self.model.get_cursor().1 + 1),
55 self.rows_filled,
56 )
57 }
58
59 fn check_model_size(&mut self, rows: usize) {
60 if rows > self.model.rows {
61 let model_cols = self.model.columns;
62 let model_rows = ((rows / (ModelLayout::ROWS_STEP + 1)) + 1) * ModelLayout::ROWS_STEP;
63 let (cur_row, cur_col) = self.model.get_cursor();
64
65 let mut model = UiModel::new(model_rows as u64, model_cols as u64);
66 self.model.swap_rows(&mut model, self.rows_filled - 1);
67 model.set_cursor(cur_row, cur_col);
68 self.model = model;
69 }
70 }
71
72 pub fn insert_char(&mut self, ch: String, shift: bool, hl: Rc<Highlight>) {
73 if ch.is_empty() {
74 return;
75 }
76
77 let (row, col) = self.model.get_cursor();
78
79 if shift {
80 self.insert_into_lines(ch);
81 self.layout_replace(0, 0);
82 } else {
83 self.model.put_one(row, col, &ch, false, hl);
84 }
85 }
86
87 fn insert_into_lines(&mut self, ch: String) {
88 let line = &mut self.lines[0];
89
90 let cur_col = self.model.cur_col;
91
92 let mut col_idx = 0;
93 for &mut (_, ref mut chars) in line {
94 if cur_col < col_idx + chars.len() {
95 let col_sub_idx = cur_col - col_idx;
96 chars.insert(col_sub_idx, ch);
97 break;
98 } else {
99 col_idx += chars.len();
100 }
101 }
102 }
103
104 fn layout_replace(&mut self, row_offset: usize, take_from: usize) {
108 let rows = ModelLayout::count_lines(&self.lines[take_from..], self.model.columns);
109
110 self.check_model_size(rows + row_offset);
111 self.rows_filled = rows + row_offset;
112
113 let lines = &self.lines[take_from..];
114
115 let mut max_col_idx = 0;
116 let mut col_idx = 0;
117 let mut row_idx = row_offset;
118 for content in lines {
119 for &(ref hl, ref ch_list) in content {
120 for ch in ch_list {
121 let ch_width = max(1, ch.width());
122
123 if col_idx + ch_width > self.model.columns {
124 col_idx = 0;
125 row_idx += 1;
126 }
127
128 self.model.put_one(row_idx, col_idx, ch, false, hl.clone());
129 if ch_width > 1 {
130 self.model.put_one(row_idx, col_idx, "", true, hl.clone());
131 }
132
133 if max_col_idx < col_idx {
134 max_col_idx = col_idx + ch_width - 1;
135 }
136
137 col_idx += ch_width;
138 }
139
140 if col_idx < self.model.columns {
141 self.model.model[row_idx].clear(
142 col_idx,
143 self.model.columns - 1,
144 &Rc::new(Highlight::new()),
145 );
146 }
147 }
148 col_idx = 0;
149 row_idx += 1;
150 }
151
152 if self.rows_filled == 1 {
153 self.cols_filled = max_col_idx + 1;
154 } else {
155 self.cols_filled = max(self.cols_filled, max_col_idx + 1);
156 }
157 }
158
159 fn count_lines(lines: &[Vec<(Rc<Highlight>, Vec<String>)>], max_columns: usize) -> usize {
160 let mut row_count = 0;
161
162 for line in lines {
163 let len: usize = line.iter().map(|c| c.1.len()).sum();
164 row_count += len / (max_columns + 1) + 1;
165 }
166
167 row_count
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
176 fn test_count_lines() {
177 let lines = vec![vec![(Rc::new(Highlight::new()), vec!["a".to_owned(); 5])]];
178
179 let rows = ModelLayout::count_lines(&lines, 4);
180 assert_eq!(2, rows);
181 }
182
183 #[test]
184 fn test_resize() {
185 let lines = vec![
186 vec![(Rc::new(Highlight::new()), vec!["a".to_owned(); 5])];
187 ModelLayout::ROWS_STEP
188 ];
189 let mut model = ModelLayout::new(5);
190
191 model.layout(lines.clone());
192 let (cols, rows) = model.size();
193 assert_eq!(5, cols);
194 assert_eq!(ModelLayout::ROWS_STEP, rows);
195
196 model.layout_append(lines);
197 let (cols, rows) = model.size();
198 assert_eq!(5, cols);
199 assert_eq!(ModelLayout::ROWS_STEP * 2, rows);
200 assert_eq!(ModelLayout::ROWS_STEP * 2, model.model.rows);
201 }
202
203 #[test]
204 fn test_cols_filled() {
205 let lines = vec![vec![(Rc::new(Highlight::new()), vec!["a".to_owned(); 3])]; 1];
206 let mut model = ModelLayout::new(5);
207
208 model.layout(lines);
209 model.set_cursor(3);
212 let (cols, _) = model.size();
213 assert_eq!(4, cols); let lines = vec![vec![(Rc::new(Highlight::new()), vec!["a".to_owned(); 2])]; 1];
216
217 model.layout_append(lines);
218 model.set_cursor(2);
219 let (cols, _) = model.size();
220 assert_eq!(3, cols);
221 }
222
223 #[test]
224 fn test_insert_shift() {
225 let lines = vec![vec![(Rc::new(Highlight::new()), vec!["a".to_owned(); 3])]; 1];
226 let mut model = ModelLayout::new(5);
227 model.layout(lines);
228 model.set_cursor(1);
229
230 model.insert_char("b".to_owned(), true, Rc::new(Highlight::new()));
231
232 let (cols, _) = model.size();
233 assert_eq!(4, cols);
234 assert_eq!("b", model.model.model()[0].line[1].ch);
235 }
236
237 #[test]
238 fn test_insert_no_shift() {
239 let lines = vec![vec![(Rc::new(Highlight::new()), vec!["a".to_owned(); 3])]; 1];
240 let mut model = ModelLayout::new(5);
241 model.layout(lines);
242 model.set_cursor(1);
243
244 model.insert_char("b".to_owned(), false, Rc::new(Highlight::new()));
245
246 let (cols, _) = model.size();
247 assert_eq!(3, cols);
248 assert_eq!("b", model.model.model()[0].line[1].ch);
249 }
250
251 #[test]
252 fn test_double_width() {
253 let lines = vec![vec![(Rc::new(Highlight::new()), vec!["あ".to_owned(); 3])]; 1];
254 let mut model = ModelLayout::new(7);
255 model.layout(lines);
256 model.set_cursor(1);
257
258 let (cols, rows) = model.size();
259 assert_eq!(1, rows);
260 assert_eq!(6, cols);
261 }
262}