Files
PowerToys/src/modules/cmdpal/Microsoft.CmdPal.Core.ViewModels/CommandBarViewModel.cs
Michael Jolley 6642c805b7
Some checks failed
Spell checking / Check Spelling (push) Has been cancelled
Spell checking / Update PR (push) Has been cancelled
Spell checking / Report (Push) (push) Has been cancelled
Spell checking / Report (PR) (push) Has been cancelled
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
2025-07-16 06:25:24 -05:00

169 lines
5.0 KiB
C#

// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.System;
namespace Microsoft.CmdPal.Core.ViewModels;
public partial class CommandBarViewModel : ObservableObject,
IRecipient<UpdateCommandBarMessage>
{
public ICommandBarContext? SelectedItem
{
get => field;
set
{
if (field != null)
{
field.PropertyChanged -= SelectedItemPropertyChanged;
}
field = value;
SetSelectedItem(value);
OnPropertyChanged(nameof(SelectedItem));
}
}
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(HasPrimaryCommand))]
public partial CommandItemViewModel? PrimaryCommand { get; set; }
public bool HasPrimaryCommand => PrimaryCommand != null && PrimaryCommand.ShouldBeVisible;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(HasSecondaryCommand))]
public partial CommandItemViewModel? SecondaryCommand { get; set; }
public bool HasSecondaryCommand => SecondaryCommand != null;
[ObservableProperty]
public partial bool ShouldShowContextMenu { get; set; } = false;
[ObservableProperty]
public partial PageViewModel? CurrentPage { get; set; }
public CommandBarViewModel()
{
WeakReferenceMessenger.Default.Register<UpdateCommandBarMessage>(this);
}
public void Receive(UpdateCommandBarMessage message) => SelectedItem = message.ViewModel;
private void SetSelectedItem(ICommandBarContext? value)
{
if (value != null)
{
PrimaryCommand = value.PrimaryCommand;
value.PropertyChanged += SelectedItemPropertyChanged;
}
else
{
if (SelectedItem != null)
{
SelectedItem.PropertyChanged -= SelectedItemPropertyChanged;
}
PrimaryCommand = null;
}
UpdateContextItems();
}
private void SelectedItemPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(SelectedItem.HasMoreCommands):
UpdateContextItems();
break;
}
}
private void UpdateContextItems()
{
if (SelectedItem == null)
{
SecondaryCommand = null;
ShouldShowContextMenu = false;
return;
}
SecondaryCommand = SelectedItem.SecondaryCommand;
ShouldShowContextMenu = SelectedItem.MoreCommands
.OfType<CommandContextItemViewModel>()
.Count() > 1;
OnPropertyChanged(nameof(HasSecondaryCommand));
OnPropertyChanged(nameof(SecondaryCommand));
OnPropertyChanged(nameof(ShouldShowContextMenu));
}
// InvokeItemCommand is what this will be in Xaml due to source generator
// this comes in when an item in the list is tapped
// [RelayCommand]
public ContextKeybindingResult InvokeItem(CommandContextItemViewModel item) =>
PerformCommand(item);
// this comes in when the primary button is tapped
public void InvokePrimaryCommand()
{
PerformCommand(PrimaryCommand);
}
// this comes in when the secondary button is tapped
public void InvokeSecondaryCommand()
{
PerformCommand(SecondaryCommand);
}
public ContextKeybindingResult CheckKeybinding(bool ctrl, bool alt, bool shift, bool win, VirtualKey key)
{
var keybindings = SelectedItem?.Keybindings();
if (keybindings != null)
{
// Does the pressed key match any of the keybindings?
var pressedKeyChord = KeyChordHelpers.FromModifiers(ctrl, alt, shift, win, key, 0);
if (keybindings.TryGetValue(pressedKeyChord, out var matchedItem))
{
return matchedItem != null ? PerformCommand(matchedItem) : ContextKeybindingResult.Unhandled;
}
}
return ContextKeybindingResult.Unhandled;
}
private ContextKeybindingResult PerformCommand(CommandItemViewModel? command)
{
if (command == null)
{
return ContextKeybindingResult.Unhandled;
}
if (command.HasMoreCommands)
{
WeakReferenceMessenger.Default.Send<PerformCommandMessage>(new(command.Command.Model, command.Model));
return ContextKeybindingResult.KeepOpen;
}
else
{
WeakReferenceMessenger.Default.Send<PerformCommandMessage>(new(command.Command.Model, command.Model));
return ContextKeybindingResult.Hide;
}
}
}
public enum ContextKeybindingResult
{
Unhandled,
Hide,
KeepOpen,
}