Move ui and melib to different crates

closes #9
embed
Manos Pitsidianakis 2018-07-20 12:15:06 +03:00
parent 6dd247b371
commit 8c98d3a5a0
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
28 changed files with 461 additions and 363 deletions

View File

@ -3,32 +3,16 @@ name = "meli"
version = "0.1.0"
authors = ["Manos Pitsidianakis <el13635@mail.ntua.gr>"]
[lib]
name = "melib"
path = "src/lib.rs"
[[bin]]
name = "meli"
path = "src/bin.rs"
[dependencies]
chrono = "0.4"
xdg = "2.1.0"
config = "0.6"
serde_derive = "^1.0.8"
serde = "^1.0.8"
nom = "3.2.0"
memmap = "0.5.2"
base64 = "*"
crossbeam = "^0.3.0"
fnv = "1.0.3"
encoding = "0.2.33"
bitflags = "1.0"
notify = "4.0.1"
termion = "1.5.1"
chan = "0.1.21"
chan-signal = "0.3.1"
notify-rust = "^3"
melib = { path = "melib", version = "*" }
ui = { path = "ui", version = "*" }
[profile.release]
#lto = true

22
melib/Cargo.toml 100644
View File

@ -0,0 +1,22 @@
[package]
name = "melib"
version = "0.0.1" #:version
authors = []
workspace = ".."
[dependencies]
notify = "4.0.1"
notify-rust = "^3"
crossbeam = "^0.3.0"
fnv = "1.0.3"
config = "0.6"
xdg = "2.1.0"
chrono = "0.4"
serde_derive = "^1.0.8"
serde = "^1.0.8"
nom = "3.2.0"
memmap = "0.5.2"
base64 = "*"
encoding = "0.2.33"
bitflags = "1.0"
termion = "1.5.1"

View File

@ -204,7 +204,7 @@ impl AttachmentBuilder {
};
AttachmentType::Multipart {
of_type: multipart_type,
subattachments: Attachment::subattachments(&self.raw, b),
subattachments: Self::subattachments(&self.raw, b),
}
}
ContentType::Unsupported { ref tag } => AttachmentType::Data { tag: tag.clone() },
@ -216,6 +216,48 @@ impl AttachmentBuilder {
attachment_type: attachment_type,
}
}
pub fn subattachments(raw: &[u8], boundary: &str) -> Vec<Attachment> {
let boundary_length = boundary.len();
match parser::attachments(raw, &boundary[0..boundary_length - 2], boundary).to_full_result()
{
Ok(attachments) => {
let mut vec = Vec::with_capacity(attachments.len());
for a in attachments {
let (headers, body) = match parser::attachment(a).to_full_result() {
Ok(v) => v,
Err(_) => {
eprintln!("error in parsing attachment");
eprintln!("\n-------------------------------");
eprintln!("{}\n", ::std::string::String::from_utf8_lossy(a));
eprintln!("-------------------------------\n");
continue;
}
};
let mut builder = AttachmentBuilder::new(body);
for (name, value) in headers {
if name.eq_ignore_ascii_case("content-type") {
builder.content_type(value);
} else if name.eq_ignore_ascii_case("content-transfer-encoding") {
builder.content_transfer_encoding(value);
}
}
vec.push(builder.build());
}
vec
}
a => {
eprintln!(
"error {:?}\n\traw: {:?}\n\tboundary: {:?}",
a,
::std::str::from_utf8(raw).unwrap(),
boundary
);
Vec::new()
}
}
}
}
@ -267,45 +309,28 @@ impl Attachment {
pub fn get_tag(&self) -> String {
format!("{}/{}", self.content_type.0, self.content_type.1).to_string()
}
pub fn subattachments(raw: &[u8], boundary: &str) -> Vec<Attachment> {
let boundary_length = boundary.len();
match parser::attachments(raw, &boundary[0..boundary_length - 2], boundary).to_full_result()
{
Ok(attachments) => {
let mut vec = Vec::with_capacity(attachments.len());
for a in attachments {
let (headers, body) = match parser::attachment(a).to_full_result() {
Ok(v) => v,
Err(_) => {
eprintln!("error in parsing attachment");
eprintln!("\n-------------------------------");
eprintln!("{}\n", ::std::string::String::from_utf8_lossy(a));
eprintln!("-------------------------------\n");
pub fn count_attachments(&mut self) -> usize {
let mut counter = 0;
continue;
}
};
let mut builder = AttachmentBuilder::new(body);
for (name, value) in headers {
if name.eq_ignore_ascii_case("content-type") {
builder.content_type(value);
} else if name.eq_ignore_ascii_case("content-transfer-encoding") {
builder.content_transfer_encoding(value);
}
}
vec.push(builder.build());
fn count_recursive(att: &Attachment, counter: &mut usize) {
match att.attachment_type {
AttachmentType::Data { .. } => {
*counter += 1;
}
vec
}
a => {
eprintln!(
"error in 469 {:?}\n\traw: {:?}\n\tboundary: {:?}",
a,
::std::str::from_utf8(raw).unwrap(),
boundary
);
Vec::new()
AttachmentType::Text { .. } => {
}
AttachmentType::Multipart {
of_type: ref multipart_type,
subattachments: ref sub_att_vec,
} => if *multipart_type != MultipartType::Alternative {
for a in sub_att_vec {
count_recursive(a, counter);
}
},
}
}
count_recursive(&self, &mut counter);
counter
}
}

View File

@ -25,12 +25,8 @@ The mail handling stuff is done in the `melib` crate which includes all backend
*/
extern crate melib;
#[macro_use]
extern crate nom;
extern crate termion;
extern crate notify_rust;
extern crate ui;
pub mod ui;
use ui::*;
pub use melib::*;

22
src/python/mod.rs 100644
View File

@ -0,0 +1,22 @@
#![cfg(feature = "python")]
use pyo3::prelude::*;
#[pymodinit(pythonmeli)]
fn pythonmeli(py: Python, m: &PyModule) -> PyResult<()> {
// pyo3 aware function. All of our python interface could be declared in a separate module.
// Note that the `#[pyfn()]` annotation automatically converts the arguments from
// Python objects to Rust values; and the Rust return value back into a Python object.
#[pyfn(m, "sum_as_string")]
fn sum_as_string_py(_py: Python, a:i64, b:i64) -> PyResult<String> {
let out = sum_as_string(a, b);
Ok(out)
}
Ok(())
}
// logic implemented as a normal rust function
fn sum_as_string(a:i64, b:i64) -> String {
format!("{}", a + b).to_string()
}

13
ui/Cargo.toml 100644
View File

@ -0,0 +1,13 @@
[package]
name = "ui"
version = "0.0.1" #:version
authors = []
workspace = ".."
[dependencies]
melib = { path = "../melib", version = "*" }
termion = "1.5.1"
chan = "0.1.21"
notify = "4.0.1"
notify-rust = "^3"
nom = "3.2.0"

View File

@ -1,9 +1,4 @@
/*! Entities that handle Mail specific functions.
*/
use ui::components::*;
use ui::cells::*;
use super::*;
const MAX_COLS: usize = 500;
/// A list of all mail (`Envelope`s) in a `Mailbox`. On `\n` it opens the `Envelope` content in a
@ -30,9 +25,6 @@ impl MailListing {
format!("{} {} {:.85}",idx,&e.get_datetime().format("%Y-%m-%d %H:%M:%S").to_string(),e.get_subject())
}
pub fn new() -> Self {
let content = CellBuffer::new(0, 0, Cell::with_char(' '));
MailListing {
@ -60,7 +52,12 @@ impl MailListing {
// Inform State that we changed the current folder view.
context.replies.push_back(UIEvent { id: 0, event_type: UIEventType::RefreshMailbox((self.cursor_pos.0, self.cursor_pos.1)) });
self.length = mailbox.len();
self.length = if threaded {
mailbox.threaded_collection.len()
} else {
mailbox.len()
};
let mut content = CellBuffer::new(MAX_COLS, self.length+1, Cell::with_char(' '));
if self.length == 0 {
write_string_to_grid(&format!("Folder `{}` is empty.",
@ -68,7 +65,8 @@ impl MailListing {
&mut content,
Color::Default,
Color::Default,
((0, 0), (MAX_COLS-1, 0)));
((0, 0), (MAX_COLS-1, 0)),
true);
self.content = content;
return;
}
@ -118,11 +116,12 @@ impl MailListing {
} else {
Color::Default
};
let x = write_string_to_grid(&MailListing::make_thread_entry(envelope, idx, indentation, container, &indentations),
let (x, y) = write_string_to_grid(&MailListing::make_thread_entry(envelope, idx, indentation, container, &indentations),
&mut content,
fg_color,
bg_color,
((0, idx) , (MAX_COLS-1, idx)));
((0, idx) , (MAX_COLS-1, idx)),
false);
for x in x..MAX_COLS {
content[(x,idx)].set_ch(' ');
content[(x,idx)].set_bg(bg_color);
@ -171,11 +170,12 @@ impl MailListing {
} else {
Color::Default
};
let x = write_string_to_grid(&MailListing::make_entry_string(envelope, idx),
let (x, y)= write_string_to_grid(&MailListing::make_entry_string(envelope, idx),
&mut content,
fg_color,
bg_color,
((0, y) , (MAX_COLS-1, y)));
((0, y) , (MAX_COLS-1, y)),
false);
for x in x..MAX_COLS {
content[(x,y)].set_ch(' ');
@ -189,9 +189,15 @@ impl MailListing {
self.content = content;
}
fn highlight_line(&self, grid: &mut CellBuffer, area: Area, idx: usize, context: &mut Context) {
let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1].as_ref().unwrap().as_ref().unwrap();
let envelope: &Envelope = &mailbox.collection[idx];
fn highlight_line(&self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) {
let threaded = context.accounts[self.cursor_pos.0].settings.threaded;
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1].as_ref().unwrap().as_ref().unwrap();
let envelope: &Envelope = if threaded {
let i = mailbox.get_threaded_mail(idx);
&mailbox.collection[i]
} else {
&mailbox.collection[idx]
};
let fg_color = if !envelope.is_seen() {
Color::Byte(0)
@ -202,7 +208,7 @@ impl MailListing {
Color::Byte(246)
} else {
if !envelope.is_seen() {
Color::Byte(252)
Color::Byte(251)
} else if idx % 2 == 0 {
Color::Byte(236)
} else {
@ -256,40 +262,6 @@ impl MailListing {
context.dirty_areas.push_back(area);
}
/// Create a pager for the `Envelope` currently under the cursor.
fn draw_header_view(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
{
let threaded = context.accounts[self.cursor_pos.0].settings.threaded;
let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1].as_ref().unwrap().as_ref().unwrap();
let envelope: &Envelope = if threaded {
let i = mailbox.get_threaded_mail(self.cursor_pos.2);
&mailbox.collection[i]
} else {
&mailbox.collection[self.cursor_pos.2]
};
let pager_filter = context.settings.pager.filter.clone();
self.pager = Some(Pager::new(&envelope, pager_filter));
}
self.pager.as_mut().map(|p| p.draw(grid, area, context));
}
/// Create a pager for the `Envelope` currently under the cursor.
fn draw_attachment_view(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
{
let threaded = context.accounts[self.cursor_pos.0].settings.threaded;
let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1].as_ref().unwrap().as_ref().unwrap();
let envelope: &Envelope = if threaded {
let i = mailbox.get_threaded_mail(self.cursor_pos.2);
&mailbox.collection[i]
} else {
&mailbox.collection[self.cursor_pos.2]
};
let pager_filter = context.settings.pager.filter.clone();
self.pager = Some(Pager::new(&envelope, pager_filter));
}
self.pager.as_mut().map(|p| p.draw(grid, area, context));
}
/// Create a pager for the `Envelope` currently under the cursor.
fn draw_mail_view(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
{
@ -335,6 +307,8 @@ impl MailListing {
}
s.push('─'); s.push('>');
}
s.push_str(&format!(" {}", envelope.get_body().count_attachments()));
if show_subject {
s.push_str(&format!("{:.85}", envelope.get_subject()));
}
@ -360,37 +334,23 @@ impl Component for MailListing {
clear_area(grid, area);
context.dirty_areas.push_back(area);
}
// TODO: Make this configurable. User should be able to choose what headers to display,
// and toggle between full header view and custom header view like in mutt.
let headers_rows: usize = 6;
/* Render the mail body in a pager, basically copy what HSplit does */
let total_rows = get_y(bottom_right) - get_y(upper_left);
let pager_ratio = context.settings.pager.pager_ratio;
let mut bottom_entity_rows = (pager_ratio*total_rows )/100;
if bottom_entity_rows < headers_rows + 2 {
bottom_entity_rows = headers_rows + 2;
}
let bottom_entity_rows = (pager_ratio*total_rows )/100;
if bottom_entity_rows > total_rows {
clear_area(grid, area);
context.dirty_areas.push_back(area);
return;
}
let mid = get_y(upper_left) + total_rows - bottom_entity_rows;
if !self.dirty {
if let Some(ref mut p) = self.pager {
p.draw(grid,
((get_x(upper_left), get_y(upper_left) + mid + headers_rows + 1), bottom_right),
context);
}
return;
}
self.dirty = false;
self.draw_list(grid,
(upper_left, (get_x(bottom_right), get_y(upper_left)+ mid-1)),
context);
if self.length == 0 {
self.dirty = false;
return;
}
{
@ -405,8 +365,10 @@ impl Component for MailListing {
grid[(i, mid)].set_ch('─');
}
}
// TODO: Make headers view configurable
/* Draw header */
let y =
{
let threaded = context.accounts[self.cursor_pos.0].settings.threaded;
let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1].as_ref().unwrap().as_ref().unwrap();
@ -417,66 +379,82 @@ impl Component for MailListing {
&mailbox.collection[self.cursor_pos.2]
};
let x = write_string_to_grid(&format!("Date: {}", envelope.get_date_as_str()),
let (x,y) = write_string_to_grid(&format!("Date: {}", envelope.get_date_as_str()),
grid,
Color::Byte(33),
Color::Default,
(set_y(upper_left, mid+1), set_y(bottom_right, mid+1)));
(set_y(upper_left, mid+1), bottom_right),
true);
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);
grid[(x, y)].set_ch(' ');
grid[(x, y)].set_bg(Color::Default);
grid[(x, y)].set_fg(Color::Default);
}
let x = write_string_to_grid(&format!("From: {}", envelope.get_from()),
let (x,y) = write_string_to_grid(&format!("From: {}", envelope.get_from()),
grid,
Color::Byte(33),
Color::Default,
(set_y(upper_left, mid+2), set_y(bottom_right, mid+2)));
(set_y(upper_left, y+1), bottom_right),
true);
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);
grid[(x, y)].set_ch(' ');
grid[(x, y)].set_bg(Color::Default);
grid[(x, y)].set_fg(Color::Default);
}
let x = write_string_to_grid(&format!("To: {}", envelope.get_to()),
let (x,y) = write_string_to_grid(&format!("To: {}", envelope.get_to()),
grid,
Color::Byte(33),
Color::Default,
(set_y(upper_left, mid+3), set_y(bottom_right, mid+3)));
(set_y(upper_left, y+1), bottom_right),
true);
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);
grid[(x, y)].set_ch(' ');
grid[(x, y)].set_bg(Color::Default);
grid[(x, y)].set_fg(Color::Default);
}
let x = write_string_to_grid(&format!("Subject: {}", envelope.get_subject()),
let (x,y) = write_string_to_grid(&format!("Subject: {}", envelope.get_subject()),
grid,
Color::Byte(33),
Color::Default,
(set_y(upper_left, mid+4), set_y(bottom_right, mid+4)));
(set_y(upper_left, y+1), bottom_right),
true);
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);
grid[(x, y)].set_ch(' ');
grid[(x, y)].set_bg(Color::Default);
grid[(x, y)].set_fg(Color::Default);
}
let x = write_string_to_grid(&format!("Message-ID: {}", envelope.get_message_id_raw()),
let (x, y) = write_string_to_grid(&format!("Message-ID: {}", envelope.get_message_id_raw()),
grid,
Color::Byte(33),
Color::Default,
(set_y(upper_left, mid+5), set_y(bottom_right, mid+5)));
(set_y(upper_left, y+1), bottom_right),
true);
for x in x..=get_x(bottom_right) {
grid[(x, mid+5)].set_ch(' ');
grid[(x, mid+5)].set_bg(Color::Default);
grid[(x, mid+5)].set_fg(Color::Default);
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+1), set_y(bottom_right, y+2)));
context.dirty_areas.push_back((set_y(upper_left, mid), set_y(bottom_right, y+1)));
y + 2
};
if !self.dirty {
if let Some(ref mut p) = self.pager {
p.draw(grid,
((get_x(upper_left), y), bottom_right),
context);
}
return;
}
clear_area(grid,
(set_y(upper_left, mid+headers_rows), set_y(bottom_right, mid+headers_rows)));
context.dirty_areas.push_back((set_y(upper_left, mid), set_y(bottom_right, mid+headers_rows)));
self.dirty = false;
/* Draw body */
self.draw_mail_view(grid,
((get_x(upper_left), get_y(upper_left) + mid + headers_rows + 1), bottom_right),
((get_x(upper_left), y), bottom_right),
context);
}
@ -563,180 +541,3 @@ impl Component for MailListing {
self.dirty || self.pager.as_ref().map(|p| p.is_dirty()).unwrap_or(false)
}
}
#[derive(Debug)]
struct AccountMenuEntry {
name: 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(accounts: &Vec<Account>) -> Self {
let 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 {
accounts: accounts,
dirty: true,
cursor: None,
}
}
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);
let highlight = self.cursor.map(|(x,_)| x == a.index).unwrap_or(false);
let mut parents: Vec<Option<usize>> = vec!(None; a.entries.len());
for (idx, e) in a.entries.iter().enumerate() {
for c in e.1.get_children() {
parents[*c] = Some(idx);
}
}
let mut roots = Vec::new();
for (idx, c) in parents.iter().enumerate() {
if c.is_none() {
roots.push(idx);
}
}
let mut inc = 0;
let mut depth = String::from("");
let mut s = String::from(format!("{}\n", a.name));
fn pop(depth: &mut String) {
depth.pop();
depth.pop();
}
fn push(depth: &mut String, c: char) {
depth.push(c);
}
fn print(root: usize, parents: &Vec<Option<usize>>, depth: &mut String, entries: &Vec<(usize, Folder)>, s: &mut String, inc: &mut usize) -> () {
let len = s.len();
s.insert_str(len, &format!("{} {}\n ", *inc, &entries[root].1.get_name()));
*inc += 1;
let children_no = entries[root].1.get_children().len();
for (idx, child) in entries[root].1.get_children().iter().enumerate() {
let len = s.len();
s.insert_str(len, &format!("{}├─", depth));
push(depth, if idx == children_no - 1 {'│'} else { ' ' });
print(*child, parents, depth, entries, s, inc);
pop(depth);
}
}
for r in roots {
print(r, &parents, &mut depth, &a.entries, &mut s, &mut inc);
}
let lines: Vec<&str> = s.lines().collect();
let lines_len = lines.len();
let mut idx = 0;
for y in get_y(upper_left)..get_y(bottom_right) {
if idx == lines_len {
break;
}
let s = if idx == lines_len - 2 {
format!("{}", lines[idx].replace("", ""))
} else {
format!("{}", lines[idx])
};
let color_fg = if highlight {
if idx > 1 && self.cursor.unwrap().1 == idx - 2 {
Color::Byte(233)
} else {
Color::Byte(15)
}
} else {
Color::Default
};
let color_bg = if highlight {
if idx > 1 && self.cursor.unwrap().1 == idx - 2 {
Color::Byte(15)
} else {
Color::Byte(233)
}
} else {
Color::Default
};
let x = write_string_to_grid(&s,
grid,
color_fg,
color_bg,
(set_y(upper_left, y), bottom_right));
if highlight && idx > 1 && self.cursor.unwrap().1 == idx - 2 {
change_colors(grid, ((x, y),(get_x(bottom_right)+1, y)), color_fg , color_bg);
} else {
change_colors(grid, ((x, y),set_y(bottom_right, y)), color_fg , color_bg);
}
idx += 1;
}
idx - 1
}
}
impl Component for AccountMenu {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
if !self.is_dirty() {
return;
}
clear_area(grid, area);
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);
self.dirty = false;
let mut y = get_y(upper_left) + 1;
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) {
match event.event_type {
UIEventType::RefreshMailbox(c) => {
self.cursor = Some(c);
self.dirty = true;
},
UIEventType::ChangeMode(UIMode::Normal) => {
self.dirty = true;
},
UIEventType::Resize => {
self.dirty = true;
},
_ => {
},
}
}
fn is_dirty(&self) -> bool {
self.dirty
}
}

View File

@ -0,0 +1,189 @@
/*! Entities that handle Mail specific functions.
*/
use super::*;
pub mod listing;
pub use listing::*;
#[derive(Debug)]
struct AccountMenuEntry {
name: 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(accounts: &Vec<Account>) -> Self {
let 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 {
accounts: accounts,
dirty: true,
cursor: None,
}
}
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);
let highlight = self.cursor.map(|(x,_)| x == a.index).unwrap_or(false);
let mut parents: Vec<Option<usize>> = vec!(None; a.entries.len());
for (idx, e) in a.entries.iter().enumerate() {
for c in e.1.get_children() {
parents[*c] = Some(idx);
}
}
let mut roots = Vec::new();
for (idx, c) in parents.iter().enumerate() {
if c.is_none() {
roots.push(idx);
}
}
let mut inc = 0;
let mut depth = String::from("");
let mut s = String::from(format!("{}\n", a.name));
fn pop(depth: &mut String) {
depth.pop();
depth.pop();
}
fn push(depth: &mut String, c: char) {
depth.push(c);
}
fn print(root: usize, parents: &Vec<Option<usize>>, depth: &mut String, entries: &Vec<(usize, Folder)>, s: &mut String, inc: &mut usize) -> () {
let len = s.len();
s.insert_str(len, &format!("{} {}\n ", *inc, &entries[root].1.get_name()));
*inc += 1;
let children_no = entries[root].1.get_children().len();
for (idx, child) in entries[root].1.get_children().iter().enumerate() {
let len = s.len();
s.insert_str(len, &format!("{}├─", depth));
push(depth, if idx == children_no - 1 {'│'} else { ' ' });
print(*child, parents, depth, entries, s, inc);
pop(depth);
}
}
for r in roots {
print(r, &parents, &mut depth, &a.entries, &mut s, &mut inc);
}
let lines: Vec<&str> = s.lines().collect();
let lines_len = lines.len();
let mut idx = 0;
for y in get_y(upper_left)..get_y(bottom_right) {
if idx == lines_len {
break;
}
let s = if idx == lines_len - 2 {
format!("{}", lines[idx].replace("", ""))
} else {
format!("{}", lines[idx])
};
let color_fg = if highlight {
if idx > 1 && self.cursor.unwrap().1 == idx - 2 {
Color::Byte(233)
} else {
Color::Byte(15)
}
} else {
Color::Default
};
let color_bg = if highlight {
if idx > 1 && self.cursor.unwrap().1 == idx - 2 {
Color::Byte(15)
} else {
Color::Byte(233)
}
} else {
Color::Default
};
let (x, _) = write_string_to_grid(&s,
grid,
color_fg,
color_bg,
(set_y(upper_left, y), bottom_right),
false);
if highlight && idx > 1 && self.cursor.unwrap().1 == idx - 2 {
change_colors(grid, ((x, y),(get_x(bottom_right)+1, y)), color_fg , color_bg);
} else {
change_colors(grid, ((x, y),set_y(bottom_right, y)), color_fg , color_bg);
}
idx += 1;
}
if idx == 0 {
0
} else {
idx - 1
}
}
}
impl Component for AccountMenu {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
if !self.is_dirty() {
return;
}
clear_area(grid, area);
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);
self.dirty = false;
let mut y = get_y(upper_left) + 1;
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) {
match event.event_type {
UIEventType::RefreshMailbox(c) => {
self.cursor = Some(c);
self.dirty = true;
},
UIEventType::ChangeMode(UIMode::Normal) => {
self.dirty = true;
},
UIEventType::Resize => {
self.dirty = true;
},
_ => {
},
}
}
fn is_dirty(&self) -> bool {
self.dirty
}
}

View File

@ -25,11 +25,11 @@
See the `Component` Trait for more details.
*/
use super::*;
pub mod utilities;
pub mod mail;
pub mod notifications;
use super::*;
pub use utilities::*;
pub use mail::*;
@ -126,34 +126,43 @@ pub fn change_colors(grid: &mut CellBuffer, area: Area, fg_color: Color, bg_colo
/// Write an `&str` to a `CellBuffer` in a specified `Area` with the passed colors.
fn write_string_to_grid(s: &str, grid: &mut CellBuffer, fg_color: Color, bg_color: Color, area: Area) -> usize {
fn write_string_to_grid(s: &str, grid: &mut CellBuffer, fg_color: Color, bg_color: Color, area: Area, line_break: bool) -> Pos {
let bounds = grid.size();
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);
let (mut x, mut y) = upper_left;
if y > (get_y(bottom_right)) || x > get_x(bottom_right) ||
y > get_y(bounds) || x > get_x(bounds) {
return 0;
eprintln!(" Invalid area with string {} and area {:?}", s, area);
return (x, y);
}
for c in s.chars() {
grid[(x,y)].set_ch(c);
grid[(x,y)].set_fg(fg_color);
grid[(x,y)].set_bg(bg_color);
x += 1;
for l in s.lines() {
'char: for c in l.chars() {
grid[(x,y)].set_ch(c);
grid[(x,y)].set_fg(fg_color);
grid[(x,y)].set_bg(bg_color);
x += 1;
if x == (get_x(bottom_right))+1 || x > get_x(bounds) {
x = get_x(upper_left);
y += 1;
if y == (get_y(bottom_right))+1 || y > get_y(bounds) {
return x;
if x == (get_x(bottom_right))+1 || x > get_x(bounds) {
x = get_x(upper_left);
y += 1;
if y >= (get_y(bottom_right)) || y > get_y(bounds) {
return (x, y - 1);
}
if !line_break {
break 'char;
}
}
}
}
x
(x, y)
}
/// Completely clear an `Area` with an empty char and the terminal's default colors.
fn clear_area(grid: &mut CellBuffer, area: Area) {
if !is_valid_area!(area) {
return;
}
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);
for y in get_y(upper_left)..=get_y(bottom_right) {

View File

@ -3,8 +3,8 @@
*/
use notify_rust::Notification as notify_Notification;
use ui::*;
use ui::components::*;
use super::*;
use super::components::*;
/// Passes notifications to the OS using the XDG specifications.
pub struct XDGNotifications {}

View File

@ -1,8 +1,6 @@
/*! Various useful components that can be used in a generic fashion.
*/
use ui::components::*;
use ui::cells::*;
use super::*;
/// A horizontally split in half container.
pub struct HSplit {
@ -24,6 +22,9 @@ impl HSplit {
impl Component for HSplit {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
if !is_valid_area!(area) {
return;
}
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);
let total_rows = get_y(bottom_right) - get_y(upper_left);
@ -71,6 +72,9 @@ impl VSplit {
impl Component for VSplit {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
if !is_valid_area!(area) {
return;
}
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);
let total_cols = get_x(bottom_right) - get_x(upper_left);
@ -186,7 +190,8 @@ impl Pager {
content,
Color::Default,
Color::Default,
((0, i), (width -1, i)));
((0, i), (width -1, i)),
true);
}
}
}
@ -194,6 +199,9 @@ impl Pager {
impl Component for Pager {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
if !is_valid_area!(area) {
return;
}
if !self.is_dirty() {
return;
}
@ -272,7 +280,7 @@ impl StatusBar {
grid,
Color::Byte(123),
Color::Byte(26),
area);
area, false);
change_colors(grid, area, Color::Byte(123), Color::Byte(26));
context.dirty_areas.push_back(area);
}
@ -282,7 +290,7 @@ impl StatusBar {
grid,
Color::Byte(219),
Color::Byte(88),
area);
area, false);
change_colors(grid, area, Color::Byte(219), Color::Byte(88));
context.dirty_areas.push_back(area);
}
@ -291,6 +299,9 @@ impl StatusBar {
impl Component for StatusBar {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
if !is_valid_area!(area) {
return;
}
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);
@ -359,3 +370,26 @@ impl Component for StatusBar {
self.dirty || self.container.component.is_dirty()
}
}
// 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) {
}
fn process_event(&mut self, event: &UIEvent, context: &mut Context) {
return;
}
}

View File

@ -30,20 +30,20 @@
#[macro_use]
mod position;
pub mod components;
mod cells;
pub mod components;
#[macro_use]
mod execute;
use self::execute::goto;
use execute::goto;
pub use self::position::*;
use self::cells::*;
pub use self::components::*;
extern crate melib;
extern crate notify_rust;
use melib::*;
use std;
use std::io::{Write, };
use std::collections::VecDeque;
use std::fmt;
@ -53,6 +53,9 @@ use termion::raw::IntoRawMode;
use termion::event::{Key as TermionKey, };
use termion::input::TermRead;
extern crate chan;
#[macro_use]
extern crate nom;
use chan::Sender;
/// `ThreadEvent` encapsulates all of the possible values we need to transfer between our threads