From c887b8597cb82f61d9785ba63d57a22ca25463cf Mon Sep 17 00:00:00 2001 From: Richard Menzies <52405405+WizardOhio24@users.noreply.github.com> Date: Tue, 29 Sep 2020 19:13:09 +0100 Subject: [PATCH] Fix scrolling in filetrees (#296) * Add selection offset considering non-visible items to scroll_top * Add test for filetree scrolling * Fix scroll test * Fix scroll when multiple folders folded onto one line are folded up * Add test for not visible folded up folder scrolling correctly --- src/components/filetree.rs | 128 +++++++++++++++++++++++++++++++++++-- 1 file changed, 121 insertions(+), 7 deletions(-) diff --git a/src/components/filetree.rs b/src/components/filetree.rs index 34a89838..9786c129 100644 --- a/src/components/filetree.rs +++ b/src/components/filetree.rs @@ -222,11 +222,13 @@ impl FileTreeComponent { /// allowing folders to be folded up if they are alone in their directory fn build_vec_text_draw_info_for_drawing( &self, - ) -> (Vec, usize) { + ) -> (Vec, usize, usize) { let mut should_skip_over: usize = 0; let mut selection_offset: usize = 0; + let mut selection_offset_visible: usize = 0; let mut vec_draw_text_info: Vec = vec![]; let tree_items = self.tree.tree.items(); + for (index, item) in tree_items.iter().enumerate() { if should_skip_over > 0 { should_skip_over -= 1; @@ -236,6 +238,10 @@ impl FileTreeComponent { let index_above_select = index < self.tree.selection.unwrap_or(0); + if !item.info.visible && index_above_select { + selection_offset_visible += 1; + } + vec_draw_text_info.push(TextDrawInfo { name: item.info.path.clone(), indent: item.info.indent, @@ -260,9 +266,12 @@ impl FileTreeComponent { should_skip_over -= 1; break; } - // don't fold up if more than one folder in folder - if self.tree.tree.multiple_items_at_path(idx_temp) { + else if self + .tree + .tree + .multiple_items_at_path(idx_temp) + { should_skip_over -= 1; break; } else { @@ -280,7 +289,11 @@ impl FileTreeComponent { } } } - (vec_draw_text_info, selection_offset) + ( + vec_draw_text_info, + selection_offset, + selection_offset_visible, + ) } } @@ -314,8 +327,11 @@ impl DrawableComponent for FileTreeComponent { &self.theme, ); } else { - let (vec_draw_text_info, selection_offset) = - self.build_vec_text_draw_info_for_drawing(); + let ( + vec_draw_text_info, + selection_offset, + selection_offset_visible, + ) = self.build_vec_text_draw_info_for_drawing(); let select = self .tree @@ -327,7 +343,7 @@ impl DrawableComponent for FileTreeComponent { self.scroll_top.set(ui::calc_scroll_top( self.scroll_top.get(), tree_height, - select, + select.saturating_sub(selection_offset_visible), )); let items = vec_draw_text_info @@ -414,3 +430,101 @@ impl Component for FileTreeComponent { self.show_selection(focus); } } + +#[cfg(test)] +mod tests { + use super::*; + use asyncgit::StatusItemType; + + fn string_vec_to_status(items: &[&str]) -> Vec { + items + .iter() + .map(|a| StatusItem { + path: String::from(*a), + status: StatusItemType::Modified, + }) + .collect::>() + } + + #[test] + fn test_correct_scroll_position() { + let items = string_vec_to_status(&[ + "a/b/b1", // + "a/b/b2", // + "a/c/c1", // + ]); + + //0 a/ + //1 b/ + //2 b1 + //3 b2 + //4 c/ + //5 c1 + + // Set up test terminal + let test_backend = tui::backend::TestBackend::new(100, 100); + let mut terminal = tui::Terminal::new(test_backend) + .expect("Unable to set up terminal"); + let mut frame = terminal.get_frame(); + + // set up file tree + let mut ftc = FileTreeComponent::new( + "title", + true, + None, + SharedTheme::default(), + SharedKeyConfig::default(), + ); + ftc.update(&items) + .expect("Updating FileTreeComponent failed"); + + ftc.move_selection(MoveSelection::Down); // Move to b/ + ftc.move_selection(MoveSelection::Left); // Fold b/ + ftc.move_selection(MoveSelection::Down); // Move to c/ + + ftc.draw(&mut frame, Rect::new(0, 0, 10, 5)) + .expect("Draw failed"); + + assert_eq!(ftc.scroll_top.get(), 0); // should still be at top + } + + #[test] + fn test_correct_foldup_and_not_visible_scroll_position() { + let items = string_vec_to_status(&[ + "a/b/b1", // + "c/d1", // + "c/d2", // + ]); + + //0 a/b/ + //2 b1 + //3 c/ + //4 d1 + //5 d2 + + // Set up test terminal + let test_backend = tui::backend::TestBackend::new(100, 100); + let mut terminal = tui::Terminal::new(test_backend) + .expect("Unable to set up terminal"); + let mut frame = terminal.get_frame(); + + // set up file tree + let mut ftc = FileTreeComponent::new( + "title", + true, + None, + SharedTheme::default(), + SharedKeyConfig::default(), + ); + ftc.update(&items) + .expect("Updating FileTreeComponent failed"); + + ftc.move_selection(MoveSelection::Left); // Fold a/b/ + ftc.move_selection(MoveSelection::Down); // Move to c/ + + ftc.draw(&mut frame, Rect::new(0, 0, 10, 5)) + .expect("Draw failed"); + + assert_eq!(ftc.scroll_top.get(), 0); // should still be at top + } +}