Moved accounts to State
parent
11aac5ec34
commit
5fcacc80b8
56
src/bin.rs
56
src/bin.rs
|
@ -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') => {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue