Upgrade MailboxManager component to new TUI API
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>pull/312/head
parent
5dd71ef1cd
commit
b5cc2a095f
|
@ -85,8 +85,8 @@ pub use crate::mail::*;
|
|||
|
||||
pub mod notifications;
|
||||
|
||||
//pub mod mailbox_management;
|
||||
//pub use mailbox_management::*;
|
||||
pub mod mailbox_management;
|
||||
pub use mailbox_management::*;
|
||||
|
||||
pub mod jobs_view;
|
||||
pub use jobs_view::*;
|
||||
|
|
|
@ -2294,11 +2294,11 @@ impl Component for Listing {
|
|||
return true;
|
||||
}
|
||||
UIEvent::Action(Action::Tab(ManageMailboxes)) => {
|
||||
//let account_pos = self.cursor_pos.account;
|
||||
//let mgr = MailboxManager::new(context, account_pos);
|
||||
//context
|
||||
// .replies
|
||||
// .push_back(UIEvent::Action(Tab(New(Some(Box::new(mgr))))));
|
||||
let account_pos = self.cursor_pos.account;
|
||||
let mgr = MailboxManager::new(context, account_pos);
|
||||
context
|
||||
.replies
|
||||
.push_back(UIEvent::Action(Tab(New(Some(Box::new(mgr))))));
|
||||
return true;
|
||||
}
|
||||
UIEvent::Action(Action::Tab(ManageJobs)) => {
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
use std::cmp;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use melib::backends::AccountHash;
|
||||
use melib::{backends::AccountHash, SortOrder};
|
||||
|
||||
use super::*;
|
||||
use crate::{accounts::MailboxEntry, melib::text_processing::TextProcessing};
|
||||
|
@ -41,6 +41,23 @@ enum ViewMode {
|
|||
Action(UIDialog<MailboxAction>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[repr(u8)]
|
||||
enum Column {
|
||||
_0 = 0,
|
||||
_1,
|
||||
_2,
|
||||
_3,
|
||||
}
|
||||
|
||||
const fn _assert_len() {
|
||||
if MailboxManager::HEADERS.len() != Column::_3 as usize + 1 {
|
||||
panic!("MailboxManager::HEADERS length changed, please update Column enum accordingly.");
|
||||
}
|
||||
}
|
||||
|
||||
const _: () = _assert_len();
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MailboxManager {
|
||||
cursor_pos: usize,
|
||||
|
@ -48,7 +65,10 @@ pub struct MailboxManager {
|
|||
account_pos: usize,
|
||||
account_hash: AccountHash,
|
||||
length: usize,
|
||||
data_columns: DataColumns<5>,
|
||||
data_columns: DataColumns<4>,
|
||||
min_width: [usize; 4],
|
||||
sort_col: Column,
|
||||
sort_order: SortOrder,
|
||||
entries: IndexMap<MailboxHash, MailboxEntry>,
|
||||
mode: ViewMode,
|
||||
|
||||
|
@ -69,6 +89,8 @@ impl std::fmt::Display for MailboxManager {
|
|||
}
|
||||
|
||||
impl MailboxManager {
|
||||
const HEADERS: [&'static str; 4] = ["name", "path", "size", "subscribed"];
|
||||
|
||||
pub fn new(context: &Context, account_pos: usize) -> Self {
|
||||
let account_hash = context.accounts[account_pos].hash();
|
||||
let theme_default = crate::conf::value(context, "theme_default");
|
||||
|
@ -83,6 +105,9 @@ impl MailboxManager {
|
|||
length: 0,
|
||||
account_pos,
|
||||
data_columns,
|
||||
sort_col: Column::_1,
|
||||
sort_order: SortOrder::Asc,
|
||||
min_width: [0; 4],
|
||||
theme_default,
|
||||
highlight_theme: crate::conf::value(context, "highlight"),
|
||||
initialized: false,
|
||||
|
@ -95,113 +120,157 @@ impl MailboxManager {
|
|||
fn initialize(&mut self, context: &mut Context) {
|
||||
let account = &context.accounts[self.account_pos];
|
||||
self.length = account.mailbox_entries.len();
|
||||
self.entries = account.mailbox_entries.clone();
|
||||
self.entries
|
||||
.sort_by(|_, a, _, b| a.ref_mailbox.path().cmp(b.ref_mailbox.path()));
|
||||
|
||||
self.set_dirty(true);
|
||||
let mut min_width = (
|
||||
"name".len(),
|
||||
"path".len(),
|
||||
"size".len(),
|
||||
"subscribed".len(),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
|
||||
for c in self.entries.values() {
|
||||
/* title */
|
||||
min_width.0 = cmp::max(min_width.0, c.name().split_graphemes().len());
|
||||
/* path */
|
||||
min_width.1 = cmp::max(min_width.1, c.ref_mailbox.path().len());
|
||||
let mut entries = account.mailbox_entries.clone();
|
||||
entries.sort_by(|_, a, _, b| match (self.sort_col, self.sort_order) {
|
||||
(Column::_0, SortOrder::Asc) => a.ref_mailbox.name().cmp(b.ref_mailbox.name()),
|
||||
(Column::_0, SortOrder::Desc) => b.ref_mailbox.name().cmp(a.ref_mailbox.name()),
|
||||
(Column::_1, SortOrder::Asc) => a.ref_mailbox.path().cmp(b.ref_mailbox.path()),
|
||||
(Column::_1, SortOrder::Desc) => b.ref_mailbox.path().cmp(a.ref_mailbox.path()),
|
||||
(Column::_2, SortOrder::Asc) => {
|
||||
let (_, a) = a.ref_mailbox.count().ok().unwrap_or((0, 0));
|
||||
let (_, b) = b.ref_mailbox.count().ok().unwrap_or((0, 0));
|
||||
a.cmp(&b)
|
||||
}
|
||||
(Column::_2, SortOrder::Desc) => {
|
||||
let (_, a) = a.ref_mailbox.count().ok().unwrap_or((0, 0));
|
||||
let (_, b) = b.ref_mailbox.count().ok().unwrap_or((0, 0));
|
||||
b.cmp(&a)
|
||||
}
|
||||
(Column::_3, SortOrder::Asc)
|
||||
if a.ref_mailbox.is_subscribed() && b.ref_mailbox.is_subscribed() =>
|
||||
{
|
||||
std::cmp::Ordering::Equal
|
||||
}
|
||||
(Column::_3, SortOrder::Asc) if a.ref_mailbox.is_subscribed() => {
|
||||
std::cmp::Ordering::Greater
|
||||
}
|
||||
(Column::_3, SortOrder::Desc) if a.ref_mailbox.is_subscribed() => {
|
||||
std::cmp::Ordering::Less
|
||||
}
|
||||
(Column::_3, SortOrder::Asc) => std::cmp::Ordering::Less,
|
||||
(Column::_3, SortOrder::Desc) => std::cmp::Ordering::Greater,
|
||||
});
|
||||
self.entries = entries;
|
||||
macro_rules! hdr {
|
||||
($idx:literal) => {{
|
||||
Self::HEADERS[$idx].len() + if self.sort_col as u8 == $idx { 1 } else { 0 }
|
||||
}};
|
||||
}
|
||||
|
||||
/* name column */
|
||||
self.data_columns.columns[0] =
|
||||
CellBuffer::new_with_context(min_width.0, self.length, None, context);
|
||||
/* path column */
|
||||
self.data_columns.columns[1] =
|
||||
CellBuffer::new_with_context(min_width.1, self.length, None, context);
|
||||
/* size column */
|
||||
self.data_columns.columns[2] =
|
||||
CellBuffer::new_with_context(min_width.2, self.length, None, context);
|
||||
/* subscribed column */
|
||||
self.data_columns.columns[3] =
|
||||
CellBuffer::new_with_context(min_width.3, self.length, None, context);
|
||||
self.set_dirty(true);
|
||||
let mut min_width = [hdr!(0), hdr!(1), hdr!(2), hdr!(3)];
|
||||
|
||||
for c in self.entries.values() {
|
||||
// title
|
||||
min_width[0] = cmp::max(min_width[0], c.name().split_graphemes().len());
|
||||
// path
|
||||
min_width[1] = cmp::max(min_width[1], c.ref_mailbox.path().len());
|
||||
}
|
||||
|
||||
// name column
|
||||
_ = self.data_columns.columns[0].resize_with_context(min_width[0], self.length, context);
|
||||
self.data_columns.columns[0].grid_mut().clear(None);
|
||||
// path column
|
||||
_ = self.data_columns.columns[1].resize_with_context(min_width[1], self.length, context);
|
||||
self.data_columns.columns[1].grid_mut().clear(None);
|
||||
// size column
|
||||
_ = self.data_columns.columns[2].resize_with_context(min_width[2], self.length, context);
|
||||
self.data_columns.columns[2].grid_mut().clear(None);
|
||||
// subscribed column
|
||||
_ = self.data_columns.columns[3].resize_with_context(min_width[3], self.length, context);
|
||||
self.data_columns.columns[3].grid_mut().clear(None);
|
||||
|
||||
for (idx, e) in self.entries.values().enumerate() {
|
||||
self.data_columns.columns[0].write_string(
|
||||
e.name(),
|
||||
self.theme_default.fg,
|
||||
self.theme_default.bg,
|
||||
self.theme_default.attrs,
|
||||
((0, idx), (min_width.0, idx)),
|
||||
None,
|
||||
);
|
||||
{
|
||||
let area = self.data_columns.columns[0].area().nth_row(idx);
|
||||
self.data_columns.columns[0].grid_mut().write_string(
|
||||
e.name(),
|
||||
self.theme_default.fg,
|
||||
self.theme_default.bg,
|
||||
self.theme_default.attrs,
|
||||
area,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
self.data_columns.columns[1].write_string(
|
||||
e.ref_mailbox.path(),
|
||||
self.theme_default.fg,
|
||||
self.theme_default.bg,
|
||||
self.theme_default.attrs,
|
||||
((0, idx), (min_width.1, idx)),
|
||||
None,
|
||||
);
|
||||
{
|
||||
let area = self.data_columns.columns[1].area().nth_row(idx);
|
||||
self.data_columns.columns[1].grid_mut().write_string(
|
||||
e.ref_mailbox.path(),
|
||||
self.theme_default.fg,
|
||||
self.theme_default.bg,
|
||||
self.theme_default.attrs,
|
||||
area,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
let (_unseen, total) = e.ref_mailbox.count().ok().unwrap_or((0, 0));
|
||||
self.data_columns.columns[2].write_string(
|
||||
&total.to_string(),
|
||||
self.theme_default.fg,
|
||||
self.theme_default.bg,
|
||||
self.theme_default.attrs,
|
||||
((0, idx), (min_width.2, idx)),
|
||||
None,
|
||||
);
|
||||
{
|
||||
let area = self.data_columns.columns[2].area().nth_row(idx);
|
||||
let (_unseen, total) = e.ref_mailbox.count().ok().unwrap_or((0, 0));
|
||||
self.data_columns.columns[2].grid_mut().write_string(
|
||||
&total.to_string(),
|
||||
self.theme_default.fg,
|
||||
self.theme_default.bg,
|
||||
self.theme_default.attrs,
|
||||
area,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
self.data_columns.columns[3].write_string(
|
||||
if e.ref_mailbox.is_subscribed() {
|
||||
"yes"
|
||||
} else {
|
||||
"no"
|
||||
},
|
||||
self.theme_default.fg,
|
||||
self.theme_default.bg,
|
||||
self.theme_default.attrs,
|
||||
((0, idx), (min_width.3, idx)),
|
||||
None,
|
||||
);
|
||||
{
|
||||
let area = self.data_columns.columns[3].area().nth_row(idx);
|
||||
self.data_columns.columns[3].grid_mut().write_string(
|
||||
if e.ref_mailbox.is_subscribed() {
|
||||
"yes"
|
||||
} else {
|
||||
"no"
|
||||
},
|
||||
self.theme_default.fg,
|
||||
self.theme_default.bg,
|
||||
self.theme_default.attrs,
|
||||
area,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if self.length == 0 {
|
||||
let message = "No mailboxes.".to_string();
|
||||
self.data_columns.columns[0] =
|
||||
CellBuffer::new_with_context(message.len(), self.length, None, context);
|
||||
self.data_columns.columns[0].write_string(
|
||||
&message,
|
||||
self.theme_default.fg,
|
||||
self.theme_default.bg,
|
||||
self.theme_default.attrs,
|
||||
((0, 0), (message.len() - 1, 0)),
|
||||
None,
|
||||
);
|
||||
if self.data_columns.columns[0].resize_with_context(message.len(), self.length, context)
|
||||
{
|
||||
let area = self.data_columns.columns[0].area();
|
||||
self.data_columns.columns[0].grid_mut().write_string(
|
||||
&message,
|
||||
self.theme_default.fg,
|
||||
self.theme_default.bg,
|
||||
self.theme_default.attrs,
|
||||
area,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.min_width = min_width;
|
||||
}
|
||||
|
||||
fn draw_list(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
let (upper_left, bottom_right) = area;
|
||||
let rows = area.height();
|
||||
if rows < 2 {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.length == 0 {
|
||||
grid.clear_area(area, self.theme_default);
|
||||
|
||||
grid.copy_area(
|
||||
&self.data_columns.columns[0],
|
||||
self.data_columns.columns[0].grid(),
|
||||
area,
|
||||
((0, 0), pos_dec(self.data_columns.columns[0].size(), (1, 1))),
|
||||
self.data_columns.columns[0].area(),
|
||||
);
|
||||
context.dirty_areas.push_back(area);
|
||||
return;
|
||||
}
|
||||
let rows = get_y(bottom_right) - get_y(upper_left) + 1;
|
||||
|
||||
if let Some(mvm) = self.movement.take() {
|
||||
match mvm {
|
||||
|
@ -228,7 +297,20 @@ impl MailboxManager {
|
|||
self.new_cursor_pos = (self.length / rows) * rows;
|
||||
}
|
||||
}
|
||||
PageMovement::Right(_) | PageMovement::Left(_) => {}
|
||||
PageMovement::Right(amount) => {
|
||||
self.data_columns.x_offset += amount;
|
||||
self.data_columns.x_offset = self.data_columns.x_offset.min(
|
||||
self.data_columns
|
||||
.widths
|
||||
.iter()
|
||||
.map(|w| w + 2)
|
||||
.sum::<usize>()
|
||||
.saturating_sub(2),
|
||||
);
|
||||
}
|
||||
PageMovement::Left(amount) => {
|
||||
self.data_columns.x_offset = self.data_columns.x_offset.saturating_sub(amount);
|
||||
}
|
||||
PageMovement::Home => {
|
||||
self.new_cursor_pos = 0;
|
||||
}
|
||||
|
@ -264,8 +346,8 @@ impl MailboxManager {
|
|||
)));
|
||||
}
|
||||
|
||||
/* If cursor position has changed, remove the highlight from the previous
|
||||
* position and apply it in the new one. */
|
||||
// If cursor position has changed, remove the highlight from the previous
|
||||
// position and apply it in the new one.
|
||||
if self.cursor_pos != self.new_cursor_pos && prev_page_no == page_no {
|
||||
let old_cursor_pos = self.cursor_pos;
|
||||
self.cursor_pos = self.new_cursor_pos;
|
||||
|
@ -292,27 +374,21 @@ impl MailboxManager {
|
|||
self.new_cursor_pos = self.length - 1;
|
||||
self.cursor_pos = self.new_cursor_pos;
|
||||
}
|
||||
/* Page_no has changed, so draw new page */
|
||||
// Page_no has changed, so draw new page
|
||||
_ = self
|
||||
.data_columns
|
||||
.recalc_widths((area.width(), area.height()), top_idx);
|
||||
grid.clear_area(area, self.theme_default);
|
||||
/* copy table columns */
|
||||
// copy table columns
|
||||
self.data_columns
|
||||
.draw(grid, top_idx, self.cursor_pos, grid.bounds_iter(area));
|
||||
|
||||
/* highlight cursor */
|
||||
// highlight cursor
|
||||
grid.change_theme(area.nth_row(self.cursor_pos % rows), self.highlight_theme);
|
||||
|
||||
/* clear gap if available height is more than count of entries */
|
||||
// clear gap if available height is more than count of entries
|
||||
if top_idx + rows > self.length {
|
||||
grid.clear_area(
|
||||
(
|
||||
pos_inc(upper_left, (0, self.length - top_idx)),
|
||||
bottom_right,
|
||||
),
|
||||
self.theme_default,
|
||||
);
|
||||
grid.change_theme(area.skip_rows(self.length - top_idx), self.theme_default);
|
||||
}
|
||||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
|
@ -326,7 +402,42 @@ impl Component for MailboxManager {
|
|||
if !self.initialized {
|
||||
self.initialize(context);
|
||||
}
|
||||
|
||||
if self.dirty {
|
||||
let area = area.nth_row(0);
|
||||
// Draw column headers.
|
||||
grid.clear_area(area, self.theme_default);
|
||||
let mut x_offset = 0;
|
||||
for (i, (h, w)) in Self::HEADERS.iter().zip(self.min_width).enumerate() {
|
||||
grid.write_string(
|
||||
h,
|
||||
self.theme_default.fg,
|
||||
self.theme_default.bg,
|
||||
self.theme_default.attrs | Attr::BOLD,
|
||||
area.skip_cols(x_offset),
|
||||
None,
|
||||
);
|
||||
if self.sort_col as usize == i {
|
||||
use SortOrder::*;
|
||||
let arrow = match (grid.ascii_drawing, self.sort_order) {
|
||||
(true, Asc) => DataColumns::<4>::ARROW_UP_ASCII,
|
||||
(true, Desc) => DataColumns::<4>::ARROW_DOWN_ASCII,
|
||||
(false, Asc) => DataColumns::<4>::ARROW_UP,
|
||||
(false, Desc) => DataColumns::<4>::ARROW_DOWN,
|
||||
};
|
||||
grid.write_string(
|
||||
arrow,
|
||||
self.theme_default.fg,
|
||||
self.theme_default.bg,
|
||||
self.theme_default.attrs,
|
||||
area.skip_cols(x_offset + h.len()),
|
||||
None,
|
||||
);
|
||||
}
|
||||
x_offset += w + 2;
|
||||
}
|
||||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
let area = area.skip_rows(1);
|
||||
self.draw_list(grid, area, context);
|
||||
if let ViewMode::Action(ref mut s) = self.mode {
|
||||
s.draw(grid, area, context);
|
||||
|
@ -373,7 +484,14 @@ impl Component for MailboxManager {
|
|||
.to_string(),
|
||||
))
|
||||
{
|
||||
context.replies.push_back(UIEvent::Notification { title: None, source: None, body: err.to_string().into(), kind: Some(crate::types::NotificationType::Error(err.kind)), });
|
||||
context.replies.push_back(UIEvent::Notification {
|
||||
title: None,
|
||||
source: None,
|
||||
body: err.to_string().into(),
|
||||
kind: Some(crate::types::NotificationType::Error(
|
||||
err.kind,
|
||||
)),
|
||||
});
|
||||
}
|
||||
}
|
||||
MailboxAction::Unsubscribe => {
|
||||
|
@ -385,7 +503,14 @@ impl Component for MailboxManager {
|
|||
.to_string(),
|
||||
))
|
||||
{
|
||||
context.replies.push_back(UIEvent::Notification { title: None, source: None, body: err.to_string().into(), kind: Some(crate::types::NotificationType::Error(err.kind)), });
|
||||
context.replies.push_back(UIEvent::Notification {
|
||||
title: None,
|
||||
source: None,
|
||||
body: err.to_string().into(),
|
||||
kind: Some(crate::types::NotificationType::Error(
|
||||
err.kind,
|
||||
)),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -406,7 +531,6 @@ impl Component for MailboxManager {
|
|||
self.initialize(context);
|
||||
|
||||
self.set_dirty(true);
|
||||
//self.menu_content.empty();
|
||||
context
|
||||
.replies
|
||||
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(match msg {
|
||||
|
@ -414,6 +538,53 @@ impl Component for MailboxManager {
|
|||
None => self.status(context),
|
||||
})));
|
||||
}
|
||||
UIEvent::Action(Action::SortColumn(column, order)) => {
|
||||
let column = match *column {
|
||||
0 => Column::_0,
|
||||
1 => Column::_1,
|
||||
2 => Column::_2,
|
||||
3 => Column::_3,
|
||||
other => {
|
||||
context.replies.push_back(UIEvent::StatusEvent(
|
||||
StatusEvent::DisplayMessage(format!(
|
||||
"Invalid column index `{}`: there are {} columns.",
|
||||
other,
|
||||
Self::HEADERS.len()
|
||||
)),
|
||||
));
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
if (self.sort_col, self.sort_order) != (column, *order) {
|
||||
self.sort_col = column;
|
||||
self.sort_order = *order;
|
||||
self.initialized = false;
|
||||
self.set_dirty(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(Key::Char(ref c)) if c.is_ascii_digit() => {
|
||||
let n = *c as u8 - b'0'; // safe cast because of is_ascii_digit() check;
|
||||
let column = match n {
|
||||
1 => Column::_0,
|
||||
2 => Column::_1,
|
||||
3 => Column::_2,
|
||||
4 => Column::_3,
|
||||
_ => {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
if self.sort_col == column {
|
||||
self.sort_order = !self.sort_order;
|
||||
} else {
|
||||
self.sort_col = column;
|
||||
self.sort_order = SortOrder::default();
|
||||
}
|
||||
self.initialized = false;
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(ref key)
|
||||
if shortcut!(key == shortcuts[Shortcuts::GENERAL]["scroll_up"]) =>
|
||||
{
|
||||
|
@ -528,7 +699,11 @@ impl Component for MailboxManager {
|
|||
true
|
||||
}
|
||||
|
||||
fn status(&self, _context: &Context) -> String {
|
||||
format!("{} entries", self.entries.len())
|
||||
fn status(&self, context: &Context) -> String {
|
||||
format!(
|
||||
"{} {} entries",
|
||||
context.accounts[&self.account_hash].name(),
|
||||
self.entries.len()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue