Add collapse option for mailboxes in sidebar menu
Closes #130 Feature request: collapsible folders with total counter #130 https://git.meli.delivery/meli/meli/issues/130pull/144/head
parent
4a79b2021d
commit
b716e4383e
|
@ -407,6 +407,11 @@ Show a different name for this mailbox in the UI
|
||||||
Load this mailbox on startup
|
Load this mailbox on startup
|
||||||
.\" default value
|
.\" default value
|
||||||
.Pq Em true
|
.Pq Em true
|
||||||
|
.It Ic collapsed Ar boolean
|
||||||
|
.Pq Em optional
|
||||||
|
Collapse this mailbox subtree in menu.
|
||||||
|
.\" default value
|
||||||
|
.Pq Em false
|
||||||
.It Ic subscribe Ar boolean
|
.It Ic subscribe Ar boolean
|
||||||
.Pq Em optional
|
.Pq Em optional
|
||||||
Watch this mailbox for updates
|
Watch this mailbox for updates
|
||||||
|
@ -691,6 +696,10 @@ Go to previous mailbox.
|
||||||
Open selected mailbox
|
Open selected mailbox
|
||||||
.\" default value
|
.\" default value
|
||||||
.Pq Em Enter
|
.Pq Em Enter
|
||||||
|
.It Ic toggle_mailbox_collapse
|
||||||
|
Toggle mailbox visibility in menu.
|
||||||
|
.\" default value
|
||||||
|
.Pq Em Space
|
||||||
.It Ic search
|
.It Ic search
|
||||||
Search within list of e-mails.
|
Search within list of e-mails.
|
||||||
.\" default value
|
.\" default value
|
||||||
|
|
|
@ -24,7 +24,7 @@ use crate::conf::accounts::JobRequest;
|
||||||
use crate::types::segment_tree::SegmentTree;
|
use crate::types::segment_tree::SegmentTree;
|
||||||
use melib::backends::EnvelopeHashBatch;
|
use melib::backends::EnvelopeHashBatch;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{BTreeSet, HashMap, HashSet};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
@ -161,12 +161,23 @@ column_str!(struct SubjectString(String));
|
||||||
column_str!(struct FlagString(String));
|
column_str!(struct FlagString(String));
|
||||||
column_str!(struct TagString(String, SmallVec<[Option<Color>; 8]>));
|
column_str!(struct TagString(String, SmallVec<[Option<Color>; 8]>));
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct MailboxMenuEntry {
|
||||||
|
depth: usize,
|
||||||
|
indentation: u32,
|
||||||
|
has_sibling: bool,
|
||||||
|
visible: bool,
|
||||||
|
collapsed: bool,
|
||||||
|
mailbox_hash: MailboxHash,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct AccountMenuEntry {
|
struct AccountMenuEntry {
|
||||||
name: String,
|
name: String,
|
||||||
hash: AccountHash,
|
hash: AccountHash,
|
||||||
index: usize,
|
index: usize,
|
||||||
entries: SmallVec<[(usize, u32, bool, MailboxHash); 16]>,
|
visible: bool,
|
||||||
|
entries: SmallVec<[MailboxMenuEntry; 16]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MailListingTrait: ListingTrait {
|
pub trait MailListingTrait: ListingTrait {
|
||||||
|
@ -768,6 +779,18 @@ impl Component for Listing {
|
||||||
if self.cursor_pos.0 == account_index {
|
if self.cursor_pos.0 == account_index {
|
||||||
self.change_account(context);
|
self.change_account(context);
|
||||||
} else {
|
} else {
|
||||||
|
let previous_collapsed_mailboxes: BTreeSet<MailboxHash> = self.accounts
|
||||||
|
[account_index]
|
||||||
|
.entries
|
||||||
|
.iter()
|
||||||
|
.filter_map(|e| {
|
||||||
|
if e.collapsed {
|
||||||
|
Some(e.mailbox_hash)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<_>();
|
||||||
self.accounts[account_index].entries = context.accounts[&*account_hash]
|
self.accounts[account_index].entries = context.accounts[&*account_hash]
|
||||||
.list_mailboxes()
|
.list_mailboxes()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -776,7 +799,18 @@ impl Component for Listing {
|
||||||
.ref_mailbox
|
.ref_mailbox
|
||||||
.is_subscribed()
|
.is_subscribed()
|
||||||
})
|
})
|
||||||
.map(|f| (f.depth, f.indentation, f.has_sibling, f.hash))
|
.map(|f| MailboxMenuEntry {
|
||||||
|
depth: f.depth,
|
||||||
|
indentation: f.indentation,
|
||||||
|
has_sibling: f.has_sibling,
|
||||||
|
mailbox_hash: f.hash,
|
||||||
|
visible: true,
|
||||||
|
collapsed: if previous_collapsed_mailboxes.is_empty() {
|
||||||
|
context.accounts[&*account_hash][&f.hash].conf.collapsed
|
||||||
|
} else {
|
||||||
|
previous_collapsed_mailboxes.contains(&f.hash)
|
||||||
|
},
|
||||||
|
})
|
||||||
.collect::<_>();
|
.collect::<_>();
|
||||||
self.set_dirty(true);
|
self.set_dirty(true);
|
||||||
self.menu_content.empty();
|
self.menu_content.empty();
|
||||||
|
@ -795,6 +829,18 @@ impl Component for Listing {
|
||||||
.get_index_of(account_hash)
|
.get_index_of(account_hash)
|
||||||
.expect("Invalid account_hash in UIEventMailbox{Delete,Create}");
|
.expect("Invalid account_hash in UIEventMailbox{Delete,Create}");
|
||||||
self.menu_content.empty();
|
self.menu_content.empty();
|
||||||
|
let previous_collapsed_mailboxes: BTreeSet<MailboxHash> = self.accounts
|
||||||
|
[account_index]
|
||||||
|
.entries
|
||||||
|
.iter()
|
||||||
|
.filter_map(|e| {
|
||||||
|
if e.collapsed {
|
||||||
|
Some(e.mailbox_hash)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<_>();
|
||||||
self.accounts[account_index].entries = context.accounts[&*account_hash]
|
self.accounts[account_index].entries = context.accounts[&*account_hash]
|
||||||
.list_mailboxes()
|
.list_mailboxes()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -803,7 +849,14 @@ impl Component for Listing {
|
||||||
.ref_mailbox
|
.ref_mailbox
|
||||||
.is_subscribed()
|
.is_subscribed()
|
||||||
})
|
})
|
||||||
.map(|f| (f.depth, f.indentation, f.has_sibling, f.hash))
|
.map(|f| MailboxMenuEntry {
|
||||||
|
depth: f.depth,
|
||||||
|
indentation: f.indentation,
|
||||||
|
has_sibling: f.has_sibling,
|
||||||
|
mailbox_hash: f.hash,
|
||||||
|
visible: true,
|
||||||
|
collapsed: previous_collapsed_mailboxes.contains(&f.hash),
|
||||||
|
})
|
||||||
.collect::<_>();
|
.collect::<_>();
|
||||||
let mut fallback = 0;
|
let mut fallback = 0;
|
||||||
if let MenuEntryCursor::Mailbox(ref mut cur) = self.cursor_pos.1 {
|
if let MenuEntryCursor::Mailbox(ref mut cur) = self.cursor_pos.1 {
|
||||||
|
@ -821,7 +874,7 @@ impl Component for Listing {
|
||||||
.process_event(&mut UIEvent::VisibilityChange(false), context);
|
.process_event(&mut UIEvent::VisibilityChange(false), context);
|
||||||
self.component.set_coordinates((
|
self.component.set_coordinates((
|
||||||
self.accounts[self.cursor_pos.0].hash,
|
self.accounts[self.cursor_pos.0].hash,
|
||||||
self.accounts[self.cursor_pos.0].entries[fallback].3,
|
self.accounts[self.cursor_pos.0].entries[fallback].mailbox_hash,
|
||||||
));
|
));
|
||||||
self.component.refresh_mailbox(context, true);
|
self.component.refresh_mailbox(context, true);
|
||||||
}
|
}
|
||||||
|
@ -840,7 +893,7 @@ impl Component for Listing {
|
||||||
self.set_dirty(true);
|
self.set_dirty(true);
|
||||||
}
|
}
|
||||||
UIEvent::Action(Action::ViewMailbox(ref idx)) => {
|
UIEvent::Action(Action::ViewMailbox(ref idx)) => {
|
||||||
if let Some((_, _, _, mailbox_hash)) =
|
if let Some(MailboxMenuEntry { mailbox_hash, .. }) =
|
||||||
self.accounts[self.cursor_pos.0].entries.get(*idx)
|
self.accounts[self.cursor_pos.0].entries.get(*idx)
|
||||||
{
|
{
|
||||||
let account_hash = self.accounts[self.cursor_pos.0].hash;
|
let account_hash = self.accounts[self.cursor_pos.0].hash;
|
||||||
|
@ -1311,6 +1364,33 @@ impl Component for Listing {
|
||||||
)));
|
)));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
UIEvent::Input(ref k)
|
||||||
|
if shortcut!(
|
||||||
|
k == shortcuts[Listing::DESCRIPTION]["toggle_mailbox_collapse"]
|
||||||
|
) && matches!(self.menu_cursor_pos.1, MenuEntryCursor::Mailbox(_)) =>
|
||||||
|
{
|
||||||
|
let target_mailbox_idx =
|
||||||
|
if let MenuEntryCursor::Mailbox(idx) = self.menu_cursor_pos.1 {
|
||||||
|
idx
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if let Some(target) = self.accounts[self.menu_cursor_pos.0]
|
||||||
|
.entries
|
||||||
|
.get_mut(target_mailbox_idx)
|
||||||
|
{
|
||||||
|
target.collapsed = !(target.collapsed);
|
||||||
|
self.dirty = true;
|
||||||
|
self.menu_content.empty();
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate(
|
||||||
|
ScrollUpdate::End(self.id),
|
||||||
|
)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
UIEvent::Input(ref k)
|
UIEvent::Input(ref k)
|
||||||
if shortcut!(k == shortcuts[Listing::DESCRIPTION]["open_mailbox"]) =>
|
if shortcut!(k == shortcuts[Listing::DESCRIPTION]["open_mailbox"]) =>
|
||||||
{
|
{
|
||||||
|
@ -1371,13 +1451,22 @@ impl Component for Listing {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(_, MenuEntryCursor::Mailbox(ref mut mailbox_idx)) => {
|
(
|
||||||
|
ref account_cursor,
|
||||||
|
MenuEntryCursor::Mailbox(ref mut mailbox_idx),
|
||||||
|
) => loop {
|
||||||
if *mailbox_idx > 0 {
|
if *mailbox_idx > 0 {
|
||||||
*mailbox_idx -= 1;
|
*mailbox_idx -= 1;
|
||||||
|
if self.accounts[*account_cursor].entries[*mailbox_idx]
|
||||||
|
.visible
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.menu_cursor_pos.1 = MenuEntryCursor::Status;
|
self.menu_cursor_pos.1 = MenuEntryCursor::Status;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
amount -= 1;
|
amount -= 1;
|
||||||
|
@ -1407,18 +1496,24 @@ impl Component for Listing {
|
||||||
(
|
(
|
||||||
ref mut account_cursor,
|
ref mut account_cursor,
|
||||||
MenuEntryCursor::Mailbox(ref mut mailbox_idx),
|
MenuEntryCursor::Mailbox(ref mut mailbox_idx),
|
||||||
) => {
|
) => loop {
|
||||||
if (*mailbox_idx + 1)
|
if (*mailbox_idx + 1)
|
||||||
< self.accounts[*account_cursor].entries.len()
|
< self.accounts[*account_cursor].entries.len()
|
||||||
{
|
{
|
||||||
*mailbox_idx += 1;
|
*mailbox_idx += 1;
|
||||||
|
if self.accounts[*account_cursor].entries[*mailbox_idx]
|
||||||
|
.visible
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
} else if *account_cursor + 1 < self.accounts.len() {
|
} else if *account_cursor + 1 < self.accounts.len() {
|
||||||
*account_cursor += 1;
|
*account_cursor += 1;
|
||||||
self.menu_cursor_pos.1 = MenuEntryCursor::Status;
|
self.menu_cursor_pos.1 = MenuEntryCursor::Status;
|
||||||
|
break;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
amount -= 1;
|
amount -= 1;
|
||||||
|
@ -1648,7 +1743,7 @@ impl Component for Listing {
|
||||||
fn get_status(&self, context: &Context) -> String {
|
fn get_status(&self, context: &Context) -> String {
|
||||||
let mailbox_hash = match self.cursor_pos.1 {
|
let mailbox_hash = match self.cursor_pos.1 {
|
||||||
MenuEntryCursor::Mailbox(idx) => {
|
MenuEntryCursor::Mailbox(idx) => {
|
||||||
if let Some((_, _, _, mailbox_hash)) =
|
if let Some(MailboxMenuEntry { mailbox_hash, .. }) =
|
||||||
self.accounts[self.cursor_pos.0].entries.get(idx)
|
self.accounts[self.cursor_pos.0].entries.get(idx)
|
||||||
{
|
{
|
||||||
*mailbox_hash
|
*mailbox_hash
|
||||||
|
@ -1695,17 +1790,25 @@ impl Listing {
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, (h, a))| {
|
.map(|(i, (h, a))| {
|
||||||
let entries: SmallVec<[(usize, u32, bool, MailboxHash); 16]> = a
|
let entries: SmallVec<[MailboxMenuEntry; 16]> = a
|
||||||
.list_mailboxes()
|
.list_mailboxes()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|mailbox_node| a[&mailbox_node.hash].ref_mailbox.is_subscribed())
|
.filter(|mailbox_node| a[&mailbox_node.hash].ref_mailbox.is_subscribed())
|
||||||
.map(|f| (f.depth, f.indentation, f.has_sibling, f.hash))
|
.map(|f| MailboxMenuEntry {
|
||||||
|
depth: f.depth,
|
||||||
|
indentation: f.indentation,
|
||||||
|
has_sibling: f.has_sibling,
|
||||||
|
mailbox_hash: f.hash,
|
||||||
|
visible: true,
|
||||||
|
collapsed: a[&f.hash].conf.collapsed,
|
||||||
|
})
|
||||||
.collect::<_>();
|
.collect::<_>();
|
||||||
|
|
||||||
AccountMenuEntry {
|
AccountMenuEntry {
|
||||||
name: a.name().to_string(),
|
name: a.name().to_string(),
|
||||||
hash: *h,
|
hash: *h,
|
||||||
index: i,
|
index: i,
|
||||||
|
visible: true,
|
||||||
entries,
|
entries,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1848,6 +1951,19 @@ impl Listing {
|
||||||
*/
|
*/
|
||||||
fn print_account(&mut self, area: Area, aidx: usize, context: &mut Context) -> usize {
|
fn print_account(&mut self, area: Area, aidx: usize, context: &mut Context) -> usize {
|
||||||
debug_assert!(is_valid_area!(area));
|
debug_assert!(is_valid_area!(area));
|
||||||
|
|
||||||
|
#[derive(Copy, Debug, Clone)]
|
||||||
|
struct Line {
|
||||||
|
visible: bool,
|
||||||
|
collapsed: bool,
|
||||||
|
depth: usize,
|
||||||
|
inc: usize,
|
||||||
|
indentation: u32,
|
||||||
|
has_sibling: bool,
|
||||||
|
mailbox_idx: MailboxHash,
|
||||||
|
count: Option<usize>,
|
||||||
|
collapsed_count: Option<usize>,
|
||||||
|
}
|
||||||
// Each entry and its index in the account
|
// Each entry and its index in the account
|
||||||
let mailboxes: HashMap<MailboxHash, Mailbox> = context.accounts[self.accounts[aidx].index]
|
let mailboxes: HashMap<MailboxHash, Mailbox> = context.accounts[self.accounts[aidx].index]
|
||||||
.mailbox_entries
|
.mailbox_entries
|
||||||
|
@ -1865,25 +1981,47 @@ impl Listing {
|
||||||
|
|
||||||
let must_highlight_account: bool = cursor.0 == self.accounts[aidx].index;
|
let must_highlight_account: bool = cursor.0 == self.accounts[aidx].index;
|
||||||
|
|
||||||
let mut lines: Vec<(usize, usize, u32, bool, MailboxHash, Option<usize>)> = Vec::new();
|
let mut lines: Vec<Line> = Vec::new();
|
||||||
|
|
||||||
for (i, &(depth, indentation, has_sibling, mailbox_hash)) in
|
for (
|
||||||
self.accounts[aidx].entries.iter().enumerate()
|
i,
|
||||||
|
&MailboxMenuEntry {
|
||||||
|
depth,
|
||||||
|
indentation,
|
||||||
|
has_sibling,
|
||||||
|
mailbox_hash,
|
||||||
|
visible,
|
||||||
|
collapsed,
|
||||||
|
},
|
||||||
|
) in self.accounts[aidx].entries.iter().enumerate()
|
||||||
{
|
{
|
||||||
if mailboxes[&mailbox_hash].is_subscribed() {
|
if mailboxes[&mailbox_hash].is_subscribed() {
|
||||||
match context.accounts[self.accounts[aidx].index][&mailbox_hash].status {
|
match context.accounts[self.accounts[aidx].index][&mailbox_hash].status {
|
||||||
crate::conf::accounts::MailboxStatus::Failed(_) => {
|
crate::conf::accounts::MailboxStatus::Failed(_) => {
|
||||||
lines.push((depth, i, indentation, has_sibling, mailbox_hash, None));
|
lines.push(Line {
|
||||||
}
|
visible,
|
||||||
_ => {
|
collapsed,
|
||||||
lines.push((
|
|
||||||
depth,
|
depth,
|
||||||
i,
|
inc: i,
|
||||||
indentation,
|
indentation,
|
||||||
has_sibling,
|
has_sibling,
|
||||||
mailbox_hash,
|
mailbox_idx: mailbox_hash,
|
||||||
mailboxes[&mailbox_hash].count().ok().map(|(v, _)| v),
|
count: None,
|
||||||
));
|
collapsed_count: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
lines.push(Line {
|
||||||
|
visible,
|
||||||
|
collapsed,
|
||||||
|
depth,
|
||||||
|
inc: i,
|
||||||
|
indentation,
|
||||||
|
has_sibling,
|
||||||
|
mailbox_idx: mailbox_hash,
|
||||||
|
count: mailboxes[&mailbox_hash].count().ok().map(|(v, _)| v),
|
||||||
|
collapsed_count: None,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1931,10 +2069,44 @@ impl Listing {
|
||||||
let mut idx = 0;
|
let mut idx = 0;
|
||||||
let mut branches = String::with_capacity(16);
|
let mut branches = String::with_capacity(16);
|
||||||
|
|
||||||
for y in get_y(upper_left) + 1..get_y(bottom_right) {
|
// What depth to skip if a mailbox is toggled to collapse
|
||||||
|
// The value should be the collapsed mailbox's indentation, so that its children are not
|
||||||
|
// visible.
|
||||||
|
let mut skip: Option<usize> = None;
|
||||||
|
let mut skipped_counter: usize = 0;
|
||||||
|
'grid_loop: for y in get_y(upper_left) + 1..get_y(bottom_right) {
|
||||||
if idx == lines_len {
|
if idx == lines_len {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
let mut l = lines[idx];
|
||||||
|
while let Some(p) = skip {
|
||||||
|
if l.depth > p {
|
||||||
|
self.accounts[aidx].entries[idx].visible = false;
|
||||||
|
idx += 1;
|
||||||
|
skipped_counter += 1;
|
||||||
|
if idx >= lines.len() {
|
||||||
|
break 'grid_loop;
|
||||||
|
}
|
||||||
|
l = lines[idx];
|
||||||
|
} else {
|
||||||
|
skip = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.accounts[aidx].entries[idx].visible = true;
|
||||||
|
if l.collapsed {
|
||||||
|
skip = Some(l.depth);
|
||||||
|
// Calculate total unseen from hidden children mailboxes
|
||||||
|
let mut idx = idx + 1;
|
||||||
|
let mut counter = 0;
|
||||||
|
while idx < lines.len() {
|
||||||
|
if lines[idx].depth <= l.depth {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
counter += lines[idx].count.unwrap_or(0);
|
||||||
|
idx += 1;
|
||||||
|
}
|
||||||
|
l.collapsed_count = Some(counter);
|
||||||
|
}
|
||||||
let (att, index_att, unread_count_att) = if must_highlight_account {
|
let (att, index_att, unread_count_att) = if must_highlight_account {
|
||||||
if match cursor.1 {
|
if match cursor.1 {
|
||||||
MenuEntryCursor::Mailbox(c) => c == idx,
|
MenuEntryCursor::Mailbox(c) => c == idx,
|
||||||
|
@ -1970,7 +2142,6 @@ impl Listing {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let (depth, inc, indentation, has_sibling, mailbox_idx, count) = lines[idx];
|
|
||||||
/* Calculate how many columns the mailbox index tags should occupy with right alignment,
|
/* Calculate how many columns the mailbox index tags should occupy with right alignment,
|
||||||
* eg.
|
* eg.
|
||||||
* 1
|
* 1
|
||||||
|
@ -2025,7 +2196,7 @@ impl Listing {
|
||||||
.unwrap_or(" ");
|
.unwrap_or(" ");
|
||||||
|
|
||||||
let (x, _) = write_string_to_grid(
|
let (x, _) = write_string_to_grid(
|
||||||
&format!("{:>width$}", inc, width = total_mailbox_no_digits),
|
&format!("{:>width$}", l.inc, width = total_mailbox_no_digits),
|
||||||
&mut self.menu_content,
|
&mut self.menu_content,
|
||||||
index_att.fg,
|
index_att.fg,
|
||||||
index_att.bg,
|
index_att.bg,
|
||||||
|
@ -2036,18 +2207,18 @@ impl Listing {
|
||||||
{
|
{
|
||||||
branches.clear();
|
branches.clear();
|
||||||
branches.push_str(no_sibling_str);
|
branches.push_str(no_sibling_str);
|
||||||
let leading_zeros = indentation.leading_zeros();
|
let leading_zeros = l.indentation.leading_zeros();
|
||||||
let mut o = 1_u32.wrapping_shl(31_u32.saturating_sub(leading_zeros));
|
let mut o = 1_u32.wrapping_shl(31_u32.saturating_sub(leading_zeros));
|
||||||
for _ in 0..(32_u32.saturating_sub(leading_zeros)) {
|
for _ in 0..(32_u32.saturating_sub(leading_zeros)) {
|
||||||
if indentation & o > 0 {
|
if l.indentation & o > 0 {
|
||||||
branches.push_str(has_sibling_str);
|
branches.push_str(has_sibling_str);
|
||||||
} else {
|
} else {
|
||||||
branches.push_str(no_sibling_str);
|
branches.push_str(no_sibling_str);
|
||||||
}
|
}
|
||||||
o >>= 1;
|
o >>= 1;
|
||||||
}
|
}
|
||||||
if depth > 0 {
|
if l.depth > 0 {
|
||||||
if has_sibling {
|
if l.has_sibling {
|
||||||
branches.push_str(has_sibling_leaf_str);
|
branches.push_str(has_sibling_leaf_str);
|
||||||
} else {
|
} else {
|
||||||
branches.push_str(no_sibling_leaf_str);
|
branches.push_str(no_sibling_leaf_str);
|
||||||
|
@ -2064,7 +2235,7 @@ impl Listing {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
let (x, _) = write_string_to_grid(
|
let (x, _) = write_string_to_grid(
|
||||||
context.accounts[self.accounts[aidx].index].mailbox_entries[&mailbox_idx].name(),
|
context.accounts[self.accounts[aidx].index].mailbox_entries[&l.mailbox_idx].name(),
|
||||||
&mut self.menu_content,
|
&mut self.menu_content,
|
||||||
att.fg,
|
att.fg,
|
||||||
att.bg,
|
att.bg,
|
||||||
|
@ -2074,14 +2245,15 @@ impl Listing {
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Unread message count */
|
/* Unread message count */
|
||||||
let count_string = if let Some(c) = count {
|
let count_string = match (l.count, l.collapsed_count) {
|
||||||
if c > 0 {
|
(None, None) => " ...".to_string(),
|
||||||
format!(" {}", c)
|
(Some(0), None) => String::new(),
|
||||||
} else {
|
(Some(0), Some(0)) | (None, Some(0)) => " v".to_string(),
|
||||||
String::new()
|
(Some(0), Some(coll)) => format!(" ({}) v", coll),
|
||||||
}
|
(Some(c), Some(0)) => format!(" {} v", c),
|
||||||
} else {
|
(Some(c), Some(coll)) => format!(" {} ({}) v", c, coll),
|
||||||
" ...".to_string()
|
(Some(c), None) => format!(" {}", c),
|
||||||
|
(None, Some(coll)) => format!(" ({}) v", coll),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (x, _) = write_string_to_grid(
|
let (x, _) = write_string_to_grid(
|
||||||
|
@ -2090,7 +2262,7 @@ impl Listing {
|
||||||
unread_count_att.fg,
|
unread_count_att.fg,
|
||||||
unread_count_att.bg,
|
unread_count_att.bg,
|
||||||
unread_count_att.attrs
|
unread_count_att.attrs
|
||||||
| if count.unwrap_or(0) > 0 {
|
| if l.count.unwrap_or(0) > 0 {
|
||||||
Attr::BOLD
|
Attr::BOLD
|
||||||
} else {
|
} else {
|
||||||
Attr::DEFAULT
|
Attr::DEFAULT
|
||||||
|
@ -2116,12 +2288,23 @@ impl Listing {
|
||||||
if idx == 0 {
|
if idx == 0 {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
idx - 1
|
idx - 1 - skipped_counter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn change_account(&mut self, context: &mut Context) {
|
fn change_account(&mut self, context: &mut Context) {
|
||||||
let account_hash = context.accounts[self.cursor_pos.0].hash();
|
let account_hash = context.accounts[self.cursor_pos.0].hash();
|
||||||
|
let previous_collapsed_mailboxes: BTreeSet<MailboxHash> = self.accounts[self.cursor_pos.0]
|
||||||
|
.entries
|
||||||
|
.iter()
|
||||||
|
.filter_map(|e| {
|
||||||
|
if e.collapsed {
|
||||||
|
Some(e.mailbox_hash)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<_>();
|
||||||
self.accounts[self.cursor_pos.0].entries = context.accounts[self.cursor_pos.0]
|
self.accounts[self.cursor_pos.0].entries = context.accounts[self.cursor_pos.0]
|
||||||
.list_mailboxes()
|
.list_mailboxes()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -2130,12 +2313,23 @@ impl Listing {
|
||||||
.ref_mailbox
|
.ref_mailbox
|
||||||
.is_subscribed()
|
.is_subscribed()
|
||||||
})
|
})
|
||||||
.map(|f| (f.depth, f.indentation, f.has_sibling, f.hash))
|
.map(|f| MailboxMenuEntry {
|
||||||
|
depth: f.depth,
|
||||||
|
indentation: f.indentation,
|
||||||
|
has_sibling: f.has_sibling,
|
||||||
|
mailbox_hash: f.hash,
|
||||||
|
visible: true,
|
||||||
|
collapsed: if previous_collapsed_mailboxes.is_empty() {
|
||||||
|
context.accounts[self.cursor_pos.0][&f.hash].conf.collapsed
|
||||||
|
} else {
|
||||||
|
previous_collapsed_mailboxes.contains(&f.hash)
|
||||||
|
},
|
||||||
|
})
|
||||||
.collect::<_>();
|
.collect::<_>();
|
||||||
match self.cursor_pos.1 {
|
match self.cursor_pos.1 {
|
||||||
MenuEntryCursor::Mailbox(idx) => {
|
MenuEntryCursor::Mailbox(idx) => {
|
||||||
/* Account might have no mailboxes yet if it's offline */
|
/* Account might have no mailboxes yet if it's offline */
|
||||||
if let Some((_, _, _, mailbox_hash)) =
|
if let Some(MailboxMenuEntry { mailbox_hash, .. }) =
|
||||||
self.accounts[self.cursor_pos.0].entries.get(idx)
|
self.accounts[self.cursor_pos.0].entries.get(idx)
|
||||||
{
|
{
|
||||||
self.component
|
self.component
|
||||||
|
|
|
@ -135,6 +135,8 @@ pub struct MailUIConf {
|
||||||
pub struct FileMailboxConf {
|
pub struct FileMailboxConf {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub conf_override: MailUIConf,
|
pub conf_override: MailUIConf,
|
||||||
|
#[serde(default = "false_val")]
|
||||||
|
pub collapsed: bool,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub mailbox_conf: MailboxConf,
|
pub mailbox_conf: MailboxConf,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2389,6 +2389,6 @@ fn build_mailboxes_order(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rec(node, &mailbox_entries, 0, 0, false);
|
rec(node, &mailbox_entries, 1, 0, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,6 +176,7 @@ shortcut_key_values! { "listing",
|
||||||
prev_account |> "Go to previous account." |> Key::Char('l'),
|
prev_account |> "Go to previous account." |> Key::Char('l'),
|
||||||
prev_mailbox |> "Go to previous mailbox." |> Key::Char('K'),
|
prev_mailbox |> "Go to previous mailbox." |> Key::Char('K'),
|
||||||
open_mailbox |> "Open selected mailbox" |> Key::Char('\n'),
|
open_mailbox |> "Open selected mailbox" |> Key::Char('\n'),
|
||||||
|
toggle_mailbox_collapse |> "Toggle mailbox collapse in menu." |> Key::Char(' '),
|
||||||
prev_page |> "Go to previous page." |> Key::PageUp,
|
prev_page |> "Go to previous page." |> Key::PageUp,
|
||||||
search |> "Search within list of e-mails." |> Key::Char('/'),
|
search |> "Search within list of e-mails." |> Key::Char('/'),
|
||||||
refresh |> "Manually request a mailbox refresh." |> Key::F(5),
|
refresh |> "Manually request a mailbox refresh." |> Key::F(5),
|
||||||
|
|
Loading…
Reference in New Issue