summaryrefslogtreecommitdiffstats
path: root/ui/src
diff options
context:
space:
mode:
authorManos Pitsidianakis <el13635@mail.ntua.gr>2018-10-14 19:49:16 +0300
committerManos Pitsidianakis <el13635@mail.ntua.gr>2019-06-10 19:40:36 +0300
commit5a283200047f00d45d894b2873c583be7f00ad8b (patch)
tree536ff537b4cc26d96816e0ee70b9e2be9b214a5f /ui/src
parent21a918e4c0a20c2c7dce594979f4419e06ee16b1 (diff)
WIP
Diffstat (limited to 'ui/src')
-rw-r--r--ui/src/components/indexer/index.rs187
-rw-r--r--ui/src/components/indexer/mod.rs128
-rw-r--r--ui/src/components/mail/listing/compact.rs29
-rw-r--r--ui/src/components/mail/listing/mod.rs2
-rw-r--r--ui/src/components/mail/listing/thread.rs272
-rw-r--r--ui/src/components/mail/mod.rs3
-rw-r--r--ui/src/components/mod.rs3
-rw-r--r--ui/src/lib.rs3
-rw-r--r--ui/src/state.rs34
-rw-r--r--ui/src/types/accounts.rs75
-rw-r--r--ui/src/types/cells.rs6
-rw-r--r--ui/src/workers/mod.rs298
12 files changed, 883 insertions, 157 deletions
diff --git a/ui/src/components/indexer/index.rs b/ui/src/components/indexer/index.rs
new file mode 100644
index 00000000..3faed023
--- /dev/null
+++ b/ui/src/components/indexer/index.rs
@@ -0,0 +1,187 @@
+use super::*;
+use components::utilities::PageMovement;
+
+pub trait IndexContent: Component {
+ /* Handles the drawing of one entry */
+ fn make_entry(&mut self, idx: usize) -> ();
+
+ /* Handles what happens when the user selects an entry in the index listing */
+ fn enter_entry(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) -> ();
+
+ /* Refreshes content */
+ fn refresh(&mut self, context: &mut Context) -> ();
+
+ fn search(&self, term: &str) -> Option<usize>;
+}
+
+#[derive(Debug, PartialEq)]
+enum IndexState {
+ Uninitialized,
+ Listing,
+ Unfocused,
+ Search,
+}
+
+#[derive(Debug)]
+pub struct Index {
+ cursor_pos: usize,
+ new_cursor_pos: usize,
+ length: usize,
+
+ /// Cache current view.
+ canvas: CellBuffer,
+ /// If we must redraw on next redraw event
+ dirty: bool,
+ state: IndexState,
+
+ content: Box<IndexContent>,
+}
+
+impl Index {
+ fn highlight_line(&self, grid: &mut CellBuffer, area: Area, idx: usize) {
+ let fg_color = Color::Default;
+ let bg_color = if self.cursor_pos == idx {
+ Color::Byte(246)
+ /* } else if idx % 2 == 0 {
+ Color::Byte(236)*/
+ } else {
+ Color::Default
+ };
+ change_colors(grid, area, fg_color, bg_color);
+ }
+}
+
+impl Component for Index {
+ fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
+ if !self.dirty {
+ return;
+ }
+
+ match self.state {
+ IndexState::Uninitialized => {
+ self.content.refresh(context);
+
+ /* copy area */
+ self.state = IndexState::Listing;
+ self.draw(grid, area, context);
+ return;
+ }
+ IndexState::Listing => {
+ /* rehighlight entries, redraw pages */
+ let upper_left = upper_left!(area);
+ let bottom_right = bottom_right!(area);
+ let rows = get_y(bottom_right) - get_y(upper_left) + 1;
+ let prev_page_no = (self.cursor_pos).wrapping_div(rows);
+ let page_no = (self.new_cursor_pos).wrapping_div(rows);
+
+ let top_idx = page_no * rows;
+ if self.new_cursor_pos >= self.length {
+ self.new_cursor_pos = self.length - 1;
+ }
+ /* If cursor position has changed, remove the highlight from the previous position and
+ * apply it in the new one. */
+ if self.cursor_pos != self.new_cursor_pos && 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, self.new_cursor_pos] {
+ 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.dirty_areas.push_back(new_area);
+ }
+ return;
+ } else if self.cursor_pos != self.new_cursor_pos {
+ self.cursor_pos = self.new_cursor_pos;
+ }
+
+ /* Page_no has changed, so draw new page */
+ copy_area(
+ grid,
+ &self.canvas,
+ area,
+ ((0, top_idx), (500 - 1, self.length)),
+ );
+ self.highlight_line(
+ grid,
+ (
+ (
+ get_x(upper_left),
+ get_y(upper_left) + (self.cursor_pos % rows),
+ ),
+ (
+ get_x(bottom_right),
+ get_y(upper_left) + (self.cursor_pos % rows),
+ ),
+ ),
+ self.cursor_pos,
+ );
+ context.dirty_areas.push_back(area);
+ }
+ IndexState::Unfocused => {
+ self.content.draw(grid, area, context);
+ }
+ IndexState::Search => unreachable!(),
+ }
+
+ self.dirty = false;
+ return;
+ }
+
+ fn process_event(&mut self, event: &UIEvent, context: &mut Context) -> bool {
+ if self.content.process_event(event, context) {
+ return true;
+ }
+ match event.event_type {
+ UIEventType::Input(Key::Up) => {
+ if self.cursor_pos > 0 {
+ self.new_cursor_pos = self.new_cursor_pos.saturating_sub(1);
+ self.set_dirty();
+ }
+ return true;
+ }
+ UIEventType::Input(Key::Down) => {
+ if self.length > 0 && self.new_cursor_pos < self.length - 1 {
+ self.new_cursor_pos += 1;
+ self.set_dirty();
+ }
+ return true;
+ }
+ UIEventType::Input(Key::Char('\n')) if self.state == IndexState::Listing => {
+ self.state = IndexState::Unfocused;
+ self.set_dirty();
+ return true;
+ }
+ UIEventType::Input(Key::Char('i')) if self.state == IndexState::Unfocused => {
+ self.state = IndexState::Listing;
+ self.set_dirty();
+ return true;
+ }
+ UIEventType::ChangeMode(UIMode::Normal) => {
+ self.set_dirty();
+ }
+ UIEventType::Resize => {
+ self.set_dirty();
+ }
+ _ => {}
+ }
+
+ false
+ }
+ fn is_dirty(&self) -> bool {
+ self.dirty || self.content.is_dirty()
+ }
+ fn set_dirty(&mut self) {
+ self.dirty = true;
+ }
+}
+
+impl fmt::Display for Index {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Display::fmt(&self.content, f)
+ }
+}
diff --git a/ui/src/components/indexer/mod.rs b/ui/src/components/indexer/mod.rs
new file mode 100644
index 00000000..078685bd
--- /dev/null
+++ b/ui/src/components/indexer/mod.rs
@@ -0,0 +1,128 @@
+/*
+ * meli - ui crate.
+ *
+ * Copyright 2017-2018 Manos Pitsidianakis
+ *
+ * This file is part of meli.
+ *
+ * meli is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * meli is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with meli. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*! Entities that handle Mail specific functions.
+ */
+use super::*;
+
+pub mod index;
+pub use self::index::*;
+
+#[derive(Debug)]
+struct MenuEntry {
+ name: String,
+ subentries: Vec<MenuEntry>,
+ index: Index,
+}
+
+#[derive(Debug)]
+pub struct Indexer {
+ entries: Vec<MenuEntry>,
+ dirty: bool,
+ cursor: Vec<usize>,
+}
+
+impl fmt::Display for Indexer {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ // TODO display subject/info
+ write!(f, "index")
+ }
+}
+
+impl Default for Indexer {
+ fn default() -> Self {
+ Indexer {
+ entries: Vec::with_capacity(8),
+ dirty: true,
+ cursor: Vec::with_capacity(8),
+ }
+ }
+}
+
+impl Indexer {
+ fn draw_menu(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {}
+}
+
+impl Component for Indexer {
+ fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
+ if !self.is_dirty() {
+ return;
+ }
+
+ clear_area(grid, area);
+ let upper_left = upper_left!(area);
+ let bottom_right = bottom_right!(area);
+
+ let total_cols = get_x(bottom_right) - get_x(upper_left);
+ let index_entity_width = (30 * total_cols) / 100;
+ let mid = get_x(bottom_right) - index_entity_width;
+ for i in get_y(upper_left)..=get_y(bottom_right) {
+ set_and_join_box(grid, (mid, i), VERT_BOUNDARY);
+ }
+
+ let left_menu_area = (upper_left, (set_x(bottom_right, mid - 1)));
+ let right_index_area = (set_x(upper_left, mid + 1), bottom_right);
+
+ self.draw_menu(grid, left_menu_area, context);
+ self.entries[self.cursor[0]]
+ .index
+ .draw(grid, right_index_area, context);
+
+ self.dirty = false;
+ context.dirty_areas.push_back(area);
+ }
+
+ fn process_event(&mut self, event: &UIEvent, _context: &mut Context) -> bool {
+ if !self.entries[self.cursor[0]]
+ .index
+ .process_event(event, _context)
+ {
+ for i in 0..self.entries.len() {
+ if i == self.cursor[0] {
+ continue;
+ }
+ self.entries[i].index.process_event(event, _context);
+ }
+ }
+
+ match event.event_type {
+ UIEventType::RefreshMailbox(c) => {
+ self.dirty = true;
+ }
+ UIEventType::ChangeMode(UIMode::Normal) => {
+ self.dirty = true;
+ }
+ UIEventType::Resize => {
+ self.dirty = true;
+ }
+ _ => {}
+ }
+ false
+ }
+
+ fn is_dirty(&self) -> bool {
+ self.dirty
+ }
+
+ fn set_dirty(&mut self) {
+ self.dirty = true;
+ }
+}
diff --git a/ui/src/components/mail/listing/compact.rs b/ui/src/components/mail/listing/compact.rs
index 3d339737..8814d30e 100644
--- a/ui/src/components/mail/listing/compact.rs
+++ b/ui/src/components/mail/listing/compact.rs
@@ -63,7 +63,7 @@ impl CompactListing {
/// Helper function to format entry strings for CompactListing */
/* TODO: Make this configurable */
fn make_entry_string(e: &Envelope, len: usize, idx: usize) -> String {
- if len > 1 {
+ if len > 0 {
format!(
"{} {} {:.85} ({})",
idx,
@@ -101,8 +101,13 @@ impl CompactListing {
/// chosen.
fn refresh_mailbox(&mut self, context: &mut Context) {
self.dirty = true;
- self.cursor_pos.2 = 0;
- self.new_cursor_pos.2 = 0;
+ 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;
+ }
self.cursor_pos.1 = self.new_cursor_pos.1;
self.cursor_pos.0 = self.new_cursor_pos.0;
@@ -124,7 +129,7 @@ impl CompactListing {
Color::Default,
Color::Default,
((0, 0), (MAX_COLS - 1, 0)),
- true,
+ false,
);
return;
}
@@ -142,7 +147,7 @@ impl CompactListing {
Color::Default,
Color::Default,
((0, 0), (MAX_COLS - 1, 0)),
- true,
+ false,
);
return;
}
@@ -204,6 +209,9 @@ impl CompactListing {
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1]
.as_ref()
.unwrap();
+ if mailbox.len() == 0 {
+ return;
+ }
let threads = &mailbox.collection.threads;
let thread_node = threads.root_set(idx);
let thread_node = &threads.thread_nodes()[thread_node];
@@ -216,6 +224,7 @@ impl CompactListing {
}
threads.thread_nodes()[iter_ptr].message().unwrap()
};
+
let root_envelope: &Envelope = &mailbox.collection[&i];
let fg_color = if !root_envelope.is_seen() {
Color::Byte(0)
@@ -253,7 +262,12 @@ impl CompactListing {
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, 0)));
+ copy_area(
+ grid,
+ &self.content,
+ area,
+ ((0, 0), (MAX_COLS - 1, self.length)),
+ );
context.dirty_areas.push_back(area);
return;
}
@@ -346,7 +360,6 @@ impl Component for CompactListing {
if !self.is_dirty() {
return;
}
- self.dirty = false;
/* Draw the entire list */
self.draw_list(grid, area, context);
} else {
@@ -365,8 +378,8 @@ impl Component for CompactListing {
}
self.view = Some(ThreadView::new(self.cursor_pos, None, context));
self.view.as_mut().unwrap().draw(grid, area, context);
- self.dirty = false;
}
+ self.dirty = false;
}
fn process_event(&mut self, event: &UIEvent, context: &mut Context) -> bool {
if let Some(ref mut v) = self.view {
diff --git a/ui/src/components/mail/listing/mod.rs b/ui/src/components/mail/listing/mod.rs
index d600d33a..4bd6898d 100644
--- a/ui/src/components/mail/listing/mod.rs
+++ b/ui/src/components/mail/listing/mod.rs
@@ -49,7 +49,7 @@ impl fmt::Display for Listing {
impl Default for Listing {
fn default() -> Self {
- Listing::Compact(Default::default())
+ Listing::Threaded(Default::default())
}
}
diff --git a/ui/src/components/mail/listing/thread.rs b/ui/src/components/mail/listing/thread.rs
index efc3a291..9df89568 100644
--- a/ui/src/components/mail/listing/thread.rs
+++ b/ui/src/components/mail/listing/thread.rs
@@ -35,10 +35,13 @@ pub struct ThreadListing {
subsort: (SortField, SortOrder),
/// Cache current view.
content: CellBuffer,
+
+ locations: Vec<(usize, usize)>,
/// If we must redraw on next redraw event
dirty: bool,
/// If `self.view` exists or not.
unfocused: bool,
+ initialised: bool,
view: Option<MailView>,
}
@@ -64,17 +67,24 @@ impl ThreadListing {
sort: (Default::default(), Default::default()),
subsort: (Default::default(), Default::default()),
content,
+ locations: Vec::new(),
dirty: true,
unfocused: false,
view: None,
+ initialised: false,
}
}
/// Fill the `self.content` `CellBuffer` with the contents of the account folder the user has
/// chosen.
fn refresh_mailbox(&mut self, context: &mut Context) {
self.dirty = true;
- self.cursor_pos.2 = 0;
- self.new_cursor_pos.2 = 0;
+ 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;
+ }
self.cursor_pos.1 = self.new_cursor_pos.1;
self.cursor_pos.0 = self.new_cursor_pos.0;
@@ -85,20 +95,27 @@ impl ThreadListing {
});
// Get mailbox as a reference.
//
- loop {
- // TODO: Show progress visually
- if context.accounts[self.cursor_pos.0]
- .status(self.cursor_pos.1)
- .is_ok()
- {
- break;
+ match context.accounts[self.cursor_pos.0].status(self.cursor_pos.1) {
+ Ok(_) => {}
+ Err(_) => {
+ self.content = CellBuffer::new(MAX_COLS, 1, Cell::with_char(' '));
+ self.length = 0;
+ write_string_to_grid(
+ "Loading.",
+ &mut self.content,
+ Color::Default,
+ Color::Default,
+ ((0, 0), (MAX_COLS - 1, 0)),
+ false,
+ );
+ return;
}
}
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1]
.as_ref()
.unwrap();
- self.length = mailbox.collection.threads.root_len();
+ self.length = mailbox.collection.threads.len();
self.content = CellBuffer::new(MAX_COLS, self.length + 1, Cell::with_char(' '));
if self.length == 0 {
write_string_to_grid(
@@ -118,25 +135,19 @@ impl ThreadListing {
let threads = &mailbox.collection.threads;
threads.sort_by(self.sort, self.subsort, &mailbox.collection);
let thread_nodes: &Vec<ThreadNode> = &threads.thread_nodes();
- let mut iter = threads.root_iter().peekable();
- let len = threads.root_len().to_string().chars().count();
+ self.locations = threads.threads_iter().collect();
+ let mut iter = self.locations.iter().peekable();
/* This is just a desugared for loop so that we can use .peek() */
let mut idx = 0;
- while let Some(i) = iter.next() {
- let thread_node = &thread_nodes[i];
-
- if !thread_node.has_message() {
- continue;
- }
-
- let indentation = thread_node.indentation();
+ while let Some((indentation, i)) = iter.next() {
+ let thread_node = &thread_nodes[*i];
- if indentation == 0 {
+ if *indentation == 0 {
thread_idx += 1;
}
match iter.peek() {
- Some(&x) if thread_nodes[x].indentation() == indentation => {
+ Some((x, _)) if *x == *indentation => {
indentations.pop();
indentations.push(true);
}
@@ -145,51 +156,58 @@ impl ThreadListing {
indentations.push(false);
}
}
- if threads.has_sibling(i) {
+ if threads.has_sibling(*i) {
indentations.pop();
indentations.push(true);
}
- let envelope: &Envelope = &mailbox.collection[&thread_node.message().unwrap()];
- let fg_color = if !envelope.is_seen() {
- Color::Byte(0)
- } else {
- Color::Default
- };
- let bg_color = if !envelope.is_seen() {
- Color::Byte(251)
- } else if thread_idx % 2 == 0 {
- Color::Byte(236)
+ if thread_node.has_message() {
+ let envelope: &Envelope = &mailbox.collection[&thread_node.message().unwrap()];
+ let fg_color = if !envelope.is_seen() {
+ Color::Byte(0)
+ } else {
+ Color::Default
+ };
+ let bg_color = if !envelope.is_seen() {
+ Color::Byte(251)
+ } else if thread_idx % 2 == 0 {
+ Color::Byte(236)
+ } else {
+ Color::Default
+ };
+ let (x, _) = write_string_to_grid(
+ &ThreadListing::make_thread_entry(
+ envelope,
+ idx,
+ *indentation,
+ *i,
+ threads,
+ &indentations,
+ self.length,
+ // context.accounts[self.cursor_pos.0].backend.operation(envelope.hash())
+ ),
+ &mut self.content,
+ fg_color,
+ bg_color,
+ ((0, idx), (MAX_COLS - 1, idx)),
+ false,
+ );
+ for x in x..MAX_COLS {
+ self.content[(x, idx)].set_ch(' ');
+ self.content[(x, idx)].set_bg(bg_color);
+ }
} else {
- Color::Default
- };
- let (x, _) = write_string_to_grid(
- &ThreadListing::make_thread_entry(
- envelope,
- idx,
- indentation,
- i,
- threads,
- &indentations,
- len,
- // context.accounts[self.cursor_pos.0].backend.operation(envelope.hash())
- ),
- &mut self.content,
- fg_color,
- bg_color,
- ((0, idx), (MAX_COLS - 1, idx)),
- false,
- );
- for x in x..MAX_COLS {
- self.content[(x, idx)].set_ch(' ');
- self.content[(x, idx)].set_bg(bg_color);
+ for x in 0..MAX_COLS {
+ self.content[(x, idx)].set_ch(' ');
+ self.content[(x, idx)].set_bg(Color::Default);
+ }
}
match iter.peek() {
- Some(&x) if thread_nodes[x].indentation() > indentation => {
+ Some((x, _)) if *x > *indentation => {
indentations.push(false);
}
- Some(&x) if thread_nodes[x].indentation() < indentation => {
- for _ in 0..(indentation - thread_nodes[x].indentation()) {
+ Some((x, _)) if *x < *indentation => {
+ for _ in 0..(*indentation - *x) {
indentations.pop();
}
}
@@ -203,49 +221,64 @@ impl ThreadListing {
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1]
.as_ref()
.unwrap();
- let envelope: &Envelope = mailbox.thread_to_mail(idx);
+ if mailbox.len() == 0 {
+ return;
+ }
+ if let Some(hash) =
+ mailbox.collection.threads.thread_nodes()[self.locations[idx].1].message()
+ {
+ let envelope: &Envelope = &mailbox.collection[&hash];
- let fg_color = if !envelope.is_seen() {
- Color::Byte(0)
- } else {
- Color::Default
- };
- let bg_color = if !envelope.is_seen() {
- Color::Byte(251)
- } else if idx % 2 == 0 {
- Color::Byte(236)
- } else {
- Color::Default
- };
- change_colors(
- &mut self.content,
- ((0, idx), (MAX_COLS - 1, idx)),
- fg_color,
- bg_color,
- );
+ let fg_color = if !envelope.is_seen() {
+ Color::Byte(0)
+ } else {
+ Color::Default
+ };
+ let bg_color = if !envelope.is_seen() {
+ Color::Byte(251)
+ } else if idx % 2 == 0 {
+ Color::Byte(236)
+ } else {
+ Color::Default
+ };
+ change_colors(
+ &mut self.content,
+ ((0, idx), (MAX_COLS - 1, idx)),
+ fg_color,
+ bg_color,
+ );
+ }
}
fn highlight_line(&self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) {
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1]
.as_ref()
.unwrap();
- let envelope: &Envelope = mailbox.thread_to_mail(idx);
+ if mailbox.len() == 0 || mailbox.len() <= idx {
+ return;
+ }
- let fg_color = if !envelope.is_seen() {
- Color::Byte(0)
- } else {
- Color::Default
- };
- let bg_color = if self.cursor_pos.2 == idx {
- Color::Byte(246)
- } else if !envelope.is_seen() {
- Color::Byte(251)
- } else if idx % 2 == 0 {
- Color::Byte(236)
- } else {
- Color::Default
- };
- change_colors(grid, area, fg_color, bg_color);
+ if let Some(hash) =
+ mailbox.collection.threads.thread_nodes()[self.locations[idx].1].message()
+ {
+ let envelope: &Envelope = &mailbox.collection[&hash];
+
+ let fg_color = if !envelope.is_seen() {
+ Color::Byte(0)
+ } else {
+ Color::Default
+ };
+ let bg_color = if self.cursor_pos.2 == idx {
+ Color::Byte(246)
+ } else if !envelope.is_seen() {
+ Color::Byte(251)
+ } else if idx % 2 == 0 {
+ Color::Byte(236)
+ } else {
+ Color::Default
+ };
+ change_colors(grid, area, fg_color, bg_color);
+ }
}
/// Draw the list of `Envelope`s.
@@ -266,7 +299,25 @@ impl ThreadListing {
let page_no = (self.new_cursor_pos.2).wrapping_div(rows);
let top_idx = page_no * rows;
-
+ if !self.initialised {
+ self.initialised = false;
+ copy_area(
+ grid,
+ &self.content,
+ area,
+ ((0, top_idx), (MAX_COLS - 1, self.length)),
+ );
+ self.highlight_line(
+ grid,
+ (
+ set_y(upper_left, get_y(upper_left) + (self.cursor_pos.2 % rows)),
+ set_y(bottom_right, get_y(upper_left) + (self.cursor_pos.2 % rows)),
+ ),
+ self.cursor_pos.2,
+ context,
+ );
+ context.dirty_areas.push_back(area);
+ }
/* 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 {
@@ -402,8 +453,26 @@ impl Component for ThreadListing {
context.dirty_areas.push_back(area);
return;
}
- /* Mark message as read */
+
let idx = self.cursor_pos.2;
+
+ {
+ let has_message: bool = {
+ let account = &context.accounts[self.cursor_pos.0];
+ let mailbox = &account[self.cursor_pos.1].as_ref().unwrap();
+ mailbox.collection.threads.thread_nodes()
+ [self.locations[self.new_cursor_pos.2].1]
+ .message()
+ .is_some()
+ };
+ if !has_message {
+ self.dirty = false;
+ /* Draw the entire list */
+ return self.draw_list(grid, area, context);
+ }
+ }
+
+ /* Mark message as read */
let must_highlight = {
if self.length == 0 {
false
@@ -411,7 +480,8 @@ impl Component for ThreadListing {
let account = &mut context.accounts[self.cursor_pos.0];
let (hash, is_seen) = {
let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap();
- let envelope: &mut Envelope = mailbox.thread_to_mail_mut(idx);
+ let envelope: &mut Envelope =
+ mailbox.thread_to_mail_mut(self.locations[self.new_cursor_pos.2].1);
(envelope.hash(), envelope.is_seen())
};
if !is_seen {
@@ -424,7 +494,8 @@ impl Component for ThreadListing {
backend.operation(hash, folder_hash)
};
let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap();
- let envelope: &mut Envelope = mailbox.thread_to_mail_mut(idx);
+ let envelope: &mut Envelope =
+ mailbox.thread_to_mail_mut(self.locations[self.new_cursor_pos.2].1);
envelope.set_seen(op).unwrap();
true
} else {
@@ -476,7 +547,7 @@ impl Component for ThreadListing {
let coordinates = (
self.cursor_pos.0,
self.cursor_pos.1,
- mailbox.threaded_mail(self.cursor_pos.2),
+ mailbox.threaded_mail(self.locations[self.cursor_pos.2].1),
);
self.view = Some(MailView::new(coordinates, None, None));
}
@@ -587,6 +658,7 @@ impl Component for ThreadListing {
if *idxa == self.new_cursor_pos.0 && *idxf == self.new_cursor_pos.1 {
self.dirty = true;
self.refresh_mailbox(context);
+ eprintln!("mailboxupdate");
}
}
UIEventType::ChangeMode(UIMode::Normal) => {
@@ -596,8 +668,8 @@ impl Component for ThreadListing {
self.dirty = true;
}
UIEventType::Action(ref action) => match action {
- Action::ViewMailbox(idx) => {
- self.new_cursor_pos.1 = *idx;
+ Action::ViewMailbox(idx_m) => {
+ self.new_cursor_pos.1 = *idx_m;
self.dirty = true;
self.refresh_mailbox(context);
return true;
diff --git a/ui/src/components/mail/mod.rs b/ui/src/components/mail/mod.rs
index 62f9243c..f3dbc19a 100644
--- a/ui/src/components/mail/mod.rs
+++ b/ui/src/components/mail/mod.rs
@@ -70,8 +70,7 @@ impl AccountMenu {
}
entries
},
- })
- .collect();
+ }).collect();
AccountMenu {
accounts,
dirty: true,
diff --git a/ui/src/components/mod.rs b/ui/src/components/mod.rs
index 4eba1579..a3ffa21c 100644
--- a/ui/src/components/mod.rs
+++ b/ui/src/components/mod.rs
@@ -32,6 +32,9 @@ pub use mail::*;
pub mod notifications;
+pub mod indexer;
+pub use self::indexer::*;
+
pub mod utilities;
pub use self::utilities::*;
diff --git a/ui/src/lib.rs b/ui/src/lib.rs
index 2482eec3..7a8ba50a 100644
--- a/ui/src/lib.rs
+++ b/ui/src/lib.rs
@@ -59,3 +59,6 @@ pub use components::*;
pub mod conf;
pub use conf::*;
+
+pub mod workers;
+pub use workers::*;
diff --git a/ui/src/state.rs b/ui/src/state.rs
index 23879f19..b9a6ef6f 100644
--- a/ui/src/state.rs
+++ b/ui/src/state.rs
@@ -100,14 +100,14 @@ impl Context {
pub fn restore_input(&self) {
self.input.restore(self.sender.clone());
}
- pub fn account_status(&mut self, idx_a: usize, idx_m: usize) -> result::Result<bool, usize> {
+ pub fn account_status(&mut self, idx_a: usize, idx_m: usize) -> result::Result<(), usize> {
match self.accounts[idx_a].status(idx_m) {
Ok(()) => {
self.replies.push_back(UIEvent {
id: 0,
event_type: UIEventType::MailboxUpdate((idx_a, idx_m)),
});
- Ok(true)
+ Ok(())
}
Err(n) => Err(n),
}
@@ -127,6 +127,7 @@ pub struct State {
entities: Vec<Entity>,
pub context: Context,
threads: FnvHashMap<thread::ThreadId, (chan::Sender<bool>, thread::JoinHandle<()>)>,
+ work_controller: WorkController,
}
impl Drop for State {
@@ -155,7 +156,7 @@ impl State {
pub fn new() -> Self {
/* Create a channel to communicate with other threads. The main process is the sole receiver.
* */
- let (sender, receiver) = chan::sync(::std::mem::size_of::<ThreadEvent>());
+ let (sender, receiv