Moved accounts to State

embed
Manos Pitsidianakis 2018-07-14 21:41:38 +03:00
parent 11aac5ec34
commit 5fcacc80b8
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
5 changed files with 148 additions and 94 deletions

View File

@ -45,8 +45,6 @@ fn main() {
*/
let set = Settings::new();
let backends = Backends::new();
let (sender, receiver): (SyncSender<ThreadEvent>, Receiver<ThreadEvent>) = sync_channel(::std::mem::size_of::<ThreadEvent>());
{
@ -54,26 +52,26 @@ fn main() {
let sender = sender.clone();
thread::Builder::new().name("input-thread".to_string()).spawn(move || {
get_events(stdin, move | k| {
eprintln!("{:?}: queue is {:?}", Instant::now(), cmd_queue);
//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());
//eprintln!("{:?}: got 'g' in thread",Instant::now());
push = Some((Instant::now(), v));
} else if v > '/' && v < ':' {
eprintln!("{:?}: got '{}' in thread", Instant::now(), v);
//eprintln!("{:?}: got '{}' in thread", Instant::now(), v);
if let Some((_, 'g')) = front {
eprintln!("{:?}: 'g' is front", Instant::now());
//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);
//eprintln!("{:?}: check for num c={}, n={}", Instant::now(),cmd, n);
if n > 0x2f && n < 0x3a {
eprintln!("{:?}: got a num {}", Instant::now(), cmd);
//eprintln!("{:?}: got a num {}", Instant::now(), cmd);
let now = Instant::now();
if now - i < Duration::from_millis(300) {
push = Some((now,cmd));
@ -85,9 +83,9 @@ fn main() {
},
Some((i, cmd)) => {
let n = v as u8;
eprintln!("{:?}: check for num c={}, n={}", Instant::now(),v, n);
//eprintln!("{:?}: check for num c={}, n={}", Instant::now(),v, n);
if n > 0x2f && n < 0x3a {
eprintln!("{:?}: got a num {}", Instant::now(), v);
//eprintln!("{:?}: got a num {}", Instant::now(), v);
let now = Instant::now();
if now - i < Duration::from_millis(300) {
push = Some((now,v));
@ -99,7 +97,7 @@ fn main() {
}
s.push(v);
let times = s.parse::<usize>();
eprintln!("{:?}: parsed {:?}", Instant::now(), times);
//eprintln!("{:?}: parsed {:?}", Instant::now(), times);
if let Ok(g) = times {
sender.send(ThreadEvent::GoCmd(g)).unwrap();
return;
@ -124,40 +122,32 @@ fn main() {
})}).unwrap();
}
//let mailbox = Mailbox::new("/home/epilys/Downloads/rust/nutt/Inbox4");
let mut j = 0;
/*
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| {
sender.send(ThreadEvent::from(r)).unwrap();
})));
}
*/
let mut state = State::new(_stdout);
eprintln!("account is {:?}", account);
let mut state = State::new(_stdout, set);
let menu = Entity {component: Box::new(AccountMenu::new(&account)) };
let menu = Entity {component: Box::new(AccountMenu::new(&state.context.accounts)) };
let listing = MailListing::new(Mailbox::new_dummy());
let b = Entity { component: Box::new(listing) };
let window = Entity { component: Box::new(VSplit::new(menu,b,90)) };
let status_bar = Entity { component: Box::new(StatusBar::new(window)) };
state.register_entity(status_bar);
state.render();
let mut idxa = 0;
let mut idxm = 0;
'main: loop {
let mailbox = &mut account[j];
match *mailbox.as_ref().unwrap() {
Ok(ref v) => {
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::RefreshMailbox(v.clone()) });
},
Err(_) => {},
};
state.refresh_mailbox(idxa,idxm);
let folder_length = state.context.accounts[idxa].len();
state.render();
state.redraw();
'inner: loop {
@ -185,12 +175,12 @@ fn main() {
Key::Char('q') | Key::Char('Q') => {
break 'main;
},
Key::Char('J') => if j < folder_length - 1 {
j += 1;
Key::Char('J') => if idxm + 1 < folder_length {
idxm += 1;
break 'inner;
},
Key::Char('K') => if j > 0 {
j -= 1;
Key::Char('K') => if idxm > 0 {
idxm -= 1;
break 'inner;
},
Key::Char(k @ 'g') => {

View File

@ -37,7 +37,7 @@ pub struct Account {
impl Account {
pub fn new(name: String, settings: AccountSettings, backends: Backends) -> Self {
pub fn new(name: String, settings: AccountSettings, backends: &Backends) -> Self {
eprintln!("new acc");
let sent_folder = settings
.folders

View File

@ -93,10 +93,10 @@ impl MailListing {
};
let new_area = (set_y(upper_left, get_y(upper_left)+(*idx % rows)), bottom_right);
let x = write_string_to_grid(&make_entry_string(envelope, *idx),
grid,
fg_color,
bg_color,
new_area);
grid,
fg_color,
bg_color,
new_area);
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(bg_color);
@ -131,16 +131,16 @@ impl MailListing {
if !envelope.is_seen() {
Color::Byte(251)
} else if idx % 2 == 0 {
Color::Byte(236)
Color::Byte(236)
} else {
Color::Default
}
};
let x = write_string_to_grid(&make_entry_string(envelope, idx),
grid,
fg_color,
bg_color,
(set_y(upper_left, y), bottom_right));
grid,
fg_color,
bg_color,
(set_y(upper_left, y), bottom_right));
for x in x..=get_x(bottom_right) {
grid[(x,y)].set_ch(' ');
@ -234,52 +234,51 @@ impl Component for MailListing {
{
let ref mail = self.mailbox.collection[self.cursor_pos];
eprintln!("writing headers {} {}", mail.get_date_as_str(), mail.get_subject());
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(bottom_right, mid+1)));
grid,
Color::Byte(33),
Color::Default,
(set_y(upper_left, mid+1), set_y(bottom_right, mid+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(bottom_right, mid+2)));
grid,
Color::Byte(33),
Color::Default,
(set_y(upper_left, mid+2), set_y(bottom_right, mid+2)));
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(bottom_right, mid+3)));
grid,
Color::Byte(33),
Color::Default,
(set_y(upper_left, mid+3), set_y(bottom_right, mid+3)));
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(bottom_right, mid+4)));
grid,
Color::Byte(33),
Color::Default,
(set_y(upper_left, mid+4), set_y(bottom_right, mid+4)));
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(bottom_right, mid+5)));
grid,
Color::Byte(33),
Color::Default,
(set_y(upper_left, mid+5), set_y(bottom_right, mid+5)));
for x in x..=get_x(bottom_right) {
grid[(x, mid+5)].set_ch(' ');
grid[(x, mid+5)].set_bg(Color::Default);
@ -341,44 +340,53 @@ impl Component for MailListing {
}
#[derive(Debug)]
pub struct AccountMenu {
entries: Vec<(usize, Folder)>,
dirty: bool,
struct AccountMenuEntry {
name: String,
cursor: Option<String>,
index: usize,
entries: Vec<(usize, Folder)>,
}
#[derive(Debug)]
pub struct AccountMenu {
accounts: Vec<AccountMenuEntry>,
dirty: bool,
cursor: Option<(usize, usize)>,
}
impl AccountMenu {
pub fn new(account: &Account) -> Self{
let mut entries = Vec::with_capacity(account.len());
for (idx, acc) in account.list_folders().iter().enumerate() {
entries.push((idx, acc.clone()));
}
pub fn new(accounts: &Vec<Account>) -> Self {
let mut accounts = accounts.iter().enumerate().map(|(i, a)| {
AccountMenuEntry {
name: a.get_name().to_string(),
index: i,
entries: {
let mut entries = Vec::with_capacity(a.len());
for (idx, acc) in a.list_folders().iter().enumerate() {
entries.push((idx, acc.clone()));
}
entries}
}
}).collect();
AccountMenu {
entries: entries,
accounts: accounts,
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());
self.cursor = None;
}
}
impl Component for AccountMenu {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
if !(self.dirty) {
return;
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);
self.dirty = false;
let mut parents: Vec<Option<usize>> = vec!(None; self.entries.len());
let mut parents: Vec<Option<usize>> = vec!(None; a.entries.len());
for (idx, e) in self.entries.iter().enumerate() {
for (idx, e) in a.entries.iter().enumerate() {
for c in e.1.get_children() {
parents[*c] = Some(idx);
}
@ -392,7 +400,7 @@ impl Component for AccountMenu {
let mut ind = 0;
let mut depth = String::from(" ");
let mut s = String::from(format!("\n\n {}\n", self.name));
let mut s = String::from(format!("\n\n {}\n", a.name));
fn pop(depth: &mut String) {
depth.pop();
depth.pop();
@ -407,7 +415,7 @@ impl Component for AccountMenu {
depth.push(' ');
}
fn print(root: usize, parents: &Vec<Option<usize>>, depth: &mut String, entries: &Vec<(usize, Folder)>, mut s: String) -> String {
fn print(root: usize, parents: &Vec<Option<usize>>, depth: &mut String, entries: &Vec<(usize, Folder)>, s: &mut String) -> () {
let len = s.len();
s.insert_str(len, &format!("{}: {}\n ", entries[root].0, &entries[root].1.get_name()));
let children_no = entries[root].1.get_children().len();
@ -415,13 +423,12 @@ impl Component for AccountMenu {
let len = s.len();
s.insert_str(len, &format!("{}├─", depth));
push(depth, if idx == children_no - 1 {'│'} else { ' ' });
s = print(*child, parents, depth, entries, s);
print(*child, parents, depth, entries, s);
pop(depth);
}
s
}
for r in roots {
s =print(r, &parents, &mut depth, &self.entries, s);
print(r, &parents, &mut depth, &a.entries, &mut s);
}
@ -444,6 +451,27 @@ impl Component for AccountMenu {
(set_y(upper_left, y), bottom_right));
idx += 1;
}
idx
}
}
impl Component for AccountMenu {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
if !(self.dirty) {
return;
}
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);
self.dirty = false;
let mut y = get_y(upper_left);
for a in &self.accounts {
y += self.print_account(grid,
(set_y(upper_left, y), bottom_right),
&a);
}
context.dirty_areas.push_back(area);
}
fn process_event(&mut self, event: &UIEvent, _context: &mut Context) {

View File

@ -107,10 +107,17 @@ pub struct UIEvent {
}
pub struct Context {
pub accounts: Vec<Account>,
settings: Settings,
queue: VecDeque<UIEvent>,
/// Areas of the screen that must be redrawn in the next render
dirty_areas: VecDeque<Area>,
backends: Backends,
}
impl Context {
}
@ -121,18 +128,22 @@ pub struct State<W: Write> {
grid: CellBuffer,
stdout: termion::raw::RawTerminal<W>,
entities: Vec<Entity>,
context: Context,
pub context: Context,
}
impl<W: Write> Drop for State<W> {
fn drop(&mut self) {
// When done, restore the defaults to avoid messing with the terminal.
write!(self.stdout, "{}{}{}{}", clear::All, style::Reset, cursor::Goto(1, 1), cursor::Show).unwrap();
self.stdout.flush().unwrap();
}
}
impl<W: Write> State<W> {
pub fn new(stdout: W, settings: Settings) -> Self {
pub fn new(stdout: W) -> Self {
let settings = Settings::new();
let backends = Backends::new();
let termsize = termion::terminal_size().ok();
let termcols = termsize.map(|(w,_)| w);
let termrows = termsize.map(|(_,h)| h);
@ -146,12 +157,15 @@ 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(),
backends: backends,
settings: settings,
queue: VecDeque::with_capacity(5),
dirty_areas: VecDeque::with_capacity(5),
},
};
write!(s.stdout, "{}{}{}", cursor::Hide, clear::All, cursor::Goto(1,1)).unwrap();
s.stdout.flush().unwrap();
s
}
fn update_size(&mut self) {
@ -173,7 +187,7 @@ impl<W: Write> State<W> {
self.draw_entity(i);
}
let areas: Vec<Area> = self.context.dirty_areas.drain(0..).collect();
/* draw each entity */
/* draw each dirty area */
for a in areas {
self.draw_area(a);
}
@ -240,6 +254,18 @@ 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 },
};
if let Some(m) = mailbox {
self.rcv_event(UIEvent { id: 0, event_type: UIEventType::RefreshMailbox(m) });
}
}
}
pub fn convert_key(k: TermionKey ) -> Key {

View File

@ -24,6 +24,16 @@ pub type Area = (Pos, Pos);
macro_rules! upper_left { ($a:expr) => ( $a.0 ) }
#[macro_export]
macro_rules! bottom_right { ($a:expr) => ( $a.1 ) }
#[macro_export]
macro_rules! is_valid_area { ($a:expr) => { {
let upper_left = upper_left!($a);
let bottom_right = bottom_right!($a);
if get_y(upper_left) >= get_y(bottom_right) || get_x(upper_left) > get_x(bottom_right) {
false
} else {
true
}
} } }
/// A `(cols, rows)` size.
pub type Size = (usize, usize);