diff options
author | Sebastian Thiel <sebastian.thiel@icloud.com> | 2023-12-26 20:31:43 +0100 |
---|---|---|
committer | Sebastian Thiel <sebastian.thiel@icloud.com> | 2023-12-26 20:49:56 +0100 |
commit | 90b65d59f5dde888f81c42e3c812670929b1740a (patch) | |
tree | 0cabc5213fe69eaab1b5bf72aa989ff34648486e /src | |
parent | 46fece5f295a8fb6f90ff969741f79d7c736c140 (diff) |
upgrade to latest verison of tui-crates and native crossterm events. (#203)
Diffstat (limited to 'src')
-rw-r--r-- | src/interactive/app/eventloop.rs | 56 | ||||
-rw-r--r-- | src/interactive/app/tests/journeys_readonly.rs | 44 | ||||
-rw-r--r-- | src/interactive/app/tests/journeys_with_writes.rs | 12 | ||||
-rw-r--r-- | src/interactive/app/tests/utils.rs | 15 | ||||
-rw-r--r-- | src/interactive/widgets/glob.rs | 5 | ||||
-rw-r--r-- | src/interactive/widgets/help.rs | 16 | ||||
-rw-r--r-- | src/interactive/widgets/mark.rs | 28 | ||||
-rw-r--r-- | src/main.rs | 4 | ||||
-rw-r--r-- | src/options.rs | 2 |
9 files changed, 109 insertions, 73 deletions
diff --git a/src/interactive/app/eventloop.rs b/src/interactive/app/eventloop.rs index 00b37e5..c25b350 100644 --- a/src/interactive/app/eventloop.rs +++ b/src/interactive/app/eventloop.rs @@ -6,7 +6,8 @@ use crate::interactive::{ SortMode, }; use anyhow::Result; -use crosstermion::input::{input_channel, Event, Key}; +use crosstermion::crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; +use crosstermion::input::{input_channel, Event}; use dua::{ traverse::{EntryData, Traversal}, WalkOptions, WalkResult, @@ -104,7 +105,7 @@ impl AppState { where B: Backend, { - use crosstermion::input::Key::*; + use crosstermion::crossterm::event::KeyCode::*; use FocussedPane::*; { @@ -115,7 +116,8 @@ impl AppState { for event in events { let key = match event { Event::Key(key) => key, - Event::Resize(_, _) => Alt('\r'), + Event::Resize(_, _) => refresh_key(), + _ => continue, }; self.reset_message(); @@ -123,20 +125,20 @@ impl AppState { let glob_focussed = self.focussed == Glob; let mut tree_view = self.tree_view(traversal); let mut handled = true; - match key { + match key.code { Esc => { if let Some(value) = self.handle_quit(&mut tree_view, window) { return value; } } - Char('\t') => { + Tab => { self.cycle_focus(window); } Char('/') if !glob_focussed => { self.toggle_glob_search(window); } Char('?') if !glob_focussed => self.toggle_help_pane(window), - Ctrl('c') if !glob_focussed => { + Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) && !glob_focussed => { return Ok(ProcessingResult::ExitRequested(WalkResult { num_errors: tree_view.traversal.io_errors, })) @@ -165,14 +167,12 @@ impl AppState { } Glob => { let glob_pane = window.glob_pane.as_mut().expect("glob pane"); - match key { - Char('\n') => { - self.search_glob_pattern(&mut tree_view, &glob_pane.input) - } + match key.code { + Enter => self.search_glob_pattern(&mut tree_view, &glob_pane.input), _ => glob_pane.process_events(key), } } - Main => match key { + Main => match key.code { Char('O') => self.open_that(&tree_view), Char(' ') => self.mark_entry( CursorMode::KeepPosition, @@ -180,12 +180,6 @@ impl AppState { window, &tree_view, ), - Char('d') => self.mark_entry( - CursorMode::Advance, - MarkEntryMode::Toggle, - window, - &tree_view, - ), Char('x') => self.mark_entry( CursorMode::Advance, MarkEntryMode::MarkForDeletion, @@ -195,24 +189,34 @@ impl AppState { Char('a') => { self.mark_all_entries(MarkEntryMode::Toggle, window, &tree_view) } - Char('u') | Char('h') | Backspace | Left => { - self.exit_node_with_traversal(&tree_view) - } - Char('o') | Char('l') | Char('\n') | Right => { + Char('o') | Char('l') | Enter | Right => { self.enter_node_with_traversal(&tree_view) } Char('H') | Home => self.change_entry_selection(CursorDirection::ToTop), Char('G') | End => self.change_entry_selection(CursorDirection::ToBottom), - Ctrl('u') | PageUp => self.change_entry_selection(CursorDirection::PageUp), + PageUp => self.change_entry_selection(CursorDirection::PageUp), + Char('u') if key.modifiers.contains(KeyModifiers::CONTROL) => { + self.change_entry_selection(CursorDirection::PageUp) + } Char('k') | Up => self.change_entry_selection(CursorDirection::Up), Char('j') | Down => self.change_entry_selection(CursorDirection::Down), - Ctrl('d') | PageDown => { + PageDown => self.change_entry_selection(CursorDirection::PageDown), + Char('d') if key.modifiers.contains(KeyModifiers::CONTROL) => { self.change_entry_selection(CursorDirection::PageDown) } Char('s') => self.cycle_sorting(&tree_view), Char('m') => self.cycle_mtime_sorting(&tree_view), Char('c') => self.cycle_count_sorting(&tree_view), Char('g') => display.byte_vis.cycle(), + Char('d') => self.mark_entry( + CursorMode::Advance, + MarkEntryMode::Toggle, + window, + &tree_view, + ), + Char('u') | Char('h') | Backspace | Left => { + self.exit_node_with_traversal(&tree_view) + } _ => {} }, }; @@ -353,7 +357,7 @@ impl TerminalApp { &mut self.traversal, &mut self.display, terminal, - std::iter::once(Event::Key(Key::Alt('\r'))), + std::iter::once(Event::Key(refresh_key())), ) .ok(); } @@ -482,3 +486,7 @@ pub enum Interaction { #[allow(dead_code)] None, } + +fn refresh_key() -> KeyEvent { + KeyEvent::new(KeyCode::Char('\r'), KeyModifiers::ALT) +} diff --git a/src/interactive/app/tests/journeys_readonly.rs b/src/interactive/app/tests/journeys_readonly.rs index 9e06c3f..6781508 100644 --- a/src/interactive/app/tests/journeys_readonly.rs +++ b/src/interactive/app/tests/journeys_readonly.rs @@ -1,7 +1,9 @@ use anyhow::Result; +use crosstermion::crossterm::event::KeyCode; use pretty_assertions::assert_eq; use std::ffi::OsString; +use crate::interactive::app::tests::utils::into_codes; use crate::interactive::{ app::tests::{ utils::{ @@ -65,28 +67,28 @@ fn simple_user_journey_read_only() -> Result<()> { // SORTING { // when hitting the M key - app.process_events(&mut terminal, into_keys(b"m".iter()))?; + app.process_events(&mut terminal, into_codes("m"))?; assert_eq!( app.state.sorting, SortMode::MTimeDescending, "it sets the sort mode to descending by mtime" ); // when hitting the M key again - app.process_events(&mut terminal, into_keys(b"m".iter()))?; + app.process_events(&mut terminal, into_codes("m"))?; assert_eq!( app.state.sorting, SortMode::MTimeAscending, "it sets the sort mode to ascending by mtime" ); // when hitting the C key - app.process_events(&mut terminal, into_keys(b"c".iter()))?; + app.process_events(&mut terminal, into_codes("c"))?; assert_eq!( app.state.sorting, SortMode::CountDescending, "it sets the sort mode to descending by count" ); // when hitting the C key again - app.process_events(&mut terminal, into_keys(b"c".iter()))?; + app.process_events(&mut terminal, into_codes("c"))?; assert_eq!( app.state.sorting, SortMode::CountAscending, @@ -98,7 +100,7 @@ fn simple_user_journey_read_only() -> Result<()> { "it recomputes the cached entries" ); // when hitting the S key - app.process_events(&mut terminal, into_keys(b"s".iter()))?; + app.process_events(&mut terminal, into_codes("s"))?; assert_eq!( app.state.sorting, SortMode::SizeDescending, @@ -110,14 +112,14 @@ fn simple_user_journey_read_only() -> Result<()> { "it recomputes the cached entries" ); // when hitting the S key again - app.process_events(&mut terminal, into_keys(b"s".iter()))?; + app.process_events(&mut terminal, into_codes("s"))?; assert_eq!( app.state.sorting, SortMode::SizeAscending, "it sets the sort mode to ascending by size" ); // hit the S key again to get Descending - the rest depends on it - app.process_events(&mut terminal, into_keys(b"s".iter()))?; + app.process_events(&mut terminal, into_codes("s"))?; assert_eq!(app.state.sorting, SortMode::SizeDescending,); assert_eq!( @@ -130,35 +132,35 @@ fn simple_user_journey_read_only() -> Result<()> { // Entry-Navigation { // when hitting the j key - app.process_events(&mut terminal, into_keys(b"j".iter()))?; + app.process_events(&mut terminal, into_codes("j"))?; assert_eq!( node_by_name(&app, fixture_str(long_root)), node_by_index(&app, *app.state.navigation().selected.as_ref().unwrap()), "it moves the cursor down and selects the next entry based on the current sort mode" ); // when hitting it while there is nowhere to go - app.process_events(&mut terminal, into_keys(b"j".iter()))?; + app.process_events(&mut terminal, into_codes("j"))?; assert_eq!( node_by_name(&app, fixture_str(long_root)), node_by_index(&app, *app.state.navigation().selected.as_ref().unwrap()), "it stays at the previous position" ); // when hitting the k key - app.process_events(&mut terminal, into_keys(b"k".iter()))?; + app.process_events(&mut terminal, into_codes("k"))?; assert_eq!( node_by_name(&app, fixture_str(short_root)), node_by_index(&app, *app.state.navigation().selected.as_ref().unwrap()), "it moves the cursor up and selects the next entry based on the current sort mode" ); // when hitting the k key again - app.process_events(&mut terminal, into_keys(b"k".iter()))?; + app.process_events(&mut terminal, into_codes("k"))?; assert_eq!( node_by_name(&app, fixture_str(short_root)), node_by_index(&app, *app.state.navigation().selected.as_ref().unwrap()), "it stays at the current cursor position as there is nowhere to go" ); // when hitting the o key with a directory selected - app.process_events(&mut terminal, into_keys(b"o".iter()))?; + app.process_events(&mut terminal, into_codes("o"))?; { let new_root_idx = index_by_name(&app, fixture_str(short_root)); assert_eq!( @@ -173,7 +175,7 @@ fn simple_user_journey_read_only() -> Result<()> { ); // when hitting the u key while inside a sub-directory - app.process_events(&mut terminal, into_keys(b"u".iter()))?; + app.process_events(&mut terminal, into_codes("u"))?; { assert_eq!( app.traversal.root_index, @@ -189,7 +191,7 @@ fn simple_user_journey_read_only() -> Result<()> { } // when hitting the u key while inside of the root directory // We are moving the cursor down just to have a non-default selection - app.process_events(&mut terminal, into_keys(b"ju".iter()))?; + app.process_events(&mut terminal, into_codes("ju"))?; { assert_eq!( app.traversal.root_index, @@ -207,9 +209,9 @@ fn simple_user_journey_read_only() -> Result<()> { // Deletion { // when hitting the 'd' key (also move cursor back to start) - app.process_events(&mut terminal, into_keys(b"k".iter()))?; + app.process_events(&mut terminal, into_codes("k"))?; let previously_selected_index = *app.state.navigation().selected.as_ref().unwrap(); - app.process_events(&mut terminal, into_keys(b"d".iter()))?; + app.process_events(&mut terminal, into_codes("d"))?; { assert_eq!( Some(1), @@ -231,7 +233,7 @@ fn simple_user_journey_read_only() -> Result<()> { // when hitting the 'd' key again { - app.process_events(&mut terminal, into_keys(b"d".iter()))?; + app.process_events(&mut terminal, into_codes("d"))?; assert_eq!( Some(2), @@ -248,7 +250,7 @@ fn simple_user_journey_read_only() -> Result<()> { // when hitting the 'd' key once again { - app.process_events(&mut terminal, into_keys(b"d".iter()))?; + app.process_events(&mut terminal, into_codes("d"))?; assert_eq!( Some(1), @@ -265,7 +267,7 @@ fn simple_user_journey_read_only() -> Result<()> { } // when hitting the spacebar (after moving up to the first entry) { - app.process_events(&mut terminal, into_keys(b"k ".iter()))?; + app.process_events(&mut terminal, into_codes("k "))?; assert_eq!( None, @@ -284,7 +286,7 @@ fn simple_user_journey_read_only() -> Result<()> { // Marking { // select something - app.process_events(&mut terminal, into_keys(b" j ".iter()))?; + app.process_events(&mut terminal, into_codes(" j "))?; assert_eq!( Some(false), app.window.mark_pane.as_ref().map(|p| p.has_focus()), @@ -298,7 +300,7 @@ fn simple_user_journey_read_only() -> Result<()> { ); // when advancing the selection to the marker pane - app.process_events(&mut terminal, into_keys(b"\t".iter()))?; + app.process_events(&mut terminal, into_keys(Some(KeyCode::Tab)))?; { assert_eq!( Some(true), diff --git a/src/interactive/app/tests/journeys_with_writes.rs b/src/interactive/app/tests/journeys_with_writes.rs index d14f8b4..9a941e6 100644 --- a/src/interactive/app/tests/journeys_with_writes.rs +++ b/src/interactive/app/tests/journeys_with_writes.rs @@ -1,9 +1,9 @@ use crate::interactive::app::tests::utils::{ - initialized_app_and_terminal_from_paths, into_keys, WritableFixture, + initialized_app_and_terminal_from_paths, into_codes, WritableFixture, }; use anyhow::Result; +use crosstermion::crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; use crosstermion::input::Event; -use crosstermion::input::Key; use pretty_assertions::assert_eq; #[test] @@ -13,7 +13,7 @@ fn basic_user_journey_with_deletion() -> Result<()> { let (mut terminal, mut app) = initialized_app_and_terminal_from_paths(&[fixture.root.clone()])?; // With a selection of items - app.process_events(&mut terminal, into_keys(b"doddd".iter()))?; + app.process_events(&mut terminal, into_codes("doddd"))?; assert_eq!( app.window.mark_pane.as_ref().map(|p| p.marked().len()), @@ -26,7 +26,11 @@ fn basic_user_journey_with_deletion() -> Result<()> { // When selecting the marker window and pressing the combination to delete entries app.process_events( &mut terminal, - vec![Event::Key(Key::Char('\t')), Event::Key(Key::Ctrl('r'))].into_iter(), + vec![ + Event::Key(KeyCode::Tab.into()), + Event::Key(KeyEvent::new(KeyCode::Char('r'), KeyModifiers::CONTROL)), + ] + .into_iter(), )?; assert!( app.window.mark_pane.is_none(), diff --git a/src/interactive/app/tests/utils.rs b/src/interactive/app/tests/utils.rs index 5c76f17..f6bc354 100644 --- a/src/interactive/app/tests/utils.rs +++ b/src/interactive/app/tests/utils.rs @@ -1,4 +1,5 @@ use anyhow::{Context, Error, Result}; +use crosstermion::crossterm::event::KeyCode; use dua::{ traverse::{EntryData, Tree, TreeIndex}, ByteFormat, TraversalSorting, WalkOptions, @@ -20,13 +21,15 @@ use tui_react::Terminal; use crate::interactive::{app::tests::FIXTURE_PATH, Interaction, TerminalApp}; pub fn into_keys<'a>( - bytes: impl Iterator<Item = &'a u8> + 'a, + codes: impl IntoIterator<Item = KeyCode> + 'a, ) -> impl Iterator<Item = crosstermion::input::Event> + 'a { - bytes.map(|b| { - crosstermion::input::Event::Key(crosstermion::input::Key::Char( - std::char::from_u32(*b as u32).unwrap(), - )) - }) + codes + .into_iter() + .map(|code| crosstermion::input::Event::Key(code.into())) +} + +pub fn into_codes(input: &str) -> impl Iterator<Item = crosstermion::input::Event> + '_ { + into_keys(input.chars().map(KeyCode::Char)) } pub fn node_by_index(app: &TerminalApp, id: TreeIndex) -> &EntryData { diff --git a/src/interactive/widgets/glob.rs b/src/interactive/widgets/glob.rs index 0ef95fd..01c8e91 100644 --- a/src/interactive/widgets/glob.rs +++ b/src/interactive/widgets/glob.rs @@ -36,9 +36,8 @@ pub struct GlobPane { impl GlobPane { pub fn process_events(&mut self, key: Key) { - use crosstermion::input::Key::*; - - match key { + use crosstermion::crossterm::event::KeyCode::*; + match key.code { Char(to_insert) => { self.enter_char(to_insert); } diff --git a/src/interactive/widgets/help.rs b/src/interactive/widgets/help.rs index 0ec8b8c..0edc53e 100644 --- a/src/interactive/widgets/help.rs +++ b/src/interactive/widgets/help.rs @@ -1,5 +1,7 @@ use crate::interactive::CursorDirection; -use crosstermion::{input::Key, input::Key::*}; +pub use crosstermion::crossterm::event::KeyCode::*; +use crosstermion::crossterm::event::KeyModifiers; +use crosstermion::input::Key; use std::{borrow::Borrow, cell::RefCell}; use tui::{ buffer::Buffer, @@ -34,13 +36,19 @@ fn margin(r: Rect, margin: u16) -> Rect { impl HelpPane { pub fn process_events(&mut self, key: Key) { - match key { + match key.code { Char('H') => self.scroll_help(CursorDirection::ToTop), Char('G') => self.scroll_help(CursorDirection::ToBottom), - Ctrl('u') | PageUp => self.scroll_help(CursorDirection::PageUp), + Char('u') if key.modifiers.contains(KeyModifiers::CONTROL) => { + self.scroll_help(CursorDirection::PageUp) + } + Char('d') if key.modifiers.contains(KeyModifiers::CONTROL) => { + self.scroll_help(CursorDirection::PageDown) + } + PageUp => self.scroll_help(CursorDirection::PageUp), + PageDown => self.scroll_help(CursorDirection::PageDown), Char('k') | Up => self.scroll_help(CursorDirection::Up), Char('j') | Down => self.scroll_help(CursorDirection::Down), - Ctrl('d') | PageDown => self.scroll_help(CursorDirection::PageDown), _ => {} }; } diff --git a/src/interactive/widgets/mark.rs b/src/interactive/widgets/mark.rs index 63ed54f..dc0021e 100644 --- a/src/interactive/widgets/mark.rs +++ b/src/interactive/widgets/mark.rs @@ -3,7 +3,8 @@ use crate::interactive::{ app::tree_view::TreeView, fit_string_graphemes_with_ellipsis, widgets::entry_color, CursorDirection, }; -use crosstermion::{input::Key, input::Key::*}; +use crosstermion::crossterm::event::KeyModifiers; +use crosstermion::input::Key; use dua::{traverse::TreeIndex, ByteFormat}; use itertools::Itertools; use std::{ @@ -114,21 +115,32 @@ impl MarkPane { self.marked.into_values().map(|v| v.path) } pub fn process_events(mut self, key: Key) -> Option<(Self, Option<MarkMode>)> { + use crosstermion::crossterm::event::KeyCode::*; let action = None; - match key { - Ctrl('r') => return Some(self.prepare_deletion(MarkMode::Delete)), + match key.code { + Char('r') if key.modifiers.contains(KeyModifiers::CONTROL) => { + return Some(self.prepare_deletion(MarkMode::Delete)) + } #[cfg(feature = "trash-move")] - Ctrl('t') => return Some(self.prepare_deletion(MarkMode::Trash)), - Char('x') | Char('d') | Char(' ') => { - return self.remove_selected().map(|s| (s, action)) + Char('t') if key.modifiers.contains(KeyModifiers::CONTROL) => { + return Some(self.prepare_deletion(MarkMode::Trash)) } Char('a') => return None, Char('H') => self.change_selection(CursorDirection::ToTop), Char('G') => self.change_selection(CursorDirection::ToBottom), - Ctrl('u') | PageUp => self.change_selection(CursorDirection::PageUp), + PageUp => self.change_selection(CursorDirection::PageUp), + Char('u') if key.modifiers.contains(KeyModifiers::CONTROL) => { + self.change_selection(CursorDirection::PageUp) + } + Char('d') if key.modifiers.contains(KeyModifiers::CONTROL) => { + self.change_selection(CursorDirection::PageDown) + } + PageDown => self.change_selection(CursorDirection::PageDown), Char('k') | Up => self.change_selection(CursorDirection::Up), Char('j') | Down => self.change_selection(CursorDirection::Down), - Ctrl('d') | PageDown => self.change_selection(CursorDirection::PageDown), + Char('x') | Char('d') | Char(' ') => { + return self.remove_selected().map(|s| (s, action)) + } _ => {} }; Some((self, action)) diff --git a/src/main.rs b/src/main.rs index a610f6f..47be1f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use dua::TraversalSorting; use std::{fs, io, io::Write, path::PathBuf, process}; mod crossdev; -#[cfg(any(feature = "tui-unix", feature = "tui-crossplatform"))] +#[cfg(feature = "tui-crossplatform")] mod interactive; mod options; @@ -31,7 +31,7 @@ fn main() -> Result<()> { ignore_dirs: opt.ignore_dirs, }; let res = match opt.command { - #[cfg(any(feature = "tui-unix", feature = "tui-crossplatform"))] + #[cfg(feature = "tui-crossplatform")] Some(Interactive { input }) => { use crate::interactive::{Interaction, TerminalApp}; use anyhow::{anyhow, Context}; diff --git a/src/options.rs b/src/options.rs index 6711795..13705b3 100644 --- a/src/options.rs +++ b/src/options.rs @@ -84,7 +84,7 @@ pub struct Args { #[derive(Debug, clap::Subcommand)] pub enum Command { /// Launch the terminal user interface - #[cfg(any(feature = "tui-unix", feature = "tui-crossplatform"))] + #[cfg(feature = "tui-crossplatform")] #[clap(name = "interactive", visible_alias = "i")] Interactive { /// One or more input files or directories. If unset, we will use all entries in the current working directory. |