From 6642c805b71867df0b1837265e096aa1c3141f39 Mon Sep 17 00:00:00 2001 From: Michael Jolley Date: Wed, 16 Jul 2025 06:25:24 -0500 Subject: [PATCH] Context menu cleanup (#40584) Addressing items in #40583 - [x] When navigating the context menu with the up/down keys, separators should not be selectable. - [x] [For context items with a super long title, we need to trim those with an ellipsis. Ideally, we'd show a tooltip for just those items.](https://github.com/microsoft/PowerToys/issues/40313) - [x] [Context menu search bar text size doesn't update after changing system text size](https://github.com/microsoft/PowerToys/issues/39648) - [x] Weird "kick out" on first context menu item - [x] [Primary button doesn't work if the command has more items (fix regression)](https://github.com/microsoft/PowerToys/issues/40624) Example of long context menu item titles with tooltips: (@niels9001, look okay?) https://github.com/user-attachments/assets/fc0a4034-9c22-48ee-a3f0-44fcc2f294a6 closes #40624 --- .../CommandBarViewModel.cs | 1 + .../ContextMenuViewModel.cs | 36 +------ .../Controls/CommandBar.xaml.cs | 2 - .../Controls/ContextMenu.xaml | 24 ++++- .../Controls/ContextMenu.xaml.cs | 99 +++++++++++++++---- .../Pages/SampleListPage.cs | 2 +- 6 files changed, 106 insertions(+), 58 deletions(-) diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/CommandBarViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/CommandBarViewModel.cs index 4dda2c455d..f506c127f2 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/CommandBarViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/CommandBarViewModel.cs @@ -149,6 +149,7 @@ public partial class CommandBarViewModel : ObservableObject, if (command.HasMoreCommands) { + WeakReferenceMessenger.Default.Send(new(command.Command.Model, command.Model)); return ContextKeybindingResult.KeepOpen; } else diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/ContextMenuViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/ContextMenuViewModel.cs index 42820bb0d3..bcc414859a 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/ContextMenuViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/ContextMenuViewModel.cs @@ -22,15 +22,8 @@ public partial class ContextMenuViewModel : ObservableObject, get => field; set { - if (field != null) - { - field.PropertyChanged -= SelectedItemPropertyChanged; - } - field = value; - SetSelectedItem(value); - - OnPropertyChanged(nameof(SelectedItem)); + UpdateContextItems(); } } @@ -68,33 +61,6 @@ public partial class ContextMenuViewModel : ObservableObject, OnPropertyChanged(nameof(FilterOnTop)); } - private void SetSelectedItem(ICommandBarContext? value) - { - if (value != null) - { - value.PropertyChanged += SelectedItemPropertyChanged; - } - else - { - if (SelectedItem != null) - { - SelectedItem.PropertyChanged -= SelectedItemPropertyChanged; - } - } - - UpdateContextItems(); - } - - private void SelectedItemPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) - { - switch (e.PropertyName) - { - case nameof(SelectedItem.HasMoreCommands): - UpdateContextItems(); - break; - } - } - public void UpdateContextItems() { if (SelectedItem != null) diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/CommandBar.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/CommandBar.xaml.cs index 91b4509b54..208024fdcc 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/CommandBar.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/CommandBar.xaml.cs @@ -6,13 +6,11 @@ using CommunityToolkit.Mvvm.Messaging; using Microsoft.CmdPal.Core.ViewModels; using Microsoft.CmdPal.Core.ViewModels.Messages; using Microsoft.CmdPal.UI.Views; -using Microsoft.UI.Input; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls.Primitives; using Microsoft.UI.Xaml.Input; using Windows.System; -using Windows.UI.Core; namespace Microsoft.CmdPal.UI.Controls; diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/ContextMenu.xaml b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/ContextMenu.xaml index 94466378bf..27ac608240 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/ContextMenu.xaml +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/ContextMenu.xaml @@ -20,6 +20,7 @@ + + Text="{x:Bind Title}" + TextTrimming="WordEllipsis" + TextWrapping="NoWrap"> + + + + + Text="{x:Bind Title}" + TextTrimming="WordEllipsis" + TextWrapping="NoWrap"> + + + + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/ContextMenu.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/ContextMenu.xaml.cs index ed6554b3a3..8047cd52a3 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/ContextMenu.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/ContextMenu.xaml.cs @@ -178,34 +178,97 @@ public sealed partial class ContextMenu : UserControl, { if (e.Key == VirtualKey.Up) { - // navigate previous - if (CommandsDropdown.SelectedIndex > 0) - { - CommandsDropdown.SelectedIndex--; - } - else - { - CommandsDropdown.SelectedIndex = CommandsDropdown.Items.Count - 1; - } + NavigateUp(); e.Handled = true; } else if (e.Key == VirtualKey.Down) { - // navigate next - if (CommandsDropdown.SelectedIndex < CommandsDropdown.Items.Count - 1) - { - CommandsDropdown.SelectedIndex++; - } - else - { - CommandsDropdown.SelectedIndex = 0; - } + NavigateDown(); e.Handled = true; } } + private void NavigateUp() + { + var newIndex = CommandsDropdown.SelectedIndex; + + if (CommandsDropdown.SelectedIndex > 0) + { + newIndex--; + + while ( + newIndex >= 0 && + IsSeparator(CommandsDropdown.Items[newIndex]) && + newIndex != CommandsDropdown.SelectedIndex) + { + newIndex--; + } + + if (newIndex < 0) + { + newIndex = CommandsDropdown.Items.Count - 1; + + while ( + newIndex >= 0 && + IsSeparator(CommandsDropdown.Items[newIndex]) && + newIndex != CommandsDropdown.SelectedIndex) + { + newIndex--; + } + } + } + else + { + newIndex = CommandsDropdown.Items.Count - 1; + } + + CommandsDropdown.SelectedIndex = newIndex; + } + + private void NavigateDown() + { + var newIndex = CommandsDropdown.SelectedIndex; + + if (CommandsDropdown.SelectedIndex == CommandsDropdown.Items.Count - 1) + { + newIndex = 0; + } + else + { + newIndex++; + + while ( + newIndex < CommandsDropdown.Items.Count && + IsSeparator(CommandsDropdown.Items[newIndex]) && + newIndex != CommandsDropdown.SelectedIndex) + { + newIndex++; + } + + if (newIndex >= CommandsDropdown.Items.Count) + { + newIndex = 0; + + while ( + newIndex < CommandsDropdown.Items.Count && + IsSeparator(CommandsDropdown.Items[newIndex]) && + newIndex != CommandsDropdown.SelectedIndex) + { + newIndex++; + } + } + } + + CommandsDropdown.SelectedIndex = newIndex; + } + + private bool IsSeparator(object item) + { + return item is SeparatorContextItemViewModel; + } + private void UpdateUiForStackChange() { ContextFilterBox.Text = string.Empty; diff --git a/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPage.cs b/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPage.cs index 1777fb217c..2c3fabf5c6 100644 --- a/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPage.cs +++ b/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPage.cs @@ -99,7 +99,7 @@ internal sealed partial class SampleListPage : ListPage new CommandContextItem( new ToastCommand("Nested B invoked") { Name = "Do it", Icon = new IconInfo("B") }) { - Title = "Nested B...", + Title = "Nested B with a really, really long title that should be trimmed", RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, vkey: VirtualKey.B), MoreCommands = [ new CommandContextItem(