diff --git a/src/cascadia/TerminalApp/CommandPalette.cpp b/src/cascadia/TerminalApp/CommandPalette.cpp index bcee25c72d..6d5540703f 100644 --- a/src/cascadia/TerminalApp/CommandPalette.cpp +++ b/src/cascadia/TerminalApp/CommandPalette.cpp @@ -530,6 +530,79 @@ namespace winrt::TerminalApp::implementation } } + // Method Description: + // - This event is called when the user's mouse pointer enters an individual + // item from the list. We'll get the item that was hovered and "preview" + // the command that the user hovered. To do that, we'll dispatch the switch + // to tab command for this tab, but not dismiss the switcher. + // + // Arguments: + // - sender: the UI element that raised the event. + // Return Value: + // - + void CommandPalette::_listItemPointerEntered(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::UI::Xaml::Input::PointerRoutedEventArgs& /*args*/) + { + // cancel any pending exit timer to prevent an unwanted preview revert + if (_pointerExitTimer) + { + _pointerExitTimer.Stop(); + } + + const auto listViewItem = sender.try_as(); + if (_currentMode == CommandPaletteMode::ActionMode && listViewItem) + { + const auto enteredItem = listViewItem.Content(); + if (const auto filteredCommand{ enteredItem.try_as() }) + { + if (const auto actionPaletteItem{ filteredCommand.Item().try_as() }) + { + // immediately preview the hovered command + PreviewAction.raise(*this, actionPaletteItem.Command()); + } + } + } + } + + // Method Description: + // - This event is called when the user's mouse pointer exits an individual + // item from the list. We then revert to previewing the selected item rather + // than the hovered one, using a short delay (via a DispatcherTimer) to smooth + // transitions when rapidly moving between items. + // + // Arguments: + // - + // Return Value: + // - + void CommandPalette::_listItemPointerExited(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::UI::Xaml::Input::PointerRoutedEventArgs& /*args*/) + { + // if there is no exit timer, create one + if (!_pointerExitTimer) + { + _pointerExitTimer = winrt::Windows::UI::Xaml::DispatcherTimer(); + _pointerExitTimer.Interval(std::chrono::milliseconds(10)); + _pointerExitTimer.Tick([this](auto const&, auto const&) { + // when the timer ticks, revert the preview to the selected command + const auto selectedCommand = _filteredActionsView().SelectedItem(); + if (const auto filteredCommand{ selectedCommand.try_as() }) + { + if (_currentMode == CommandPaletteMode::ActionMode && filteredCommand) + { + if (const auto actionPaletteItem{ filteredCommand.Item().try_as() }) + { + PreviewAction.raise(*this, actionPaletteItem.Command()); + } + } + } + _pointerExitTimer.Stop(); + }); + } + + // restart the timer + _pointerExitTimer.Start(); + } + void CommandPalette::_listItemSelectionChanged(const Windows::Foundation::IInspectable& /*sender*/, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& e) { // We don't care about... @@ -1214,6 +1287,9 @@ namespace winrt::TerminalApp::implementation ParentCommandName(L""); _currentNestedCommands.Clear(); + + // revert any preview + _filteredActionsView().SelectedIndex(-1); PreviewAction.raise(*this, nullptr); } @@ -1306,6 +1382,10 @@ namespace winrt::TerminalApp::implementation else { itemContainer.DataContext(args.Item()); + + // attach the pointer event handlers to the container + itemContainer.PointerEntered({ this, &CommandPalette::_listItemPointerEntered }); + itemContainer.PointerExited({ this, &CommandPalette::_listItemPointerExited }); } } diff --git a/src/cascadia/TerminalApp/CommandPalette.h b/src/cascadia/TerminalApp/CommandPalette.h index 58e34bb1f9..0da7ee1334 100644 --- a/src/cascadia/TerminalApp/CommandPalette.h +++ b/src/cascadia/TerminalApp/CommandPalette.h @@ -78,6 +78,8 @@ namespace winrt::TerminalApp::implementation Windows::Foundation::Collections::IVector _commandsToFilter(); + winrt::Windows::UI::Xaml::DispatcherTimer _pointerExitTimer{ nullptr }; // timer to debounce pointer exit events (used to smooth preview transitions) + bool _lastFilterTextWasEmpty{ true }; void _populateCommands(); @@ -103,6 +105,10 @@ namespace winrt::TerminalApp::implementation void _listItemClicked(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::ItemClickEventArgs& e); + void _listItemPointerEntered(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& args); + + void _listItemPointerExited(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::Input::PointerRoutedEventArgs& args); + void _listItemSelectionChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& e); void _moveBackButtonClicked(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs&);