Preview actions from the command palette on mouse hover (#18518)

Allow users to preview color schemes by hovering over them with the
mouse pointer in the Command Palette.

- This PR handles issue #18238. 
- This extends the previously closed issue #6689, which allowed the `UP`
and `DOWN` arrows to trigger a preview of color schemes in the Command
Palette.

This works by attaching event handlers for `PointerEntered` and
`PointerExited` to `ListViewItem` containers. When the mouse pointer
moves into the item's bounding area, the `PreviewAction` handler is
triggered to showcase the hovered color scheme. Conversely, when the
mouse pointer leaves the item's area, the `PreviewAction` is executed on
the selected item (generally from the `UP` and `DOWN` arrows).

**Important note:**

- This also provides previews for the other features that the
`ActionPreviewHandler` handles, such as the background opacity of the
terminal.

## Validation Steps Performed

- Hover a color scheme, and it becomes the active one.
- Pressing `ESC` at any point to dismiss the command palette, and the
scheme returns to the previous one.
- I did not add any additional test, though all existing ColorScheme
tests passed.

Closes #18238
This commit is contained in:
Éléa Dufresne 2025-03-14 17:00:10 -07:00 committed by GitHub
parent 70f85a4a35
commit a86c90a045
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 86 additions and 0 deletions

View File

@ -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:
// - <none>
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<winrt::Windows::UI::Xaml::Controls::ListViewItem>();
if (_currentMode == CommandPaletteMode::ActionMode && listViewItem)
{
const auto enteredItem = listViewItem.Content();
if (const auto filteredCommand{ enteredItem.try_as<winrt::TerminalApp::FilteredCommand>() })
{
if (const auto actionPaletteItem{ filteredCommand.Item().try_as<winrt::TerminalApp::ActionPaletteItem>() })
{
// 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:
// - <none>
// Return Value:
// - <none>
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<winrt::TerminalApp::FilteredCommand>() })
{
if (_currentMode == CommandPaletteMode::ActionMode && filteredCommand)
{
if (const auto actionPaletteItem{ filteredCommand.Item().try_as<winrt::TerminalApp::ActionPaletteItem>() })
{
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) void CommandPalette::_listItemSelectionChanged(const Windows::Foundation::IInspectable& /*sender*/, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& e)
{ {
// We don't care about... // We don't care about...
@ -1214,6 +1287,9 @@ namespace winrt::TerminalApp::implementation
ParentCommandName(L""); ParentCommandName(L"");
_currentNestedCommands.Clear(); _currentNestedCommands.Clear();
// revert any preview
_filteredActionsView().SelectedIndex(-1);
PreviewAction.raise(*this, nullptr); PreviewAction.raise(*this, nullptr);
} }
@ -1306,6 +1382,10 @@ namespace winrt::TerminalApp::implementation
else else
{ {
itemContainer.DataContext(args.Item()); itemContainer.DataContext(args.Item());
// attach the pointer event handlers to the container
itemContainer.PointerEntered({ this, &CommandPalette::_listItemPointerEntered });
itemContainer.PointerExited({ this, &CommandPalette::_listItemPointerExited });
} }
} }

View File

@ -78,6 +78,8 @@ namespace winrt::TerminalApp::implementation
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _commandsToFilter(); Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _commandsToFilter();
winrt::Windows::UI::Xaml::DispatcherTimer _pointerExitTimer{ nullptr }; // timer to debounce pointer exit events (used to smooth preview transitions)
bool _lastFilterTextWasEmpty{ true }; bool _lastFilterTextWasEmpty{ true };
void _populateCommands(); 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 _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 _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&); void _moveBackButtonClicked(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs&);