listing: add focus_{left,right} shortcuts to switch focus
This allows you to make the mail entry column occupy the whole screen if you press focus_right (Right key) twice.pull/150/head
parent
3d92b41075
commit
9dc4d4055c
|
@ -9,8 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
### Added
|
||||
|
||||
- Added shortcuts for focusing to sidebar menu and back to the e-mail view (`focus_left` and `focus_right`)
|
||||
- `f76f4ea3` A new manual page, `meli.7` which contains a general tutorial for using meli.
|
||||
- `f76f4ea3` Added shortcuts for focusing to sidebar menu and back to the e-mail view (`focus_on_menu` and `focus_on_list`)
|
||||
- `cbe593cf` add configurable header preample suffix and prefix for editing
|
||||
- `a484b397` Added instructions and information to error shown when libnotmuch could not be found.
|
||||
- `a484b397` Added configuration setting `library_file_path` to notmuch backend if user wants to specify the library's location manually.
|
||||
|
|
|
@ -233,10 +233,10 @@ Press
|
|||
to toggle the sidebars visibility.
|
||||
.Pp
|
||||
Press
|
||||
.Shortcut Left listing focus_on_menu
|
||||
.Shortcut Left listing focus_right
|
||||
to switch focus on the sidebar menu.
|
||||
Press
|
||||
.Shortcut Right listing focus_on_list
|
||||
.Shortcut Right listing focus_left
|
||||
to switch focus on the e-mail list.
|
||||
.Pp
|
||||
On the e-mail list, press
|
||||
|
|
|
@ -781,6 +781,14 @@ Decrease sidebar width.
|
|||
Toggle visibility of side menu in mail list.
|
||||
.\" default value
|
||||
.Pq Em `
|
||||
.It Ic focus_left
|
||||
Switch focus on the left.
|
||||
.\" default value
|
||||
.Pq Em Left
|
||||
.It Ic focus_right
|
||||
Switch focus on the right.
|
||||
.\" default value
|
||||
.Pq Em Right
|
||||
.It Ic exit_entry
|
||||
Exit e-mail entry.
|
||||
.\" default value
|
||||
|
|
|
@ -61,6 +61,13 @@ pub use self::plain::*;
|
|||
mod offline;
|
||||
pub use self::offline::*;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Focus {
|
||||
None,
|
||||
Entry,
|
||||
EntryFullscreen,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, PartialEq, Clone)]
|
||||
pub enum Modifier {
|
||||
SymmetricDifference,
|
||||
|
@ -516,6 +523,8 @@ pub trait ListingTrait: Component {
|
|||
None
|
||||
}
|
||||
fn set_movement(&mut self, mvm: PageMovement);
|
||||
fn focus(&self) -> Focus;
|
||||
fn set_focus(&mut self, new_value: Focus, context: &mut Context);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -655,7 +664,7 @@ impl Component for Listing {
|
|||
let bottom_right = bottom_right!(area);
|
||||
let total_cols = get_x(bottom_right) - get_x(upper_left);
|
||||
|
||||
let right_component_width = if self.menu_visibility {
|
||||
let right_component_width = if self.is_menu_visible() {
|
||||
if self.focus == ListingFocus::Menu {
|
||||
(self.ratio * total_cols) / 100
|
||||
} else {
|
||||
|
@ -925,7 +934,7 @@ impl Component for Listing {
|
|||
if self.focus == ListingFocus::Mailbox {
|
||||
match *event {
|
||||
UIEvent::Input(Key::Mouse(MouseEvent::Press(MouseButton::Left, x, _y)))
|
||||
if self.menu_visibility =>
|
||||
if self.is_menu_visible() =>
|
||||
{
|
||||
match self.menu_width {
|
||||
WidgetWidth::Hold(wx) | WidgetWidth::Set(wx)
|
||||
|
@ -942,7 +951,7 @@ impl Component for Listing {
|
|||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(Key::Mouse(MouseEvent::Hold(x, _y))) if self.menu_visibility => {
|
||||
UIEvent::Input(Key::Mouse(MouseEvent::Hold(x, _y))) if self.is_menu_visible() => {
|
||||
match self.menu_width {
|
||||
WidgetWidth::Hold(ref mut hx) => {
|
||||
*hx = usize::from(x).saturating_sub(1);
|
||||
|
@ -952,7 +961,9 @@ impl Component for Listing {
|
|||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(Key::Mouse(MouseEvent::Release(x, _y))) if self.menu_visibility => {
|
||||
UIEvent::Input(Key::Mouse(MouseEvent::Release(x, _y)))
|
||||
if self.is_menu_visible() =>
|
||||
{
|
||||
match self.menu_width {
|
||||
WidgetWidth::Hold(_) => {
|
||||
self.menu_width = WidgetWidth::Set(usize::from(x).saturating_sub(1));
|
||||
|
@ -963,8 +974,8 @@ impl Component for Listing {
|
|||
return true;
|
||||
}
|
||||
UIEvent::Input(ref k)
|
||||
if self.menu_visibility
|
||||
&& shortcut!(k == shortcuts[Listing::DESCRIPTION]["focus_on_menu"]) =>
|
||||
if self.is_menu_visible()
|
||||
&& shortcut!(k == shortcuts[Listing::DESCRIPTION]["focus_left"]) =>
|
||||
{
|
||||
self.focus = ListingFocus::Menu;
|
||||
if self.show_menu_scrollbar != ShowMenuScrollbar::Never {
|
||||
|
@ -1337,7 +1348,7 @@ impl Component for Listing {
|
|||
} else if self.focus == ListingFocus::Menu {
|
||||
match *event {
|
||||
UIEvent::Input(ref k)
|
||||
if shortcut!(k == shortcuts[Listing::DESCRIPTION]["focus_on_list"]) =>
|
||||
if shortcut!(k == shortcuts[Listing::DESCRIPTION]["focus_right"]) =>
|
||||
{
|
||||
self.focus = ListingFocus::Mailbox;
|
||||
context
|
||||
|
@ -2374,4 +2385,8 @@ impl Listing {
|
|||
self.get_status(context),
|
||||
)));
|
||||
}
|
||||
|
||||
fn is_menu_visible(&self) -> bool {
|
||||
!matches!(self.component.focus(), Focus::EntryFullscreen) && self.menu_visibility
|
||||
}
|
||||
}
|
||||
|
|
|
@ -187,7 +187,7 @@ pub struct CompactListing {
|
|||
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,
|
||||
|
@ -304,7 +304,7 @@ impl MailListingTrait for CompactListing {
|
|||
|
||||
if !force && old_cursor_pos == self.new_cursor_pos {
|
||||
self.view.update(context);
|
||||
} else if self.unfocused {
|
||||
} else if self.unfocused() {
|
||||
let thread = self.get_thread_under_cursor(self.cursor_pos.2);
|
||||
|
||||
self.view = ThreadView::new(self.new_cursor_pos, thread, None, context);
|
||||
|
@ -490,7 +490,7 @@ impl ListingTrait for CompactListing {
|
|||
|
||||
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();
|
||||
|
@ -818,7 +818,7 @@ impl ListingTrait for CompactListing {
|
|||
}
|
||||
|
||||
fn unfocused(&self) -> bool {
|
||||
self.unfocused
|
||||
!matches!(self.focus, Focus::None)
|
||||
}
|
||||
|
||||
fn set_modifier_active(&mut self, new_val: bool) {
|
||||
|
@ -837,6 +837,33 @@ impl ListingTrait for CompactListing {
|
|||
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 CompactListing {
|
||||
|
@ -863,13 +890,13 @@ impl CompactListing {
|
|||
filtered_selection: Vec::new(),
|
||||
filtered_order: HashMap::default(),
|
||||
selection: HashMap::default(),
|
||||
focus: Focus::None,
|
||||
row_updates: SmallVec::new(),
|
||||
data_columns: DataColumns::default(),
|
||||
rows_drawn: SegmentTree::default(),
|
||||
rows: vec![],
|
||||
dirty: true,
|
||||
force_draw: true,
|
||||
unfocused: false,
|
||||
view: ThreadView::default(),
|
||||
color_cache: ColorCache::default(),
|
||||
movement: None,
|
||||
|
@ -1465,10 +1492,15 @@ impl CompactListing {
|
|||
|
||||
impl Component for CompactListing {
|
||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
if !self.unfocused {
|
||||
if !self.is_dirty() {
|
||||
return;
|
||||
}
|
||||
|
||||
if matches!(self.focus, Focus::EntryFullscreen) {
|
||||
return self.view.draw(grid, area, context);
|
||||
}
|
||||
|
||||
if !self.unfocused() {
|
||||
let mut area = area;
|
||||
if !self.filter_term.is_empty() {
|
||||
let (upper_left, bottom_right) = area;
|
||||
|
@ -1715,40 +1747,81 @@ impl Component for CompactListing {
|
|||
}
|
||||
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::Entry)
|
||||
&& 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::None)
|
||||
&& shortcut!(k == shortcuts[Listing::DESCRIPTION]["focus_right"]) =>
|
||||
{
|
||||
self.set_focus(Focus::Entry, 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() {
|
||||
|
@ -1762,7 +1835,7 @@ impl Component for CompactListing {
|
|||
}
|
||||
UIEvent::Action(ref action) => {
|
||||
match action {
|
||||
Action::Sort(field, order) if !self.unfocused => {
|
||||
Action::Sort(field, order) if !self.unfocused() => {
|
||||
debug!("Sort {:?} , {:?}", field, order);
|
||||
self.sort = (*field, *order);
|
||||
self.sortcmd = true;
|
||||
|
@ -1774,13 +1847,13 @@ impl Component for CompactListing {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
Action::SubSort(field, order) if !self.unfocused => {
|
||||
Action::SubSort(field, order) if !self.unfocused() => {
|
||||
debug!("SubSort {:?} , {:?}", field, order);
|
||||
self.subsort = (*field, *order);
|
||||
// FIXME: perform subsort.
|
||||
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
|
||||
|
@ -1871,7 +1944,7 @@ impl Component for CompactListing {
|
|||
|
||||
self.dirty = true;
|
||||
|
||||
if self.unfocused {
|
||||
if self.unfocused() {
|
||||
self.view
|
||||
.process_event(&mut UIEvent::EnvelopeRename(*old_hash, *new_hash), context);
|
||||
}
|
||||
|
@ -1901,7 +1974,7 @@ impl Component for CompactListing {
|
|||
|
||||
self.dirty = true;
|
||||
|
||||
if self.unfocused {
|
||||
if self.unfocused() {
|
||||
self.view
|
||||
.process_event(&mut UIEvent::EnvelopeUpdate(*env_hash), context);
|
||||
}
|
||||
|
@ -1913,7 +1986,7 @@ impl Component for CompactListing {
|
|||
self.dirty = true;
|
||||
}
|
||||
UIEvent::Input(Key::Esc)
|
||||
if !self.unfocused
|
||||
if !self.unfocused()
|
||||
&& self.selection.values().cloned().any(std::convert::identity) =>
|
||||
{
|
||||
for v in self.selection.values_mut() {
|
||||
|
@ -1922,13 +1995,13 @@ impl Component for CompactListing {
|
|||
self.dirty = true;
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(Key::Esc) if !self.unfocused && !self.filter_term.is_empty() => {
|
||||
UIEvent::Input(Key::Esc) 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);
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
UIEvent::Action(Action::Listing(Search(ref filter_term))) if !self.unfocused => {
|
||||
UIEvent::Action(Action::Listing(Search(ref filter_term))) if !self.unfocused() => {
|
||||
match context.accounts[&self.cursor_pos.0].search(
|
||||
filter_term,
|
||||
self.sort,
|
||||
|
@ -1950,7 +2023,7 @@ impl Component for CompactListing {
|
|||
};
|
||||
self.set_dirty(true);
|
||||
}
|
||||
UIEvent::Action(Action::Listing(Select(ref search_term))) if !self.unfocused => {
|
||||
UIEvent::Action(Action::Listing(Select(ref search_term))) if !self.unfocused() => {
|
||||
match context.accounts[&self.cursor_pos.0].search(
|
||||
search_term,
|
||||
self.sort,
|
||||
|
@ -2017,23 +2090,24 @@ impl Component for CompactListing {
|
|||
}
|
||||
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) {
|
||||
self.dirty = value;
|
||||
if self.unfocused {
|
||||
if self.unfocused() {
|
||||
self.view.set_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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -84,6 +84,12 @@ impl ListingTrait for OfflineListing {
|
|||
}
|
||||
|
||||
fn set_movement(&mut self, _: PageMovement) {}
|
||||
|
||||
fn focus(&self) -> Focus {
|
||||
Focus::None
|
||||
}
|
||||
|
||||
fn set_focus(&mut self, _new_value: Focus, _context: &mut Context) {}
|
||||
}
|
||||
|
||||
impl fmt::Display for OfflineListing {
|
||||
|
|
|
@ -146,7 +146,7 @@ pub struct PlainListing {
|
|||
dirty: bool,
|
||||
force_draw: bool,
|
||||
/// If `self.view` exists or not.
|
||||
unfocused: bool,
|
||||
focus: Focus,
|
||||
view: MailView,
|
||||
row_updates: SmallVec<[EnvelopeHash; 8]>,
|
||||
_row_updates: SmallVec<[ThreadHash; 8]>,
|
||||
|
@ -296,7 +296,7 @@ impl MailListingTrait for PlainListing {
|
|||
let temp = (self.new_cursor_pos.0, self.new_cursor_pos.1, env_hash);
|
||||
if !force && old_cursor_pos == self.new_cursor_pos {
|
||||
self.view.update(temp, context);
|
||||
} else if self.unfocused {
|
||||
} else if self.unfocused() {
|
||||
self.view = MailView::new(temp, None, None, context);
|
||||
}
|
||||
}
|
||||
|
@ -335,7 +335,7 @@ impl ListingTrait for PlainListing {
|
|||
|
||||
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 = MailView::default();
|
||||
self.filtered_selection.clear();
|
||||
self.filtered_order.clear();
|
||||
|
@ -641,13 +641,44 @@ impl ListingTrait for PlainListing {
|
|||
}
|
||||
|
||||
fn unfocused(&self) -> bool {
|
||||
self.unfocused
|
||||
!matches!(self.focus, Focus::None)
|
||||
}
|
||||
|
||||
fn set_movement(&mut self, mvm: PageMovement) {
|
||||
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 => {
|
||||
let env_hash = self.get_env_under_cursor(self.cursor_pos.2, context);
|
||||
let temp = (self.cursor_pos.0, self.cursor_pos.1, env_hash);
|
||||
self.view = MailView::new(temp, None, None, context);
|
||||
self.force_draw = true;
|
||||
self.dirty = true;
|
||||
self.view.set_dirty(true);
|
||||
}
|
||||
Focus::EntryFullscreen => {
|
||||
self.dirty = true;
|
||||
self.view.set_dirty(true);
|
||||
}
|
||||
}
|
||||
self.focus = new_value;
|
||||
}
|
||||
|
||||
fn focus(&self) -> Focus {
|
||||
self.focus
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PlainListing {
|
||||
|
@ -680,7 +711,7 @@ impl PlainListing {
|
|||
data_columns: DataColumns::default(),
|
||||
dirty: true,
|
||||
force_draw: true,
|
||||
unfocused: false,
|
||||
focus: Focus::None,
|
||||
view: MailView::default(),
|
||||
color_cache: ColorCache::default(),
|
||||
active_jobs: HashMap::default(),
|
||||
|
@ -1060,10 +1091,15 @@ impl PlainListing {
|
|||
|
||||
impl Component for PlainListing {
|
||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
if !self.unfocused {
|
||||
if !self.is_dirty() {
|
||||
return;
|
||||
}
|
||||
|
||||
if matches!(self.focus, Focus::EntryFullscreen) {
|
||||
return self.view.draw(grid, area, context);
|
||||
}
|
||||
|
||||
if matches!(self.focus, Focus::None) {
|
||||
let mut area = area;
|
||||
if !self.filter_term.is_empty() {
|
||||
let (upper_left, bottom_right) = area;
|
||||
|
@ -1129,48 +1165,79 @@ impl Component for PlainListing {
|
|||
}
|
||||
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 env_hash = self.get_env_under_cursor(self.cursor_pos.2, context);
|
||||
let temp = (self.cursor_pos.0, self.cursor_pos.1, env_hash);
|
||||
self.view = MailView::new(temp, None, 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::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"]) =>
|
||||
{
|
||||
let env_hash = self.get_env_under_cursor(self.cursor_pos.2, context);
|
||||
self.selection.entry(env_hash).and_modify(|e| *e = !*e);
|
||||
}
|
||||
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);
|
||||
//if !self.filtered_selection.is_empty() {
|
||||
|
@ -1181,7 +1248,7 @@ impl Component for PlainListing {
|
|||
//}
|
||||
return true;
|
||||
}
|
||||
Action::Sort(field, order) if !self.unfocused => {
|
||||
Action::Sort(field, order) if !self.unfocused() => {
|
||||
debug!("Sort {:?} , {:?}", field, order);
|
||||
self.sort = (*field, *order);
|
||||
return true;
|
||||
|
@ -1189,7 +1256,7 @@ impl Component for PlainListing {
|
|||
Action::Listing(a @ ListingAction::SetSeen)
|
||||
| Action::Listing(a @ ListingAction::SetUnseen)
|
||||
| Action::Listing(a @ ListingAction::Delete)
|
||||
if !self.unfocused =>
|
||||
if !self.unfocused() =>
|
||||
{
|
||||
let is_selection_empty =
|
||||
self.selection.values().cloned().any(std::convert::identity);
|
||||
|
@ -1295,7 +1362,7 @@ impl Component for PlainListing {
|
|||
|
||||
self.dirty = true;
|
||||
|
||||
if self.unfocused {
|
||||
if self.unfocused() {
|
||||
self.view
|
||||
.process_event(&mut UIEvent::EnvelopeRename(*old_hash, *new_hash), context);
|
||||
}
|
||||
|
@ -1314,7 +1381,7 @@ impl Component for PlainListing {
|
|||
self.row_updates.push(*env_hash);
|
||||
self.dirty = true;
|
||||
|
||||
if self.unfocused {
|
||||
if self.unfocused() {
|
||||
self.view
|
||||
.process_event(&mut UIEvent::EnvelopeUpdate(*env_hash), context);
|
||||
}
|
||||
|
@ -1326,7 +1393,7 @@ impl Component for PlainListing {
|
|||
self.dirty = true;
|
||||
}
|
||||
UIEvent::Input(Key::Esc)
|
||||
if !self.unfocused
|
||||
if !self.unfocused()
|
||||
&& self.selection.values().cloned().any(std::convert::identity) =>
|
||||
{
|
||||
for v in self.selection.values_mut() {
|
||||
|
@ -1335,13 +1402,13 @@ impl Component for PlainListing {
|
|||
self.dirty = true;
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(Key::Esc) if !self.unfocused && !self.filter_term.is_empty() => {
|
||||
UIEvent::Input(Key::Esc) if !self.unfocused() && !self.filter_term.is_empty() => {
|
||||
self.set_coordinates((self.new_cursor_pos.0, self.new_cursor_pos.1));
|
||||
self.set_dirty(true);
|
||||
self.refresh_mailbox(context, false);
|
||||
return true;
|
||||
}
|
||||
UIEvent::Action(Action::Listing(Search(ref filter_term))) if !self.unfocused => {
|
||||
UIEvent::Action(Action::Listing(Search(ref filter_term))) if !self.unfocused() => {
|
||||
match context.accounts[&self.cursor_pos.0].search(
|
||||
filter_term,
|
||||
self.sort,
|
||||
|
@ -1389,23 +1456,23 @@ impl Component for PlainListing {
|
|||
}
|
||||
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 | Focus::EntryFullscreen => self.view.is_dirty(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_dirty(&mut self, value: bool) {
|
||||
self.dirty = value;
|
||||
if self.unfocused {
|
||||
if self.unfocused() {
|
||||
self.view.set_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()
|
||||
|
|
|
@ -128,7 +128,7 @@ pub struct ThreadListing {
|
|||
/// If we must redraw on next redraw event
|
||||
dirty: bool,
|
||||
/// If `self.view` is focused or not.
|
||||
unfocused: bool,
|
||||
focus: Focus,
|
||||
initialised: bool,
|
||||
view: Option<MailView>,
|
||||
movement: Option<PageMovement>,
|
||||
|
@ -410,9 +410,10 @@ impl ListingTrait for ThreadListing {
|
|||
fn coordinates(&self) -> (AccountHash, MailboxHash) {
|
||||
(self.new_cursor_pos.0, self.new_cursor_pos.1)
|
||||
}
|
||||
|
||||
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 = None;
|
||||
self.order.clear();
|
||||
self.row_updates.clear();
|
||||
|
@ -741,13 +742,55 @@ impl ListingTrait for ThreadListing {
|
|||
}
|
||||
|
||||
fn unfocused(&self) -> bool {
|
||||
self.unfocused
|
||||
!matches!(self.focus, Focus::None)
|
||||
}
|
||||
|
||||
fn set_movement(&mut self, mvm: PageMovement) {
|
||||
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 = None;
|
||||
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;
|
||||
let coordinates = (
|
||||
self.cursor_pos.0,
|
||||
self.cursor_pos.1,
|
||||
self.get_env_under_cursor(self.cursor_pos.2, context),
|
||||
);
|
||||
|
||||
if let Some(ref mut v) = self.view {
|
||||
v.update(coordinates, context);
|
||||
} else {
|
||||
self.view = Some(MailView::new(coordinates, None, None, context));
|
||||
}
|
||||
|
||||
if let Some(ref mut s) = self.view {
|
||||
s.set_dirty(true);
|
||||
}
|
||||
}
|
||||
Focus::EntryFullscreen => {
|
||||
if let Some(ref mut s) = self.view {
|
||||
s.set_dirty(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.focus = new_value;
|
||||
}
|
||||
|
||||
fn focus(&self) -> Focus {
|
||||
self.focus
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ThreadListing {
|
||||
|
@ -772,7 +815,7 @@ impl ThreadListing {
|
|||
selection: HashMap::default(),
|
||||
order: HashMap::default(),
|
||||
dirty: true,
|
||||
unfocused: false,
|
||||
focus: Focus::None,
|
||||
view: None,
|
||||
initialised: false,
|
||||
movement: None,
|
||||
|
@ -1092,10 +1135,17 @@ impl Component for ThreadListing {
|
|||
}
|
||||
}
|
||||
*/
|
||||
if !self.unfocused {
|
||||
if !self.is_dirty() {
|
||||
return;
|
||||
}
|
||||
|
||||
if matches!(self.focus, Focus::EntryFullscreen) {
|
||||
if let Some(v) = self.view.as_mut() {
|
||||
return v.draw(grid, area, context);
|
||||
}
|
||||
}
|
||||
|
||||
if !self.unfocused() {
|
||||
self.dirty = false;
|
||||
/* Draw the entire list */
|
||||
self.draw_list(grid, area, context);
|
||||
|
@ -1198,12 +1248,38 @@ impl Component for ThreadListing {
|
|||
self.dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
|
||||
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 let Some(ref mut v) = self.view {
|
||||
if v.process_event(event, context) {
|
||||
if !matches!(self.focus, Focus::None) && v.process_event(event, context) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
match *event {
|
||||
UIEvent::ConfigReload { old_settings: _ } => {
|
||||
self.color_cache = ColorCache {
|
||||
|
@ -1238,18 +1314,43 @@ impl Component for ThreadListing {
|
|||
}
|
||||
self.set_dirty(true);
|
||||
}
|
||||
UIEvent::Input(Key::Char('\n')) if !self.unfocused => {
|
||||
self.unfocused = true;
|
||||
self.dirty = true;
|
||||
UIEvent::Input(ref k)
|
||||
if matches!(self.focus, Focus::None)
|
||||
&& (shortcut!(k == shortcuts[Listing::DESCRIPTION]["open_entry"])
|
||||
|| shortcut!(k == shortcuts[Listing::DESCRIPTION]["focus_right"])) =>
|
||||
{
|
||||
self.set_focus(Focus::Entry, context);
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(Key::Char('i')) if self.unfocused => {
|
||||
self.unfocused = false;
|
||||
if let Some(ref mut s) = self.view {
|
||||
s.process_event(&mut UIEvent::VisibilityChange(false), context);
|
||||
UIEvent::Input(ref k)
|
||||
if !matches!(self.focus, Focus::None)
|
||||
&& shortcut!(k == shortcuts[Listing::DESCRIPTION]["exit_entry"]) =>
|
||||
{
|
||||
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!();
|
||||
}
|
||||
}
|
||||
self.dirty = true;
|
||||
self.view = None;
|
||||
return true;
|
||||
}
|
||||
UIEvent::MailboxUpdate((ref idxa, ref idxf))
|
||||
|
@ -1275,7 +1376,7 @@ impl Component for ThreadListing {
|
|||
|
||||
self.dirty = true;
|
||||
|
||||
if self.unfocused {
|
||||
if self.unfocused() {
|
||||
if let Some(v) = self.view.as_mut() {
|
||||
v.process_event(
|
||||
&mut UIEvent::EnvelopeRename(*old_hash, *new_hash),
|
||||
|
@ -1301,7 +1402,7 @@ impl Component for ThreadListing {
|
|||
|
||||
self.dirty = true;
|
||||
|
||||
if self.unfocused {
|
||||
if self.unfocused() {
|
||||
if let Some(v) = self.view.as_mut() {
|
||||
v.process_event(&mut UIEvent::EnvelopeUpdate(*env_hash), context);
|
||||
}
|
||||
|
@ -1328,7 +1429,7 @@ impl Component for ThreadListing {
|
|||
self.refresh_mailbox(context, false);
|
||||
return true;
|
||||
}
|
||||
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,
|
||||
|
@ -1379,20 +1480,36 @@ impl Component for ThreadListing {
|
|||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_dirty(&self) -> bool {
|
||||
self.dirty || self.view.as_ref().map(|p| p.is_dirty()).unwrap_or(false)
|
||||
match self.focus {
|
||||
Focus::None => self.dirty,
|
||||
Focus::Entry => self.dirty || self.view.as_ref().map(|p| p.is_dirty()).unwrap_or(false),
|
||||
Focus::EntryFullscreen => self.view.as_ref().map(|p| p.is_dirty()).unwrap_or(false),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_dirty(&mut self, value: bool) {
|
||||
if let Some(p) = self.view.as_mut() {
|
||||
p.set_dirty(value);
|
||||
};
|
||||
self.dirty = value;
|
||||
}
|
||||
|
||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut map = if self.unfocused() {
|
||||
self.view
|
||||
.as_ref()
|
||||
.map(|p| p.get_shortcuts(context))
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
ShortcutMaps::default()
|
||||
};
|
||||
|
||||
let config_map = context.settings.shortcuts.listing.key_values();
|
||||
map.insert(Listing::DESCRIPTION, config_map);
|
||||
|
||||
map
|
||||
}
|
||||
|
||||
fn id(&self) -> ComponentId {
|
||||
|
|
|
@ -160,8 +160,8 @@ shortcut_key_values! { "listing",
|
|||
increase_sidebar |> "Increase sidebar width." |> Key::Ctrl('p'),
|
||||
decrease_sidebar |> "Decrease sidebar width." |> Key::Ctrl('o'),
|
||||
toggle_menu_visibility |> "Toggle visibility of side menu in mail list." |> Key::Char('`'),
|
||||
focus_on_menu |> "Switch focus on sidebar menu." |> Key::Left,
|
||||
focus_on_list |> "Switch focus on mail list." |> Key::Right,
|
||||
focus_left |> "Switch focus on the left." |> Key::Left,
|
||||
focus_right |> "Switch focus on the right." |> Key::Right,
|
||||
exit_entry |> "Exit e-mail entry." |> Key::Char('i'),
|
||||
open_entry |> "Open e-mail entry." |> Key::Char('\n')
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue