parent
5634f95553
commit
4f45b10974
|
@ -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<Vec<u8>> =
|
||||
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<TagHash, String>,
|
||||
from: &[Address],
|
||||
threads: &Threads,
|
||||
other_subjects: &IndexSet<String>,
|
||||
tags: &IndexSet<TagHash>,
|
||||
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<Vec<u8>> =
|
||||
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 = (
|
||||
|
|
|
@ -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<Vec<u8>> =
|
||||
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<TagHash, String>,
|
||||
from: &[Address],
|
||||
threads: &Threads,
|
||||
other_subjects: &IndexSet<String>,
|
||||
tags: &IndexSet<TagHash>,
|
||||
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<Vec<u8>> =
|
||||
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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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::<SmallVec<_>>(),
|
||||
);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue