diff options
author | DLFW <daniel@llin.info> | 2022-08-30 19:46:22 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-30 13:46:22 -0400 |
commit | 826e24eaf4a10921bdd087079c4d7330896b79c2 (patch) | |
tree | ecce44e4243f7b58d110e071759a5ec32c6be2fb /src/fs | |
parent | edf620b4aceaaadffedd14d85d120d485a6ad8f3 (diff) |
Add "Visual-Mode" (#192)
* Add "Visual-Mode"
This adds a "visual-mode" file selection feature where a range-selection follows the file-cursor.
Description of usage is added to the docs.
Also, the "normal" and the new "visual-mode-selection" are now preserved
when a `DirList` is reloaded.
Wrap-up of changes:
* Add command `toggle_visual`, mapped to `V`
* Add command `escape`, mapped to `ESCAPE`
* Add style `[visual_mode_selection]` for file entries which are
"temporarily" selected by visual-mode
* For `JoshutoDirEntry`, the attribute `selected` has been renamed to
`permanent_selected`, and a second selection-attribute
`visual_mode_selected` has been added. "Setters" and "getters" have
been adapted/added accordingly. The former "getter" for the `selecetd`
attribute still exists and returns `True` for an entry which is
"permanant selected" _or_ "visual-mode selected". So any higher logic
which acts on selected files does not need to care about "how" and
entry is selected.
* Each `JoshutoDirList` has an optional index of the file where
visual-mode has been entered and re-calculates the "visual-mode
selecetd" status for each entry any time the cursor-index changes.
* The footer has been extended so it shows a "VIS" marker when the user
is in visual-mode.
This implementation of visual-mode is a bit different from the ranger
one, where the visual-selection is turned into a "normal selection" when
a command (like `copy-files`) is issued. This implementation keeps
both selections separate until the user toggles back to "normal mode".
Only then the visual-selection is taken over to the "normal selection".
The user also can withdraw the visual-selection with `escape`.
The `escape` command may be used also for other "reset"-actions in the
future.
* fix syntax for Rust stable
* cargo clippy
Diffstat (limited to 'src/fs')
-rw-r--r-- | src/fs/dirlist.rs | 62 | ||||
-rw-r--r-- | src/fs/entry.rs | 26 | ||||
-rw-r--r-- | src/fs/metadata.rs | 2 |
3 files changed, 84 insertions, 6 deletions
diff --git a/src/fs/dirlist.rs b/src/fs/dirlist.rs index 096c9a0..567b8eb 100644 --- a/src/fs/dirlist.rs +++ b/src/fs/dirlist.rs @@ -15,6 +15,8 @@ pub struct JoshutoDirList { index: Option<usize>, /// The index in this dir list to start with when rendering the list viewport_index: usize, + /// The index in this dir list where visual mode has started or None if not in visual mode + visual_mode_anchor_index: Option<usize>, _need_update: bool, } @@ -24,6 +26,7 @@ impl JoshutoDirList { contents: Vec<JoshutoDirEntry>, index: Option<usize>, viewport_index: usize, + visual_mode_anchor_index: Option<usize>, metadata: JoshutoMetadata, ) -> Self { Self { @@ -32,6 +35,7 @@ impl JoshutoDirList { metadata, index, viewport_index, + visual_mode_anchor_index, _need_update: false, } } @@ -55,6 +59,7 @@ impl JoshutoDirList { _need_update: false, index, viewport_index: if let Some(ix) = index { ix } else { 0 }, + visual_mode_anchor_index: None, }) } @@ -62,6 +67,62 @@ impl JoshutoDirList { self.index } + pub fn get_visual_mode_anchor_index(&self) -> Option<usize> { + self.visual_mode_anchor_index + } + + fn update_visual_mode_selection(&mut self) { + //! To be invoked any time the cursor index, the visual mode anchor index, + //! or the shown sub-set of entries changes. + if let Some(vmix) = self.visual_mode_anchor_index { + if let Some(cix) = self.index { + self.iter_mut().enumerate().for_each(|(i, entry)| { + entry.set_visual_mode_selected( + (if vmix > cix { + cix..vmix + 1 + } else { + vmix..cix + 1 + }) + .contains(&i), + ) + }) + } + } else { + self.iter_mut() + .for_each(|entry| entry.set_visual_mode_selected(false)) + } + } + + fn visual_mode_enable(&mut self) { + if let Some(ix) = self.index { + self.visual_mode_anchor_index = Some(ix) + }; + self.update_visual_mode_selection(); + } + + fn visual_mode_disable(&mut self) { + self.visual_mode_anchor_index = None; + self.iter_mut().for_each(|entry| { + if entry.is_visual_mode_selected() { + entry.set_permanent_selected(true) + } + }); + self.update_visual_mode_selection(); + } + + pub fn visual_mode_cancel(&mut self) { + self.visual_mode_anchor_index = None; + self.update_visual_mode_selection(); + } + + pub fn toggle_visual_mode(&mut self) { + if self.get_visual_mode_anchor_index().is_none() { + self.visual_mode_enable() + } else { + self.visual_mode_disable() + } + } + fn update_viewport(&mut self, ui_context: &UiContext, options: &DisplayOption) { if let Some(ix) = self.index { let height = ui_context.layout[0].height as usize; @@ -106,6 +167,7 @@ impl JoshutoDirList { if !ui_context.layout.is_empty() { self.update_viewport(ui_context, options); } + self.update_visual_mode_selection(); } pub fn iter(&self) -> Iter<JoshutoDirEntry> { diff --git a/src/fs/entry.rs b/src/fs/entry.rs index 8337bc4..f7dae32 100644 --- a/src/fs/entry.rs +++ b/src/fs/entry.rs @@ -12,7 +12,10 @@ pub struct JoshutoDirEntry { label: String, path: path::PathBuf, pub metadata: JoshutoMetadata, - selected: bool, + /// Directly selected by the user, _not_ by a current visual mode selection + permanent_selected: bool, + /// Temporarily selected by the visual mode range + visual_mode_selected: bool, _marked: bool, } @@ -63,11 +66,23 @@ impl JoshutoDirEntry { } pub fn is_selected(&self) -> bool { - self.selected + self.permanent_selected || self.visual_mode_selected } - pub fn set_selected(&mut self, selected: bool) { - self.selected = selected; + pub fn is_permanent_selected(&self) -> bool { + self.permanent_selected + } + + pub fn is_visual_mode_selected(&self) -> bool { + self.visual_mode_selected + } + + pub fn set_permanent_selected(&mut self, selected: bool) { + self.permanent_selected = selected; + } + + pub fn set_visual_mode_selected(&mut self, visual_mode_selected: bool) { + self.visual_mode_selected = visual_mode_selected; } pub fn get_ext(&self) -> &str { @@ -102,7 +117,8 @@ impl JoshutoDirEntry { label, path, metadata, - selected: false, + permanent_selected: false, + visual_mode_selected: false, _marked: false, }) } diff --git a/src/fs/metadata.rs b/src/fs/metadata.rs index fd192cc..616e09c 100644 --- a/src/fs/metadata.rs +++ b/src/fs/metadata.rs @@ -1,6 +1,6 @@ use std::{fs, io, path, time}; -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum FileType { Directory, File, |