mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 02:36:19 +02:00
CmdPal: Add context commands for pinning nested commands (#45673)
_targets #45572_ This change allows our contact menu factory to actually create and add additional context menu commands for pinning commands to the top level. Now for any command provider built with the latest SDK that return subcommands with an ID, we will add additional context menu commands that allows you to pin that command to the top level. <img width="540" height="181" alt="image" src="https://github.com/user-attachments/assets/6c2cfe3c-4143-44d1-9308-bfc71db4c842" /> <img width="729" height="317" alt="image" src="https://github.com/user-attachments/assets/4ff75c9f-1f35-4c1e-a03e-6fab5cbab423" /> related to https://github.com/microsoft/PowerToys/issues/45191 related to https://github.com/microsoft/PowerToys/issues/45201 This PR notably does not remove pinning from the apps extension. I thought that made sense to do as a follow-up PR for the sake of reviewability. --- description from #45676 which was merged into this Removes the code that the apps provider was using to support pinning apps to the top level list of commands. Now the all apps provider just uses the global support for pinning commands to the top level. This does have the side effect of removing the separation of pinned apps from unpinned apps on the All Apps page. However, we all pretty much agree that wasn't a particularly widely used feature, and it's safe to remove. With this, we can finally call this issue done 🎉 closes https://github.com/microsoft/PowerToys/issues/45191
This commit is contained in:
@@ -2,8 +2,13 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.UI.ViewModels;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using RS_ = Microsoft.CmdPal.UI.Helpers.ResourceLoaderInstance;
|
||||
|
||||
namespace Microsoft.CmdPal.UI;
|
||||
|
||||
@@ -24,8 +29,164 @@ internal sealed partial class CommandPaletteContextMenuFactory : IContextMenuFac
|
||||
{
|
||||
var results = DefaultContextMenuFactory.Instance.UnsafeBuildAndInitMoreCommands(items, commandItem);
|
||||
|
||||
// TODO: #45201 Here, we'll want to add pin/unpin commands for pinning
|
||||
// items to the top-level or to the dock.
|
||||
List<IContextItem> moreCommands = [];
|
||||
var itemId = commandItem.Command.Id;
|
||||
|
||||
if (commandItem.PageContext.TryGetTarget(out var page) &&
|
||||
page.ProviderContext.SupportsPinning &&
|
||||
!string.IsNullOrEmpty(itemId))
|
||||
{
|
||||
// Add pin/unpin commands for pinning items to the top-level or to
|
||||
// the dock.
|
||||
var providerId = page.ProviderContext.ProviderId;
|
||||
if (_topLevelCommandManager.LookupProvider(providerId) is CommandProviderWrapper provider)
|
||||
{
|
||||
var providerSettings = _settingsModel.GetProviderSettings(provider);
|
||||
|
||||
var alreadyPinnedToTopLevel = providerSettings.PinnedCommandIds.Contains(itemId);
|
||||
|
||||
// Don't add pin/unpin commands for items displayed as
|
||||
// TopLevelViewModels that aren't already pinned.
|
||||
//
|
||||
// We can't look up if this command item is in the top level
|
||||
// items in the manager, because we are being called _before_ we
|
||||
// get added to the manager's list of commands.
|
||||
var isTopLevelItem = page is TopLevelItemPageContext;
|
||||
|
||||
if (!isTopLevelItem || alreadyPinnedToTopLevel)
|
||||
{
|
||||
var pinToTopLevelCommand = new PinToCommand(
|
||||
commandId: itemId,
|
||||
providerId: providerId,
|
||||
pin: !alreadyPinnedToTopLevel,
|
||||
PinLocation.TopLevel,
|
||||
_settingsModel,
|
||||
_topLevelCommandManager);
|
||||
|
||||
var contextItem = new PinToContextItem(pinToTopLevelCommand, commandItem);
|
||||
moreCommands.Add(contextItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (moreCommands.Count > 0)
|
||||
{
|
||||
moreCommands.Insert(0, new Separator());
|
||||
var moreResults = DefaultContextMenuFactory.Instance.UnsafeBuildAndInitMoreCommands(moreCommands.ToArray(), commandItem);
|
||||
results.AddRange(moreResults);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
internal enum PinLocation
|
||||
{
|
||||
TopLevel,
|
||||
Dock,
|
||||
}
|
||||
|
||||
private sealed partial class PinToContextItem : CommandContextItem
|
||||
{
|
||||
private readonly PinToCommand _command;
|
||||
private readonly CommandItemViewModel _commandItem;
|
||||
|
||||
public PinToContextItem(PinToCommand command, CommandItemViewModel commandItem)
|
||||
: base(command)
|
||||
{
|
||||
_command = command;
|
||||
_commandItem = commandItem;
|
||||
command.PinStateChanged += this.OnPinStateChanged;
|
||||
}
|
||||
|
||||
private void OnPinStateChanged(object? sender, EventArgs e)
|
||||
{
|
||||
// update our MoreCommands
|
||||
_commandItem.RefreshMoreCommands();
|
||||
}
|
||||
|
||||
~PinToContextItem()
|
||||
{
|
||||
_command.PinStateChanged -= this.OnPinStateChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed partial class PinToCommand : InvokableCommand
|
||||
{
|
||||
private readonly string _commandId;
|
||||
private readonly string _providerId;
|
||||
private readonly SettingsModel _settings;
|
||||
private readonly TopLevelCommandManager _topLevelCommandManager;
|
||||
private readonly bool _pin;
|
||||
private readonly PinLocation _pinLocation;
|
||||
|
||||
public override IconInfo Icon => _pin ? Icons.PinIcon : Icons.UnpinIcon;
|
||||
|
||||
public override string Name => _pin ? RS_.GetString("top_level_pin_command_name") : RS_.GetString("top_level_unpin_command_name");
|
||||
|
||||
internal event EventHandler? PinStateChanged;
|
||||
|
||||
public PinToCommand(
|
||||
string commandId,
|
||||
string providerId,
|
||||
bool pin,
|
||||
PinLocation pinLocation,
|
||||
SettingsModel settings,
|
||||
TopLevelCommandManager topLevelCommandManager)
|
||||
{
|
||||
_commandId = commandId;
|
||||
_providerId = providerId;
|
||||
_pinLocation = pinLocation;
|
||||
_settings = settings;
|
||||
_topLevelCommandManager = topLevelCommandManager;
|
||||
_pin = pin;
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
Logger.LogDebug($"PinTo{_pinLocation}Command.Invoke({_pin}): {_providerId}/{_commandId}");
|
||||
if (_pin)
|
||||
{
|
||||
switch (_pinLocation)
|
||||
{
|
||||
case PinLocation.TopLevel:
|
||||
PinToTopLevel();
|
||||
break;
|
||||
|
||||
// TODO: After dock is added:
|
||||
// case PinLocation.Dock:
|
||||
// PinToDock();
|
||||
// break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (_pinLocation)
|
||||
{
|
||||
case PinLocation.TopLevel:
|
||||
UnpinFromTopLevel();
|
||||
break;
|
||||
|
||||
// case PinLocation.Dock:
|
||||
// UnpinFromDock();
|
||||
// break;
|
||||
}
|
||||
}
|
||||
|
||||
PinStateChanged?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
return CommandResult.KeepOpen();
|
||||
}
|
||||
|
||||
private void PinToTopLevel()
|
||||
{
|
||||
PinCommandItemMessage message = new(_providerId, _commandId);
|
||||
WeakReferenceMessenger.Default.Send(message);
|
||||
}
|
||||
|
||||
private void UnpinFromTopLevel()
|
||||
{
|
||||
UnpinCommandItemMessage message = new(_providerId, _commandId);
|
||||
WeakReferenceMessenger.Default.Send(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,9 +26,9 @@ internal sealed class PowerToysAppHostService : IAppHostService
|
||||
return topLevelHost ?? currentHost ?? CommandPaletteHost.Instance;
|
||||
}
|
||||
|
||||
public CommandProviderContext GetProviderContextForCommand(object? command, CommandProviderContext? currentContext)
|
||||
public ICommandProviderContext GetProviderContextForCommand(object? command, ICommandProviderContext? currentContext)
|
||||
{
|
||||
CommandProviderContext? topLevelId = null;
|
||||
ICommandProviderContext? topLevelId = null;
|
||||
if (command is TopLevelViewModel topLevelViewModel)
|
||||
{
|
||||
topLevelId = topLevelViewModel.ProviderContext;
|
||||
|
||||
@@ -801,10 +801,26 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
|
||||
<value>K</value>
|
||||
<comment>Keyboard key</comment>
|
||||
</data>
|
||||
<data name="ConfigureShortcut" xml:space="preserve">
|
||||
<data name="ConfigureShortcut" xml:space="preserve">
|
||||
<value>Configure shortcut</value>
|
||||
</data>
|
||||
<data name="ConfigureShortcutText.Text" xml:space="preserve">
|
||||
<value>Assign shortcut</value>
|
||||
</data>
|
||||
<data name="top_level_pin_command_name" xml:space="preserve">
|
||||
<value>Pin to home</value>
|
||||
<comment>Command name for pinning an item to the top level list of commands</comment>
|
||||
</data>
|
||||
<data name="top_level_unpin_command_name" xml:space="preserve">
|
||||
<value>Unpin from home</value>
|
||||
<comment>Command name for unpinning an item from the top level list of commands</comment>
|
||||
</data>
|
||||
<data name="dock_pin_command_name" xml:space="preserve">
|
||||
<value>Pin to dock</value>
|
||||
<comment>Command name for pinning an item to the dock</comment>
|
||||
</data>
|
||||
<data name="dock_unpin_command_name" xml:space="preserve">
|
||||
<value>Unpin from dock</value>
|
||||
<comment>Command name for unpinning an item from the dock</comment>
|
||||
</data>
|
||||
</root>
|
||||
Reference in New Issue
Block a user