mirror of
https://github.com/microsoft/PowerToys
synced 2025-09-03 16:05:12 +00:00
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
This commit is contained in:
@@ -149,6 +149,7 @@ public partial class CommandBarViewModel : ObservableObject,
|
|||||||
|
|
||||||
if (command.HasMoreCommands)
|
if (command.HasMoreCommands)
|
||||||
{
|
{
|
||||||
|
WeakReferenceMessenger.Default.Send<PerformCommandMessage>(new(command.Command.Model, command.Model));
|
||||||
return ContextKeybindingResult.KeepOpen;
|
return ContextKeybindingResult.KeepOpen;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@@ -22,15 +22,8 @@ public partial class ContextMenuViewModel : ObservableObject,
|
|||||||
get => field;
|
get => field;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (field != null)
|
|
||||||
{
|
|
||||||
field.PropertyChanged -= SelectedItemPropertyChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
field = value;
|
field = value;
|
||||||
SetSelectedItem(value);
|
UpdateContextItems();
|
||||||
|
|
||||||
OnPropertyChanged(nameof(SelectedItem));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,33 +61,6 @@ public partial class ContextMenuViewModel : ObservableObject,
|
|||||||
OnPropertyChanged(nameof(FilterOnTop));
|
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()
|
public void UpdateContextItems()
|
||||||
{
|
{
|
||||||
if (SelectedItem != null)
|
if (SelectedItem != null)
|
||||||
|
@@ -6,13 +6,11 @@ using CommunityToolkit.Mvvm.Messaging;
|
|||||||
using Microsoft.CmdPal.Core.ViewModels;
|
using Microsoft.CmdPal.Core.ViewModels;
|
||||||
using Microsoft.CmdPal.Core.ViewModels.Messages;
|
using Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||||
using Microsoft.CmdPal.UI.Views;
|
using Microsoft.CmdPal.UI.Views;
|
||||||
using Microsoft.UI.Input;
|
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||||
using Microsoft.UI.Xaml.Input;
|
using Microsoft.UI.Xaml.Input;
|
||||||
using Windows.System;
|
using Windows.System;
|
||||||
using Windows.UI.Core;
|
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.UI.Controls;
|
namespace Microsoft.CmdPal.UI.Controls;
|
||||||
|
|
||||||
|
@@ -20,6 +20,7 @@
|
|||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<cmdpalUI:KeyChordToStringConverter x:Key="KeyChordToStringConverter" />
|
<cmdpalUI:KeyChordToStringConverter x:Key="KeyChordToStringConverter" />
|
||||||
|
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
||||||
|
|
||||||
<cmdpalUI:ContextItemTemplateSelector
|
<cmdpalUI:ContextItemTemplateSelector
|
||||||
x:Key="ContextItemTemplateSelector"
|
x:Key="ContextItemTemplateSelector"
|
||||||
@@ -43,9 +44,18 @@
|
|||||||
SourceKey="{x:Bind Icon}"
|
SourceKey="{x:Bind Icon}"
|
||||||
SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}" />
|
SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
|
x:Name="TitleTextBlock"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
|
MaxWidth="200"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Bind Title}" />
|
Text="{x:Bind Title}"
|
||||||
|
TextTrimming="WordEllipsis"
|
||||||
|
TextWrapping="NoWrap">
|
||||||
|
<ToolTipService.ToolTip>
|
||||||
|
<ToolTip Content="{x:Bind Title}" Visibility="{Binding IsTextTrimmed, ElementName=TitleTextBlock, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||||
|
</ToolTipService.ToolTip>
|
||||||
|
</TextBlock>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="16,0,0,0"
|
Margin="16,0,0,0"
|
||||||
@@ -74,10 +84,19 @@
|
|||||||
SourceKey="{x:Bind Icon}"
|
SourceKey="{x:Bind Icon}"
|
||||||
SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}" />
|
SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
|
x:Name="TitleTextBlock"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
|
MaxWidth="200"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Style="{StaticResource ContextItemTitleTextBlockCriticalStyle}"
|
Style="{StaticResource ContextItemTitleTextBlockCriticalStyle}"
|
||||||
Text="{x:Bind Title}" />
|
Text="{x:Bind Title}"
|
||||||
|
TextTrimming="WordEllipsis"
|
||||||
|
TextWrapping="NoWrap">
|
||||||
|
<ToolTipService.ToolTip>
|
||||||
|
<ToolTip Content="{x:Bind Title}" Visibility="{Binding IsTextTrimmed, ElementName=TitleTextBlock, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||||
|
</ToolTipService.ToolTip>
|
||||||
|
</TextBlock>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="16,0,0,0"
|
Margin="16,0,0,0"
|
||||||
@@ -129,6 +148,7 @@
|
|||||||
x:Name="ContextFilterBox"
|
x:Name="ContextFilterBox"
|
||||||
x:Uid="ContextFilterBox"
|
x:Uid="ContextFilterBox"
|
||||||
Margin="4"
|
Margin="4"
|
||||||
|
IsTextScaleFactorEnabled="True"
|
||||||
KeyDown="ContextFilterBox_KeyDown"
|
KeyDown="ContextFilterBox_KeyDown"
|
||||||
PreviewKeyDown="ContextFilterBox_PreviewKeyDown"
|
PreviewKeyDown="ContextFilterBox_PreviewKeyDown"
|
||||||
TextChanged="ContextFilterBox_TextChanged" />
|
TextChanged="ContextFilterBox_TextChanged" />
|
||||||
|
@@ -178,34 +178,97 @@ public sealed partial class ContextMenu : UserControl,
|
|||||||
{
|
{
|
||||||
if (e.Key == VirtualKey.Up)
|
if (e.Key == VirtualKey.Up)
|
||||||
{
|
{
|
||||||
// navigate previous
|
NavigateUp();
|
||||||
if (CommandsDropdown.SelectedIndex > 0)
|
|
||||||
{
|
|
||||||
CommandsDropdown.SelectedIndex--;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CommandsDropdown.SelectedIndex = CommandsDropdown.Items.Count - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
else if (e.Key == VirtualKey.Down)
|
else if (e.Key == VirtualKey.Down)
|
||||||
{
|
{
|
||||||
// navigate next
|
NavigateDown();
|
||||||
if (CommandsDropdown.SelectedIndex < CommandsDropdown.Items.Count - 1)
|
|
||||||
{
|
|
||||||
CommandsDropdown.SelectedIndex++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CommandsDropdown.SelectedIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
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()
|
private void UpdateUiForStackChange()
|
||||||
{
|
{
|
||||||
ContextFilterBox.Text = string.Empty;
|
ContextFilterBox.Text = string.Empty;
|
||||||
|
@@ -99,7 +99,7 @@ internal sealed partial class SampleListPage : ListPage
|
|||||||
new CommandContextItem(
|
new CommandContextItem(
|
||||||
new ToastCommand("Nested B invoked") { Name = "Do it", Icon = new IconInfo("B") })
|
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),
|
RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, vkey: VirtualKey.B),
|
||||||
MoreCommands = [
|
MoreCommands = [
|
||||||
new CommandContextItem(
|
new CommandContextItem(
|
||||||
|
Reference in New Issue
Block a user