Run rustfmt

embed
Manos Pitsidianakis 2019-03-14 12:19:25 +02:00
parent e7c95ba229
commit bf038428c2
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
36 changed files with 1023 additions and 726 deletions

View File

@ -19,8 +19,8 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
use chrono::{DateTime, Local}; use chrono::{DateTime, Local};
use uuid::Uuid;
use fnv::FnvHashMap; use fnv::FnvHashMap;
use uuid::Uuid;
use std::ops::Deref; use std::ops::Deref;
@ -31,7 +31,7 @@ pub struct AddressBook {
display_name: String, display_name: String,
created: DateTime<Local>, created: DateTime<Local>,
last_edited: DateTime<Local>, last_edited: DateTime<Local>,
cards: FnvHashMap<CardId, Card> cards: FnvHashMap<CardId, Card>,
} }
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
@ -44,7 +44,6 @@ pub struct Card {
name_prefix: String, name_prefix: String,
name_suffix: String, name_suffix: String,
//address //address
birthday: Option<DateTime<Local>>, birthday: Option<DateTime<Local>>,
email: String, email: String,
url: String, url: String,
@ -74,7 +73,11 @@ impl AddressBook {
self.cards.contains_key(&card_id) self.cards.contains_key(&card_id)
} }
pub fn search(&self, term: &str) -> Vec<String> { pub fn search(&self, term: &str) -> Vec<String> {
self.cards.values().filter(|c| c.email.contains(term)).map(|c| c.email.clone()).collect() self.cards
.values()
.filter(|c| c.email.contains(term))
.map(|c| c.email.clone())
.collect()
} }
} }
@ -86,7 +89,6 @@ impl Deref for AddressBook {
} }
} }
impl Card { impl Card {
pub fn new() -> Card { pub fn new() -> Card {
Card { Card {
@ -98,7 +100,6 @@ impl Card {
name_prefix: String::new(), name_prefix: String::new(),
name_suffix: String::new(), name_suffix: String::new(),
//address //address
birthday: None, birthday: None,
email: String::new(), email: String::new(),
url: String::new(), url: String::new(),
@ -182,7 +183,6 @@ impl Card {
pub fn extra_property(&self, key: &str) -> Option<&str> { pub fn extra_property(&self, key: &str) -> Option<&str> {
self.extra_properties.get(key).map(|v| v.as_str()) self.extra_properties.get(key).map(|v| v.as_str())
} }
} }
impl From<FnvHashMap<String, String>> for Card { impl From<FnvHashMap<String, String>> for Card {

View File

@ -18,11 +18,11 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
pub mod addressbook;
pub mod async; pub mod async;
pub mod conf; pub mod conf;
pub mod error; pub mod error;
pub mod mailbox; pub mod mailbox;
pub mod addressbook;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
@ -38,8 +38,8 @@ extern crate chan;
#[macro_use] #[macro_use]
extern crate bitflags; extern crate bitflags;
extern crate uuid;
extern crate fnv; extern crate fnv;
extern crate uuid;
pub use conf::*; pub use conf::*;
pub use mailbox::*; pub use mailbox::*;

View File

@ -27,7 +27,7 @@ extern crate xdg;
use super::{MaildirFolder, MaildirOp}; use super::{MaildirFolder, MaildirOp};
use async::*; use async::*;
use conf::AccountSettings; use conf::AccountSettings;
use error::{Result, MeliError}; use error::{MeliError, Result};
use mailbox::backends::{ use mailbox::backends::{
BackendFolder, BackendOp, Folder, FolderHash, MailBackend, RefreshEvent, RefreshEventConsumer, BackendFolder, BackendOp, Folder, FolderHash, MailBackend, RefreshEvent, RefreshEventConsumer,
RefreshEventKind::*, RefreshEventKind::*,
@ -347,7 +347,10 @@ impl MailBackend for MaildirType {
} }
} }
Err(MeliError::new(format!("'{}' is not a valid folder.", folder))) Err(MeliError::new(format!(
"'{}' is not a valid folder.",
folder
)))
} }
} }

View File

@ -94,8 +94,8 @@ impl PartialEq for Address {
s.address_spec.display(&s.raw) == o.address_spec.display(&o.raw) s.address_spec.display(&s.raw) == o.address_spec.display(&o.raw)
} }
(Address::Group(s), Address::Group(o)) => { (Address::Group(s), Address::Group(o)) => {
s.display_name.display(&s.raw) == o.display_name.display(&o.raw) && s s.display_name.display(&s.raw) == o.display_name.display(&o.raw)
.mailbox_list && s.mailbox_list
.iter() .iter()
.zip(o.mailbox_list.iter()) .zip(o.mailbox_list.iter())
.fold(true, |b, (s, o)| b && (s == o)) .fold(true, |b, (s, o)| b && (s == o))

View File

@ -71,7 +71,8 @@ impl AttachmentBuilder {
} }
pub fn content_type(&mut self, value: &[u8]) -> &Self { pub fn content_type(&mut self, value: &[u8]) -> &Self {
match parser::content_type(value).to_full_result() { match parser::content_type(value).to_full_result() {
Ok((ct, cst, params)) => if ct.eq_ignore_ascii_case(b"multipart") { Ok((ct, cst, params)) => {
if ct.eq_ignore_ascii_case(b"multipart") {
let mut boundary = None; let mut boundary = None;
for (n, v) in params { for (n, v) in params {
if n.eq_ignore_ascii_case(b"boundary") { if n.eq_ignore_ascii_case(b"boundary") {
@ -81,7 +82,8 @@ impl AttachmentBuilder {
} }
assert!(boundary.is_some()); assert!(boundary.is_some());
let _boundary = boundary.unwrap(); let _boundary = boundary.unwrap();
let offset = (_boundary.as_ptr() as usize).wrapping_sub(value.as_ptr() as usize); let offset =
(_boundary.as_ptr() as usize).wrapping_sub(value.as_ptr() as usize);
let boundary = SliceBuild::new(offset, _boundary.len()); let boundary = SliceBuild::new(offset, _boundary.len());
let subattachments = Self::subattachments(&self.raw, boundary.get(&value)); let subattachments = Self::subattachments(&self.raw, boundary.get(&value));
assert!(!subattachments.is_empty()); assert!(!subattachments.is_empty());
@ -129,7 +131,8 @@ impl AttachmentBuilder {
*k = Text::Other { tag: cst.into() }; *k = Text::Other { tag: cst.into() };
} }
} }
} else if ct.eq_ignore_ascii_case(b"message") && cst.eq_ignore_ascii_case(b"rfc822") { } else if ct.eq_ignore_ascii_case(b"message") && cst.eq_ignore_ascii_case(b"rfc822")
{
self.content_type = ContentType::MessageRfc822; self.content_type = ContentType::MessageRfc822;
} else { } else {
let mut tag: Vec<u8> = Vec::with_capacity(ct.len() + cst.len() + 1); let mut tag: Vec<u8> = Vec::with_capacity(ct.len() + cst.len() + 1);
@ -137,7 +140,8 @@ impl AttachmentBuilder {
tag.push(b'/'); tag.push(b'/');
tag.extend(cst); tag.extend(cst);
self.content_type = ContentType::Unsupported { tag }; self.content_type = ContentType::Unsupported { tag };
}, }
}
Err(v) => { Err(v) => {
eprintln!("parsing error in content_type: {:?} {:?}", value, v); eprintln!("parsing error in content_type: {:?} {:?}", value, v);
} }
@ -405,7 +409,8 @@ fn decode_rec_helper(a: &Attachment, filter: &Option<Filter>) -> Vec<u8> {
kind: ref multipart_type, kind: ref multipart_type,
subattachments: ref sub_att_vec, subattachments: ref sub_att_vec,
.. ..
} => if *multipart_type == MultipartType::Alternative { } => {
if *multipart_type == MultipartType::Alternative {
for a in sub_att_vec { for a in sub_att_vec {
if let ContentType::Text { if let ContentType::Text {
kind: Text::Plain, .. kind: Text::Plain, ..
@ -421,7 +426,8 @@ fn decode_rec_helper(a: &Attachment, filter: &Option<Filter>) -> Vec<u8> {
vec.extend(decode_rec_helper(a, filter)); vec.extend(decode_rec_helper(a, filter));
} }
vec vec
}, }
}
}; };
if let Some(filter) = filter { if let Some(filter) = filter {
filter(a, &mut ret); filter(a, &mut ret);

View File

@ -3,8 +3,8 @@ use chrono::{DateTime, Local};
use data_encoding::BASE64_MIME; use data_encoding::BASE64_MIME;
use std::str; use std::str;
mod random;
mod mime; mod mime;
mod random;
//use self::mime::*; //use self::mime::*;
@ -70,13 +70,16 @@ impl str::FromStr for Draft {
if ignore_header(k) { if ignore_header(k) {
continue; continue;
} }
if ret.headers.insert( if ret
.headers
.insert(
String::from_utf8(k.to_vec())?, String::from_utf8(k.to_vec())?,
String::from_utf8(v.to_vec())?, String::from_utf8(v.to_vec())?,
).is_none() { )
.is_none()
{
ret.header_order.push(String::from_utf8(k.to_vec())?); ret.header_order.push(String::from_utf8(k.to_vec())?);
} }
} }
let body = Envelope::new(0).body_bytes(s.as_bytes()); let body = Envelope::new(0).body_bytes(s.as_bytes());
@ -191,7 +194,6 @@ impl Draft {
} }
Ok(ret) Ok(ret)
} }
} }

View File

@ -136,15 +136,18 @@ named!(
named!( named!(
header_no_val<(&[u8], &[u8])>, header_no_val<(&[u8], &[u8])>,
do_parse!( do_parse!(
name: complete!(name) >> name: complete!(name)
tag!(b":") >> >> tag!(b":")
opt!(is_a!(" \t")) >> >> opt!(is_a!(" \t"))
tag!(b"\n") >> >> tag!(b"\n")
( { (name, b"") } ))); >> ({ (name, b"") })
)
);
named!( named!(
header<(&[u8], &[u8])>, header<(&[u8], &[u8])>,
alt_complete!(header_no_val | header_has_val)); alt_complete!(header_no_val | header_has_val)
);
/* Parse all headers -> Vec<(&str, Vec<&str>)> */ /* Parse all headers -> Vec<(&str, Vec<&str>)> */
named!(pub headers<std::vec::Vec<(&[u8], &[u8])>>, named!(pub headers<std::vec::Vec<(&[u8], &[u8])>>,
many1!(complete!(header))); many1!(complete!(header)));

0
rustfmt.toml 100644
View File

View File

@ -66,7 +66,11 @@ fn main() {
let menu = Entity::from(Box::new(AccountMenu::new(&state.context.accounts))); let menu = Entity::from(Box::new(AccountMenu::new(&state.context.accounts)));
let listing = listing::Listing::from(IndexStyle::Compact); let listing = listing::Listing::from(IndexStyle::Compact);
let b = Entity::from(Box::new(listing)); let b = Entity::from(Box::new(listing));
let tabs = Box::new(Tabbed::new(vec![Box::new(VSplit::new(menu, b, 90, true)), Box::new(AccountsPanel::new(&state.context)), Box::new(ContactList::default())])); let tabs = Box::new(Tabbed::new(vec![
Box::new(VSplit::new(menu, b, 90, true)),
Box::new(AccountsPanel::new(&state.context)),
Box::new(ContactList::default()),
]));
let window = Entity::from(tabs); let window = Entity::from(tabs);
let status_bar = Entity::from(Box::new(StatusBar::new(window))); let status_bar = Entity::from(Box::new(StatusBar::new(window)));
@ -75,7 +79,9 @@ fn main() {
let xdg_notifications = let xdg_notifications =
Entity::from(Box::new(ui::components::notifications::XDGNotifications {})); Entity::from(Box::new(ui::components::notifications::XDGNotifications {}));
state.register_entity(xdg_notifications); state.register_entity(xdg_notifications);
state.register_entity(Entity::from(Box::new(ui::components::notifications::NotificationFilter {}))); state.register_entity(Entity::from(Box::new(
ui::components::notifications::NotificationFilter {},
)));
/* Keep track of the input mode. See ui::UIMode for details */ /* Keep track of the input mode. See ui::UIMode for details */
'main: loop { 'main: loop {

View File

@ -76,8 +76,6 @@ const _DOUBLE_DOWN_AND_LEFT: char = '╗';
const _DOUBLE_UP_AND_LEFT: char = '╝'; const _DOUBLE_UP_AND_LEFT: char = '╝';
const _DOUBLE_UP_AND_RIGHT: char = '╚'; const _DOUBLE_UP_AND_RIGHT: char = '╚';
type EntityId = Uuid; type EntityId = Uuid;
/// `Entity` is a container for Components. /// `Entity` is a container for Components.
@ -157,7 +155,9 @@ pub trait Component: Display + Debug + Send {
fn kill(&mut self, _id: EntityId) {} fn kill(&mut self, _id: EntityId) {}
fn set_id(&mut self, _id: EntityId) {} fn set_id(&mut self, _id: EntityId) {}
fn get_shortcuts(&self, context: &Context) -> ShortcutMap { Default::default() } fn get_shortcuts(&self, context: &Context) -> ShortcutMap {
Default::default()
}
} }
/* /*
@ -429,6 +429,9 @@ pub(crate) fn set_and_join_box(grid: &mut CellBuffer, idx: Pos, ch: char) {
} }
pub fn create_box(grid: &mut CellBuffer, area: Area) { pub fn create_box(grid: &mut CellBuffer, area: Area) {
if !is_valid_area!(area) {
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);

View File

@ -97,12 +97,20 @@ impl ContactManager {
); );
self.form = FormWidget::new("Save".into()); self.form = FormWidget::new("Save".into());
self.form.add_button(("Cancel".into(), false)); self.form.add_button(("Cancel".into(), false));
self.form.push(("First Name".into(), self.card.firstname().to_string())); self.form
self.form.push(("Last Name".into(), self.card.lastname().to_string())); .push(("First Name".into(), self.card.firstname().to_string()));
self.form.push(("Additional Name".into(), self.card.additionalname().to_string())); self.form
self.form.push(("Name Prefix".into(), self.card.name_prefix().to_string())); .push(("Last Name".into(), self.card.lastname().to_string()));
self.form.push(("Name Suffix".into(), self.card.name_suffix().to_string())); self.form.push((
self.form.push(("E-mail".into(), self.card.email().to_string())); "Additional Name".into(),
self.card.additionalname().to_string(),
));
self.form
.push(("Name Prefix".into(), self.card.name_prefix().to_string()));
self.form
.push(("Name Suffix".into(), self.card.name_suffix().to_string()));
self.form
.push(("E-mail".into(), self.card.email().to_string()));
self.form.push(("url".into(), self.card.url().to_string())); self.form.push(("url".into(), self.card.url().to_string()));
self.form.push(("key".into(), self.card.key().to_string())); self.form.push(("key".into(), self.card.key().to_string()));
} }
@ -120,40 +128,56 @@ impl Component for ContactManager {
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.form.draw(grid, (set_y(upper_left, get_y(upper_left) + 1), bottom_right), context); self.form.draw(
grid,
(set_y(upper_left, get_y(upper_left) + 1), bottom_right),
context,
);
context.dirty_areas.push_back(area); context.dirty_areas.push_back(area);
} }
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool { fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
if self.form.process_event(event, context) { if self.form.process_event(event, context) {
match self.form.buttons_result() { match self.form.buttons_result() {
None => {}, None => {}
Some(true) => { Some(true) => {
let mut fields = std::mem::replace(&mut self.form, FormWidget::default()).collect().unwrap(); let mut fields = std::mem::replace(&mut self.form, FormWidget::default())
let fields: FnvHashMap<String, String> = fields.into_iter().map(|(s, v)| { .collect()
(s, match v { .unwrap();
let fields: FnvHashMap<String, String> = fields
.into_iter()
.map(|(s, v)| {
(
s,
match v {
Field::Text(v, _, _) | Field::TextArea(v, _) => v, Field::Text(v, _, _) | Field::TextArea(v, _) => v,
Field::Choice(mut v, c) => v.remove(c), Field::Choice(mut v, c) => v.remove(c),
})}).collect(); },
)
})
.collect();
let mut new_card = Card::from(fields); let mut new_card = Card::from(fields);
new_card.set_id(*self.card.id()); new_card.set_id(*self.card.id());
context.accounts[self.account_pos].address_book.add_card(new_card); context.accounts[self.account_pos]
.address_book
.add_card(new_card);
context.replies.push_back(UIEvent { context.replies.push_back(UIEvent {
id: 0, id: 0,
event_type: UIEventType::StatusEvent(StatusEvent::DisplayMessage("Saved.".into())), event_type: UIEventType::StatusEvent(StatusEvent::DisplayMessage(
"Saved.".into(),
)),
}); });
context.replies.push_back(UIEvent { context.replies.push_back(UIEvent {
id: 0, id: 0,
event_type: UIEventType::EntityKill(self.id), event_type: UIEventType::EntityKill(self.id),
}); });
}, }
Some(false) => { Some(false) => {
context.replies.push_back(UIEvent { context.replies.push_back(UIEvent {
id: 0, id: 0,
event_type: UIEventType::EntityKill(self.id), event_type: UIEventType::EntityKill(self.id),
}); });
}
},
} }
return true; return true;
} }

View File

@ -65,7 +65,8 @@ impl ContactList {
let account = &mut context.accounts[self.account_pos]; let account = &mut context.accounts[self.account_pos];
let book = &mut account.address_book; let book = &mut account.address_book;
self.length = book.len(); self.length = book.len();
self.content.resize(MAX_COLS, book.len(), Cell::with_char(' ')); self.content
.resize(MAX_COLS, book.len(), Cell::with_char(' '));
self.id_positions.clear(); self.id_positions.clear();
if self.id_positions.capacity() < book.len() { if self.id_positions.capacity() < book.len() {
@ -81,7 +82,7 @@ impl ContactList {
Color::Default, Color::Default,
Color::Default, Color::Default,
((0, i), (MAX_COLS - 1, book.len() - 1)), ((0, i), (MAX_COLS - 1, book.len() - 1)),
false false,
); );
} }
} }
@ -105,7 +106,15 @@ impl Component for ContactList {
if self.dirty { if self.dirty {
self.initialize(context); self.initialize(context);
clear_area(grid, area); clear_area(grid, area);
copy_area(grid, &self.content, area, ((0, 0), (MAX_COLS - 1, self.content.size().1.saturating_sub(1)))); copy_area(
grid,
&self.content,
area,
(
(0, 0),
(MAX_COLS - 1, self.content.size().1.saturating_sub(1)),
),
);
context.dirty_areas.push_back(area); context.dirty_areas.push_back(area);
self.dirty = false; self.dirty = false;
} }
@ -116,11 +125,27 @@ impl Component for ContactList {
/* Reset previously highlighted line */ /* Reset previously highlighted line */
let fg_color = Color::Default; let fg_color = Color::Default;
let bg_color = Color::Default; let bg_color = Color::Default;
change_colors(grid, (pos_inc(upper_left, (0, self.cursor_pos)), set_y(bottom_right, get_y(upper_left) + self.cursor_pos)), fg_color, bg_color); change_colors(
grid,
(
pos_inc(upper_left, (0, self.cursor_pos)),
set_y(bottom_right, get_y(upper_left) + self.cursor_pos),
),
fg_color,
bg_color,
);
/* Highlight current line */ /* Highlight current line */
let bg_color = Color::Byte(246); let bg_color = Color::Byte(246);
change_colors(grid, (pos_inc(upper_left, (0, self.new_cursor_pos)), set_y(bottom_right, get_y(upper_left) + self.new_cursor_pos)), fg_color, bg_color); change_colors(
grid,
(
pos_inc(upper_left, (0, self.new_cursor_pos)),
set_y(bottom_right, get_y(upper_left) + self.new_cursor_pos),
),
fg_color,
bg_color,
);
self.cursor_pos = self.new_cursor_pos; self.cursor_pos = self.new_cursor_pos;
} }
@ -141,7 +166,7 @@ impl Component for ContactList {
self.view = Some(entity); self.view = Some(entity);
return true; return true;
}, }
UIEventType::Input(ref key) if *key == shortcuts["edit_contact"] && self.length > 0 => { UIEventType::Input(ref key) if *key == shortcuts["edit_contact"] && self.length > 0 => {
let account = &mut context.accounts[self.account_pos]; let account = &mut context.accounts[self.account_pos];
@ -156,7 +181,7 @@ impl Component for ContactList {
self.view = Some(entity); self.view = Some(entity);
return true; return true;
}, }
UIEventType::Input(Key::Char('n')) => { UIEventType::Input(Key::Char('n')) => {
let card = Card::new(); let card = Card::new();
let mut manager = ContactManager::default(); let mut manager = ContactManager::default();
@ -167,25 +192,24 @@ impl Component for ContactList {
self.view = Some(entity); self.view = Some(entity);
return true; return true;
}, }
UIEventType::Input(Key::Up) => { UIEventType::Input(Key::Up) => {
self.set_dirty(); self.set_dirty();
self.new_cursor_pos = self.cursor_pos.saturating_sub(1); self.new_cursor_pos = self.cursor_pos.saturating_sub(1);
return true; return true;
}, }
UIEventType::Input(Key::Down) if self.cursor_pos < self.length.saturating_sub(1) => { UIEventType::Input(Key::Down) if self.cursor_pos < self.length.saturating_sub(1) => {
self.set_dirty(); self.set_dirty();
self.new_cursor_pos += 1; self.new_cursor_pos += 1;
return true; return true;
}, }
UIEventType::EntityKill(ref kill_id) if self.mode == ViewMode::View(*kill_id) => { UIEventType::EntityKill(ref kill_id) if self.mode == ViewMode::View(*kill_id) => {
self.mode = ViewMode::List; self.mode = ViewMode::List;
self.view.take(); self.view.take();
self.set_dirty(); self.set_dirty();
return true; return true;
}
}, _ => {}
_ => {},
} }
false false
} }
@ -205,7 +229,11 @@ impl Component for ContactList {
self.mode = ViewMode::Close(uuid); self.mode = ViewMode::Close(uuid);
} }
fn get_shortcuts(&self, context: &Context) -> ShortcutMap { fn get_shortcuts(&self, context: &Context) -> ShortcutMap {
let mut map = self.view.as_ref().map(|p| p.get_shortcuts(context)).unwrap_or_default(); let mut map = self
.view
.as_ref()
.map(|p| p.get_shortcuts(context))
.unwrap_or_default();
let config_map = context.settings.shortcuts.contact_list.key_values(); let config_map = context.settings.shortcuts.contact_list.key_values();
map.insert("create_contact", (*config_map["create_contact"]).clone()); map.insert("create_contact", (*config_map["create_contact"]).clone());

View File

@ -58,8 +58,7 @@ impl Default for Indexer {
} }
impl Indexer { impl Indexer {
fn draw_menu(&mut self, _grid: &mut CellBuffer, _area: Area, _context: &mut Context) { fn draw_menu(&mut self, _grid: &mut CellBuffer, _area: Area, _context: &mut Context) {}
}
} }
impl Component for Indexer { impl Component for Indexer {

View File

@ -73,7 +73,8 @@ impl AccountMenu {
} }
entries entries
}, },
}).collect(); })
.collect();
AccountMenu { AccountMenu {
accounts, accounts,
dirty: true, dirty: true,

View File

@ -35,7 +35,6 @@ impl fmt::Display for AccountsPanel {
} }
} }
impl Component for AccountsPanel { impl Component for AccountsPanel {
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.dirty {
@ -103,9 +102,6 @@ impl Component for AccountsPanel {
((5, y + 5), (120 - 2, y + 5)), ((5, y + 5), (120 - 2, y + 5)),
true, true,
); );
} }
self.dirty = false; self.dirty = false;
} }
@ -121,22 +117,24 @@ impl Component for AccountsPanel {
self.cursor = self.cursor.saturating_sub(1); self.cursor = self.cursor.saturating_sub(1);
self.dirty = true; self.dirty = true;
return true; return true;
}, }
UIEventType::Input(Key::Down) => { UIEventType::Input(Key::Down) => {
if self.cursor + 1 < context.accounts.len() { if self.cursor + 1 < context.accounts.len() {
self.cursor += 1; self.cursor += 1;
self.dirty = true; self.dirty = true;
} }
return true; return true;
}, }
UIEventType::Input(Key::Char('\n')) => { UIEventType::Input(Key::Char('\n')) => {
context.replies.push_back(UIEvent { context.replies.push_back(UIEvent {
id: 0, id: 0,
event_type: UIEventType::Action(Tab(TabOpen(Some(Box::new(ContactList::for_account(self.cursor))) event_type: UIEventType::Action(Tab(TabOpen(Some(Box::new(
)))}); ContactList::for_account(self.cursor),
))))),
});
return true; return true;
}, }
_ => {}, _ => {}
} }
false false

View File

@ -24,7 +24,6 @@ use super::*;
use melib::Draft; use melib::Draft;
use std::str::FromStr; use std::str::FromStr;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
enum Cursor { enum Cursor {
Headers, Headers,
@ -173,10 +172,15 @@ impl Composer {
let account_cursor = self.account_cursor; let account_cursor = self.account_cursor;
for &k in &["Date", "From", "To", "Cc", "Bcc", "Subject"] { for &k in &["Date", "From", "To", "Cc", "Bcc", "Subject"] {
if k == "To" { if k == "To" {
self.form.push_cl((k.into(), headers[k].to_string(), Box::new(move |c, term| { self.form.push_cl((
k.into(),
headers[k].to_string(),
Box::new(move |c, term| {
let book: &AddressBook = &c.accounts[account_cursor].address_book; let book: &AddressBook = &c.accounts[account_cursor].address_book;
let results: Vec<String> = book.search(term); let results: Vec<String> = book.search(term);
results}))); results
}),
));
} else { } else {
self.form.push((k.into(), headers[k].to_string())); self.form.push((k.into(), headers[k].to_string()));
} }
@ -283,14 +287,21 @@ impl Component for Composer {
} }
if self.dirty { if self.dirty {
for i in get_x(upper_left) + mid + 1..=get_x(upper_left) + mid + width.saturating_sub(0) { for i in get_x(upper_left) + mid + 1..=get_x(upper_left) + mid + width.saturating_sub(0)
{
//set_and_join_box(grid, (i, header_height), HORZ_BOUNDARY); //set_and_join_box(grid, (i, header_height), HORZ_BOUNDARY);
//grid[(i, header_height)].set_fg(Color::Default); //grid[(i, header_height)].set_fg(Color::Default);
//grid[(i, header_height)].set_bg(Color::Default); //grid[(i, header_height)].set_bg(Color::Default);
} }
} }
let header_area = (pos_inc(upper_left, (mid + 1, 0)), (get_x(bottom_right).saturating_sub(mid), get_y(upper_left) + header_height + 1)); let header_area = (
pos_inc(upper_left, (mid + 1, 0)),
(
get_x(bottom_right).saturating_sub(mid),
get_y(upper_left) + header_height + 1,
),
);
let body_area = ( let body_area = (
pos_inc(upper_left, (mid + 1, header_height + 2)), pos_inc(upper_left, (mid + 1, header_height + 2)),
pos_dec(bottom_right, ((mid, 0))), pos_dec(bottom_right, ((mid, 0))),
@ -303,21 +314,26 @@ impl Component for Composer {
ViewMode::Overview | ViewMode::Pager => { ViewMode::Overview | ViewMode::Pager => {
self.pager.set_dirty(); self.pager.set_dirty();
self.pager.draw(grid, body_area, context); self.pager.draw(grid, body_area, context);
}, }
ViewMode::Discard(_) => { ViewMode::Discard(_) => {
/* Let user choose whether to quit with/without saving or cancel */ /* Let user choose whether to quit with/without saving or cancel */
let mid_x = { let mid_x = { std::cmp::max(width!(area) / 2, width / 2) - width / 2 };
std::cmp::max(width!(area) / 2, width / 2) - width / 2 let mid_y = { std::cmp::max(height!(area) / 2, 11) - 11 };
};
let mid_y = {
std::cmp::max(height!(area) / 2, 11) - 11
};
let upper_left = upper_left!(body_area); let upper_left = upper_left!(body_area);
let bottom_right = bottom_right!(body_area); let bottom_right = bottom_right!(body_area);
let area = (pos_inc(upper_left, (mid_x, mid_y)), pos_dec(bottom_right, (mid_x, mid_y))); let area = (
pos_inc(upper_left, (mid_x, mid_y)),
pos_dec(bottom_right, (mid_x, mid_y)),
);
create_box(grid, area); create_box(grid, area);
let area = (pos_inc(upper_left, (mid_x + 2, mid_y + 2)), pos_dec(bottom_right, (mid_x.saturating_sub(2), mid_y.saturating_sub(2)))); let area = (
pos_inc(upper_left, (mid_x + 2, mid_y + 2)),
pos_dec(
bottom_right,
(mid_x.saturating_sub(2), mid_y.saturating_sub(2)),
),
);
let (_, y) = write_string_to_grid( let (_, y) = write_string_to_grid(
&format!("Draft \"{:10}\"", self.draft.headers()["Subject"]), &format!("Draft \"{:10}\"", self.draft.headers()["Subject"]),
@ -351,8 +367,7 @@ impl Component for Composer {
(set_y(upper_left!(area), y + 1), bottom_right!(area)), (set_y(upper_left!(area), y + 1), bottom_right!(area)),
true, true,
); );
}
},
} }
context.dirty_areas.push_back(area); context.dirty_areas.push_back(area);
@ -371,7 +386,7 @@ impl Component for Composer {
self.dirty = true; self.dirty = true;
return true; return true;
} }
}, }
_ => {} _ => {}
} }
if self.form.process_event(event, context) { if self.form.process_event(event, context) {
@ -381,7 +396,7 @@ impl Component for Composer {
match event.event_type { match event.event_type {
UIEventType::Resize => { UIEventType::Resize => {
self.set_dirty(); self.set_dirty();
}, }
/* /*
/* Switch e-mail From: field to the `left` configured account. */ /* Switch e-mail From: field to the `left` configured account. */
UIEventType::Input(Key::Left) if self.cursor == Cursor::From => { UIEventType::Input(Key::Left) if self.cursor == Cursor::From => {
@ -407,10 +422,10 @@ impl Component for Composer {
}*/ }*/
UIEventType::Input(Key::Up) => { UIEventType::Input(Key::Up) => {
self.cursor = Cursor::Headers; self.cursor = Cursor::Headers;
}, }
UIEventType::Input(Key::Down) => { UIEventType::Input(Key::Down) => {
self.cursor = Cursor::Body; self.cursor = Cursor::Body;
}, }
UIEventType::Input(Key::Char(key)) if self.mode.is_discard() => { UIEventType::Input(Key::Char(key)) if self.mode.is_discard() => {
match (key, &self.mode) { match (key, &self.mode) {
('x', ViewMode::Discard(u)) => { ('x', ViewMode::Discard(u)) => {
@ -420,7 +435,7 @@ impl Component for Composer {
}); });
return true; return true;
} }
('n', _) => {}, ('n', _) => {}
('y', ViewMode::Discard(u)) => { ('y', ViewMode::Discard(u)) => {
let account = &context.accounts[self.account_cursor]; let account = &context.accounts[self.account_cursor];
let draft = std::mem::replace(&mut self.draft, Draft::default()); let draft = std::mem::replace(&mut self.draft, Draft::default());
@ -432,7 +447,7 @@ impl Component for Composer {
event_type: UIEventType::Action(Tab(Kill(*u))), event_type: UIEventType::Action(Tab(Kill(*u))),
}); });
return true; return true;
}, }
_ => { _ => {
return false; return false;
} }
@ -505,11 +520,14 @@ impl Component for Composer {
} }
fn is_dirty(&self) -> bool { fn is_dirty(&self) -> bool {
self.dirty || self.pager.is_dirty() || self self.dirty
|| self.pager.is_dirty()
|| self
.reply_context .reply_context
.as_ref() .as_ref()
.map(|(_, p)| p.is_dirty()) .map(|(_, p)| p.is_dirty())
.unwrap_or(false) || self.form.is_dirty() .unwrap_or(false)
|| self.form.is_dirty()
} }
fn set_dirty(&mut self) { fn set_dirty(&mut self) {

View File

@ -131,7 +131,6 @@ impl From<IndexStyle> for Listing {
IndexStyle::Plain => Listing::Plain(Default::default()), IndexStyle::Plain => Listing::Plain(Default::default()),
IndexStyle::Threaded => Listing::Threaded(Default::default()), IndexStyle::Threaded => Listing::Threaded(Default::default()),
IndexStyle::Compact => Listing::Compact(Default::default()), IndexStyle::Compact => Listing::Compact(Default::default()),
} }
} }
} }

View File

@ -342,7 +342,9 @@ impl CompactListing {
fn format_date(envelope: &Envelope) -> String { fn format_date(envelope: &Envelope) -> String {
let d = std::time::UNIX_EPOCH + std::time::Duration::from_secs(envelope.date()); let d = std::time::UNIX_EPOCH + std::time::Duration::from_secs(envelope.date());
let now: std::time::Duration = std::time::SystemTime::now().duration_since(d).unwrap_or_else(|_| std::time::Duration::new(std::u64::MAX, 0)); let now: std::time::Duration = std::time::SystemTime::now()
.duration_since(d)
.unwrap_or_else(|_| std::time::Duration::new(std::u64::MAX, 0));
match now.as_secs() { match now.as_secs() {
n if n < 10 * 60 * 60 => format!("{} hours ago{}", n / (60 * 60), " ".repeat(8)), n if n < 10 * 60 * 60 => format!("{} hours ago{}", n / (60 * 60), " ".repeat(8)),
n if n < 24 * 60 * 60 => format!("{} hours ago{}", n / (60 * 60), " ".repeat(7)), n if n < 24 * 60 * 60 => format!("{} hours ago{}", n / (60 * 60), " ".repeat(7)),
@ -527,20 +529,86 @@ impl Component for CompactListing {
} }
fn get_shortcuts(&self, context: &Context) -> ShortcutMap { fn get_shortcuts(&self, context: &Context) -> ShortcutMap {
let mut map = self.view.as_ref().map(|p| p.get_shortcuts(context)).unwrap_or_default(); let mut map = self
.view
.as_ref()
.map(|p| p.get_shortcuts(context))
.unwrap_or_default();
let config_map = context.settings.shortcuts.compact_listing.key_values(); let config_map = context.settings.shortcuts.compact_listing.key_values();
map.insert("open_thread", if let Some(key) = config_map.get("open_thread") { (*key).clone() } else { Key::Char('\n') }); map.insert(
map.insert("prev_page", if let Some(key) = config_map.get("prev_page") { (*key).clone() } else { Key::PageUp }); "open_thread",
map.insert("next_page", if let Some(key) = config_map.get("next_page") { (*key).clone() } else { Key::PageDown }); if let Some(key) = config_map.get("open_thread") {
map.insert("exit_thread", if let Some(key) = config_map.get("exit_thread") { (*key).clone() } else { Key::Char('i') }); (*key).clone()
map.insert("prev_folder", if let Some(key) = config_map.get("prev_folder") { (*key).clone() } else { Key::Char('J') }); } else {
map.insert("next_folder", if let Some(key) = config_map.get("next_folder") { (*key).clone() } else { Key::Char('K') }); Key::Char('\n')
map.insert("prev_account", if let Some(key) = config_map.get("prev_account") { (*key).clone() } else { Key::Char('h') }); },
map.insert("next_account", if let Some(key) = config_map.get("next_account") { (*key).clone() } else { Key::Char('l') }); );
map.insert("new_mail", if let Some(key) = config_map.get("new_mail") { (*key).clone() } else { Key::Char('m') }); map.insert(
"prev_page",
if let Some(key) = config_map.get("prev_page") {
(*key).clone()
} else {
Key::PageUp
},
);
map.insert(
"next_page",
if let Some(key) = config_map.get("next_page") {
(*key).clone()
} else {
Key::PageDown
},
);
map.insert(
"exit_thread",
if let Some(key) = config_map.get("exit_thread") {
(*key).clone()
} else {
Key::Char('i')
},
);
map.insert(
"prev_folder",
if let Some(key) = config_map.get("prev_folder") {
(*key).clone()
} else {
Key::Char('J')
},
);
map.insert(
"next_folder",
if let Some(key) = config_map.get("next_folder") {
(*key).clone()
} else {
Key::Char('K')
},
);
map.insert(
"prev_account",
if let Some(key) = config_map.get("prev_account") {
(*key).clone()
} else {
Key::Char('h')
},
);
map.insert(
"next_account",
if let Some(key) = config_map.get("next_account") {
(*key).clone()
} else {
Key::Char('l')
},
);
map.insert(
"new_mail",
if let Some(key) = config_map.get("new_mail") {
(*key).clone()
} else {
Key::Char('m')
},
);
map map
} }
} }

View File

@ -413,7 +413,9 @@ impl ThreadListing {
} }
fn format_date(envelope: &Envelope) -> String { fn format_date(envelope: &Envelope) -> String {
let d = std::time::UNIX_EPOCH + std::time::Duration::from_secs(envelope.date()); let d = std::time::UNIX_EPOCH + std::time::Duration::from_secs(envelope.date());
let now: std::time::Duration = std::time::SystemTime::now().duration_since(d).unwrap_or_else(|_| std::time::Duration::new(std::u64::MAX, 0)); let now: std::time::Duration = std::time::SystemTime::now()
.duration_since(d)
.unwrap_or_else(|_| std::time::Duration::new(std::u64::MAX, 0));
match now.as_secs() { match now.as_secs() {
n if n < 10 * 60 * 60 => format!("{} hours ago{}", n / (60 * 60), " ".repeat(8)), n if n < 10 * 60 * 60 => format!("{} hours ago{}", n / (60 * 60), " ".repeat(8)),
n if n < 24 * 60 * 60 => format!("{} hours ago{}", n / (60 * 60), " ".repeat(7)), n if n < 24 * 60 * 60 => format!("{} hours ago{}", n / (60 * 60), " ".repeat(7)),
@ -486,8 +488,10 @@ impl Component for ThreadListing {
backend.operation(hash, folder_hash) backend.operation(hash, folder_hash)
}; };
let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap(); let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap();
let envelope: &mut Envelope = let envelope: &mut Envelope = mailbox
mailbox.collection.get_mut(&self.locations[self.cursor_pos.2]).unwrap(); .collection
.get_mut(&self.locations[self.cursor_pos.2])
.unwrap();
envelope.set_seen(op).unwrap(); envelope.set_seen(op).unwrap();
true true
} else { } else {

View File

@ -123,7 +123,8 @@ impl MailView {
v.extend(html_filter.wait_with_output().unwrap().stdout); v.extend(html_filter.wait_with_output().unwrap().stdout);
} }
})), })),
)).into_owned(); ))
.into_owned();
match self.mode { match self.mode {
ViewMode::Normal | ViewMode::Subview => { ViewMode::Normal | ViewMode::Subview => {
let mut t = body_text.to_string(); let mut t = body_text.to_string();
@ -171,8 +172,8 @@ impl MailView {
let mut ret = "Viewing attachment. Press `r` to return \n".to_string(); let mut ret = "Viewing attachment. Press `r` to return \n".to_string();
ret.push_str(&attachments[aidx].text()); ret.push_str(&attachments[aidx].text());
ret ret
}, }
ViewMode::ContactSelector(_) => { unimplemented!()}, ViewMode::ContactSelector(_) => unimplemented!(),
} }
} }
pub fn plain_text_to_buf(s: &str, highlight_urls: bool) -> CellBuffer { pub fn plain_text_to_buf(s: &str, highlight_urls: bool) -> CellBuffer {
@ -347,7 +348,7 @@ impl Component for MailView {
if let Some(s) = self.subview.as_mut() { if let Some(s) = self.subview.as_mut() {
s.draw(grid, (set_y(upper_left, y + 1), bottom_right), context); s.draw(grid, (set_y(upper_left, y + 1), bottom_right), context);
} }
}, }
ViewMode::ContactSelector(ref mut s) => { ViewMode::ContactSelector(ref mut s) => {
clear_area(grid, (set_y(upper_left, y + 1), bottom_right)); clear_area(grid, (set_y(upper_left, y + 1), bottom_right));
s.draw(grid, (set_y(upper_left, y + 1), bottom_right), context); s.draw(grid, (set_y(upper_left, y + 1), bottom_right), context);
@ -368,12 +369,12 @@ impl Component for MailView {
return true; return true;
} }
} }
}, }
ViewMode::ContactSelector(ref mut s) => { ViewMode::ContactSelector(ref mut s) => {
if s.process_event(event, context) { if s.process_event(event, context) {
return true; return true;
} }
}, }
_ => { _ => {
if let Some(p) = self.pager.as_mut() { if let Some(p) = self.pager.as_mut() {
if p.process_event(event, context) { if p.process_event(event, context) {
@ -394,13 +395,17 @@ impl Component for MailView {
*/ */
if let ViewMode::ContactSelector(_) = self.mode { if let ViewMode::ContactSelector(_) = self.mode {
if let ViewMode::ContactSelector(s) = std::mem::replace(&mut self.mode, ViewMode::Normal) { if let ViewMode::ContactSelector(s) =
std::mem::replace(&mut self.mode, ViewMode::Normal)
{
for c in s.collect() { for c in s.collect() {
let mut new_card: Card = Card::new(); let mut new_card: Card = Card::new();
let email = String::from_utf8(c).unwrap(); let email = String::from_utf8(c).unwrap();
new_card.set_email(&email); new_card.set_email(&email);
new_card.set_firstname(""); new_card.set_firstname("");
context.accounts[self.coordinates.0].address_book.add_card(new_card); context.accounts[self.coordinates.0]
.address_book
.add_card(new_card);
} }
//eprintln!("{:?}", s.collect()); //eprintln!("{:?}", s.collect());
} }
@ -413,12 +418,18 @@ impl Component for MailView {
.unwrap(); .unwrap();
let envelope: &Envelope = &mailbox.collection[&self.coordinates.2]; let envelope: &Envelope = &mailbox.collection[&self.coordinates.2];
let mut entries = Vec::new(); let mut entries = Vec::new();
entries.push((envelope.from()[0].get_email().into_bytes(), format!("{}", envelope.from()[0]))); entries.push((
entries.push((envelope.to()[0].get_email().into_bytes(), format!("{}", envelope.to()[0]))); envelope.from()[0].get_email().into_bytes(),
format!("{}", envelope.from()[0]),
));
entries.push((
envelope.to()[0].get_email().into_bytes(),
format!("{}", envelope.to()[0]),
));
self.mode = ViewMode::ContactSelector(Selector::new(entries, true)); self.mode = ViewMode::ContactSelector(Selector::new(entries, true));
self.dirty = true; self.dirty = true;
//context.accounts.context(self.coordinates.0).address_book.add_card(new_card); //context.accounts.context(self.coordinates.0).address_book.add_card(new_card);
}, }
UIEventType::Input(Key::Esc) | UIEventType::Input(Key::Alt('')) => { UIEventType::Input(Key::Esc) | UIEventType::Input(Key::Alt('')) => {
self.cmd_buf.clear(); self.cmd_buf.clear();
context.replies.push_back(UIEvent { context.replies.push_back(UIEvent {

View File

@ -108,7 +108,8 @@ impl EnvelopeView {
v.extend(html_filter.wait_with_output().unwrap().stdout); v.extend(html_filter.wait_with_output().unwrap().stdout);
} }
})), })),
)).into_owned(); ))
.into_owned();
match self.mode { match self.mode {
ViewMode::Normal | ViewMode::Subview => { ViewMode::Normal | ViewMode::Subview => {
let mut t = body_text.to_string(); let mut t = body_text.to_string();
@ -378,8 +379,10 @@ impl Component for EnvelopeView {
ContentType::MessageRfc822 => { ContentType::MessageRfc822 => {
self.mode = ViewMode::Subview; self.mode = ViewMode::Subview;
self.subview = Some(Box::new(Pager::from_string( self.subview = Some(Box::new(Pager::from_string(
String::from_utf8_lossy(&decode_rec(u, None)).to_string(), context, String::from_utf8_lossy(&decode_rec(u, None)).to_string(),
None, None context,
None,
None,
))); )));
} }

View File

@ -45,12 +45,8 @@ impl Component for XDGNotifications {
notify_Notification::new() notify_Notification::new()
.appname("meli") .appname("meli")
.icon("mail-message-new") .icon("mail-message-new")
.summary( .summary(title.as_ref().map(|v| v.as_str()).unwrap_or("Event"))
title .body(&escape_str(body))
.as_ref()
.map(|v| v.as_str())
.unwrap_or("Event"),
).body(&escape_str(body))
.icon("dialog-information") .icon("dialog-information")
.show() .show()
.unwrap(); .unwrap();
@ -60,7 +56,6 @@ impl Component for XDGNotifications {
fn set_dirty(&mut self) {} fn set_dirty(&mut self) {}
} }
fn escape_str(s: &str) -> String { fn escape_str(s: &str) -> String {
let mut ret: String = String::with_capacity(s.len()); let mut ret: String = String::with_capacity(s.len());
for c in s.chars() { for c in s.chars() {
@ -72,16 +67,17 @@ fn escape_str(s: &str) -> String {
'"' => ret.push_str("&quot;"), '"' => ret.push_str("&quot;"),
_ => { _ => {
let i = c as u32; let i = c as u32;
if (0x1 <= i && i <= 0x8) || if (0x1 <= i && i <= 0x8)
(0xb <= i && i <= 0xc) || || (0xb <= i && i <= 0xc)
(0xe <= i && i <= 0x1f) || || (0xe <= i && i <= 0x1f)
(0x7f <= i && i <= 0x84) || || (0x7f <= i && i <= 0x84)
(0x86 <= i && i <= 0x9f) { || (0x86 <= i && i <= 0x9f)
{
ret.push_str(&format!("&#{:x}%{:x};", i, i)); ret.push_str(&format!("&#{:x}%{:x};", i, i));
} else { } else {
ret.push(c); ret.push(c);
} }
}, }
} }
} }
ret ret
@ -123,14 +119,12 @@ impl Component for NotificationFilter {
if let UIEventType::Notification(ref title, ref body) = event.event_type { if let UIEventType::Notification(ref title, ref body) = event.event_type {
if let Some(ref bin) = context.runtime_settings.notifications.script { if let Some(ref bin) = context.runtime_settings.notifications.script {
if let Err(v) = Command::new(bin) if let Err(v) = Command::new(bin)
.arg(title .arg(title.as_ref().map(|v| v.as_str()).unwrap_or("Event"))
.as_ref()
.map(|v| v.as_str())
.unwrap_or("Event"))
.arg(body) .arg(body)
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn() { .spawn()
{
eprintln!("{:?}", v); eprintln!("{:?}", v);
} }
} }

View File

@ -252,7 +252,12 @@ impl Pager {
self.cursor_pos = 0; self.cursor_pos = 0;
self.max_cursor_pos = None; self.max_cursor_pos = None;
} }
pub fn from_string(mut text: String, context: &mut Context, cursor_pos: Option<usize>, width: Option<usize>) -> Self { pub fn from_string(
mut text: String,
context: &mut Context,
cursor_pos: Option<usize>,
width: Option<usize>,
) -> Self {
let pager_filter: Option<&String> = context.settings.pager.filter.as_ref(); let pager_filter: Option<&String> = context.settings.pager.filter.as_ref();
//let format_flowed: bool = context.settings.pager.format_flowed; //let format_flowed: bool = context.settings.pager.format_flowed;
if let Some(bin) = pager_filter { if let Some(bin) = pager_filter {
@ -275,10 +280,10 @@ impl Pager {
.wait_with_output() .wait_with_output()
.expect("Failed to wait on filter") .expect("Failed to wait on filter")
.stdout, .stdout,
).to_string(); )
.to_string();
} }
let content = { let content = {
let lines: Vec<&str> = if let Some(width) = width { let lines: Vec<&str> = if let Some(width) = width {
word_break_string(text.as_str(), width) word_break_string(text.as_str(), width)
@ -824,7 +829,10 @@ impl Tabbed {
let cslice: &mut [Cell] = grid; let cslice: &mut [Cell] = grid;
//TODO: bounds check //TODO: bounds check
let cslice_len = cslice.len(); let cslice_len = cslice.len();
for c in cslice[(y * cols) + x.saturating_sub(1)..std::cmp::min((y * cols) + x.saturating_sub(1), cslice_len)].iter_mut() { for c in cslice[(y * cols) + x.saturating_sub(1)
..std::cmp::min((y * cols) + x.saturating_sub(1), cslice_len)]
.iter_mut()
{
c.set_bg(Color::Byte(7)); c.set_bg(Color::Byte(7));
c.set_ch(' '); c.set_ch(' ');
} }
@ -846,7 +854,13 @@ impl fmt::Display for Tabbed {
impl Component for Tabbed { impl Component for Tabbed {
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.dirty {
clear_area(grid, (upper_left!(area), set_x(upper_left!(area), get_x(bottom_right!(area))))); clear_area(
grid,
(
upper_left!(area),
set_x(upper_left!(area), get_x(bottom_right!(area))),
),
);
self.dirty = false; self.dirty = false;
} }
@ -871,18 +885,33 @@ impl Component for Tabbed {
if self.show_shortcuts { if self.show_shortcuts {
let area = ( let area = (
pos_inc(upper_left!(area), (2, 1)), set_x(bottom_right!(area), get_x(bottom_right!(area)).saturating_sub(2))); pos_inc(upper_left!(area), (2, 1)),
set_x(
bottom_right!(area),
get_x(bottom_right!(area)).saturating_sub(2),
),
);
clear_area(grid, area); clear_area(grid, area);
create_box(grid, area); create_box(grid, area);
// TODO: print into a pager // TODO: print into a pager
for (idx, (k, v)) in self.children[self.cursor_pos].get_shortcuts(context).into_iter().enumerate() { for (idx, (k, v)) in self.children[self.cursor_pos]
.get_shortcuts(context)
.into_iter()
.enumerate()
{
let (x, y) = write_string_to_grid( let (x, y) = write_string_to_grid(
&k, &k,
grid, grid,
Color::Byte(29), Color::Byte(29),
Color::Default, Color::Default,
(pos_inc(upper_left!(area), (2, 1 + idx)), set_x(bottom_right!(area), get_x(bottom_right!(area)).saturating_sub(2))), (
pos_inc(upper_left!(area), (2, 1 + idx)),
set_x(
bottom_right!(area),
get_x(bottom_right!(area)).saturating_sub(2),
),
),
false, false,
); );
write_string_to_grid( write_string_to_grid(
@ -890,10 +919,16 @@ impl Component for Tabbed {
grid, grid,
Color::Default, Color::Default,
Color::Default, Color::Default,
((x + 2, y), set_x(bottom_right!(area), get_x(bottom_right!(area)).saturating_sub(2))), (
(x + 2, y),
set_x(
bottom_right!(area),
get_x(bottom_right!(area)).saturating_sub(2),
),
),
false, false,
); );
}; }
context.dirty_areas.push_back(area); context.dirty_areas.push_back(area);
} }
} }
@ -959,12 +994,12 @@ impl Component for Tabbed {
} }
} }
type EntryIdentifier = Vec<u8>; type EntryIdentifier = Vec<u8>;
/// Shows selection to user /// Shows selection to user
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct Selector { pub struct Selector {
single_only: bool, /// allow only one selection single_only: bool,
/// allow only one selection
entries: Vec<(EntryIdentifier, bool)>, entries: Vec<(EntryIdentifier, bool)>,
selected_entry_count: u32, selected_entry_count: u32,
content: CellBuffer, content: CellBuffer,
@ -982,14 +1017,8 @@ impl fmt::Display for Selector {
impl Component for Selector { impl Component for Selector {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
eprintln!("drawing");
let (width, height) = self.content.size(); let (width, height) = self.content.size();
copy_area_with_break( copy_area_with_break(grid, &self.content, area, ((0, 0), (width, height)));
grid,
&self.content,
area,
((0, 0), (width, height)),
);
context.dirty_areas.push_back(area); context.dirty_areas.push_back(area);
} }
fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool { fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool {
@ -1018,17 +1047,17 @@ impl Component for Selector {
} }
self.dirty = true; self.dirty = true;
return true; return true;
}, }
UIEventType::Input(Key::Up) if self.cursor > 0 => { UIEventType::Input(Key::Up) if self.cursor > 0 => {
self.cursor -= 1; self.cursor -= 1;
self.dirty = true; self.dirty = true;
return true; return true;
}, }
UIEventType::Input(Key::Down) if self.cursor < height.saturating_sub(1) => { UIEventType::Input(Key::Down) if self.cursor < height.saturating_sub(1) => {
self.cursor += 1; self.cursor += 1;
self.dirty = true; self.dirty = true;
return true; return true;
}, }
_ => {} _ => {}
} }
@ -1044,10 +1073,18 @@ impl Component for Selector {
impl Selector { impl Selector {
pub fn new(mut entries: Vec<(EntryIdentifier, String)>, single_only: bool) -> Selector { pub fn new(mut entries: Vec<(EntryIdentifier, String)>, single_only: bool) -> Selector {
let width = entries.iter().max_by_key(|e| e.1.len()).map(|v| v.1.len()).unwrap_or(0) + 4; let width = entries
.iter()
.max_by_key(|e| e.1.len())
.map(|v| v.1.len())
.unwrap_or(0)
+ 4;
let height = entries.len(); let height = entries.len();
let mut content = CellBuffer::new(width, height, Cell::with_char(' ')); let mut content = CellBuffer::new(width, height, Cell::with_char(' '));
let identifiers = entries.iter_mut().map(|(id, _)| (std::mem::replace(&mut *id, Vec::new()), false)).collect(); let identifiers = entries
.iter_mut()
.map(|(id, _)| (std::mem::replace(&mut *id, Vec::new()), false))
.collect();
for (i, e) in entries.into_iter().enumerate() { for (i, e) in entries.into_iter().enumerate() {
write_string_to_grid( write_string_to_grid(
&format!("[ ] {}", e.1), &format!("[ ] {}", e.1),
@ -1070,6 +1107,10 @@ impl Selector {
} }
pub fn collect(self) -> Vec<EntryIdentifier> { pub fn collect(self) -> Vec<EntryIdentifier> {
self.entries.into_iter().filter(|v| v.1).map(|(id, _)| id).collect() self.entries
.into_iter()
.filter(|v| v.1)
.map(|(id, _)| id)
.collect()
} }
} }

View File

@ -17,7 +17,11 @@ impl Default for FormFocus {
} }
pub enum Field { pub enum Field {
Text(String, Cursor, Option<(Box<Fn(&Context, &str) -> Vec<String> + Send>, AutoComplete)>), Text(
String,
Cursor,
Option<(Box<Fn(&Context, &str) -> Vec<String> + Send>, AutoComplete)>,
),
Choice(Vec<String>, Cursor), Choice(Vec<String>, Cursor),
TextArea(String, Cursor), TextArea(String, Cursor),
} }
@ -42,12 +46,8 @@ impl Default for Field {
impl Field { impl Field {
fn as_str(&self) -> &str { fn as_str(&self) -> &str {
match self { match self {
Text(ref s, _, _) => { Text(ref s, _, _) => s,
s TextArea(ref s, _) => s,
},
TextArea(ref s, _) => {
s
},
Choice(ref v, cursor) => { Choice(ref v, cursor) => {
if v.is_empty() { if v.is_empty() {
"" ""
@ -60,35 +60,42 @@ impl Field {
pub fn into_string(self) -> String { pub fn into_string(self) -> String {
match self { match self {
Text(s, _, _) => { Text(s, _, _) => s,
s TextArea(s, _) => s,
}, Choice(mut v, cursor) => v.remove(cursor),
TextArea(s, _) => {
s
},
Choice(mut v, cursor) => {
v.remove(cursor)
}
} }
} }
fn draw_cursor(&mut self, grid: &mut CellBuffer, area: Area, secondary_area: Area, context: &mut Context) { fn draw_cursor(
&mut self,
grid: &mut CellBuffer,
area: Area,
secondary_area: Area,
context: &mut Context,
) {
let upper_left = upper_left!(area); let upper_left = upper_left!(area);
match self { match self {
Text(ref term, cursor, auto_complete_fn) => { Text(ref term, cursor, auto_complete_fn) => {
change_colors(grid, (pos_inc(upper_left, (*cursor, 0)), (pos_inc(upper_left, (*cursor, 0)))), Color::Default, Color::Byte(248)); change_colors(
if term.chars().count() <= 2 { return; } grid,
(
pos_inc(upper_left, (*cursor, 0)),
(pos_inc(upper_left, (*cursor, 0))),
),
Color::Default,
Color::Byte(248),
);
if term.chars().count() <= 2 {
return;
}
if let Some((auto_complete_fn, auto_complete)) = auto_complete_fn { if let Some((auto_complete_fn, auto_complete)) = auto_complete_fn {
let entries = auto_complete_fn(context, term); let entries = auto_complete_fn(context, term);
auto_complete.set_suggestions(entries); auto_complete.set_suggestions(entries);
auto_complete.draw(grid, secondary_area, context); auto_complete.draw(grid, secondary_area, context);
} }
},
TextArea(_, _) => {
},
Choice(_, _cursor) => {
} }
TextArea(_, _) => {}
Choice(_, _cursor) => {}
} }
} }
} }
@ -101,7 +108,8 @@ impl Component for Field {
Color::Default, Color::Default,
Color::Default, Color::Default,
area, area,
true); true,
);
} }
fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool { fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool {
if let Text(ref mut s, ref mut cursor, Some((_, auto_complete))) = self { if let Text(ref mut s, ref mut cursor, Some((_, auto_complete))) = self {
@ -112,8 +120,8 @@ impl Component for Field {
*cursor = s.chars().count(); *cursor = s.chars().count();
return true; return true;
} }
}, }
_ => {}, _ => {}
} }
} }
@ -124,21 +132,20 @@ impl Component for Field {
} else { } else {
return false; return false;
} }
}, }
UIEventType::InsertInput(Key::Down) => { UIEventType::InsertInput(Key::Down) => {
if let Text(_, _, Some((_, auto_complete))) = self { if let Text(_, _, Some((_, auto_complete))) = self {
auto_complete.inc_cursor(); auto_complete.inc_cursor();
} else { } else {
return false; return false;
} }
}, }
UIEventType::InsertInput(Key::Right) => { UIEventType::InsertInput(Key::Right) => match self {
match self {
TextArea(ref s, ref mut cursor) | Text(ref s, ref mut cursor, _) => { TextArea(ref s, ref mut cursor) | Text(ref s, ref mut cursor, _) => {
if *cursor < s.len() { if *cursor < s.len() {
*cursor += 1; *cursor += 1;
} }
}, }
Choice(ref vec, ref mut cursor) => { Choice(ref vec, ref mut cursor) => {
*cursor = if *cursor == vec.len().saturating_sub(1) { *cursor = if *cursor == vec.len().saturating_sub(1) {
0 0
@ -146,17 +153,15 @@ impl Component for Field {
*cursor + 1 *cursor + 1
}; };
} }
}
}, },
UIEventType::InsertInput(Key::Left) => { UIEventType::InsertInput(Key::Left) => match self {
match self {
TextArea(_, ref mut cursor) | Text(_, ref mut cursor, _) => { TextArea(_, ref mut cursor) | Text(_, ref mut cursor, _) => {
if *cursor == 0 { if *cursor == 0 {
return false; return false;
} else { } else {
*cursor -= 1; *cursor -= 1;
} }
}, }
Choice(_, ref mut cursor) => { Choice(_, ref mut cursor) => {
if *cursor == 0 { if *cursor == 0 {
return false; return false;
@ -164,34 +169,31 @@ impl Component for Field {
*cursor -= 1; *cursor -= 1;
} }
} }
}
}, },
UIEventType::InsertInput(Key::Char(k)) => { UIEventType::InsertInput(Key::Char(k)) => match self {
match self {
Text(ref mut s, ref mut cursor, _) | TextArea(ref mut s, ref mut cursor) => { Text(ref mut s, ref mut cursor, _) | TextArea(ref mut s, ref mut cursor) => {
s.insert(*cursor, k); s.insert(*cursor, k);
*cursor += 1; *cursor += 1;
},
_ => {}
} }
_ => {}
}, },
UIEventType::InsertInput(Key::Backspace) => { UIEventType::InsertInput(Key::Backspace) => match self {
match self {
Text(ref mut s, ref mut cursor, ref mut auto_complete) => { Text(ref mut s, ref mut cursor, ref mut auto_complete) => {
if *cursor > 0 { if *cursor > 0 {
*cursor -= 1; *cursor -= 1;
s.remove(*cursor); s.remove(*cursor);
} }
auto_complete.as_mut().map(|ac| ac.1.set_suggestions(Vec::new())); auto_complete
}, .as_mut()
.map(|ac| ac.1.set_suggestions(Vec::new()));
}
TextArea(ref mut s, ref mut cursor) => { TextArea(ref mut s, ref mut cursor) => {
if *cursor > 0 { if *cursor > 0 {
*cursor -= 1; *cursor -= 1;
s.remove(*cursor); s.remove(*cursor);
} }
},
_ => {}
} }
_ => {}
}, },
_ => { _ => {
return false; return false;
@ -212,7 +214,6 @@ impl fmt::Display for Field {
} }
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct FormWidget { pub struct FormWidget {
fields: FnvHashMap<String, Field>, fields: FnvHashMap<String, Field>,
@ -272,10 +273,20 @@ impl FormWidget {
self.layout.push(value.0.clone()); self.layout.push(value.0.clone());
self.fields.insert(value.0, TextArea(value.1, 0)); self.fields.insert(value.0, TextArea(value.1, 0));
} }
pub fn push_cl(&mut self, value: (String, String, Box<Fn(&Context, &str) -> Vec<String> + Send>)) { pub fn push_cl(
&mut self,
value: (
String,
String,
Box<Fn(&Context, &str) -> Vec<String> + Send>,
),
) {
self.field_name_max_length = std::cmp::max(self.field_name_max_length, value.0.len()); self.field_name_max_length = std::cmp::max(self.field_name_max_length, value.0.len());
self.layout.push(value.0.clone()); self.layout.push(value.0.clone());
self.fields.insert(value.0, Text(value.1, 0, Some((value.2, AutoComplete::new(Vec::new()))))); self.fields.insert(
value.0,
Text(value.1, 0, Some((value.2, AutoComplete::new(Vec::new())))),
);
} }
pub fn push(&mut self, value: (String, String)) { pub fn push(&mut self, value: (String, String)) {
self.field_name_max_length = std::cmp::max(self.field_name_max_length, value.0.len()); self.field_name_max_length = std::cmp::max(self.field_name_max_length, value.0.len());
@ -317,33 +328,64 @@ impl Component for FormWidget {
grid, grid,
Color::Default, Color::Default,
Color::Default, Color::Default,
(pos_inc(upper_left, (1, i)), set_y(bottom_right, i + get_y(upper_left))), (
pos_inc(upper_left, (1, i)),
set_y(bottom_right, i + get_y(upper_left)),
),
false, false,
); );
/* draw field */ /* draw field */
v.draw(grid, v.draw(
(pos_inc(upper_left, (self.field_name_max_length + 3, i)), set_y(bottom_right, i + get_y(upper_left))), context); grid,
(
pos_inc(upper_left, (self.field_name_max_length + 3, i)),
set_y(bottom_right, i + get_y(upper_left)),
),
context,
);
/* Highlight if necessary */ /* Highlight if necessary */
if i == self.cursor { if i == self.cursor {
if self.focus == FormFocus::Fields { if self.focus == FormFocus::Fields {
change_colors(grid, (pos_inc(upper_left, (0, i)), set_y(bottom_right, i + get_y(upper_left))), Color::Default, Color::Byte(246)); change_colors(
grid,
(
pos_inc(upper_left, (0, i)),
set_y(bottom_right, i + get_y(upper_left)),
),
Color::Default,
Color::Byte(246),
);
} }
if self.focus == FormFocus::TextInput { if self.focus == FormFocus::TextInput {
v.draw_cursor(grid, v.draw_cursor(
(pos_inc(upper_left, (self.field_name_max_length + 3 , i)), grid,
(get_x(upper_left) + self.field_name_max_length + 3, i + get_y(upper_left))), (
(pos_inc(upper_left, (self.field_name_max_length + 3 , i + 1)), bottom_right), pos_inc(upper_left, (self.field_name_max_length + 3, i)),
context); (
get_x(upper_left) + self.field_name_max_length + 3,
i + get_y(upper_left),
),
),
(
pos_inc(upper_left, (self.field_name_max_length + 3, i + 1)),
bottom_right,
),
context,
);
} }
} }
} }
if !self.hide_buttons { if !self.hide_buttons {
let length = self.layout.len(); let length = self.layout.len();
self.buttons.draw(grid, self.buttons.draw(
(pos_inc(upper_left, (1, length * 2 + 3)), set_y(bottom_right, length * 2 + 3 + get_y(upper_left))), grid,
context); (
pos_inc(upper_left, (1, length * 2 + 3)),
set_y(bottom_right, length * 2 + 3 + get_y(upper_left)),
),
context,
);
} }
self.dirty = false; self.dirty = false;
context.dirty_areas.push_back(area); context.dirty_areas.push_back(area);
@ -356,43 +398,43 @@ impl Component for FormWidget {
match event.event_type { match event.event_type {
UIEventType::Input(Key::Up) if self.focus == FormFocus::Buttons => { UIEventType::Input(Key::Up) if self.focus == FormFocus::Buttons => {
self.focus = FormFocus::Fields; self.focus = FormFocus::Fields;
}, }
UIEventType::InsertInput(Key::Up) if self.focus == FormFocus::TextInput => { UIEventType::InsertInput(Key::Up) if self.focus == FormFocus::TextInput => {
let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap(); let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap();
field.process_event(event, context); field.process_event(event, context);
}, }
UIEventType::Input(Key::Up) => { UIEventType::Input(Key::Up) => {
self.cursor = self.cursor.saturating_sub(1); self.cursor = self.cursor.saturating_sub(1);
}, }
UIEventType::InsertInput(Key::Down) if self.focus == FormFocus::TextInput => { UIEventType::InsertInput(Key::Down) if self.focus == FormFocus::TextInput => {
let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap(); let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap();
field.process_event(event, context); field.process_event(event, context);
}, }
UIEventType::Input(Key::Down) if self.cursor < self.layout.len().saturating_sub(1) => { UIEventType::Input(Key::Down) if self.cursor < self.layout.len().saturating_sub(1) => {
self.cursor += 1; self.cursor += 1;
}, }
UIEventType::Input(Key::Down) if self.focus == FormFocus::Fields => { UIEventType::Input(Key::Down) if self.focus == FormFocus::Fields => {
self.focus = FormFocus::Buttons; self.focus = FormFocus::Buttons;
if self.hide_buttons { if self.hide_buttons {
self.set_dirty(); self.set_dirty();
return false; return false;
} }
}, }
UIEventType::InsertInput(Key::Char('\t')) if self.focus == FormFocus::TextInput => { UIEventType::InsertInput(Key::Char('\t')) if self.focus == FormFocus::TextInput => {
let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap(); let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap();
field.process_event(event, context); field.process_event(event, context);
}, }
UIEventType::Input(Key::Char('\n')) if self.focus == FormFocus::Fields => { UIEventType::Input(Key::Char('\n')) if self.focus == FormFocus::Fields => {
self.focus = FormFocus::TextInput; self.focus = FormFocus::TextInput;
context.replies.push_back(UIEvent { context.replies.push_back(UIEvent {
id: 0, id: 0,
event_type: UIEventType::ChangeMode(UIMode::Insert), event_type: UIEventType::ChangeMode(UIMode::Insert),
}); });
}, }
UIEventType::InsertInput(Key::Right) if self.focus == FormFocus::TextInput => { UIEventType::InsertInput(Key::Right) if self.focus == FormFocus::TextInput => {
let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap(); let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap();
field.process_event(event, context); field.process_event(event, context);
}, }
UIEventType::InsertInput(Key::Left) if self.focus == FormFocus::TextInput => { UIEventType::InsertInput(Key::Left) if self.focus == FormFocus::TextInput => {
let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap(); let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap();
if !field.process_event(event, context) { if !field.process_event(event, context) {
@ -402,18 +444,18 @@ impl Component for FormWidget {
event_type: UIEventType::ChangeMode(UIMode::Normal), event_type: UIEventType::ChangeMode(UIMode::Normal),
}); });
} }
}, }
UIEventType::ChangeMode(UIMode::Normal) if self.focus == FormFocus::TextInput => { UIEventType::ChangeMode(UIMode::Normal) if self.focus == FormFocus::TextInput => {
self.focus = FormFocus::Fields; self.focus = FormFocus::Fields;
}, }
UIEventType::InsertInput(Key::Char(_)) if self.focus == FormFocus::TextInput => { UIEventType::InsertInput(Key::Char(_)) if self.focus == FormFocus::TextInput => {
let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap(); let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap();
field.process_event(event, context); field.process_event(event, context);
}, }
UIEventType::InsertInput(Key::Backspace) if self.focus == FormFocus::TextInput => { UIEventType::InsertInput(Key::Backspace) if self.focus == FormFocus::TextInput => {
let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap(); let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap();
field.process_event(event, context); field.process_event(event, context);
}, }
_ => { _ => {
return false; return false;
} }
@ -429,9 +471,11 @@ impl Component for FormWidget {
} }
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct ButtonWidget<T> where T: std::fmt::Debug + Default + Send{ pub struct ButtonWidget<T>
where
T: std::fmt::Debug + Default + Send,
{
buttons: FnvHashMap<String, T>, buttons: FnvHashMap<String, T>,
layout: Vec<String>, layout: Vec<String>,
@ -439,13 +483,19 @@ pub struct ButtonWidget<T> where T: std::fmt::Debug + Default + Send{
cursor: usize, cursor: usize,
} }
impl<T> fmt::Display for ButtonWidget<T> where T: std::fmt::Debug + Default + Send { impl<T> fmt::Display for ButtonWidget<T>
where
T: std::fmt::Debug + Default + Send,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt("", f) Display::fmt("", f)
} }
} }
impl<T> ButtonWidget<T> where T: std::fmt::Debug + Default + Send { impl<T> ButtonWidget<T>
where
T: std::fmt::Debug + Default + Send,
{
pub fn new(init_val: (String, T)) -> ButtonWidget<T> { pub fn new(init_val: (String, T)) -> ButtonWidget<T> {
ButtonWidget { ButtonWidget {
layout: vec![init_val.0.clone()], layout: vec![init_val.0.clone()],
@ -465,8 +515,10 @@ impl<T> ButtonWidget<T> where T: std::fmt::Debug + Default + Send {
} }
} }
impl<T> Component for ButtonWidget<T>
impl<T> Component for ButtonWidget<T> where T: std::fmt::Debug + Default + Send { where
T: std::fmt::Debug + Default + Send,
{
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);
@ -477,8 +529,15 @@ impl<T> Component for ButtonWidget<T> where T: std::fmt::Debug + Default + Send
k.as_str(), k.as_str(),
grid, grid,
Color::Default, Color::Default,
if i == self.cursor { Color::Byte(246) } else { Color::Default }, if i == self.cursor {
(pos_inc(upper_left, (len, 0)), pos_inc(upper_left, (cur_len + len, 0))), Color::Byte(246)
} else {
Color::Default
},
(
pos_inc(upper_left, (len, 0)),
pos_inc(upper_left, (cur_len + len, 0)),
),
false, false,
); );
len += cur_len + 3; len += cur_len + 3;
@ -487,17 +546,21 @@ impl<T> Component for ButtonWidget<T> where T: std::fmt::Debug + Default + Send
fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool { fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool {
match event.event_type { match event.event_type {
UIEventType::Input(Key::Char('\n')) => { UIEventType::Input(Key::Char('\n')) => {
self.result = Some(self.buttons.remove(&self.layout[self.cursor]).unwrap_or_default()); self.result = Some(
self.buttons
.remove(&self.layout[self.cursor])
.unwrap_or_default(),
);
return true; return true;
}, }
UIEventType::Input(Key::Left) => { UIEventType::Input(Key::Left) => {
self.cursor = self.cursor.saturating_sub(1); self.cursor = self.cursor.saturating_sub(1);
return true; return true;
}, }
UIEventType::Input(Key::Right) if self.cursor < self.layout.len().saturating_sub(1) => { UIEventType::Input(Key::Right) if self.cursor < self.layout.len().saturating_sub(1) => {
self.cursor += 1; self.cursor += 1;
return true; return true;
}, }
_ => {} _ => {}
} }
@ -509,8 +572,6 @@ impl<T> Component for ButtonWidget<T> where T: std::fmt::Debug + Default + Send
fn set_dirty(&mut self) {} fn set_dirty(&mut self) {}
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct AutoComplete { pub struct AutoComplete {
entries: Vec<String>, entries: Vec<String>,
@ -528,7 +589,9 @@ impl fmt::Display for AutoComplete {
impl Component for AutoComplete { impl Component for AutoComplete {
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.entries.is_empty() { return; }; if self.entries.is_empty() {
return;
};
let upper_left = upper_left!(area); let upper_left = upper_left!(area);
self.dirty = false; self.dirty = false;
@ -540,7 +603,15 @@ impl Component for AutoComplete {
((0, 0), (width.saturating_sub(1), height.saturating_sub(1))), ((0, 0), (width.saturating_sub(1), height.saturating_sub(1))),
); );
/* Highlight cursor */ /* Highlight cursor */
change_colors(grid, (pos_inc(upper_left, (0, self.cursor)), pos_inc(upper_left, (width.saturating_sub(1), self.cursor))), Color::Default, Color::Byte(246)); change_colors(
grid,
(
pos_inc(upper_left, (0, self.cursor)),
pos_inc(upper_left, (width.saturating_sub(1), self.cursor)),
),
Color::Default,
Color::Byte(246),
);
context.dirty_areas.push_back(area); context.dirty_areas.push_back(area);
} }
fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool { fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool {
@ -571,7 +642,11 @@ impl AutoComplete {
return; return;
} }
let mut content = CellBuffer::new(entries.iter().map(|e| e.len()).max().unwrap_or(0) + 1, entries.len(), Cell::with_style(Color::Byte(23), Color::Byte(7), Attr::Default)); let mut content = CellBuffer::new(
entries.iter().map(|e| e.len()).max().unwrap_or(0) + 1,
entries.len(),
Cell::with_style(Color::Byte(23), Color::Byte(7), Attr::Default),
);
let width = content.cols(); let width = content.cols();
for (i, e) in entries.iter().enumerate() { for (i, e) in entries.iter().enumerate() {
let (x, _) = write_string_to_grid( let (x, _) = write_string_to_grid(
@ -596,8 +671,16 @@ impl AutoComplete {
self.cursor = 0; self.cursor = 0;
} }
pub fn inc_cursor(&mut self) { if self.cursor < self.entries.len().saturating_sub(1) { self.cursor += 1; self.set_dirty(); } } pub fn inc_cursor(&mut self) {
pub fn dec_cursor(&mut self) { self.cursor = self.cursor.saturating_sub(1); self.set_dirty(); } if self.cursor < self.entries.len().saturating_sub(1) {
self.cursor += 1;
self.set_dirty();
}
}
pub fn dec_cursor(&mut self) {
self.cursor = self.cursor.saturating_sub(1);
self.set_dirty();
}
pub fn get_suggestion(&mut self) -> Option<String> { pub fn get_suggestion(&mut self) -> Option<String> {
if self.entries.is_empty() { if self.entries.is_empty() {

View File

@ -19,25 +19,24 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
extern crate bincode;
extern crate config; extern crate config;
extern crate serde; extern crate serde;
extern crate xdg; extern crate xdg;
extern crate bincode;
pub mod pager;
pub mod notifications; pub mod notifications;
pub mod pager;
pub mod shortcuts; pub mod shortcuts;
pub mod accounts; pub mod accounts;
pub use self::accounts::Account; pub use self::accounts::Account;
pub use self::shortcuts::*;
use self::config::{Config, File, FileFormat}; use self::config::{Config, File, FileFormat};
pub use self::shortcuts::*;
use self::notifications::NotificationsSettings;
use melib::conf::AccountSettings; use melib::conf::AccountSettings;
use melib::error::*; use melib::error::*;
use pager::PagerSettings; use pager::PagerSettings;
use self::notifications::NotificationsSettings;
use self::serde::{de, Deserialize, Deserializer}; use self::serde::{de, Deserialize, Deserializer};
use std::collections::HashMap; use std::collections::HashMap;
@ -194,7 +193,6 @@ impl Settings {
} }
} }
#[derive(Copy, Debug, Clone, Deserialize)] #[derive(Copy, Debug, Clone, Deserialize)]
pub enum IndexStyle { pub enum IndexStyle {
Plain, Plain,
@ -209,7 +207,8 @@ impl Default for IndexStyle {
} }
fn index_from_str<'de, D>(deserializer: D) -> std::result::Result<IndexStyle, D::Error> fn index_from_str<'de, D>(deserializer: D) -> std::result::Result<IndexStyle, D::Error>
where D: Deserializer<'de> where
D: Deserializer<'de>,
{ {
let s = <String>::deserialize(deserializer)?; let s = <String>::deserialize(deserializer)?;
match s.as_str() { match s.as_str() {

View File

@ -58,7 +58,6 @@ pub struct Account {
pub(crate) workers: Vec<Worker>, pub(crate) workers: Vec<Worker>,
pub(crate) settings: AccountConf, pub(crate) settings: AccountConf,
pub(crate) runtime_settings: AccountConf, pub(crate) runtime_settings: AccountConf,
pub(crate) backend: Box<MailBackend>, pub(crate) backend: Box<MailBackend>,
@ -68,9 +67,7 @@ pub struct Account {
impl Drop for Account { impl Drop for Account {
fn drop(&mut self) { fn drop(&mut self) {
//TODO: Avoid panics //TODO: Avoid panics
let data_dir = let data_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
xdg::BaseDirectories::with_profile("meli", &self.name)
.unwrap();
if let Ok(data) = data_dir.place_data_file("addressbook") { if let Ok(data) = data_dir.place_data_file("addressbook") {
/* place result in cache directory */ /* place result in cache directory */
let f = match fs::File::create(data) { let f = match fs::File::create(data) {
@ -99,9 +96,7 @@ impl Account {
folders.push(None); folders.push(None);
workers.push(Account::new_worker(f, &mut backend, notify_fn.clone())); workers.push(Account::new_worker(f, &mut backend, notify_fn.clone()));
} }
let data_dir = let data_dir = xdg::BaseDirectories::with_profile("meli", &name).unwrap();
xdg::BaseDirectories::with_profile("meli", &name)
.unwrap();
let address_book = if let Ok(data) = data_dir.place_data_file("addressbook") { let address_book = if let Ok(data) = data_dir.place_data_file("addressbook") {
if data.exists() { if data.exists() {
let reader = io::BufReader::new(fs::File::open(data).unwrap()); let reader = io::BufReader::new(fs::File::open(data).unwrap());
@ -303,7 +298,8 @@ impl Account {
} }
pub fn save_draft(&self, draft: Draft) -> Result<()> { pub fn save_draft(&self, draft: Draft) -> Result<()> {
self.backend.save(draft.to_string()?, &self.settings.conf.draft_folder) self.backend
.save(draft.to_string()?, &self.settings.conf.draft_folder)
} }
} }

View File

@ -52,7 +52,6 @@ pub struct CompactListingShortcuts {
} }
} }
impl Default for CompactListingShortcuts { impl Default for CompactListingShortcuts {
fn default() -> Self { fn default() -> Self {
CompactListingShortcuts { CompactListingShortcuts {

View File

@ -23,20 +23,20 @@
* User actions that need to be handled by the UI * User actions that need to be handled by the UI
*/ */
pub use melib::mailbox::{SortField, SortOrder};
use components::Component; use components::Component;
pub use melib::mailbox::{SortField, SortOrder};
extern crate uuid; extern crate uuid;
use uuid::Uuid; use uuid::Uuid;
#[derive(Debug, )] #[derive(Debug)]
pub enum ListingAction { pub enum ListingAction {
SetPlain, SetPlain,
SetThreaded, SetThreaded,
SetCompact, SetCompact,
} }
#[derive(Debug, )] #[derive(Debug)]
pub enum TabAction { pub enum TabAction {
TabOpen(Option<Box<Component>>), TabOpen(Option<Box<Component>>),
NewDraft, NewDraft,
@ -45,7 +45,7 @@ pub enum TabAction {
Kill(Uuid), Kill(Uuid),
} }
#[derive(Debug, )] #[derive(Debug)]
pub enum Action { pub enum Action {
Listing(ListingAction), Listing(ListingAction),
ViewMailbox(usize), ViewMailbox(usize),

View File

@ -64,7 +64,8 @@ impl InputHandler {
}, },
&rx, &rx,
) )
}).unwrap(); })
.unwrap();
} }
fn kill(&self) { fn kill(&self) {
self.tx.send(false); self.tx.send(false);
@ -141,7 +142,8 @@ impl Drop for State {
cursor::Goto(1, 1), cursor::Goto(1, 1),
cursor::Show, cursor::Show,
BracketModeEnd, BracketModeEnd,
).unwrap(); )
.unwrap();
self.flush(); self.flush();
} }
} }
@ -187,7 +189,8 @@ impl State {
sender.send(ThreadEvent::UIEvent(UIEventType::StartupCheck)) sender.send(ThreadEvent::UIEvent(UIEventType::StartupCheck))
})), })),
) )
}).collect(); })
.collect();
accounts.sort_by(|a, b| a.name().cmp(&b.name())); accounts.sort_by(|a, b| a.name().cmp(&b.name()));
let mut s = State { let mut s = State {
cols, cols,
@ -235,7 +238,8 @@ impl State {
cursor::Hide, cursor::Hide,
clear::All, clear::All,
cursor::Goto(1, 1) cursor::Goto(1, 1)
).unwrap(); )
.unwrap();
s.flush(); s.flush();
eprintln!("DEBUG: inserting mailbox hashes:"); eprintln!("DEBUG: inserting mailbox hashes:");
for (x, account) in s.context.accounts.iter_mut().enumerate() { for (x, account) in s.context.accounts.iter_mut().enumerate() {
@ -305,7 +309,8 @@ impl State {
"{}{}", "{}{}",
termion::screen::ToMainScreen, termion::screen::ToMainScreen,
cursor::Show cursor::Show
).unwrap(); )
.unwrap();
self.flush(); self.flush();
self.stdout = None; self.stdout = None;
self.context.input.kill(); self.context.input.kill();
@ -322,7 +327,8 @@ impl State {
cursor::Hide, cursor::Hide,
clear::All, clear::All,
cursor::Goto(1, 1) cursor::Goto(1, 1)
).unwrap(); )
.unwrap();
self.flush(); self.flush();
} }
@ -381,7 +387,8 @@ impl State {
self.stdout(), self.stdout(),
"{}", "{}",
cursor::Goto(get_x(upper_left) as u16 + 1, (y + 1) as u16) cursor::Goto(get_x(upper_left) as u16 + 1, (y + 1) as u16)
).unwrap(); )
.unwrap();
for x in get_x(upper_left)..=get_x(bottom_right) { for x in get_x(upper_left)..=get_x(bottom_right) {
let c = self.grid[(x, y)]; let c = self.grid[(x, y)];
@ -397,14 +404,16 @@ impl State {
self.stdout(), self.stdout(),
"{}", "{}",
termion::color::Bg(termion::color::Reset) termion::color::Bg(termion::color::Reset)
).unwrap(); )
.unwrap();
} }
if c.fg() != Color::Default { if c.fg() != Color::Default {
write!( write!(
self.stdout(), self.stdout(),
"{}", "{}",
termion::color::Fg(termion::color::Reset) termion::color::Fg(termion::color::Reset)
).unwrap(); )
.unwrap();
} }
} }
} }
@ -473,12 +482,12 @@ impl State {
self.flush(); self.flush();
} }
return; return;
}, }
UIEventType::ChangeMode(m) => { UIEventType::ChangeMode(m) => {
self.context self.context
.sender .sender
.send(ThreadEvent::UIEvent(UIEventType::ChangeMode(m))); .send(ThreadEvent::UIEvent(UIEventType::ChangeMode(m)));
}, }
_ => {} _ => {}
} }
/* inform each entity */ /* inform each entity */

View File

@ -19,8 +19,8 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
extern crate serde; extern crate serde;
use self::serde::{de, Deserialize, Deserializer, };
use self::serde::de::Visitor; use self::serde::de::Visitor;
use self::serde::{de, Deserialize, Deserializer};
#[macro_use] #[macro_use]
mod position; mod position;

View File

@ -183,7 +183,6 @@ impl CellBuffer {
self.cols = 0; self.cols = 0;
self.rows = 0; self.rows = 0;
} }
} }
impl HasSize for CellBuffer { impl HasSize for CellBuffer {
@ -755,7 +754,10 @@ pub fn word_break_string(mut s: &str, width: usize) -> Vec<&str> {
} }
} }
if s.len() > width { if s.len() > width {
if let Some(next_idx) = s.as_bytes()[..width].iter().rposition(u8::is_ascii_whitespace) { if let Some(next_idx) = s.as_bytes()[..width]
.iter()
.rposition(u8::is_ascii_whitespace)
{
ret.push(&s[..next_idx]); ret.push(&s[..next_idx]);
s = &s[next_idx + 1..]; s = &s[next_idx + 1..];
} else { } else {
@ -766,7 +768,6 @@ pub fn word_break_string(mut s: &str, width: usize) -> Vec<&str> {
ret.push(s); ret.push(s);
break; break;
} }
} }
ret ret

View File

@ -203,7 +203,6 @@ pub const BRACKET_PASTE_END: &[u8] = b"\x1B[201~";
const FIELDS: &[&str] = &[]; const FIELDS: &[&str] = &[];
impl<'de> Deserialize<'de> for Key { impl<'de> Deserialize<'de> for Key {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where where

View File

@ -206,7 +206,7 @@ fn wcwidth(ucs: WChar) -> Option<usize> {
(ucs >= 0xff00 && ucs <= 0xff5f) || /* Fullwidth Forms */ (ucs >= 0xff00 && ucs <= 0xff5f) || /* Fullwidth Forms */
(ucs >= 0xffe0 && ucs <= 0xffe6) || (ucs >= 0xffe0 && ucs <= 0xffe6) ||
(ucs >= 0x20000 && ucs <= 0x2ffff)) (ucs >= 0x20000 && ucs <= 0x2ffff))
) ),
); );
} }