listing: add {previous,next}_entry shortcuts to quickly open other mail entries

When reading a mail entry, with Ctrl+n you can switch to the next entry,
and with Ctrl+p to the previous one. They can be reconfigured by setting
the shortcuts.listing.next_entry and shortcuts.listing.previous_entry
settings.
pull/237/head
Manos Pitsidianakis 2023-06-19 22:15:06 +03:00
parent 8cab9d9da8
commit 363f493099
Signed by: Manos Pitsidianakis
GPG Key ID: 7729C7707F7E09D0
8 changed files with 210 additions and 42 deletions

View File

@ -870,11 +870,19 @@ Select thread entry.
.It Ic increase_sidebar
Increase sidebar width.
.\" default value
.Pq Em C-p
.Pq Em C-f
.It Ic decrease_sidebar
Decrease sidebar width.
.\" default value
.Pq Em C-o
.Pq Em C-d
.It Ic next_entry
When reading a mail item, change focus on next entry according to the current sorting.
.\" default value
.Pq Em C-n
.It Ic previous_entry
When reading a mail item, change focus on previous entry according to the current sorting.
.\" default value
.Pq Em C-p
.It Ic toggle_menu_visibility
Toggle visibility of side menu in mail list.
.\" default value

View File

@ -780,6 +780,8 @@ pub trait MailListingTrait: ListingTrait {
pub trait ListingTrait: Component {
fn coordinates(&self) -> (AccountHash, MailboxHash);
fn set_coordinates(&mut self, _: (AccountHash, MailboxHash));
fn next_entry(&mut self, context: &mut Context);
fn prev_entry(&mut self, context: &mut Context);
fn draw_list(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context);
fn highlight_line(&mut self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context);
fn filter(
@ -1679,6 +1681,20 @@ impl Component for Listing {
self.component
.set_modifier_command(Some(Modifier::Intersection));
}
UIEvent::Input(ref key)
if self.component.unfocused()
&& shortcut!(key == shortcuts[Shortcuts::LISTING]["next_entry"]) =>
{
self.component.next_entry(context);
}
UIEvent::Input(ref key)
if self.component.unfocused()
&& shortcut!(
key == shortcuts[Shortcuts::LISTING]["previous_entry"]
) =>
{
self.component.prev_entry(context);
}
_ => {}
}
}

View File

@ -584,6 +584,43 @@ impl ListingTrait for CompactListing {
self.rows.row_updates.clear();
}
fn next_entry(&mut self, context: &mut Context) {
if self
.get_thread_under_cursor(self.cursor_pos.2 + 1)
.is_some()
{
// TODO: makes this less ugly.
self.movement = Some(PageMovement::Down(1));
self.force_draw = true;
self.dirty = true;
self.cursor_pos.2 += 1;
self.new_cursor_pos.2 += 1;
self.set_focus(Focus::Entry, context);
self.cursor_pos.2 -= 1;
self.new_cursor_pos.2 -= 1;
}
}
fn prev_entry(&mut self, context: &mut Context) {
if self.cursor_pos.2 == 0 {
return;
}
if self
.get_thread_under_cursor(self.cursor_pos.2 - 1)
.is_some()
{
// TODO: makes this less ugly.
self.movement = Some(PageMovement::Up(1));
self.force_draw = true;
self.dirty = true;
self.cursor_pos.2 -= 1;
self.new_cursor_pos.2 -= 1;
self.set_focus(Focus::Entry, context);
self.cursor_pos.2 += 1;
self.new_cursor_pos.2 += 1;
}
}
fn highlight_line(&mut self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) {
let thread_hash = if let Some(h) = self.get_thread_under_cursor(idx) {
h
@ -861,8 +898,24 @@ impl ListingTrait for CompactListing {
self.force_draw = true;
}
Focus::Entry => {
self.force_draw = true;
self.dirty = true;
if let Some((thread_hash, env_hash)) = self
.get_thread_under_cursor(self.cursor_pos.2)
.and_then(|thread| self.rows.thread_to_env.get(&thread).map(|e| (thread, e[0])))
{
self.force_draw = true;
self.dirty = true;
self.kick_parent(
self.parent,
ListingMessage::OpenEntryUnderCursor {
thread_hash,
env_hash,
show_thread: true,
},
context,
);
} else {
return;
}
}
Focus::EntryFullscreen => {
self.dirty = true;
@ -1738,23 +1791,8 @@ impl Component for CompactListing {
&& (shortcut!(k == shortcuts[Shortcuts::LISTING]["open_entry"])
|| shortcut!(k == shortcuts[Shortcuts::LISTING]["focus_right"])) =>
{
if let Some((thread_hash, env_hash)) = self
.get_thread_under_cursor(self.cursor_pos.2)
.and_then(|thread| {
self.rows.thread_to_env.get(&thread).map(|e| (thread, e[0]))
})
{
self.kick_parent(
self.parent,
ListingMessage::OpenEntryUnderCursor {
thread_hash,
env_hash,
show_thread: true,
},
context,
);
self.set_focus(Focus::Entry, context);
}
self.set_focus(Focus::Entry, context);
return true;
}
UIEvent::Input(ref k)

View File

@ -397,6 +397,43 @@ impl ListingTrait for ConversationsListing {
self.rows.clear();
}
fn next_entry(&mut self, context: &mut Context) {
if self
.get_thread_under_cursor(self.cursor_pos.2 + 1)
.is_some()
{
// TODO: makes this less ugly.
self.movement = Some(PageMovement::Down(1));
self.force_draw = true;
self.dirty = true;
self.cursor_pos.2 += 1;
self.new_cursor_pos.2 += 1;
self.set_focus(Focus::Entry, context);
self.cursor_pos.2 -= 1;
self.new_cursor_pos.2 -= 1;
}
}
fn prev_entry(&mut self, context: &mut Context) {
if self.cursor_pos.2 == 0 {
return;
}
if self
.get_thread_under_cursor(self.cursor_pos.2 - 1)
.is_some()
{
// TODO: makes this less ugly.
self.movement = Some(PageMovement::Up(1));
self.force_draw = true;
self.dirty = true;
self.cursor_pos.2 -= 1;
self.new_cursor_pos.2 -= 1;
self.set_focus(Focus::Entry, context);
self.cursor_pos.2 += 1;
self.new_cursor_pos.2 += 1;
}
}
fn highlight_line(&mut self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) {
if self.length == 0 {
return;
@ -605,8 +642,24 @@ impl ListingTrait for ConversationsListing {
self.force_draw = true;
}
Focus::Entry => {
self.force_draw = true;
self.dirty = true;
if let Some((thread_hash, env_hash)) = self
.get_thread_under_cursor(self.cursor_pos.2)
.and_then(|thread| self.rows.thread_to_env.get(&thread).map(|e| (thread, e[0])))
{
self.force_draw = true;
self.dirty = true;
self.kick_parent(
self.parent,
ListingMessage::OpenEntryUnderCursor {
thread_hash,
env_hash,
show_thread: true,
},
context,
);
} else {
return;
}
}
Focus::EntryFullscreen => {}
}
@ -1296,23 +1349,8 @@ impl Component for ConversationsListing {
&& (shortcut!(k == shortcuts[Shortcuts::LISTING]["open_entry"])
|| shortcut!(k == shortcuts[Shortcuts::LISTING]["focus_right"])) =>
{
if let Some((thread_hash, env_hash)) = self
.get_thread_under_cursor(self.cursor_pos.2)
.and_then(|thread| {
self.rows.thread_to_env.get(&thread).map(|e| (thread, e[0]))
})
{
self.kick_parent(
self.parent,
ListingMessage::OpenEntryUnderCursor {
thread_hash,
env_hash,
show_thread: true,
},
context,
);
self.set_focus(Focus::Entry, context);
}
self.set_focus(Focus::Entry, context);
return true;
}
UIEvent::Input(ref k)

View File

@ -71,6 +71,10 @@ impl ListingTrait for OfflineListing {
self.cursor_pos = coordinates;
}
fn next_entry(&mut self, _: &mut Context) {}
fn prev_entry(&mut self, _: &mut Context) {}
fn highlight_line(
&mut self,
_grid: &mut CellBuffer,

View File

@ -321,6 +321,37 @@ impl ListingTrait for PlainListing {
self.rows.row_updates.clear();
}
fn next_entry(&mut self, context: &mut Context) {
if self.get_env_under_cursor(self.cursor_pos.2 + 1).is_some() {
// TODO: makes this less ugly.
self.movement = Some(PageMovement::Down(1));
self.force_draw = true;
self.dirty = true;
self.cursor_pos.2 += 1;
self.new_cursor_pos.2 += 1;
self.set_focus(Focus::Entry, context);
self.cursor_pos.2 -= 1;
self.new_cursor_pos.2 -= 1;
}
}
fn prev_entry(&mut self, context: &mut Context) {
if self.cursor_pos.2 == 0 {
return;
}
if self.get_env_under_cursor(self.cursor_pos.2 - 1).is_some() {
// TODO: makes this less ugly.
self.movement = Some(PageMovement::Up(1));
self.force_draw = true;
self.dirty = true;
self.cursor_pos.2 -= 1;
self.new_cursor_pos.2 -= 1;
self.set_focus(Focus::Entry, context);
self.cursor_pos.2 += 1;
self.new_cursor_pos.2 += 1;
}
}
fn highlight_line(&mut self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) {
let i = if let Some(i) = self.get_env_under_cursor(idx) {
i
@ -606,6 +637,8 @@ impl ListingTrait for PlainListing {
},
context,
);
} else {
return;
}
}
Focus::EntryFullscreen => {

View File

@ -435,6 +435,33 @@ impl ListingTrait for ThreadListing {
(self.new_cursor_pos.0, self.new_cursor_pos.1)
}
fn next_entry(&mut self, context: &mut Context) {
if self.get_env_under_cursor(self.cursor_pos.2 + 1).is_some() {
// TODO: makes this less ugly.
self.movement = Some(PageMovement::Down(1));
self.force_draw = true;
self.dirty = true;
self.cursor_pos.2 += 1;
self.set_focus(Focus::Entry, context);
self.cursor_pos.2 -= 1;
}
}
fn prev_entry(&mut self, context: &mut Context) {
if self.cursor_pos.2 == 0 {
return;
}
if self.get_env_under_cursor(self.cursor_pos.2 - 1).is_some() {
// TODO: makes this less ugly.
self.movement = Some(PageMovement::Up(1));
self.force_draw = true;
self.dirty = true;
self.cursor_pos.2 -= 1;
self.set_focus(Focus::Entry, context);
self.cursor_pos.2 += 1;
}
}
fn set_coordinates(&mut self, coordinates: (AccountHash, MailboxHash)) {
self.new_cursor_pos = (coordinates.0, coordinates.1, 0);
self.focus = Focus::None;
@ -674,6 +701,8 @@ impl ListingTrait for ThreadListing {
},
context,
);
} else {
return;
}
}
Focus::EntryFullscreen => {

View File

@ -168,8 +168,10 @@ shortcut_key_values! { "listing",
diff_modifier |> "Difference modifier." |> Key::Ctrl('d'),
intersection_modifier |> "Intersection modifier." |> Key::Ctrl('i'),
select_entry |> "Select thread entry." |> Key::Char('v'),
increase_sidebar |> "Increase sidebar width." |> Key::Ctrl('p'),
decrease_sidebar |> "Decrease sidebar width." |> Key::Ctrl('o'),
increase_sidebar |> "Increase sidebar width." |> Key::Ctrl('f'),
decrease_sidebar |> "Decrease sidebar width." |> Key::Ctrl('d'),
next_entry |> "Focus on next entry." |> Key::Ctrl('n'),
previous_entry |> "Focus on previous entry." |> Key::Ctrl('p'),
toggle_menu_visibility |> "Toggle visibility of side menu in mail list." |> Key::Char('`'),
focus_left |> "Switch focus on the left." |> Key::Left,
focus_right |> "Switch focus on the right." |> Key::Right,