Add some doc
parent
ba8508b987
commit
c141496038
21
src/bin.rs
21
src/bin.rs
|
@ -19,12 +19,13 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
mod ui;
|
||||
pub mod ui;
|
||||
use ui::*;
|
||||
|
||||
extern crate melib;
|
||||
extern crate nom;
|
||||
extern crate termion;
|
||||
use melib::*;
|
||||
pub use melib::*;
|
||||
|
||||
use std::sync::mpsc::{sync_channel, SyncSender, Receiver};
|
||||
use std::thread;
|
||||
|
@ -64,12 +65,13 @@ fn main() {
|
|||
})));
|
||||
}
|
||||
|
||||
eprintln!("account is {:?}", account);
|
||||
|
||||
|
||||
|
||||
let mut state = State::new(_stdout);
|
||||
|
||||
let a = Entity {component: Box::new(TextBox::new("a text box".to_string())) };
|
||||
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)) };
|
||||
|
@ -77,11 +79,6 @@ fn main() {
|
|||
state.render();
|
||||
'main: loop {
|
||||
let mailbox = &mut account[j];
|
||||
//let mut index: Box<Window> = match *mailbox.as_ref().unwrap() {
|
||||
// Ok(ref v) => Box::new(Index::new(v)),
|
||||
// Err(ref v) => Box::new(ErrorWindow::new((*v).clone())),
|
||||
//};
|
||||
////eprintln!("{:?}", set);
|
||||
match *mailbox.as_ref().unwrap() {
|
||||
Ok(ref v) => {
|
||||
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::RefreshMailbox(v.clone()) });
|
||||
|
@ -89,8 +86,6 @@ fn main() {
|
|||
Err(_) => {},
|
||||
};
|
||||
|
||||
//index.draw();
|
||||
//
|
||||
state.render();
|
||||
|
||||
'inner: loop {
|
||||
|
@ -100,13 +95,12 @@ fn main() {
|
|||
key @ Key::Char('j') | key @ Key::Char('k') => {
|
||||
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Input(key)});
|
||||
state.render();
|
||||
},
|
||||
},
|
||||
key @ Key::Up | key @ Key::Down => {
|
||||
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Input(key)});
|
||||
state.render();
|
||||
}
|
||||
Key::Char('\n') => {
|
||||
// index.handle_input(k);
|
||||
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Input(Key::Char('\n'))});
|
||||
state.render();
|
||||
}
|
||||
|
@ -115,9 +109,6 @@ fn main() {
|
|||
state.render();
|
||||
}
|
||||
Key::F(_) => {
|
||||
// if !index.handle_input(k) {
|
||||
// break 'main;
|
||||
// }
|
||||
},
|
||||
Key::Char('q') | Key::Char('Q') => {
|
||||
break 'main;
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
extern crate config;
|
||||
extern crate xdg;
|
||||
extern crate serde;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::fs;
|
||||
|
|
|
@ -23,6 +23,7 @@ use mailbox::*;
|
|||
use mailbox::backends::{RefreshEventConsumer, Backends};
|
||||
use conf::AccountSettings;
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Account {
|
||||
name: String,
|
||||
|
@ -60,6 +61,13 @@ impl Account {
|
|||
pub fn watch(&self, r: RefreshEventConsumer) -> () {
|
||||
self.backend.watch(r, &self.settings.folders);
|
||||
}
|
||||
/* This doesn't represent the number of correctly parsed mailboxes though */
|
||||
pub fn len(&self) -> usize {
|
||||
self.folders.len()
|
||||
}
|
||||
pub fn list_folders(&self) -> Vec<String> {
|
||||
self.settings.folders.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for Account {
|
||||
|
|
|
@ -63,7 +63,7 @@ impl Mailbox {
|
|||
threaded_collection: threaded_collection,
|
||||
})
|
||||
}
|
||||
pub fn get_length(&self) -> usize {
|
||||
pub fn len(&self) -> usize {
|
||||
self.collection.len()
|
||||
}
|
||||
pub fn get_threaded_mail(&self, i: usize) -> usize {
|
||||
|
|
|
@ -5,8 +5,10 @@ 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_WIDTH: usize = 500;
|
||||
|
||||
/// A list of all mail (`Envelope`s) in a `Mailbox`. On `\n` it opens the `Envelope` content in a
|
||||
/// `Pager`.
|
||||
pub struct MailListing {
|
||||
cursor_pos: usize,
|
||||
new_cursor_pos: usize,
|
||||
|
@ -14,6 +16,7 @@ pub struct MailListing {
|
|||
// sorting
|
||||
content: CellBuffer,
|
||||
dirty: bool,
|
||||
/// If `self.pager` exists or not.
|
||||
unfocused: bool,
|
||||
// content (2-d vec of bytes) or Cells?
|
||||
// current view on top of content
|
||||
|
@ -25,13 +28,13 @@ pub struct MailListing {
|
|||
|
||||
impl MailListing {
|
||||
pub fn new(mailbox: Mailbox) -> Self {
|
||||
let length = mailbox.get_length();
|
||||
let length = mailbox.len();
|
||||
|
||||
MailListing {
|
||||
cursor_pos: 0,
|
||||
new_cursor_pos: 0,
|
||||
length: length,
|
||||
content: CellBuffer::new(max_width, length+1, Cell::with_char(' ')),
|
||||
content: CellBuffer::new(MAX_WIDTH, length+1, Cell::with_char(' ')),
|
||||
dirty: false,
|
||||
unfocused: false,
|
||||
mailbox: mailbox,
|
||||
|
@ -42,11 +45,14 @@ impl MailListing {
|
|||
|
||||
|
||||
impl MailListing {
|
||||
/// Draw only the list of `Envelope`s.
|
||||
fn draw_list(&mut self, grid: &mut CellBuffer, upper_left: Pos, bottom_right: Pos) {
|
||||
if self.length == 0 {
|
||||
write_string_to_grid(&format!("Folder `{}` is empty.", self.mailbox.path), grid, Color::Default, Color::Default, upper_left, upper_left);
|
||||
return;
|
||||
}
|
||||
/* 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 {
|
||||
for idx in [self.cursor_pos, self.new_cursor_pos].iter() {
|
||||
let color = if self.cursor_pos == *idx { if *idx % 2 == 0 { Color::Byte(236) } else {Color::Default } } else { Color::Byte(246) };
|
||||
|
@ -60,17 +66,10 @@ impl MailListing {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
let mut idx = 0;
|
||||
for y in get_y(upper_left)..get_y(bottom_right) {
|
||||
if idx == self.length {
|
||||
for _y in y..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);
|
||||
}
|
||||
}
|
||||
clear_area(grid, set_y(upper_left, y), bottom_right);
|
||||
break;
|
||||
}
|
||||
/* Write an entire line for each envelope entry. */
|
||||
|
@ -87,37 +86,17 @@ impl MailListing {
|
|||
}
|
||||
}
|
||||
|
||||
/// Create a pager for the `Envelope` currently under the cursor.
|
||||
fn draw_mail_view(&mut self, grid: &mut CellBuffer, upper_left: Pos, bottom_right: Pos) {
|
||||
//Pager
|
||||
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);
|
||||
|
||||
self.pager = Some(Pager::new(mail, height, width));
|
||||
let pager = self.pager.as_mut().unwrap();
|
||||
pager.dirty = true;
|
||||
pager.draw(grid, upper_left,bottom_right);
|
||||
|
||||
/*
|
||||
let text = mail.get_body().get_text();
|
||||
let lines: Vec<&str> = text.trim().split('\n').collect();
|
||||
let lines_length = lines.len();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
for (i, l) in lines.iter().enumerate() {
|
||||
write_string_to_grid(l, grid, Color::Default, Color::Default, set_y(upper_left, get_y(upper_left)+i), bottom_right);
|
||||
}
|
||||
*/
|
||||
}
|
||||
fn redraw_cursor(&mut self, _upper_left: Pos, _bottom_right: Pos, _grid: &mut CellBuffer) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Component for MailListing {
|
||||
|
@ -135,7 +114,7 @@ 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: ratio in Configuration */
|
||||
/* 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;
|
||||
|
||||
|
@ -150,8 +129,17 @@ impl Component for MailListing {
|
|||
if self.length == 0 {
|
||||
return;
|
||||
}
|
||||
for i in get_x(upper_left)..get_x(bottom_right)+1 {
|
||||
grid[(i, mid)].set_ch('─');
|
||||
{
|
||||
/* TODO: Move the box drawing business in separate functions */
|
||||
if get_x(upper_left) > 0 {
|
||||
if grid[(get_x(upper_left) - 1, mid)].ch() == VERT_BOUNDARY {
|
||||
grid[(get_x(upper_left) - 1, mid)].set_ch(LIGHT_VERTICAL_AND_RIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
for i in get_x(upper_left)..get_x(bottom_right)+1 {
|
||||
grid[(i, mid)].set_ch('─');
|
||||
}
|
||||
}
|
||||
|
||||
let headers_height: usize = 6;
|
||||
|
@ -200,13 +188,13 @@ impl Component for MailListing {
|
|||
match event.event_type {
|
||||
UIEventType::Input(Key::Up) => {
|
||||
if self.cursor_pos > 0 {
|
||||
self.new_cursor_pos -= 1;
|
||||
self.new_cursor_pos -= 1;
|
||||
self.dirty = true;
|
||||
}
|
||||
},
|
||||
UIEventType::Input(Key::Down) => {
|
||||
if self.length > 0 && self.cursor_pos < self.length - 1 {
|
||||
self.new_cursor_pos += 1;
|
||||
self.new_cursor_pos += 1;
|
||||
self.dirty = true;
|
||||
}
|
||||
},
|
||||
|
@ -237,3 +225,47 @@ impl Component for MailListing {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AccountMenu {
|
||||
entries: Vec<(usize, String)>,
|
||||
dirty: bool,
|
||||
|
||||
}
|
||||
|
||||
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() {
|
||||
eprintln!("acc[{}] is {:?}", idx, acc);
|
||||
entries.push((idx, acc.clone()));
|
||||
}
|
||||
AccountMenu {
|
||||
entries: entries,
|
||||
dirty: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for AccountMenu {
|
||||
fn draw(&mut self, grid: &mut CellBuffer, upper_left: Pos, bottom_right: Pos) {
|
||||
eprintln!("reached accountmenu draw {:?}", self);
|
||||
if !(self.dirty) {
|
||||
return;
|
||||
}
|
||||
self.dirty = false;
|
||||
let mut idx = 0;
|
||||
for y in get_y(upper_left)..get_y(bottom_right) {
|
||||
if idx == self.entries.len() {
|
||||
break;
|
||||
}
|
||||
let s = format!("{:?}",self.entries[idx]);
|
||||
eprintln!("wrote {} to menu", s);
|
||||
write_string_to_grid(&s, grid, Color::Red, Color::Default, set_y(upper_left, y), bottom_right);
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
fn process_event(&mut self, _event: &UIEvent, _queue: &mut VecDeque<UIEvent>) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,12 +55,15 @@ const LIGHT_DOWN_AND_HORIZONTAL: char = '┬';
|
|||
|
||||
const LIGHT_UP_AND_HORIZONTAL: char = '┴';
|
||||
|
||||
/// `Entity` is a container for Components. Totally useless now so if it is not useful in the
|
||||
/// future (ie hold some information, id or state) it should be removed.
|
||||
pub struct Entity {
|
||||
//queue: VecDeque,
|
||||
pub component: Box<Component>, // more than one?
|
||||
}
|
||||
|
||||
impl Entity {
|
||||
/// Pass events to child component.
|
||||
pub fn rcv_event(&mut self, event: &UIEvent, queue: &mut VecDeque<UIEvent>) {
|
||||
self.component.process_event(&event, queue);
|
||||
}
|
||||
|
@ -72,16 +75,18 @@ impl fmt::Debug for Entity {
|
|||
}
|
||||
}
|
||||
|
||||
/// Types implementing this Trait can draw on the terminal and receive events.
|
||||
/// If a type wants to skip drawing if it has not changed anything, it can hold some flag in its
|
||||
/// fields (eg self.dirty = false) and act upon that in their `draw` implementation.
|
||||
pub trait Component {
|
||||
fn draw(&mut self, grid: &mut CellBuffer, upper_left: Pos, bottom_right: Pos);
|
||||
fn process_event(&mut self, &UIEvent, &mut VecDeque<UIEvent>);
|
||||
fn process_event(&mut self, event: &UIEvent, queue: &mut VecDeque<UIEvent>);
|
||||
}
|
||||
|
||||
|
||||
fn write_string_to_grid(s: &str, grid: &mut CellBuffer, fg_color: Color, bg_color: Color, upper_left: Pos, bottom_right: Pos) -> usize {
|
||||
let (mut x, mut y) = upper_left;
|
||||
for c in s.chars() {
|
||||
eprintln!(" (x,y) = ({},{})", x,y);
|
||||
grid[(x,y)].set_ch(c);
|
||||
grid[(x,y)].set_fg(fg_color);
|
||||
grid[(x,y)].set_bg(bg_color);
|
||||
|
|
|
@ -70,10 +70,11 @@ impl Component for HSplit {
|
|||
}
|
||||
}
|
||||
|
||||
/// A horizontally split in half container.
|
||||
/// A vertically split in half container.
|
||||
pub struct VSplit {
|
||||
left: Entity,
|
||||
right: Entity,
|
||||
/// This is the width of the right container to the entire width.
|
||||
ratio: usize, // right/(container width) * 100
|
||||
}
|
||||
|
||||
|
@ -93,7 +94,7 @@ impl Component for VSplit {
|
|||
let total_cols = get_x(bottom_right) - get_x(upper_left);
|
||||
let right_entity_width = (self.ratio*total_cols )/100;
|
||||
let mid = get_x(bottom_right) - right_entity_width;
|
||||
|
||||
|
||||
if get_y(upper_left)> 1 {
|
||||
let c = grid.get(mid, get_y(upper_left)-1).map(|a| a.ch()).unwrap_or_else(|| ' ');
|
||||
match c {
|
||||
|
@ -159,6 +160,9 @@ impl Component for TextBox {
|
|||
}
|
||||
}
|
||||
|
||||
/// A pager for text.
|
||||
/// `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.
|
||||
pub struct Pager {
|
||||
cursor_pos: usize,
|
||||
rows: usize,
|
||||
|
@ -192,7 +196,7 @@ impl Component for Pager {
|
|||
fn draw(&mut self, grid: &mut CellBuffer, upper_left: Pos, bottom_right: Pos) {
|
||||
if !self.dirty {
|
||||
return;
|
||||
}
|
||||
}
|
||||
clear_area(grid, (get_x(upper_left), get_y(upper_left)-1), bottom_right);
|
||||
self.dirty = false;
|
||||
let mut inner_x = 0;
|
||||
|
|
Loading…
Reference in New Issue