From 363f4930994d1d2e88220878b3848f176b8c5f97 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 19 Jun 2023 22:15:06 +0300 Subject: [PATCH] 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. --- docs/meli.conf.5 | 12 +++- src/components/mail/listing.rs | 16 +++++ src/components/mail/listing/compact.rs | 76 +++++++++++++++----- src/components/mail/listing/conversations.rs | 76 +++++++++++++++----- src/components/mail/listing/offline.rs | 4 ++ src/components/mail/listing/plain.rs | 33 +++++++++ src/components/mail/listing/thread.rs | 29 ++++++++ src/conf/shortcuts.rs | 6 +- 8 files changed, 210 insertions(+), 42 deletions(-) diff --git a/docs/meli.conf.5 b/docs/meli.conf.5 index 315bd224..daf6d455 100644 --- a/docs/meli.conf.5 +++ b/docs/meli.conf.5 @@ -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 diff --git a/src/components/mail/listing.rs b/src/components/mail/listing.rs index df6dc752..82d6267f 100644 --- a/src/components/mail/listing.rs +++ b/src/components/mail/listing.rs @@ -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); + } _ => {} } } diff --git a/src/components/mail/listing/compact.rs b/src/components/mail/listing/compact.rs index d5edce37..90c67bc3 100644 --- a/src/components/mail/listing/compact.rs +++ b/src/components/mail/listing/compact.rs @@ -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) diff --git a/src/components/mail/listing/conversations.rs b/src/components/mail/listing/conversations.rs index e6b98e4d..f0b895a6 100644 --- a/src/components/mail/listing/conversations.rs +++ b/src/components/mail/listing/conversations.rs @@ -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) diff --git a/src/components/mail/listing/offline.rs b/src/components/mail/listing/offline.rs index 6bfe9ec1..6dc31681 100644 --- a/src/components/mail/listing/offline.rs +++ b/src/components/mail/listing/offline.rs @@ -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, diff --git a/src/components/mail/listing/plain.rs b/src/components/mail/listing/plain.rs index aaac5410..75401419 100644 --- a/src/components/mail/listing/plain.rs +++ b/src/components/mail/listing/plain.rs @@ -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 => { diff --git a/src/components/mail/listing/thread.rs b/src/components/mail/listing/thread.rs index 839401db..ab5c1827 100644 --- a/src/components/mail/listing/thread.rs +++ b/src/components/mail/listing/thread.rs @@ -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 => { diff --git a/src/conf/shortcuts.rs b/src/conf/shortcuts.rs index f85ec146..8f3328a1 100644 --- a/src/conf/shortcuts.rs +++ b/src/conf/shortcuts.rs @@ -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,