listing: scroll account sidebar menu

Closes #85 Accounts sidebar doesn't scroll
jmap-eventsource
Manos Pitsidianakis 2020-11-27 23:05:35 +02:00
parent b053aaa145
commit b659749880
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
11 changed files with 403 additions and 356 deletions

View File

@ -136,25 +136,18 @@ impl ContactList {
min_width.2 = cmp::max(min_width.2, c.url().split_graphemes().len()); min_width.2 = cmp::max(min_width.2, c.url().split_graphemes().len());
} }
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(self.theme_default.fg)
.set_bg(self.theme_default.bg)
.set_attrs(self.theme_default.attrs);
ret
};
/* name column */ /* name column */
self.data_columns.columns[0] = self.data_columns.columns[0] =
CellBuffer::new_with_context(min_width.0, self.length, default_cell, context); CellBuffer::new_with_context(min_width.0, self.length, None, context);
/* email column */ /* email column */
self.data_columns.columns[1] = self.data_columns.columns[1] =
CellBuffer::new_with_context(min_width.1, self.length, default_cell, context); CellBuffer::new_with_context(min_width.1, self.length, None, context);
/* url column */ /* url column */
self.data_columns.columns[2] = self.data_columns.columns[2] =
CellBuffer::new_with_context(min_width.2, self.length, default_cell, context); CellBuffer::new_with_context(min_width.2, self.length, None, context);
/* source column */ /* source column */
self.data_columns.columns[3] = self.data_columns.columns[3] =
CellBuffer::new_with_context("external".len(), self.length, default_cell, context); CellBuffer::new_with_context("external".len(), self.length, None, context);
let account = &context.accounts[self.account_pos]; let account = &context.accounts[self.account_pos];
let book = &account.address_book; let book = &account.address_book;
@ -209,16 +202,9 @@ impl ContactList {
} }
if self.length == 0 { if self.length == 0 {
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(self.theme_default.fg)
.set_bg(self.theme_default.bg)
.set_attrs(self.theme_default.attrs);
ret
};
let message = "Address book is empty.".to_string(); let message = "Address book is empty.".to_string();
self.data_columns.columns[0] = self.data_columns.columns[0] =
CellBuffer::new_with_context(message.len(), self.length, default_cell, context); CellBuffer::new_with_context(message.len(), self.length, None, context);
write_string_to_grid( write_string_to_grid(
&message, &message,
&mut self.data_columns.columns[0], &mut self.data_columns.columns[0],

View File

@ -468,6 +468,12 @@ enum ListingFocus {
Mailbox, Mailbox,
} }
#[derive(PartialEq, Copy, Clone, Debug)]
enum MenuEntryCursor {
Status,
Mailbox(usize),
}
#[derive(Debug)] #[derive(Debug)]
pub struct Listing { pub struct Listing {
component: ListingComponent, component: ListingComponent,
@ -475,8 +481,9 @@ pub struct Listing {
status: Option<AccountStatus>, status: Option<AccountStatus>,
dirty: bool, dirty: bool,
visible: bool, visible: bool,
cursor_pos: (usize, usize), cursor_pos: (usize, MenuEntryCursor),
menu_cursor_pos: (usize, usize), menu_cursor_pos: (usize, MenuEntryCursor),
menu_content: CellBuffer,
startup_checks_rate: RateLimit, startup_checks_rate: RateLimit,
id: ComponentId, id: ComponentId,
theme_default: ThemeAttribute, theme_default: ThemeAttribute,
@ -632,6 +639,7 @@ impl Component for Listing {
.map(|f| (f.depth, f.indentation, f.has_sibling, f.hash)) .map(|f| (f.depth, f.indentation, f.has_sibling, f.hash))
.collect::<_>(); .collect::<_>();
self.set_dirty(true); self.set_dirty(true);
self.menu_content.empty();
context context
.replies .replies
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus( .push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
@ -640,12 +648,13 @@ impl Component for Listing {
} }
return true; return true;
} }
UIEvent::MailboxDelete((account_hash, _mailbox_hash)) UIEvent::MailboxDelete((account_hash, mailbox_hash))
| UIEvent::MailboxCreate((account_hash, _mailbox_hash)) => { | UIEvent::MailboxCreate((account_hash, mailbox_hash)) => {
let account_index = context let account_index = context
.accounts .accounts
.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.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()
@ -656,14 +665,21 @@ impl Component for Listing {
}) })
.map(|f| (f.depth, f.indentation, f.has_sibling, f.hash)) .map(|f| (f.depth, f.indentation, f.has_sibling, f.hash))
.collect::<_>(); .collect::<_>();
if self.cursor_pos.0 == account_index { let mut fallback = 0;
self.cursor_pos.1 = std::cmp::min( if let MenuEntryCursor::Mailbox(ref mut cur) = self.cursor_pos.1 {
self.accounts[self.cursor_pos.0].entries.len() - 1, *cur = std::cmp::min(
self.cursor_pos.1, self.accounts[self.cursor_pos.0]
.entries
.len()
.saturating_sub(1),
*cur,
); );
fallback = *cur;
}
if self.component.coordinates() == (*account_hash, *mailbox_hash) {
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[self.cursor_pos.1].3, self.accounts[self.cursor_pos.0].entries[fallback].3,
)); ));
self.component.refresh_mailbox(context, true); self.component.refresh_mailbox(context, true);
} }
@ -675,6 +691,26 @@ impl Component for Listing {
self.set_dirty(true); self.set_dirty(true);
return true; return true;
} }
UIEvent::ChangeMode(UIMode::Normal) => {
self.dirty = true;
}
UIEvent::Resize => {
self.set_dirty(true);
}
UIEvent::Action(Action::ViewMailbox(ref idx)) => {
if let Some((_, _, _, mailbox_hash)) =
self.accounts[self.cursor_pos.0].entries.get(*idx)
{
let account_hash = self.accounts[self.cursor_pos.0].hash;
self.cursor_pos.1 = MenuEntryCursor::Mailbox(*idx);
self.status = None;
self.component
.set_coordinates((account_hash, *mailbox_hash));
self.menu_content.empty();
self.set_dirty(true);
}
return true;
}
_ => {} _ => {}
} }
@ -759,34 +795,37 @@ impl Component for Listing {
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); .push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
return true; return true;
}; };
match k { let target = match k {
k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_mailbox"]) => { k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_mailbox"]) => {
if self.accounts[self.cursor_pos.0] match self.cursor_pos.1 {
.entries MenuEntryCursor::Status => amount.saturating_sub(1),
.get(self.cursor_pos.1 + amount) MenuEntryCursor::Mailbox(idx) => idx + amount,
.is_some()
{
self.cursor_pos.1 += amount;
} else {
return true;
} }
} }
k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_mailbox"]) => { k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_mailbox"]) => {
if self.cursor_pos.1 >= amount { match self.cursor_pos.1 {
if self.accounts[self.cursor_pos.0] MenuEntryCursor::Status => {
.entries
.get(self.cursor_pos.1 - amount)
.is_some()
{
self.cursor_pos.1 -= amount;
} else {
return true; return true;
} }
} else { MenuEntryCursor::Mailbox(idx) => {
return true; if idx >= amount {
idx - amount
} else {
return true;
}
}
} }
} }
_ => {} _ => return true,
};
if self.accounts[self.cursor_pos.0]
.entries
.get(target)
.is_some()
{
self.cursor_pos.1 = MenuEntryCursor::Mailbox(target)
} else {
return true;
} }
self.change_account(context); self.change_account(context);
return true; return true;
@ -815,14 +854,16 @@ impl Component for Listing {
match k { match k {
k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_account"]) => { k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_account"]) => {
if self.cursor_pos.0 + amount < self.accounts.len() { if self.cursor_pos.0 + amount < self.accounts.len() {
self.cursor_pos = (self.cursor_pos.0 + amount, 0); self.cursor_pos =
(self.cursor_pos.0 + amount, MenuEntryCursor::Mailbox(0));
} else { } else {
return true; return true;
} }
} }
k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_account"]) => { k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_account"]) => {
if self.cursor_pos.0 >= amount { if self.cursor_pos.0 >= amount {
self.cursor_pos = (self.cursor_pos.0 - amount, 0); self.cursor_pos =
(self.cursor_pos.0 - amount, MenuEntryCursor::Mailbox(0));
} else { } else {
return true; return true;
} }
@ -841,25 +882,6 @@ impl Component for Listing {
self.menu_visibility = !self.menu_visibility; self.menu_visibility = !self.menu_visibility;
self.set_dirty(true); self.set_dirty(true);
} }
UIEvent::ChangeMode(UIMode::Normal) => {
self.dirty = true;
}
UIEvent::Resize => {
self.set_dirty(true);
}
UIEvent::Action(Action::ViewMailbox(ref idx)) => {
if let Some((_, _, _, mailbox_hash)) =
self.accounts[self.cursor_pos.0].entries.get(*idx)
{
let account_hash = self.accounts[self.cursor_pos.0].hash;
self.cursor_pos.1 = *idx;
self.status = None;
self.component
.set_coordinates((account_hash, *mailbox_hash));
self.set_dirty(true);
}
return true;
}
_ => {} _ => {}
} }
@ -1047,14 +1069,15 @@ impl Component for Listing {
if shortcut!(key == shortcuts[Listing::DESCRIPTION]["refresh"]) => if shortcut!(key == shortcuts[Listing::DESCRIPTION]["refresh"]) =>
{ {
let account = &mut context.accounts[self.cursor_pos.0]; let account = &mut context.accounts[self.cursor_pos.0];
if let Some(&mailbox_hash) = account.mailboxes_order.get(self.cursor_pos.1) if let MenuEntryCursor::Mailbox(idx) = self.cursor_pos.1 {
{ if let Some(&mailbox_hash) = account.mailboxes_order.get(idx) {
if let Err(err) = account.refresh(mailbox_hash) { if let Err(err) = account.refresh(mailbox_hash) {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification(
Some("Could not refresh.".to_string()), Some("Could not refresh.".to_string()),
err.to_string(), err.to_string(),
Some(NotificationType::Error(err.kind)), Some(NotificationType::Error(err.kind)),
)); ));
}
} }
} }
return true; return true;
@ -1101,12 +1124,11 @@ impl Component for Listing {
} }
UIEvent::Input(ref k) UIEvent::Input(ref k)
if shortcut!(k == shortcuts[Listing::DESCRIPTION]["open_mailbox"]) if shortcut!(k == shortcuts[Listing::DESCRIPTION]["open_mailbox"])
&& self.menu_cursor_pos.1 == 0 => && self.menu_cursor_pos.1 == MenuEntryCursor::Status =>
{ {
self.status = Some(AccountStatus::new( self.cursor_pos = self.menu_cursor_pos;
self.menu_cursor_pos.0, self.open_status(self.menu_cursor_pos.0, context);
self.theme_default, self.set_dirty(true);
));
self.focus = ListingFocus::Mailbox; self.focus = ListingFocus::Mailbox;
self.ratio = 90; self.ratio = 90;
return true; return true;
@ -1115,8 +1137,6 @@ impl Component for Listing {
if shortcut!(k == shortcuts[Listing::DESCRIPTION]["open_mailbox"]) => if shortcut!(k == shortcuts[Listing::DESCRIPTION]["open_mailbox"]) =>
{ {
self.cursor_pos = self.menu_cursor_pos; self.cursor_pos = self.menu_cursor_pos;
self.cursor_pos.1 = self.cursor_pos.1.saturating_sub(1);
self.status = None;
self.change_account(context); self.change_account(context);
self.focus = ListingFocus::Mailbox; self.focus = ListingFocus::Mailbox;
self.ratio = 90; self.ratio = 90;
@ -1151,32 +1171,77 @@ impl Component for Listing {
}; };
if shortcut!(k == shortcuts[Listing::DESCRIPTION]["scroll_up"]) { if shortcut!(k == shortcuts[Listing::DESCRIPTION]["scroll_up"]) {
while amount > 0 { while amount > 0 {
if self.menu_cursor_pos.1 > 0 { match self.menu_cursor_pos {
self.menu_cursor_pos.1 -= 1; (
} else if self.menu_cursor_pos.0 > 0 { ref mut account_cursor,
self.menu_cursor_pos.0 -= 1; ref mut entry_cursor @ MenuEntryCursor::Status,
self.menu_cursor_pos.1 = ) => {
self.accounts[self.menu_cursor_pos.0].entries.len(); if *account_cursor > 0 {
} else { *account_cursor -= 1;
return true; *entry_cursor = MenuEntryCursor::Mailbox(
self.accounts[*account_cursor]
.entries
.len()
.saturating_sub(1),
);
} else {
return true;
}
}
(_, MenuEntryCursor::Mailbox(ref mut mailbox_idx)) => {
if *mailbox_idx > 0 {
*mailbox_idx -= 1;
} else {
self.menu_cursor_pos.1 = MenuEntryCursor::Status;
}
}
} }
amount -= 1; amount -= 1;
} }
} else if shortcut!(k == shortcuts[Listing::DESCRIPTION]["scroll_down"]) { } else if shortcut!(k == shortcuts[Listing::DESCRIPTION]["scroll_down"]) {
while amount > 0 { while amount > 0 {
if self.menu_cursor_pos.1 match self.menu_cursor_pos {
< self.accounts[self.menu_cursor_pos.0].entries.len() /* If current account has mailboxes, go to first mailbox */
{ (
self.menu_cursor_pos.1 += 1; ref account_cursor,
} else if self.menu_cursor_pos.0 + 1 < self.accounts.len() { ref mut entry_cursor @ MenuEntryCursor::Status,
self.menu_cursor_pos.0 += 1; ) if !self.accounts[*account_cursor].entries.is_empty() => {
self.menu_cursor_pos.1 = 0; *entry_cursor = MenuEntryCursor::Mailbox(0);
} else { }
return true; /* If current account has no mailboxes, go to next account */
(
ref mut account_cursor,
ref mut entry_cursor @ MenuEntryCursor::Status,
) if *account_cursor + 1 < self.accounts.len() => {
*account_cursor += 1;
*entry_cursor = MenuEntryCursor::Status;
}
/* If current account has no mailboxes and there is no next account, return true */
(_, MenuEntryCursor::Status) => {
return true;
}
(
ref mut account_cursor,
MenuEntryCursor::Mailbox(ref mut mailbox_idx),
) => {
if (*mailbox_idx + 1)
< self.accounts[*account_cursor].entries.len()
{
*mailbox_idx += 1;
} else if *account_cursor + 1 < self.accounts.len() {
*account_cursor += 1;
self.menu_cursor_pos.1 = MenuEntryCursor::Status;
} else {
return true;
}
}
} }
amount -= 1; amount -= 1;
} }
} }
self.menu_content.empty();
self.set_dirty(true); self.set_dirty(true);
return true; return true;
} }
@ -1201,37 +1266,39 @@ impl Component for Listing {
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); .push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
return true; return true;
}; };
match k { let target = match k {
k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_mailbox"]) => { k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_mailbox"]) => {
if self.accounts[self.menu_cursor_pos.0] match self.menu_cursor_pos.1 {
.entries MenuEntryCursor::Status => amount.saturating_sub(1),
.get(self.menu_cursor_pos.1.saturating_sub(1) + amount) MenuEntryCursor::Mailbox(idx) => idx + amount,
.is_some()
{
self.menu_cursor_pos.1 += amount;
self.set_dirty(true);
} else {
return true;
} }
} }
k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_mailbox"]) => { k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_mailbox"]) => {
if self.cursor_pos.1 >= amount + 1 { match self.menu_cursor_pos.1 {
if self.accounts[self.menu_cursor_pos.0] MenuEntryCursor::Status => {
.entries
.get(self.menu_cursor_pos.1.saturating_sub(1) - amount)
.is_some()
{
self.menu_cursor_pos.1 -= amount;
self.set_dirty(true);
} else {
return true; return true;
} }
} else { MenuEntryCursor::Mailbox(idx) => {
return true; if idx >= amount {
idx - amount
} else {
return true;
}
}
} }
} }
_ => {} _ => return true,
};
if self.accounts[self.menu_cursor_pos.0]
.entries
.get(target)
.is_some()
{
self.menu_cursor_pos.1 = MenuEntryCursor::Mailbox(target)
} else {
return true;
} }
self.menu_content.empty();
return true; return true;
} }
UIEvent::Input(ref k) UIEvent::Input(ref k)
@ -1262,7 +1329,8 @@ impl Component for Listing {
|| shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_page"]) => || shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_page"]) =>
{ {
if self.menu_cursor_pos.0 + amount < self.accounts.len() { if self.menu_cursor_pos.0 + amount < self.accounts.len() {
self.menu_cursor_pos = (self.menu_cursor_pos.0 + amount, 0); self.menu_cursor_pos =
(self.menu_cursor_pos.0 + amount, MenuEntryCursor::Mailbox(0));
} else { } else {
return true; return true;
} }
@ -1271,13 +1339,15 @@ impl Component for Listing {
|| shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_page"]) => || shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_page"]) =>
{ {
if self.menu_cursor_pos.0 >= amount { if self.menu_cursor_pos.0 >= amount {
self.menu_cursor_pos = (self.menu_cursor_pos.0 - amount, 0); self.menu_cursor_pos =
(self.menu_cursor_pos.0 - amount, MenuEntryCursor::Mailbox(0));
} else { } else {
return true; return true;
} }
} }
_ => return false, _ => return false,
} }
self.menu_content.empty();
self.set_dirty(true); self.set_dirty(true);
return true; return true;
@ -1379,13 +1449,19 @@ impl Component for Listing {
} }
fn get_status(&self, context: &Context) -> String { fn get_status(&self, context: &Context) -> String {
let mailbox_hash = if let Some((_, _, _, mailbox_hash)) = self.accounts[self.cursor_pos.0] let mailbox_hash = match self.cursor_pos.1 {
.entries MenuEntryCursor::Mailbox(idx) => {
.get(self.cursor_pos.1) if let Some((_, _, _, mailbox_hash)) =
{ self.accounts[self.cursor_pos.0].entries.get(idx)
*mailbox_hash {
} else { *mailbox_hash
return String::new(); } else {
return String::new();
}
}
MenuEntryCursor::Status => {
return format!("{} status", &self.accounts[self.cursor_pos.0].name)
}
}; };
let account = &context.accounts[self.cursor_pos.0]; let account = &context.accounts[self.cursor_pos.0];
@ -1439,8 +1515,9 @@ impl Listing {
status: None, status: None,
visible: true, visible: true,
dirty: true, dirty: true,
cursor_pos: (0, 0), cursor_pos: (0, MenuEntryCursor::Mailbox(0)),
menu_cursor_pos: (0, 0), menu_cursor_pos: (0, MenuEntryCursor::Mailbox(0)),
menu_content: CellBuffer::new_with_context(0, 0, None, context),
startup_checks_rate: RateLimit::new(2, 1000, context.job_executor.clone()), startup_checks_rate: RateLimit::new(2, 1000, context.job_executor.clone()),
theme_default: conf::value(context, "theme_default"), theme_default: conf::value(context, "theme_default"),
id: ComponentId::new_v4(), id: ComponentId::new_v4(),
@ -1459,37 +1536,80 @@ impl Listing {
} }
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) {
if !self.is_dirty() {
return;
}
clear_area(grid, area, self.theme_default); clear_area(grid, area, self.theme_default);
let upper_left = upper_left!(area); let total_height: usize = 3 * (self.accounts.len())
let bottom_right = bottom_right!(area); + self
self.dirty = false; .accounts
let mut y = get_y(upper_left); .iter()
for a in &self.accounts { .map(|entry| entry.entries.len() + 1)
if y > get_y(bottom_right) { .sum::<usize>();
break; let min_width: usize = 2 * width!(area);
let (width, height) = self.menu_content.size();
let cursor = match self.focus {
ListingFocus::Mailbox => self.cursor_pos,
ListingFocus::Menu => self.menu_cursor_pos,
};
if min_width > width || height < total_height || self.dirty {
let _ = self.menu_content.resize(
min_width * 2,
total_height,
self.menu_content.default_cell,
);
let bottom_right = pos_dec(self.menu_content.size(), (1, 1));
let mut y = 0;
for a in 0..self.accounts.len() {
if y > get_y(bottom_right) {
break;
}
y += self.print_account(((0, y), bottom_right), a, context);
y += 3;
} }
y += self.print_account(grid, (set_y(upper_left, y), bottom_right), &a, context);
y += 3;
} }
let rows = height!(area);
let (width, height) = self.menu_content.size();
const SCROLLING_CONTEXT: usize = 3;
let y_offset = (cursor.0)
+ self
.accounts
.iter()
.take(cursor.0)
.map(|entry| entry.entries.len() + 1)
.sum::<usize>()
+ match cursor.1 {
MenuEntryCursor::Status => 0,
MenuEntryCursor::Mailbox(idx) => idx + 1,
}
+ SCROLLING_CONTEXT;
let skip_offset = if y_offset <= rows {
0
} else {
rows * y_offset.wrapping_div(rows).saturating_sub(1) + y_offset.wrapping_rem(rows)
};
copy_area(
grid,
&self.menu_content,
area,
(
(
0,
std::cmp::min((height - 1).saturating_sub(rows), skip_offset),
),
(width - 1, std::cmp::min(skip_offset + rows, height - 1)),
),
);
context.dirty_areas.push_back(area); context.dirty_areas.push_back(area);
} }
/* /*
* Print a single account in the menu area. * Print a single account in the menu area.
*/ */
fn print_account( fn print_account(&mut self, area: Area, aidx: usize, context: &mut Context) -> usize {
&self,
grid: &mut CellBuffer,
area: Area,
a: &AccountMenuEntry,
context: &mut Context,
) -> usize {
debug_assert!(is_valid_area!(area)); debug_assert!(is_valid_area!(area));
// Each entry and its index in the account // Each entry and its index in the account
let mailboxes: HashMap<MailboxHash, Mailbox> = context.accounts[a.index] let mailboxes: HashMap<MailboxHash, Mailbox> = context.accounts[self.accounts[aidx].index]
.mailbox_entries .mailbox_entries
.iter() .iter()
.map(|(&hash, entry)| (hash, entry.ref_mailbox.clone())) .map(|(&hash, entry)| (hash, entry.ref_mailbox.clone()))
@ -1498,15 +1618,20 @@ impl Listing {
let upper_left = upper_left!(area); let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area); let bottom_right = bottom_right!(area);
let must_highlight_account: bool = (self.focus == ListingFocus::Mailbox let cursor = match self.focus {
&& self.cursor_pos.0 == a.index) ListingFocus::Mailbox => self.cursor_pos,
|| (self.focus == ListingFocus::Menu && self.menu_cursor_pos.0 == a.index); ListingFocus::Menu => self.menu_cursor_pos,
};
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<(usize, usize, u32, bool, MailboxHash, Option<usize>)> = Vec::new();
for (i, &(depth, indentation, has_sibling, mailbox_hash)) in a.entries.iter().enumerate() { for (i, &(depth, indentation, has_sibling, mailbox_hash)) in
self.accounts[aidx].entries.iter().enumerate()
{
if mailboxes[&mailbox_hash].is_subscribed() { if mailboxes[&mailbox_hash].is_subscribed() {
match context.accounts[a.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((depth, i, indentation, has_sibling, mailbox_hash, None));
} }
@ -1525,7 +1650,7 @@ impl Listing {
} }
let account_attrs = if must_highlight_account { let account_attrs = if must_highlight_account {
if self.focus == ListingFocus::Menu && self.menu_cursor_pos.1 == 0 { if cursor.1 == MenuEntryCursor::Status {
let mut v = crate::conf::value(context, "mail.sidebar_highlighted"); let mut v = crate::conf::value(context, "mail.sidebar_highlighted");
if !context.settings.terminal.use_color() { if !context.settings.terminal.use_color() {
v.attrs |= Attr::REVERSE; v.attrs |= Attr::REVERSE;
@ -1540,8 +1665,8 @@ impl Listing {
/* Print account name first */ /* Print account name first */
write_string_to_grid( write_string_to_grid(
&a.name, &self.accounts[aidx].name,
grid, &mut self.menu_content,
account_attrs.fg, account_attrs.fg,
account_attrs.bg, account_attrs.bg,
account_attrs.attrs, account_attrs.attrs,
@ -1552,7 +1677,7 @@ impl Listing {
if lines.is_empty() { if lines.is_empty() {
write_string_to_grid( write_string_to_grid(
"offline", "offline",
grid, &mut self.menu_content,
Color::Byte(243), Color::Byte(243),
account_attrs.bg, account_attrs.bg,
account_attrs.attrs, account_attrs.attrs,
@ -1571,9 +1696,10 @@ impl Listing {
break; break;
} }
let (att, index_att, unread_count_att) = if must_highlight_account { let (att, index_att, unread_count_att) = if must_highlight_account {
if (self.focus == ListingFocus::Mailbox && self.cursor_pos.1 == idx) if match cursor.1 {
|| (self.focus == ListingFocus::Menu && self.menu_cursor_pos.1 == idx + 1) MenuEntryCursor::Mailbox(c) => c == idx,
{ _ => false,
} {
let mut ret = ( let mut ret = (
crate::conf::value(context, "mail.sidebar_highlighted"), crate::conf::value(context, "mail.sidebar_highlighted"),
crate::conf::value(context, "mail.sidebar_highlighted_index"), crate::conf::value(context, "mail.sidebar_highlighted_index"),
@ -1623,19 +1749,25 @@ impl Listing {
ctr ctr
}; };
let has_sibling_str: &str = let has_sibling_str: &str = account_settings!(
account_settings!(context[a.hash].listing.sidebar_mailbox_tree_has_sibling) context[self.accounts[aidx].hash]
.as_ref() .listing
.map(|s| s.as_str()) .sidebar_mailbox_tree_has_sibling
.unwrap_or(" "); )
let no_sibling_str: &str = .as_ref()
account_settings!(context[a.hash].listing.sidebar_mailbox_tree_no_sibling) .map(|s| s.as_str())
.as_ref() .unwrap_or(" ");
.map(|s| s.as_str()) let no_sibling_str: &str = account_settings!(
.unwrap_or(" "); context[self.accounts[aidx].hash]
.listing
.sidebar_mailbox_tree_no_sibling
)
.as_ref()
.map(|s| s.as_str())
.unwrap_or(" ");
let has_sibling_leaf_str: &str = account_settings!( let has_sibling_leaf_str: &str = account_settings!(
context[a.hash] context[self.accounts[aidx].hash]
.listing .listing
.sidebar_mailbox_tree_has_sibling_leaf .sidebar_mailbox_tree_has_sibling_leaf
) )
@ -1643,15 +1775,18 @@ impl Listing {
.map(|s| s.as_str()) .map(|s| s.as_str())
.unwrap_or(" "); .unwrap_or(" ");
let no_sibling_leaf_str: &str = let no_sibling_leaf_str: &str = account_settings!(
account_settings!(context[a.hash].listing.sidebar_mailbox_tree_no_sibling_leaf) context[self.accounts[aidx].hash]
.as_ref() .listing
.map(|s| s.as_str()) .sidebar_mailbox_tree_no_sibling_leaf
.unwrap_or(" "); )
.as_ref()
.map(|s| s.as_str())
.unwrap_or(" ");
let (x, _) = write_string_to_grid( let (x, _) = write_string_to_grid(
&format!("{:>width$}", inc, width = total_mailbox_no_digits), &format!("{:>width$}", inc, width = total_mailbox_no_digits),
grid, &mut self.menu_content,
index_att.fg, index_att.fg,
index_att.bg, index_att.bg,
index_att.attrs, index_att.attrs,
@ -1681,7 +1816,7 @@ impl Listing {
} }
let (x, _) = write_string_to_grid( let (x, _) = write_string_to_grid(
&branches, &branches,
grid, &mut self.menu_content,
att.fg, att.fg,
att.bg, att.bg,
att.attrs, att.attrs,
@ -1689,8 +1824,8 @@ impl Listing {
None, None,
); );
let (x, _) = write_string_to_grid( let (x, _) = write_string_to_grid(
context.accounts[a.index].mailbox_entries[&mailbox_idx].name(), context.accounts[self.accounts[aidx].index].mailbox_entries[&mailbox_idx].name(),
grid, &mut self.menu_content,
att.fg, att.fg,
att.bg, att.bg,
att.attrs, att.attrs,
@ -1711,7 +1846,7 @@ impl Listing {
let (x, _) = write_string_to_grid( let (x, _) = write_string_to_grid(
&count_string, &count_string,
grid, &mut self.menu_content,
unread_count_att.fg, unread_count_att.fg,
unread_count_att.bg, unread_count_att.bg,
unread_count_att.attrs unread_count_att.attrs
@ -1730,8 +1865,11 @@ impl Listing {
), ),
None, None,
); );
for c in grid.row_iter(x..(get_x(bottom_right) + 1), y) { for c in self.menu_content.row_iter(x..(get_x(bottom_right) + 1), y) {
grid[c].set_fg(att.fg).set_bg(att.bg).set_attrs(att.attrs); self.menu_content[c]
.set_fg(att.fg)
.set_bg(att.bg)
.set_attrs(att.attrs);
} }
idx += 1; idx += 1;
} }
@ -1754,31 +1892,48 @@ impl Listing {
}) })
.map(|f| (f.depth, f.indentation, f.has_sibling, f.hash)) .map(|f| (f.depth, f.indentation, f.has_sibling, f.hash))
.collect::<_>(); .collect::<_>();
/* Account might have no mailboxes yet if it's offline */ match self.cursor_pos.1 {
if let Some((_, _, _, mailbox_hash)) = self.accounts[self.cursor_pos.0] MenuEntryCursor::Mailbox(idx) => {
.entries /* Account might have no mailboxes yet if it's offline */
.get(self.cursor_pos.1) if let Some((_, _, _, mailbox_hash)) =
{ self.accounts[self.cursor_pos.0].entries.get(idx)
self.component {
.set_coordinates((account_hash, *mailbox_hash)); self.component
/* Check if per-mailbox configuration overrides general configuration */ .set_coordinates((account_hash, *mailbox_hash));
/* Check if per-mailbox configuration overrides general configuration */
let index_style = let index_style =
mailbox_settings!(context[account_hash][mailbox_hash].listing.index_style); mailbox_settings!(context[account_hash][mailbox_hash].listing.index_style);
self.component.set_style(*index_style); self.component.set_style(*index_style);
} else { } else {
/* Set to dummy */ /* Set to dummy */
self.component = Offline(OfflineListing::new((account_hash, 0))); self.component = Offline(OfflineListing::new((account_hash, 0)));
}
self.status = None;
context
.replies
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
self.get_status(context),
)));
}
MenuEntryCursor::Status => {
self.open_status(self.cursor_pos.0, context);
}
} }
self.sidebar_divider = *account_settings!(context[account_hash].listing.sidebar_divider); self.sidebar_divider = *account_settings!(context[account_hash].listing.sidebar_divider);
self.status = None;
self.set_dirty(true); self.set_dirty(true);
self.menu_cursor_pos = self.cursor_pos;
/* clear menu to force redraw */
self.menu_content.empty();
}
fn open_status(&mut self, account_idx: usize, context: &mut Context) {
self.status = Some(AccountStatus::new(account_idx, self.theme_default));
self.menu_content.empty();
context context
.replies .replies
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus( .push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
self.get_status(context), self.get_status(context),
))); )));
self.menu_cursor_pos = self.cursor_pos;
self.menu_cursor_pos.1 += 1;
} }
} }

View File

@ -227,17 +227,10 @@ impl MailListingTrait for CompactListing {
match context.accounts[&self.cursor_pos.0].load(self.cursor_pos.1) { match context.accounts[&self.cursor_pos.0].load(self.cursor_pos.1) {
Ok(()) => {} Ok(()) => {}
Err(_) => { Err(_) => {
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg)
.set_bg(self.color_cache.theme_default.bg)
.set_attrs(self.color_cache.theme_default.attrs);
ret
};
let message: String = let message: String =
context.accounts[&self.cursor_pos.0][&self.cursor_pos.1].status(); context.accounts[&self.cursor_pos.0][&self.cursor_pos.1].status();
self.data_columns.columns[0] = self.data_columns.columns[0] =
CellBuffer::new_with_context(message.len(), 1, default_cell, context); CellBuffer::new_with_context(message.len(), 1, None, context);
self.length = 0; self.length = 0;
write_string_to_grid( write_string_to_grid(
message.as_str(), message.as_str(),
@ -392,30 +385,23 @@ impl MailListingTrait for CompactListing {
min_width.0 = self.length.saturating_sub(1).to_string().len(); min_width.0 = self.length.saturating_sub(1).to_string().len();
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg)
.set_bg(self.color_cache.theme_default.bg)
.set_attrs(self.color_cache.theme_default.attrs);
ret
};
/* index column */ /* index column */
self.data_columns.columns[0] = self.data_columns.columns[0] =
CellBuffer::new_with_context(min_width.0, rows.len(), default_cell, context); CellBuffer::new_with_context(min_width.0, rows.len(), None, context);
/* date column */ /* date column */
self.data_columns.columns[1] = self.data_columns.columns[1] =
CellBuffer::new_with_context(min_width.1, rows.len(), default_cell, context); CellBuffer::new_with_context(min_width.1, rows.len(), None, context);
/* from column */ /* from column */
self.data_columns.columns[2] = self.data_columns.columns[2] =
CellBuffer::new_with_context(min_width.2, rows.len(), default_cell, context); CellBuffer::new_with_context(min_width.2, rows.len(), None, context);
self.data_columns.segment_tree[2] = row_widths.2.into(); self.data_columns.segment_tree[2] = row_widths.2.into();
/* flags column */ /* flags column */
self.data_columns.columns[3] = self.data_columns.columns[3] =
CellBuffer::new_with_context(min_width.3, rows.len(), default_cell, context); CellBuffer::new_with_context(min_width.3, rows.len(), None, context);
/* subject column */ /* subject column */
self.data_columns.columns[4] = self.data_columns.columns[4] =
CellBuffer::new_with_context(min_width.4, rows.len(), default_cell, context); CellBuffer::new_with_context(min_width.4, rows.len(), None, context);
self.data_columns.segment_tree[4] = row_widths.4.into(); self.data_columns.segment_tree[4] = row_widths.4.into();
self.rows = rows; self.rows = rows;
@ -433,7 +419,7 @@ impl MailListingTrait for CompactListing {
if self.length == 0 && self.filter_term.is_empty() { if self.length == 0 && self.filter_term.is_empty() {
let message: String = account[&self.cursor_pos.1].status(); let message: String = account[&self.cursor_pos.1].status();
self.data_columns.columns[0] = self.data_columns.columns[0] =
CellBuffer::new_with_context(message.len(), self.length + 1, default_cell, context); CellBuffer::new_with_context(message.len(), self.length + 1, None, context);
write_string_to_grid( write_string_to_grid(
&message, &message,
&mut self.data_columns.columns[0], &mut self.data_columns.columns[0],
@ -804,15 +790,8 @@ impl ListingTrait for CompactListing {
self.new_cursor_pos.2 = self.new_cursor_pos.2 =
std::cmp::min(self.filtered_selection.len() - 1, self.cursor_pos.2); std::cmp::min(self.filtered_selection.len() - 1, self.cursor_pos.2);
} else { } else {
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg)
.set_bg(self.color_cache.theme_default.bg)
.set_attrs(self.color_cache.theme_default.attrs);
ret
};
self.data_columns.columns[0] = self.data_columns.columns[0] =
CellBuffer::new_with_context(0, 0, default_cell, context); CellBuffer::new_with_context(0, 0, None, context);
} }
self.redraw_threads_list( self.redraw_threads_list(
context, context,
@ -831,15 +810,8 @@ impl ListingTrait for CompactListing {
format!("Failed to search for term {}: {}", &self.filter_term, e), format!("Failed to search for term {}: {}", &self.filter_term, e),
ERROR, ERROR,
); );
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg)
.set_bg(self.color_cache.theme_default.bg)
.set_attrs(self.color_cache.theme_default.attrs);
ret
};
self.data_columns.columns[0] = self.data_columns.columns[0] =
CellBuffer::new_with_context(message.len(), 1, default_cell, context); CellBuffer::new_with_context(message.len(), 1, None, context);
write_string_to_grid( write_string_to_grid(
&message, &message,
&mut self.data_columns.columns[0], &mut self.data_columns.columns[0],

View File

@ -195,17 +195,9 @@ impl MailListingTrait for ConversationsListing {
match context.accounts[&self.cursor_pos.0].load(self.cursor_pos.1) { match context.accounts[&self.cursor_pos.0].load(self.cursor_pos.1) {
Ok(()) => {} Ok(()) => {}
Err(_) => { Err(_) => {
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg)
.set_bg(self.color_cache.theme_default.bg)
.set_attrs(self.color_cache.theme_default.attrs);
ret
};
let message: String = let message: String =
context.accounts[&self.cursor_pos.0][&self.cursor_pos.1].status(); context.accounts[&self.cursor_pos.0][&self.cursor_pos.1].status();
self.content = self.content = CellBuffer::new_with_context(message.len(), 1, None, context);
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
self.length = 0; self.length = 0;
write_string_to_grid( write_string_to_grid(
message.as_str(), message.as_str(),
@ -353,8 +345,7 @@ impl MailListingTrait for ConversationsListing {
} }
let width = max_entry_columns; let width = max_entry_columns;
self.content = self.content = CellBuffer::new_with_context(width, 4 * rows.len(), None, context);
CellBuffer::new_with_context(width, 4 * rows.len(), Cell::with_char(' '), context);
let padding_fg = self.color_cache.padding.fg; let padding_fg = self.color_cache.padding.fg;
@ -484,15 +475,8 @@ impl MailListingTrait for ConversationsListing {
} }
} }
if self.length == 0 && self.filter_term.is_empty() { if self.length == 0 && self.filter_term.is_empty() {
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg)
.set_bg(self.color_cache.theme_default.bg)
.set_attrs(self.color_cache.theme_default.attrs);
ret
};
let message: String = account[&self.cursor_pos.1].status(); let message: String = account[&self.cursor_pos.1].status();
self.content = CellBuffer::new_with_context(message.len(), 1, default_cell, context); self.content = CellBuffer::new_with_context(message.len(), 1, None, context);
write_string_to_grid( write_string_to_grid(
&message, &message,
&mut self.content, &mut self.content,
@ -820,14 +804,7 @@ impl ListingTrait for ConversationsListing {
self.new_cursor_pos.2 = self.new_cursor_pos.2 =
std::cmp::min(self.filtered_selection.len() - 1, self.cursor_pos.2); std::cmp::min(self.filtered_selection.len() - 1, self.cursor_pos.2);
} else { } else {
let default_cell = { self.content = CellBuffer::new_with_context(0, 0, None, context);
let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg)
.set_bg(self.color_cache.theme_default.bg)
.set_attrs(self.color_cache.theme_default.attrs);
ret
};
self.content = CellBuffer::new_with_context(0, 0, default_cell, context);
} }
self.redraw_threads_list( self.redraw_threads_list(
context, context,
@ -846,15 +823,7 @@ impl ListingTrait for ConversationsListing {
format!("Failed to search for term {}: {}", self.filter_term, e), format!("Failed to search for term {}: {}", self.filter_term, e),
ERROR, ERROR,
); );
let default_cell = { self.content = CellBuffer::new_with_context(message.len(), 1, None, context);
let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg)
.set_bg(self.color_cache.theme_default.bg)
.set_attrs(self.color_cache.theme_default.attrs);
ret
};
self.content =
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
write_string_to_grid( write_string_to_grid(
&message, &message,
&mut self.content, &mut self.content,

View File

@ -223,17 +223,10 @@ impl MailListingTrait for PlainListing {
match context.accounts[&self.cursor_pos.0].load(self.cursor_pos.1) { match context.accounts[&self.cursor_pos.0].load(self.cursor_pos.1) {
Ok(()) => {} Ok(()) => {}
Err(_) => { Err(_) => {
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg)
.set_bg(self.color_cache.theme_default.bg)
.set_attrs(self.color_cache.theme_default.attrs);
ret
};
let message: String = let message: String =
context.accounts[&self.cursor_pos.0][&self.cursor_pos.1].status(); context.accounts[&self.cursor_pos.0][&self.cursor_pos.1].status();
self.data_columns.columns[0] = self.data_columns.columns[0] =
CellBuffer::new_with_context(message.len(), 1, default_cell, context); CellBuffer::new_with_context(message.len(), 1, None, context);
self.length = 0; self.length = 0;
write_string_to_grid( write_string_to_grid(
message.as_str(), message.as_str(),
@ -650,15 +643,8 @@ impl ListingTrait for PlainListing {
self.new_cursor_pos.2 = self.new_cursor_pos.2 =
std::cmp::min(self.filtered_selection.len() - 1, self.cursor_pos.2); std::cmp::min(self.filtered_selection.len() - 1, self.cursor_pos.2);
} else { } else {
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg)
.set_bg(self.color_cache.theme_default.bg)
.set_attrs(self.color_cache.theme_default.attrs);
ret
};
self.data_columns.columns[0] = self.data_columns.columns[0] =
CellBuffer::new_with_context(0, 0, default_cell, context); CellBuffer::new_with_context(0, 0, None, context);
} }
self.redraw_list( self.redraw_list(
context, context,
@ -677,15 +663,8 @@ impl ListingTrait for PlainListing {
format!("Failed to search for term {}: {}", &self.filter_term, e), format!("Failed to search for term {}: {}", &self.filter_term, e),
ERROR, ERROR,
); );
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg)
.set_bg(self.color_cache.theme_default.bg)
.set_attrs(self.color_cache.theme_default.attrs);
ret
};
self.data_columns.columns[0] = self.data_columns.columns[0] =
CellBuffer::new_with_context(message.len(), 1, default_cell, context); CellBuffer::new_with_context(message.len(), 1, None, context);
write_string_to_grid( write_string_to_grid(
&message, &message,
&mut self.data_columns.columns[0], &mut self.data_columns.columns[0],
@ -851,28 +830,21 @@ impl PlainListing {
min_width.0 = self.length.saturating_sub(1).to_string().len(); min_width.0 = self.length.saturating_sub(1).to_string().len();
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg)
.set_bg(self.color_cache.theme_default.bg)
.set_attrs(self.color_cache.theme_default.attrs);
ret
};
/* index column */ /* index column */
self.data_columns.columns[0] = self.data_columns.columns[0] =
CellBuffer::new_with_context(min_width.0, rows.len(), default_cell, context); CellBuffer::new_with_context(min_width.0, rows.len(), None, context);
/* date column */ /* date column */
self.data_columns.columns[1] = self.data_columns.columns[1] =
CellBuffer::new_with_context(min_width.1, rows.len(), default_cell, context); CellBuffer::new_with_context(min_width.1, rows.len(), None, context);
/* from column */ /* from column */
self.data_columns.columns[2] = self.data_columns.columns[2] =
CellBuffer::new_with_context(min_width.2, rows.len(), default_cell, context); CellBuffer::new_with_context(min_width.2, rows.len(), None, context);
/* flags column */ /* flags column */
self.data_columns.columns[3] = self.data_columns.columns[3] =
CellBuffer::new_with_context(min_width.3, rows.len(), default_cell, context); CellBuffer::new_with_context(min_width.3, rows.len(), None, context);
/* subject column */ /* subject column */
self.data_columns.columns[4] = self.data_columns.columns[4] =
CellBuffer::new_with_context(min_width.4, rows.len(), default_cell, context); CellBuffer::new_with_context(min_width.4, rows.len(), None, context);
let iter = if self.filter_term.is_empty() { let iter = if self.filter_term.is_empty() {
Box::new(self.local_collection.iter().cloned()) Box::new(self.local_collection.iter().cloned())
@ -1005,7 +977,7 @@ impl PlainListing {
if self.length == 0 && self.filter_term.is_empty() { if self.length == 0 && self.filter_term.is_empty() {
let message: String = account[&self.cursor_pos.1].status(); let message: String = account[&self.cursor_pos.1].status();
self.data_columns.columns[0] = self.data_columns.columns[0] =
CellBuffer::new_with_context(message.len(), self.length + 1, default_cell, context); CellBuffer::new_with_context(message.len(), self.length + 1, None, context);
write_string_to_grid( write_string_to_grid(
&message, &message,
&mut self.data_columns.columns[0], &mut self.data_columns.columns[0],

View File

@ -182,17 +182,10 @@ impl MailListingTrait for ThreadListing {
match context.accounts[&self.cursor_pos.0].load(self.cursor_pos.1) { match context.accounts[&self.cursor_pos.0].load(self.cursor_pos.1) {
Ok(_) => {} Ok(_) => {}
Err(_) => { Err(_) => {
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg)
.set_bg(self.color_cache.theme_default.bg)
.set_attrs(self.color_cache.theme_default.attrs);
ret
};
let message: String = let message: String =
context.accounts[&self.cursor_pos.0][&self.cursor_pos.1].status(); context.accounts[&self.cursor_pos.0][&self.cursor_pos.1].status();
self.data_columns.columns[0] = self.data_columns.columns[0] =
CellBuffer::new_with_context(message.len(), 1, default_cell, context); CellBuffer::new_with_context(message.len(), 1, None, context);
self.length = 0; self.length = 0;
write_string_to_grid( write_string_to_grid(
message.as_str(), message.as_str(),
@ -231,17 +224,10 @@ impl MailListingTrait for ThreadListing {
let threads = account.collection.get_threads(self.cursor_pos.1); let threads = account.collection.get_threads(self.cursor_pos.1);
self.length = 0; self.length = 0;
self.order.clear(); self.order.clear();
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg)
.set_bg(self.color_cache.theme_default.bg)
.set_attrs(self.color_cache.theme_default.attrs);
ret
};
if threads.len() == 0 { if threads.len() == 0 {
let message: String = account[&self.cursor_pos.1].status(); let message: String = account[&self.cursor_pos.1].status();
self.data_columns.columns[0] = self.data_columns.columns[0] =
CellBuffer::new_with_context(message.len(), 1, default_cell, context); CellBuffer::new_with_context(message.len(), 1, None, context);
write_string_to_grid( write_string_to_grid(
message.as_str(), message.as_str(),
&mut self.data_columns.columns[0], &mut self.data_columns.columns[0],
@ -380,21 +366,21 @@ impl MailListingTrait for ThreadListing {
min_width.0 = idx.saturating_sub(1).to_string().len(); min_width.0 = idx.saturating_sub(1).to_string().len();
/* index column */ /* index column */
self.data_columns.columns[0] = self.data_columns.columns[0] =
CellBuffer::new_with_context(min_width.0, rows.len(), default_cell, context); CellBuffer::new_with_context(min_width.0, rows.len(), None, context);
/* date column */ /* date column */
self.data_columns.columns[1] = self.data_columns.columns[1] =
CellBuffer::new_with_context(min_width.1, rows.len(), default_cell, context); CellBuffer::new_with_context(min_width.1, rows.len(), None, context);
/* from column */ /* from column */
self.data_columns.columns[2] = self.data_columns.columns[2] =
CellBuffer::new_with_context(min_width.2, rows.len(), default_cell, context); CellBuffer::new_with_context(min_width.2, rows.len(), None, context);
self.data_columns.segment_tree[2] = row_widths.2.into(); self.data_columns.segment_tree[2] = row_widths.2.into();
/* flags column */ /* flags column */
self.data_columns.columns[3] = self.data_columns.columns[3] =
CellBuffer::new_with_context(min_width.3, rows.len(), default_cell, context); CellBuffer::new_with_context(min_width.3, rows.len(), None, context);
/* subject column */ /* subject column */
self.data_columns.columns[4] = self.data_columns.columns[4] =
CellBuffer::new_with_context(min_width.4, rows.len(), default_cell, context); CellBuffer::new_with_context(min_width.4, rows.len(), None, context);
self.data_columns.segment_tree[4] = row_widths.4.into(); self.data_columns.segment_tree[4] = row_widths.4.into();
self.rows = rows; self.rows = rows;

View File

@ -233,7 +233,7 @@ impl ThreadView {
e.heading = string; e.heading = string;
width = cmp::max(width, e.index.0 * 4 + e.heading.grapheme_width() + 2); width = cmp::max(width, e.index.0 * 4 + e.heading.grapheme_width() + 2);
} }
let mut content = CellBuffer::new_with_context(width, height, Cell::default(), context); let mut content = CellBuffer::new_with_context(width, height, None, context);
if self.reversed { if self.reversed {
for (y, e) in self.entries.iter().rev().enumerate() { for (y, e) in self.entries.iter().rev().enumerate() {
/* Box character drawing stuff */ /* Box character drawing stuff */

View File

@ -953,13 +953,8 @@ impl Component for Tabbed {
), ),
); );
} }
let mut empty_cell = Cell::default();
empty_cell
.set_fg(self.theme_default.fg)
.set_bg(self.theme_default.bg)
.set_attrs(self.theme_default.attrs);
self.help_content = self.help_content =
CellBuffer::new_with_context(max_width, max_length + 2, empty_cell, context); CellBuffer::new_with_context(max_width, max_length + 2, None, context);
self.help_content.set_growable(true); self.help_content.set_growable(true);
let (width, height) = self.help_content.size(); let (width, height) = self.help_content.size();
let (cols, rows) = (width!(area), height!(area)); let (cols, rows) = (width!(area), height!(area));

View File

@ -740,11 +740,6 @@ impl<T: PartialEq + Debug + Clone + Sync + Send, F: 'static + Sync + Send> Selec
context: &Context, context: &Context,
) -> Selector<T, F> { ) -> Selector<T, F> {
let theme_default = crate::conf::value(context, "theme_default"); let theme_default = crate::conf::value(context, "theme_default");
let mut empty_cell = Cell::with_char(' ');
empty_cell
.set_fg(theme_default.fg)
.set_bg(theme_default.bg)
.set_attrs(theme_default.attrs);
let width = std::cmp::max( let width = std::cmp::max(
OK_CANCEL.len(), OK_CANCEL.len(),
std::cmp::max( std::cmp::max(
@ -763,7 +758,7 @@ impl<T: PartialEq + Debug + Clone + Sync + Send, F: 'static + Sync + Send> Selec
/* Extra room for buttons Okay/Cancel */ /* Extra room for buttons Okay/Cancel */
2 2
}; };
let mut content = CellBuffer::new_with_context(width, height, empty_cell, context); let mut content = CellBuffer::new_with_context(width, height, None, context);
if single_only { if single_only {
for (i, e) in entries.iter().enumerate() { for (i, e) in entries.iter().enumerate() {
write_string_to_grid( write_string_to_grid(

View File

@ -179,12 +179,12 @@ impl Pager {
return Pager::from_buf(content, cursor_pos); return Pager::from_buf(content, cursor_pos);
} }
let content = { let content = {
let mut empty_cell = Cell::with_char(' ');
empty_cell.set_fg(colors.fg);
empty_cell.set_bg(colors.bg);
if let Some(context) = context { if let Some(context) = context {
CellBuffer::new_with_context(1, 1, empty_cell, context) CellBuffer::new_with_context(1, 1, None, context)
} else { } else {
let mut empty_cell = Cell::with_char(' ');
empty_cell.set_fg(colors.fg);
empty_cell.set_bg(colors.bg);
CellBuffer::new(1, 1, empty_cell) CellBuffer::new(1, 1, empty_cell)
} }
}; };

View File

@ -61,6 +61,7 @@ pub struct CellBuffer {
cols: usize, cols: usize,
rows: usize, rows: usize,
buf: Vec<Cell>, buf: Vec<Cell>,
pub default_cell: Cell,
/// ASCII-only flag. /// ASCII-only flag.
pub ascii_drawing: bool, pub ascii_drawing: bool,
/// If printing to this buffer and we run out of space, expand it. /// If printing to this buffer and we run out of space, expand it.
@ -99,6 +100,7 @@ impl CellBuffer {
cols, cols,
rows, rows,
buf: vec![cell; cols * rows], buf: vec![cell; cols * rows],
default_cell: cell,
growable: false, growable: false,
ascii_drawing: false, ascii_drawing: false,
tag_table: Default::default(), tag_table: Default::default(),
@ -106,11 +108,25 @@ impl CellBuffer {
} }
} }
pub fn new_with_context(cols: usize, rows: usize, cell: Cell, context: &Context) -> CellBuffer { pub fn new_with_context(
cols: usize,
rows: usize,
default_cell: Option<Cell>,
context: &Context,
) -> CellBuffer {
let default_cell = default_cell.unwrap_or_else(|| {
let mut ret = Cell::default();
let theme_default = crate::conf::value(context, "theme_default");
ret.set_fg(theme_default.fg)
.set_bg(theme_default.bg)
.set_attrs(theme_default.attrs);
ret
});
CellBuffer { CellBuffer {
cols, cols,
rows, rows,
buf: vec![cell; cols * rows], buf: vec![default_cell; cols * rows],
default_cell,
growable: false, growable: false,
ascii_drawing: context.settings.terminal.ascii_drawing, ascii_drawing: context.settings.terminal.ascii_drawing,
tag_table: Default::default(), tag_table: Default::default(),
@ -2301,6 +2317,7 @@ pub mod ansi {
buf, buf,
rows, rows,
cols: max_cols, cols: max_cols,
default_cell: Cell::default(),
growable: false, growable: false,
ascii_drawing: false, ascii_drawing: false,
tag_table: Default::default(), tag_table: Default::default(),