summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorqkzk <qu3nt1n@gmail.com>2022-11-21 20:28:37 +0100
committerqkzk <qu3nt1n@gmail.com>2022-11-21 20:28:37 +0100
commitcf86e7d29109f8eb60a341011e034f4347d9e3f3 (patch)
treef655f1abefcf219e30c84538c973a1daf6396b1a
parent95db27f292e7ee8ea0a63583987431df1ea79dd1 (diff)
improve tab navigation interfacetab-bar
-rw-r--r--readme.md6
-rw-r--r--src/actioner.rs24
-rw-r--r--src/status.rs16
-rw-r--r--src/term_manager.rs47
4 files changed, 82 insertions, 11 deletions
diff --git a/readme.md b/readme.md
index a9f7b7d..21c854f 100644
--- a/readme.md
+++ b/readme.md
@@ -130,6 +130,12 @@
- [x] preview
- [ ] auto mount usb keys ??? [rusb](https://github.com/a1ien/rusb)
- [ ] mtp... but fast [libmtp.rs](https://docs.rs/libmtp-rs/0.7.7/libmtp_rs/)
+- [x] improve tabs interface
+ - [x] tab bar
+ - [x] digit move to respective tab
+ - [x] <TAB> creates a new tab if only one
+ - [x] <BACKTAB> moves to previous tab
+ - [x] hardcoded limit to 10 tabs
## BUGS
diff --git a/src/actioner.rs b/src/actioner.rs
index 7e49a33..0c0a33c 100644
--- a/src/actioner.rs
+++ b/src/actioner.rs
@@ -78,6 +78,7 @@ impl Actioner {
Event::Key(Key::PageUp) => self.page_up(status),
Event::Key(Key::Enter) => self.enter(status),
Event::Key(Key::Tab) => self.tab(status),
+ Event::Key(Key::BackTab) => self.backtab(status),
Event::Key(Key::WheelUp(_, _, _)) => self.up(status),
Event::Key(Key::WheelDown(_, _, _)) => self.down(status),
Event::Key(Key::SingleClick(MouseButton::Left, row, _)) => {
@@ -299,17 +300,31 @@ impl Actioner {
}
/// Select next completion and insert it
+ /// Select next tab
fn tab(&self, status: &mut Status) -> FmResult<()> {
match status.selected().mode {
Mode::Goto | Mode::Exec | Mode::Search => {
status.selected().event_replace_input_with_completion()
}
- Mode::Normal => status.next(),
+ Mode::Normal => {
+ if status.len_index_of_tabs().0 == 1 {
+ status.new_tab()
+ }
+ status.next()
+ }
_ => (),
};
Ok(())
}
+ /// Select previous tab
+ fn backtab(&self, status: &mut Status) -> FmResult<()> {
+ if let Mode::Normal = status.selected().mode {
+ status.prev()
+ }
+ Ok(())
+ }
+
fn ctrl_f(&self, status: &mut Status) -> FmResult<()> {
let output = Skimer::new(self.term.clone()).no_source(
status
@@ -362,7 +377,12 @@ impl Actioner {
}
Mode::Normal => match self.binds.get(&c) {
Some(event_char) => event_char.match_char(status),
- None => Ok(()),
+ None => {
+ if c.is_ascii_digit() {
+ status.go_tab(c)
+ }
+ Ok(())
+ }
},
Mode::Help | Mode::Preview | Mode::Shortcut => status.selected().event_normal(),
Mode::Jump => Ok(()),
diff --git a/src/status.rs b/src/status.rs
index 0c32cfa..9bd14ee 100644
--- a/src/status.rs
+++ b/src/status.rs
@@ -44,6 +44,7 @@ pub struct Status {
}
impl Status {
+ const MAX_TAB: u32 = 10;
const MAX_PERMISSIONS: u32 = 0o777;
pub fn new(args: Args, config: Config, height: usize, term: Arc<Term>) -> FmResult<Self> {
@@ -58,8 +59,21 @@ impl Status {
})
}
+ pub fn len_index_of_tabs(&self) -> (usize, usize) {
+ (self.tabs.len(), self.index)
+ }
+
pub fn new_tab(&mut self) {
- self.tabs.push(self.tabs[self.index].clone())
+ if self.tabs.len() < Self::MAX_TAB as usize {
+ self.tabs.push(self.tabs[self.index].clone())
+ }
+ }
+
+ pub fn go_tab(&mut self, digit: char) {
+ let index = digit.to_digit(10).unwrap_or(Self::MAX_TAB) as usize;
+ if self.tabs.len() > index {
+ self.index = index
+ }
}
pub fn drop_tab(&mut self) {
diff --git a/src/term_manager.rs b/src/term_manager.rs
index f6bdd7f..8e98797 100644
--- a/src/term_manager.rs
+++ b/src/term_manager.rs
@@ -104,17 +104,23 @@ impl Display {
/// When a confirmation is needed we ask the user to input `'y'` or
/// something else.
fn first_line(&mut self, status: &Status, disk_space: String) -> FmResult<()> {
- let first_row = self.create_first_row(status, disk_space)?;
- self.draw_colored_strings(first_row)?;
+ let (offset, first_row) = self.create_first_row(status, disk_space)?;
+ self.draw_colored_strings(offset, first_row)?;
Ok(())
}
- fn create_first_row(&mut self, status: &Status, disk_space: String) -> FmResult<Vec<String>> {
+ fn create_first_row(
+ &mut self,
+ status: &Status,
+ disk_space: String,
+ ) -> FmResult<(usize, Vec<String>)> {
let tab = status.selected_non_mut();
- Ok(match tab.mode {
+ let mut offset = 0;
+ let first_row = match tab.mode {
Mode::Normal => {
+ offset = self.tab_bar(status)?;
vec![
- format!("Tab: {}/{} ", status.index + 1, status.len()),
+ // format!("Tab: {}/{} ", status.index + 1, status.len()),
format!("{} ", tab.path_content.path_to_str()?),
format!("{} files ", tab.path_content.files.len()),
format!("{} ", tab.path_content.used_space()),
@@ -149,13 +155,38 @@ impl Display {
format!("{}", tab.input.string.clone()),
]
}
- })
+ };
+ Ok((offset, first_row))
+ }
+
+ fn tab_bar(&self, status: &Status) -> FmResult<usize> {
+ let mut attr = Attr::default();
+ self.term.print_with_attr(0, 0, "[", attr)?;
+ let (number_of_tabs, selected_index) = status.len_index_of_tabs();
+
+ for tab in 0..(number_of_tabs) {
+ if tab == selected_index {
+ attr = Attr {
+ bg: Color::default(),
+ fg: Color::CYAN,
+ effect: Effect::REVERSE,
+ };
+ }
+ self.term
+ .print_with_attr(0, 2 * tab + 1, &format!("{}", tab), attr)?;
+ attr = Attr::default();
+ self.term
+ .print_with_attr(0, 2 * number_of_tabs, " ", Attr::default())?;
+ }
+ self.term
+ .print_with_attr(0, 2 * number_of_tabs, "]", Attr::default())?;
+ Ok(2 * number_of_tabs + 2)
}
- fn draw_colored_strings(&self, first_row: Vec<String>) -> FmResult<()> {
+ fn draw_colored_strings(&self, offset: usize, first_row: Vec<String>) -> FmResult<()> {
let mut col = 0;
for (text, color) in std::iter::zip(first_row.iter(), Self::LINE_COLORS.iter().cycle()) {
- self.term.print_with_attr(0, col, text, *color)?;
+ self.term.print_with_attr(0, offset + col, text, *color)?;
col += text.len()
}
Ok(())