summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorManos Pitsidianakis <el13635@mail.ntua.gr>2019-04-10 16:51:15 +0300
committerManos Pitsidianakis <el13635@mail.ntua.gr>2019-06-10 19:40:44 +0300
commit7e80ad2a86de587cbbd8fa6a0e80b8dda74fa5fa (patch)
tree7e7af1ec8cda5291d4b39e87300b0f1f369ccda9
parent0e2e8b09f6403a80967487eff8dc04d9ab3c008a (diff)
ui: hold live entries for each mailbox in CompactListing
-rw-r--r--ui/src/components/mail/listing/compact.rs254
1 files changed, 190 insertions, 64 deletions
diff --git a/ui/src/components/mail/listing/compact.rs b/ui/src/components/mail/listing/compact.rs
index deaf70bb..e885cf09 100644
--- a/ui/src/components/mail/listing/compact.rs
+++ b/ui/src/components/mail/listing/compact.rs
@@ -26,10 +26,8 @@ use components::utilities::PageMovement;
const MAX_COLS: usize = 500;
-/// 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 {
+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),
@@ -47,28 +45,13 @@ pub struct CompactListing {
movement: Option<PageMovement>,
}
-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);
- }
-}
-
-impl Default for CompactListing {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl fmt::Display for CompactListing {
+impl fmt::Display for MailboxView {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "mail")
+ write!(f, "")
}
}
-impl CompactListing {
+impl MailboxView {
/// Helper function to format entry strings for CompactListing */
/* TODO: Make this configurable */
fn make_entry_string(e: &Envelope, len: usize, idx: usize) -> String {
@@ -76,7 +59,7 @@ impl CompactListing {
format!(
"{} {} {:.85} ({})",
idx,
- &CompactListing::format_date(e),
+ &MailboxView::format_date(e),
e.subject(),
len
)
@@ -84,15 +67,15 @@ impl CompactListing {
format!(
"{} {} {:.85}",
idx,
- &CompactListing::format_date(e),
+ &MailboxView::format_date(e),
e.subject(),
)
}
}
- pub fn new() -> Self {
+ fn new() -> Self {
let content = CellBuffer::new(0, 0, Cell::with_char(' '));
- CompactListing {
+ MailboxView {
cursor_pos: (0, 1, 0),
new_cursor_pos: (0, 0, 0),
length: 0,
@@ -114,7 +97,6 @@ impl CompactListing {
if !(self.cursor_pos.0 == self.new_cursor_pos.0
&& self.cursor_pos.1 == self.new_cursor_pos.1)
{
- //TODO: store cursor_pos in each folder
self.cursor_pos.2 = 0;
self.new_cursor_pos.2 = 0;
}
@@ -209,7 +191,7 @@ impl CompactListing {
Color::Default
};
let (x, _) = write_string_to_grid(
- &CompactListing::make_entry_string(root_envelope, thread_node.len(), idx),
+ &MailboxView::make_entry_string(root_envelope, thread_node.len(), idx),
&mut self.content,
fg_color,
bg_color,
@@ -377,7 +359,7 @@ impl CompactListing {
}
}
-impl Component for CompactListing {
+impl Component for MailboxView {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
if !self.unfocused {
if !self.is_dirty() {
@@ -438,47 +420,16 @@ impl Component for CompactListing {
}
UIEventType::Input(Key::Char(k @ 'J')) | UIEventType::Input(Key::Char(k @ 'K')) => {
let folder_length = context.accounts[self.cursor_pos.0].len();
- let accounts_length = context.accounts.len();
match k {
- 'J' if folder_length > 0 => {
- if self.new_cursor_pos.1 < folder_length - 1 {
- self.new_cursor_pos.1 = self.cursor_pos.1 + 1;
- self.refresh_mailbox(context);
- } else if accounts_length > 0 && self.new_cursor_pos.0 < accounts_length - 1
- {
- self.new_cursor_pos.0 = self.cursor_pos.0 + 1;
- self.new_cursor_pos.1 = 0;
- self.refresh_mailbox(context);
- }
- }
- 'K' => {
- if self.cursor_pos.1 > 0 {
- self.new_cursor_pos.1 = self.cursor_pos.1 - 1;
- self.refresh_mailbox(context);
- } else if self.cursor_pos.0 > 0 {
- self.new_cursor_pos.0 = self.cursor_pos.0 - 1;
- self.new_cursor_pos.1 = 0;
- self.refresh_mailbox(context);
- }
- }
- _ => {}
- }
- return true;
- }
- UIEventType::Input(Key::Char(k @ 'h')) | UIEventType::Input(Key::Char(k @ 'l')) => {
- let accounts_length = context.accounts.len();
- match k {
- 'h' if accounts_length > 0 && self.new_cursor_pos.0 < accounts_length - 1 => {
- self.new_cursor_pos.0 = self.cursor_pos.0 + 1;
- self.new_cursor_pos.1 = 0;
+ 'J' if folder_length > 0 && self.new_cursor_pos.1 < folder_length - 1 => {
+ self.new_cursor_pos.1 = self.cursor_pos.1 + 1;
self.refresh_mailbox(context);
}
- 'l' if self.cursor_pos.0 > 0 => {
- self.new_cursor_pos.0 = self.cursor_pos.0 - 1;
- self.new_cursor_pos.1 = 0;
+ 'K' if self.cursor_pos.1 > 0 => {
+ self.new_cursor_pos.1 = self.cursor_pos.1 - 1;
self.refresh_mailbox(context);
}
- _ => {}
+ _ => return false,
}
return true;
}
@@ -637,3 +588,178 @@ impl Component for CompactListing {
map
}
}
+
+/// 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 {
+ views: Vec<MailboxView>,
+ cursor: usize,
+ dirty: bool,
+ populated: bool,
+}
+
+impl ListingTrait for CompactListing {
+ fn coordinates(&self) -> (usize, usize, Option<EnvelopeHash>) {
+ (self.cursor, self.views[self.cursor].cursor_pos.1, None)
+ }
+ fn set_coordinates(&mut self, coordinates: (usize, usize, Option<EnvelopeHash>)) {
+ self.views[self.cursor].new_cursor_pos = (coordinates.0, coordinates.1, 0);
+ }
+}
+
+impl fmt::Display for CompactListing {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "mail")
+ }
+}
+
+impl Default for CompactListing {
+ fn default() -> Self {
+ CompactListing::new()
+ }
+}
+
+impl CompactListing {
+ pub fn new() -> Self {
+ CompactListing {
+ views: Vec::with_capacity(8),
+ cursor: 0,
+ dirty: true,
+ populated: false,
+ }
+ }
+}
+
+impl Component for CompactListing {
+ fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
+ if !self.populated {
+ eprintln!("populating");
+ for (idx, a) in context.accounts.iter().enumerate() {
+ for (fidx, _) in a.iter_mailboxes().enumerate() {
+ let mut m = MailboxView::new();
+ m.new_cursor_pos = (idx, fidx, 0);
+ self.views.push(m);
+ }
+ }
+ self.populated = true;
+ }
+
+ if self.views.is_empty() {
+ return;
+ }
+ self.views[self.cursor].draw(grid, area, context);
+ }
+ fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
+ if self.views.is_empty() {
+ return false;
+ }
+ match event.event_type {
+ UIEventType::Input(Key::Char(k @ 'J')) | UIEventType::Input(Key::Char(k @ 'K')) => {
+ let folder_length = context.accounts[self.views[self.cursor].cursor_pos.0].len();
+ match k {
+ 'J' if folder_length > 0 => {
+ if self.cursor < self.views.len() - 1 {
+ self.cursor += 1;
+ self.dirty = true;
+ }
+ }
+ 'K' if self.cursor > 0 => {
+ self.cursor -= 1;
+ self.dirty = true;
+ }
+ _ => return false,
+ }
+ self.views[self.cursor].refresh_mailbox(context);
+ return true;
+ }
+ UIEventType::Input(Key::Char(k @ 'h')) | UIEventType::Input(Key::Char(k @ 'l')) => {
+ let binary_search_account = |entries: &[MailboxView], x: usize| -> Option<usize> {
+ if entries.is_empty() {
+ return None;
+ }
+
+ let mut low = 0;
+ let mut high = entries.len() - 1;
+ while low < high {
+ let mid = low + (high - low) / 2;
+ if x > entries[mid].new_cursor_pos.0 {
+ low = mid + 1;
+ } else {
+ high = mid;
+ }
+ }
+ return Some(low);
+ };
+ match k {
+ 'h' => {
+ if let Some(next) = binary_search_account(
+ &self.views.as_slice()[self.cursor..],
+ self.views[self.cursor].new_cursor_pos.0 + 1,
+ ) {
+ self.cursor += next;
+ self.dirty = true;
+ }
+ }
+ 'l' if self.views[self.cursor].cursor_pos.0 > 0 => {
+ if let Some(next) = binary_search_account(
+ &self.views.as_slice()[..self.cursor],
+ self.views[self.cursor].new_cursor_pos.0 - 1,
+ ) {
+ self.cursor = next;
+ self.dirty = true;
+ }
+ }
+ _ => return false,
+ }
+ self.views[self.cursor].refresh_mailbox(context);
+ return true;
+ }
+ _ => {
+ return self.views[self.cursor].process_event(event, context);
+ }
+ }
+ }
+
+ fn is_dirty(&self) -> bool {
+ if self.views.is_empty() {
+ return self.dirty;
+ }
+ self.dirty || self.views[self.cursor].is_dirty()
+ }
+ fn set_dirty(&mut self) {
+ if self.views.is_empty() {
+ return;
+ }
+
+ self.views[self.cursor].set_dirty();
+ self.dirty = true;
+ }
+
+ fn get_shortcuts(&self, context: &Context) -> ShortcutMap {
+ if self.views.is_empty() {
+ return Default::default();
+ }
+ let mut map = self.views[self.cursor].get_shortcuts(context);
+
+ let config_map = context.settings.shortcuts.compact_listing.key_values();
+ map.insert(
+ "prev_account",
+ if let Some(key) = config_map.get("prev_account") {
+ (*key).clone()
+ } else {
+ Key::Char('h')
+ },
+ );
+ map.insert(
+ "next_account",
+ if let Some(key) = config_map.get("next_account") {
+ (*key).clone()
+ } else {
+ Key::Char('l')
+ },
+ );
+
+ map
+ }
+}