diff --git a/src/cascadia/TerminalApp/SuggestionsControl.cpp b/src/cascadia/TerminalApp/SuggestionsControl.cpp index e6f482ac19..80206ea5dd 100644 --- a/src/cascadia/TerminalApp/SuggestionsControl.cpp +++ b/src/cascadia/TerminalApp/SuggestionsControl.cpp @@ -269,6 +269,8 @@ namespace winrt::TerminalApp::implementation const auto selectedCommand = _filteredActionsView().SelectedItem(); const auto filteredCommand{ selectedCommand.try_as() }; + _filteredActionsView().ScrollIntoView(selectedCommand); + PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"SelectedItem" }); // Make sure to not send the preview if we're collapsed. This can @@ -795,14 +797,46 @@ namespace winrt::TerminalApp::implementation // here will ensure that we can check this case appropriately. _lastFilterTextWasEmpty = _searchBox().Text().empty(); - const auto lastSelectedIndex = _filteredActionsView().SelectedIndex(); + const auto lastSelectedIndex = std::max(0, _filteredActionsView().SelectedIndex()); // SelectedIndex will return -1 for "nothing" _updateFilteredActions(); - // In the command line mode we want the user to explicitly select the command - _filteredActionsView().SelectedIndex(std::min(lastSelectedIndex, _filteredActionsView().Items().Size() - 1)); + if (const auto newSelectedIndex = _filteredActionsView().SelectedIndex(); + newSelectedIndex == -1) + { + // Make sure something stays selected + _scrollToIndex(lastSelectedIndex); + } + else + { + // BODGY: Calling ScrollIntoView on a ListView doesn't always work + // immediately after a change to the items. See: + // https://stackoverflow.com/questions/16942580/why-doesnt-listview-scrollintoview-ever-work + // The SelectionChanged thing we do (in _selectedCommandChanged), + // but because we're also not changing the actual selected item when + // the size of the list grows (it _stays_ selected, so it never + // _changes_), we never get a SelectionChanged. + // + // To mitigate, only in the case of totally clearing out the filter + // (like hitting `esc`), we want to briefly select the 0th item, + // then immediately select the one we want to make visible. That + // will make sure we get a SelectionChanged when the ListView is + // ready, and we can use that to scroll to the right item. + // + // If we do this on _every_ change, then the preview text flickers + // between the 0th item and the correct one. + if (_lastFilterTextWasEmpty) + { + _filteredActionsView().SelectedIndex(0); + } + _scrollToIndex(newSelectedIndex); + } const auto currentNeedleHasResults{ _filteredActions.Size() > 0 }; + if (!currentNeedleHasResults) + { + PreviewAction.raise(*this, nullptr); + } _noMatchesText().Visibility(currentNeedleHasResults ? Visibility::Collapsed : Visibility::Visible); if (auto automationPeer{ Automation::Peers::FrameworkElementAutomationPeer::FromElement(_searchBox()) }) { @@ -1203,7 +1237,7 @@ namespace winrt::TerminalApp::implementation if (_direction == TerminalApp::SuggestionsDirection::BottomUp) { const auto last = _filteredActionsView().Items().Size() - 1; - _filteredActionsView().SelectedIndex(last); + _scrollToIndex(last); } // Move the cursor to the very last position, so it starts immediately // after the text. This is apparently done by starting a 0-wide