|
|
|
@ -114,7 +114,7 @@ pub struct ConversationsListing {
|
|
|
|
|
dirty: bool,
|
|
|
|
|
force_draw: bool,
|
|
|
|
|
/// If `self.view` exists or not.
|
|
|
|
|
unfocused: bool,
|
|
|
|
|
focus: Focus,
|
|
|
|
|
view: ThreadView,
|
|
|
|
|
row_updates: SmallVec<[ThreadHash; 8]>,
|
|
|
|
|
color_cache: ColorCache,
|
|
|
|
@ -215,7 +215,7 @@ impl MailListingTrait for ConversationsListing {
|
|
|
|
|
if !force && old_cursor_pos == self.new_cursor_pos && old_mailbox_hash == self.cursor_pos.1
|
|
|
|
|
{
|
|
|
|
|
self.view.update(context);
|
|
|
|
|
} else if self.unfocused {
|
|
|
|
|
} else if self.unfocused() {
|
|
|
|
|
let thread_group = self.get_thread_under_cursor(self.cursor_pos.2);
|
|
|
|
|
|
|
|
|
|
self.view = ThreadView::new(self.new_cursor_pos, thread_group, None, context);
|
|
|
|
@ -352,7 +352,7 @@ impl ListingTrait for ConversationsListing {
|
|
|
|
|
|
|
|
|
|
fn set_coordinates(&mut self, coordinates: (AccountHash, MailboxHash)) {
|
|
|
|
|
self.new_cursor_pos = (coordinates.0, coordinates.1, 0);
|
|
|
|
|
self.unfocused = false;
|
|
|
|
|
self.focus = Focus::None;
|
|
|
|
|
self.view = ThreadView::default();
|
|
|
|
|
self.filtered_selection.clear();
|
|
|
|
|
self.filtered_order.clear();
|
|
|
|
@ -537,7 +537,7 @@ impl ListingTrait for ConversationsListing {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn unfocused(&self) -> bool {
|
|
|
|
|
self.unfocused
|
|
|
|
|
!matches!(self.focus, Focus::None)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_modifier_active(&mut self, new_val: bool) {
|
|
|
|
@ -556,6 +556,33 @@ impl ListingTrait for ConversationsListing {
|
|
|
|
|
self.movement = Some(mvm);
|
|
|
|
|
self.set_dirty(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_focus(&mut self, new_value: Focus, context: &mut Context) {
|
|
|
|
|
match new_value {
|
|
|
|
|
Focus::None => {
|
|
|
|
|
self.view
|
|
|
|
|
.process_event(&mut UIEvent::VisibilityChange(false), context);
|
|
|
|
|
self.dirty = true;
|
|
|
|
|
/* If self.row_updates is not empty and we exit a thread, the row_update events
|
|
|
|
|
* will be performed but the list will not be drawn. So force a draw in any case.
|
|
|
|
|
* */
|
|
|
|
|
self.force_draw = true;
|
|
|
|
|
}
|
|
|
|
|
Focus::Entry => {
|
|
|
|
|
self.force_draw = true;
|
|
|
|
|
self.dirty = true;
|
|
|
|
|
self.view.set_dirty(true);
|
|
|
|
|
}
|
|
|
|
|
Focus::EntryFullscreen => {
|
|
|
|
|
self.view.set_dirty(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
self.focus = new_value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn focus(&self) -> Focus {
|
|
|
|
|
self.focus
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for ConversationsListing {
|
|
|
|
@ -569,7 +596,7 @@ impl ConversationsListing {
|
|
|
|
|
//const PADDING_CHAR: char = ' '; //░';
|
|
|
|
|
|
|
|
|
|
pub fn new(coordinates: (AccountHash, MailboxHash)) -> Box<Self> {
|
|
|
|
|
Box::new(ConversationsListing {
|
|
|
|
|
Box::new(Self {
|
|
|
|
|
cursor_pos: (coordinates.0, 1, 0),
|
|
|
|
|
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
|
|
|
|
length: 0,
|
|
|
|
@ -586,7 +613,7 @@ impl ConversationsListing {
|
|
|
|
|
rows: Ok(Vec::with_capacity(1024)),
|
|
|
|
|
dirty: true,
|
|
|
|
|
force_draw: true,
|
|
|
|
|
unfocused: false,
|
|
|
|
|
focus: Focus::None,
|
|
|
|
|
view: ThreadView::default(),
|
|
|
|
|
color_cache: ColorCache::default(),
|
|
|
|
|
movement: None,
|
|
|
|
@ -907,6 +934,11 @@ impl Component for ConversationsListing {
|
|
|
|
|
if !self.is_dirty() {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if matches!(self.focus, Focus::EntryFullscreen) {
|
|
|
|
|
return self.view.draw(grid, area, context);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let (upper_left, bottom_right) = area;
|
|
|
|
|
{
|
|
|
|
|
let mut area = area;
|
|
|
|
@ -1142,7 +1174,7 @@ impl Component for ConversationsListing {
|
|
|
|
|
self.draw_list(grid, area, context);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if self.unfocused {
|
|
|
|
|
if matches!(self.focus, Focus::Entry) {
|
|
|
|
|
if self.length == 0 && self.dirty {
|
|
|
|
|
clear_area(grid, area, self.color_cache.theme_default);
|
|
|
|
|
context.dirty_areas.push_back(area);
|
|
|
|
@ -1157,40 +1189,81 @@ impl Component for ConversationsListing {
|
|
|
|
|
}
|
|
|
|
|
self.dirty = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
|
|
|
|
|
if self.unfocused && self.view.process_event(event, context) {
|
|
|
|
|
let shortcuts = self.get_shortcuts(context);
|
|
|
|
|
|
|
|
|
|
match (&event, self.focus) {
|
|
|
|
|
(UIEvent::Input(ref k), Focus::Entry)
|
|
|
|
|
if shortcut!(k == shortcuts[Listing::DESCRIPTION]["focus_right"]) =>
|
|
|
|
|
{
|
|
|
|
|
self.set_focus(Focus::EntryFullscreen, context);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
(UIEvent::Input(ref k), Focus::EntryFullscreen)
|
|
|
|
|
if shortcut!(k == shortcuts[Listing::DESCRIPTION]["focus_left"]) =>
|
|
|
|
|
{
|
|
|
|
|
self.set_focus(Focus::Entry, context);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
(UIEvent::Input(ref k), Focus::Entry)
|
|
|
|
|
if shortcut!(k == shortcuts[Listing::DESCRIPTION]["focus_left"]) =>
|
|
|
|
|
{
|
|
|
|
|
self.set_focus(Focus::None, context);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if self.unfocused() && self.view.process_event(event, context) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let shortcuts = self.get_shortcuts(context);
|
|
|
|
|
if self.length > 0 {
|
|
|
|
|
match *event {
|
|
|
|
|
UIEvent::Input(ref k)
|
|
|
|
|
if !self.unfocused
|
|
|
|
|
&& shortcut!(k == shortcuts[Listing::DESCRIPTION]["open_entry"]) =>
|
|
|
|
|
if matches!(self.focus, Focus::None)
|
|
|
|
|
&& (shortcut!(k == shortcuts[Listing::DESCRIPTION]["open_entry"])
|
|
|
|
|
|| shortcut!(k == shortcuts[Listing::DESCRIPTION]["focus_right"])) =>
|
|
|
|
|
{
|
|
|
|
|
let thread = self.get_thread_under_cursor(self.cursor_pos.2);
|
|
|
|
|
self.view = ThreadView::new(self.cursor_pos, thread, None, context);
|
|
|
|
|
self.unfocused = true;
|
|
|
|
|
self.dirty = true;
|
|
|
|
|
self.set_focus(Focus::Entry, context);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
UIEvent::Input(ref k)
|
|
|
|
|
if self.unfocused
|
|
|
|
|
if !matches!(self.focus, Focus::None)
|
|
|
|
|
&& shortcut!(k == shortcuts[Listing::DESCRIPTION]["exit_entry"]) =>
|
|
|
|
|
{
|
|
|
|
|
self.unfocused = false;
|
|
|
|
|
self.view
|
|
|
|
|
.process_event(&mut UIEvent::VisibilityChange(false), context);
|
|
|
|
|
self.dirty = true;
|
|
|
|
|
/* If self.row_updates is not empty and we exit a thread, the row_update events
|
|
|
|
|
* will be performed but the list will not be drawn. So force a draw in any case.
|
|
|
|
|
* */
|
|
|
|
|
self.force_draw = true;
|
|
|
|
|
self.set_focus(Focus::None, context);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
UIEvent::Input(ref k)
|
|
|
|
|
if matches!(self.focus, Focus::Entry)
|
|
|
|
|
&& shortcut!(k == shortcuts[Listing::DESCRIPTION]["focus_right"]) =>
|
|
|
|
|
{
|
|
|
|
|
self.set_focus(Focus::EntryFullscreen, context);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
UIEvent::Input(ref k)
|
|
|
|
|
if !matches!(self.focus, Focus::None)
|
|
|
|
|
&& shortcut!(k == shortcuts[Listing::DESCRIPTION]["focus_left"]) =>
|
|
|
|
|
{
|
|
|
|
|
match self.focus {
|
|
|
|
|
Focus::Entry => {
|
|
|
|
|
self.set_focus(Focus::None, context);
|
|
|
|
|
}
|
|
|
|
|
Focus::EntryFullscreen => {
|
|
|
|
|
self.set_focus(Focus::Entry, context);
|
|
|
|
|
}
|
|
|
|
|
Focus::None => {
|
|
|
|
|
unreachable!();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
UIEvent::Input(ref key)
|
|
|
|
|
if !self.unfocused
|
|
|
|
|
if !self.unfocused()
|
|
|
|
|
&& shortcut!(key == shortcuts[Listing::DESCRIPTION]["select_entry"]) =>
|
|
|
|
|
{
|
|
|
|
|
if self.modifier_active && self.modifier_command.is_none() {
|
|
|
|
@ -1221,7 +1294,7 @@ impl Component for ConversationsListing {
|
|
|
|
|
|
|
|
|
|
self.dirty = true;
|
|
|
|
|
|
|
|
|
|
if self.unfocused {
|
|
|
|
|
if self.unfocused() {
|
|
|
|
|
self.view.process_event(
|
|
|
|
|
&mut UIEvent::EnvelopeRename(*old_hash, *new_hash),
|
|
|
|
|
context,
|
|
|
|
@ -1253,13 +1326,13 @@ impl Component for ConversationsListing {
|
|
|
|
|
|
|
|
|
|
self.dirty = true;
|
|
|
|
|
|
|
|
|
|
if self.unfocused {
|
|
|
|
|
if self.unfocused() {
|
|
|
|
|
self.view
|
|
|
|
|
.process_event(&mut UIEvent::EnvelopeUpdate(*env_hash), context);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
UIEvent::Action(ref action) => match action {
|
|
|
|
|
Action::SubSort(field, order) if !self.unfocused => {
|
|
|
|
|
Action::SubSort(field, order) if !self.unfocused() => {
|
|
|
|
|
debug!("SubSort {:?} , {:?}", field, order);
|
|
|
|
|
self.subsort = (*field, *order);
|
|
|
|
|
// FIXME subsort
|
|
|
|
@ -1271,7 +1344,7 @@ impl Component for ConversationsListing {
|
|
|
|
|
//}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
Action::Sort(field, order) if !self.unfocused => {
|
|
|
|
|
Action::Sort(field, order) if !self.unfocused() => {
|
|
|
|
|
debug!("Sort {:?} , {:?}", field, order);
|
|
|
|
|
// FIXME sort
|
|
|
|
|
/*
|
|
|
|
@ -1291,7 +1364,7 @@ impl Component for ConversationsListing {
|
|
|
|
|
*/
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
Action::Listing(ToggleThreadSnooze) if !self.unfocused => {
|
|
|
|
|
Action::Listing(ToggleThreadSnooze) if !self.unfocused() => {
|
|
|
|
|
let thread = self.get_thread_under_cursor(self.cursor_pos.2);
|
|
|
|
|
let account = &mut context.accounts[&self.cursor_pos.0];
|
|
|
|
|
account
|
|
|
|
@ -1359,7 +1432,7 @@ impl Component for ConversationsListing {
|
|
|
|
|
self.dirty = true;
|
|
|
|
|
}
|
|
|
|
|
UIEvent::Action(ref action) => match action {
|
|
|
|
|
Action::Listing(Search(ref filter_term)) if !self.unfocused => {
|
|
|
|
|
Action::Listing(Search(ref filter_term)) if !self.unfocused() => {
|
|
|
|
|
match context.accounts[&self.cursor_pos.0].search(
|
|
|
|
|
filter_term,
|
|
|
|
|
self.sort,
|
|
|
|
@ -1385,7 +1458,7 @@ impl Component for ConversationsListing {
|
|
|
|
|
_ => {}
|
|
|
|
|
},
|
|
|
|
|
UIEvent::Input(Key::Esc)
|
|
|
|
|
if !self.unfocused
|
|
|
|
|
if !self.unfocused()
|
|
|
|
|
&& self.selection.values().cloned().any(std::convert::identity) =>
|
|
|
|
|
{
|
|
|
|
|
for (k, v) in self.selection.iter_mut() {
|
|
|
|
@ -1398,7 +1471,7 @@ impl Component for ConversationsListing {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
UIEvent::Input(Key::Esc) | UIEvent::Input(Key::Char(''))
|
|
|
|
|
if !self.unfocused && !&self.filter_term.is_empty() =>
|
|
|
|
|
if !self.unfocused() && !&self.filter_term.is_empty() =>
|
|
|
|
|
{
|
|
|
|
|
self.set_coordinates((self.new_cursor_pos.0, self.new_cursor_pos.1));
|
|
|
|
|
self.refresh_mailbox(context, false);
|
|
|
|
@ -1432,23 +1505,24 @@ impl Component for ConversationsListing {
|
|
|
|
|
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn is_dirty(&self) -> bool {
|
|
|
|
|
self.dirty
|
|
|
|
|
|| if self.unfocused {
|
|
|
|
|
self.view.is_dirty()
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
match self.focus {
|
|
|
|
|
Focus::None => self.dirty,
|
|
|
|
|
Focus::Entry => self.dirty || self.view.is_dirty(),
|
|
|
|
|
Focus::EntryFullscreen => self.view.is_dirty(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_dirty(&mut self, value: bool) {
|
|
|
|
|
if self.unfocused {
|
|
|
|
|
if self.unfocused() {
|
|
|
|
|
self.view.set_dirty(value);
|
|
|
|
|
}
|
|
|
|
|
self.dirty = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
|
|
|
|
let mut map = if self.unfocused {
|
|
|
|
|
let mut map = if self.unfocused() {
|
|
|
|
|
self.view.get_shortcuts(context)
|
|
|
|
|
} else {
|
|
|
|
|
ShortcutMaps::default()
|
|
|
|
|