CmdPal: Fix SUI crash ; Allow extensions to be disabled (#38040)

Both `TopLevelCommandItemWrapper` and `TopLevelViewModel` were really the same thing. The latter was from an earlier prototype, and the former is a more correct, safer abstraction. We really should have only ever used the former, but alas, we only used it for the SUI, and it piggy-backed off the latter, and that meant the latter's bugs became the former's.


tldr: I made the icon access safe in the SUI. 

And while I was doing this, because we now have a cleaner VM abstraction here in the host, we can actually cleanly disable extensions, because the `CommandProviderWrapper` knows which `ViewModel`s it made. 

Closes https://github.com/zadjii-msft/PowerToys/issues/426
Closes https://github.com/zadjii-msft/PowerToys/issues/478
Closes https://github.com/zadjii-msft/PowerToys/issues/577
This commit is contained in:
Mike Griese
2025-03-20 15:36:10 -05:00
committed by GitHub
parent 57cbcc2c3e
commit 14919dff10
34 changed files with 590 additions and 528 deletions

View File

@@ -123,18 +123,25 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
// Or the command may be a stub. Future us problem.
try
{
var host = ViewModel.CurrentPage?.ExtensionHost ?? CommandPaletteHost.Instance;
var pageHost = ViewModel.CurrentPage?.ExtensionHost;
var messageHost = message.ExtensionHost;
if (command is TopLevelCommandWrapper wrapper)
// Use the host from the current page if it has one, else use the
// one specified in the PerformMessage for a top-level command,
// else just use the global one.
var host = pageHost ?? messageHost ?? CommandPaletteHost.Instance;
extension = pageHost?.Extension ?? messageHost?.Extension ?? null;
if (extension != null)
{
var tlc = wrapper;
command = wrapper.Command;
host = tlc.ExtensionHost != null ? tlc.ExtensionHost! : host;
extension = tlc.ExtensionHost?.Extension;
if (extension != null)
if (messageHost != null)
{
Logger.LogDebug($"Activated top-level command from {extension.ExtensionDisplayName}");
}
else
{
Logger.LogDebug($"Activated command from {extension.ExtensionDisplayName}");
}
}
ViewModel.SetActiveExtension(extension);
@@ -476,8 +483,8 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
var topLevelCommand = tlcManager.LookupCommand(commandId);
if (topLevelCommand != null)
{
var command = topLevelCommand.Command;
var isPage = command is TopLevelCommandWrapper wrapper && wrapper.Command is not IInvokableCommand;
var command = topLevelCommand.CommandViewModel.Model.Unsafe;
var isPage = command is not IInvokableCommand;
// If the bound command is an invokable command, then
// we don't want to open the window at all - we want to