summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorManos Pitsidianakis <el13635@mail.ntua.gr>2019-06-21 12:02:11 +0300
committerManos Pitsidianakis <el13635@mail.ntua.gr>2019-06-21 12:02:11 +0300
commitbb292486f45d780ec13f4308f9d16c38225173d1 (patch)
treedc07ef42116128adbbe32cc23ea6b225ff4f5605
parentc34a55dcac480092ba44de5ee0f3b482a1b3cf79 (diff)
ui: expand Listing trait with draw_list and highlight_line
In the course of making the Listing trait a generic way for any kind of listing (eg. NNTP, RSS, et all)
-rw-r--r--ui/src/components/mail/listing.rs24
-rw-r--r--ui/src/components/mail/listing/compact.rs886
-rw-r--r--ui/src/components/mail/listing/plain.rs200
-rw-r--r--ui/src/components/mail/listing/thread.rs214
4 files changed, 622 insertions, 702 deletions
diff --git a/ui/src/components/mail/listing.rs b/ui/src/components/mail/listing.rs
index bdd0b1de..d8e40c19 100644
--- a/ui/src/components/mail/listing.rs
+++ b/ui/src/components/mail/listing.rs
@@ -30,6 +30,12 @@ pub use self::thread::*;
mod plain;
pub use self::plain::*;
+#[derive(Debug, Default, Clone)]
+pub(in crate::listing) struct DataColumns {
+ columns: [CellBuffer; 12],
+ widths: [usize; 12], // widths of columns calculated in first draw and after size changes
+}
+
#[derive(Debug)]
struct AccountMenuEntry {
name: String,
@@ -40,6 +46,8 @@ struct AccountMenuEntry {
trait ListingTrait {
fn coordinates(&self) -> (usize, usize, Option<EnvelopeHash>);
fn set_coordinates(&mut self, _: (usize, usize, Option<EnvelopeHash>));
+ fn draw_list(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context);
+ fn highlight_line(&mut self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context);
}
#[derive(Debug)]
@@ -65,6 +73,20 @@ impl ListingTrait for ListingComponent {
Threaded(ref mut l) => l.set_coordinates(c),
}
}
+ fn draw_list(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
+ match self {
+ Compact(ref mut l) => l.draw_list(grid, area, context),
+ Plain(ref mut l) => l.draw_list(grid, area, context),
+ Threaded(ref mut l) => l.draw_list(grid, area, context),
+ }
+ }
+ fn highlight_line(&mut self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) {
+ match self {
+ Compact(ref mut l) => l.highlight_line(grid, area, idx, context),
+ Plain(ref mut l) => l.highlight_line(grid, area, idx, context),
+ Threaded(ref mut l) => l.highlight_line(grid, area, idx, context),
+ }
+ }
}
#[derive(Debug)]
@@ -123,11 +145,11 @@ impl Component for Listing {
grid[(mid, i)].set_bg(Color::Default);
}
}
- self.dirty = false;
context
.dirty_areas
.push_back(((mid, get_y(upper_left)), (mid, get_y(bottom_right))));
}
+ self.dirty = false;
if right_component_width == total_cols {
match self.component {
Compact(ref mut l) => l.draw(grid, area, context),
diff --git a/ui/src/components/mail/listing/compact.rs b/ui/src/components/mail/listing/compact.rs
index f29e8ebf..eb3b79d6 100644
--- a/ui/src/components/mail/listing/compact.rs
+++ b/ui/src/components/mail/listing/compact.rs
@@ -28,30 +28,6 @@ use std::ops::{Deref, DerefMut};
const MAX_COLS: usize = 500;
-#[derive(Debug)]
-struct MailboxView {
- /// (x, y, z): x is accounts, y is folders, z is index inside a folder.
- cursor_pos: (usize, usize, usize),
- new_cursor_pos: (usize, usize, usize),
- length: usize,
- sort: (SortField, SortOrder),
- subsort: (SortField, SortOrder),
- order: FnvHashMap<EnvelopeHash, usize>,
- /// Cache current view.
- content: CellBuffer,
- columns: [CellBuffer; 5],
- widths: [usize; 5], // widths of columns calculated in first draw and after size changes
- /// If we must redraw on next redraw event
- dirty: bool,
- /// If `self.view` exists or not.
- unfocused: bool,
- view: ThreadView,
- row_updates: StackVec<EnvelopeHash>,
-
- movement: Option<PageMovement>,
- id: ComponentId,
-}
-
macro_rules! address_list {
(($name:expr) as comma_sep_list) => {{
let mut ret: String =
@@ -93,16 +69,359 @@ column_str!(struct FromString(String));
column_str!(struct SubjectString(String));
column_str!(struct FlagString(String));
-impl fmt::Display for MailboxView {
+/// A list of all mail (`Envelope`s) in a `Mailbox`. On `\n` it opens the `Envelope` content in a
+/// `ThreadView`.
+#[derive(Debug)]
+pub struct CompactListing {
+ /// (x, y, z): x is accounts, y is folders, z is index inside a folder.
+ cursor_pos: (usize, usize, usize),
+ new_cursor_pos: (usize, usize, usize),
+ length: usize,
+ sort: (SortField, SortOrder),
+ subsort: (SortField, SortOrder),
+ order: FnvHashMap<EnvelopeHash, usize>,
+ /// Cache current view.
+ data_columns: DataColumns,
+ /// If we must redraw on next redraw event
+ dirty: bool,
+ /// If `self.view` exists or not.
+ unfocused: bool,
+ view: ThreadView,
+ row_updates: StackVec<EnvelopeHash>,
+
+ movement: Option<PageMovement>,
+ id: ComponentId,
+}
+
+impl ListingTrait for CompactListing {
+ fn coordinates(&self) -> (usize, usize, Option<EnvelopeHash>) {
+ (self.cursor_pos.0, self.cursor_pos.1, None)
+ }
+ fn set_coordinates(&mut self, coordinates: (usize, usize, Option<EnvelopeHash>)) {
+ self.new_cursor_pos = (coordinates.0, coordinates.1, 0);
+ self.unfocused = false;
+ }
+ fn highlight_line(&mut self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) {
+ let is_seen = {
+ if self.length == 0 {
+ return;
+ }
+ let account = &context.accounts[self.cursor_pos.0];
+ let mailbox = account[self.cursor_pos.1].as_ref().unwrap();
+ let threads = &account.collection.threads[&mailbox.folder.hash()];
+ let thread_node = threads.root_set(idx);
+ let thread_node = &threads.thread_nodes()[&thread_node];
+ let i = if let Some(i) = thread_node.message() {
+ i
+ } else {
+ let mut iter_ptr = thread_node.children()[0];
+ while threads.thread_nodes()[&iter_ptr].message().is_none() {
+ iter_ptr = threads.thread_nodes()[&iter_ptr].children()[0];
+ }
+ threads.thread_nodes()[&iter_ptr].message().unwrap()
+ };
+
+ let root_envelope: &Envelope = &account.get_env(&i);
+ root_envelope.is_seen()
+ };
+
+ let fg_color = if !is_seen {
+ Color::Byte(0)
+ } else {
+ Color::Default
+ };
+ let bg_color = if self.cursor_pos.2 == idx {
+ Color::Byte(246)
+ } else if !is_seen {
+ Color::Byte(251)
+ } else if idx % 2 == 0 {
+ Color::Byte(236)
+ } else {
+ Color::Default
+ };
+ if !grid.is_empty() {
+ change_colors(grid, area, fg_color, bg_color);
+ return;
+ }
+
+ let (upper_left, bottom_right) = area;
+ let (mut x, _y) = upper_left;
+ for i in 0..self.data_columns.columns.len() {
+ let (width, height) = self.data_columns.columns[i].size();
+ if self.data_columns.widths[i] == 0 {
+ continue;
+ }
+ copy_area(
+ grid,
+ &self.data_columns.columns[i],
+ (
+ set_x(upper_left, x),
+ set_x(
+ bottom_right,
+ std::cmp::min(get_x(bottom_right), x + (self.data_columns.widths[i])),
+ ),
+ ),
+ ((0, idx), (width.saturating_sub(1), height - 1)),
+ );
+ if i != self.data_columns.columns.len() - 1 {
+ change_colors(
+ grid,
+ (
+ set_x(
+ upper_left,
+ x + self.data_columns.widths[i].saturating_sub(1),
+ ),
+ set_x(bottom_right, x + self.data_columns.widths[i] + 1),
+ ),
+ fg_color,
+ bg_color,
+ );
+ } else {
+ change_colors(
+ grid,
+ (
+ set_x(
+ upper_left,
+ std::cmp::min(get_x(bottom_right), x + (self.data_columns.widths[i])),
+ ),
+ bottom_right,
+ ),
+ fg_color,
+ bg_color,
+ );
+ }
+ x += self.data_columns.widths[i] + 2; // + SEPARATOR
+ if x > get_x(bottom_right) {
+ break;
+ }
+ }
+ }
+ /// Draw the list of `Envelope`s.
+ fn draw_list(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
+ if self.cursor_pos.1 != self.new_cursor_pos.1 || self.cursor_pos.0 != self.new_cursor_pos.0
+ {
+ self.refresh_mailbox(context);
+ }
+ let upper_left = upper_left!(area);
+ let bottom_right = bottom_right!(area);
+ if self.length == 0 {
+ clear_area(grid, area);
+ copy_area(
+ grid,
+ &self.data_columns.columns[0],
+ area,
+ ((0, 0), (MAX_COLS - 1, self.length)),
+ );
+ context.dirty_areas.push_back(area);
+ return;
+ }
+ let rows = get_y(bottom_right) - get_y(upper_left) + 1;
+
+ if let Some(mvm) = self.movement.take() {
+ match mvm {
+ PageMovement::PageUp => {
+ self.new_cursor_pos.2 = self.new_cursor_pos.2.saturating_sub(rows);
+ }
+ PageMovement::PageDown => {
+ if self.new_cursor_pos.2 + rows + 1 < self.length {
+ self.new_cursor_pos.2 += rows;
+ } else {
+ self.new_cursor_pos.2 = (self.length / rows) * rows;
+ }
+ }
+ PageMovement::Home => {
+ self.new_cursor_pos.2 = 0;
+ }
+ PageMovement::End => {
+ self.new_cursor_pos.2 = (self.length / rows) * rows;
+ }
+ }
+ }
+
+ let prev_page_no = (self.cursor_pos.2).wrapping_div(rows);
+ let page_no = (self.new_cursor_pos.2).wrapping_div(rows);
+
+ let top_idx = page_no * rows;
+
+ /* If cursor position has changed, remove the highlight from the previous position and
+ * apply it in the new one. */
+ if self.cursor_pos.2 != self.new_cursor_pos.2 && prev_page_no == page_no {
+ let old_cursor_pos = self.cursor_pos;
+ self.cursor_pos = self.new_cursor_pos;
+ for idx in &[old_cursor_pos.2, self.new_cursor_pos.2] {
+ if *idx >= self.length {
+ continue; //bounds check
+ }
+ let new_area = (
+ set_y(upper_left, get_y(upper_left) + (*idx % rows)),
+ set_y(bottom_right, get_y(upper_left) + (*idx % rows)),
+ );
+ self.highlight_line(grid, new_area, *idx, context);
+ context.dirty_areas.push_back(new_area);
+ }
+ return;
+ } else if self.cursor_pos != self.new_cursor_pos {
+ self.cursor_pos = self.new_cursor_pos;
+ }
+ if self.new_cursor_pos.2 >= self.length {
+ self.new_cursor_pos.2 = self.length - 1;
+ self.cursor_pos.2 = self.new_cursor_pos.2;
+ }
+
+ let width = width!(area);
+ self.data_columns.widths = Default::default();
+ self.data_columns.widths[0] = self.data_columns.columns[0].size().0;
+ self.data_columns.widths[1] = self.data_columns.columns[1].size().0; /* date*/
+ self.data_columns.widths[2] = self.data_columns.columns[2].size().0; /* from */
+ self.data_columns.widths[3] = self.data_columns.columns[3].size().0; /* flags */
+ self.data_columns.widths[4] = self.data_columns.columns[4].size().0; /* subject */
+
+ let min_col_width = std::cmp::min(
+ 15,
+ std::cmp::min(self.data_columns.widths[4], self.data_columns.widths[2]),
+ );
+ if self.data_columns.widths[0] + self.data_columns.widths[1] + 3 * min_col_width + 8 > width
+ {
+ let remainder = width
+ .saturating_sub(self.data_columns.widths[0])
+ .saturating_sub(self.data_columns.widths[1])
+ - 4;
+ self.data_columns.widths[2] = remainder / 6;
+ self.data_columns.widths[4] = (2 * remainder) / 3 - self.data_columns.widths[3];
+ } else {
+ let remainder = width
+ .saturating_sub(self.data_columns.widths[0])
+ .saturating_sub(self.data_columns.widths[1])
+ .saturating_sub(8);
+ if min_col_width + self.data_columns.widths[4] > remainder {
+ self.data_columns.widths[4] =
+ remainder - min_col_width - self.data_columns.widths[3];
+ self.data_columns.widths[2] = min_col_width;
+ }
+ }
+ clear_area(grid, area);
+ /* Page_no has changed, so draw new page */
+ let mut x = get_x(upper_left);
+ let mut flag_x = 0;
+ for i in 0..self.data_columns.columns.len() {
+ let column_width = self.data_columns.columns[i].size().0;
+ if i == 3 {
+ flag_x = x;
+ }
+ if self.data_columns.widths[i] == 0 {
+ continue;
+ }
+ copy_area(
+ grid,
+ &self.data_columns.columns[i],
+ (
+ set_x(upper_left, x),
+ set_x(
+ bottom_right,
+ std::cmp::min(get_x(bottom_right), x + (self.data_columns.widths[i])),
+ ),
+ ),
+ (
+ (0, top_idx),
+ (column_width.saturating_sub(1), self.length - 1),
+ ),
+ );
+ x += self.data_columns.widths[i] + 2; // + SEPARATOR
+ if x > get_x(bottom_right) {
+ break;
+ }
+ }
+ for r in 0..cmp::min(self.length - top_idx, rows) {
+ let (fg_color, bg_color) = {
+ let c = &self.data_columns.columns[0][(0, r + top_idx)];
+ (c.fg(), c.bg())
+ };
+ change_colors(
+ grid,
+ (
+ pos_inc(upper_left, (0, r)),
+ (flag_x - 1, get_y(upper_left) + r),
+ ),
+ fg_color,
+ bg_color,
+ );
+ for x in flag_x..(flag_x + 2 + self.data_columns.widths[3]) {
+ grid[(x, get_y(upper_left) + r)].set_bg(bg_color);
+ }
+ change_colors(
+ grid,
+ (
+ (
+ flag_x + 2 + self.data_columns.widths[3],
+ get_y(upper_left) + r,
+ ),
+ (get_x(bottom_right), get_y(upper_left) + r),
+ ),
+ fg_color,
+ bg_color,
+ );
+ }
+ let temp_copy_because_of_nll = self.cursor_pos.2; // FIXME
+ self.highlight_line(
+ grid,
+ (
+ set_y(
+ upper_left,
+ get_y(upper_left) + (temp_copy_because_of_nll % rows),
+ ),
+ set_y(
+ bottom_right,
+ get_y(upper_left) + (temp_copy_because_of_nll % rows),
+ ),
+ ),
+ temp_copy_because_of_nll,
+ context,
+ );
+ if top_idx + rows > self.length {
+ clear_area(
+ grid,
+ (
+ pos_inc(upper_left, (0, self.length - top_idx)),
+ bottom_right,
+ ),
+ );
+ }
+ context.dirty_areas.push_back(area);
+ }
+}
+
+impl fmt::Display for CompactListing {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{}", MailboxView::DESCRIPTION)
+ write!(f, "mail")
+ }
+}
+
+impl Default for CompactListing {
+ fn default() -> Self {
+ CompactListing::new()
}
}
-impl MailboxView {
- const DESCRIPTION: &'static str = "";
- /// Helper function to format entry strings for CompactListing */
- /* TODO: Make this configurable */
+impl CompactListing {
+ const DESCRIPTION: &'static str = "compact listing";
+ fn new() -> Self {
+ CompactListing {
+ cursor_pos: (0, 1, 0),
+ new_cursor_pos: (0, 0, 0),
+ length: 0,
+ sort: (Default::default(), Default::default()),
+ subsort: (SortField::Date, SortOrder::Desc),
+ order: FnvHashMap::default(),
+ row_updates: StackVec::new(),
+ data_columns: DataColumns::default(),
+ dirty: true,
+ unfocused: false,
+ view: ThreadView::default(),
+
+ movement: None,
+ id: ComponentId::new_v4(),
+ }
+ }
fn make_entry_string(
e: &Envelope,
len: usize,
@@ -118,7 +437,7 @@ impl MailboxView {
if len > 0 {
(
IndexNoString(idx.to_string()),
- DateString(MailboxView::format_date(e)),
+ DateString(CompactListing::format_date(e)),
FromString(address_list!((e.from()) as comma_sep_list)),
FlagString(format!(
"{}{}",
@@ -130,7 +449,7 @@ impl MailboxView {
} else {
(
IndexNoString(idx.to_string()),
- DateString(MailboxView::format_date(e)),
+ DateString(CompactListing::format_date(e)),
FromString(address_list!((e.from()) as comma_sep_list)),
FlagString(format!(
"{}{}",
@@ -142,34 +461,7 @@ impl MailboxView {
}
}
- fn new() -> Self {
- let content = CellBuffer::new(0, 0, Cell::with_char(' '));
- MailboxView {
- cursor_pos: (0, 1, 0),
- new_cursor_pos: (0, 0, 0),
- length: 0,
- sort: (Default::default(), Default::default()),
- subsort: (SortField::Date, SortOrder::Desc),
- order: FnvHashMap::default(),
- row_updates: StackVec::new(),
- columns: [
- CellBuffer::default(),
- CellBuffer::default(),
- CellBuffer::default(),
- CellBuffer::default(),
- CellBuffer::default(),
- ],
- widths: [0; 5],
- content,
- dirty: true,
- unfocused: false,
- view: ThreadView::default(),
-
- movement: None,
- id: ComponentId::new_v4(),
- }
- }
- /// Fill the `self.content` `CellBuffer` with the contents of the account folder the user has
+ /// Fill the `self.data_columns` `CellBuffers` with the contents of the account folder the user has
/// chosen.
fn refresh_mailbox(&mut self, context: &mut Context) {
self.dirty = true;
@@ -201,11 +493,12 @@ impl MailboxView {
match context.accounts[self.cursor_pos.0].status(folder_hash) {
Ok(_) => {}
Err(_) => {
- self.content = CellBuffer::new(MAX_COLS, 1, Cell::with_char(' '));
+ self.data_columns.columns[0] =
+ CellBuffer::new("Loading.".len(), 1, Cell::with_char(' '));
self.length = 0;
write_string_to_grid(
"Loading.",
- &mut self.content,
+ &mut self.data_columns.columns[0],
Color::Default,
Color::Default,
((0, 0), (MAX_COLS - 1, 0)),
@@ -255,7 +548,7 @@ impl MailboxView {
}
let root_envelope: &Envelope = &context.accounts[self.cursor_pos.0].get_env(&i);
- let strings = MailboxView::make_entry_string(
+ let strings = CompactListing::make_entry_string(
root_envelope,
thread_node.len(),
idx,
@@ -271,15 +564,20 @@ impl MailboxView {
}
/* index column */
- self.columns[0] = CellBuffer::new(min_width.0, rows.len(), Cell::with_char(' '));
+ self.data_columns.columns[0] =
+ CellBuffer::new(min_width.0, rows.len(), Cell::with_char(' '));
/* date column */
- self.columns[1] = CellBuffer::new(min_width.1, rows.len(), Cell::with_char(' '));
+ self.data_columns.columns[1] =
+ CellBuffer::new(min_width.1, rows.len(), Cell::with_char(' '));
/* from column */
- self.columns[2] = CellBuffer::new(min_width.2, rows.len(), Cell::with_char(' '));
+ self.data_columns.columns[2] =
+ CellBuffer::new(min_width.2, rows.len(), Cell::with_char(' '));
/* flags column */
- self.columns[3] = CellBuffer::new(min_width.3, rows.len(), Cell::with_char(' '));
+ self.data_columns.columns[3] =
+ CellBuffer::new(min_width.3, rows.len(), Cell::with_char(' '));
/* subject column */
- self.columns[4] = CellBuffer::new(min_width.4, rows.len(), Cell::with_char(' '));
+ self.data_columns.columns[4] =
+ CellBuffer::new(min_width.4, rows.len(), Cell::with_char(' '));
for ((idx, root_idx), strings) in threads.root_iter().enumerate().zip(rows) {
let thread_node = &threads.thread_nodes()[&root_idx];
@@ -317,58 +615,58 @@ impl MailboxView {
};
let (x, _) = write_string_to_grid(
&strings.0,
- &mut self.columns[0],
+ &mut self.data_columns.columns[0],
fg_color,
bg_color,
((0, idx), (min_width.0, idx)),
false,
);
for x in x..min_width.0 {
- self.columns[0][(x, idx)].set_bg(bg_color);
+ self.data_columns.columns[0][(x, idx)].set_bg(bg_color);
}
let (x, _) = write_string_to_grid(
&strings.1,
- &mut self.columns[1],
+ &mut self.data_columns.columns[1],
fg_color,
bg_color,
((0, idx), (min_width.1, idx)),
false,
);
for x in x..min_width.1 {
- self.columns[1][(x, idx)].set_bg(bg_color);
+ self.data_columns.columns[1][(x, idx)].set_bg(bg_color);
}
let (x, _) = write_string_to_grid(
&strings.2,
- &mut self.columns[2],
+ &mut self.data_columns.columns[2],
fg_color,
bg_color,
((0, idx), (min_width.2, idx)),
false,
);
for x in x..min_width.2 {
- self.columns[2][(x, idx)].set_bg(bg_color);
+ self.data_columns.columns[2][(x, idx)].set_bg(bg_color);
}
let (x, _) = write_string_to_grid(
&strings.3,
- &mut self.columns[3],
+ &mut self.data_columns.columns[3],
fg_color,
bg_color,
((0, idx), (min_width.3, idx)),
false,
);
for x in x..min_width.3 {
- self.columns[3][(x, idx)].set_bg(bg_color);
+ self.data_columns.columns[3][(x, idx)].set_bg(bg_color);
}
let (x, _) = write_string_to_grid(
&strings.4,
- &mut self.columns[4],
+ &mut self.data_columns.columns[4],
fg_color,
bg_color,
((0, idx), (min_width.4, idx)),
false,
);
for x in x..min_width.4 {
- self.columns[4][(x, idx)].set_bg(bg_color);
+ self.data_columns.columns[4][(x, idx)].set_bg(bg_color);
}
match (
threads.is_snoozed(root_idx),
@@ -377,25 +675,27 @@ impl MailboxView {
.has_attachments(),
) {
(true, true) => {
- self.columns[3][(0, idx)].set_fg(Color::Red);
- self.columns[3][(1, idx)].set_fg(Color::Byte(103));
+ self.data_columns.columns[3][(0, idx)].set_fg(Color::Red);
+ self.data_columns.columns[3][(1, idx)].set_fg(Color::Byte(103));
}
(true, false) => {
- self.columns[3][(0, idx)].set_fg(Color::Red);
+ self.data_columns.columns[3][(0, idx)].set_fg(Color::Red);
}
(false, true) => {
- self.columns[3][(0, idx)].set_fg(Color::Byte(103));
+ self.data_columns.columns[3][(0, idx)].set_fg(Color::Byte(103));
}
(false, false) => {}
}
self.order.insert(i, idx);
}
- self.content = CellBuffer::new(MAX_COLS, self.length + 1, Cell::with_char(' '));
+ let message = format!("Folder `{}` is empty.", mailbox.folder.name());
if self.length == 0 {
+ self.data_columns.columns[0] =
+ CellBuffer::new(message.len(), self.length + 1, Cell::with_char(' '));
write_string_to_grid(
- &format!("Folder `{}` is empty.", mailbox.folder.name()),
- &mut self.content,
+ &message,
+ &mut self.data_columns.columns[0],
Color::Default,
Color::Default,
((0, 0), (MAX_COLS - 1, 0)),
@@ -405,295 +705,6 @@ impl MailboxView {
}
}
- fn highlight_line(
- &mut self,
- grid: Option<&mut CellBuffer>,
- area: Area,
- idx: usize,
- context: &Context,
- ) {
- let is_seen = {
- if self.length == 0 {
- return;
- }
- let account = &context.accounts[self.cursor_pos.0];
- let mailbox = account[self.cursor_pos.1].as_ref().unwrap();
- let threads = &account.collection.threads[&mailbox.folder.hash()];
- let thread_node = threads.root_set(idx);
- let thread_node = &threads.thread_nodes()[&thread_node];
- let i = if let Some(i) = thread_node.message() {
- i
- } else {
- let mut iter_ptr = thread_node.children()[0];
- while threads.thread_nodes()[&iter_ptr].message().is_none() {
- iter_ptr = threads.thread_nodes()[&iter_ptr].children()[0];
- }
- threads.thread_nodes()[&iter_ptr].message().unwrap()
- };
-
- let root_envelope: &Envelope = &account.get_env(&i);
- root_envelope.is_seen()
- };
-
- let fg_color = if !is_seen {
- Color::Byte(0)
- } else {
- Color::Default
- };
- let bg_color = if self.cursor_pos.2 == idx {
- Color::Byte(246)
- } else if !is_seen {
- Color::Byte(251)
- } else if idx % 2 == 0 {
- Color::Byte(236)
- } else {
- Color::Default
- };
- if idx == self.cursor_pos.2 || grid.is_none() {
- if let Some(grid) = grid {
- change_colors(grid, area, fg_color, bg_color);
- } else {
- change_colors(&mut self.content, area, fg_color, bg_color);
- }
- return;
- }
-
- let (upper_left, bottom_right) = area;
- let grid = grid.unwrap();
- let (mut x, _y) = upper_left;
- for i in 0..self.columns.len() {
- let (width, height) = self.columns[i].size();
- if self.widths[i] == 0 {
- continue;
- }
- copy_area(
- grid,
- &self.columns[i],
- (
- set_x(upper_left, x),
- set_x(
- bottom_right,
- std::cmp::min(get_x(bottom_right), x + (self.widths[i])),
- ),
- ),
- ((0, idx), (width.saturating_sub(1), height - 1)),
- );
- if i != self.columns.len() - 1 {
- change_colors(
- grid,
- (
- set_x(upper_left, x + self.widths[i].saturating_sub(1)),
- set_x(bottom_right, x + self.widths[i] + 1),
- ),
- fg_color,
- bg_color,
- );
- } else {
- change_colors(
- grid,
- (
- set_x(
- upper_left,
- std::cmp::min(get_x(bottom_right), x + (self.widths[i])),
- ),
- bottom_right,
- ),
- fg_color,
- bg_color,
- );
- }
- x += self.widths[i] + 2; // + SEPARATOR
- if x > get_x(bottom_right) {
- break;
- }
- }
- }
-
- /// Draw the list of `Envelope`s.
- fn draw_list(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
- if self.cursor_pos.1 != self.new_cursor_pos.1 || self.cursor_pos.0 != self.new_cursor_pos.0
- {
- self.refresh_mailbox(context);
- }
- let upper_left = upper_left!(area);
- let bottom_right = bottom_right!(area);
- if self.length == 0 {
- clear_area(grid, area);
- copy_area(
- grid,
- &self.content,
- area,
- ((0, 0), (MAX_COLS - 1, self.length)),
- );
- context.dirty_areas.push_back(area);
- return;
- }
- let rows = get_y(bottom_right) - get_y(upper_left) + 1;
-
- if let Some(mvm) = self.movement.take() {
- match mvm {
- PageMovement::PageUp => {
- self.new_cursor_pos.2 = self.new_cursor_pos.2.saturating_sub(rows);
- }
- PageMovement::PageDown => {
- if self.new_cursor_pos.2 + rows + 1 < self.length {
- self.new_cursor_pos.2 += rows;
- } else {
- self.new_cursor_pos.2 = (self.length / rows) * rows;
- }
- }
- PageMovement::Home => {
- self.new_cursor_pos.2 = 0;
- }
- PageMovement::End => {
- self.new_cursor_pos.2 = (self.length / rows) * rows;
- }
- }
- }
-
- let prev_page_no = (self.cursor_pos.2).wrapping_div(rows);
- let page_no = (self.new_cursor_pos.2).wrapping_div(rows);
-
- let top_idx = page_no * rows;
-
- /* If cursor position has changed, remove the highlight from the previous position and
- * apply it in the new one. */
- if self.cursor_pos.2 != self.new_cursor_pos.2 && prev_page_no == page_no {
- let old_cursor_pos = self.cursor_pos;
- self.cursor_pos = self.new_cursor_pos;
- for idx in &[old_cursor_pos.2, self.new_cursor_pos.2] {
- if *idx >= self.length {
- continue; //bounds check
- }
- let new_area = (
- set_y(upper_left, get_y(upper_left) + (*idx % rows)),
- set_y(bottom_right, get_y(upper_left) + (*idx % rows)),
- );
- self.highlight_line(Some(grid), new_area, *idx, context);
- context.dirty_areas.push_back(new_area);
- }
- return;
- } else if self.cursor_pos != self.new_cursor_pos {
- self.cursor_pos = self.new_cursor_pos;
- }
- if self.new_cursor_pos.2 >= self.length {
- self.new_cursor_pos.2 = self.length - 1;
- self.cursor_pos.2 = self.new_cursor_pos.2;
- }
-
- let width = width!(area);
- self.widths = [
- self.columns[0].size().0,
- self.columns[1].size().0, /* date*/
- self.columns[2].size().0, /* from */
- self.columns[3].size().0, /* flags */
- self.columns[4].size().0, /* subject */
- ];
- let min_col_width = std::cmp::min(15, std::cmp::min(self.widths[4], self.widths[2]));
- if self.widths[0] + self.widths[1] + 3 * min_col_width + 8 > width {
- let remainder = width
- .saturating_sub(self.widths[0])
- .saturating_sub(self.widths[1])
- - 4;
- self.widths[2] = remainder / 6;
- self.widths[4] = (2 * remainder) / 3 - self.widths[3];
- } else {
- let remainder = width
- .saturating_sub(self.widths[0])
- .saturating_sub(self.widths[1])
- .saturating_sub(8);
- if min_col_width + self.widths[4] > remainder {
- self.widths[4] = remainder - min_col_width - self.widths[3];
- self.widths[2] = min_col_width;
- }
- }
- clear_area(grid, area);
- /* Page_no has changed, so draw new page */
- let mut x = get_x(upper_left);
- let mut flag_x = 0;
- for i in 0..self.columns.len() {
- let column_width = self.columns[i].size().0;
- if i == 3 {
- flag_x = x;
- }
- if self.widths[i] == 0 {
- continue;
- }
- copy_area(
- grid,
- &self.columns[i],
- (
- set_x(upper_left, x),
- set_x(
- bottom_right,
- std::cmp::min(get_x(bottom_right), x + (self.widths[i])),
- ),
- ),
- (
- (0, top_idx),
- (column_width.saturating_sub(1), self.length - 1),
- ),
- );
- x += self.widths[i] + 2; // + SEPARATOR
- if x > get_x(bottom_right) {
- break;
- }
- }
- for r in 0..cmp::min(self.length - top_idx, rows) {
- let (fg_color, bg_color) = {
- let c = &self.columns[0][(0, r + top_idx)];
- (c.fg(), c.bg())
- };
- change_colors(
- grid,
- (
- pos_inc(upper_left, (0, r)),
- (flag_x - 1, get_y(upper_left) + r),
- ),
- fg_color,
- bg_color,
- );
- for x in flag_x..(flag_x + 2 + self.widths[3]) {
- grid[(x, get_y(upper_left) + r)].set_bg(bg_color);
- }
- change_colors(
- grid,
- (
- (flag_x + 2 + self.widths[3], get_y(upper_left) + r),
- (get_x(bottom_right), get_y(upper_left) + r),
- ),
- fg_color,
- bg_color,
- );
- }
- let temp_copy_because_of_nll = self.cursor_pos.2; // FIXME
- self.highlight_line(
- Some(grid),
- (
- set_y(
- upper_left,
- get_y(upper_left) + (temp_copy_because_of_nll % rows),
- ),
- set_y(
- bottom_right,
- get_y(upper_left) + (temp_copy_because_of_nll % rows),
- ),
- ),