From 16ccff0f445debd9bafeb080fc6a7185cbd842ca Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Sun, 1 Dec 2019 17:04:55 +0200 Subject: [PATCH] ui: add RowIterator and BoundsIterator for CellBuffer Use `RowIterator` to iterate the cells of a row without the need to do any bounds checking; the iterator will simply return `None` when it reaches the end of the row. `RowIterator` can be created via the `CellBuffer::row_iter` method and can be returned by `BoundsIterator` which iterates each row. --- ui/src/components/mail/listing/plain.rs | 90 ++++++++++++++----------- ui/src/terminal/cells.rs | 84 +++++++++++++++++++++++ 2 files changed, 134 insertions(+), 40 deletions(-) diff --git a/ui/src/components/mail/listing/plain.rs b/ui/src/components/mail/listing/plain.rs index cd4a374b..8e323882 100644 --- a/ui/src/components/mail/listing/plain.rs +++ b/ui/src/components/mail/listing/plain.rs @@ -123,7 +123,7 @@ impl ListingTrait for PlainListing { let (upper_left, bottom_right) = area; change_colors(grid, area, fg_color, bg_color); - let mut x = get_x(upper_left) + let x = get_x(upper_left) + self.data_columns.widths[0] + self.data_columns.widths[1] + self.data_columns.widths[2] @@ -138,9 +138,11 @@ impl ListingTrait for PlainListing { pos_dec(self.data_columns.columns[3].size(), (1, 1)), ), ); - for _ in 0..self.data_columns.widths[3] { - grid[(x, get_y(upper_left))].set_bg(bg_color); - x += 1; + for c in grid.row_iter( + (x, x + self.data_columns.widths[3].saturating_sub(1)), + get_y(upper_left), + ) { + grid[c].set_bg(bg_color); } return; } @@ -310,13 +312,18 @@ impl ListingTrait for PlainListing { fg_color, bg_color, ); - for x in flag_x - ..std::cmp::min( - get_x(bottom_right), - flag_x + 2 + self.data_columns.widths[3], - ) - { - grid[(x, get_y(upper_left) + r)].set_bg(bg_color); + for c in grid.row_iter( + ( + flag_x, + std::cmp::min( + get_x(bottom_right), + flag_x + 2 + self.data_columns.widths[3], + ) + .saturating_sub(1), + ), + get_y(upper_left) + r, + ) { + grid[c].set_bg(bg_color); } change_colors( grid, @@ -683,6 +690,7 @@ impl PlainListing { as Box> }; + let columns = &mut self.data_columns.columns; for ((idx, i), strings) in iter.enumerate().zip(rows) { if !context.accounts[self.cursor_pos.0].contains_key(i) { //debug!("key = {}", i); @@ -721,55 +729,55 @@ impl PlainListing { }; let (x, _) = write_string_to_grid( &idx.to_string(), - &mut self.data_columns.columns[0], + &mut columns[0], fg_color, bg_color, Attr::Default, ((0, idx), (min_width.0, idx)), None, ); - for x in x..min_width.0 { - self.data_columns.columns[0][(x, idx)].set_bg(bg_color); + for c in columns[0].row_iter((x, min_width.0.saturating_sub(1)), idx) { + columns[0][c].set_bg(bg_color); } let (x, _) = write_string_to_grid( &strings.date, - &mut self.data_columns.columns[1], + &mut columns[1], fg_color, bg_color, Attr::Default, ((0, idx), (min_width.1, idx)), None, ); - for x in x..min_width.1 { - self.data_columns.columns[1][(x, idx)].set_bg(bg_color); + for c in columns[1].row_iter((x, min_width.1.saturating_sub(1)), idx) { + columns[1][c].set_bg(bg_color); } let (x, _) = write_string_to_grid( &strings.from, - &mut self.data_columns.columns[2], + &mut columns[2], fg_color, bg_color, Attr::Default, ((0, idx), (min_width.2, idx)), None, ); - for x in x..min_width.2 { - self.data_columns.columns[2][(x, idx)].set_bg(bg_color); + for c in columns[2].row_iter((x, min_width.2.saturating_sub(1)), idx) { + columns[2][c].set_bg(bg_color); } let (x, _) = write_string_to_grid( &strings.flag, - &mut self.data_columns.columns[3], + &mut columns[3], fg_color, bg_color, Attr::Default, ((0, idx), (min_width.3, idx)), None, ); - for x in x..min_width.3 { - self.data_columns.columns[3][(x, idx)].set_bg(bg_color); + for c in columns[3].row_iter((x, min_width.3.saturating_sub(1)), idx) { + columns[3][c].set_bg(bg_color); } let (x, _) = write_string_to_grid( &strings.subject, - &mut self.data_columns.columns[4], + &mut columns[4], fg_color, bg_color, Attr::Default, @@ -778,41 +786,43 @@ impl PlainListing { ); let x = { let mut x = x + 1; - use std::convert::TryInto; - for (m, t) in strings.tags.split_whitespace().enumerate() { - let m = 2 * m.try_into().unwrap_or(0); + for (t, &color) in strings.tags.split_whitespace().zip(strings.tags.1.iter()) { let (_x, _) = write_string_to_grid( t, - &mut self.data_columns.columns[4], + &mut columns[4], Color::White, - Color::Byte(103 + m), + Color::Byte(color), Attr::Bold, ((x + 1, idx), (min_width.4, idx)), None, ); - self.data_columns.columns[4][(x, idx)].set_bg(Color::Byte(103 + m)); - if _x < min_width.4 { - self.data_columns.columns[4][(_x, idx)].set_bg(Color::Byte(103 + m)); - self.data_columns.columns[4][(_x, idx)].set_keep_bg(true); + for c in columns[4].row_iter((x, x), idx) { + columns[4][c].set_bg(Color::Byte(color)); } - for x in (x + 1).._x { - self.data_columns.columns[4][(x, idx)].set_keep_fg(true); - self.data_columns.columns[4][(x, idx)].set_keep_bg(true); + for c in columns[4].row_iter((_x, _x), idx) { + columns[4][c].set_bg(Color::Byte(color)); + columns[4][c].set_keep_bg(true); + } + for c in columns[4].row_iter((x + 1, _x), idx) { + columns[4][c].set_keep_fg(true); + columns[4][c].set_keep_bg(true); + } + for c in columns[4].row_iter((x, x), idx) { + columns[4][c].set_keep_bg(true); } - self.data_columns.columns[4][(x, idx)].set_keep_bg(true); x = _x + 1; } x }; - for x in x..min_width.4 { - self.data_columns.columns[4][(x, idx)].set_bg(bg_color); + for c in columns[4].row_iter((x, min_width.4.saturating_sub(1)), idx) { + columns[4][c].set_bg(bg_color); } if context.accounts[self.cursor_pos.0] .collection .get_env(i) .has_attachments() { - self.data_columns.columns[3][(0, idx)].set_fg(Color::Byte(103)); + columns[3][(0, idx)].set_fg(Color::Byte(103)); } } if self.length == 0 && self.filter_term.is_empty() { diff --git a/ui/src/terminal/cells.rs b/ui/src/terminal/cells.rs index a2ddb17f..9038400b 100644 --- a/ui/src/terminal/cells.rs +++ b/ui/src/terminal/cells.rs @@ -350,6 +350,29 @@ impl CellBuffer { } } } + + pub fn bounds_iter(&self, area: Area) -> BoundsIterator { + BoundsIterator { + rows: std::cmp::min(self.rows.saturating_sub(1), get_y(upper_left!(area))) + ..(std::cmp::min(self.rows, get_y(bottom_right!(area))) + 1), + cols: ( + std::cmp::min(self.cols.saturating_sub(1), get_x(upper_left!(area))), + std::cmp::min(self.cols.saturating_sub(1), get_x(bottom_right!(area))), + ), + } + } + + pub fn row_iter(&self, bounds: (usize, usize), row: usize) -> RowIterator { + if row < self.rows { + RowIterator { + row, + col: std::cmp::min(self.cols.saturating_sub(1), bounds.0) + ..(std::cmp::min(self.cols, bounds.1 + 1)), + } + } else { + RowIterator { row, col: 0..0 } + } + } } impl Deref for CellBuffer { @@ -1262,3 +1285,64 @@ pub mod ansi { }) } } + +/// Use `RowIterator` to iterate the cells of a row without the need to do any bounds checking; +/// the iterator will simply return `None` when it reaches the end of the row. +/// `RowIterator` can be created via the `CellBuffer::row_iter` method and can be returned by +/// `BoundsIterator` which iterates each row. +/// ```norun +/// for c in grid.row_iter( +/// (x, x + 10), +/// 0, +/// ) { +/// grid[c].set_ch('w'); +/// } +/// ``` +pub struct RowIterator { + row: usize, + col: std::ops::Range, +} + +pub struct BoundsIterator { + rows: std::ops::Range, + cols: (usize, usize), +} + +impl Iterator for BoundsIterator { + type Item = RowIterator; + fn next(&mut self) -> Option { + if let Some(next_row) = self.rows.next() { + Some(RowIterator { + row: next_row, + col: self.cols.0..(self.cols.1 + 1), + }) + } else { + None + } + } +} + +impl Iterator for RowIterator { + type Item = (usize, usize); + fn next(&mut self) -> Option { + if let Some(next_col) = self.col.next() { + Some((next_col, self.row)) + } else { + None + } + } +} + +impl RowIterator { + pub fn forward_col(mut self, new_val: usize) -> Self { + if self.col.start > new_val { + self + } else if self.col.end <= new_val { + self.col.start = self.col.end; + self + } else { + self.col.start = new_val; + self + } + } +}