Add threaded view, notifications, pager filter
parent
51813510b1
commit
e95cc4c1e9
|
@ -28,6 +28,7 @@ notify = "4.0.1"
|
|||
termion = "1.5.1"
|
||||
chan = "0.1.21"
|
||||
chan-signal = "0.3.1"
|
||||
notify-rust = "^3"
|
||||
|
||||
[profile.release]
|
||||
#lto = true
|
||||
|
|
10
src/bin.rs
10
src/bin.rs
|
@ -22,6 +22,7 @@ extern crate melib;
|
|||
#[macro_use]
|
||||
extern crate nom;
|
||||
extern crate termion;
|
||||
extern crate notify_rust;
|
||||
|
||||
pub mod ui;
|
||||
use ui::*;
|
||||
|
@ -69,10 +70,14 @@ fn main() {
|
|||
let menu = Entity {component: Box::new(AccountMenu::new(&state.context.accounts)) };
|
||||
let listing = MailListing::new();
|
||||
let b = Entity { component: Box::new(listing) };
|
||||
let window = Entity { component: Box::new(VSplit::new(menu,b,90)) };
|
||||
let window = Entity { component: Box::new(VSplit::new(menu, b, 80)) };
|
||||
let status_bar = Entity { component: Box::new(StatusBar::new(window)) };
|
||||
state.register_entity(status_bar);
|
||||
|
||||
|
||||
let xdg_notifications = Entity { component: Box::new(ui::components::notifications::XDGNotifications {}) };
|
||||
state.register_entity(xdg_notifications);
|
||||
|
||||
/* Keep track of the input mode. See ui::UIMode for details */
|
||||
let mut mode: UIMode = UIMode::Normal;
|
||||
'main: loop {
|
||||
|
@ -94,6 +99,7 @@ fn main() {
|
|||
UIMode::Normal => {
|
||||
match k {
|
||||
Key::Char('q') | Key::Char('Q') => {
|
||||
drop(state);
|
||||
break 'main;
|
||||
},
|
||||
Key::Char(';') => {
|
||||
|
@ -124,6 +130,8 @@ fn main() {
|
|||
}
|
||||
},
|
||||
ThreadEvent::RefreshMailbox { name : n } => {
|
||||
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Notification(n.clone())});
|
||||
state.redraw();
|
||||
/* Don't handle this yet. */
|
||||
eprintln!("Refresh mailbox {}", n);
|
||||
},
|
||||
|
|
|
@ -87,7 +87,7 @@ pub struct AccountSettings {
|
|||
pub folders: Vec<Folder>,
|
||||
format: String,
|
||||
pub sent_folder: String,
|
||||
threaded: bool,
|
||||
pub threaded: bool,
|
||||
}
|
||||
|
||||
impl AccountSettings {
|
||||
|
@ -157,8 +157,7 @@ impl Settings {
|
|||
if path.is_dir() {
|
||||
folders.push(Folder::new(path.to_str().unwrap().to_string(), path.file_name().unwrap().to_str().unwrap().to_string(), path_children));
|
||||
}
|
||||
//recurse_folders(&mut folders, &x.folders);
|
||||
eprintln!("folders is {:?}", folders);
|
||||
//folders.sort_by(|a, b| b.name.cmp(&a.name));
|
||||
s.insert(
|
||||
id.clone(),
|
||||
AccountSettings {
|
||||
|
@ -170,7 +169,6 @@ impl Settings {
|
|||
},
|
||||
);
|
||||
}
|
||||
eprintln!("pager settings are {:?}", fs.pager);
|
||||
|
||||
Settings { accounts: s, pager: fs.pager }
|
||||
}
|
||||
|
|
|
@ -13,6 +13,10 @@ fn eighty_percent () -> usize {
|
|||
80
|
||||
}
|
||||
|
||||
fn none() -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Settings for the pager function.
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct PagerSettings {
|
||||
|
@ -35,4 +39,9 @@ pub struct PagerSettings {
|
|||
/// Default: 80
|
||||
#[serde(default = "eighty_percent")]
|
||||
pub pager_ratio: usize,
|
||||
|
||||
/// A command to pipe mail output through for viewing in pager.
|
||||
/// Default: None
|
||||
#[serde(default = "none")]
|
||||
pub filter: Option<String>,
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ pub struct Account {
|
|||
|
||||
sent_folder: Option<usize>,
|
||||
|
||||
settings: AccountSettings,
|
||||
pub settings: AccountSettings,
|
||||
pub backend: Box<MailBackend>,
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ pub struct ImapOp {
|
|||
}
|
||||
|
||||
impl ImapOp {
|
||||
pub fn new(path: String) -> Self {
|
||||
pub fn new(_path: String) -> Self {
|
||||
ImapOp {
|
||||
}
|
||||
}
|
||||
|
@ -63,16 +63,16 @@ pub struct ImapType {
|
|||
|
||||
|
||||
impl MailBackend for ImapType {
|
||||
fn get(&self, folder: &Folder) -> Result<Vec<Envelope>> {
|
||||
fn get(&self, _folder: &Folder) -> Result<Vec<Envelope>> {
|
||||
unimplemented!();
|
||||
}
|
||||
fn watch(&self, sender: RefreshEventConsumer, folders: &[Folder]) -> () {
|
||||
fn watch(&self, _sender: RefreshEventConsumer, _folders: &[Folder]) -> () {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl ImapType {
|
||||
pub fn new(path: &str) -> Self {
|
||||
pub fn new(_path: &str) -> Self {
|
||||
ImapType {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ pub struct MboxOp {
|
|||
}
|
||||
|
||||
impl MboxOp {
|
||||
pub fn new(path: String) -> Self {
|
||||
pub fn new(_path: String) -> Self {
|
||||
MboxOp {
|
||||
}
|
||||
}
|
||||
|
@ -65,16 +65,16 @@ pub struct MboxType {
|
|||
|
||||
|
||||
impl MailBackend for MboxType {
|
||||
fn get(&self, folder: &Folder) -> Result<Vec<Envelope>> {
|
||||
fn get(&self, _folder: &Folder) -> Result<Vec<Envelope>> {
|
||||
unimplemented!();
|
||||
}
|
||||
fn watch(&self, sender: RefreshEventConsumer, folders: &[Folder]) -> () {
|
||||
fn watch(&self, _sender: RefreshEventConsumer, _folders: &[Folder]) -> () {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl MboxType {
|
||||
pub fn new(path: &str) -> Self {
|
||||
pub fn new(_path: &str) -> Self {
|
||||
MboxType {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ impl MailListing {
|
|||
|
||||
|
||||
pub fn new() -> Self {
|
||||
let mut content = CellBuffer::new(0, 0, Cell::with_char(' '));
|
||||
let content = CellBuffer::new(0, 0, Cell::with_char(' '));
|
||||
MailListing {
|
||||
cursor_pos: (0, 1, 0),
|
||||
new_cursor_pos: (0, 0, 0),
|
||||
|
@ -50,11 +50,13 @@ impl MailListing {
|
|||
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;
|
||||
|
||||
let threaded = context.accounts[self.cursor_pos.0].settings.threaded;
|
||||
// Get mailbox as a reference.
|
||||
let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1].as_ref().unwrap().as_ref().unwrap();
|
||||
// Inform State that we changed the current folder view.
|
||||
context.replies.push_back(UIEvent { id: 0, event_type: UIEventType::RefreshMailbox(mailbox.clone()) });
|
||||
context.replies.push_back(UIEvent { id: 0, event_type: UIEventType::RefreshMailbox((self.cursor_pos.0, self.cursor_pos.1)) });
|
||||
|
||||
self.length = mailbox.len();
|
||||
let mut content = CellBuffer::new(MAX_COLS, self.length+1, Cell::with_char(' '));
|
||||
|
@ -69,6 +71,79 @@ impl MailListing {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
// TODO: Fix the threaded hell and refactor stuff into seperate functions and/or modules.
|
||||
if threaded {
|
||||
let mut indentations: Vec<bool> = Vec::with_capacity(6);
|
||||
/* Draw threaded view. */
|
||||
let mut iter = mailbox
|
||||
.threaded_collection
|
||||
.iter()
|
||||
.enumerate()
|
||||
.peekable();
|
||||
/* This is just a desugared for loop so that we can use .peek() */
|
||||
while let Some((idx, i)) = iter.next() {
|
||||
let container = mailbox.get_thread(*i);
|
||||
let indentation = container.get_indentation();
|
||||
|
||||
assert_eq!(container.has_message(), true);
|
||||
match iter.peek() {
|
||||
Some(&(_, x))
|
||||
if mailbox.get_thread(*x).get_indentation() == indentation =>
|
||||
{
|
||||
indentations.pop();
|
||||
indentations.push(true);
|
||||
}
|
||||
_ => {
|
||||
indentations.pop();
|
||||
indentations.push(false);
|
||||
}
|
||||
}
|
||||
if container.has_sibling() {
|
||||
indentations.pop();
|
||||
indentations.push(true);
|
||||
}
|
||||
let envelope : &Envelope = &mailbox.collection[container.get_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 idx % 2 == 0 {
|
||||
Color::Byte(236)
|
||||
} else {
|
||||
Color::Default
|
||||
}
|
||||
let x = write_string_to_grid(&MailListing::make_thread_entry(envelope, idx, indentation, container, idx == self.cursor_pos.2, &indentations),
|
||||
&mut content,
|
||||
fg_color,
|
||||
bg_color,
|
||||
((0, idx) , (MAX_COLS-1, idx)));
|
||||
for x in x..MAX_COLS {
|
||||
content[(x,idx)].set_ch(' ');
|
||||
content[(x,idx)].set_bg(bg_color);
|
||||
}
|
||||
|
||||
match iter.peek() {
|
||||
Some(&(_, x))
|
||||
if mailbox.get_thread(*x).get_indentation() > indentation =>
|
||||
{
|
||||
indentations.push(false);
|
||||
}
|
||||
Some(&(_, x))
|
||||
if mailbox.get_thread(*x).get_indentation() < indentation =>
|
||||
{
|
||||
for _ in 0..(indentation - mailbox.get_thread(*x).get_indentation()) {
|
||||
indentations.pop();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// Populate `CellBuffer` with every entry.
|
||||
// TODO: Lazy load?
|
||||
let mut idx = 0;
|
||||
|
@ -108,6 +183,8 @@ impl MailListing {
|
|||
idx+=1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
self.content = content;
|
||||
}
|
||||
fn highlight_line(&self, grid: &mut CellBuffer, area: Area, idx: usize, context: &mut Context) {
|
||||
|
@ -180,13 +257,69 @@ impl MailListing {
|
|||
/// Create a pager for the `Envelope` currently under the cursor.
|
||||
fn draw_mail_view(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
{
|
||||
let threaded = context.accounts[self.cursor_pos.0].settings.threaded;
|
||||
let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1].as_ref().unwrap().as_ref().unwrap();
|
||||
let envelope: &Envelope = &mailbox.collection[self.cursor_pos.2];
|
||||
let envelope: &Envelope = if threaded {
|
||||
let i = mailbox.get_threaded_mail(self.cursor_pos.2);
|
||||
&mailbox.collection[i]
|
||||
} else {
|
||||
&mailbox.collection[self.cursor_pos.2]
|
||||
};
|
||||
|
||||
self.pager = Some(Pager::new(envelope));
|
||||
let pager_filter = context.settings.pager.filter.clone();
|
||||
self.pager = Some(Pager::new(&envelope, pager_filter));
|
||||
}
|
||||
self.pager.as_mut().map(|p| p.draw(grid, area, context));
|
||||
}
|
||||
fn make_thread_entry(envelope: &Envelope, idx: usize, indent: usize,
|
||||
container: &Container, highlight: bool, indentations: &Vec<bool>) -> String {
|
||||
let has_sibling = container.has_sibling();
|
||||
let has_parent = container.has_parent();
|
||||
let show_subject = container.get_show_subject();
|
||||
|
||||
let fg_color = if !envelope.is_seen() {
|
||||
Color::Byte(0)
|
||||
} else {
|
||||
Color::Default
|
||||
};
|
||||
let bg_color = if highlight {
|
||||
if !envelope.is_seen() {
|
||||
Color::Byte(252)
|
||||
} else if idx % 2 == 0 {
|
||||
Color::Byte(236)
|
||||
} else {
|
||||
Color::Default
|
||||
}
|
||||
} else {
|
||||
Color::Byte(246)
|
||||
};
|
||||
let mut s = format!("{} {} ", idx, &envelope.get_datetime().format("%Y-%m-%d %H:%M:%S").to_string()); // {} {:.85}",idx,),e.get_subject())
|
||||
for i in 0..indent {
|
||||
if indentations.len() > i && indentations[i]
|
||||
{
|
||||
s.push('│');
|
||||
} else {
|
||||
s.push(' ');
|
||||
}
|
||||
if i > 0 {
|
||||
s.push(' ');
|
||||
}
|
||||
}
|
||||
if indent > 0 {
|
||||
if has_sibling && has_parent {
|
||||
s.push('├');
|
||||
} else if has_sibling {
|
||||
s.push('┬');
|
||||
} else {
|
||||
s.push('└');
|
||||
}
|
||||
s.push('─'); s.push('>');
|
||||
}
|
||||
if show_subject {
|
||||
s.push_str(&format!("{:.85}", envelope.get_subject()));
|
||||
}
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for MailListing {
|
||||
|
@ -255,8 +388,14 @@ impl Component for MailListing {
|
|||
|
||||
/* Draw header */
|
||||
{
|
||||
let threaded = context.accounts[self.cursor_pos.0].settings.threaded;
|
||||
let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1].as_ref().unwrap().as_ref().unwrap();
|
||||
let envelope: &Envelope = &mailbox.collection[self.cursor_pos.2];
|
||||
let envelope: &Envelope = if threaded {
|
||||
let i = mailbox.get_threaded_mail(self.cursor_pos.2);
|
||||
&mailbox.collection[i]
|
||||
} else {
|
||||
&mailbox.collection[self.cursor_pos.2]
|
||||
};
|
||||
|
||||
let x = write_string_to_grid(&format!("Date: {}", envelope.get_date_as_str()),
|
||||
grid,
|
||||
|
@ -369,11 +508,13 @@ impl Component for MailListing {
|
|||
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;
|
||||
self.dirty = true;
|
||||
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;
|
||||
self.dirty = true;
|
||||
self.refresh_mailbox(context);
|
||||
},
|
||||
|
@ -438,16 +579,16 @@ impl AccountMenu {
|
|||
cursor: None,
|
||||
}
|
||||
}
|
||||
fn highlight_folder(&mut self, m: &Mailbox) {
|
||||
self.dirty = true;
|
||||
self.cursor = None;
|
||||
}
|
||||
fn print_account(&self, grid: &mut CellBuffer, area: Area, a: &AccountMenuEntry) -> usize {
|
||||
if !is_valid_area!(area) {
|
||||
eprintln!("BUG: invalid area in print_account");
|
||||
}
|
||||
let upper_left = upper_left!(area);
|
||||
let bottom_right = bottom_right!(area);
|
||||
|
||||
|
||||
let highlight = self.cursor.map(|(x,_)| x == a.index).unwrap_or(false);
|
||||
|
||||
let mut parents: Vec<Option<usize>> = vec!(None; a.entries.len());
|
||||
|
||||
for (idx, e) in a.entries.iter().enumerate() {
|
||||
|
@ -461,10 +602,11 @@ impl AccountMenu {
|
|||
roots.push(idx);
|
||||
}
|
||||
}
|
||||
eprintln!("roots is {:?}", roots);
|
||||
|
||||
let mut inc = 0;
|
||||
let mut depth = String::from("");
|
||||
let mut s = String::from(format!("\n\n {}\n", a.name));
|
||||
let mut s = String::from(format!("{}\n", a.name));
|
||||
fn pop(depth: &mut String) {
|
||||
depth.pop();
|
||||
depth.pop();
|
||||
|
@ -489,8 +631,8 @@ impl AccountMenu {
|
|||
}
|
||||
for r in roots {
|
||||
print(r, &parents, &mut depth, &a.entries, &mut s, &mut inc);
|
||||
|
||||
}
|
||||
eprintln!("s = {}", s);
|
||||
|
||||
let lines: Vec<&str> = s.lines().collect();
|
||||
let lines_len = lines.len();
|
||||
|
@ -504,14 +646,40 @@ impl AccountMenu {
|
|||
} else {
|
||||
format!("{}", lines[idx])
|
||||
};
|
||||
write_string_to_grid(&s,
|
||||
let color_fg = if highlight {
|
||||
if idx > 1 && self.cursor.unwrap().1 == idx - 2 {
|
||||
Color::Byte(233)
|
||||
} else {
|
||||
Color::Byte(15)
|
||||
}
|
||||
} else {
|
||||
Color::Default
|
||||
};
|
||||
|
||||
let color_bg = if highlight {
|
||||
if idx > 1 && self.cursor.unwrap().1 == idx - 2 {
|
||||
Color::Byte(15)
|
||||
} else {
|
||||
Color::Byte(233)
|
||||
}
|
||||
} else {
|
||||
Color::Default
|
||||
};
|
||||
|
||||
let x = write_string_to_grid(&s,
|
||||
grid,
|
||||
Color::Byte(30),
|
||||
Color::Default,
|
||||
color_fg,
|
||||
color_bg,
|
||||
(set_y(upper_left, y), bottom_right));
|
||||
|
||||
if highlight && idx > 1 && self.cursor.unwrap().1 == idx - 2 {
|
||||
change_colors(grid, ((x, y),(get_x(bottom_right)+1, y)), color_fg , color_bg);
|
||||
} else {
|
||||
change_colors(grid, ((x, y),set_y(bottom_right, y)), color_fg , color_bg);
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
idx
|
||||
idx - 1
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -525,19 +693,22 @@ impl Component for AccountMenu {
|
|||
let upper_left = upper_left!(area);
|
||||
let bottom_right = bottom_right!(area);
|
||||
self.dirty = false;
|
||||
let mut y = get_y(upper_left);
|
||||
let mut y = get_y(upper_left) + 1;
|
||||
for a in &self.accounts {
|
||||
eprintln!("\n\naccount: {:?}\n\n", a);
|
||||
y += self.print_account(grid,
|
||||
(set_y(upper_left, y), bottom_right),
|
||||
&a);
|
||||
}
|
||||
|
||||
eprintln!("\n\naccountentries: {:?}\n\n", self.accounts);
|
||||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
fn process_event(&mut self, event: &UIEvent, _context: &mut Context) {
|
||||
match event.event_type {
|
||||
UIEventType::RefreshMailbox(ref m) => {
|
||||
self.highlight_folder(m);
|
||||
UIEventType::RefreshMailbox(c) => {
|
||||
self.cursor = Some(c);
|
||||
self.dirty = true;
|
||||
},
|
||||
UIEventType::ChangeMode(UIMode::Normal) => {
|
||||
self.dirty = true;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
pub mod utilities;
|
||||
pub mod mail;
|
||||
pub mod notifications;
|
||||
|
||||
use super::*;
|
||||
pub use utilities::*;
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
use notify_rust::Notification as notify_Notification;
|
||||
|
||||
use ui::*;
|
||||
use ui::components::*;
|
||||
pub struct XDGNotifications {}
|
||||
|
||||
impl Component for XDGNotifications {
|
||||
fn draw(&mut self, _grid: &mut CellBuffer, _area: Area, _context: &mut Context) {
|
||||
|
||||
}
|
||||
fn process_event(&mut self, event: &UIEvent, _context: &mut Context) {
|
||||
match event.event_type {
|
||||
UIEventType::Notification(ref t) => {
|
||||
notify_Notification::new()
|
||||
.summary("Refresh Event")
|
||||
.body(t)
|
||||
.icon("dialog-information")
|
||||
.show().unwrap();
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -126,21 +126,31 @@ pub struct Pager {
|
|||
}
|
||||
|
||||
impl Pager {
|
||||
pub fn new(mail: &Envelope) -> Self {
|
||||
let text = mail.get_body().get_text();
|
||||
pub fn new(mail: &Envelope, pager_filter: Option<String>) -> Self {
|
||||
let mut text = mail.get_body().get_text();
|
||||
if let Some(bin) = pager_filter {
|
||||
use std::io::Write;
|
||||
use std::process::{Command, Stdio};
|
||||
eprintln!("{}", bin);
|
||||
let mut filter_child = Command::new(bin)
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.expect("Failed to start pager filter process");
|
||||
{
|
||||
let mut stdin =
|
||||
filter_child.stdin.as_mut().expect("failed to open stdin");
|
||||
stdin.write_all(text.as_bytes()).expect("Failed to write to stdin");
|
||||
}
|
||||
|
||||
|
||||
text = String::from_utf8_lossy(&filter_child.wait_with_output().expect("Failed to wait on filter").stdout).to_string();
|
||||
}
|
||||
let lines: Vec<&str> = text.trim().split('\n').collect();
|
||||
let height = lines.len();
|
||||
let width = lines.iter().map(|l| l.len()).max().unwrap_or(0);
|
||||
let mut content = CellBuffer::new(width, height, Cell::with_char(' '));
|
||||
if width > 0 {
|
||||
for (i, l) in lines.iter().enumerate() {
|
||||
write_string_to_grid(l,
|
||||
&mut content,
|
||||
Color::Default,
|
||||
Color::Default,
|
||||
((0, i), (width -1, i)));
|
||||
}
|
||||
}
|
||||
Pager::print_string(&mut content, &text);
|
||||
Pager {
|
||||
cursor_pos: 0,
|
||||
height: height,
|
||||
|
@ -149,6 +159,19 @@ impl Pager {
|
|||
content: content,
|
||||
}
|
||||
}
|
||||
pub fn print_string(content: &mut CellBuffer, s: &str) {
|
||||
let lines: Vec<&str> = s.trim().split('\n').collect();
|
||||
let width = lines.iter().map(|l| l.len()).max().unwrap_or(0);
|
||||
if width > 0 {
|
||||
for (i, l) in lines.iter().enumerate() {
|
||||
write_string_to_grid(l,
|
||||
content,
|
||||
Color::Default,
|
||||
Color::Default,
|
||||
((0, i), (width -1, i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Pager {
|
||||
|
@ -163,6 +186,7 @@ impl Component for Pager {
|
|||
if self.height == 0 || self.height == self.cursor_pos || self.width == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
clear_area(grid,
|
||||
(upper_left, bottom_right));
|
||||
context.dirty_areas.push_back((upper_left, bottom_right));
|
||||
|
@ -228,18 +252,20 @@ impl StatusBar {
|
|||
clear_area(grid, area);
|
||||
write_string_to_grid(&self.status,
|
||||
grid,
|
||||
Color::Byte(36),
|
||||
Color::Default,
|
||||
Color::Byte(123),
|
||||
Color::Byte(26),
|
||||
area);
|
||||
change_colors(grid, area, Color::Byte(123), Color::Byte(26));
|
||||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
fn draw_execute_bar(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
clear_area(grid, area);
|
||||
write_string_to_grid(&self.ex_buffer,
|
||||
grid,
|
||||
Color::Byte(124),
|
||||
Color::Default,
|
||||
Color::Byte(219),
|
||||
Color::Byte(88),
|
||||
area);
|
||||
change_colors(grid, area, Color::Byte(219), Color::Byte(88));
|
||||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
}
|
||||
|
@ -279,7 +305,8 @@ impl Component for StatusBar {
|
|||
fn process_event(&mut self, event: &UIEvent, context: &mut Context) {
|
||||
self.container.rcv_event(event, context);
|
||||
match event.event_type {
|
||||
UIEventType::RefreshMailbox(ref m) => {
|
||||
UIEventType::RefreshMailbox((idx_a, idx_f)) => {
|
||||
let m = &context.accounts[idx_a][idx_f].as_ref().unwrap().as_ref().unwrap();
|
||||
self.status = format!("{} |Mailbox: {}, Messages: {}, New: {}", self.mode, m.folder.get_name(), m.collection.len(), m.collection.iter().filter(|e| !e.is_seen()).count());
|
||||
self.dirty = true;
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
use std;
|
||||
use nom::digit;
|
||||
|
||||
|
||||
named!(usize_c<usize>,
|
||||
map_res!(map_res!(ws!(digit), std::str::from_utf8), std::str::FromStr::from_str));
|
||||
|
||||
named!(pub goto<usize>,
|
||||
preceded!(tag!("b "),
|
||||
call!(usize_c))
|
||||
);
|
|
@ -93,12 +93,13 @@ impl From<RefreshEvent> for ThreadEvent {
|
|||
pub enum UIEventType {
|
||||
Input(Key),
|
||||
ExInput(Key),
|
||||
RefreshMailbox(Mailbox),
|
||||
RefreshMailbox((usize,usize)),
|
||||
//Quit?
|
||||
Resize,
|
||||
ChangeMailbox(usize),
|
||||
ChangeMode(UIMode),
|
||||
Command(String),
|
||||
Notification(String),
|
||||
}
|
||||
|
||||
|
||||
|
@ -123,6 +124,13 @@ impl fmt::Display for UIMode {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Notification {
|
||||
title: String,
|
||||
content: String,
|
||||
|
||||
timestamp: std::time::Instant,
|
||||
}
|
||||
|
||||
pub struct Context {
|
||||
pub accounts: Vec<Account>,
|
||||
settings: Settings,
|
||||
|
@ -140,6 +148,7 @@ impl Context {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct State<W: Write> {
|
||||
cols: usize,
|
||||
rows: usize,
|
||||
|
@ -169,6 +178,8 @@ impl<W: Write> State<W> {
|
|||
let termrows = termsize.map(|(_,h)| h);
|
||||
let cols = termcols.unwrap_or(0) as usize;
|
||||
let rows = termrows.unwrap_or(0) as usize;
|
||||
let mut accounts: Vec<Account> = settings.accounts.iter().map(|(n, a_s)| { Account::new(n.to_string(), a_s.clone(), &backends) }).collect();
|
||||
accounts.sort_by(|a,b| a.get_name().cmp(&b.get_name()) );
|
||||
let mut s = State {
|
||||
cols: cols,
|
||||
rows: rows,
|
||||
|
@ -178,7 +189,7 @@ impl<W: Write> State<W> {
|
|||
entities: Vec::with_capacity(1),
|
||||
|
||||
context: Context {
|
||||
accounts: settings.accounts.iter().map(|(n, a_s)| { Account::new(n.to_string(), a_s.clone(), &backends) }).collect(),
|
||||
accounts: accounts,
|
||||
backends: backends,
|
||||
settings: settings,
|
||||
dirty_areas: VecDeque::with_capacity(5),
|
||||
|
@ -197,7 +208,6 @@ impl<W: Write> State<W> {
|
|||
s
|
||||
}
|
||||
pub fn update_size(&mut self) {
|
||||
/* update dimensions. TODO: Only do that in size change events. ie SIGWINCH */
|
||||
let termsize = termion::terminal_size().ok();
|
||||
let termcols = termsize.map(|(w,_)| w);
|
||||
let termrows = termsize.map(|(_,h)| h);
|
||||
|
@ -304,17 +314,20 @@ impl<W: Write> State<W> {
|
|||
self.entities[i].rcv_event(&event, &mut self.context);
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to load a mailbox's content
|
||||
pub fn refresh_mailbox(&mut self, account_idx: usize, folder_idx: usize) {
|
||||
let mailbox = match &mut self.context.accounts[account_idx][folder_idx] {
|
||||
Some(Ok(v)) => { Some(v.clone()) },
|
||||
Some(Err(e)) => { eprintln!("error {:?}", e); None },
|
||||
None => { eprintln!("None"); None },
|
||||
let flag = match &mut self.context.accounts[account_idx][folder_idx] {
|
||||
Some(Ok(_)) => {
|
||||
true
|
||||
},
|
||||
Some(Err(e)) => { eprintln!("error {:?}", e); false },
|
||||
None => { eprintln!("None"); false },
|
||||
};
|
||||
if let Some(m) = mailbox {
|
||||
self.rcv_event(UIEvent { id: 0, event_type: UIEventType::RefreshMailbox(m) });
|
||||
}
|
||||
if flag {
|
||||
|
||||
self.rcv_event(UIEvent { id: 0, event_type: UIEventType::RefreshMailbox((account_idx, folder_idx)) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue