ui: hold live entries for each mailbox in CompactListing
parent
0e2e8b09f6
commit
7e80ad2a86
|
@ -26,10 +26,8 @@ use components::utilities::PageMovement;
|
||||||
|
|
||||||
const MAX_COLS: usize = 500;
|
const MAX_COLS: usize = 500;
|
||||||
|
|
||||||
/// A list of all mail (`Envelope`s) in a `Mailbox`. On `\n` it opens the `Envelope` content in a
|
|
||||||
/// `ThreadView`.
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CompactListing {
|
struct MailboxView {
|
||||||
/// (x, y, z): x is accounts, y is folders, z is index inside a folder.
|
/// (x, y, z): x is accounts, y is folders, z is index inside a folder.
|
||||||
cursor_pos: (usize, usize, usize),
|
cursor_pos: (usize, usize, usize),
|
||||||
new_cursor_pos: (usize, usize, usize),
|
new_cursor_pos: (usize, usize, usize),
|
||||||
|
@ -47,28 +45,13 @@ pub struct CompactListing {
|
||||||
movement: Option<PageMovement>,
|
movement: Option<PageMovement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ListingTrait for CompactListing {
|
impl fmt::Display for MailboxView {
|
||||||
fn coordinates(&self) -> (usize, usize, Option<EnvelopeHash>) {
|
|
||||||
(self.cursor_pos.0, self.cursor_pos.1, None)
|
|
||||||
}
|
|
||||||
fn set_coordinates(&mut self, coordinates: (usize, usize, Option<EnvelopeHash>)) {
|
|
||||||
self.new_cursor_pos = (coordinates.0, coordinates.1, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CompactListing {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for CompactListing {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "mail")
|
write!(f, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompactListing {
|
impl MailboxView {
|
||||||
/// Helper function to format entry strings for CompactListing */
|
/// Helper function to format entry strings for CompactListing */
|
||||||
/* TODO: Make this configurable */
|
/* TODO: Make this configurable */
|
||||||
fn make_entry_string(e: &Envelope, len: usize, idx: usize) -> String {
|
fn make_entry_string(e: &Envelope, len: usize, idx: usize) -> String {
|
||||||
|
@ -76,7 +59,7 @@ impl CompactListing {
|
||||||
format!(
|
format!(
|
||||||
"{} {} {:.85} ({})",
|
"{} {} {:.85} ({})",
|
||||||
idx,
|
idx,
|
||||||
&CompactListing::format_date(e),
|
&MailboxView::format_date(e),
|
||||||
e.subject(),
|
e.subject(),
|
||||||
len
|
len
|
||||||
)
|
)
|
||||||
|
@ -84,15 +67,15 @@ impl CompactListing {
|
||||||
format!(
|
format!(
|
||||||
"{} {} {:.85}",
|
"{} {} {:.85}",
|
||||||
idx,
|
idx,
|
||||||
&CompactListing::format_date(e),
|
&MailboxView::format_date(e),
|
||||||
e.subject(),
|
e.subject(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new() -> Self {
|
fn new() -> Self {
|
||||||
let content = CellBuffer::new(0, 0, Cell::with_char(' '));
|
let content = CellBuffer::new(0, 0, Cell::with_char(' '));
|
||||||
CompactListing {
|
MailboxView {
|
||||||
cursor_pos: (0, 1, 0),
|
cursor_pos: (0, 1, 0),
|
||||||
new_cursor_pos: (0, 0, 0),
|
new_cursor_pos: (0, 0, 0),
|
||||||
length: 0,
|
length: 0,
|
||||||
|
@ -114,7 +97,6 @@ impl CompactListing {
|
||||||
if !(self.cursor_pos.0 == self.new_cursor_pos.0
|
if !(self.cursor_pos.0 == self.new_cursor_pos.0
|
||||||
&& self.cursor_pos.1 == self.new_cursor_pos.1)
|
&& self.cursor_pos.1 == self.new_cursor_pos.1)
|
||||||
{
|
{
|
||||||
//TODO: store cursor_pos in each folder
|
|
||||||
self.cursor_pos.2 = 0;
|
self.cursor_pos.2 = 0;
|
||||||
self.new_cursor_pos.2 = 0;
|
self.new_cursor_pos.2 = 0;
|
||||||
}
|
}
|
||||||
|
@ -209,7 +191,7 @@ impl CompactListing {
|
||||||
Color::Default
|
Color::Default
|
||||||
};
|
};
|
||||||
let (x, _) = write_string_to_grid(
|
let (x, _) = write_string_to_grid(
|
||||||
&CompactListing::make_entry_string(root_envelope, thread_node.len(), idx),
|
&MailboxView::make_entry_string(root_envelope, thread_node.len(), idx),
|
||||||
&mut self.content,
|
&mut self.content,
|
||||||
fg_color,
|
fg_color,
|
||||||
bg_color,
|
bg_color,
|
||||||
|
@ -377,7 +359,7 @@ impl CompactListing {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for CompactListing {
|
impl Component for MailboxView {
|
||||||
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.unfocused {
|
if !self.unfocused {
|
||||||
if !self.is_dirty() {
|
if !self.is_dirty() {
|
||||||
|
@ -438,47 +420,16 @@ impl Component for CompactListing {
|
||||||
}
|
}
|
||||||
UIEventType::Input(Key::Char(k @ 'J')) | UIEventType::Input(Key::Char(k @ 'K')) => {
|
UIEventType::Input(Key::Char(k @ 'J')) | UIEventType::Input(Key::Char(k @ 'K')) => {
|
||||||
let folder_length = context.accounts[self.cursor_pos.0].len();
|
let folder_length = context.accounts[self.cursor_pos.0].len();
|
||||||
let accounts_length = context.accounts.len();
|
|
||||||
match k {
|
match k {
|
||||||
'J' if folder_length > 0 => {
|
'J' if folder_length > 0 && self.new_cursor_pos.1 < folder_length - 1 => {
|
||||||
if self.new_cursor_pos.1 < folder_length - 1 {
|
self.new_cursor_pos.1 = self.cursor_pos.1 + 1;
|
||||||
self.new_cursor_pos.1 = self.cursor_pos.1 + 1;
|
|
||||||
self.refresh_mailbox(context);
|
|
||||||
} else if accounts_length > 0 && self.new_cursor_pos.0 < accounts_length - 1
|
|
||||||
{
|
|
||||||
self.new_cursor_pos.0 = self.cursor_pos.0 + 1;
|
|
||||||
self.new_cursor_pos.1 = 0;
|
|
||||||
self.refresh_mailbox(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
'K' => {
|
|
||||||
if self.cursor_pos.1 > 0 {
|
|
||||||
self.new_cursor_pos.1 = self.cursor_pos.1 - 1;
|
|
||||||
self.refresh_mailbox(context);
|
|
||||||
} else if self.cursor_pos.0 > 0 {
|
|
||||||
self.new_cursor_pos.0 = self.cursor_pos.0 - 1;
|
|
||||||
self.new_cursor_pos.1 = 0;
|
|
||||||
self.refresh_mailbox(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
UIEventType::Input(Key::Char(k @ 'h')) | UIEventType::Input(Key::Char(k @ 'l')) => {
|
|
||||||
let accounts_length = context.accounts.len();
|
|
||||||
match k {
|
|
||||||
'h' if accounts_length > 0 && self.new_cursor_pos.0 < accounts_length - 1 => {
|
|
||||||
self.new_cursor_pos.0 = self.cursor_pos.0 + 1;
|
|
||||||
self.new_cursor_pos.1 = 0;
|
|
||||||
self.refresh_mailbox(context);
|
self.refresh_mailbox(context);
|
||||||
}
|
}
|
||||||
'l' if self.cursor_pos.0 > 0 => {
|
'K' if self.cursor_pos.1 > 0 => {
|
||||||
self.new_cursor_pos.0 = self.cursor_pos.0 - 1;
|
self.new_cursor_pos.1 = self.cursor_pos.1 - 1;
|
||||||
self.new_cursor_pos.1 = 0;
|
|
||||||
self.refresh_mailbox(context);
|
self.refresh_mailbox(context);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => return false,
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -637,3 +588,178 @@ impl Component for CompactListing {
|
||||||
map
|
map
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A list of all mail (`Envelope`s) in a `Mailbox`. On `\n` it opens the `Envelope` content in a
|
||||||
|
/// `ThreadView`.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CompactListing {
|
||||||
|
views: Vec<MailboxView>,
|
||||||
|
cursor: usize,
|
||||||
|
dirty: bool,
|
||||||
|
populated: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListingTrait for CompactListing {
|
||||||
|
fn coordinates(&self) -> (usize, usize, Option<EnvelopeHash>) {
|
||||||
|
(self.cursor, self.views[self.cursor].cursor_pos.1, None)
|
||||||
|
}
|
||||||
|
fn set_coordinates(&mut self, coordinates: (usize, usize, Option<EnvelopeHash>)) {
|
||||||
|
self.views[self.cursor].new_cursor_pos = (coordinates.0, coordinates.1, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for CompactListing {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "mail")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CompactListing {
|
||||||
|
fn default() -> Self {
|
||||||
|
CompactListing::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompactListing {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
CompactListing {
|
||||||
|
views: Vec::with_capacity(8),
|
||||||
|
cursor: 0,
|
||||||
|
dirty: true,
|
||||||
|
populated: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for CompactListing {
|
||||||
|
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||||
|
if !self.populated {
|
||||||
|
eprintln!("populating");
|
||||||
|
for (idx, a) in context.accounts.iter().enumerate() {
|
||||||
|
for (fidx, _) in a.iter_mailboxes().enumerate() {
|
||||||
|
let mut m = MailboxView::new();
|
||||||
|
m.new_cursor_pos = (idx, fidx, 0);
|
||||||
|
self.views.push(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.populated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.views.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.views[self.cursor].draw(grid, area, context);
|
||||||
|
}
|
||||||
|
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
|
||||||
|
if self.views.is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
match event.event_type {
|
||||||
|
UIEventType::Input(Key::Char(k @ 'J')) | UIEventType::Input(Key::Char(k @ 'K')) => {
|
||||||
|
let folder_length = context.accounts[self.views[self.cursor].cursor_pos.0].len();
|
||||||
|
match k {
|
||||||
|
'J' if folder_length > 0 => {
|
||||||
|
if self.cursor < self.views.len() - 1 {
|
||||||
|
self.cursor += 1;
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'K' if self.cursor > 0 => {
|
||||||
|
self.cursor -= 1;
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
_ => return false,
|
||||||
|
}
|
||||||
|
self.views[self.cursor].refresh_mailbox(context);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UIEventType::Input(Key::Char(k @ 'h')) | UIEventType::Input(Key::Char(k @ 'l')) => {
|
||||||
|
let binary_search_account = |entries: &[MailboxView], x: usize| -> Option<usize> {
|
||||||
|
if entries.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut low = 0;
|
||||||
|
let mut high = entries.len() - 1;
|
||||||
|
while low < high {
|
||||||
|
let mid = low + (high - low) / 2;
|
||||||
|
if x > entries[mid].new_cursor_pos.0 {
|
||||||
|
low = mid + 1;
|
||||||
|
} else {
|
||||||
|
high = mid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Some(low);
|
||||||
|
};
|
||||||
|
match k {
|
||||||
|
'h' => {
|
||||||
|
if let Some(next) = binary_search_account(
|
||||||
|
&self.views.as_slice()[self.cursor..],
|
||||||
|
self.views[self.cursor].new_cursor_pos.0 + 1,
|
||||||
|
) {
|
||||||
|
self.cursor += next;
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'l' if self.views[self.cursor].cursor_pos.0 > 0 => {
|
||||||
|
if let Some(next) = binary_search_account(
|
||||||
|
&self.views.as_slice()[..self.cursor],
|
||||||
|
self.views[self.cursor].new_cursor_pos.0 - 1,
|
||||||
|
) {
|
||||||
|
self.cursor = next;
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return false,
|
||||||
|
}
|
||||||
|
self.views[self.cursor].refresh_mailbox(context);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return self.views[self.cursor].process_event(event, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_dirty(&self) -> bool {
|
||||||
|
if self.views.is_empty() {
|
||||||
|
return self.dirty;
|
||||||
|
}
|
||||||
|
self.dirty || self.views[self.cursor].is_dirty()
|
||||||
|
}
|
||||||
|
fn set_dirty(&mut self) {
|
||||||
|
if self.views.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.views[self.cursor].set_dirty();
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_shortcuts(&self, context: &Context) -> ShortcutMap {
|
||||||
|
if self.views.is_empty() {
|
||||||
|
return Default::default();
|
||||||
|
}
|
||||||
|
let mut map = self.views[self.cursor].get_shortcuts(context);
|
||||||
|
|
||||||
|
let config_map = context.settings.shortcuts.compact_listing.key_values();
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue