From e68526b8d8fce059a0ccd1026a6d6e9780efa808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pol=C3=A1=C5=A1ek?= Date: Fri, 5 Dec 2025 23:32:24 +0100 Subject: [PATCH] CmdPal: Add configuration option for Escape key behavior (#43354) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary of the Pull Request This PR adds a new option to the **General** page in **Settings**: Escape key behavior — a dropdown with the following choices: - Clear search first, then go back - Current behavior. - If the search box contains text, it is cleared; otherwise goes back. - On the home page, CmdPal is dismissed. - Go back - Leaves the search text intact. - If the page is not transient, the search text reappears when returning. - On the home page, CmdPal is dismissed. - Hide window and go home (Always dismiss) - Immediately dismisses CmdPal and navigates to the home page. - Ignores the **Go home when activated** setting. - Search text is cleared. - Hide window - Just hides the window. - Intended to be used with #43355. This implementation preserves existing behavior, except for **Always dismiss**, which always forces navigation to the home page. ## Pictures? Pictures! image ## PR Checklist - [x] Closes: #38311 - [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end-user-facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed --- .../Messages/DismissMessage.cs | 4 +- .../Messages/NavigateBackMessage.cs | 4 +- .../ShellViewModel.cs | 4 +- .../SettingsModel.cs | 10 ++++ .../SettingsViewModel.cs | 10 ++++ .../Controls/SearchBar.xaml.cs | 49 ++++++++++++++----- .../Microsoft.CmdPal.UI/MainWindow.xaml.cs | 5 ++ .../Pages/ShellPage.xaml.cs | 2 +- .../Settings/GeneralPage.xaml | 9 ++++ .../Strings/en-us/Resources.resw | 18 +++++++ 10 files changed, 93 insertions(+), 22 deletions(-) diff --git a/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.ViewModels/Messages/DismissMessage.cs b/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.ViewModels/Messages/DismissMessage.cs index 98a5b34562..8150ee6584 100644 --- a/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.ViewModels/Messages/DismissMessage.cs +++ b/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.ViewModels/Messages/DismissMessage.cs @@ -4,6 +4,4 @@ namespace Microsoft.CmdPal.Core.ViewModels.Messages; -public record DismissMessage() -{ -} +public record DismissMessage(bool ForceGoHome = false); diff --git a/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.ViewModels/Messages/NavigateBackMessage.cs b/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.ViewModels/Messages/NavigateBackMessage.cs index 3e085184ed..e6152b75d5 100644 --- a/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.ViewModels/Messages/NavigateBackMessage.cs +++ b/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.ViewModels/Messages/NavigateBackMessage.cs @@ -4,6 +4,4 @@ namespace Microsoft.CmdPal.Core.ViewModels.Messages; -public record NavigateBackMessage(bool FromBackspace = false) -{ -} +public record NavigateBackMessage(bool FromBackspace = false); diff --git a/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.ViewModels/ShellViewModel.cs b/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.ViewModels/ShellViewModel.cs index 046d7ea336..2abbd83d3e 100644 --- a/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.ViewModels/ShellViewModel.cs +++ b/src/modules/cmdpal/Core/Microsoft.CmdPal.Core.ViewModels/ShellViewModel.cs @@ -378,7 +378,7 @@ public partial class ShellViewModel : ObservableObject, { // Reset the palette to the main page and dismiss GoHome(withAnimation: false, focusSearch: false); - WeakReferenceMessenger.Default.Send(); + WeakReferenceMessenger.Default.Send(new DismissMessage()); break; } @@ -398,7 +398,7 @@ public partial class ShellViewModel : ObservableObject, case CommandResultKind.Hide: { // Keep this page open, but hide the palette. - WeakReferenceMessenger.Default.Send(); + WeakReferenceMessenger.Default.Send(new DismissMessage()); break; } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsModel.cs index aee23ef0ca..dae50b3f3e 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsModel.cs @@ -60,6 +60,8 @@ public partial class SettingsModel : ObservableObject public TimeSpan AutoGoHomeInterval { get; set; } = Timeout.InfiniteTimeSpan; + public EscapeKeyBehavior EscapeKeyBehaviorSetting { get; set; } = EscapeKeyBehavior.ClearSearchFirstThenGoBack; + // END SETTINGS /////////////////////////////////////////////////////////////////////////// @@ -282,3 +284,11 @@ public enum MonitorBehavior InPlace = 3, ToLast = 4, } + +public enum EscapeKeyBehavior +{ + ClearSearchFirstThenGoBack = 0, + AlwaysGoBack = 1, + AlwaysDismiss = 2, + AlwaysHide = 3, +} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsViewModel.cs index 4d44db7d8a..586670bff7 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsViewModel.cs @@ -160,6 +160,16 @@ public partial class SettingsViewModel : INotifyPropertyChanged } } + public int EscapeKeyBehaviorIndex + { + get => (int)_settings.EscapeKeyBehaviorSetting; + set + { + _settings.EscapeKeyBehaviorSetting = (EscapeKeyBehavior)value; + Save(); + } + } + public ObservableCollection CommandProviders { get; } = []; public SettingsExtensionsViewModel Extensions { get; } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/SearchBar.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/SearchBar.xaml.cs index f5bac4a286..169b34a8b0 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/SearchBar.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/SearchBar.xaml.cs @@ -7,8 +7,10 @@ using CommunityToolkit.WinUI; using Microsoft.CmdPal.Core.ViewModels; using Microsoft.CmdPal.Core.ViewModels.Commands; using Microsoft.CmdPal.Core.ViewModels.Messages; -using Microsoft.CmdPal.UI.Messages; +using Microsoft.CmdPal.Ext.ClipboardHistory.Messages; +using Microsoft.CmdPal.UI.ViewModels; using Microsoft.CmdPal.UI.Views; +using Microsoft.Extensions.DependencyInjection; using Microsoft.UI.Dispatching; using Microsoft.UI.Input; using Microsoft.UI.Xaml; @@ -49,6 +51,8 @@ public sealed partial class SearchBar : UserControl, // 0.6+ suggestions private string? _textToSuggest; + private SettingsModel Settings => App.Current.Services.GetRequiredService(); + public PageViewModel? CurrentPageViewModel { get => (PageViewModel?)GetValue(CurrentPageViewModelProperty); @@ -131,20 +135,39 @@ public sealed partial class SearchBar : UserControl, } else if (e.Key == VirtualKey.Escape) { - if (string.IsNullOrEmpty(FilterBox.Text)) + switch (Settings.EscapeKeyBehaviorSetting) { - WeakReferenceMessenger.Default.Send(new()); - } - else - { - // Clear the search box - FilterBox.Text = string.Empty; + case EscapeKeyBehavior.AlwaysGoBack: + WeakReferenceMessenger.Default.Send(new()); + break; - // hack TODO GH #245 - if (CurrentPageViewModel is not null) - { - CurrentPageViewModel.SearchTextBox = FilterBox.Text; - } + case EscapeKeyBehavior.AlwaysDismiss: + WeakReferenceMessenger.Default.Send(new(ForceGoHome: true)); + break; + + case EscapeKeyBehavior.AlwaysHide: + WeakReferenceMessenger.Default.Send(new()); + break; + + case EscapeKeyBehavior.ClearSearchFirstThenGoBack: + default: + if (string.IsNullOrEmpty(FilterBox.Text)) + { + WeakReferenceMessenger.Default.Send(new()); + } + else + { + // Clear the search box + FilterBox.Text = string.Empty; + + // hack TODO GH #245 + if (CurrentPageViewModel is not null) + { + CurrentPageViewModel.SearchTextBox = FilterBox.Text; + } + } + + break; } e.Handled = true; diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs index 2936f8447e..1655626714 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs @@ -530,6 +530,11 @@ public sealed partial class MainWindow : WindowEx, public void Receive(DismissMessage message) { + if (message.ForceGoHome) + { + WeakReferenceMessenger.Default.Send(new GoHomeMessage(false, false)); + } + // This might come in off the UI thread. Make sure to hop back. DispatcherQueue.TryEnqueue(() => { diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Pages/ShellPage.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Pages/ShellPage.xaml.cs index dc12cf142b..7e8dc9eebd 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Pages/ShellPage.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Pages/ShellPage.xaml.cs @@ -133,7 +133,7 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page, if (!message.FromBackspace) { // If we can't go back then we must be at the top and thus escape again should quit. - WeakReferenceMessenger.Default.Send(); + WeakReferenceMessenger.Default.Send(new DismissMessage()); PowerToysTelemetry.Log.WriteEvent(new CmdPalDismissedOnEsc()); } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Settings/GeneralPage.xaml b/src/modules/cmdpal/Microsoft.CmdPal.UI/Settings/GeneralPage.xaml index 5f3ea2a55e..eb0264a683 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Settings/GeneralPage.xaml +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Settings/GeneralPage.xaml @@ -89,6 +89,15 @@ + + + + + + + + + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Strings/en-us/Resources.resw b/src/modules/cmdpal/Microsoft.CmdPal.UI/Strings/en-us/Resources.resw index ef4458d803..6ed0a3bfaa 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Strings/en-us/Resources.resw +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Strings/en-us/Resources.resw @@ -556,4 +556,22 @@ Right-click to remove the key combination, thereby deactivating the shortcut. Extensions + + Clear search first, then go back + + + Go back + + + Hide window and go home + + + Hide window + + + Escape key behavior + + + Choose how Escape key behaves + \ No newline at end of file