From b9030a684c0ad64951a388e49d5825c12b483fb4 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Thu, 1 Dec 2022 21:08:56 +0200 Subject: [PATCH] listings: fix selection not appearing immediately and invalid motions --- src/components/mail/listing.rs | 3 +- src/components/mail/listing/compact.rs | 52 ++--- src/components/mail/listing/conversations.rs | 45 ++-- src/components/mail/listing/plain.rs | 212 ++++++++++++++++--- src/components/mail/listing/thread.rs | 1 + 5 files changed, 233 insertions(+), 80 deletions(-) diff --git a/src/components/mail/listing.rs b/src/components/mail/listing.rs index a94c1107..dc0115b5 100644 --- a/src/components/mail/listing.rs +++ b/src/components/mail/listing.rs @@ -93,9 +93,10 @@ impl RowsState { &mut self, thread: ThreadHash, metadata: T, - env_hashes: SmallVec<[EnvelopeHash; 8]>, + mut env_hashes: SmallVec<[EnvelopeHash; 8]>, entry_strings: EntryStrings, ) { + env_hashes.dedup(); let index = self.entries.len(); self.thread_order.insert(thread, index); self.all_threads.insert(thread); diff --git a/src/components/mail/listing/compact.rs b/src/components/mail/listing/compact.rs index cd49a685..dc476586 100644 --- a/src/components/mail/listing/compact.rs +++ b/src/components/mail/listing/compact.rs @@ -1372,9 +1372,7 @@ impl Component for CompactListing { if let Some(mvm) = self.movement.as_ref() { match mvm { PageMovement::Up(amount) => { - for c in self.new_cursor_pos.2.saturating_sub(*amount) - ..=self.new_cursor_pos.2 - { + for c in self.cursor_pos.2.saturating_sub(*amount)..=self.cursor_pos.2 { if let Some(thread) = self.get_thread_under_cursor(c) { self.rows.update_selection_with_thread( thread, @@ -1390,8 +1388,8 @@ impl Component for CompactListing { } } if modifier == Modifier::Intersection { - for c in (0..self.new_cursor_pos.2.saturating_sub(*amount)) - .chain((self.new_cursor_pos.2 + 2)..self.length) + for c in (0..self.cursor_pos.2.saturating_sub(*amount)) + .chain((self.cursor_pos.2 + 2)..self.length) { if let Some(thread) = self.get_thread_under_cursor(c) { self.rows @@ -1401,8 +1399,8 @@ impl Component for CompactListing { } } PageMovement::PageUp(multiplier) => { - for c in self.new_cursor_pos.2.saturating_sub(rows * multiplier) - ..=self.new_cursor_pos.2 + for c in self.cursor_pos.2.saturating_sub(rows * multiplier) + ..=self.cursor_pos.2 { if let Some(thread) = self.get_thread_under_cursor(c) { self.rows.update_selection_with_thread( @@ -1420,8 +1418,8 @@ impl Component for CompactListing { } } PageMovement::Down(amount) => { - for c in self.new_cursor_pos.2 - ..std::cmp::min(self.length, self.new_cursor_pos.2 + amount + 1) + for c in self.cursor_pos.2 + ..std::cmp::min(self.length, self.cursor_pos.2 + amount + 1) { if let Some(thread) = self.get_thread_under_cursor(c) { self.rows.update_selection_with_thread( @@ -1438,9 +1436,9 @@ impl Component for CompactListing { } } if modifier == Modifier::Intersection { - for c in (0..self.new_cursor_pos.2).chain( - (std::cmp::min(self.length, self.new_cursor_pos.2 + amount + 1) - + 1)..self.length, + for c in (0..self.cursor_pos.2).chain( + (std::cmp::min(self.length, self.cursor_pos.2 + amount) + 1) + ..self.length, ) { if let Some(thread) = self.get_thread_under_cursor(c) { self.rows @@ -1450,9 +1448,9 @@ impl Component for CompactListing { } } PageMovement::PageDown(multiplier) => { - for c in self.new_cursor_pos.2 + for c in self.cursor_pos.2 ..std::cmp::min( - self.new_cursor_pos.2 + rows * multiplier + 1, + self.cursor_pos.2 + rows * multiplier + 1, self.length, ) { @@ -1471,9 +1469,9 @@ impl Component for CompactListing { } } if modifier == Modifier::Intersection { - for c in (0..self.new_cursor_pos.2).chain( + for c in (0..self.cursor_pos.2).chain( (std::cmp::min( - self.new_cursor_pos.2 + rows * multiplier + 1, + self.cursor_pos.2 + rows * multiplier, self.length, ) + 1)..self.length, ) { @@ -1486,7 +1484,7 @@ impl Component for CompactListing { } PageMovement::Right(_) | PageMovement::Left(_) => {} PageMovement::Home => { - for c in 0..=self.new_cursor_pos.2 { + for c in 0..=self.cursor_pos.2 { if let Some(thread) = self.get_thread_under_cursor(c) { self.rows.update_selection_with_thread( thread, @@ -1502,7 +1500,7 @@ impl Component for CompactListing { } } if modifier == Modifier::Intersection { - for c in (self.new_cursor_pos.2 + 1)..self.length { + for c in (self.cursor_pos.2)..self.length { if let Some(thread) = self.get_thread_under_cursor(c) { self.rows .update_selection_with_thread(thread, |e| *e = false); @@ -1511,7 +1509,7 @@ impl Component for CompactListing { } } PageMovement::End => { - for c in self.new_cursor_pos.2..self.length { + for c in self.cursor_pos.2..self.length { if let Some(thread) = self.get_thread_under_cursor(c) { self.rows.update_selection_with_thread( thread, @@ -1527,7 +1525,7 @@ impl Component for CompactListing { } } if modifier == Modifier::Intersection { - for c in 0..self.new_cursor_pos.2 { + for c in 0..self.cursor_pos.2 { if let Some(thread) = self.get_thread_under_cursor(c) { self.rows .update_selection_with_thread(thread, |e| *e = false); @@ -1547,18 +1545,7 @@ impl Component for CompactListing { let page_no = (self.new_cursor_pos.2).wrapping_div(rows); let top_idx = page_no * rows; - if row >= top_idx && row < top_idx + rows { - let new_area = nth_row_area(area, row % rows); - self.data_columns.draw( - grid, - row, - self.cursor_pos.2, - grid.bounds_iter(new_area), - ); - let row_attr = self.rows.row_attr_cache[&row]; - change_colors(grid, new_area, row_attr.fg, row_attr.bg); - context.dirty_areas.push_back(new_area); - } + self.force_draw |= row >= top_idx && row < top_idx + rows; } if self.force_draw { /* Draw the entire list */ @@ -1666,6 +1653,7 @@ impl Component for CompactListing { { self.rows .update_selection_with_thread(thread_hash, |e| *e = !*e); + self.set_dirty(true); } return true; } diff --git a/src/components/mail/listing/conversations.rs b/src/components/mail/listing/conversations.rs index be088a2b..c6d8b6c9 100644 --- a/src/components/mail/listing/conversations.rs +++ b/src/components/mail/listing/conversations.rs @@ -473,7 +473,9 @@ impl ListingTrait for ConversationsListing { self.highlight_line(grid, new_area, *idx, context); context.dirty_areas.push_back(new_area); } - return; + if !self.force_draw { + return; + } } else if self.cursor_pos != self.new_cursor_pos { self.cursor_pos = self.new_cursor_pos; } @@ -1012,9 +1014,7 @@ impl Component for ConversationsListing { if let Some(mvm) = self.movement.as_ref() { match mvm { PageMovement::Up(amount) => { - for c in self.new_cursor_pos.2.saturating_sub(*amount) - ..=self.new_cursor_pos.2 - { + for c in self.cursor_pos.2.saturating_sub(*amount)..=self.cursor_pos.2 { if let Some(thread) = self.get_thread_under_cursor(c) { self.rows.update_selection_with_thread( thread, @@ -1030,8 +1030,8 @@ impl Component for ConversationsListing { } } if modifier == Modifier::Intersection { - for c in (0..self.new_cursor_pos.2.saturating_sub(*amount)) - .chain((self.new_cursor_pos.2 + 2)..self.length) + for c in (0..self.cursor_pos.2.saturating_sub(*amount)) + .chain((self.cursor_pos.2 + 2)..self.length) { if let Some(thread) = self.get_thread_under_cursor(c) { self.rows @@ -1041,8 +1041,8 @@ impl Component for ConversationsListing { } } PageMovement::PageUp(multiplier) => { - for c in self.new_cursor_pos.2.saturating_sub(rows * multiplier) - ..=self.new_cursor_pos.2 + for c in self.cursor_pos.2.saturating_sub(rows * multiplier) + ..=self.cursor_pos.2 { if let Some(thread) = self.get_thread_under_cursor(c) { self.rows.update_selection_with_thread( @@ -1060,8 +1060,8 @@ impl Component for ConversationsListing { } } PageMovement::Down(amount) => { - for c in self.new_cursor_pos.2 - ..std::cmp::min(self.length, self.new_cursor_pos.2 + amount + 1) + for c in self.cursor_pos.2 + ..std::cmp::min(self.length, self.cursor_pos.2 + amount + 1) { if let Some(thread) = self.get_thread_under_cursor(c) { self.rows.update_selection_with_thread( @@ -1078,9 +1078,9 @@ impl Component for ConversationsListing { } } if modifier == Modifier::Intersection { - for c in (0..self.new_cursor_pos.2).chain( - (std::cmp::min(self.length, self.new_cursor_pos.2 + amount + 1) - + 1)..self.length, + for c in (0..self.cursor_pos.2).chain( + (std::cmp::min(self.length, self.cursor_pos.2 + amount + 1) + 1) + ..self.length, ) { if let Some(thread) = self.get_thread_under_cursor(c) { self.rows @@ -1090,9 +1090,9 @@ impl Component for ConversationsListing { } } PageMovement::PageDown(multiplier) => { - for c in self.new_cursor_pos.2 + for c in self.cursor_pos.2 ..std::cmp::min( - self.new_cursor_pos.2 + rows * multiplier + 1, + self.cursor_pos.2 + rows * multiplier + 1, self.length, ) { @@ -1111,9 +1111,9 @@ impl Component for ConversationsListing { } } if modifier == Modifier::Intersection { - for c in (0..self.new_cursor_pos.2).chain( + for c in (0..self.cursor_pos.2).chain( (std::cmp::min( - self.new_cursor_pos.2 + rows * multiplier + 1, + self.cursor_pos.2 + rows * multiplier + 1, self.length, ) + 1)..self.length, ) { @@ -1126,7 +1126,7 @@ impl Component for ConversationsListing { } PageMovement::Right(_) | PageMovement::Left(_) => {} PageMovement::Home => { - for c in 0..=self.new_cursor_pos.2 { + for c in 0..=self.cursor_pos.2 { if let Some(thread) = self.get_thread_under_cursor(c) { self.rows.update_selection_with_thread( thread, @@ -1142,7 +1142,7 @@ impl Component for ConversationsListing { } } if modifier == Modifier::Intersection { - for c in (self.new_cursor_pos.2 + 1)..self.length { + for c in (self.cursor_pos.2 + 1)..self.length { if let Some(thread) = self.get_thread_under_cursor(c) { self.rows .update_selection_with_thread(thread, |e| *e = false); @@ -1151,7 +1151,7 @@ impl Component for ConversationsListing { } } PageMovement::End => { - for c in self.new_cursor_pos.2..self.length { + for c in self.cursor_pos.2..self.length { if let Some(thread) = self.get_thread_under_cursor(c) { self.rows.update_selection_with_thread( thread, @@ -1167,7 +1167,7 @@ impl Component for ConversationsListing { } } if modifier == Modifier::Intersection { - for c in 0..self.new_cursor_pos.2 { + for c in 0..self.cursor_pos.2 { if let Some(thread) = self.get_thread_under_cursor(c) { self.rows .update_selection_with_thread(thread, |e| *e = false); @@ -1177,7 +1177,9 @@ impl Component for ConversationsListing { } } } + self.force_draw = true; } + if !self.rows.row_updates.is_empty() { /* certain rows need to be updated (eg an unseen message was just set seen) * */ @@ -1305,6 +1307,7 @@ impl Component for ConversationsListing { self.modifier_command = Some(Modifier::default()); } else if let Some(thread) = self.get_thread_under_cursor(self.cursor_pos.2) { self.rows.update_selection_with_thread(thread, |e| *e = !*e); + self.set_dirty(true); } return true; } diff --git a/src/components/mail/listing/plain.rs b/src/components/mail/listing/plain.rs index 3e45f528..01a0ff48 100644 --- a/src/components/mail/listing/plain.rs +++ b/src/components/mail/listing/plain.rs @@ -461,7 +461,9 @@ impl ListingTrait for PlainListing { } context.dirty_areas.push_back(new_area); } - return; + if !self.force_draw { + return; + } } else if self.cursor_pos != self.new_cursor_pos { self.cursor_pos = self.new_cursor_pos; } @@ -1025,37 +1027,194 @@ impl Component for PlainListing { area = (set_y(upper_left, y + 1), bottom_right); } + let (upper_left, bottom_right) = area; + let rows = get_y(bottom_right) - get_y(upper_left) + 1; + + if let Some(modifier) = self.modifier_command.take() { + if let Some(mvm) = self.movement.as_ref() { + match mvm { + PageMovement::Up(amount) => { + for c in self.cursor_pos.2.saturating_sub(*amount)..=self.cursor_pos.2 { + if let Some(env_hash) = self.get_env_under_cursor(c) { + self.rows.update_selection_with_env( + env_hash, + match modifier { + Modifier::SymmetricDifference => { + |e: &mut bool| *e = !*e + } + Modifier::Union => |e: &mut bool| *e = true, + Modifier::Difference => |e: &mut bool| *e = false, + Modifier::Intersection => |_: &mut bool| {}, + }, + ); + } + } + if modifier == Modifier::Intersection { + for c in (0..self.cursor_pos.2.saturating_sub(*amount)) + .chain((self.cursor_pos.2 + 2)..self.length) + { + if let Some(env_hash) = self.get_env_under_cursor(c) { + self.rows + .update_selection_with_env(env_hash, |e| *e = false); + } + } + } + } + PageMovement::PageUp(multiplier) => { + for c in self.cursor_pos.2.saturating_sub(rows * multiplier) + ..=self.cursor_pos.2 + { + if let Some(env_hash) = self.get_env_under_cursor(c) { + self.rows.update_selection_with_env( + env_hash, + match modifier { + Modifier::SymmetricDifference => { + |e: &mut bool| *e = !*e + } + Modifier::Union => |e: &mut bool| *e = true, + Modifier::Difference => |e: &mut bool| *e = false, + Modifier::Intersection => |_: &mut bool| {}, + }, + ); + } + } + } + PageMovement::Down(amount) => { + for c in self.cursor_pos.2 + ..std::cmp::min(self.length, self.cursor_pos.2 + amount + 1) + { + if let Some(env_hash) = self.get_env_under_cursor(c) { + self.rows.update_selection_with_env( + env_hash, + match modifier { + Modifier::SymmetricDifference => { + |e: &mut bool| *e = !*e + } + Modifier::Union => |e: &mut bool| *e = true, + Modifier::Difference => |e: &mut bool| *e = false, + Modifier::Intersection => |_: &mut bool| {}, + }, + ); + } + } + if modifier == Modifier::Intersection { + for c in (0..self.cursor_pos.2).chain( + (std::cmp::min(self.length, self.cursor_pos.2 + amount) + 1) + ..self.length, + ) { + if let Some(env_hash) = self.get_env_under_cursor(c) { + self.rows + .update_selection_with_env(env_hash, |e| *e = false); + } + } + } + } + PageMovement::PageDown(multiplier) => { + for c in self.cursor_pos.2 + ..std::cmp::min(self.cursor_pos.2 + rows * multiplier, self.length) + { + if let Some(env_hash) = self.get_env_under_cursor(c) { + self.rows.update_selection_with_env( + env_hash, + match modifier { + Modifier::SymmetricDifference => { + |e: &mut bool| *e = !*e + } + Modifier::Union => |e: &mut bool| *e = true, + Modifier::Difference => |e: &mut bool| *e = false, + Modifier::Intersection => |_: &mut bool| {}, + }, + ); + } + } + if modifier == Modifier::Intersection { + for c in (0..self.cursor_pos.2).chain( + (std::cmp::min( + self.cursor_pos.2 + rows * multiplier, + self.length, + ) + 1)..self.length, + ) { + if let Some(env_hash) = self.get_env_under_cursor(c) { + self.rows + .update_selection_with_env(env_hash, |e| *e = false); + } + } + } + } + PageMovement::Right(_) | PageMovement::Left(_) => {} + PageMovement::Home => { + for c in 0..=self.cursor_pos.2 { + if let Some(env_hash) = self.get_env_under_cursor(c) { + self.rows.update_selection_with_env( + env_hash, + match modifier { + Modifier::SymmetricDifference => { + |e: &mut bool| *e = !*e + } + Modifier::Union => |e: &mut bool| *e = true, + Modifier::Difference => |e: &mut bool| *e = false, + Modifier::Intersection => |_: &mut bool| {}, + }, + ); + } + } + if modifier == Modifier::Intersection { + for c in (self.cursor_pos.2)..self.length { + if let Some(env_hash) = self.get_env_under_cursor(c) { + self.rows + .update_selection_with_env(env_hash, |e| *e = false); + } + } + } + } + PageMovement::End => { + for c in self.cursor_pos.2..self.length { + if let Some(env_hash) = self.get_env_under_cursor(c) { + self.rows.update_selection_with_env( + env_hash, + match modifier { + Modifier::SymmetricDifference => { + |e: &mut bool| *e = !*e + } + Modifier::Union => |e: &mut bool| *e = true, + Modifier::Difference => |e: &mut bool| *e = false, + Modifier::Intersection => |_: &mut bool| {}, + }, + ); + } + } + if modifier == Modifier::Intersection { + for c in 0..self.cursor_pos.2 { + if let Some(env_hash) = self.get_env_under_cursor(c) { + self.rows + .update_selection_with_env(env_hash, |e| *e = false); + } + } + } + } + } + } + self.force_draw = true; + } + if !self.rows.row_updates.is_empty() { - let (upper_left, bottom_right) = area; while let Some(env_hash) = self.rows.row_updates.pop() { let row: usize = self.rows.env_order[&env_hash]; - let rows = get_y(bottom_right) - get_y(upper_left) + 1; + let envelope: EnvelopeRef = context.accounts[&self.cursor_pos.0] + .collection + .get_env(env_hash); + let row_attr = row_attr!( + self.color_cache, + row % 2 == 0, + !envelope.is_seen(), + false, + self.rows.selection[&env_hash] + ); + self.rows.row_attr_cache.insert(row, row_attr); let page_no = (self.new_cursor_pos.2).wrapping_div(rows); let top_idx = page_no * rows; - if row >= top_idx && row <= top_idx + rows { - let new_area = nth_row_area(area, row % rows); - self.data_columns.draw( - grid, - row, - self.cursor_pos.2, - grid.bounds_iter(new_area), - ); - let envelope: EnvelopeRef = context.accounts[&self.cursor_pos.0] - .collection - .get_env(env_hash); - let row_attr = row_attr!( - self.color_cache, - row % 2 == 0, - !envelope.is_seen(), - false, - self.rows.selection[&env_hash] - ); - self.rows.row_attr_cache.insert(row, row_attr); - - change_colors(grid, new_area, row_attr.fg, row_attr.bg); - context.dirty_areas.push_back(new_area); - } + self.force_draw |= row >= top_idx && row < top_idx + rows; } if self.force_draw { /* Draw the entire list */ @@ -1149,6 +1308,7 @@ impl Component for PlainListing { self.modifier_command = Some(Modifier::default()); } else if let Some(env_hash) = self.get_env_under_cursor(self.cursor_pos.2) { self.rows.update_selection_with_env(env_hash, |e| *e = !*e); + self.set_dirty(true); } return true; } diff --git a/src/components/mail/listing/thread.rs b/src/components/mail/listing/thread.rs index 29a99679..2f89bc88 100644 --- a/src/components/mail/listing/thread.rs +++ b/src/components/mail/listing/thread.rs @@ -1505,6 +1505,7 @@ impl Component for ThreadListing { self.modifier_command = Some(Modifier::default()); } else if let Some(env_hash) = self.get_env_under_cursor(self.cursor_pos.2) { self.rows.update_selection_with_env(env_hash, |e| *e = !*e); + self.set_dirty(true); } return true; }