From 4f45b109745ebc29febc452b9bcb0cd88f131ffc Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Fri, 9 Dec 2022 12:30:09 +0200 Subject: [PATCH] mail/listing: fix tag updates not showing up right away Closes #132 Closes #133 --- src/components/mail/listing/compact.rs | 170 +++++++++++++++++-- src/components/mail/listing/conversations.rs | 111 ++++++------ src/components/mail/listing/plain.rs | 125 ++++++++++++++ src/components/mail/listing/thread.rs | 119 +++++++------ 4 files changed, 404 insertions(+), 121 deletions(-) diff --git a/src/components/mail/listing/compact.rs b/src/components/mail/listing/compact.rs index 52387433..3ddc89c6 100644 --- a/src/components/mail/listing/compact.rs +++ b/src/components/mail/listing/compact.rs @@ -22,7 +22,9 @@ use super::*; use crate::components::PageMovement; use crate::jobs::JoinHandle; +use indexmap::IndexSet; use std::cmp; +use std::collections::BTreeMap; use std::convert::TryInto; use std::iter::FromIterator; @@ -329,6 +331,13 @@ impl MailListingTrait for CompactListing { SmallVec::new(), ); + let tags_lck = account.collection.tag_index.read().unwrap(); + + let mut other_subjects = IndexSet::new(); + let mut tags = IndexSet::new(); + let mut from_address_list = Vec::new(); + let mut from_address_set: std::collections::HashSet> = + std::collections::HashSet::new(); 'items_for_loop: for thread in items { let thread_node = &threads.thread_nodes()[&threads.thread_ref(thread).root()]; let root_env_hash = if let Some(h) = thread_node.message().or_else(|| { @@ -374,6 +383,45 @@ impl MailListingTrait for CompactListing { continue; } } + other_subjects.clear(); + tags.clear(); + from_address_list.clear(); + from_address_set.clear(); + for (envelope, show_subject) in threads + .thread_group_iter(thread) + .filter_map(|(_, h)| { + Some(( + threads.thread_nodes()[&h].message()?, + threads.thread_nodes()[&h].show_subject(), + )) + }) + .map(|(env_hash, show_subject)| { + ( + context.accounts[&self.cursor_pos.0] + .collection + .get_env(env_hash), + show_subject, + ) + }) + { + if show_subject { + other_subjects.insert(envelope.subject().to_string()); + } + if account.backend_capabilities.supports_tags { + for &t in envelope.tags().iter() { + tags.insert(t); + } + } + + for addr in envelope.from().iter() { + if from_address_set.contains(addr.address_spec_raw()) { + continue; + } + from_address_set.insert(addr.address_spec_raw().to_vec()); + from_address_list.push(addr.clone()); + } + } + let row_attr = row_attr!( self.color_cache, self.length % 2 == 0, @@ -383,7 +431,16 @@ impl MailListingTrait for CompactListing { ); self.rows.row_attr_cache.insert(self.length, row_attr); - let entry_strings = self.make_entry_string(&root_envelope, context, &threads, thread); + let entry_strings = self.make_entry_string( + &root_envelope, + context, + &tags_lck, + &from_address_list, + &threads, + &other_subjects, + &tags, + thread, + ); row_widths .0 .push(digits_of_num!(self.length).try_into().unwrap_or(255)); @@ -843,18 +900,21 @@ impl CompactListing { fn make_entry_string( &self, - e: &Envelope, + root_envelope: &Envelope, context: &Context, + tags_lck: &BTreeMap, + from: &[Address], threads: &Threads, + other_subjects: &IndexSet, + tags: &IndexSet, hash: ThreadHash, ) -> EntryStrings { let thread = threads.thread_ref(hash); - let mut tags = String::new(); + let mut tags_string = String::new(); let mut colors: SmallVec<[_; 8]> = SmallVec::new(); let account = &context.accounts[&self.cursor_pos.0]; if account.backend_capabilities.supports_tags { - let tags_lck = account.collection.tag_index.read().unwrap(); - for t in e.tags().iter() { + for t in tags { if mailbox_settings!( context[self.cursor_pos.0][&self.cursor_pos.1] .tags @@ -867,9 +927,9 @@ impl CompactListing { { continue; } - tags.push(' '); - tags.push_str(tags_lck.get(t).as_ref().unwrap()); - tags.push(' '); + tags_string.push(' '); + tags_string.push_str(tags_lck.get(t).as_ref().unwrap()); + tags_string.push(' '); colors.push( mailbox_settings!(context[self.cursor_pos.0][&self.cursor_pos.1].tags.colors) .get(t) @@ -882,22 +942,44 @@ impl CompactListing { }), ); } - if !tags.is_empty() { - tags.pop(); + if !tags_string.is_empty() { + tags_string.pop(); } } - let mut subject = e.subject().to_string(); + let mut subject = if *mailbox_settings!( + context[self.cursor_pos.0][&self.cursor_pos.1] + .listing + .thread_subject_pack + ) { + other_subjects + .into_iter() + .fold(String::new(), |mut acc, s| { + if !acc.is_empty() { + acc.push_str(", "); + } + acc.push_str(s); + acc + }) + } else { + root_envelope.subject().to_string() + }; subject.truncate_at_boundary(150); EntryStrings { date: DateString(ConversationsListing::format_date(context, thread.date())), subject: if thread.len() > 1 { - SubjectString(format!("{} ({})", subject, thread.len(),)) + SubjectString(format!("{} ({})", subject, thread.len())) } else { SubjectString(subject) }, flag: FlagString(format!( "{selected}{snoozed}{unseen}{attachments}{whitespace}", - selected = if self.rows.selection.get(&e.hash()).cloned().unwrap_or(false) { + selected = if self + .rows + .selection + .get(&root_envelope.hash()) + .cloned() + .unwrap_or(false) + { mailbox_settings!( context[self.cursor_pos.0][&self.cursor_pos.1] .listing @@ -945,7 +1027,12 @@ impl CompactListing { } else { "" }, - whitespace = if self.rows.selection.get(&e.hash()).cloned().unwrap_or(false) + whitespace = if self + .rows + .selection + .get(&root_envelope.hash()) + .cloned() + .unwrap_or(false) || thread.unseen() > 0 || thread.snoozed() || thread.has_attachments() @@ -955,8 +1042,8 @@ impl CompactListing { "" }, )), - from: FromString(address_list!((e.from()) as comma_sep_list)), - tags: TagString(tags, colors), + from: FromString(address_list!((from) as comma_sep_list)), + tags: TagString(tags_string, colors), } } @@ -981,6 +1068,7 @@ impl CompactListing { * arrive */ return; } + let tags_lck = account.collection.tag_index.read().unwrap(); let envelope: EnvelopeRef = account.collection.get_env(env_hash); let thread_hash = self.rows.env_to_thread[&env_hash]; let threads = account.collection.get_threads(self.cursor_pos.1); @@ -994,7 +1082,55 @@ impl CompactListing { self.rows.is_thread_selected(thread_hash) ); self.rows.row_attr_cache.insert(idx, row_attr); - let strings = self.make_entry_string(&envelope, context, &threads, thread_hash); + + let mut other_subjects = IndexSet::new(); + let mut tags = IndexSet::new(); + let mut from_address_list = Vec::new(); + let mut from_address_set: std::collections::HashSet> = + std::collections::HashSet::new(); + for (envelope, show_subject) in threads + .thread_group_iter(thread_hash) + .filter_map(|(_, h)| { + threads.thread_nodes()[&h] + .message() + .map(|env_hash| (env_hash, threads.thread_nodes()[&h].show_subject())) + }) + .map(|(env_hash, show_subject)| { + ( + context.accounts[&self.cursor_pos.0] + .collection + .get_env(env_hash), + show_subject, + ) + }) + { + if show_subject { + other_subjects.insert(envelope.subject().to_string()); + } + if account.backend_capabilities.supports_tags { + for &t in envelope.tags().iter() { + tags.insert(t); + } + } + for addr in envelope.from().iter() { + if from_address_set.contains(addr.address_spec_raw()) { + continue; + } + from_address_set.insert(addr.address_spec_raw().to_vec()); + from_address_list.push(addr.clone()); + } + } + + let strings = self.make_entry_string( + &envelope, + context, + &tags_lck, + &from_address_list, + &threads, + &other_subjects, + &tags, + thread_hash, + ); drop(envelope); let columns = &mut self.data_columns.columns; let min_width = ( diff --git a/src/components/mail/listing/conversations.rs b/src/components/mail/listing/conversations.rs index d462d738..a014afaf 100644 --- a/src/components/mail/listing/conversations.rs +++ b/src/components/mail/listing/conversations.rs @@ -23,6 +23,7 @@ use super::*; use crate::components::PageMovement; use crate::jobs::JoinHandle; use indexmap::IndexSet; +use std::collections::BTreeMap; use std::iter::FromIterator; macro_rules! row_attr { @@ -226,6 +227,8 @@ impl MailListingTrait for ConversationsListing { let account = &context.accounts[&self.cursor_pos.0]; let threads = account.collection.get_threads(self.cursor_pos.1); + let tags_lck = account.collection.tag_index.read().unwrap(); + self.rows.clear(); self.length = 0; if self.error.is_err() { @@ -234,6 +237,7 @@ impl MailListingTrait for ConversationsListing { let mut max_entry_columns = 0; let mut other_subjects = IndexSet::new(); + let mut tags = IndexSet::new(); let mut from_address_list = Vec::new(); let mut from_address_set: std::collections::HashSet> = std::collections::HashSet::new(); @@ -267,7 +271,23 @@ impl MailListingTrait for ConversationsListing { panic!(); } + let root_envelope: &EnvelopeRef = &context.accounts[&self.cursor_pos.0] + .collection + .get_env(root_env_hash); + use melib::search::QueryTrait; + if let Some(filter_query) = mailbox_settings!( + context[self.cursor_pos.0][&self.cursor_pos.1] + .listing + .filter + ) + .as_ref() + { + if !root_envelope.is_match(filter_query) { + continue; + } + } other_subjects.clear(); + tags.clear(); from_address_list.clear(); from_address_set.clear(); for (envelope, show_subject) in threads @@ -290,6 +310,11 @@ impl MailListingTrait for ConversationsListing { if show_subject { other_subjects.insert(envelope.subject().to_string()); } + if account.backend_capabilities.supports_tags { + for &t in envelope.tags().iter() { + tags.insert(t); + } + } for addr in envelope.from().iter() { if from_address_set.contains(addr.address_spec_raw()) { @@ -299,28 +324,15 @@ impl MailListingTrait for ConversationsListing { from_address_list.push(addr.clone()); } } - let root_envelope: &EnvelopeRef = &context.accounts[&self.cursor_pos.0] - .collection - .get_env(root_env_hash); - use melib::search::QueryTrait; - if let Some(filter_query) = mailbox_settings!( - context[self.cursor_pos.0][&self.cursor_pos.1] - .listing - .filter - ) - .as_ref() - { - if !root_envelope.is_match(filter_query) { - continue; - } - } let strings = self.make_entry_string( root_envelope, context, + &tags_lck, &from_address_list, &threads, &other_subjects, + &tags, thread, ); max_entry_columns = std::cmp::max( @@ -628,20 +640,21 @@ impl ConversationsListing { pub(super) fn make_entry_string( &self, - e: &Envelope, + root_envelope: &Envelope, context: &Context, + tags_lck: &BTreeMap, from: &[Address], threads: &Threads, other_subjects: &IndexSet, + tags: &IndexSet, hash: ThreadHash, ) -> EntryStrings { let thread = threads.thread_ref(hash); - let mut tags = String::new(); + let mut tags_string = String::new(); let mut colors = SmallVec::new(); let account = &context.accounts[&self.cursor_pos.0]; if account.backend_capabilities.supports_tags { - let tags_lck = account.collection.tag_index.read().unwrap(); - for t in e.tags().iter() { + for t in tags { if mailbox_settings!( context[self.cursor_pos.0][&self.cursor_pos.1] .tags @@ -654,9 +667,9 @@ impl ConversationsListing { { continue; } - tags.push(' '); - tags.push_str(tags_lck.get(t).as_ref().unwrap()); - tags.push(' '); + tags_string.push(' '); + tags_string.push_str(tags_lck.get(t).as_ref().unwrap()); + tags_string.push(' '); colors.push( mailbox_settings!(context[self.cursor_pos.0][&self.cursor_pos.1].tags.colors) .get(t) @@ -669,8 +682,8 @@ impl ConversationsListing { }), ); } - if !tags.is_empty() { - tags.pop(); + if !tags_string.is_empty() { + tags_string.pop(); } } let mut subject = if *mailbox_settings!( @@ -688,33 +701,23 @@ impl ConversationsListing { acc }) } else { - e.subject().to_string() + root_envelope.subject().to_string() }; subject.truncate_at_boundary(100); - if thread.len() > 1 { - EntryStrings { - date: DateString(ConversationsListing::format_date(context, thread.date())), - subject: SubjectString(format!("{} ({})", subject, thread.len())), - flag: FlagString(format!( - "{}{}", - if thread.has_attachments() { "📎" } else { "" }, - if thread.snoozed() { "💤" } else { "" } - )), - from: FromString(address_list!((from) as comma_sep_list)), - tags: TagString(tags, colors), - } - } else { - EntryStrings { - date: DateString(ConversationsListing::format_date(context, thread.date())), - subject: SubjectString(subject), - flag: FlagString(format!( - "{}{}", - if thread.has_attachments() { "📎" } else { "" }, - if thread.snoozed() { "💤" } else { "" } - )), - from: FromString(address_list!((from) as comma_sep_list)), - tags: TagString(tags, colors), - } + EntryStrings { + date: DateString(ConversationsListing::format_date(context, thread.date())), + subject: SubjectString(if thread.len() > 1 { + format!("{} ({})", subject, thread.len()) + } else { + subject + }), + flag: FlagString(format!( + "{}{}", + if thread.has_attachments() { "📎" } else { "" }, + if thread.snoozed() { "💤" } else { "" } + )), + from: FromString(address_list!((from) as comma_sep_list)), + tags: TagString(tags_string, colors), } } @@ -768,9 +771,11 @@ impl ConversationsListing { let account = &context.accounts[&self.cursor_pos.0]; let thread_hash = self.rows.env_to_thread[&env_hash]; let threads = account.collection.get_threads(self.cursor_pos.1); + let tags_lck = account.collection.tag_index.read().unwrap(); let idx: usize = self.rows.thread_order[&thread_hash]; let mut other_subjects = IndexSet::new(); + let mut tags = IndexSet::new(); let mut from_address_list = Vec::new(); let mut from_address_set: std::collections::HashSet> = std::collections::HashSet::new(); @@ -793,6 +798,11 @@ impl ConversationsListing { if show_subject { other_subjects.insert(envelope.subject().to_string()); } + if account.backend_capabilities.supports_tags { + for &t in envelope.tags().iter() { + tags.insert(t); + } + } for addr in envelope.from().iter() { if from_address_set.contains(addr.address_spec_raw()) { continue; @@ -805,9 +815,11 @@ impl ConversationsListing { let strings = self.make_entry_string( &envelope, context, + &tags_lck, &from_address_list, &threads, &other_subjects, + &tags, thread_hash, ); drop(envelope); @@ -819,6 +831,7 @@ impl ConversationsListing { fn draw_rows(&self, grid: &mut CellBuffer, area: Area, context: &Context, top_idx: usize) { let account = &context.accounts[&self.cursor_pos.0]; let threads = account.collection.get_threads(self.cursor_pos.1); + clear_area(grid, area, self.color_cache.theme_default); let (mut upper_left, bottom_right) = area; for (idx, ((thread_hash, root_env_hash), strings)) in self.rows.entries.iter().enumerate().skip(top_idx) diff --git a/src/components/mail/listing/plain.rs b/src/components/mail/listing/plain.rs index 1d28f389..44eef4f1 100644 --- a/src/components/mail/listing/plain.rs +++ b/src/components/mail/listing/plain.rs @@ -968,6 +968,130 @@ impl PlainListing { _ => melib::datetime::timestamp_to_string(envelope.datetime(), None, false), } } + + fn update_line(&mut self, context: &Context, env_hash: EnvelopeHash) { + let account = &context.accounts[&self.cursor_pos.0]; + + if !account.contains_key(env_hash) { + /* The envelope has been renamed or removed, so wait for the appropriate event to + * arrive */ + return; + } + let envelope: EnvelopeRef = account.collection.get_env(env_hash); + let thread_hash = self.rows.env_to_thread[&env_hash]; + let idx = self.rows.env_order[&env_hash]; + let row_attr = row_attr!( + self.color_cache, + idx % 2 == 0, + !envelope.is_seen(), + false, + self.rows.selection[&env_hash] + ); + self.rows.row_attr_cache.insert(idx, row_attr); + + let strings = self.make_entry_string(&envelope, context); + drop(envelope); + let columns = &mut self.data_columns.columns; + let min_width = ( + columns[0].size().0, + columns[1].size().0, + columns[2].size().0, + columns[3].size().0, + ); + + clear_area(&mut columns[0], ((0, idx), (min_width.0, idx)), row_attr); + clear_area(&mut columns[1], ((0, idx), (min_width.1, idx)), row_attr); + clear_area(&mut columns[2], ((0, idx), (min_width.2, idx)), row_attr); + clear_area(&mut columns[3], ((0, idx), (min_width.3, idx)), row_attr); + + let (x, _) = write_string_to_grid( + &idx.to_string(), + &mut columns[0], + row_attr.fg, + row_attr.bg, + row_attr.attrs, + ((0, idx), (min_width.0, idx)), + None, + ); + for c in columns[0].row_iter(x..min_width.0, idx) { + columns[0][c].set_bg(row_attr.bg).set_attrs(row_attr.attrs); + } + let (x, _) = write_string_to_grid( + &strings.date, + &mut columns[1], + row_attr.fg, + row_attr.bg, + row_attr.attrs, + ((0, idx), (min_width.1, idx)), + None, + ); + for c in columns[1].row_iter(x..min_width.1, idx) { + columns[1][c].set_bg(row_attr.bg).set_attrs(row_attr.attrs); + } + let (x, _) = write_string_to_grid( + &strings.from, + &mut columns[2], + row_attr.fg, + row_attr.bg, + row_attr.attrs, + ((0, idx), (min_width.2, idx)), + None, + ); + for c in columns[2].row_iter(x..min_width.2, idx) { + columns[2][c].set_bg(row_attr.bg).set_attrs(row_attr.attrs); + } + let (x, _) = write_string_to_grid( + &strings.flag, + &mut columns[3], + row_attr.fg, + row_attr.bg, + row_attr.attrs, + ((0, idx), (min_width.3, idx)), + None, + ); + let (x, _) = write_string_to_grid( + &strings.subject, + &mut columns[3], + row_attr.fg, + row_attr.bg, + row_attr.attrs, + ((x, idx), (min_width.3, idx)), + None, + ); + let x = { + let mut x = x + 1; + for (t, &color) in strings.tags.split_whitespace().zip(strings.tags.1.iter()) { + let color = color.unwrap_or(self.color_cache.tag_default.bg); + let (_x, _) = write_string_to_grid( + t, + &mut columns[3], + self.color_cache.tag_default.fg, + color, + self.color_cache.tag_default.attrs, + ((x + 1, idx), (min_width.3, idx)), + None, + ); + for c in columns[3].row_iter(x..(x + 1), idx) { + columns[3][c].set_bg(color); + } + for c in columns[3].row_iter(_x..(_x + 1), idx) { + columns[3][c].set_bg(color).set_keep_bg(true); + } + for c in columns[3].row_iter((x + 1)..(_x + 1), idx) { + columns[3][c].set_keep_fg(true).set_keep_bg(true); + } + for c in columns[3].row_iter(x..(x + 1), idx) { + columns[3][c].set_keep_bg(true); + } + x = _x + 1; + } + x + }; + for c in columns[3].row_iter(x..min_width.3, idx) { + columns[3][c].set_bg(row_attr.bg).set_attrs(row_attr.attrs); + } + *self.rows.entries.get_mut(idx).unwrap() = ((thread_hash, env_hash), strings); + } } impl Component for PlainListing { @@ -1181,6 +1305,7 @@ impl Component for PlainListing { if !self.rows.row_updates.is_empty() { while let Some(env_hash) = self.rows.row_updates.pop() { + self.update_line(context, env_hash); let row: usize = self.rows.env_order[&env_hash]; let envelope: EnvelopeRef = context.accounts[&self.cursor_pos.0] .collection diff --git a/src/components/mail/listing/thread.rs b/src/components/mail/listing/thread.rs index 89b641c7..3d9eb8b0 100644 --- a/src/components/mail/listing/thread.rs +++ b/src/components/mail/listing/thread.rs @@ -122,10 +122,10 @@ pub struct ThreadListing { )>, data_columns: DataColumns<5>, - rows_drawn: SegmentTree, rows: RowsState<(ThreadHash, EnvelopeHash)>, /// If we must redraw on next redraw event dirty: bool, + force_draw: bool, /// If `self.view` is focused or not. focus: Focus, initialised: bool, @@ -408,12 +408,6 @@ impl MailListingTrait for ThreadListing { CellBuffer::new_with_context(min_width.4, self.rows.len(), None, context); self.data_columns.segment_tree[4] = row_widths.4.into(); - self.rows_drawn = SegmentTree::from( - std::iter::repeat(1) - .take(self.rows.len()) - .collect::>(), - ); - debug_assert!(self.rows_drawn.array.len() == self.rows.len()); self.draw_rows( context, 0, @@ -490,11 +484,6 @@ impl ListingTrait for ThreadListing { let page_no = (self.new_cursor_pos.2).wrapping_div(rows); let top_idx = page_no * rows; - self.draw_rows( - context, - top_idx, - cmp::min(self.length.saturating_sub(1), top_idx + rows - 1), - ); /* If cursor position has changed, remove the highlight from the previous position and * apply it in the new one. */ @@ -516,7 +505,9 @@ impl ListingTrait for ThreadListing { } 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; } @@ -526,16 +517,12 @@ impl ListingTrait for ThreadListing { self.new_cursor_pos.2 = self.length - 1; self.cursor_pos.2 = self.new_cursor_pos.2; } + self.draw_rows( + context, + top_idx, + cmp::min(self.length.saturating_sub(1), top_idx + rows - 1), + ); - _ = self - .data_columns - .recalc_widths((width!(area), height!(area)), top_idx); - clear_area(grid, area, self.color_cache.theme_default); - /* Page_no has changed, so draw new page */ - self.data_columns - .draw(grid, top_idx, self.cursor_pos.2, grid.bounds_iter(area)); - - /* Page_no has changed, so draw new page */ _ = self .data_columns .recalc_widths((width!(area), height!(area)), top_idx); @@ -650,11 +637,11 @@ impl ListingTrait for ThreadListing { /* If self.rows.row_updates is not empty and we exit a thread, the row_update events * will be performed but the list will not be drawn. So force a draw in any case. * */ - // self.force_draw = true; + self.force_draw = true; } Focus::Entry => { if let Some(env_hash) = self.get_env_under_cursor(self.cursor_pos.2) { - // self.force_draw = true; + self.force_draw = true; self.dirty = true; let coordinates = (self.cursor_pos.0, self.cursor_pos.1, env_hash); @@ -699,9 +686,9 @@ impl ThreadListing { subsort: (Default::default(), Default::default()), color_cache: ColorCache::default(), data_columns: DataColumns::default(), - rows_drawn: SegmentTree::default(), rows: RowsState::default(), dirty: true, + force_draw: true, focus: Focus::None, view: None, initialised: false, @@ -840,14 +827,7 @@ impl ThreadListing { return; } debug_assert!(end >= start); - if self.rows_drawn.get_max(start, end) == 0 { - //debug!("not drawing {}-{}", start, end); - return; - } //debug!("drawing {}-{}", start, end); - for i in start..=end { - self.rows_drawn.update(i, 0); - } let min_width = ( self.data_columns.columns[0].size().0, self.data_columns.columns[1].size().0, @@ -997,6 +977,50 @@ impl ThreadListing { } } } + + fn update_line(&mut self, context: &Context, env_hash: EnvelopeHash) { + let account = &context.accounts[&self.cursor_pos.0]; + + if !account.contains_key(env_hash) { + /* The envelope has been renamed or removed, so wait for the appropriate event to + * arrive */ + return; + } + let envelope: EnvelopeRef = account.collection.get_env(env_hash); + let thread_hash = self.rows.env_to_thread[&env_hash]; + let idx = self.rows.env_order[&env_hash]; + let row_attr = row_attr!( + self.color_cache, + idx % 2 == 0, + !envelope.is_seen(), + false, + self.rows.selection[&env_hash] + ); + self.rows.row_attr_cache.insert(idx, row_attr); + + let mut strings = self.make_entry_string(&envelope, context); + drop(envelope); + std::mem::swap( + &mut self.rows.entries.get_mut(idx).unwrap().1.subject, + &mut strings.subject, + ); + let columns = &mut self.data_columns.columns; + let min_width = ( + columns[0].size().0, + columns[1].size().0, + columns[2].size().0, + columns[3].size().0, + columns[4].size().0, + ); + + clear_area(&mut columns[0], ((0, idx), (min_width.0, idx)), row_attr); + clear_area(&mut columns[1], ((0, idx), (min_width.1, idx)), row_attr); + clear_area(&mut columns[2], ((0, idx), (min_width.2, idx)), row_attr); + clear_area(&mut columns[3], ((0, idx), (min_width.3, idx)), row_attr); + clear_area(&mut columns[4], ((0, idx), (min_width.4, idx)), row_attr); + + *self.rows.entries.get_mut(idx).unwrap() = ((thread_hash, env_hash), strings); + } } impl Component for ThreadListing { @@ -1161,7 +1185,7 @@ impl Component for ThreadListing { } } } - //self.force_draw = true; + self.force_draw = true; } if !self.rows.row_updates.is_empty() { @@ -1169,30 +1193,15 @@ impl Component for ThreadListing { let top_idx = page_no * rows; while let Some(env_hash) = self.rows.row_updates.pop() { + self.update_line(context, env_hash); let row: usize = self.rows.env_order[&env_hash]; - 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 */ + self.draw_list(grid, area, context); + self.force_draw = false; } }