diff --git a/README.md b/README.md index 6ac1a9d..1c015b0 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,8 @@ Some normal mode controls vary based on whether you are currently selecting a fe - `q`/`Esc` - quit Russ - `hjkl`/arrows - move up/down/left/right between feeds and entries, scroll up/down on an entry +- `g`/`G` - move to top/bottom of the current entry or list +- `ctrl-o` - return to previous place after jumping with `g`/`G` - `Enter` - read selected entry - `r` - refresh the selected feed - `r` - mark the selected entry as read diff --git a/src/app.rs b/src/app.rs index 5f4e0ae..1d0d12f 100644 --- a/src/app.rs +++ b/src/app.rs @@ -4,7 +4,7 @@ use crate::modes::{Mode, ReadMode, Selected}; use crate::util; use anyhow::Result; use copypasta::{ClipboardContext, ClipboardProvider}; -use ratatui::{backend::CrosstermBackend, Terminal}; +use ratatui::{Terminal, backend::CrosstermBackend}; use std::sync::{Arc, Mutex}; macro_rules! delegate_to_locked_inner { @@ -56,6 +56,8 @@ impl App { (on_left, Result<()>), (on_right, Result<()>), (on_up, Result<()>), + (on_top, Result<()>), + (on_bottom, Result<()>), (page_up, ()), (page_down, ()), (pop_feed_subscription_input, ()), @@ -595,7 +597,9 @@ impl AppImpl { #[cfg(not(target_os = "linux"))] { - unreachable!("This should never happen. This code should only be reachable if the target OS is WSL.") + unreachable!( + "This should never happen. This code should only be reachable if the target OS is WSL." + ) } } else if let Some(current_link) = current_link { let mut ctx = ClipboardContext::new().map_err(|e| anyhow::anyhow!(e))?; @@ -702,6 +706,53 @@ impl AppImpl { Ok(()) } + pub fn on_top(&mut self) -> Result<()> { + match self.selected { + Selected::Feeds => { + self.feeds.first(); + self.update_current_feed_and_entries()?; + } + Selected::Entries => { + if !self.entries.items.is_empty() { + self.entries.first(); + self.entry_selection_position = self.entries.state.selected().unwrap(); + self.update_current_entry_meta()?; + } + } + Selected::Entry(_) => { + self.entry_scroll_position = 0; + } + Selected::None => (), + } + + Ok(()) + } + + pub fn on_bottom(&mut self) -> Result<()> { + match self.selected { + Selected::Feeds => { + self.feeds.last(); + self.update_current_feed_and_entries()?; + } + Selected::Entries => { + if !self.entries.items.is_empty() { + self.entries.last(); + self.entry_selection_position = self.entries.state.selected().unwrap(); + self.update_current_entry_meta()?; + } + } + Selected::Entry(_) => { + self.entry_scroll_position = self + .entry_lines_len + .saturating_sub(self.entry_lines_rendered_len.into()) + .saturating_sub(1) as u16; + } + Selected::None => (), + } + + Ok(()) + } + pub fn mode(&self) -> Mode { self.mode } diff --git a/src/main.rs b/src/main.rs index 1aa986b..d734525 100644 --- a/src/main.rs +++ b/src/main.rs @@ -261,6 +261,8 @@ enum Action { MoveDown, MoveUp, MoveRight, + MoveTop, + MoveBottom, PageUp, PageDown, RefreshAll, @@ -304,6 +306,8 @@ fn get_action(app: &App, event: Event) -> Option { (KeyCode::Right, _) | (KeyCode::Char('l'), _) => Some(Action::MoveRight), (KeyCode::Down, _) | (KeyCode::Char('j'), _) => Some(Action::MoveDown), (KeyCode::Up, _) | (KeyCode::Char('k'), _) => Some(Action::MoveUp), + (KeyCode::Char('g'), _) => Some(Action::MoveTop), + (KeyCode::Char('G'), _) => Some(Action::MoveBottom), (KeyCode::PageUp, _) | (KeyCode::Char('u'), KeyModifiers::CONTROL) => { Some(Action::PageUp) } @@ -366,6 +370,8 @@ fn update(app: &mut App, action: Action) -> Result<()> { Action::MoveDown => app.on_down()?, Action::MoveUp => app.on_up()?, Action::MoveRight => app.on_right()?, + Action::MoveTop => app.on_top()?, + Action::MoveBottom => app.on_bottom()?, Action::PageUp => app.page_up(), Action::PageDown => app.page_down(), Action::ToggleHelp => app.toggle_help()?, diff --git a/src/ui.rs b/src/ui.rs index 2db7af4..b59a5bb 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -403,7 +403,6 @@ fn draw_entry(f: &mut Frame, area: Rect, app: &mut AppImpl) { let paragraph = Paragraph::new(app.current_entry_text.as_str()) .block(block) - .wrap(Wrap { trim: false }) .scroll((scroll, 0)); let entry_chunk_height = area.height - 2; diff --git a/src/util.rs b/src/util.rs index b07654e..d5eb5cd 100644 --- a/src/util.rs +++ b/src/util.rs @@ -44,6 +44,14 @@ impl StatefulList { self.state.select(Some(i)); } + pub fn first(&mut self) { + self.state.select(Some(0)); + } + + pub fn last(&mut self) { + self.state.select(Some(self.items.len() - 1)); + } + pub fn reset(&mut self) { self.state.select(Some(0)); }