diff --git a/src/bin.rs b/src/bin.rs index 2a25de03..79cc8cb7 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -45,8 +45,6 @@ fn main() { */ - let set = Settings::new(); - let backends = Backends::new(); let (sender, receiver): (SyncSender, Receiver) = sync_channel(::std::mem::size_of::()); { @@ -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::(); - 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') => { diff --git a/src/mailbox/accounts.rs b/src/mailbox/accounts.rs index 04faf91f..cfb3c2e9 100644 --- a/src/mailbox/accounts.rs +++ b/src/mailbox/accounts.rs @@ -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 diff --git a/src/ui/components/mail.rs b/src/ui/components/mail.rs index 8bb98d4d..ba038295 100644 --- a/src/ui/components/mail.rs +++ b/src/ui/components/mail.rs @@ -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, + index: usize, + entries: Vec<(usize, Folder)>, +} + + +#[derive(Debug)] +pub struct AccountMenu { + accounts: Vec, + 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) -> 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> = vec!(None; self.entries.len()); + let mut parents: Vec> = 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>, depth: &mut String, entries: &Vec<(usize, Folder)>, mut s: String) -> String { + fn print(root: usize, parents: &Vec>, 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) { diff --git a/src/ui/mod.rs b/src/ui/mod.rs index ba8fd275..34504df7 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -107,10 +107,17 @@ pub struct UIEvent { } pub struct Context { + pub accounts: Vec, settings: Settings, queue: VecDeque, /// Areas of the screen that must be redrawn in the next render dirty_areas: VecDeque, + backends: Backends, + +} + +impl Context { + } @@ -121,18 +128,22 @@ pub struct State { grid: CellBuffer, stdout: termion::raw::RawTerminal, entities: Vec, - context: Context, + pub context: Context, } impl Drop for State { 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 State { - 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 State { 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 State { self.draw_entity(i); } let areas: Vec = 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 State { 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 { diff --git a/src/ui/position.rs b/src/ui/position.rs index e3ed4950..a6e06aed 100644 --- a/src/ui/position.rs +++ b/src/ui/position.rs @@ -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);