From b0e7473760d89da6a6da378470c99559499b9745 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 23 Apr 2025 06:45:34 -0500 Subject: [PATCH] CmdPal: Tidy up some winget experiences (#38174) I'm filing this so that I don't lose it on this machine I use less often. We can probably hold it out of 0.90 Fixes: * If a package is installed, we always display the version as "Unknown" * also deals with a case where getting the package metadata could fail, and we'd hide the list item. That's only possible in the "installed, no updates available" case * Allow package updates, add an icon for updates * moves off the preview winget API onto a higher stable version --- Directory.Packages.props | 2 +- NOTICE.md | 2 +- .../Pages/InstallPackageCommand.cs | 52 +++++++++++++++---- .../Pages/InstallPackageListItem.cs | 25 +++++++-- .../Properties/Resources.Designer.cs | 9 ++++ .../Properties/Resources.resx | 4 ++ 6 files changed, 76 insertions(+), 18 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 4393655dab..0d8879746d 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -45,7 +45,7 @@ - + diff --git a/NOTICE.md b/NOTICE.md index 92bdf5fe39..edf8e75c51 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1473,7 +1473,7 @@ SOFTWARE. - Microsoft.Windows.CsWinRT 2.2.0 - Microsoft.Windows.SDK.BuildTools 10.0.22621.2428 - Microsoft.WindowsAppSDK 1.6.250205002 -- Microsoft.WindowsPackageManager.ComInterop 1.10.120-preview +- Microsoft.WindowsPackageManager.ComInterop 1.10.340 - Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9 - Microsoft.Xaml.Behaviors.Wpf 1.1.39 - ModernWpfUI 0.9.4 diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageCommand.cs index 1aa22bfa26..cf85359bdf 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageCommand.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageCommand.cs @@ -22,10 +22,12 @@ public partial class InstallPackageCommand : InvokableCommand private IAsyncOperationWithProgress? _unInstallAction; private Task? _installTask; - public bool IsInstalled { get; private set; } + public PackageInstallCommandState InstallCommandState { get; private set; } public static IconInfo CompletedIcon { get; } = new("\uE930"); // Completed + public static IconInfo UpdateIcon { get; } = new("\uE74A"); // Up + public static IconInfo DownloadIcon { get; } = new("\uE896"); // Download public static IconInfo DeleteIcon { get; } = new("\uE74D"); // Delete @@ -44,23 +46,41 @@ public partial class InstallPackageCommand : InvokableCommand internal bool SkipDependencies { get; set; } - public InstallPackageCommand(CatalogPackage package, bool isInstalled) + public InstallPackageCommand(CatalogPackage package, PackageInstallCommandState isInstalled) { _package = package; - IsInstalled = isInstalled; + InstallCommandState = isInstalled; UpdateAppearance(); } internal void FakeChangeStatus() { - IsInstalled = !IsInstalled; + InstallCommandState = InstallCommandState switch + { + PackageInstallCommandState.Install => PackageInstallCommandState.Uninstall, + PackageInstallCommandState.Update => PackageInstallCommandState.Uninstall, + PackageInstallCommandState.Uninstall => PackageInstallCommandState.Install, + _ => throw new NotImplementedException(), + }; UpdateAppearance(); } private void UpdateAppearance() { - Icon = IsInstalled ? CompletedIcon : DownloadIcon; - Name = IsInstalled ? Properties.Resources.winget_uninstall_name : Properties.Resources.winget_install_name; + Icon = InstallCommandState switch + { + PackageInstallCommandState.Install => DownloadIcon, + PackageInstallCommandState.Update => UpdateIcon, + PackageInstallCommandState.Uninstall => CompletedIcon, + _ => throw new NotImplementedException(), + }; + Name = InstallCommandState switch + { + PackageInstallCommandState.Install => Properties.Resources.winget_install_name, + PackageInstallCommandState.Update => Properties.Resources.winget_update_name, + PackageInstallCommandState.Uninstall => Properties.Resources.winget_uninstall_name, + _ => throw new NotImplementedException(), + }; } public override ICommandResult Invoke() @@ -72,7 +92,7 @@ public partial class InstallPackageCommand : InvokableCommand return CommandResult.KeepOpen(); } - if (IsInstalled) + if (InstallCommandState == PackageInstallCommandState.Uninstall) { // Uninstall _installBanner.State = MessageState.Info; @@ -88,7 +108,8 @@ public partial class InstallPackageCommand : InvokableCommand _installTask = Task.Run(() => TryDoInstallOperation(_unInstallAction)); } - else + else if (InstallCommandState is PackageInstallCommandState.Install or + PackageInstallCommandState.Update) { // Install _installBanner.State = MessageState.Info; @@ -117,7 +138,8 @@ public partial class InstallPackageCommand : InvokableCommand try { await action.AsTask(); - _installBanner.Message = IsInstalled ? + + _installBanner.Message = InstallCommandState == PackageInstallCommandState.Uninstall ? string.Format(CultureInfo.CurrentCulture, UninstallPackageFinished, _package.Name) : string.Format(CultureInfo.CurrentCulture, InstallPackageFinished, _package.Name); @@ -125,9 +147,10 @@ public partial class InstallPackageCommand : InvokableCommand _installBanner.State = MessageState.Success; _installTask = null; - _ = Task.Run(() => + _ = Task.Run(async () => { - Thread.Sleep(2500); + await Task.Delay(2500).ConfigureAwait(false); + if (_installTask == null) { WinGetExtensionHost.Instance.HideStatus(_installBanner); @@ -228,3 +251,10 @@ public partial class InstallPackageCommand : InvokableCommand } } } + +public enum PackageInstallCommandState +{ + Uninstall = 0, + Update = 1, + Install = 2, +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageListItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageListItem.cs index 2ce192f262..5c556ec3cb 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageListItem.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageListItem.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Runtime.InteropServices; using System.Threading.Tasks; using ManagedCommon; using Microsoft.CommandPalette.Extensions; @@ -31,7 +32,7 @@ public partial class InstallPackageListItem : ListItem { _package = package; - var version = _package.DefaultInstallVersion; + var version = _package.DefaultInstallVersion ?? _package.InstalledVersion; var versionTagText = "Unknown"; if (version != null) { @@ -49,7 +50,16 @@ public partial class InstallPackageListItem : ListItem private Details? BuildDetails(PackageVersionInfo? version) { - var metadata = version?.GetCatalogPackageMetadata(); + CatalogPackageMetadata? metadata = null; + try + { + metadata = version?.GetCatalogPackageMetadata(); + } + catch (COMException ex) + { + Logger.LogWarning($"{ex.ErrorCode}"); + } + if (metadata != null) { if (metadata.Tags.Where(t => t.Equals(WinGetExtensionPage.ExtensionsTag, StringComparison.OrdinalIgnoreCase)).Any()) @@ -149,12 +159,17 @@ public partial class InstallPackageListItem : ListItem var status = await _package.CheckInstalledStatusAsync(); var isInstalled = _package.InstalledVersion != null; + var installedState = isInstalled ? + (_package.IsUpdateAvailable ? + PackageInstallCommandState.Update : PackageInstallCommandState.Uninstall) : + PackageInstallCommandState.Install; + // might be an uninstall command - InstallPackageCommand installCommand = new(_package, isInstalled); + InstallPackageCommand installCommand = new(_package, installedState); if (isInstalled) { - this.Icon = InstallPackageCommand.CompletedIcon; + this.Icon = installCommand.Icon; this.Command = new NoOpCommand(); List contextMenu = []; CommandContextItem uninstallContextItem = new(installCommand) @@ -180,7 +195,7 @@ public partial class InstallPackageListItem : ListItem } // didn't find the app - _installCommand = new InstallPackageCommand(_package, isInstalled); + _installCommand = new InstallPackageCommand(_package, installedState); this.Command = _installCommand; Icon = _installCommand.Icon; diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Properties/Resources.Designer.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Properties/Resources.Designer.cs index f62a291d36..418cccf58c 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Properties/Resources.Designer.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Properties/Resources.Designer.cs @@ -330,6 +330,15 @@ namespace Microsoft.CmdPal.Ext.WinGet.Properties { } } + /// + /// Looks up a localized string similar to Update. + /// + public static string winget_update_name { + get { + return ResourceManager.GetString("winget_update_name", resourceCulture); + } + } + /// /// Looks up a localized string similar to View online. /// diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Properties/Resources.resx b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Properties/Resources.resx index 2be07cec68..ac2128430d 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Properties/Resources.resx +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Properties/Resources.resx @@ -154,6 +154,10 @@ Install + + Update + + Uninstalling {0}... {0} will be replaced by the name of an app package