diff --git a/crates/file_finder/Cargo.toml b/crates/file_finder/Cargo.toml index 9bcfb553e5..72ac5e5562 100644 --- a/crates/file_finder/Cargo.toml +++ b/crates/file_finder/Cargo.toml @@ -37,6 +37,7 @@ editor = { workspace = true, features = ["test-support"] } env_logger.workspace = true gpui = { workspace = true, features = ["test-support"] } language = { workspace = true, features = ["test-support"] } +picker = { workspace = true, features = ["test-support"] } serde_json.workspace = true theme = { workspace = true, features = ["test-support"] } workspace = { workspace = true, features = ["test-support"] } diff --git a/crates/file_finder/src/file_finder_tests.rs b/crates/file_finder/src/file_finder_tests.rs index eaa591c84f..9d70581f91 100644 --- a/crates/file_finder/src/file_finder_tests.rs +++ b/crates/file_finder/src/file_finder_tests.rs @@ -1747,6 +1747,45 @@ async fn test_extending_modifiers_does_not_confirm_selection(cx: &mut gpui::Test active_file_picker(&workspace, cx); } +#[gpui::test] +async fn test_repeat_toggle_action(cx: &mut gpui::TestAppContext) { + let app_state = init_test(cx); + app_state + .fs + .as_fake() + .insert_tree( + "/test", + json!({ + "00.txt": "", + "01.txt": "", + "02.txt": "", + "03.txt": "", + "04.txt": "", + "05.txt": "", + }), + ) + .await; + + let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await; + let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx)); + + cx.dispatch_action(Toggle::default()); + let picker = active_file_picker(&workspace, cx); + picker.update(cx, |picker, _| { + assert_eq!(picker.delegate.selected_index, 0); + assert_eq!(picker.logical_scroll_top_index(), 0); + }); + + // When toggling repeatedly, the picker scrolls to reveal the selected item. + cx.dispatch_action(Toggle::default()); + cx.dispatch_action(Toggle::default()); + cx.dispatch_action(Toggle::default()); + picker.update(cx, |picker, _| { + assert_eq!(picker.delegate.selected_index, 3); + assert_eq!(picker.logical_scroll_top_index(), 3); + }); +} + async fn open_close_queried_buffer( input: &str, expected_matches: usize, diff --git a/crates/gpui/src/elements/uniform_list.rs b/crates/gpui/src/elements/uniform_list.rs index eafb6b2d72..a08acf95b0 100644 --- a/crates/gpui/src/elements/uniform_list.rs +++ b/crates/gpui/src/elements/uniform_list.rs @@ -98,6 +98,13 @@ impl UniformListScrollHandle { pub fn scroll_to_item(&mut self, ix: usize) { self.deferred_scroll_to_item.replace(Some(ix)); } + + /// Get the index of the topmost visible child. + pub fn logical_scroll_top_index(&self) -> usize { + self.deferred_scroll_to_item + .borrow() + .unwrap_or_else(|| self.base_handle.logical_scroll_top().0) + } } impl Styled for UniformList { diff --git a/crates/picker/Cargo.toml b/crates/picker/Cargo.toml index cd83d1d805..db42c1cd80 100644 --- a/crates/picker/Cargo.toml +++ b/crates/picker/Cargo.toml @@ -12,6 +12,9 @@ workspace = true path = "src/picker.rs" doctest = false +[features] +test-support = [] + [dependencies] anyhow.workspace = true editor.workspace = true diff --git a/crates/picker/src/picker.rs b/crates/picker/src/picker.rs index 3ed0080fe5..0cae18f50b 100644 --- a/crates/picker/src/picker.rs +++ b/crates/picker/src/picker.rs @@ -310,7 +310,7 @@ impl Picker { let count = self.delegate.match_count(); let index = self.delegate.selected_index(); let new_index = if index + 1 == count { 0 } else { index + 1 }; - self.set_selected_index(new_index, false, cx); + self.set_selected_index(new_index, true, cx); cx.notify(); } @@ -538,6 +538,16 @@ impl Picker { .into_any_element(), } } + + #[cfg(any(test, feature = "test-support"))] + pub fn logical_scroll_top_index(&self) -> usize { + match &self.element_container { + ElementContainer::List(state) => state.logical_scroll_top().item_ix, + ElementContainer::UniformList(scroll_handle) => { + scroll_handle.logical_scroll_top_index() + } + } + } } impl EventEmitter for Picker {}