@akiojin/gwt 9.7.0 → 9.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,742 @@
1
+ use crate::term::BufWrite as _;
2
+
3
+ #[derive(Clone, Debug)]
4
+ pub struct Grid {
5
+ size: Size,
6
+ pos: Pos,
7
+ saved_pos: Pos,
8
+ rows: Vec<crate::row::Row>,
9
+ scroll_top: u16,
10
+ scroll_bottom: u16,
11
+ origin_mode: bool,
12
+ saved_origin_mode: bool,
13
+ scrollback: std::collections::VecDeque<crate::row::Row>,
14
+ scrollback_len: usize,
15
+ scrollback_offset: usize,
16
+ }
17
+
18
+ impl Grid {
19
+ pub fn new(size: Size, scrollback_len: usize) -> Self {
20
+ Self {
21
+ size,
22
+ pos: Pos::default(),
23
+ saved_pos: Pos::default(),
24
+ rows: vec![],
25
+ scroll_top: 0,
26
+ scroll_bottom: size.rows - 1,
27
+ origin_mode: false,
28
+ saved_origin_mode: false,
29
+ scrollback: std::collections::VecDeque::new(),
30
+ scrollback_len,
31
+ scrollback_offset: 0,
32
+ }
33
+ }
34
+
35
+ pub fn allocate_rows(&mut self) {
36
+ if self.rows.is_empty() {
37
+ self.rows.extend(
38
+ std::iter::repeat_with(|| {
39
+ crate::row::Row::new(self.size.cols)
40
+ })
41
+ .take(usize::from(self.size.rows)),
42
+ );
43
+ }
44
+ }
45
+
46
+ fn new_row(&self) -> crate::row::Row {
47
+ crate::row::Row::new(self.size.cols)
48
+ }
49
+
50
+ pub fn clear(&mut self) {
51
+ self.pos = Pos::default();
52
+ self.saved_pos = Pos::default();
53
+ for row in self.drawing_rows_mut() {
54
+ row.clear(crate::attrs::Attrs::default());
55
+ }
56
+ self.scroll_top = 0;
57
+ self.scroll_bottom = self.size.rows - 1;
58
+ self.origin_mode = false;
59
+ self.saved_origin_mode = false;
60
+ }
61
+
62
+ pub fn size(&self) -> Size {
63
+ self.size
64
+ }
65
+
66
+ pub fn set_size(&mut self, size: Size) {
67
+ if size.cols != self.size.cols {
68
+ for row in &mut self.rows {
69
+ row.wrap(false);
70
+ }
71
+ }
72
+
73
+ if self.scroll_bottom == self.size.rows - 1 {
74
+ self.scroll_bottom = size.rows - 1;
75
+ }
76
+
77
+ self.size = size;
78
+ for row in &mut self.rows {
79
+ row.resize(size.cols, crate::Cell::new());
80
+ }
81
+ self.rows.resize(usize::from(size.rows), self.new_row());
82
+
83
+ if self.scroll_bottom >= size.rows {
84
+ self.scroll_bottom = size.rows - 1;
85
+ }
86
+ if self.scroll_bottom < self.scroll_top {
87
+ self.scroll_top = 0;
88
+ }
89
+
90
+ self.row_clamp_top(false);
91
+ self.row_clamp_bottom(false);
92
+ self.col_clamp();
93
+
94
+ if self.saved_pos.row > self.size.rows - 1 {
95
+ self.saved_pos.row = self.size.rows - 1;
96
+ }
97
+ if self.saved_pos.col > self.size.cols - 1 {
98
+ self.saved_pos.col = self.size.cols - 1;
99
+ }
100
+ }
101
+
102
+ pub fn pos(&self) -> Pos {
103
+ self.pos
104
+ }
105
+
106
+ pub fn set_pos(&mut self, mut pos: Pos) {
107
+ if self.origin_mode {
108
+ pos.row = pos.row.saturating_add(self.scroll_top);
109
+ }
110
+ self.pos = pos;
111
+ self.row_clamp_top(self.origin_mode);
112
+ self.row_clamp_bottom(self.origin_mode);
113
+ self.col_clamp();
114
+ }
115
+
116
+ pub fn save_cursor(&mut self) {
117
+ self.saved_pos = self.pos;
118
+ self.saved_origin_mode = self.origin_mode;
119
+ }
120
+
121
+ pub fn restore_cursor(&mut self) {
122
+ self.pos = self.saved_pos;
123
+ self.origin_mode = self.saved_origin_mode;
124
+ }
125
+
126
+ pub fn visible_rows(&self) -> impl Iterator<Item = &crate::row::Row> {
127
+ let scrollback_len = self.scrollback.len();
128
+ let rows_len = self.rows.len();
129
+ self.scrollback
130
+ .iter()
131
+ .skip(scrollback_len - self.scrollback_offset)
132
+ // when scrollback_offset > rows_len (e.g. rows = 3,
133
+ // scrollback_len = 10, offset = 9) the skip(10 - 9)
134
+ // will take 9 rows instead of 3. we need to set
135
+ // the upper bound to rows_len (e.g. 3)
136
+ .take(rows_len)
137
+ // same for rows_len - scrollback_offset (e.g. 3 - 9).
138
+ // it'll panic with overflow. we have to saturate the subtraction.
139
+ .chain(
140
+ self.rows
141
+ .iter()
142
+ .take(rows_len.saturating_sub(self.scrollback_offset)),
143
+ )
144
+ }
145
+
146
+ pub fn drawing_rows(&self) -> impl Iterator<Item = &crate::row::Row> {
147
+ self.rows.iter()
148
+ }
149
+
150
+ pub fn drawing_rows_mut(
151
+ &mut self,
152
+ ) -> impl Iterator<Item = &mut crate::row::Row> {
153
+ self.rows.iter_mut()
154
+ }
155
+
156
+ pub fn visible_row(&self, row: u16) -> Option<&crate::row::Row> {
157
+ self.visible_rows().nth(usize::from(row))
158
+ }
159
+
160
+ pub fn drawing_row(&self, row: u16) -> Option<&crate::row::Row> {
161
+ self.drawing_rows().nth(usize::from(row))
162
+ }
163
+
164
+ pub fn drawing_row_mut(
165
+ &mut self,
166
+ row: u16,
167
+ ) -> Option<&mut crate::row::Row> {
168
+ self.drawing_rows_mut().nth(usize::from(row))
169
+ }
170
+
171
+ pub fn current_row_mut(&mut self) -> &mut crate::row::Row {
172
+ self.drawing_row_mut(self.pos.row)
173
+ // we assume self.pos.row is always valid
174
+ .unwrap()
175
+ }
176
+
177
+ pub fn visible_cell(&self, pos: Pos) -> Option<&crate::Cell> {
178
+ self.visible_row(pos.row).and_then(|r| r.get(pos.col))
179
+ }
180
+
181
+ pub fn drawing_cell(&self, pos: Pos) -> Option<&crate::Cell> {
182
+ self.drawing_row(pos.row).and_then(|r| r.get(pos.col))
183
+ }
184
+
185
+ pub fn drawing_cell_mut(&mut self, pos: Pos) -> Option<&mut crate::Cell> {
186
+ self.drawing_row_mut(pos.row)
187
+ .and_then(|r| r.get_mut(pos.col))
188
+ }
189
+
190
+ pub fn scrollback_len(&self) -> usize {
191
+ self.scrollback_len
192
+ }
193
+
194
+ pub fn scrollback(&self) -> usize {
195
+ self.scrollback_offset
196
+ }
197
+
198
+ pub fn set_scrollback(&mut self, rows: usize) {
199
+ self.scrollback_offset = rows.min(self.scrollback.len());
200
+ }
201
+
202
+ pub fn write_contents(&self, contents: &mut String) {
203
+ let mut wrapping = false;
204
+ for row in self.visible_rows() {
205
+ row.write_contents(contents, 0, self.size.cols, wrapping);
206
+ if !row.wrapped() {
207
+ contents.push('\n');
208
+ }
209
+ wrapping = row.wrapped();
210
+ }
211
+
212
+ while contents.ends_with('\n') {
213
+ contents.truncate(contents.len() - 1);
214
+ }
215
+ }
216
+
217
+ pub fn write_contents_formatted(
218
+ &self,
219
+ contents: &mut Vec<u8>,
220
+ ) -> crate::attrs::Attrs {
221
+ crate::term::ClearAttrs.write_buf(contents);
222
+ crate::term::ClearScreen.write_buf(contents);
223
+
224
+ let mut prev_attrs = crate::attrs::Attrs::default();
225
+ let mut prev_pos = Pos::default();
226
+ let mut wrapping = false;
227
+ for (i, row) in self.visible_rows().enumerate() {
228
+ // we limit the number of cols to a u16 (see Size), so
229
+ // visible_rows() can never return more rows than will fit
230
+ let i = i.try_into().unwrap();
231
+ let (new_pos, new_attrs) = row.write_contents_formatted(
232
+ contents,
233
+ 0,
234
+ self.size.cols,
235
+ i,
236
+ wrapping,
237
+ Some(prev_pos),
238
+ Some(prev_attrs),
239
+ );
240
+ prev_pos = new_pos;
241
+ prev_attrs = new_attrs;
242
+ wrapping = row.wrapped();
243
+ }
244
+
245
+ self.write_cursor_position_formatted(
246
+ contents,
247
+ Some(prev_pos),
248
+ Some(prev_attrs),
249
+ );
250
+
251
+ prev_attrs
252
+ }
253
+
254
+ pub fn write_contents_diff(
255
+ &self,
256
+ contents: &mut Vec<u8>,
257
+ prev: &Self,
258
+ mut prev_attrs: crate::attrs::Attrs,
259
+ ) -> crate::attrs::Attrs {
260
+ let mut prev_pos = prev.pos;
261
+ let mut wrapping = false;
262
+ let mut prev_wrapping = false;
263
+ for (i, (row, prev_row)) in
264
+ self.visible_rows().zip(prev.visible_rows()).enumerate()
265
+ {
266
+ // we limit the number of cols to a u16 (see Size), so
267
+ // visible_rows() can never return more rows than will fit
268
+ let i = i.try_into().unwrap();
269
+ let (new_pos, new_attrs) = row.write_contents_diff(
270
+ contents,
271
+ prev_row,
272
+ 0,
273
+ self.size.cols,
274
+ i,
275
+ wrapping,
276
+ prev_wrapping,
277
+ prev_pos,
278
+ prev_attrs,
279
+ );
280
+ prev_pos = new_pos;
281
+ prev_attrs = new_attrs;
282
+ wrapping = row.wrapped();
283
+ prev_wrapping = prev_row.wrapped();
284
+ }
285
+
286
+ self.write_cursor_position_formatted(
287
+ contents,
288
+ Some(prev_pos),
289
+ Some(prev_attrs),
290
+ );
291
+
292
+ prev_attrs
293
+ }
294
+
295
+ pub fn write_cursor_position_formatted(
296
+ &self,
297
+ contents: &mut Vec<u8>,
298
+ prev_pos: Option<Pos>,
299
+ prev_attrs: Option<crate::attrs::Attrs>,
300
+ ) {
301
+ let prev_attrs = prev_attrs.unwrap_or_default();
302
+ // writing a character to the last column of a row doesn't wrap the
303
+ // cursor immediately - it waits until the next character is actually
304
+ // drawn. it is only possible for the cursor to have this kind of
305
+ // position after drawing a character though, so if we end in this
306
+ // position, we need to redraw the character at the end of the row.
307
+ if prev_pos != Some(self.pos) && self.pos.col >= self.size.cols {
308
+ let mut pos = Pos {
309
+ row: self.pos.row,
310
+ col: self.size.cols - 1,
311
+ };
312
+ if self
313
+ .drawing_cell(pos)
314
+ // we assume self.pos.row is always valid, and self.size.cols
315
+ // - 1 is always a valid column
316
+ .unwrap()
317
+ .is_wide_continuation()
318
+ {
319
+ pos.col = self.size.cols - 2;
320
+ }
321
+ let cell =
322
+ // we assume self.pos.row is always valid, and self.size.cols
323
+ // - 2 must be a valid column because self.size.cols - 1 is
324
+ // always valid and we just checked that the cell at
325
+ // self.size.cols - 1 is a wide continuation character, which
326
+ // means that the first half of the wide character must be
327
+ // before it
328
+ self.drawing_cell(pos).unwrap();
329
+ if cell.has_contents() {
330
+ if let Some(prev_pos) = prev_pos {
331
+ crate::term::MoveFromTo::new(prev_pos, pos)
332
+ .write_buf(contents);
333
+ } else {
334
+ crate::term::MoveTo::new(pos).write_buf(contents);
335
+ }
336
+ cell.attrs().write_escape_code_diff(contents, &prev_attrs);
337
+ contents.extend(cell.contents().as_bytes());
338
+ prev_attrs.write_escape_code_diff(contents, cell.attrs());
339
+ } else {
340
+ // if the cell doesn't have contents, we can't have gotten
341
+ // here by drawing a character in the last column. this means
342
+ // that as far as i'm aware, we have to have reached here from
343
+ // a newline when we were already after the end of an earlier
344
+ // row. in the case where we are already after the end of an
345
+ // earlier row, we can just write a few newlines, otherwise we
346
+ // also need to do the same as above to get ourselves to after
347
+ // the end of a row.
348
+ let mut found = false;
349
+ for i in (0..self.pos.row).rev() {
350
+ pos.row = i;
351
+ pos.col = self.size.cols - 1;
352
+ if self
353
+ .drawing_cell(pos)
354
+ // i is always less than self.pos.row, which we assume
355
+ // to be always valid, so it must also be valid.
356
+ // self.size.cols - 1 is always a valid col.
357
+ .unwrap()
358
+ .is_wide_continuation()
359
+ {
360
+ pos.col = self.size.cols - 2;
361
+ }
362
+ let cell = self
363
+ .drawing_cell(pos)
364
+ // i is always less than self.pos.row, which we assume
365
+ // to be always valid, so it must also be valid.
366
+ // self.size.cols - 2 is valid because self.size.cols
367
+ // - 1 is always valid, and col gets set to
368
+ // self.size.cols - 2 when the cell at self.size.cols
369
+ // - 1 is a wide continuation character, meaning that
370
+ // the first half of the wide character must be before
371
+ // it
372
+ .unwrap();
373
+ if cell.has_contents() {
374
+ if let Some(prev_pos) = prev_pos {
375
+ if prev_pos.row != i
376
+ || prev_pos.col < self.size.cols
377
+ {
378
+ crate::term::MoveFromTo::new(prev_pos, pos)
379
+ .write_buf(contents);
380
+ cell.attrs().write_escape_code_diff(
381
+ contents,
382
+ &prev_attrs,
383
+ );
384
+ contents.extend(cell.contents().as_bytes());
385
+ prev_attrs.write_escape_code_diff(
386
+ contents,
387
+ cell.attrs(),
388
+ );
389
+ }
390
+ } else {
391
+ crate::term::MoveTo::new(pos).write_buf(contents);
392
+ cell.attrs().write_escape_code_diff(
393
+ contents,
394
+ &prev_attrs,
395
+ );
396
+ contents.extend(cell.contents().as_bytes());
397
+ prev_attrs.write_escape_code_diff(
398
+ contents,
399
+ cell.attrs(),
400
+ );
401
+ }
402
+ contents.extend(
403
+ "\n".repeat(usize::from(self.pos.row - i))
404
+ .as_bytes(),
405
+ );
406
+ found = true;
407
+ break;
408
+ }
409
+ }
410
+
411
+ // this can happen if you get the cursor off the end of a row,
412
+ // and then do something to clear the end of the current row
413
+ // without moving the cursor (IL, DL, ED, EL, etc). we know
414
+ // there can't be something in the last column because we
415
+ // would have caught that above, so it should be safe to
416
+ // overwrite it.
417
+ if !found {
418
+ pos = Pos {
419
+ row: self.pos.row,
420
+ col: self.size.cols - 1,
421
+ };
422
+ if let Some(prev_pos) = prev_pos {
423
+ crate::term::MoveFromTo::new(prev_pos, pos)
424
+ .write_buf(contents);
425
+ } else {
426
+ crate::term::MoveTo::new(pos).write_buf(contents);
427
+ }
428
+ contents.push(b' ');
429
+ // we know that the cell has no contents, but it still may
430
+ // have drawing attributes (background color, etc)
431
+ let end_cell = self
432
+ .drawing_cell(pos)
433
+ // we assume self.pos.row is always valid, and
434
+ // self.size.cols - 1 is always a valid column
435
+ .unwrap();
436
+ end_cell
437
+ .attrs()
438
+ .write_escape_code_diff(contents, &prev_attrs);
439
+ crate::term::SaveCursor.write_buf(contents);
440
+ crate::term::Backspace.write_buf(contents);
441
+ crate::term::EraseChar::new(1).write_buf(contents);
442
+ crate::term::RestoreCursor.write_buf(contents);
443
+ prev_attrs
444
+ .write_escape_code_diff(contents, end_cell.attrs());
445
+ }
446
+ }
447
+ } else if let Some(prev_pos) = prev_pos {
448
+ crate::term::MoveFromTo::new(prev_pos, self.pos)
449
+ .write_buf(contents);
450
+ } else {
451
+ crate::term::MoveTo::new(self.pos).write_buf(contents);
452
+ }
453
+ }
454
+
455
+ pub fn erase_all(&mut self, attrs: crate::attrs::Attrs) {
456
+ for row in self.drawing_rows_mut() {
457
+ row.clear(attrs);
458
+ }
459
+ }
460
+
461
+ pub fn erase_all_forward(&mut self, attrs: crate::attrs::Attrs) {
462
+ let pos = self.pos;
463
+ for row in self.drawing_rows_mut().skip(usize::from(pos.row) + 1) {
464
+ row.clear(attrs);
465
+ }
466
+
467
+ self.erase_row_forward(attrs);
468
+ }
469
+
470
+ pub fn erase_all_backward(&mut self, attrs: crate::attrs::Attrs) {
471
+ let pos = self.pos;
472
+ for row in self.drawing_rows_mut().take(usize::from(pos.row)) {
473
+ row.clear(attrs);
474
+ }
475
+
476
+ self.erase_row_backward(attrs);
477
+ }
478
+
479
+ pub fn erase_row(&mut self, attrs: crate::attrs::Attrs) {
480
+ self.current_row_mut().clear(attrs);
481
+ }
482
+
483
+ pub fn erase_row_forward(&mut self, attrs: crate::attrs::Attrs) {
484
+ let size = self.size;
485
+ let pos = self.pos;
486
+ let row = self.current_row_mut();
487
+ for col in pos.col..size.cols {
488
+ row.erase(col, attrs);
489
+ }
490
+ }
491
+
492
+ pub fn erase_row_backward(&mut self, attrs: crate::attrs::Attrs) {
493
+ let size = self.size;
494
+ let pos = self.pos;
495
+ let row = self.current_row_mut();
496
+ for col in 0..=pos.col.min(size.cols - 1) {
497
+ row.erase(col, attrs);
498
+ }
499
+ }
500
+
501
+ pub fn insert_cells(&mut self, count: u16) {
502
+ let size = self.size;
503
+ let pos = self.pos;
504
+ let wide = pos.col < size.cols
505
+ && self
506
+ .drawing_cell(pos)
507
+ // we assume self.pos.row is always valid, and we know we are
508
+ // not off the end of a row because we just checked pos.col <
509
+ // size.cols
510
+ .unwrap()
511
+ .is_wide_continuation();
512
+ let row = self.current_row_mut();
513
+ for _ in 0..count {
514
+ if wide {
515
+ row.get_mut(pos.col).unwrap().set_wide_continuation(false);
516
+ }
517
+ row.insert(pos.col, crate::Cell::new());
518
+ if wide {
519
+ row.get_mut(pos.col).unwrap().set_wide_continuation(true);
520
+ }
521
+ }
522
+ row.truncate(size.cols);
523
+ }
524
+
525
+ pub fn delete_cells(&mut self, count: u16) {
526
+ let size = self.size;
527
+ let pos = self.pos;
528
+ let row = self.current_row_mut();
529
+ for _ in 0..(count.min(size.cols - pos.col)) {
530
+ row.remove(pos.col);
531
+ }
532
+ row.resize(size.cols, crate::Cell::new());
533
+ }
534
+
535
+ pub fn erase_cells(&mut self, count: u16, attrs: crate::attrs::Attrs) {
536
+ let size = self.size;
537
+ let pos = self.pos;
538
+ let row = self.current_row_mut();
539
+ for col in pos.col..((pos.col.saturating_add(count)).min(size.cols)) {
540
+ row.erase(col, attrs);
541
+ }
542
+ }
543
+
544
+ pub fn insert_lines(&mut self, count: u16) {
545
+ for _ in 0..count {
546
+ self.rows.remove(usize::from(self.scroll_bottom));
547
+ self.rows.insert(usize::from(self.pos.row), self.new_row());
548
+ // self.scroll_bottom is maintained to always be a valid row
549
+ self.rows[usize::from(self.scroll_bottom)].wrap(false);
550
+ }
551
+ }
552
+
553
+ pub fn delete_lines(&mut self, count: u16) {
554
+ for _ in 0..(count.min(self.size.rows - self.pos.row)) {
555
+ self.rows
556
+ .insert(usize::from(self.scroll_bottom) + 1, self.new_row());
557
+ self.rows.remove(usize::from(self.pos.row));
558
+ }
559
+ }
560
+
561
+ pub fn scroll_up(&mut self, count: u16) {
562
+ for _ in 0..(count.min(self.size.rows - self.scroll_top)) {
563
+ self.rows
564
+ .insert(usize::from(self.scroll_bottom) + 1, self.new_row());
565
+ let removed = self.rows.remove(usize::from(self.scroll_top));
566
+ if self.scrollback_len > 0 && !self.scroll_region_active() {
567
+ self.scrollback.push_back(removed);
568
+ while self.scrollback.len() > self.scrollback_len {
569
+ self.scrollback.pop_front();
570
+ }
571
+ if self.scrollback_offset > 0 {
572
+ self.scrollback_offset =
573
+ self.scrollback.len().min(self.scrollback_offset + 1);
574
+ }
575
+ }
576
+ }
577
+ }
578
+
579
+ pub fn scroll_down(&mut self, count: u16) {
580
+ for _ in 0..count {
581
+ self.rows.remove(usize::from(self.scroll_bottom));
582
+ self.rows
583
+ .insert(usize::from(self.scroll_top), self.new_row());
584
+ // self.scroll_bottom is maintained to always be a valid row
585
+ self.rows[usize::from(self.scroll_bottom)].wrap(false);
586
+ }
587
+ }
588
+
589
+ pub fn set_scroll_region(&mut self, top: u16, bottom: u16) {
590
+ let bottom = bottom.min(self.size().rows - 1);
591
+ if top < bottom {
592
+ self.scroll_top = top;
593
+ self.scroll_bottom = bottom;
594
+ } else {
595
+ self.scroll_top = 0;
596
+ self.scroll_bottom = self.size().rows - 1;
597
+ }
598
+ self.pos.row = self.scroll_top;
599
+ self.pos.col = 0;
600
+ }
601
+
602
+ fn in_scroll_region(&self) -> bool {
603
+ self.pos.row >= self.scroll_top && self.pos.row <= self.scroll_bottom
604
+ }
605
+
606
+ fn scroll_region_active(&self) -> bool {
607
+ self.scroll_top != 0 || self.scroll_bottom != self.size.rows - 1
608
+ }
609
+
610
+ pub fn set_origin_mode(&mut self, mode: bool) {
611
+ self.origin_mode = mode;
612
+ self.set_pos(Pos { row: 0, col: 0 });
613
+ }
614
+
615
+ pub fn row_inc_clamp(&mut self, count: u16) {
616
+ let in_scroll_region = self.in_scroll_region();
617
+ self.pos.row = self.pos.row.saturating_add(count);
618
+ self.row_clamp_bottom(in_scroll_region);
619
+ }
620
+
621
+ pub fn row_inc_scroll(&mut self, count: u16) -> u16 {
622
+ let in_scroll_region = self.in_scroll_region();
623
+ self.pos.row = self.pos.row.saturating_add(count);
624
+ let lines = self.row_clamp_bottom(in_scroll_region);
625
+ if in_scroll_region {
626
+ self.scroll_up(lines);
627
+ lines
628
+ } else {
629
+ 0
630
+ }
631
+ }
632
+
633
+ pub fn row_dec_clamp(&mut self, count: u16) {
634
+ let in_scroll_region = self.in_scroll_region();
635
+ self.pos.row = self.pos.row.saturating_sub(count);
636
+ self.row_clamp_top(in_scroll_region);
637
+ }
638
+
639
+ pub fn row_dec_scroll(&mut self, count: u16) {
640
+ let in_scroll_region = self.in_scroll_region();
641
+ // need to account for clamping by both row_clamp_top and by
642
+ // saturating_sub
643
+ let extra_lines = count.saturating_sub(self.pos.row);
644
+ self.pos.row = self.pos.row.saturating_sub(count);
645
+ let lines = self.row_clamp_top(in_scroll_region);
646
+ self.scroll_down(lines + extra_lines);
647
+ }
648
+
649
+ pub fn row_set(&mut self, i: u16) {
650
+ self.pos.row = i;
651
+ self.row_clamp();
652
+ }
653
+
654
+ pub fn col_inc(&mut self, count: u16) {
655
+ self.pos.col = self.pos.col.saturating_add(count);
656
+ }
657
+
658
+ pub fn col_inc_clamp(&mut self, count: u16) {
659
+ self.pos.col = self.pos.col.saturating_add(count);
660
+ self.col_clamp();
661
+ }
662
+
663
+ pub fn col_dec(&mut self, count: u16) {
664
+ self.pos.col = self.pos.col.saturating_sub(count);
665
+ }
666
+
667
+ pub fn col_tab(&mut self) {
668
+ self.pos.col -= self.pos.col % 8;
669
+ self.pos.col += 8;
670
+ self.col_clamp();
671
+ }
672
+
673
+ pub fn col_set(&mut self, i: u16) {
674
+ self.pos.col = i;
675
+ self.col_clamp();
676
+ }
677
+
678
+ pub fn col_wrap(&mut self, width: u16, wrap: bool) {
679
+ if self.pos.col > self.size.cols - width {
680
+ let mut prev_pos = self.pos;
681
+ self.pos.col = 0;
682
+ let scrolled = self.row_inc_scroll(1);
683
+ prev_pos.row -= scrolled;
684
+ let new_pos = self.pos;
685
+ self.drawing_row_mut(prev_pos.row)
686
+ // we assume self.pos.row is always valid, and so prev_pos.row
687
+ // must be valid because it is always less than or equal to
688
+ // self.pos.row
689
+ .unwrap()
690
+ .wrap(wrap && prev_pos.row + 1 == new_pos.row);
691
+ }
692
+ }
693
+
694
+ fn row_clamp_top(&mut self, limit_to_scroll_region: bool) -> u16 {
695
+ if limit_to_scroll_region && self.pos.row < self.scroll_top {
696
+ let rows = self.scroll_top - self.pos.row;
697
+ self.pos.row = self.scroll_top;
698
+ rows
699
+ } else {
700
+ 0
701
+ }
702
+ }
703
+
704
+ fn row_clamp_bottom(&mut self, limit_to_scroll_region: bool) -> u16 {
705
+ let bottom = if limit_to_scroll_region {
706
+ self.scroll_bottom
707
+ } else {
708
+ self.size.rows - 1
709
+ };
710
+ if self.pos.row > bottom {
711
+ let rows = self.pos.row - bottom;
712
+ self.pos.row = bottom;
713
+ rows
714
+ } else {
715
+ 0
716
+ }
717
+ }
718
+
719
+ fn row_clamp(&mut self) {
720
+ if self.pos.row > self.size.rows - 1 {
721
+ self.pos.row = self.size.rows - 1;
722
+ }
723
+ }
724
+
725
+ fn col_clamp(&mut self) {
726
+ if self.pos.col > self.size.cols - 1 {
727
+ self.pos.col = self.size.cols - 1;
728
+ }
729
+ }
730
+ }
731
+
732
+ #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
733
+ pub struct Size {
734
+ pub rows: u16,
735
+ pub cols: u16,
736
+ }
737
+
738
+ #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
739
+ pub struct Pos {
740
+ pub row: u16,
741
+ pub col: u16,
742
+ }