Add NORMAL and EXECUTE modes
parent
dc348dde6d
commit
16e0960cd5
207
src/bin.rs
207
src/bin.rs
|
@ -30,8 +30,6 @@ pub use melib::*;
|
||||||
use std::sync::mpsc::{sync_channel, SyncSender, Receiver};
|
use std::sync::mpsc::{sync_channel, SyncSender, Receiver};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::io::{stdout, stdin, };
|
use std::io::{stdout, stdin, };
|
||||||
use std::collections::VecDeque;
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
/* Lock all stdios */
|
/* Lock all stdios */
|
||||||
|
@ -48,91 +46,23 @@ fn main() {
|
||||||
|
|
||||||
let (sender, receiver): (SyncSender<ThreadEvent>, Receiver<ThreadEvent>) = sync_channel(::std::mem::size_of::<ThreadEvent>());
|
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();
|
let sender = sender.clone();
|
||||||
thread::Builder::new().name("input-thread".to_string()).spawn(move || {
|
thread::Builder::new().name("input-thread".to_string()).spawn(move || {
|
||||||
get_events(stdin, move | k| {
|
get_events(stdin, move | k| { sender.send(ThreadEvent::Input(k)).unwrap();
|
||||||
//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();
|
})}).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
let folder_length = set.accounts["test_account"].folders.len();
|
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 mut account = Account::new("test_account".to_string(), set.accounts["test_account"].clone(), backends);
|
||||||
|
|
||||||
{
|
{
|
||||||
let sender = sender.clone();
|
let sender = sender.clone();
|
||||||
account.watch(RefreshEventConsumer::new(Box::new(move |r| {
|
account.watch(RefreshEventConsumer::new(Box::new(move |r| {
|
||||||
sender.send(ThreadEvent::from(r)).unwrap();
|
sender.send(ThreadEvent::from(r)).unwrap();
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
let mut state = State::new(_stdout);
|
let mut state = State::new(_stdout);
|
||||||
|
|
||||||
let menu = Entity {component: Box::new(AccountMenu::new(&state.context.accounts)) };
|
let menu = Entity {component: Box::new(AccountMenu::new(&state.context.accounts)) };
|
||||||
|
@ -145,6 +75,7 @@ fn main() {
|
||||||
let mut idxa = 0;
|
let mut idxa = 0;
|
||||||
let mut idxm = 0;
|
let mut idxm = 0;
|
||||||
let account_length = state.context.accounts.len();
|
let account_length = state.context.accounts.len();
|
||||||
|
let mut mode: UIMode = UIMode::Normal;
|
||||||
'main: loop {
|
'main: loop {
|
||||||
state.refresh_mailbox(idxa,idxm);
|
state.refresh_mailbox(idxa,idxm);
|
||||||
let folder_length = state.context.accounts[idxa].len();
|
let folder_length = state.context.accounts[idxa].len();
|
||||||
|
@ -153,53 +84,74 @@ fn main() {
|
||||||
'inner: loop {
|
'inner: loop {
|
||||||
match receiver.recv().unwrap() {
|
match receiver.recv().unwrap() {
|
||||||
ThreadEvent::Input(k) => {
|
ThreadEvent::Input(k) => {
|
||||||
match k {
|
match mode {
|
||||||
key @ Key::Char('j') | key @ Key::Char('k') => {
|
UIMode::Normal => {
|
||||||
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Input(key)});
|
match k {
|
||||||
state.redraw();
|
key @ Key::Char('j') | key @ Key::Char('k') => {
|
||||||
|
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Input(key)});
|
||||||
|
state.redraw();
|
||||||
|
},
|
||||||
|
key @ Key::Up | key @ Key::Down => {
|
||||||
|
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Input(key)});
|
||||||
|
state.redraw();
|
||||||
|
}
|
||||||
|
Key::Char('\n') => {
|
||||||
|
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Input(Key::Char('\n'))});
|
||||||
|
state.redraw();
|
||||||
|
}
|
||||||
|
Key::Char('i') | Key::Esc => {
|
||||||
|
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Input(Key::Esc)});
|
||||||
|
state.redraw();
|
||||||
|
}
|
||||||
|
Key::F(_) => {
|
||||||
|
},
|
||||||
|
Key::Char('q') | Key::Char('Q') => {
|
||||||
|
break 'main;
|
||||||
|
},
|
||||||
|
Key::Char('J') => if idxm + 1 < folder_length {
|
||||||
|
idxm += 1;
|
||||||
|
break 'inner;
|
||||||
|
},
|
||||||
|
Key::Char('K') => if idxm > 0 {
|
||||||
|
idxm -= 1;
|
||||||
|
break 'inner;
|
||||||
|
},
|
||||||
|
Key::Char('l') => if idxa + 1 < account_length {
|
||||||
|
idxa += 1;
|
||||||
|
idxm = 0;
|
||||||
|
break 'inner;
|
||||||
|
},
|
||||||
|
Key::Char('h') => if idxa > 0 {
|
||||||
|
idxa -= 1;
|
||||||
|
idxm = 0;
|
||||||
|
break 'inner;
|
||||||
|
},
|
||||||
|
Key::Char('r') => {
|
||||||
|
state.update_size();
|
||||||
|
state.render();
|
||||||
|
},
|
||||||
|
Key::Char(' ') => {
|
||||||
|
mode = UIMode::Execute;
|
||||||
|
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::ChangeMode(mode)});
|
||||||
|
state.redraw();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
key @ Key::Up | key @ Key::Down => {
|
UIMode::Execute => {
|
||||||
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Input(key)});
|
match k {
|
||||||
state.redraw();
|
Key::Char('\n') | Key::Esc => {
|
||||||
}
|
mode = UIMode::Normal;
|
||||||
Key::Char('\n') => {
|
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::ChangeMode(mode)});
|
||||||
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Input(Key::Char('\n'))});
|
state.render();
|
||||||
state.redraw();
|
},
|
||||||
}
|
k @ Key::Char(_) => {
|
||||||
Key::Char('i') | Key::Esc => {
|
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::ExInput(k)});
|
||||||
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Input(Key::Esc)});
|
state.redraw();
|
||||||
state.redraw();
|
},
|
||||||
}
|
_ => {},
|
||||||
Key::F(_) => {
|
}
|
||||||
},
|
},
|
||||||
Key::Char('q') | Key::Char('Q') => {
|
|
||||||
break 'main;
|
|
||||||
},
|
|
||||||
Key::Char('J') => if idxm + 1 < folder_length {
|
|
||||||
idxm += 1;
|
|
||||||
break 'inner;
|
|
||||||
},
|
|
||||||
Key::Char('K') => if idxm > 0 {
|
|
||||||
idxm -= 1;
|
|
||||||
break 'inner;
|
|
||||||
},
|
|
||||||
Key::Char('l') => if idxa + 1 < account_length {
|
|
||||||
idxa += 1;
|
|
||||||
idxm = 0;
|
|
||||||
break 'inner;
|
|
||||||
},
|
|
||||||
Key::Char('h') => if idxa > 0 {
|
|
||||||
idxa -= 1;
|
|
||||||
idxm = 0;
|
|
||||||
break 'inner;
|
|
||||||
},
|
|
||||||
Key::Char('r') => {
|
|
||||||
state.update_size();
|
|
||||||
state.render();
|
|
||||||
},
|
|
||||||
Key::Char(v) if v > '/' && v < ':' => {
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ThreadEvent::RefreshMailbox { name : n } => {
|
ThreadEvent::RefreshMailbox { name : n } => {
|
||||||
|
@ -209,9 +161,6 @@ fn main() {
|
||||||
state.rcv_event(UIEvent { id: 0, event_type: e});
|
state.rcv_event(UIEvent { id: 0, event_type: e});
|
||||||
state.render();
|
state.render();
|
||||||
},
|
},
|
||||||
ThreadEvent::GoCmd(v) => {
|
|
||||||
eprintln!("got go cmd with {:?}", v);
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,14 +153,8 @@ impl MailListing {
|
||||||
|
|
||||||
/// Create a pager for the `Envelope` currently under the cursor.
|
/// Create a pager for the `Envelope` currently under the cursor.
|
||||||
fn draw_mail_view(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
fn draw_mail_view(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||||
let upper_left = upper_left!(area);
|
|
||||||
let bottom_right = bottom_right!(area);
|
|
||||||
|
|
||||||
let envelope: &Envelope = &self.mailbox.collection[self.cursor_pos];
|
let envelope: &Envelope = &self.mailbox.collection[self.cursor_pos];
|
||||||
|
|
||||||
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(envelope));
|
self.pager = Some(Pager::new(envelope));
|
||||||
self.pager.as_mut().map(|p| p.draw(grid, area, context));
|
self.pager.as_mut().map(|p| p.draw(grid, area, context));
|
||||||
}
|
}
|
||||||
|
@ -169,13 +163,11 @@ impl MailListing {
|
||||||
impl Component for MailListing {
|
impl Component for MailListing {
|
||||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||||
if !self.unfocused {
|
if !self.unfocused {
|
||||||
if !self.dirty {
|
if !self.is_dirty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
/* Draw the entire list */
|
/* Draw the entire list */
|
||||||
let upper_left = upper_left!(area);
|
|
||||||
let bottom_right = bottom_right!(area);
|
|
||||||
self.draw_list(grid,
|
self.draw_list(grid,
|
||||||
area,
|
area,
|
||||||
context);
|
context);
|
||||||
|
@ -202,7 +194,7 @@ impl Component for MailListing {
|
||||||
}
|
}
|
||||||
let mid = get_y(upper_left) + total_rows - bottom_entity_rows;
|
let mid = get_y(upper_left) + total_rows - bottom_entity_rows;
|
||||||
|
|
||||||
if !self.dirty {
|
if !self.is_dirty() {
|
||||||
if let Some(ref mut p) = self.pager {
|
if let Some(ref mut p) = self.pager {
|
||||||
p.draw(grid,
|
p.draw(grid,
|
||||||
((get_x(upper_left), get_y(upper_left) + mid + headers_rows + 1), bottom_right),
|
((get_x(upper_left), get_y(upper_left) + mid + headers_rows + 1), bottom_right),
|
||||||
|
@ -331,6 +323,9 @@ impl Component for MailListing {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
self.pager = None;
|
self.pager = None;
|
||||||
},
|
},
|
||||||
|
UIEventType::ChangeMode(UIMode::Normal) => {
|
||||||
|
self.dirty = true;
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -360,7 +355,7 @@ pub struct AccountMenu {
|
||||||
|
|
||||||
impl AccountMenu {
|
impl AccountMenu {
|
||||||
pub fn new(accounts: &Vec<Account>) -> Self {
|
pub fn new(accounts: &Vec<Account>) -> Self {
|
||||||
let mut accounts = accounts.iter().enumerate().map(|(i, a)| {
|
let accounts = accounts.iter().enumerate().map(|(i, a)| {
|
||||||
AccountMenuEntry {
|
AccountMenuEntry {
|
||||||
name: a.get_name().to_string(),
|
name: a.get_name().to_string(),
|
||||||
index: i,
|
index: i,
|
||||||
|
@ -458,9 +453,10 @@ impl AccountMenu {
|
||||||
|
|
||||||
impl Component for AccountMenu {
|
impl Component for AccountMenu {
|
||||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||||
if !(self.dirty) {
|
if !self.is_dirty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
clear_area(grid, area);
|
||||||
let upper_left = upper_left!(area);
|
let upper_left = upper_left!(area);
|
||||||
let bottom_right = bottom_right!(area);
|
let bottom_right = bottom_right!(area);
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
|
@ -479,6 +475,9 @@ impl Component for AccountMenu {
|
||||||
UIEventType::RefreshMailbox(ref m) => {
|
UIEventType::RefreshMailbox(ref m) => {
|
||||||
self.highlight_folder(m);
|
self.highlight_folder(m);
|
||||||
},
|
},
|
||||||
|
UIEventType::ChangeMode(UIMode::Normal) => {
|
||||||
|
self.dirty = true;
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +1,5 @@
|
||||||
use ui::components::*;
|
use ui::components::*;
|
||||||
use ui::cells::*;
|
use ui::cells::*;
|
||||||
///A simple box with borders and no content.
|
|
||||||
pub struct BoxPanel {
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Component for BoxPanel {
|
|
||||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
|
||||||
let upper_left = upper_left!(area);
|
|
||||||
let bottom_right = bottom_right!(area);
|
|
||||||
grid[upper_left].set_ch('u');
|
|
||||||
grid[bottom_right].set_ch('b');
|
|
||||||
let width = get_x(bottom_right) - get_x(upper_left);
|
|
||||||
let height = get_y(bottom_right) - get_y(upper_left);
|
|
||||||
|
|
||||||
grid[upper_left].set_ch('┌');
|
|
||||||
grid[(get_x(upper_left), get_y(bottom_right))].set_ch(BOTTOM_LEFT_CORNER);
|
|
||||||
grid[(get_x(bottom_right), get_y(upper_left))].set_ch('┐');
|
|
||||||
grid[bottom_right].set_ch('┘');
|
|
||||||
for i in get_y(upper_left) + 1..get_y(bottom_right) {
|
|
||||||
grid[(get_x(upper_left), i)].set_ch('│');
|
|
||||||
grid[(get_x(upper_left) + width, i)].set_ch('│');
|
|
||||||
}
|
|
||||||
for i in get_x(upper_left)+1..get_x(bottom_right) {
|
|
||||||
grid[(i, get_y(upper_left))].set_ch('─');
|
|
||||||
grid[(i, get_y(upper_left) + height)].set_ch('─');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn process_event(&mut self, _event: &UIEvent, _context: &mut Context) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A horizontally split in half container.
|
/// A horizontally split in half container.
|
||||||
pub struct HSplit {
|
pub struct HSplit {
|
||||||
|
@ -116,6 +86,8 @@ impl Component for VSplit {
|
||||||
|
|
||||||
for i in get_y(upper_left)..=get_y(bottom_right) {
|
for i in get_y(upper_left)..=get_y(bottom_right) {
|
||||||
grid[(mid, i)].set_ch(VERT_BOUNDARY);
|
grid[(mid, i)].set_ch(VERT_BOUNDARY);
|
||||||
|
grid[(mid, i)].set_fg(Color::Default);
|
||||||
|
grid[(mid, i)].set_bg(Color::Default);
|
||||||
}
|
}
|
||||||
if get_y(bottom_right)> 1 {
|
if get_y(bottom_right)> 1 {
|
||||||
let c = grid.get(mid, get_y(bottom_right)-1).map(|a| a.ch()).unwrap_or_else(|| ' ');
|
let c = grid.get(mid, get_y(bottom_right)-1).map(|a| a.ch()).unwrap_or_else(|| ' ');
|
||||||
|
@ -142,42 +114,6 @@ impl Component for VSplit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A box with a text content.
|
|
||||||
pub struct TextBox {
|
|
||||||
content: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TextBox {
|
|
||||||
pub fn new(s: String) -> Self {
|
|
||||||
TextBox {
|
|
||||||
content: s,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Component for TextBox {
|
|
||||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
|
||||||
let upper_left = upper_left!(area);
|
|
||||||
let bottom_right = bottom_right!(area);
|
|
||||||
let mut x = get_x(upper_left);
|
|
||||||
let y = get_y(upper_left);
|
|
||||||
for c in self.content.chars() {
|
|
||||||
grid[(x,y)].set_ch(c);
|
|
||||||
x += 1;
|
|
||||||
if x == get_x(bottom_right) + 1 {
|
|
||||||
x = get_x(upper_left);
|
|
||||||
}
|
|
||||||
|
|
||||||
if y == get_y(bottom_right) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn process_event(&mut self, _event: &UIEvent, _context: &mut Context) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A pager for text.
|
/// A pager for text.
|
||||||
/// `Pager` holds its own content in its own `CellBuffer` and when `draw` is called, it draws the
|
/// `Pager` holds its own content in its own `CellBuffer` and when `draw` is called, it draws the
|
||||||
/// current view of the text. It is responsible for scrolling etc.
|
/// current view of the text. It is responsible for scrolling etc.
|
||||||
|
@ -196,12 +132,14 @@ impl Pager {
|
||||||
let height = lines.len();
|
let height = lines.len();
|
||||||
let width = lines.iter().map(|l| l.len()).max().unwrap_or(0);
|
let width = lines.iter().map(|l| l.len()).max().unwrap_or(0);
|
||||||
let mut content = CellBuffer::new(width, height, Cell::with_char(' '));
|
let mut content = CellBuffer::new(width, height, Cell::with_char(' '));
|
||||||
for (i, l) in lines.iter().enumerate() {
|
if width > 0 {
|
||||||
write_string_to_grid(l,
|
for (i, l) in lines.iter().enumerate() {
|
||||||
&mut content,
|
write_string_to_grid(l,
|
||||||
Color::Default,
|
&mut content,
|
||||||
Color::Default,
|
Color::Default,
|
||||||
((0, i), (width -1, i)));
|
Color::Default,
|
||||||
|
((0, i), (width -1, i)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Pager {
|
Pager {
|
||||||
cursor_pos: 0,
|
cursor_pos: 0,
|
||||||
|
@ -215,24 +153,24 @@ impl Pager {
|
||||||
|
|
||||||
impl Component for Pager {
|
impl Component for Pager {
|
||||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||||
if !self.dirty {
|
if !self.is_dirty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let upper_left = upper_left!(area);
|
let upper_left = upper_left!(area);
|
||||||
let bottom_right = bottom_right!(area);
|
let bottom_right = bottom_right!(area);
|
||||||
|
|
||||||
|
self.dirty = false;
|
||||||
|
if self.height == 0 || self.height == self.cursor_pos {
|
||||||
|
return;
|
||||||
|
}
|
||||||
clear_area(grid,
|
clear_area(grid,
|
||||||
(upper_left, bottom_right));
|
(upper_left, bottom_right));
|
||||||
context.dirty_areas.push_back((upper_left, bottom_right));
|
context.dirty_areas.push_back((upper_left, bottom_right));
|
||||||
|
|
||||||
if self.height == 0 {
|
//let pager_context: usize = context.settings.pager.pager_context;
|
||||||
return;
|
//let pager_stop: bool = context.settings.pager.pager_stop;
|
||||||
}
|
//let rows = get_y(bottom_right) - get_y(upper_left);
|
||||||
|
//let page_length = rows / self.height;
|
||||||
let pager_context: usize = context.settings.pager.pager_context;
|
|
||||||
let pager_stop: bool = context.settings.pager.pager_stop;
|
|
||||||
let rows = get_y(bottom_right) - get_y(upper_left);
|
|
||||||
let page_length = rows / self.height;
|
|
||||||
self.dirty = false;
|
|
||||||
let mut inner_x = 0;
|
let mut inner_x = 0;
|
||||||
let mut inner_y = self.cursor_pos;
|
let mut inner_y = self.cursor_pos;
|
||||||
|
|
||||||
|
@ -267,6 +205,9 @@ impl Component for Pager {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
UIEventType::ChangeMode(UIMode::Normal) => {
|
||||||
|
self.dirty = true;
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -279,8 +220,10 @@ impl Component for Pager {
|
||||||
/// Status bar.
|
/// Status bar.
|
||||||
pub struct StatusBar {
|
pub struct StatusBar {
|
||||||
container: Entity,
|
container: Entity,
|
||||||
position: usize,
|
|
||||||
status: String,
|
status: String,
|
||||||
|
ex_buffer: String,
|
||||||
|
mode: UIMode,
|
||||||
|
height: usize,
|
||||||
dirty: bool,
|
dirty: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,24 +231,31 @@ impl StatusBar {
|
||||||
pub fn new(container: Entity) -> Self {
|
pub fn new(container: Entity) -> Self {
|
||||||
StatusBar {
|
StatusBar {
|
||||||
container: container,
|
container: container,
|
||||||
position: 0,
|
status: String::with_capacity(256),
|
||||||
status: String::with_capacity(250),
|
ex_buffer: String::with_capacity(256),
|
||||||
dirty: true,
|
dirty: true,
|
||||||
|
mode: UIMode::Normal,
|
||||||
|
height: 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn draw_status_bar(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
fn draw_status_bar(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||||
if !self.dirty {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.dirty = false;
|
|
||||||
clear_area(grid, area);
|
clear_area(grid, area);
|
||||||
let x = write_string_to_grid(&self.status,
|
write_string_to_grid(&self.status,
|
||||||
grid,
|
grid,
|
||||||
Color::Byte(36),
|
Color::Byte(36),
|
||||||
Color::Default,
|
Color::Default,
|
||||||
area);
|
area);
|
||||||
context.dirty_areas.push_back(area);
|
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,
|
||||||
|
area);
|
||||||
|
context.dirty_areas.push_back(area);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -313,24 +263,60 @@ impl Component for StatusBar {
|
||||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||||
let upper_left = upper_left!(area);
|
let upper_left = upper_left!(area);
|
||||||
let bottom_right = bottom_right!(area);
|
let bottom_right = bottom_right!(area);
|
||||||
|
|
||||||
let total_rows = get_y(bottom_right) - get_y(upper_left);
|
let total_rows = get_y(bottom_right) - get_y(upper_left);
|
||||||
if total_rows == 0 {
|
if total_rows <= self.height {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let height = self.height;
|
||||||
|
|
||||||
let _ = self.container.component.draw(grid,
|
let _ = self.container.component.draw(grid,
|
||||||
(upper_left, (get_x(bottom_right), get_y(bottom_right)-1)),
|
(upper_left, (get_x(bottom_right), get_y(bottom_right) - height)),
|
||||||
context);
|
context);
|
||||||
|
|
||||||
|
if !self.is_dirty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.dirty = false;
|
||||||
self.draw_status_bar(grid, (set_y(upper_left, get_y(bottom_right)), bottom_right), context);
|
self.draw_status_bar(grid, (set_y(upper_left, get_y(bottom_right)), bottom_right), context);
|
||||||
|
match self.mode {
|
||||||
|
UIMode::Normal => {
|
||||||
|
},
|
||||||
|
UIMode::Execute => {
|
||||||
|
self.draw_execute_bar(grid,
|
||||||
|
(set_y(upper_left, get_y(bottom_right) - height + 1), set_y(bottom_right, get_y(bottom_right) - height+1)),
|
||||||
|
context);
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn process_event(&mut self, event: &UIEvent, context: &mut Context) {
|
fn process_event(&mut self, event: &UIEvent, context: &mut Context) {
|
||||||
self.container.rcv_event(event, context);
|
self.container.rcv_event(event, context);
|
||||||
match event.event_type {
|
match event.event_type {
|
||||||
UIEventType::RefreshMailbox(ref m) => {
|
UIEventType::RefreshMailbox(ref m) => {
|
||||||
self.status = format!("Mailbox: {}, Messages: {}, New: {}", m.folder.get_name(), m.collection.len(), m.collection.iter().filter(|e| !e.is_seen()).count());
|
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;
|
self.dirty = true;
|
||||||
|
|
||||||
},
|
},
|
||||||
|
UIEventType::ChangeMode(m) => {
|
||||||
|
let offset = self.status.find('|').unwrap_or(self.status.len());
|
||||||
|
self.status.replace_range(..offset, &format!("{} ", m));
|
||||||
|
self.dirty = true;
|
||||||
|
self.mode = m;
|
||||||
|
match m {
|
||||||
|
UIMode::Normal => {
|
||||||
|
self.height = 1;
|
||||||
|
self.ex_buffer.clear()
|
||||||
|
},
|
||||||
|
UIMode::Execute => {
|
||||||
|
self.height = 2;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
UIEventType::ExInput(Key::Char(c)) => {
|
||||||
|
self.dirty = true;
|
||||||
|
self.ex_buffer.push(c);
|
||||||
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,8 @@ extern crate ncurses;
|
||||||
extern crate melib;
|
extern crate melib;
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
pub use self::position::*;
|
pub use self::position::*;
|
||||||
|
|
||||||
/* Color pairs; foreground && background. */
|
/* Color pairs; foreground && background. */
|
||||||
|
@ -57,7 +59,6 @@ pub enum ThreadEvent {
|
||||||
/// A watched folder has been refreshed.
|
/// A watched folder has been refreshed.
|
||||||
RefreshMailbox{ name: String },
|
RefreshMailbox{ name: String },
|
||||||
UIEventType(UIEventType),
|
UIEventType(UIEventType),
|
||||||
GoCmd(usize),
|
|
||||||
//Decode { _ }, // For gpg2 signature check
|
//Decode { _ }, // For gpg2 signature check
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,10 +94,12 @@ pub use self::components::*;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum UIEventType {
|
pub enum UIEventType {
|
||||||
Input(Key),
|
Input(Key),
|
||||||
|
ExInput(Key),
|
||||||
RefreshMailbox(Mailbox),
|
RefreshMailbox(Mailbox),
|
||||||
//Quit?
|
//Quit?
|
||||||
Resize,
|
Resize,
|
||||||
ChangeMailbox(usize),
|
ChangeMailbox(usize),
|
||||||
|
ChangeMode(UIMode),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -106,19 +109,27 @@ pub struct UIEvent {
|
||||||
pub event_type: UIEventType,
|
pub event_type: UIEventType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum UIMode {
|
||||||
|
Normal,
|
||||||
|
Execute,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for UIMode {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", match *self {
|
||||||
|
UIMode::Normal => { "NORMAL" },
|
||||||
|
UIMode::Execute => { "EX" },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
pub accounts: Vec<Account>,
|
pub accounts: Vec<Account>,
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
queue: VecDeque<UIEvent>,
|
|
||||||
/// Areas of the screen that must be redrawn in the next render
|
/// Areas of the screen that must be redrawn in the next render
|
||||||
dirty_areas: VecDeque<Area>,
|
dirty_areas: VecDeque<Area>,
|
||||||
backends: Backends,
|
backends: Backends,
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Context {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct State<W: Write> {
|
pub struct State<W: Write> {
|
||||||
|
@ -160,7 +171,6 @@ impl<W: Write> State<W> {
|
||||||
accounts: settings.accounts.iter().map(|(n, a_s)| { Account::new(n.to_string(), a_s.clone(), &backends) }).collect(),
|
accounts: settings.accounts.iter().map(|(n, a_s)| { Account::new(n.to_string(), a_s.clone(), &backends) }).collect(),
|
||||||
backends: backends,
|
backends: backends,
|
||||||
settings: settings,
|
settings: settings,
|
||||||
queue: VecDeque::with_capacity(5),
|
|
||||||
dirty_areas: VecDeque::with_capacity(5),
|
dirty_areas: VecDeque::with_capacity(5),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -187,6 +197,7 @@ impl<W: Write> State<W> {
|
||||||
self.draw_entity(i);
|
self.draw_entity(i);
|
||||||
}
|
}
|
||||||
let areas: Vec<Area> = self.context.dirty_areas.drain(0..).collect();
|
let areas: Vec<Area> = self.context.dirty_areas.drain(0..).collect();
|
||||||
|
eprintln!("redrawing {} areas", areas.len());
|
||||||
/* draw each dirty area */
|
/* draw each dirty area */
|
||||||
for a in areas {
|
for a in areas {
|
||||||
self.draw_area(a);
|
self.draw_area(a);
|
||||||
|
@ -202,17 +213,17 @@ impl<W: Write> State<W> {
|
||||||
let c = self.grid[(x,y)];
|
let c = self.grid[(x,y)];
|
||||||
|
|
||||||
if c.get_bg() != cells::Color::Default {
|
if c.get_bg() != cells::Color::Default {
|
||||||
write!(self.stdout, "{}", termion::color::Bg(c.get_bg().as_termion()));
|
write!(self.stdout, "{}", termion::color::Bg(c.get_bg().as_termion())).unwrap();
|
||||||
}
|
}
|
||||||
if c.get_fg() != cells::Color::Default {
|
if c.get_fg() != cells::Color::Default {
|
||||||
write!(self.stdout, "{}", termion::color::Fg(c.get_fg().as_termion()));
|
write!(self.stdout, "{}", termion::color::Fg(c.get_fg().as_termion())).unwrap();
|
||||||
}
|
}
|
||||||
write!(self.stdout, "{}",c.ch()).unwrap();
|
write!(self.stdout, "{}",c.ch()).unwrap();
|
||||||
if c.get_bg() != cells::Color::Default {
|
if c.get_bg() != cells::Color::Default {
|
||||||
write!(self.stdout, "{}", termion::color::Bg(termion::color::Reset));
|
write!(self.stdout, "{}", termion::color::Bg(termion::color::Reset)).unwrap();
|
||||||
}
|
}
|
||||||
if c.get_fg() != cells::Color::Default {
|
if c.get_fg() != cells::Color::Default {
|
||||||
write!(self.stdout, "{}", termion::color::Fg(termion::color::Reset));
|
write!(self.stdout, "{}", termion::color::Fg(termion::color::Reset)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -229,9 +240,7 @@ impl<W: Write> State<W> {
|
||||||
let cols = self.cols;
|
let cols = self.cols;
|
||||||
let rows = self.rows;
|
let rows = self.rows;
|
||||||
|
|
||||||
/* Only draw dirty areas */
|
|
||||||
self.draw_area(((0, 0), (cols-1, rows-1)));
|
self.draw_area(((0, 0), (cols-1, rows-1)));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
pub fn draw_entity(&mut self, idx: usize) {
|
pub fn draw_entity(&mut self, idx: usize) {
|
||||||
let entity = &mut self.entities[idx];
|
let entity = &mut self.entities[idx];
|
||||||
|
|
Loading…
Reference in New Issue