mirror of
https://git.meli.delivery/meli/meli
synced 2024-11-17 03:26:20 +00:00
ui/contacts: add side-menu, remove accounts tab
- Rename accounts tab to status tab - add side menu to contacts tab to switch between accounts
This commit is contained in:
parent
ce11447a1a
commit
a9425be61e
@ -201,11 +201,10 @@ fn main() -> std::result::Result<(), std::io::Error> {
|
||||
|
||||
let signal_recvr = notify(signals, sender)?;
|
||||
|
||||
/* Register some reasonably useful interfaces */
|
||||
let window = Box::new(Tabbed::new(vec![
|
||||
Box::new(listing::Listing::new(&state.context.accounts)),
|
||||
Box::new(AccountsPanel::new(&state.context)),
|
||||
Box::new(ContactList::default()),
|
||||
Box::new(ContactList::new(&state.context)),
|
||||
Box::new(StatusPanel::new()),
|
||||
]));
|
||||
|
||||
let status_bar = Box::new(StatusBar::new(window));
|
||||
|
@ -11,8 +11,16 @@ enum ViewMode {
|
||||
View(ComponentId),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct AccountMenuEntry {
|
||||
name: String,
|
||||
// Index in the config account vector.
|
||||
index: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ContactList {
|
||||
accounts: Vec<AccountMenuEntry>,
|
||||
cursor_pos: usize,
|
||||
new_cursor_pos: usize,
|
||||
account_pos: usize,
|
||||
@ -24,17 +32,14 @@ pub struct ContactList {
|
||||
|
||||
mode: ViewMode,
|
||||
dirty: bool,
|
||||
show_divider: bool,
|
||||
menu_visibility: bool,
|
||||
movement: Option<PageMovement>,
|
||||
view: Option<ContactManager>,
|
||||
ratio: usize, // right/(container width) * 100
|
||||
id: ComponentId,
|
||||
}
|
||||
|
||||
impl Default for ContactList {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ContactList {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", ContactList::DESCRIPTION)
|
||||
@ -43,8 +48,18 @@ impl fmt::Display for ContactList {
|
||||
|
||||
impl ContactList {
|
||||
const DESCRIPTION: &'static str = "contact list";
|
||||
pub fn new() -> Self {
|
||||
pub fn new(context: &Context) -> Self {
|
||||
let accounts = context
|
||||
.accounts
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, a)| AccountMenuEntry {
|
||||
name: a.name().to_string(),
|
||||
index: i,
|
||||
})
|
||||
.collect();
|
||||
ContactList {
|
||||
accounts,
|
||||
cursor_pos: 0,
|
||||
new_cursor_pos: 0,
|
||||
length: 0,
|
||||
@ -56,14 +71,17 @@ impl ContactList {
|
||||
dirty: true,
|
||||
movement: None,
|
||||
view: None,
|
||||
ratio: 90,
|
||||
show_divider: false,
|
||||
menu_visibility: true,
|
||||
id: ComponentId::new_v4(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_account(pos: usize) -> Self {
|
||||
pub fn for_account(pos: usize, context: &Context) -> Self {
|
||||
ContactList {
|
||||
account_pos: pos,
|
||||
..Self::new()
|
||||
..Self::new(context)
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,23 +222,109 @@ impl ContactList {
|
||||
};
|
||||
change_colors(grid, area, fg_color, bg_color);
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for ContactList {
|
||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
if let Some(mgr) = self.view.as_mut() {
|
||||
mgr.draw(grid, area, context);
|
||||
return;
|
||||
}
|
||||
|
||||
if !self.dirty {
|
||||
fn draw_menu(&mut self, grid: &mut CellBuffer, mut area: Area, context: &mut Context) {
|
||||
if !self.is_dirty() {
|
||||
return;
|
||||
}
|
||||
clear_area(grid, area);
|
||||
/* visually divide menu and listing */
|
||||
area = (area.0, pos_dec(area.1, (1, 0)));
|
||||
let upper_left = upper_left!(area);
|
||||
let bottom_right = bottom_right!(area);
|
||||
self.dirty = false;
|
||||
if !self.initialized {
|
||||
self.initialize(context);
|
||||
let mut y = get_y(upper_left);
|
||||
for a in &self.accounts {
|
||||
self.print_account(grid, (set_y(upper_left, y), bottom_right), &a, context);
|
||||
y += 1;
|
||||
}
|
||||
|
||||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
/*
|
||||
* Print a single account in the menu area.
|
||||
*/
|
||||
fn print_account(
|
||||
&self,
|
||||
grid: &mut CellBuffer,
|
||||
area: Area,
|
||||
a: &AccountMenuEntry,
|
||||
context: &mut Context,
|
||||
) {
|
||||
if !is_valid_area!(area) {
|
||||
debug!("BUG: invalid area in print_account");
|
||||
}
|
||||
|
||||
let width = width!(area);
|
||||
let must_highlight_account: bool = self.account_pos == a.index;
|
||||
let (fg_color, bg_color) = if must_highlight_account {
|
||||
if self.account_pos == a.index {
|
||||
(Color::Byte(233), Color::Byte(15))
|
||||
} else {
|
||||
(Color::Byte(15), Color::Byte(233))
|
||||
}
|
||||
} else {
|
||||
(Color::Default, Color::Default)
|
||||
};
|
||||
|
||||
let s = format!(" [{}]", context.accounts[a.index].address_book.len());
|
||||
|
||||
if a.name.grapheme_len() + s.len() > width + 1 {
|
||||
/* Print account name */
|
||||
write_string_to_grid(&a.name, grid, fg_color, bg_color, Attr::Bold, area, false);
|
||||
write_string_to_grid(
|
||||
&s,
|
||||
grid,
|
||||
fg_color,
|
||||
bg_color,
|
||||
Attr::Bold,
|
||||
(
|
||||
pos_dec(
|
||||
(get_x(bottom_right!(area)), get_y(upper_left!(area))),
|
||||
(s.len() - 1, 0),
|
||||
),
|
||||
bottom_right!(area),
|
||||
),
|
||||
false,
|
||||
);
|
||||
write_string_to_grid(
|
||||
"…",
|
||||
grid,
|
||||
fg_color,
|
||||
bg_color,
|
||||
Attr::Bold,
|
||||
(
|
||||
pos_dec(
|
||||
(get_x(bottom_right!(area)), get_y(upper_left!(area))),
|
||||
(s.len() - 1, 0),
|
||||
),
|
||||
bottom_right!(area),
|
||||
),
|
||||
false,
|
||||
);
|
||||
} else {
|
||||
/* Print account name */
|
||||
|
||||
write_string_to_grid(&a.name, grid, fg_color, bg_color, Attr::Bold, area, false);
|
||||
write_string_to_grid(
|
||||
&s,
|
||||
grid,
|
||||
fg_color,
|
||||
bg_color,
|
||||
Attr::Bold,
|
||||
(
|
||||
pos_dec(
|
||||
(get_x(bottom_right!(area)), get_y(upper_left!(area))),
|
||||
(s.len() - 1, 0),
|
||||
),
|
||||
bottom_right!(area),
|
||||
),
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_list(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
let upper_left = upper_left!(area);
|
||||
let bottom_right = bottom_right!(area);
|
||||
|
||||
@ -371,6 +475,60 @@ impl Component for ContactList {
|
||||
);
|
||||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for ContactList {
|
||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
if let Some(mgr) = self.view.as_mut() {
|
||||
mgr.draw(grid, area, context);
|
||||
return;
|
||||
}
|
||||
|
||||
if !self.dirty {
|
||||
return;
|
||||
}
|
||||
if !self.initialized {
|
||||
self.initialize(context);
|
||||
}
|
||||
|
||||
let upper_left = upper_left!(area);
|
||||
let bottom_right = bottom_right!(area);
|
||||
let total_cols = get_x(bottom_right) - get_x(upper_left);
|
||||
|
||||
let right_component_width = if self.menu_visibility {
|
||||
(self.ratio * total_cols) / 100
|
||||
} else {
|
||||
total_cols
|
||||
};
|
||||
let mid = get_x(bottom_right) - right_component_width;
|
||||
if self.dirty && mid != get_x(upper_left) {
|
||||
if self.show_divider {
|
||||
for i in get_y(upper_left)..=get_y(bottom_right) {
|
||||
grid[(mid, i)].set_ch(VERT_BOUNDARY);
|
||||
grid[(mid, i)].set_fg(Color::Default);
|
||||
grid[(mid, i)].set_bg(Color::Default);
|
||||
}
|
||||
} else {
|
||||
for i in get_y(upper_left)..=get_y(bottom_right) {
|
||||
grid[(mid, i)].set_fg(Color::Default);
|
||||
grid[(mid, i)].set_bg(Color::Default);
|
||||
}
|
||||
}
|
||||
context
|
||||
.dirty_areas
|
||||
.push_back(((mid, get_y(upper_left)), (mid, get_y(bottom_right))));
|
||||
}
|
||||
|
||||
if right_component_width == total_cols {
|
||||
self.draw_list(grid, area, context);
|
||||
} else if right_component_width == 0 {
|
||||
self.draw_menu(grid, area, context);
|
||||
} else {
|
||||
self.draw_menu(grid, (upper_left, (mid, get_y(bottom_right))), context);
|
||||
self.draw_list(grid, (set_x(upper_left, mid + 1), bottom_right), context);
|
||||
}
|
||||
self.dirty = false;
|
||||
}
|
||||
|
||||
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
|
||||
if let Some(ref mut v) = self.view {
|
||||
@ -409,24 +567,53 @@ impl Component for ContactList {
|
||||
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(Key::Char('n')) if self.view.is_none() => {
|
||||
let card = Card::new();
|
||||
let mut manager = ContactManager::default();
|
||||
manager.set_parent_id(self.id);
|
||||
manager.card = card;
|
||||
manager.account_pos = self.account_pos;
|
||||
|
||||
self.mode = ViewMode::View(manager.id());
|
||||
self.view = Some(manager);
|
||||
UIEvent::Input(ref key) if *key == shortcuts["next_account"] && self.view.is_none() => {
|
||||
if self.accounts.is_empty() {
|
||||
return true;
|
||||
}
|
||||
if self.account_pos < self.accounts.len() - 1 {
|
||||
self.account_pos += 1;
|
||||
self.set_dirty();
|
||||
self.initialized = false;
|
||||
context
|
||||
.replies
|
||||
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
|
||||
self.get_status(context).unwrap(),
|
||||
)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(Key::Up) => {
|
||||
UIEvent::Input(ref key) if *key == shortcuts["prev_account"] && self.view.is_none() => {
|
||||
if self.accounts.is_empty() {
|
||||
return true;
|
||||
}
|
||||
if self.account_pos > 0 {
|
||||
self.account_pos -= 1;
|
||||
self.set_dirty();
|
||||
self.initialized = false;
|
||||
context
|
||||
.replies
|
||||
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
|
||||
self.get_status(context).unwrap(),
|
||||
)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(ref k)
|
||||
if k == shortcuts["toggle_menu_visibility"] && self.view.is_none() =>
|
||||
{
|
||||
self.menu_visibility = !self.menu_visibility;
|
||||
self.set_dirty();
|
||||
}
|
||||
UIEvent::Input(Key::Up) if self.view.is_none() => {
|
||||
self.set_dirty();
|
||||
self.new_cursor_pos = self.cursor_pos.saturating_sub(1);
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(Key::Down) if self.cursor_pos < self.length.saturating_sub(1) => {
|
||||
UIEvent::Input(Key::Down)
|
||||
if self.cursor_pos < self.length.saturating_sub(1) && self.view.is_none() =>
|
||||
{
|
||||
self.set_dirty();
|
||||
self.new_cursor_pos += 1;
|
||||
return true;
|
||||
@ -496,4 +683,11 @@ impl Component for ContactList {
|
||||
.map(|p| p.can_quit_cleanly(context))
|
||||
.unwrap_or(true)
|
||||
}
|
||||
|
||||
fn get_status(&self, context: &Context) -> Option<String> {
|
||||
Some(format!(
|
||||
"{} entries",
|
||||
context.accounts[self.account_pos].address_book.len()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -35,8 +35,8 @@ pub use self::compose::*;
|
||||
|
||||
pub mod pgp;
|
||||
|
||||
mod accounts;
|
||||
pub use self::accounts::*;
|
||||
mod status;
|
||||
pub use self::status::*;
|
||||
|
||||
fn get_display_name(context: &Context, idx: usize) -> String {
|
||||
let settings = context.accounts[idx].runtime_settings.account();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* meli - accounts module.
|
||||
* meli - status tab module.
|
||||
*
|
||||
* Copyright 2019 Manos Pitsidianakis
|
||||
*
|
||||
@ -23,25 +23,23 @@ use super::*;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AccountsPanel {
|
||||
pub struct StatusPanel {
|
||||
cursor: (usize, usize),
|
||||
account_cursor: usize,
|
||||
content: CellBuffer,
|
||||
dirty: bool,
|
||||
id: ComponentId,
|
||||
}
|
||||
|
||||
impl fmt::Display for AccountsPanel {
|
||||
impl fmt::Display for StatusPanel {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "accounts")
|
||||
write!(f, "status")
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for AccountsPanel {
|
||||
impl Component for StatusPanel {
|
||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
if self.dirty {
|
||||
self.initialize(context);
|
||||
self.dirty = false;
|
||||
if !self.dirty {
|
||||
return;
|
||||
}
|
||||
let (width, height) = self.content.size();
|
||||
{
|
||||
@ -145,22 +143,11 @@ impl Component for AccountsPanel {
|
||||
),
|
||||
),
|
||||
);
|
||||
self.dirty = false;
|
||||
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 {
|
||||
match *event {
|
||||
UIEvent::Input(Key::Char('k')) => {
|
||||
self.account_cursor = self.account_cursor.saturating_sub(1);
|
||||
self.dirty = true;
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(Key::Char('j')) => {
|
||||
if self.account_cursor + 1 < context.accounts.len() {
|
||||
self.account_cursor += 1;
|
||||
self.dirty = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(Key::Left) => {
|
||||
self.cursor.0 = self.cursor.0.saturating_sub(1);
|
||||
self.dirty = true;
|
||||
@ -181,14 +168,6 @@ impl Component for AccountsPanel {
|
||||
self.dirty = true;
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(Key::Char('\n')) => {
|
||||
context
|
||||
.replies
|
||||
.push_back(UIEvent::Action(Tab(New(Some(Box::new(
|
||||
ContactList::for_account(self.account_cursor),
|
||||
))))));
|
||||
return true;
|
||||
}
|
||||
UIEvent::MailboxUpdate(_) => {
|
||||
self.dirty = true;
|
||||
}
|
||||
@ -212,96 +191,15 @@ impl Component for AccountsPanel {
|
||||
}
|
||||
}
|
||||
|
||||
impl AccountsPanel {
|
||||
pub fn new(context: &Context) -> AccountsPanel {
|
||||
let content = CellBuffer::new(120, 40 + context.accounts.len() * 20, Cell::default());
|
||||
impl StatusPanel {
|
||||
pub fn new() -> StatusPanel {
|
||||
let content = CellBuffer::new(120, 40, Cell::default());
|
||||
|
||||
AccountsPanel {
|
||||
StatusPanel {
|
||||
cursor: (0, 0),
|
||||
account_cursor: 0,
|
||||
content,
|
||||
dirty: true,
|
||||
id: ComponentId::new_v4(),
|
||||
}
|
||||
}
|
||||
fn initialize(&mut self, context: &Context) {
|
||||
write_string_to_grid(
|
||||
"Accounts",
|
||||
&mut self.content,
|
||||
Color::Default,
|
||||
Color::Default,
|
||||
Attr::Default,
|
||||
((2, 10), (120 - 1, 10)),
|
||||
true,
|
||||
);
|
||||
|
||||
for (i, a) in context.accounts.iter().enumerate() {
|
||||
for x in 2..(120 - 1) {
|
||||
set_and_join_box(&mut self.content, (x, 12 + i * 10), HORZ_BOUNDARY);
|
||||
}
|
||||
//create_box(&mut self.content, ((2, 5 + i * 10), (120 - 1, 15 + i * 10)));
|
||||
let (x, y) = write_string_to_grid(
|
||||
a.name(),
|
||||
&mut self.content,
|
||||
Color::Default,
|
||||
Color::Default,
|
||||
Attr::Bold,
|
||||
((3, 12 + i * 10), (120 - 2, 12 + i * 10)),
|
||||
true,
|
||||
);
|
||||
write_string_to_grid(
|
||||
" ▒██▒ ",
|
||||
&mut self.content,
|
||||
Color::Byte(32),
|
||||
Color::Default,
|
||||
Attr::Default,
|
||||
((x, y), (120 - 2, 12 + i * 10)),
|
||||
true,
|
||||
);
|
||||
write_string_to_grid(
|
||||
&a.runtime_settings.account().identity,
|
||||
&mut self.content,
|
||||
Color::Default,
|
||||
Color::Default,
|
||||
Attr::Default,
|
||||
((4, y + 2), (120 - 2, y + 2)),
|
||||
true,
|
||||
);
|
||||
if i == self.account_cursor {
|
||||
for h in 1..8 {
|
||||
self.content[(2, h + y + 1)].set_ch('*');
|
||||
}
|
||||
} else {
|
||||
for h in 1..8 {
|
||||
self.content[(2, h + y + 1)].set_ch(' ');
|
||||
}
|
||||
}
|
||||
write_string_to_grid(
|
||||
&format!(
|
||||
"Messages total {}, unseen {}",
|
||||
a.collection.len(),
|
||||
a.collection
|
||||
.envelopes
|
||||
.values()
|
||||
.filter(|e| !e.is_seen())
|
||||
.count()
|
||||
),
|
||||
&mut self.content,
|
||||
Color::Default,
|
||||
Color::Default,
|
||||
Attr::Default,
|
||||
((5, y + 3), (120 - 2, y + 3)),
|
||||
true,
|
||||
);
|
||||
write_string_to_grid(
|
||||
&format!("Contacts total {}", a.address_book.len()),
|
||||
&mut self.content,
|
||||
Color::Default,
|
||||
Color::Default,
|
||||
Attr::Default,
|
||||
((5, y + 4), (120 - 2, y + 4)),
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -84,7 +84,10 @@ shortcut_key_values! { "contact-list",
|
||||
/// Shortcut listing for the contact list view
|
||||
pub struct ContactListShortcuts {
|
||||
create_contact: Key |> "Create new contact." |> Key::Char('c'),
|
||||
edit_contact: Key |> "Edit contact under cursor." |> Key::Char('e')
|
||||
edit_contact: Key |> "Edit contact under cursor." |> Key::Char('e'),
|
||||
toggle_menu_visibility: Key |> "Toggle visibility of side menu in mail list." |> Key::Char('`'),
|
||||
prev_account: Key |> "Go to previous account." |> Key::Char('l'),
|
||||
next_account: Key |> "Go to next account." |> Key::Char('h')
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user