ui: add columns in PlainListing
parent
d3ce424361
commit
831ec97d64
|
@ -21,7 +21,48 @@
|
|||
|
||||
use super::*;
|
||||
|
||||
use std::cmp;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
const MAX_COLS: usize = 500;
|
||||
macro_rules! address_list {
|
||||
(($name:expr) as comma_sep_list) => {{
|
||||
let mut ret: String =
|
||||
$name
|
||||
.into_iter()
|
||||
.fold(String::new(), |mut s: String, n: &Address| {
|
||||
s.extend(n.to_string().chars());
|
||||
s.push_str(", ");
|
||||
s
|
||||
});
|
||||
ret.pop();
|
||||
ret.pop();
|
||||
ret
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! column_str {
|
||||
(
|
||||
struct $name:ident(String)) => {
|
||||
pub struct $name(String);
|
||||
|
||||
impl Deref for $name {
|
||||
type Target = String;
|
||||
fn deref(&self) -> &String {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
impl DerefMut for $name {
|
||||
fn deref_mut(&mut self) -> &mut String {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
column_str!(struct IndexNoString(String));
|
||||
column_str!(struct DateString(String));
|
||||
column_str!(struct FromString(String));
|
||||
column_str!(struct SubjectString(String));
|
||||
|
||||
/// A list of all mail (`Envelope`s) in a `Mailbox`. On `\n` it opens the `Envelope` content in a
|
||||
/// `MailView`.
|
||||
|
@ -69,12 +110,19 @@ impl fmt::Display for PlainListing {
|
|||
impl PlainListing {
|
||||
/// Helper function to format entry strings for PlainListing */
|
||||
/* TODO: Make this configurable */
|
||||
fn make_entry_string(e: &Envelope, idx: usize) -> String {
|
||||
format!(
|
||||
"{} {} {}",
|
||||
idx,
|
||||
&e.datetime().format("%Y-%m-%d %H:%M:%S").to_string(),
|
||||
e.subject()
|
||||
fn make_entry_string(
|
||||
e: &Envelope,
|
||||
idx: usize,
|
||||
) -> (IndexNoString, FromString, DateString, SubjectString) {
|
||||
(
|
||||
IndexNoString(idx.to_string()),
|
||||
FromString(address_list!((e.from()) as comma_sep_list)),
|
||||
DateString(PlainListing::format_date(e)),
|
||||
SubjectString(format!(
|
||||
"{}{}",
|
||||
e.subject(),
|
||||
if e.has_attachments() { " 📎" } else { "" },
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -147,7 +195,56 @@ impl PlainListing {
|
|||
);
|
||||
return;
|
||||
}
|
||||
self.local_collection = account.collection.keys().cloned().collect();
|
||||
let sort = self.sort;
|
||||
self.local_collection.sort_by(|a, b| match sort {
|
||||
(SortField::Date, SortOrder::Desc) => {
|
||||
let ma = &account.get_env(a);
|
||||
let mb = &account.get_env(b);
|
||||
mb.date().cmp(&ma.date())
|
||||
}
|
||||
(SortField::Date, SortOrder::Asc) => {
|
||||
let ma = &account.get_env(a);
|
||||
let mb = &account.get_env(b);
|
||||
ma.date().cmp(&mb.date())
|
||||
}
|
||||
(SortField::Subject, SortOrder::Desc) => {
|
||||
let ma = &account.get_env(a);
|
||||
let mb = &account.get_env(b);
|
||||
ma.subject().cmp(&mb.subject())
|
||||
}
|
||||
(SortField::Subject, SortOrder::Asc) => {
|
||||
let ma = &account.get_env(a);
|
||||
let mb = &account.get_env(b);
|
||||
mb.subject().cmp(&ma.subject())
|
||||
}
|
||||
});
|
||||
|
||||
let mut rows = Vec::with_capacity(1024);
|
||||
let mut min_width = (0, 0, 0);
|
||||
let widths: (usize, usize, usize);
|
||||
|
||||
for idx in 0..self.local_collection.len() {
|
||||
let envelope: &Envelope = &account.get_env(&self.local_collection[idx]);
|
||||
|
||||
let strings = PlainListing::make_entry_string(envelope, idx);
|
||||
min_width.0 = cmp::max(min_width.0, strings.0.len()); /* index */
|
||||
min_width.1 = cmp::max(min_width.1, strings.2.split_graphemes().len()); /* date */
|
||||
min_width.2 = cmp::max(min_width.2, strings.3.split_graphemes().len()); /* subject */
|
||||
rows.push(strings);
|
||||
}
|
||||
let column_sep: usize = if MAX_COLS >= min_width.0 + min_width.1 + min_width.2 {
|
||||
widths = min_width;
|
||||
2
|
||||
} else {
|
||||
let width = MAX_COLS - 3 - min_width.0;
|
||||
widths = (
|
||||
min_width.0,
|
||||
cmp::min(min_width.1, width / 3),
|
||||
cmp::min(min_width.2, width / 3),
|
||||
);
|
||||
1
|
||||
};
|
||||
// Populate `CellBuffer` with every entry.
|
||||
let mut idx = 0;
|
||||
for y in 0..=self.length {
|
||||
|
@ -157,30 +254,6 @@ impl PlainListing {
|
|||
break;
|
||||
}
|
||||
/* Write an entire line for each envelope entry. */
|
||||
self.local_collection = account.collection.keys().cloned().collect();
|
||||
let sort = self.sort;
|
||||
self.local_collection.sort_by(|a, b| match sort {
|
||||
(SortField::Date, SortOrder::Desc) => {
|
||||
let ma = &account.get_env(a);
|
||||
let mb = &account.get_env(b);
|
||||
mb.date().cmp(&ma.date())
|
||||
}
|
||||
(SortField::Date, SortOrder::Asc) => {
|
||||
let ma = &account.get_env(a);
|
||||
let mb = &account.get_env(b);
|
||||
ma.date().cmp(&mb.date())
|
||||
}
|
||||
(SortField::Subject, SortOrder::Desc) => {
|
||||
let ma = &account.get_env(a);
|
||||
let mb = &account.get_env(b);
|
||||
ma.subject().cmp(&mb.subject())
|
||||
}
|
||||
(SortField::Subject, SortOrder::Asc) => {
|
||||
let ma = &account.get_env(a);
|
||||
let mb = &account.get_env(b);
|
||||
mb.subject().cmp(&ma.subject())
|
||||
}
|
||||
});
|
||||
let envelope: &Envelope = &account.get_env(&self.local_collection[idx]);
|
||||
|
||||
let fg_color = if !envelope.is_seen() {
|
||||
|
@ -195,12 +268,48 @@ impl PlainListing {
|
|||
} else {
|
||||
Color::Default
|
||||
};
|
||||
let (x, y) = write_string_to_grid(
|
||||
&PlainListing::make_entry_string(envelope, idx),
|
||||
let (x, _) = write_string_to_grid(
|
||||
&rows[idx].0,
|
||||
&mut self.content,
|
||||
fg_color,
|
||||
bg_color,
|
||||
((0, y), (MAX_COLS - 1, y)),
|
||||
((0, idx), (widths.0, idx)),
|
||||
false,
|
||||
);
|
||||
for x in x..=widths.0 + column_sep {
|
||||
self.content[(x, idx)].set_bg(bg_color);
|
||||
}
|
||||
let mut _x = widths.0 + column_sep;
|
||||
let (mut x, _) = write_string_to_grid(
|
||||
&rows[idx].2,
|
||||
&mut self.content,
|
||||
fg_color,
|
||||
bg_color,
|
||||
((_x, idx), (widths.1 + _x, idx)),
|
||||
false,
|
||||
);
|
||||
_x += widths.1 + column_sep + 1;
|
||||
for x in x.._x {
|
||||
self.content[(x, idx)].set_bg(bg_color);
|
||||
}
|
||||
let (x, _) = write_string_to_grid(
|
||||
&rows[idx].1,
|
||||
&mut self.content,
|
||||
fg_color,
|
||||
bg_color,
|
||||
((_x, idx), (widths.1 + _x, idx)),
|
||||
false,
|
||||
);
|
||||
_x += widths.1 + column_sep + 2;
|
||||
for x in x.._x {
|
||||
self.content[(x, idx)].set_bg(bg_color);
|
||||
}
|
||||
let (x, _) = write_string_to_grid(
|
||||
&rows[idx].3,
|
||||
&mut self.content,
|
||||
fg_color,
|
||||
bg_color,
|
||||
((_x, idx), (widths.2 + _x, idx)),
|
||||
false,
|
||||
);
|
||||
|
||||
|
@ -328,6 +437,20 @@ impl PlainListing {
|
|||
);
|
||||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
fn format_date(envelope: &Envelope) -> String {
|
||||
let d = std::time::UNIX_EPOCH + std::time::Duration::from_secs(envelope.date());
|
||||
let now: std::time::Duration = std::time::SystemTime::now()
|
||||
.duration_since(d)
|
||||
.unwrap_or_else(|_| std::time::Duration::new(std::u64::MAX, 0));
|
||||
match now.as_secs() {
|
||||
n if n < 10 * 60 * 60 => format!("{} hours ago{}", n / (60 * 60), " ".repeat(8)),
|
||||
n if n < 24 * 60 * 60 => format!("{} hours ago{}", n / (60 * 60), " ".repeat(7)),
|
||||
n if n < 4 * 24 * 60 * 60 => {
|
||||
format!("{} days ago{}", n / (24 * 60 * 60), " ".repeat(9))
|
||||
}
|
||||
_ => envelope.datetime().format("%Y-%m-%d %H:%M:%S").to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for PlainListing {
|
||||
|
|
Loading…
Reference in New Issue