diff --git a/database-tree/src/databasetree.rs b/database-tree/src/databasetree.rs index 032ab36..d2a7c73 100644 --- a/database-tree/src/databasetree.rs +++ b/database-tree/src/databasetree.rs @@ -10,6 +10,8 @@ use std::{collections::BTreeSet, usize}; pub enum MoveSelection { Up, Down, + MultipleUp, + MultipleDown, Left, Right, Top, @@ -102,8 +104,10 @@ impl DatabaseTree { pub fn move_selection(&mut self, dir: MoveSelection) -> bool { self.selection.map_or(false, |selection| { let new_index = match dir { - MoveSelection::Up => self.selection_updown(selection, true), - MoveSelection::Down => self.selection_updown(selection, false), + MoveSelection::Up => self.selection_up(selection, 1), + MoveSelection::Down => self.selection_down(selection, 1), + MoveSelection::MultipleUp => self.selection_up(selection, 10), + MoveSelection::MultipleDown => self.selection_down(selection, 10), MoveSelection::Left => self.selection_left(selection), MoveSelection::Right => self.selection_right(selection), MoveSelection::Top => Self::selection_start(selection), @@ -188,6 +192,59 @@ impl DatabaseTree { } } + fn selection_up(&self, current_index: usize, lines: usize) -> Option { + let mut index = current_index; + + 'a: for _ in 0..lines { + loop { + if index == 0 { + break 'a; + } + + index = index.saturating_sub(1); + + if self.is_visible_index(index) { + break; + } + } + } + + if index == current_index { + None + } else { + Some(index) + } + } + + fn selection_down(&self, current_index: usize, lines: usize) -> Option { + let mut index = current_index; + let last_visible_item_index = self + .items + .tree_items + .iter() + .rposition(|x| x.info().is_visible())?; + + 'a: for _ in 0..lines { + loop { + if index >= last_visible_item_index { + break 'a; + } + + index = index.saturating_add(1); + + if self.is_visible_index(index) { + break; + } + } + } + + if index == current_index { + None + } else { + Some(index) + } + } + fn selection_updown(&self, current_index: usize, up: bool) -> Option { let mut index = current_index; @@ -207,22 +264,6 @@ impl DatabaseTree { break; } - let item = self - .items - .tree_items - .iter() - .filter(|item| item.info().is_visible()) - .last() - .unwrap(); - - if !up - && (self.selected_item().unwrap().kind().is_database() - || self.selected_item().unwrap().kind().is_schema()) - && self.selected_item().unwrap() == item - { - break; - } - new_index }; @@ -374,6 +415,62 @@ mod test { assert_eq!(tree.selection, Some(2)); } + #[test] + fn test_selection_multiple_up_down() { + let items = vec![Database::new( + "a".to_string(), + vec!["b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"] + .iter() + .map(|x| Table::new(x.to_string()).into()) + .collect(), + )]; + + // a + // b + // ... + // j + + let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap(); + + assert!(tree.move_selection(MoveSelection::Right)); + assert_eq!(tree.selection, Some(0)); + assert!(tree.move_selection(MoveSelection::MultipleDown)); + assert_eq!(tree.selection, Some(10)); + + tree.selection = Some(11); + assert!(tree.move_selection(MoveSelection::MultipleUp)); + assert_eq!(tree.selection, Some(1)); + + let items = vec![Database::new( + "a".to_string(), + vec![Schema { + name: "b".to_string(), + tables: vec!["c", "d", "e", "f", "g", "h", "i", "j", "k", "l"] + .iter() + .map(|x| Table::new(x.to_string()).into()) + .collect(), + } + .into()], + )]; + + // a + // b + // c + // ... + // l + + let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap(); + + assert!(tree.move_selection(MoveSelection::Right)); + assert_eq!(tree.selection, Some(0)); + assert!(tree.move_selection(MoveSelection::MultipleDown)); + assert_eq!(tree.selection, Some(10)); + + tree.selection = Some(11); + assert!(tree.move_selection(MoveSelection::MultipleUp)); + assert_eq!(tree.selection, Some(1)); + } + #[test] fn test_selection_skips_collapsed() { let items = vec![ diff --git a/database-tree/src/databasetreeitems.rs b/database-tree/src/databasetreeitems.rs index ca3a77f..95659c4 100644 --- a/database-tree/src/databasetreeitems.rs +++ b/database-tree/src/databasetreeitems.rs @@ -24,7 +24,9 @@ impl DatabaseTreeItems { tree_items: self .tree_items .iter() - .filter(|item| item.is_database() || item.is_match(&filter_text)) + .filter(|item| { + item.is_database() || item.kind().is_schema() || item.is_match(&filter_text) + }) .map(|item| { let mut item = item.clone(); if item.is_database() { diff --git a/src/components/connections.rs b/src/components/connections.rs index f1d9ac3..383084f 100644 --- a/src/components/connections.rs +++ b/src/components/connections.rs @@ -31,32 +31,46 @@ impl ConnectionsComponent { } } - fn next_connection(&mut self) { + fn next_connection(&mut self, lines: usize) { let i = match self.state.selected() { Some(i) => { - if i >= self.connections.len() - 1 { - self.connections.len() - 1 + if i + lines >= self.connections.len() { + Some(self.connections.len() - 1) } else { - i + 1 + Some(i + lines) } } - None => 0, + None => None, }; - self.state.select(Some(i)); + self.state.select(i); } - fn previous_connection(&mut self) { + fn previous_connection(&mut self, lines: usize) { let i = match self.state.selected() { Some(i) => { - if i == 0 { - 0 + if i <= lines { + Some(0) } else { - i - 1 + Some(i - lines) } } - None => 0, + None => None, }; - self.state.select(Some(i)); + self.state.select(i); + } + + fn scroll_to_top(&mut self) { + if self.connections.is_empty() { + return; + } + self.state.select(Some(0)); + } + + fn scroll_to_bottom(&mut self) { + if self.connections.is_empty() { + return; + } + self.state.select(Some(self.connections.len() - 1)); } pub fn selected_connection(&self) -> Option<&Connection> { @@ -101,10 +115,22 @@ impl Component for ConnectionsComponent { fn event(&mut self, key: Key) -> Result { if key == self.key_config.scroll_down { - self.next_connection(); + self.next_connection(1); return Ok(EventState::Consumed); } else if key == self.key_config.scroll_up { - self.previous_connection(); + self.previous_connection(1); + return Ok(EventState::Consumed); + } else if key == self.key_config.scroll_down_multiple_lines { + self.next_connection(10); + return Ok(EventState::NotConsumed); + } else if key == self.key_config.scroll_up_multiple_lines { + self.previous_connection(10); + return Ok(EventState::Consumed); + } else if key == self.key_config.scroll_to_top { + self.scroll_to_top(); + return Ok(EventState::Consumed); + } else if key == self.key_config.scroll_to_bottom { + self.scroll_to_bottom(); return Ok(EventState::Consumed); } Ok(EventState::NotConsumed) diff --git a/src/components/table.rs b/src/components/table.rs index f1ebd2e..b420b00 100644 --- a/src/components/table.rs +++ b/src/components/table.rs @@ -123,7 +123,7 @@ impl TableComponent { self.selected_row.select(i); } - fn scroll_top(&mut self) { + fn scroll_to_top(&mut self) { if self.rows.is_empty() { return; } @@ -131,7 +131,7 @@ impl TableComponent { self.selected_row.select(Some(0)); } - fn scroll_bottom(&mut self) { + fn scroll_to_bottom(&mut self) { if self.rows.is_empty() { return; } @@ -504,10 +504,10 @@ impl Component for TableComponent { self.previous_row(10); return Ok(EventState::Consumed); } else if key == self.key_config.scroll_to_top { - self.scroll_top(); + self.scroll_to_top(); return Ok(EventState::Consumed); } else if key == self.key_config.scroll_to_bottom { - self.scroll_bottom(); + self.scroll_to_bottom(); return Ok(EventState::Consumed); } else if key == self.key_config.scroll_right { self.next_column(); diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 6f81822..47e937c 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -10,6 +10,10 @@ pub fn common_nav(key: Key, key_config: &KeyConfig) -> Option { Some(MoveSelection::Down) } else if key == key_config.scroll_up { Some(MoveSelection::Up) + } else if key == key_config.scroll_down_multiple_lines { + Some(MoveSelection::MultipleDown) + } else if key == key_config.scroll_up_multiple_lines { + Some(MoveSelection::MultipleUp) } else if key == key_config.scroll_right { Some(MoveSelection::Right) } else if key == key_config.scroll_left {