Index scrolling, dummy backends, and some pager settings
parent
b11eb1e494
commit
8c7a0ae540
101
src/bin.rs
101
src/bin.rs
|
@ -30,6 +30,8 @@ pub use melib::*;
|
|||
use std::sync::mpsc::{sync_channel, SyncSender, Receiver};
|
||||
use std::thread;
|
||||
use std::io::{stdout, stdin, };
|
||||
use std::collections::VecDeque;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
fn main() {
|
||||
/* Lock all stdios */
|
||||
|
@ -48,16 +50,84 @@ fn main() {
|
|||
|
||||
let (sender, receiver): (SyncSender<ThreadEvent>, Receiver<ThreadEvent>) = sync_channel(::std::mem::size_of::<ThreadEvent>());
|
||||
{
|
||||
let mut cmd_queue = VecDeque::with_capacity(5);
|
||||
let sender = sender.clone();
|
||||
thread::Builder::new().name("input-thread".to_string()).spawn(move || {
|
||||
get_events(stdin, |k| { sender.send(ThreadEvent::Input(k)).unwrap(); })
|
||||
}).unwrap();
|
||||
get_events(stdin, move | k| {
|
||||
eprintln!("{:?}: queue is {:?}", Instant::now(), cmd_queue);
|
||||
let front: Option<(Instant, char)> = cmd_queue.front().map(|v: &(Instant, char)| { v.clone() });
|
||||
let back: Option<(Instant, char)> = cmd_queue.back().map(|v: &(Instant, char)| { v.clone() });
|
||||
let mut push: Option<(Instant, char)> = None;
|
||||
|
||||
if let Key::Char(v) = k {
|
||||
if v == 'g' {
|
||||
eprintln!("{:?}: got 'g' in thread",Instant::now());
|
||||
push = Some((Instant::now(), v));
|
||||
} else if v > '/' && v < ':' {
|
||||
eprintln!("{:?}: got '{}' in thread", Instant::now(), v);
|
||||
if let Some((_, 'g')) = front {
|
||||
eprintln!("{:?}: 'g' is front", Instant::now());
|
||||
match back {
|
||||
Some((i, cmd)) if cmd != 'g' => {
|
||||
let (i, cmd) = back.unwrap();
|
||||
let n = cmd as u8;
|
||||
eprintln!("{:?}: check for num c={}, n={}", Instant::now(),cmd, n);
|
||||
if n > 0x2f && n < 0x3a {
|
||||
eprintln!("{:?}: got a num {}", Instant::now(), cmd);
|
||||
let now = Instant::now();
|
||||
if now - i < Duration::from_millis(300) {
|
||||
push = Some((now,cmd));
|
||||
let ten_millis = Duration::from_millis(10);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
Some((i, cmd)) => {
|
||||
let n = v as u8;
|
||||
eprintln!("{:?}: check for num c={}, n={}", Instant::now(),v, n);
|
||||
if n > 0x2f && n < 0x3a {
|
||||
eprintln!("{:?}: got a num {}", Instant::now(), v);
|
||||
let now = Instant::now();
|
||||
if now - i < Duration::from_millis(300) {
|
||||
push = Some((now,v));
|
||||
}
|
||||
cmd_queue.pop_front();
|
||||
let mut s = String::with_capacity(3);
|
||||
for (_, c) in cmd_queue.iter() {
|
||||
s.push(*c);
|
||||
}
|
||||
s.push(v);
|
||||
let times = s.parse::<usize>();
|
||||
eprintln!("{:?}: parsed {:?}", Instant::now(), times);
|
||||
if let Ok(g) = times {
|
||||
sender.send(ThreadEvent::GoCmd(g)).unwrap();
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {},
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
if let Some(v) = push {
|
||||
cmd_queue.push_back(v);
|
||||
return;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
if push.is_none() {sender.send(ThreadEvent::Input(k)).unwrap();}
|
||||
})}).unwrap();
|
||||
}
|
||||
|
||||
//let mailbox = Mailbox::new("/home/epilys/Downloads/rust/nutt/Inbox4");
|
||||
let mut j = 0;
|
||||
let folder_length = set.accounts["norn"].folders.len();
|
||||
let mut account = Account::new("norn".to_string(), set.accounts["norn"].clone(), backends);
|
||||
let folder_length = set.accounts["test_account"].folders.len();
|
||||
let mut account = Account::new("test_account".to_string(), set.accounts["test_account"].clone(), backends);
|
||||
{
|
||||
let sender = sender.clone();
|
||||
account.watch(RefreshEventConsumer::new(Box::new(move |r| {
|
||||
|
@ -69,13 +139,14 @@ fn main() {
|
|||
|
||||
|
||||
|
||||
let mut state = State::new(_stdout);
|
||||
let mut state = State::new(_stdout, set);
|
||||
|
||||
let a = Entity {component: Box::new(AccountMenu::new(&account)) };
|
||||
let listing = MailListing::new(Mailbox::new_dummy());
|
||||
let b = Entity { component: Box::new(listing) };
|
||||
let window = Entity { component: Box::new(VSplit::new(a,b,90)) };
|
||||
state.register_entity(window);
|
||||
|
||||
state.render();
|
||||
'main: loop {
|
||||
let mailbox = &mut account[j];
|
||||
|
@ -121,13 +192,33 @@ fn main() {
|
|||
j -= 1;
|
||||
break 'inner;
|
||||
},
|
||||
Key::Char(k @ 'g') => {
|
||||
},
|
||||
Key::Char(v) if v > '/' && v < ':' => {
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
},
|
||||
ThreadEvent::RefreshMailbox { name : n } => {
|
||||
eprintln!("Refresh mailbox {}", n);
|
||||
},
|
||||
ThreadEvent::UIEventType(e) => {
|
||||
state.rcv_event(UIEvent { id: 0, event_type: e});
|
||||
state.render();
|
||||
},
|
||||
ThreadEvent::GoCmd(v) => {
|
||||
eprintln!("got go cmd with {:?}", v);
|
||||
},
|
||||
}
|
||||
/*
|
||||
if let Some((inst, 'g')) = cmd_queue.front() {
|
||||
eprintln!("g at front");
|
||||
if (Instant::now - inst >= Duration::from_millis(300)) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,11 @@
|
|||
extern crate config;
|
||||
extern crate xdg;
|
||||
extern crate serde;
|
||||
pub mod pager;
|
||||
|
||||
|
||||
use pager::PagerSettings;
|
||||
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
|
@ -63,9 +68,12 @@ struct FileAccount {
|
|||
threaded: bool,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Deserialize, Default)]
|
||||
struct FileSettings {
|
||||
accounts: HashMap<String, FileAccount>,
|
||||
#[serde(default)]
|
||||
pager: PagerSettings,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -89,6 +97,8 @@ impl AccountSettings {
|
|||
#[derive(Debug)]
|
||||
pub struct Settings {
|
||||
pub accounts: HashMap<String, AccountSettings>,
|
||||
|
||||
pub pager: PagerSettings,
|
||||
}
|
||||
|
||||
|
||||
|
@ -166,6 +176,6 @@ impl Settings {
|
|||
);
|
||||
}
|
||||
|
||||
Settings { accounts: s }
|
||||
Settings { accounts: s, pager: fs.pager }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
fn false_val () -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn zero_val () -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Default)]
|
||||
pub struct PagerSettings {
|
||||
#[serde(default = "zero_val")]
|
||||
pager_context: usize,
|
||||
#[serde(default = "false_val")]
|
||||
pager_stop: bool,
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* meli - mailbox module.
|
||||
*
|
||||
* Copyright 2017 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/>.
|
||||
*/
|
||||
|
||||
use mailbox::email::{Envelope, Flag};
|
||||
use error::{Result};
|
||||
use mailbox::backends::{BackendOp, MailBackend, RefreshEventConsumer};
|
||||
use conf::Folder;
|
||||
|
||||
|
||||
/// `BackendOp` implementor for Imap
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct ImapOp {
|
||||
}
|
||||
|
||||
impl ImapOp {
|
||||
pub fn new(path: String) -> Self {
|
||||
ImapOp {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BackendOp for ImapOp {
|
||||
fn description(&self) -> String {
|
||||
unimplemented!();
|
||||
}
|
||||
fn as_bytes(&mut self) -> Result<&[u8]> {
|
||||
unimplemented!();
|
||||
}
|
||||
fn fetch_headers(&mut self) -> Result<&[u8]> {
|
||||
unimplemented!();
|
||||
}
|
||||
fn fetch_body(&mut self) -> Result<&[u8]> {
|
||||
unimplemented!();
|
||||
}
|
||||
fn fetch_flags(&self) -> Flag {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Imap backend
|
||||
#[derive(Debug)]
|
||||
pub struct ImapType {
|
||||
}
|
||||
|
||||
|
||||
impl MailBackend for ImapType {
|
||||
fn get(&self, folder: &Folder) -> Result<Vec<Envelope>> {
|
||||
unimplemented!();
|
||||
}
|
||||
fn watch(&self, sender: RefreshEventConsumer, folders: &[Folder]) -> () {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl ImapType {
|
||||
pub fn new(path: &str) -> Self {
|
||||
ImapType {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -158,6 +158,7 @@ impl MailBackend for MaildirType {
|
|||
continue;
|
||||
}
|
||||
let mut p = PathBuf::from(&f.get_path());
|
||||
eprintln!("watching {:?}", f);
|
||||
p.push("cur");
|
||||
watcher.watch(&p, RecursiveMode::NonRecursive).unwrap();
|
||||
p.pop();
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* meli - mailbox module.
|
||||
*
|
||||
* Copyright 2017 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/>.
|
||||
*/
|
||||
|
||||
use mailbox::email::{Envelope, Flag};
|
||||
use error::{Result};
|
||||
use mailbox::backends::{BackendOp, MailBackend, RefreshEventConsumer};
|
||||
use conf::Folder;
|
||||
|
||||
|
||||
|
||||
|
||||
/// `BackendOp` implementor for Mbox
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct MboxOp {
|
||||
}
|
||||
|
||||
impl MboxOp {
|
||||
pub fn new(path: String) -> Self {
|
||||
MboxOp {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BackendOp for MboxOp {
|
||||
fn description(&self) -> String {
|
||||
unimplemented!();
|
||||
}
|
||||
fn as_bytes(&mut self) -> Result<&[u8]> {
|
||||
unimplemented!();
|
||||
}
|
||||
fn fetch_headers(&mut self) -> Result<&[u8]> {
|
||||
unimplemented!();
|
||||
}
|
||||
fn fetch_body(&mut self) -> Result<&[u8]> {
|
||||
unimplemented!();
|
||||
}
|
||||
fn fetch_flags(&self) -> Flag {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Mbox backend
|
||||
#[derive(Debug)]
|
||||
pub struct MboxType {
|
||||
}
|
||||
|
||||
|
||||
impl MailBackend for MboxType {
|
||||
fn get(&self, folder: &Folder) -> Result<Vec<Envelope>> {
|
||||
unimplemented!();
|
||||
}
|
||||
fn watch(&self, sender: RefreshEventConsumer, folders: &[Folder]) -> () {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl MboxType {
|
||||
pub fn new(path: &str) -> Self {
|
||||
MboxType {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,10 +19,14 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
pub mod maildir;
|
||||
pub mod mbox;
|
||||
pub mod imap;
|
||||
|
||||
use conf::Folder;
|
||||
use mailbox::email::{Envelope, Flag};
|
||||
use mailbox::backends::maildir::MaildirType;
|
||||
use mailbox::backends::mbox::MboxType;
|
||||
use mailbox::backends::imap::ImapType;
|
||||
use error::Result;
|
||||
use std::fmt;
|
||||
|
||||
|
@ -43,6 +47,8 @@ impl Backends {
|
|||
map: FnvHashMap::with_capacity_and_hasher(1, Default::default())
|
||||
};
|
||||
b.register("maildir".to_string(), Box::new(|| Box::new(MaildirType::new(""))));
|
||||
b.register("mbox".to_string(), Box::new(|| Box::new(MboxType::new(""))));
|
||||
b.register("imap".to_string(), Box::new(|| Box::new(ImapType::new(""))));
|
||||
b
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ fn make_entry_string(e: &Envelope, idx: usize) -> String {
|
|||
format!("{} {} {:.85}",idx,&e.get_datetime().format("%Y-%m-%d %H:%M:%S").to_string(),e.get_subject())
|
||||
}
|
||||
|
||||
const MAX_WIDTH: usize = 500;
|
||||
const MAX_cols: usize = 500;
|
||||
|
||||
/// A list of all mail (`Envelope`s) in a `Mailbox`. On `\n` it opens the `Envelope` content in a
|
||||
/// `Pager`.
|
||||
|
@ -34,7 +34,7 @@ impl MailListing {
|
|||
cursor_pos: 0,
|
||||
new_cursor_pos: 0,
|
||||
length: length,
|
||||
content: CellBuffer::new(MAX_WIDTH, length+1, Cell::with_char(' ')),
|
||||
content: CellBuffer::new(MAX_cols, length+1, Cell::with_char(' ')),
|
||||
dirty: false,
|
||||
unfocused: false,
|
||||
mailbox: mailbox,
|
||||
|
@ -52,33 +52,51 @@ impl MailListing {
|
|||
write_string_to_grid(&format!("Folder `{}` is empty.", self.mailbox.folder.get_name()), grid, Color::Default, Color::Default, upper_left, upper_left);
|
||||
return;
|
||||
}
|
||||
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);
|
||||
|
||||
|
||||
/* 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 {
|
||||
if self.cursor_pos != self.new_cursor_pos && prev_page_no == page_no {
|
||||
for idx in [self.cursor_pos, self.new_cursor_pos].iter() {
|
||||
if *idx >= self.length {
|
||||
continue; //bounds check
|
||||
}
|
||||
let color = if self.cursor_pos == *idx { if *idx % 2 == 0 { Color::Byte(236) } else {Color::Default } } else { Color::Byte(246) };
|
||||
let x = write_string_to_grid(&make_entry_string(&self.mailbox.collection[*idx], *idx), grid, Color::Default, color, set_y(upper_left, get_y(upper_left) + *idx), bottom_right);
|
||||
for x in x..get_x(bottom_right)+1 {
|
||||
grid[(x,get_y(upper_left)+idx)].set_ch(' ');
|
||||
grid[(x,get_y(upper_left)+idx)].set_bg(color);
|
||||
let x = write_string_to_grid(&make_entry_string(&self.mailbox.collection[*idx], *idx), grid, Color::Default, color, set_y(upper_left, get_y(upper_left)+(*idx % rows)), bottom_right);
|
||||
for x in x..=get_x(bottom_right) {
|
||||
grid[(x,get_y(upper_left)+(*idx % rows))].set_ch(' ');
|
||||
grid[(x,get_y(upper_left)+(*idx % rows))].set_bg(color);
|
||||
}
|
||||
}
|
||||
self.cursor_pos = self.new_cursor_pos;
|
||||
return;
|
||||
} else if self.cursor_pos != self.new_cursor_pos {
|
||||
self.cursor_pos = self.new_cursor_pos;
|
||||
}
|
||||
|
||||
let mut idx = 0;
|
||||
for y in get_y(upper_left)..get_y(bottom_right) {
|
||||
if idx == self.length {
|
||||
let mut idx = page_no*rows;
|
||||
for y in get_y(upper_left)..=get_y(bottom_right) {
|
||||
if idx >= self.length {
|
||||
clear_area(grid, set_y(upper_left, y), bottom_right);
|
||||
break;
|
||||
}
|
||||
/* Write an entire line for each envelope entry. */
|
||||
|
||||
let color = if self.cursor_pos == idx { Color::Byte(246) } else { if idx % 2 == 0 { Color::Byte(236) } else {Color::Default } };
|
||||
let color = if self.cursor_pos == idx {
|
||||
Color::Byte(246)
|
||||
} else {
|
||||
if idx % 2 == 0 {
|
||||
Color::Byte(236)
|
||||
} else {
|
||||
Color::Default
|
||||
}
|
||||
};
|
||||
let x = write_string_to_grid(&make_entry_string(&self.mailbox.collection[idx], idx), grid, Color::Default, color, set_y(upper_left, y), bottom_right);
|
||||
|
||||
for x in x..get_x(bottom_right)+1 {
|
||||
for x in x..=get_x(bottom_right) {
|
||||
grid[(x,y)].set_ch(' ');
|
||||
grid[(x,y)].set_bg(color);
|
||||
}
|
||||
|
@ -91,10 +109,10 @@ impl MailListing {
|
|||
fn draw_mail_view(&mut self, grid: &mut CellBuffer, upper_left: Pos, bottom_right: Pos) {
|
||||
let ref mail = self.mailbox.collection[self.cursor_pos];
|
||||
|
||||
let height = get_y(bottom_right) - get_y(upper_left);
|
||||
let width = get_x(bottom_right) - get_x(upper_left);
|
||||
let rows = get_y(bottom_right) - get_y(upper_left);
|
||||
let cols = get_x(bottom_right) - get_x(upper_left);
|
||||
|
||||
self.pager = Some(Pager::new(mail, height, width));
|
||||
self.pager = Some(Pager::new(mail, rows, cols));
|
||||
let pager = self.pager.as_mut().unwrap();
|
||||
pager.draw(grid, upper_left,bottom_right);
|
||||
}
|
||||
|
@ -116,8 +134,8 @@ impl Component for MailListing {
|
|||
/* Render the mail body in a pager, basically copy what HSplit does */
|
||||
let total_rows = get_y(bottom_right) - get_y(upper_left);
|
||||
/* TODO: define ratio in Configuration file */
|
||||
let bottom_entity_height = (80*total_rows )/100;
|
||||
let mid = get_y(upper_left) + total_rows - bottom_entity_height;
|
||||
let bottom_entity_rows = (80*total_rows )/100;
|
||||
let mid = get_y(upper_left) + total_rows - bottom_entity_rows;
|
||||
|
||||
if !self.dirty {
|
||||
if let Some(ref mut p) = self.pager {
|
||||
|
@ -126,7 +144,7 @@ impl Component for MailListing {
|
|||
return;
|
||||
}
|
||||
self.dirty = false;
|
||||
self.draw_list(grid, upper_left, (get_x(bottom_right), get_y(upper_left)+ mid -1));
|
||||
self.draw_list(grid, upper_left, (get_x(bottom_right), get_y(upper_left)+ mid-3));
|
||||
if self.length == 0 {
|
||||
return;
|
||||
}
|
||||
|
@ -138,42 +156,42 @@ impl Component for MailListing {
|
|||
}
|
||||
}
|
||||
|
||||
for i in get_x(upper_left)..get_x(bottom_right)+1 {
|
||||
for i in get_x(upper_left)..get_x(bottom_right) {
|
||||
grid[(i, mid)].set_ch('─');
|
||||
}
|
||||
}
|
||||
|
||||
let headers_height: usize = 6;
|
||||
let headers_rows: usize = 6;
|
||||
/* Draw header */
|
||||
{
|
||||
let ref mail = self.mailbox.collection[self.cursor_pos];
|
||||
|
||||
let x = write_string_to_grid(&format!("Date: {}", mail.get_date_as_str()), grid, Color::Byte(33), Color::Default, set_y(upper_left, mid+1), set_y(upper_left, mid+1));
|
||||
for x in x..get_x(bottom_right)+1 {
|
||||
for x in x..get_x(bottom_right) {
|
||||
grid[(x, mid+1)].set_ch(' ');
|
||||
grid[(x, mid+1)].set_bg(Color::Default);
|
||||
grid[(x, mid+1)].set_fg(Color::Default);
|
||||
}
|
||||
let x = write_string_to_grid(&format!("From: {}", mail.get_from()), grid, Color::Byte(33), Color::Default, set_y(upper_left, mid+2), set_y(upper_left, mid+2));
|
||||
for x in x..get_x(bottom_right)+1 {
|
||||
for x in x..get_x(bottom_right) {
|
||||
grid[(x, mid+2)].set_ch(' ');
|
||||
grid[(x, mid+2)].set_bg(Color::Default);
|
||||
grid[(x, mid+2)].set_fg(Color::Default);
|
||||
}
|
||||
let x = write_string_to_grid(&format!("To: {}", mail.get_to()), grid, Color::Byte(33), Color::Default, set_y(upper_left, mid+3), set_y(upper_left, mid+3));
|
||||
for x in x..get_x(bottom_right)+1 {
|
||||
for x in x..get_x(bottom_right) {
|
||||
grid[(x, mid+3)].set_ch(' ');
|
||||
grid[(x, mid+3)].set_bg(Color::Default);
|
||||
grid[(x, mid+3)].set_fg(Color::Default);
|
||||
}
|
||||
let x = write_string_to_grid(&format!("Subject: {}", mail.get_subject()), grid, Color::Byte(33), Color::Default, set_y(upper_left, mid+4), set_y(upper_left, mid+4));
|
||||
for x in x..get_x(bottom_right)+1 {
|
||||
for x in x..get_x(bottom_right) {
|
||||
grid[(x, mid+4)].set_ch(' ');
|
||||
grid[(x, mid+4)].set_bg(Color::Default);
|
||||
grid[(x, mid+4)].set_fg(Color::Default);
|
||||
}
|
||||
let x = write_string_to_grid(&format!("Message-ID: {}", mail.get_message_id_raw()), grid, Color::Byte(33), Color::Default, set_y(upper_left, mid+5), set_y(upper_left, mid+5));
|
||||
for x in x..get_x(bottom_right)+1 {
|
||||
for x in x..get_x(bottom_right) {
|
||||
grid[(x, mid+5)].set_ch(' ');
|
||||
grid[(x, mid+5)].set_bg(Color::Default);
|
||||
grid[(x, mid+5)].set_fg(Color::Default);
|
||||
|
@ -181,7 +199,7 @@ impl Component for MailListing {
|
|||
}
|
||||
|
||||
/* Draw body */
|
||||
self.draw_mail_view(grid, (get_x(upper_left), get_y(upper_left) + mid + headers_height), bottom_right);
|
||||
self.draw_mail_view(grid, (get_x(upper_left), get_y(upper_left) + mid + headers_rows), bottom_right);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +212,7 @@ impl Component for MailListing {
|
|||
}
|
||||
},
|
||||
UIEventType::Input(Key::Down) => {
|
||||
if self.length > 0 && self.cursor_pos < self.length - 1 {
|
||||
if self.length > 0 && self.new_cursor_pos < self.length - 1 {
|
||||
self.new_cursor_pos += 1;
|
||||
self.dirty = true;
|
||||
}
|
||||
|
@ -232,7 +250,7 @@ pub struct AccountMenu {
|
|||
entries: Vec<(usize, Folder)>,
|
||||
dirty: bool,
|
||||
name: String,
|
||||
|
||||
cursor: Option<String>,
|
||||
}
|
||||
|
||||
impl AccountMenu {
|
||||
|
@ -246,8 +264,13 @@ impl AccountMenu {
|
|||
entries: entries,
|
||||
dirty: true,
|
||||
name: account.get_name().to_string(),
|
||||
cursor: None,
|
||||
}
|
||||
}
|
||||
fn highlight_folder(&mut self, m: &Mailbox) {
|
||||
self.dirty = true;
|
||||
self.cursor = Some(m.folder.get_name().to_string());
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for AccountMenu {
|
||||
|
@ -271,8 +294,8 @@ impl Component for AccountMenu {
|
|||
}
|
||||
|
||||
let mut ind = 0;
|
||||
let mut depth = String::from("");
|
||||
let mut s = String::from(format!("{}\n", self.name));
|
||||
let mut depth = String::from(" ");
|
||||
let mut s = String::from(format!("\n\n {}\n", self.name));
|
||||
fn pop(depth: &mut String) {
|
||||
depth.pop();
|
||||
depth.pop();
|
||||
|
@ -289,7 +312,7 @@ impl Component for AccountMenu {
|
|||
|
||||
fn print(root: usize, parents: &Vec<Option<usize>>, depth: &mut String, entries: &Vec<(usize, Folder)>, mut s: String) -> String {
|
||||
let len = s.len();
|
||||
s.insert_str(len, &format!("{}: {}\n", &entries[root].1.get_name(), entries[root].0));
|
||||
s.insert_str(len, &format!("{}: {}\n ", entries[root].0, &entries[root].1.get_name()));
|
||||
let children_no = entries[root].1.get_children().len();
|
||||
for (idx, child) in entries[root].1.get_children().iter().enumerate() {
|
||||
let len = s.len();
|
||||
|
@ -306,17 +329,28 @@ impl Component for AccountMenu {
|
|||
}
|
||||
|
||||
let lines: Vec<&str> = s.lines().collect();
|
||||
let lines_len = lines.len();
|
||||
let mut idx = 0;
|
||||
for y in get_y(upper_left)..get_y(bottom_right) {
|
||||
if idx == self.entries.len() {
|
||||
if idx == lines_len {
|
||||
break;
|
||||
}
|
||||
let s = format!("{}", lines[idx]);
|
||||
write_string_to_grid(&s, grid, Color::Red, Color::Default, set_y(upper_left, y), bottom_right);
|
||||
let s = if idx == lines_len - 2 {
|
||||
format!("{}", lines[idx].replace("├", "└"))
|
||||
} else {
|
||||
format!("{}", lines[idx])
|
||||
};
|
||||
write_string_to_grid(&s, grid, Color::Byte(30), Color::Default, set_y(upper_left, y), bottom_right);
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
fn process_event(&mut self, _event: &UIEvent, _queue: &mut VecDeque<UIEvent>) {
|
||||
return;
|
||||
fn process_event(&mut self, event: &UIEvent, _queue: &mut VecDeque<UIEvent>) {
|
||||
match event.event_type {
|
||||
UIEventType::RefreshMailbox(ref m) => {
|
||||
self.highlight_folder(m);
|
||||
},
|
||||
_ => {
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ fn write_string_to_grid(s: &str, grid: &mut CellBuffer, fg_color: Color, bg_colo
|
|||
if x == (get_x(bottom_right)) {
|
||||
x = get_x(upper_left);
|
||||
y += 1;
|
||||
if y > (get_y(bottom_right)) {
|
||||
if y == (get_y(bottom_right)) {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
@ -103,8 +103,8 @@ fn write_string_to_grid(s: &str, grid: &mut CellBuffer, fg_color: Color, bg_colo
|
|||
}
|
||||
|
||||
fn clear_area(grid: &mut CellBuffer, upper_left: Pos, bottom_right: Pos) {
|
||||
for y in get_y(upper_left)..get_y(bottom_right) {
|
||||
for x in get_x(upper_left)..get_x(bottom_right) {
|
||||
for y in get_y(upper_left)..=get_y(bottom_right) {
|
||||
for x in get_x(upper_left)..=get_x(bottom_right) {
|
||||
grid[(x,y)].set_ch(' ');
|
||||
grid[(x,y)].set_bg(Color::Default);
|
||||
grid[(x,y)].set_fg(Color::Default);
|
||||
|
|
|
@ -49,15 +49,11 @@ impl HSplit {
|
|||
|
||||
impl Component for HSplit {
|
||||
fn draw(&mut self, grid: &mut CellBuffer, upper_left: Pos, bottom_right: Pos) {
|
||||
grid[upper_left].set_ch('u');
|
||||
let (a,b) = upper_left;
|
||||
grid[(a+1,b)].set_ch('h');
|
||||
|
||||
let total_rows = get_y(bottom_right) - get_y(upper_left);
|
||||
let bottom_entity_height = (self.ratio*total_rows )/100;
|
||||
let mid = get_y(upper_left) + total_rows - bottom_entity_height;
|
||||
|
||||
for i in get_x(upper_left)..get_x(bottom_right)+1 {
|
||||
for i in get_x(upper_left)..=get_x(bottom_right) {
|
||||
grid[(i, mid)].set_ch('─');
|
||||
}
|
||||
let _ = self.top.component.draw(grid, upper_left, (get_x(bottom_right), get_y(upper_left) + mid-1));
|
||||
|
|
|
@ -54,6 +54,8 @@ pub enum ThreadEvent {
|
|||
Input(Key),
|
||||
/// A watched folder has been refreshed.
|
||||
RefreshMailbox{ name: String },
|
||||
UIEventType(UIEventType),
|
||||
GoCmd(usize),
|
||||
//Decode { _ }, // For gpg2 signature check
|
||||
}
|
||||
|
||||
|
@ -93,6 +95,7 @@ pub enum UIEventType {
|
|||
RefreshMailbox(Mailbox),
|
||||
//Quit?
|
||||
Resize,
|
||||
ChangeMailbox(usize),
|
||||
}
|
||||
|
||||
|
||||
|
@ -103,12 +106,13 @@ pub struct UIEvent {
|
|||
}
|
||||
|
||||
pub struct State<W: Write> {
|
||||
width: usize,
|
||||
height: usize,
|
||||
cols: usize,
|
||||
rows: usize,
|
||||
|
||||
grid: CellBuffer,
|
||||
pub stdout: termion::raw::RawTerminal<W>,
|
||||
stdout: termion::raw::RawTerminal<W>,
|
||||
entities: Vec<Entity>,
|
||||
settings: Settings,
|
||||
|
||||
}
|
||||
|
||||
|
@ -120,34 +124,37 @@ impl<W: Write> Drop for State<W> {
|
|||
}
|
||||
|
||||
impl<W: Write> State<W> {
|
||||
pub fn new(stdout: W) -> Self {
|
||||
pub fn new(stdout: W, settings: Settings) -> Self {
|
||||
let termsize = termion::terminal_size().ok();
|
||||
let termwidth = termsize.map(|(w,_)| w);
|
||||
let termheight = termsize.map(|(_,h)| h);
|
||||
let width = termwidth.unwrap_or(0) as usize;
|
||||
let height = termheight.unwrap_or(0) as usize;
|
||||
let termcols = termsize.map(|(w,_)| 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 s = State {
|
||||
width: width,
|
||||
height: height,
|
||||
cols: cols,
|
||||
rows: rows,
|
||||
//queue: VecDeque::new();
|
||||
|
||||
grid: CellBuffer::new(width+1, height+1, Cell::with_char(' ')),
|
||||
grid: CellBuffer::new(cols, rows, Cell::with_char(' ')),
|
||||
stdout: stdout.into_raw_mode().unwrap(),
|
||||
entities: Vec::with_capacity(2),
|
||||
entities: Vec::with_capacity(1),
|
||||
settings: settings,
|
||||
};
|
||||
write!(s.stdout, "{}{}{}", cursor::Hide, clear::All, cursor::Goto(1,1)).unwrap();
|
||||
s
|
||||
}
|
||||
pub fn hello_w(&mut self) {
|
||||
write!(self.stdout, "Hey there.").unwrap();
|
||||
}
|
||||
fn update_size(&mut self) {
|
||||
/* update dimensions. TODO: Only do that in size change events. ie SIGWINCH */
|
||||
let termsize = termion::terminal_size().ok();
|
||||
let termwidth = termsize.map(|(w,_)| w);
|
||||
let termheight = termsize.map(|(_,h)| h);
|
||||
self.width = termwidth.unwrap_or(72) as usize;
|
||||
self.height = termheight.unwrap_or(120) as usize;
|
||||
let termcols = termsize.map(|(w,_)| w);
|
||||
let termrows = termsize.map(|(_,h)| h);
|
||||
if termcols.unwrap_or(72) as usize != self.cols || termrows.unwrap_or(120) as usize != self.rows {
|
||||
eprintln!("Size updated, from ({}, {}) -> ({:?}, {:?})", self.cols, self.rows, termcols, termrows);
|
||||
|
||||
}
|
||||
self.cols = termcols.unwrap_or(72) as usize;
|
||||
self.rows = termrows.unwrap_or(120) as usize;
|
||||
self.grid.resize(self.cols, self.rows, Cell::with_char(' '));
|
||||
}
|
||||
|
||||
pub fn render(&mut self) {
|
||||
|
@ -158,9 +165,9 @@ impl<W: Write> State<W> {
|
|||
}
|
||||
|
||||
/* Only draw dirty areas */
|
||||
for y in 0..self.height {
|
||||
write!(self.stdout, "{}", cursor::Goto(1,y as u16)).unwrap();
|
||||
for x in 0..self.width {
|
||||
for y in 0..self.rows {
|
||||
write!(self.stdout, "{}", cursor::Goto(1,(y+1) as u16)).unwrap();
|
||||
for x in 0..self.cols {
|
||||
let c = self.grid[(x,y)];
|
||||
|
||||
if c.get_bg() != cells::Color::Default {
|
||||
|
@ -179,13 +186,12 @@ impl<W: Write> State<W> {
|
|||
|
||||
}
|
||||
}
|
||||
self.stdout.flush().unwrap();
|
||||
}
|
||||
pub fn draw_entity(&mut self, idx: usize) {
|
||||
let ref mut entity = self.entities[idx];
|
||||
eprintln!("Entity is {:?}", entity);
|
||||
let upper_left = (1,1);
|
||||
let bottom_right = (self.width, self.height);
|
||||
eprintln!("Upper left is {:?} and bottom_right is {:?}", upper_left, bottom_right);
|
||||
let upper_left = (0,0);
|
||||
let bottom_right = (self.cols-1, self.rows-1);
|
||||
|
||||
entity.component.draw(&mut self.grid, upper_left, bottom_right);
|
||||
}
|
||||
|
@ -267,12 +273,11 @@ pub enum Key {
|
|||
Esc,
|
||||
}
|
||||
|
||||
pub fn get_events<F>(stdin: std::io::Stdin, closure: F) where F: Fn(Key) -> (){
|
||||
pub fn get_events<F>(stdin: std::io::Stdin, mut closure: F) where F: FnMut(Key) -> (){
|
||||
let stdin = stdin.lock();
|
||||
for c in stdin.keys() {
|
||||
if let Ok(k) = c {
|
||||
let k = convert_key(k);
|
||||
eprintln!("Received key: {:?}", k);
|
||||
closure(k);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue