Add a new icon for run (#574)

Couple other hotfixes:

* We broke FastUpToDate in #559. `PreserveNewest` fixes this
* Winget would fetch ALL the details before displaying the list. Big yikes. Lazy load that!
* We needed to better handle the case where we fetched items in response to a `ItemsChanged`, Loading is set to false, && there's an EmptyContent - we shouldn't flash the empty state before displaying the results
* Details had itsy-bitsy text (regressed in #482)
This commit is contained in:
Mike Griese
2025-03-18 17:00:50 -05:00
committed by GitHub
parent df65e2a568
commit edad8bf784
21 changed files with 185 additions and 123 deletions

View File

@@ -3,11 +3,11 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Microsoft.CommandPalette.Extensions" Version="0.0.9" />
<PackageVersion Include="Microsoft.CommandPalette.Extensions" Version="0.1.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0-preview.24508.2" />
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.2903.40" />
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.2.46-beta" />
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.1.5" />
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.6.250205002" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />

View File

@@ -7,10 +7,6 @@
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>
<ItemGroup>
<None Remove="Assets\AllApps.png" />
<None Remove="Assets\AllApps.svg" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.IO.Abstractions" />
@@ -34,12 +30,16 @@
</Compile>
</ItemGroup>
<ItemGroup>
<None Remove="Assets\AllApps.png" />
<None Remove="Assets\AllApps.svg" />
</ItemGroup>
<ItemGroup>
<Content Update="Assets\AllApps.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="Assets\AllApps.svg">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

View File

@@ -8,9 +8,6 @@
<!-- MRT from windows app sdk will search for a pri file with the same name of the module before defaulting to resources.pri -->
<ProjectPriFileName>Microsoft.CmdPal.Ext.Calc.pri</ProjectPriFileName>
</PropertyGroup>
<ItemGroup>
<None Remove="Assets\Calculator.svg" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
</ItemGroup>
@@ -23,12 +20,15 @@
</Compile>
</ItemGroup>
<ItemGroup>
<None Remove="Assets\Calculator.svg" />
</ItemGroup>
<ItemGroup>
<Content Update="Assets\Calculator.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="Assets\Calculator.svg">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>

View File

@@ -8,9 +8,6 @@
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>
<ItemGroup>
<None Remove="Assets\FileExplorer.svg" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWin32">
@@ -30,12 +27,15 @@
</Compile>
</ItemGroup>
<ItemGroup>
<None Remove="Assets\FileExplorer.svg" />
</ItemGroup>
<ItemGroup>
<Content Update="Assets\FileExplorer.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="Assets\FileExplorer.svg">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

View File

@@ -8,9 +8,6 @@
<!-- MRT from windows app sdk will search for a pri file with the same name of the module before defaulting to resources.pri -->
<ProjectPriFileName>Microsoft.CmdPal.Ext.Registry.pri</ProjectPriFileName>
</PropertyGroup>
<ItemGroup>
<None Remove="Assets\Registry.svg" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.ServiceProcess.ServiceController" />
</ItemGroup>
@@ -24,12 +21,16 @@
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Remove="Assets\Registry.svg" />
</ItemGroup>
<ItemGroup>
<Content Update="Assets\Registry.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="Assets\Registry.svg">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

View File

@@ -23,4 +23,11 @@
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Content Update="Assets\Run@2x.svg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -20,14 +20,14 @@ public partial class ShellCommandsProvider : CommandProvider
{
Id = "Run";
DisplayName = Resources.cmd_plugin_name;
Icon = new IconInfo("\uE756");
Icon = Icons.RunV2;
Settings = _settingsManager.Settings;
_fallbackItem = new FallbackExecuteItem(_settingsManager);
_shellPageItem = new CommandItem(new ShellListPage(_settingsManager))
{
Icon = new IconInfo("\uE756"),
Icon = Icons.RunV2,
Title = Resources.shell_command_name,
Subtitle = Resources.cmd_plugin_description,
MoreCommands = [

View File

@@ -8,9 +8,6 @@
<!-- MRT from windows app sdk will search for a pri file with the same name of the module before defaulting to resources.pri -->
<ProjectPriFileName>Microsoft.CmdPal.Ext.TimeDate.pri</ProjectPriFileName>
</PropertyGroup>
<ItemGroup>
<None Include="Assets\TimeDate.png" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
@@ -32,12 +29,15 @@
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="Assets\TimeDate.png" />
</ItemGroup>
<ItemGroup>
<Content Update="Assets\TimeDate.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="Assets\TimeDate.svg">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -7,17 +7,7 @@
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Images\**" />
<Content Remove="Images\**" />
<EmbeddedResource Remove="Images\**" />
<None Remove="Images\**" />
<Page Remove="Images\**" />
<PRIResource Remove="Images\**" />
</ItemGroup>
<ItemGroup>
<None Remove="Assets\WebSearch.svg" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
</ItemGroup>
@@ -38,11 +28,18 @@
</Compile>
</ItemGroup>
<ItemGroup>
<None Remove="Assets\WebSearch.svg" />
</ItemGroup>
<ItemGroup>
<Content Update="Assets\WebSearch.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="Assets\WebSearch.svg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
@@ -50,8 +47,5 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Content Update="Assets\WebSearch.svg">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -30,10 +30,10 @@
</ItemGroup>
<ItemGroup>
<Content Update="Assets\WindowsSettings.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="Assets\WindowsSettings.svg">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>

View File

@@ -9,12 +9,6 @@
<!-- MRT from windows app sdk will search for a pri file with the same name of the module before defaulting to resources.pri -->
<ProjectPriFileName>Microsoft.CmdPal.Ext.WindowsTerminal.pri</ProjectPriFileName>
</PropertyGroup>
<ItemGroup>
<Content Remove="Assets\WindowsTerminal.dark.png" />
<Content Remove="Assets\WindowsTerminal.dark.png" />
<Content Remove="Assets\WindowsTerminal.light.png" />
<Content Remove="Assets\WindowsTerminal.light.png" />
</ItemGroup>
<ItemGroup>
<None Remove="Assets\WindowsTerminal.svg" />
</ItemGroup>
@@ -28,14 +22,22 @@
<AutoGen>True</AutoGen>
</Compile>
</ItemGroup>
<ItemGroup>
<Content Remove="Assets\WindowsTerminal.dark.png" />
<Content Remove="Assets\WindowsTerminal.dark.png" />
<Content Remove="Assets\WindowsTerminal.light.png" />
<Content Remove="Assets\WindowsTerminal.light.png" />
</ItemGroup>
<ItemGroup>
<Content Update="Assets\WindowsTerminal.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="Assets\WindowsTerminal.svg">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<LastGenOutput>Resources.Designer.cs</LastGenOutput>

View File

@@ -28,13 +28,13 @@ internal sealed partial class CreatedExtensionForm : NewExtensionFormBase
public override ICommandResult SubmitForm(string inputs, string data)
{
var dataInput = JsonNode.Parse(data)?.AsObject();
JsonObject? dataInput = JsonNode.Parse(data)?.AsObject();
if (dataInput == null)
{
return CommandResult.KeepOpen();
}
var verb = dataInput["x"]?.AsValue()?.ToString() ?? string.Empty;
string verb = dataInput["x"]?.AsValue()?.ToString() ?? string.Empty;
return verb switch
{
"sln" => OpenSolution(),
@@ -47,17 +47,17 @@ internal sealed partial class CreatedExtensionForm : NewExtensionFormBase
private ICommandResult OpenSolution()
{
string[] parts = [_path, _name, $"{_name}.sln"];
var pathToSolution = Path.Combine(parts);
string pathToSolution = Path.Combine(parts);
ShellHelpers.OpenInShell(pathToSolution);
return CommandResult.GoHome();
return CommandResult.Hide();
}
private ICommandResult OpenDirectory()
{
string[] parts = [_path, _name];
var pathToDir = Path.Combine(parts);
string pathToDir = Path.Combine(parts);
ShellHelpers.OpenInShell(pathToDir);
return CommandResult.GoHome();
return CommandResult.Hide();
}
private ICommandResult CreateNew()

View File

@@ -32,12 +32,14 @@ public partial class ListViewModel : PageViewModel, IDisposable
private readonly Lock _listLock = new();
private bool _isLoading;
private bool _isFetching;
public event TypedEventHandler<ListViewModel, object>? ItemsUpdated;
public bool ShowEmptyContent =>
IsInitialized &&
FilteredItems.Count == 0 &&
(!_isFetching) &&
IsLoading == false;
// Remember - "observable" properties from the model (via PropChanged)
@@ -122,9 +124,11 @@ public partial class ListViewModel : PageViewModel, IDisposable
{
// TEMPORARY: just plop all the items into a single group
// see 9806fe5d8 for the last commit that had this with sections
_isFetching = true;
try
{
var newItems = _model.Unsafe!.GetItems();
IListItem[] newItems = _model.Unsafe!.GetItems();
// Collect all the items into new viewmodels
Collection<ListItemViewModel> newViewModels = [];
@@ -132,7 +136,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
// TODO we can probably further optimize this by also keeping a
// HashSet of every ExtensionObject we currently have, and only
// building new viewmodels for the ones we haven't already built.
foreach (var item in newItems)
foreach (IListItem? item in newItems)
{
ListItemViewModel viewModel = new(item, new(this));
@@ -143,8 +147,8 @@ public partial class ListViewModel : PageViewModel, IDisposable
}
}
var firstTwenty = newViewModels.Take(20);
foreach (var item in firstTwenty)
IEnumerable<ListItemViewModel> firstTwenty = newViewModels.Take(20);
foreach (ListItemViewModel? item in firstTwenty)
{
item?.SafeInitializeProperties();
}
@@ -172,6 +176,10 @@ public partial class ListViewModel : PageViewModel, IDisposable
ShowException(ex, _model?.Unsafe?.Name);
throw;
}
finally
{
_isFetching = false;
}
_cancellationTokenSource = new CancellationTokenSource();
@@ -225,7 +233,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
iterable = Items.ToArray();
}
foreach (var item in iterable)
foreach (ListItemViewModel item in iterable)
{
ct.ThrowIfCancellationRequested();
@@ -258,8 +266,8 @@ public partial class ListViewModel : PageViewModel, IDisposable
return 1;
}
var nameMatch = StringMatcher.FuzzySearch(query, listItem.Title);
var descriptionMatch = StringMatcher.FuzzySearch(query, listItem.Subtitle);
MatchResult nameMatch = StringMatcher.FuzzySearch(query, listItem.Title);
MatchResult descriptionMatch = StringMatcher.FuzzySearch(query, listItem.Subtitle);
return new[] { nameMatch.Score, (descriptionMatch.Score - 4) / 2, 0 }.Max();
}
@@ -272,7 +280,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
// Similarly stolen from ListHelpers.FilterList
public static IEnumerable<ListItemViewModel> FilterList(IEnumerable<ListItemViewModel> items, string query)
{
var scores = items
IOrderedEnumerable<ScoredListItemViewModel> scores = items
.Where(i => !i.IsInErrorState)
.Select(li => new ScoredListItemViewModel() { ViewModel = li, Score = ScoreListItem(query, li) })
.Where(score => score.Score > 0)
@@ -351,7 +359,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
{
base.InitializeProperties();
var model = _model.Unsafe;
IListPage? model = _model.Unsafe;
if (model == null)
{
return; // throw?
@@ -377,7 +385,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
public void LoadMoreIfNeeded()
{
var model = this._model.Unsafe;
IListPage? model = this._model.Unsafe;
if (model == null)
{
return;
@@ -404,7 +412,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
{
base.FetchProperty(propertyName);
var model = this._model.Unsafe;
IListPage? model = this._model.Unsafe;
if (model == null)
{
return; // throw?
@@ -467,13 +475,13 @@ public partial class ListViewModel : PageViewModel, IDisposable
lock (_listLock)
{
foreach (var item in Items)
foreach (ListItemViewModel item in Items)
{
item.SafeCleanup();
}
Items.Clear();
foreach (var item in FilteredItems)
foreach (ListItemViewModel item in FilteredItems)
{
item.SafeCleanup();
}
@@ -481,7 +489,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
FilteredItems.Clear();
}
var model = _model.Unsafe;
IListPage? model = _model.Unsafe;
if (model != null)
{
model.ItemsChanged -= Model_ItemsChanged;

View File

@@ -10,6 +10,12 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary>
<cmdpalUi:PlaceholderTextConverter x:Key="PlaceholderTextConverter" />
</ResourceDictionary>
</UserControl.Resources>
<!-- Search box -->
<TextBox
x:Name="FilterBox"
@@ -17,7 +23,7 @@
VerticalAlignment="Stretch"
VerticalContentAlignment="Stretch"
KeyDown="FilterBox_KeyDown"
PlaceholderText="{x:Bind CurrentPageViewModel.PlaceholderText, Mode=OneWay}"
PlaceholderText="{x:Bind CurrentPageViewModel.PlaceholderText, Converter={StaticResource PlaceholderTextConverter}, Mode=OneWay}"
PreviewKeyDown="FilterBox_PreviewKeyDown"
PreviewKeyUp="FilterBox_PreviewKeyUp"
Style="{StaticResource SearchTextBoxStyle}"

View File

@@ -335,7 +335,6 @@
Grid.Row="2"
Margin="0,12,0,24"
Background="Transparent"
FontSize="10"
Header3FontSize="12"
Header3FontWeight="Normal"
Header3Foreground="{ThemeResource TextFillColorSecondaryBrush}"

View File

@@ -0,0 +1,19 @@
<svg width="260" height="260" viewBox="0 0 260 260" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.7189 166.405L34.8123 80.9387C37.8287 65.8564 51.0714 55 66.4524 55H210.641C231.003 55 246.274 73.6283 242.281 93.5947L225.188 179.061C222.171 194.144 208.929 205 193.548 205H49.359C28.9972 205 13.7257 186.372 17.7189 166.405Z" fill="url(#paint0_linear_15_159)" stroke="url(#paint1_linear_15_159)" stroke-width="32.2667"/>
<path d="M66.3623 129.973L70.5337 110.506C73.7217 95.6287 86.8692 85 102.084 85H162.086C182.628 85 197.94 103.941 193.636 124.027L189.465 143.494C186.277 158.371 173.129 169 157.914 169H97.9127C77.3703 169 62.0581 150.059 66.3623 129.973Z" fill="url(#paint2_linear_15_159)"/>
<path d="M85.1317 114.211L85.7539 110.478C87.0504 102.699 93.7811 96.9971 101.668 96.9971H164.956C174.925 96.9971 182.509 105.949 180.87 115.783L180.248 119.516C178.951 127.295 172.22 132.997 164.334 132.997H101.045C91.0761 132.997 83.4927 124.045 85.1317 114.211Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear_15_159" x1="148" y1="61" x2="130" y2="205" gradientUnits="userSpaceOnUse">
<stop stop-color="#CDEDFC"/>
<stop offset="1" stop-color="#BFE3F9"/>
</linearGradient>
<linearGradient id="paint1_linear_15_159" x1="46" y1="49" x2="268" y2="253" gradientUnits="userSpaceOnUse">
<stop stop-color="#3BCAF4"/>
<stop offset="1" stop-color="#006AAA"/>
</linearGradient>
<linearGradient id="paint2_linear_15_159" x1="93.9993" y1="73" x2="216.411" y2="214.919" gradientUnits="userSpaceOnUse">
<stop stop-color="#5ED0F2"/>
<stop offset="1" stop-color="#1494DF"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,12 @@
// 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 Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Ext.Shell;
internal sealed class Icons
{
internal static IconInfo RunV2 { get; } = IconHelpers.FromRelativePath("Assets\\Run@2x.svg");
}

View File

@@ -82,16 +82,16 @@
<ItemGroup>
<Content Update="Assets\Store.dark.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="Assets\Store.dark.svg">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="Assets\Store.light.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="Assets\Store.light.svg">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>

View File

@@ -18,6 +18,12 @@ namespace Microsoft.CmdPal.Ext.WinGet.Pages;
public partial class InstallPackageListItem : ListItem
{
private readonly CatalogPackage _package;
// Lazy-init the details
private readonly Lazy<Details?> _details;
public override IDetails? Details { get => _details.Value; set => base.Details = value; }
private InstallPackageCommand? _installCommand;
public InstallPackageListItem(CatalogPackage package)
@@ -25,24 +31,31 @@ public partial class InstallPackageListItem : ListItem
{
_package = package;
var version = _package.DefaultInstallVersion;
var versionText = version.Version;
var versionTagText = versionText == "Unknown" && version.PackageCatalog.Info.Id == "StoreEdgeFD" ? "msstore" : versionText;
PackageVersionInfo version = _package.DefaultInstallVersion;
string versionText = version.Version;
string versionTagText = versionText == "Unknown" && version.PackageCatalog.Info.Id == "StoreEdgeFD" ? "msstore" : versionText;
Title = _package.Name;
Subtitle = _package.Id;
Tags = [new Tag() { Text = versionTagText }];
var metadata = version.GetCatalogPackageMetadata();
_details = new Lazy<Details?>(() => BuildDetails(version));
_ = Task.Run(UpdatedInstalledStatus);
}
private Details? BuildDetails(PackageVersionInfo? version)
{
CatalogPackageMetadata? metadata = version?.GetCatalogPackageMetadata();
if (metadata != null)
{
var description = string.IsNullOrEmpty(metadata.Description) ? metadata.ShortDescription : metadata.Description;
var detailsBody = $"""
string description = string.IsNullOrEmpty(metadata.Description) ? metadata.ShortDescription : metadata.Description;
string detailsBody = $"""
{description}
""";
var heroIcon = new IconInfo(string.Empty);
var icons = metadata.Icons;
IconInfo heroIcon = new(string.Empty);
IReadOnlyList<Icon> icons = metadata.Icons;
if (icons.Count > 0)
{
// There's also a .Theme property we could probably use to
@@ -50,7 +63,7 @@ public partial class InstallPackageListItem : ListItem
heroIcon = new IconInfo(icons[0].Url);
}
Details = new Details()
return new Details()
{
Body = detailsBody,
Title = metadata.PackageName,
@@ -59,7 +72,7 @@ public partial class InstallPackageListItem : ListItem
};
}
_ = Task.Run(UpdatedInstalledStatus);
return null;
}
private List<IDetailsElement> GetDetailsMetadata(CatalogPackageMetadata metadata)
@@ -80,23 +93,23 @@ public partial class InstallPackageListItem : ListItem
{ Properties.Resources.winget_view_release_notes, (string.IsNullOrEmpty(metadata.ReleaseNotesUrl) ? string.Empty : Properties.Resources.winget_view_online, metadata.ReleaseNotesUrl) },
{ Properties.Resources.winget_publisher_support, (string.Empty, metadata.PublisherSupportUrl) },
};
var docs = metadata.Documentations.ToArray();
foreach (var item in docs)
Documentation[] docs = metadata.Documentations.ToArray();
foreach (Documentation? item in docs)
{
simpleData.Add(item.DocumentLabel, (string.Empty, item.DocumentUrl));
}
var options = default(UriCreationOptions);
foreach (var kv in simpleData)
UriCreationOptions options = default;
foreach (KeyValuePair<string, (string, string)> kv in simpleData)
{
var text = string.IsNullOrEmpty(kv.Value.Item1) ? kv.Value.Item2 : kv.Value.Item1;
var target = kv.Value.Item2;
string text = string.IsNullOrEmpty(kv.Value.Item1) ? kv.Value.Item2 : kv.Value.Item1;
string target = kv.Value.Item2;
if (!string.IsNullOrEmpty(text))
{
Uri? uri = null;
Uri.TryCreate(target, options, out uri);
var pair = new DetailsElement()
DetailsElement pair = new()
{
Key = kv.Key,
Data = new DetailsLink() { Link = uri, Text = text },
@@ -107,7 +120,7 @@ public partial class InstallPackageListItem : ListItem
if (metadata.Tags.Any())
{
var pair = new DetailsElement()
DetailsElement pair = new()
{
Key = "Tags",
Data = new DetailsTags() { Tags = metadata.Tags.Select(t => new Tag(t)).ToArray() },
@@ -120,18 +133,18 @@ public partial class InstallPackageListItem : ListItem
private async void UpdatedInstalledStatus()
{
var status = await _package.CheckInstalledStatusAsync();
var isInstalled = _package.InstalledVersion != null;
CheckInstalledStatusResult status = await _package.CheckInstalledStatusAsync();
bool isInstalled = _package.InstalledVersion != null;
// might be an uninstall command
var installCommand = new InstallPackageCommand(_package, isInstalled);
InstallPackageCommand installCommand = new(_package, isInstalled);
if (isInstalled)
{
this.Icon = InstallPackageCommand.CompletedIcon;
this.Command = new NoOpCommand();
List<IContextItem> contextMenu = [];
var uninstallContextItem = new CommandContextItem(installCommand)
CommandContextItem uninstallContextItem = new(installCommand)
{
IsCritical = true,
Icon = InstallPackageCommand.DeleteIcon,
@@ -139,8 +152,8 @@ public partial class InstallPackageListItem : ListItem
if (WinGetStatics.AppSearchCallback != null)
{
var callback = WinGetStatics.AppSearchCallback;
var installedApp = callback(_package.DefaultInstallVersion.DisplayName);
Func<string, ICommandItem?> callback = WinGetStatics.AppSearchCallback;
ICommandItem? installedApp = callback(_package.DefaultInstallVersion.DisplayName);
if (installedApp != null)
{
this.Command = installedApp.Command;
@@ -177,11 +190,11 @@ public partial class InstallPackageListItem : ListItem
Stopwatch s = new();
Logger.LogDebug($"Starting RefreshPackageCatalogAsync");
s.Start();
var refs = WinGetStatics.AvailableCatalogs.ToArray();
PackageCatalogReference[] refs = WinGetStatics.AvailableCatalogs.ToArray();
foreach (var catalog in refs)
foreach (PackageCatalogReference? catalog in refs)
{
var operation = catalog.RefreshPackageCatalogAsync();
global::Windows.Foundation.IAsyncOperationWithProgress<RefreshPackageCatalogResult, double> operation = catalog.RefreshPackageCatalogAsync();
operation.Wait();
}

View File

@@ -57,7 +57,7 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
{
// emptySearchForTag ===
// we don't have results yet, we haven't typed anything, and we're searching for a tag
var emptySearchForTag = _results == null &&
bool emptySearchForTag = _results == null &&
string.IsNullOrEmpty(SearchText) &&
HasTag;
@@ -70,8 +70,9 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
if (_results != null && _results.Any())
{
ListItem[] results = _results.Select(PackageToListItem).ToArray();
IsLoading = false;
return _results.Select(PackageToListItem).ToArray();
return results;
}
}
@@ -111,7 +112,7 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
_cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = _cancellationTokenSource.Token;
CancellationToken cancellationToken = _cancellationTokenSource.Token;
IsLoading = true;
@@ -128,7 +129,7 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
{
try
{
var results = await searchTask;
IEnumerable<CatalogPackage> results = await searchTask;
// Ensure this is still the latest task
if (_currentSearchTask == searchTask)
@@ -174,17 +175,17 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
return [];
}
var searchDebugText = $"{query}{(HasTag ? "+" : string.Empty)}{_tag}";
string searchDebugText = $"{query}{(HasTag ? "+" : string.Empty)}{_tag}";
Logger.LogDebug($"Starting search for '{searchDebugText}'");
var results = new HashSet<CatalogPackage>(new PackageIdCompare());
HashSet<CatalogPackage> results = new(new PackageIdCompare());
// Default selector: this is the way to do a `winget search <query>`
var selector = WinGetStatics.WinGetFactory.CreatePackageMatchFilter();
PackageMatchFilter selector = WinGetStatics.WinGetFactory.CreatePackageMatchFilter();
selector.Field = Microsoft.Management.Deployment.PackageMatchField.CatalogDefault;
selector.Value = query;
selector.Option = PackageFieldMatchOption.ContainsCaseInsensitive;
var opts = WinGetStatics.WinGetFactory.CreateFindPackagesOptions();
FindPackagesOptions opts = WinGetStatics.WinGetFactory.CreateFindPackagesOptions();
opts.Selectors.Add(selector);
// testing
@@ -193,7 +194,7 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
// Selectors is "OR", Filters is "AND"
if (HasTag)
{
var tagFilter = WinGetStatics.WinGetFactory.CreatePackageMatchFilter();
PackageMatchFilter tagFilter = WinGetStatics.WinGetFactory.CreatePackageMatchFilter();
tagFilter.Field = Microsoft.Management.Deployment.PackageMatchField.Tag;
tagFilter.Value = _tag;
tagFilter.Option = PackageFieldMatchOption.ContainsCaseInsensitive;
@@ -204,11 +205,11 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
// Clean up here, then...
ct.ThrowIfCancellationRequested();
var catalogTask = HasTag ? WinGetStatics.CompositeWingetCatalog : WinGetStatics.CompositeAllCatalog;
Lazy<Task<PackageCatalog>> catalogTask = HasTag ? WinGetStatics.CompositeWingetCatalog : WinGetStatics.CompositeAllCatalog;
// Both these catalogs should have been instantiated by the
// WinGetStatics static ctor when we were created.
var catalog = await catalogTask.Value;
PackageCatalog catalog = await catalogTask.Value;
if (catalog == null)
{
@@ -224,8 +225,8 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
// BODGY, re: microsoft/winget-cli#5151
// FindPackagesAsync isn't actually async.
var internalSearchTask = Task.Run(() => catalog.FindPackages(opts), ct);
var searchResults = await internalSearchTask;
Task<FindPackagesResult> internalSearchTask = Task.Run(() => catalog.FindPackages(opts), ct);
FindPackagesResult searchResults = await internalSearchTask;
// TODO more error handling like this:
if (searchResults.Status != FindPackagesResultStatus.Ok)
@@ -236,12 +237,12 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
}
Logger.LogDebug($" got results for ({query})", memberName: nameof(DoSearchAsync));
foreach (var match in searchResults.Matches.ToArray())
foreach (Management.Deployment.MatchResult? match in searchResults.Matches.ToArray())
{
ct.ThrowIfCancellationRequested();
// Print the packages
var package = match.CatalogPackage;
CatalogPackage package = match.CatalogPackage;
results.Add(package);
}