diff --git a/Bonsai.Configuration/Bootstrapper.cs b/Bonsai.Configuration/Bootstrapper.cs index 77a43bbd6..b47a2aabf 100644 --- a/Bonsai.Configuration/Bootstrapper.cs +++ b/Bonsai.Configuration/Bootstrapper.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; using System.Threading.Tasks; namespace Bonsai.Configuration @@ -29,9 +30,9 @@ public LicenseAwarePackageManager PackageManager get { return packageManager; } } - protected virtual async Task RunPackageOperationAsync(Func operationFactory) + protected virtual async Task RunPackageOperationAsync(Func operationFactory, CancellationToken cancellationToken) { - await operationFactory(); + await operationFactory(cancellationToken); } static NuGetVersion ParseVersion(string version) @@ -52,7 +53,8 @@ public async Task RunAsync( NuGetFramework projectFramework, PackageConfiguration packageConfiguration, string bootstrapperPath, - PackageIdentity bootstrapperPackage) + PackageIdentity bootstrapperPackage, + CancellationToken cancellationToken = default) { const string OldExtension = ".old"; var backupExePath = bootstrapperPath + OldExtension; @@ -65,7 +67,7 @@ public async Task RunAsync( var missingPackages = GetMissingPackages(packageConfiguration, packageManager.LocalRepository).ToList(); if (missingPackages.Count > 0) { - async Task RestoreMissingPackages() + async Task RestoreMissingPackages(CancellationToken cancellationToken) { using var monitor = new PackageConfigurationUpdater( projectFramework, @@ -75,19 +77,23 @@ async Task RestoreMissingPackages() bootstrapperPackage); foreach (var package in missingPackages) { - await packageManager.StartRestorePackage(package.Id, ParseVersion(package.Version), projectFramework); + await packageManager.RestorePackageAsync( + package.Id, + ParseVersion(package.Version), + projectFramework, + cancellationToken); } }; packageManager.RestorePackages = true; - try { await RunPackageOperationAsync(RestoreMissingPackages); } + try { await RunPackageOperationAsync(RestoreMissingPackages, cancellationToken); } finally { packageManager.RestorePackages = false; } } var editorPackage = packageManager.LocalRepository.FindLocalPackage(bootstrapperPackage.Id); if (editorPackage == null || editorPackage.Identity.Version < bootstrapperPackage.Version) { - async Task RestoreEditorPackage() + async Task RestoreEditorPackage(CancellationToken cancellationToken) { using var monitor = new PackageConfigurationUpdater( projectFramework, @@ -95,11 +101,15 @@ async Task RestoreEditorPackage() packageManager, bootstrapperPath, bootstrapperPackage); - var package = await packageManager.StartInstallPackage(bootstrapperPackage.Id, bootstrapperPackage.Version, projectFramework); + var package = await packageManager.InstallPackageAsync( + bootstrapperPackage.Id, + bootstrapperPackage.Version, + projectFramework, + cancellationToken); editorPackage = packageManager.LocalRepository.GetLocalPackage(package.GetIdentity()); }; - await RunPackageOperationAsync(RestoreEditorPackage); + await RunPackageOperationAsync(RestoreEditorPackage, cancellationToken); } } @@ -112,7 +122,7 @@ public RestorePackageManager(PackageSourceProvider packageSourceProvider, string public bool RestorePackages { get; set; } - protected override bool AcceptLicenseAgreement(IEnumerable licensePackages) + protected override bool AcceptLicenseAgreement(IEnumerable licensePackages) { if (RestorePackages) return true; else return base.AcceptLicenseAgreement(licensePackages); diff --git a/Bonsai.Configuration/PackageConfigurationUpdater.cs b/Bonsai.Configuration/PackageConfigurationUpdater.cs index db7fb95c3..77c829d2f 100644 --- a/Bonsai.Configuration/PackageConfigurationUpdater.cs +++ b/Bonsai.Configuration/PackageConfigurationUpdater.cs @@ -19,12 +19,8 @@ namespace Bonsai.Configuration { public class PackageConfigurationUpdater : IDisposable { - const string PackageTagFilter = "Bonsai"; - const string GalleryDirectory = "Gallery"; - const string ExtensionsDirectory = "Extensions"; const string BinDirectory = "bin"; const string DebugDirectory = "debug"; - const string BonsaiExtension = ".bonsai"; const string AssemblyExtension = ".dll"; const string OldExtension = ".old"; @@ -55,7 +51,7 @@ public PackageConfigurationUpdater(NuGetFramework projectFramework, PackageConfi configurationPlugin = new PackageConfigurationPlugin(this); packageManager.PackageManagerPlugins.Add(configurationPlugin); - var galleryPath = Path.Combine(bootstrapperDirectory, GalleryDirectory); + var galleryPath = Path.Combine(bootstrapperDirectory, Constants.GalleryDirectory); var galleryPackageSource = new PackageSource(galleryPath); galleryRepository = new SourceRepository(galleryPackageSource, Repository.Provider.GetCoreV3()); NormalizePathSeparators(packageConfiguration); @@ -92,12 +88,6 @@ static void NormalizePathSeparators(PackageConfiguration configuration) } } - static bool IsTaggedPackage(PackageReaderBase package) - { - var tags = package.NuspecReader.GetTags(); - return tags != null && tags.Contains(PackageTagFilter); - } - static ProcessorArchitecture ResolveArchitectureAlias(string name) { switch (name) @@ -410,10 +400,7 @@ public PackageConfigurationPlugin(PackageConfigurationUpdater owner) public override async Task OnPackageInstallingAsync(PackageIdentity package, NuGetFramework projectFramework, PackageReaderBase packageReader, string installPath) { - var entryPoint = package.Id + BonsaiExtension; - var nearestFrameworkGroup = packageReader.GetContentItems().GetNearest(projectFramework); - var executablePackage = nearestFrameworkGroup?.Items.Any(file => PathUtility.GetRelativePath(ContentFolder, file) == entryPoint); - if (executablePackage.GetValueOrDefault()) + if (packageReader.IsExecutablePackage(package, projectFramework)) { var packageFolder = Path.GetDirectoryName(packageReader.GetNuspecFile()); var resolver = new VersionFolderPathResolver(packageFolder, isLowercase: false); @@ -461,7 +448,7 @@ public override async Task OnPackageInstallingAsync(PackageIdentity packag public override Task OnPackageInstalledAsync(PackageIdentity package, NuGetFramework projectFramework, PackageReaderBase packageReader, string installPath) { var packageConfiguration = Owner.packageConfiguration; - var taggedPackage = IsTaggedPackage(packageReader); + var addReferences = packageReader.IsLibraryPackage(); var relativePath = Owner.GetRelativePath(installPath); if (!packageConfiguration.Packages.Contains(package.Id)) { @@ -469,7 +456,7 @@ public override Task OnPackageInstalledAsync(PackageIdentity package, NuGetFrame } else packageConfiguration.Packages[package.Id].Version = package.Version.ToString(); - Owner.AddContentFolders(installPath, ExtensionsDirectory); + Owner.AddContentFolders(installPath, Constants.ExtensionsDirectory); Owner.RegisterLibraryFolders(packageReader, relativePath); Owner.RegisterAssemblyLocations(packageReader, installPath, relativePath, false); if (IsRunningOnMono) Owner.AddAssemblyConfigFiles(packageReader, installPath); @@ -494,7 +481,7 @@ public override Task OnPackageInstalledAsync(PackageIdentity package, NuGetFrame // resolution is OS-specific and architecture-specific and should not be versioned together // with the package dependency graph. var assemblyLocations = GetCompatibleAssemblyReferences(projectFramework, packageReader); - Owner.RegisterAssemblyLocations(assemblyLocations, installPath, relativePath, taggedPackage); + Owner.RegisterAssemblyLocations(assemblyLocations, installPath, relativePath, addReferences); packageConfiguration.Save(); if (package.Id == Owner.bootstrapperPackageId && package.Version > Owner.bootstrapperVersion) @@ -516,11 +503,11 @@ public override Task OnPackageInstalledAsync(PackageIdentity package, NuGetFrame public override async Task OnPackageUninstalledAsync(PackageIdentity package, NuGetFramework projectFramework, PackageReaderBase packageReader, string installPath) { - var taggedPackage = IsTaggedPackage(packageReader); + var removeReferences = packageReader.IsLibraryPackage(); var relativePath = Owner.GetRelativePath(installPath); Owner.packageConfiguration.Packages.Remove(package.Id); - Owner.RemoveContentFolders(packageReader, installPath, ExtensionsDirectory); + Owner.RemoveContentFolders(packageReader, installPath, Constants.ExtensionsDirectory); Owner.RemoveLibraryFolders(packageReader, relativePath); Owner.RemoveAssemblyLocations(packageReader, relativePath, false); if (IsRunningOnMono) Owner.RemoveAssemblyConfigFiles(packageReader, installPath); @@ -541,7 +528,7 @@ public override async Task OnPackageUninstalledAsync(PackageIdentity package, Nu } var assemblyLocations = GetCompatibleAssemblyReferences(projectFramework, packageReader); - Owner.RemoveAssemblyLocations(assemblyLocations, relativePath, taggedPackage); + Owner.RemoveAssemblyLocations(assemblyLocations, relativePath, removeReferences); Owner.packageConfiguration.Save(); if (pivots.Length > 0) diff --git a/Bonsai.Editor/AboutBox.cs b/Bonsai.Editor/AboutBox.cs index 886077861..c22cccb70 100644 --- a/Bonsai.Editor/AboutBox.cs +++ b/Bonsai.Editor/AboutBox.cs @@ -16,7 +16,10 @@ public AboutBox() this.labelVersion.Text = $"Version {AssemblyVersion}"; this.labelCopyright.Text = AssemblyCopyright; this.labelCompanyName.Text = AssemblyCompany; - this.textBoxDescription.Text = AssemblyDescription + Environment.NewLine + Resources.AttributionNotices; + this.textBoxDescription.Text = string.Format( + Resources.AttributionNotices, + AssemblyProduct, + AssemblyDescription); } internal static string BuildKindTitleSuffix diff --git a/Bonsai.Editor/Properties/Resources.Designer.cs b/Bonsai.Editor/Properties/Resources.Designer.cs index 295eb181f..d144573af 100644 --- a/Bonsai.Editor/Properties/Resources.Designer.cs +++ b/Bonsai.Editor/Properties/Resources.Designer.cs @@ -61,8 +61,9 @@ internal Resources() { } /// - /// Looks up a localized string similar to - ///The Bonsai framework includes software from the Reactive Extensions for .NET and the NuGet projects developed at the .NET Foundation (https://dotnetfoundation.org/). + /// Looks up a localized string similar to {1} + /// + ///{0} includes software and media content from the Reactive Extensions for .NET and the NuGet projects developed at the .NET Foundation (https://dotnetfoundation.org/). /// ///Copyright (c) .NET Foundation and Contributors. /// diff --git a/Bonsai.Editor/Properties/Resources.resx b/Bonsai.Editor/Properties/Resources.resx index d5b31a13b..67b87626b 100644 --- a/Bonsai.Editor/Properties/Resources.resx +++ b/Bonsai.Editor/Properties/Resources.resx @@ -170,8 +170,9 @@ - -The Bonsai framework includes software from the Reactive Extensions for .NET and the NuGet projects developed at the .NET Foundation (https://dotnetfoundation.org/). + {1} + +{0} includes software and media content from the Reactive Extensions for .NET and the NuGet projects developed at the .NET Foundation (https://dotnetfoundation.org/). Copyright (c) .NET Foundation and Contributors diff --git a/Bonsai.NuGet.Design/DrawingHelper.cs b/Bonsai.NuGet.Design/DrawingHelper.cs new file mode 100644 index 000000000..755cce264 --- /dev/null +++ b/Bonsai.NuGet.Design/DrawingHelper.cs @@ -0,0 +1,28 @@ +using System.Drawing; +using System.Drawing.Drawing2D; + +namespace Bonsai.NuGet.Design +{ + static class DrawingHelper + { + public static SizeF GetImageSize(this Graphics graphics, Image image) + { + return new( + width: image.Width * graphics.DpiX / image.HorizontalResolution, + height: image.Height * graphics.DpiY / image.VerticalResolution); + } + + public static Bitmap Resize(this Image image, Size newSize) + { + var result = new Bitmap(newSize.Width, newSize.Height); + using (var graphics = Graphics.FromImage(result)) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.DrawImage(image, 0, 0, newSize.Width, newSize.Height); + } + return result; + } + } +} diff --git a/Bonsai.NuGet.Design/FormatHelper.cs b/Bonsai.NuGet.Design/FormatHelper.cs new file mode 100644 index 000000000..0cfe32485 --- /dev/null +++ b/Bonsai.NuGet.Design/FormatHelper.cs @@ -0,0 +1,18 @@ +using System.Globalization; + +namespace Bonsai.NuGet.Design +{ + internal static class FormatHelper + { + public static string ToLargeSuffix(long count) + { + return count switch + { + > 999999999 => count.ToString("0,,,.#B", CultureInfo.InvariantCulture), + > 999999 => count.ToString("0,,.#M", CultureInfo.InvariantCulture), + > 999 => count.ToString("0,.#K", CultureInfo.InvariantCulture), + _ => count.ToString(CultureInfo.InvariantCulture) + }; + } + } +} diff --git a/Bonsai.NuGet.Design/GalleryDialog.Designer.cs b/Bonsai.NuGet.Design/GalleryDialog.Designer.cs index 163149f08..6b856a94b 100644 --- a/Bonsai.NuGet.Design/GalleryDialog.Designer.cs +++ b/Bonsai.NuGet.Design/GalleryDialog.Designer.cs @@ -32,23 +32,22 @@ private void InitializeComponent() this.mainLayoutPanel = new Bonsai.NuGet.Design.TableLayoutPanel(); this.packageViewLayoutPanel = new Bonsai.NuGet.Design.TableLayoutPanel(); this.filterLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); + this.searchComboBox = new Bonsai.NuGet.Design.CueBannerComboBox(); this.refreshButton = new System.Windows.Forms.Button(); this.prereleaseCheckBox = new System.Windows.Forms.CheckBox(); this.pageSelectorPanel = new System.Windows.Forms.Panel(); + this.packagePageSelector = new Bonsai.NuGet.Design.PackagePageSelector(); this.packageViewPanel = new Bonsai.NuGet.Design.TableLayoutPanel(); + this.packageView = new Bonsai.NuGet.Design.PackageView(); this.packageIcons = new System.Windows.Forms.ImageList(this.components); this.detailsLayoutPanel = new Bonsai.NuGet.Design.TableLayoutPanel(); this.searchLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); - this.sortLabel = new System.Windows.Forms.Label(); - this.packageSourceComboBox = new System.Windows.Forms.ComboBox(); this.settingsButton = new System.Windows.Forms.Button(); + this.packageSourceComboBox = new System.Windows.Forms.ComboBox(); + this.sortLabel = new System.Windows.Forms.Label(); + this.packageDetails = new Bonsai.NuGet.Design.PackageDetails(); this.closePanel = new System.Windows.Forms.Panel(); this.closeButton = new System.Windows.Forms.Button(); - this.settingsPanel = new System.Windows.Forms.Panel(); - this.searchComboBox = new Bonsai.NuGet.Design.CueBannerComboBox(); - this.packagePageSelector = new Bonsai.NuGet.Design.PackagePageSelector(); - this.packageView = new Bonsai.NuGet.Design.PackageView(); - this.packageDetails = new Bonsai.NuGet.Design.PackageDetails(); this.saveFolderDialog = new Bonsai.NuGet.Design.SaveFolderDialog(); this.mainLayoutPanel.SuspendLayout(); this.packageViewLayoutPanel.SuspendLayout(); @@ -64,20 +63,17 @@ private void InitializeComponent() // this.mainLayoutPanel.ColumnCount = 2; this.mainLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.mainLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 337F)); - this.mainLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 27F)); + this.mainLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 300F)); this.mainLayoutPanel.Controls.Add(this.packageViewLayoutPanel, 0, 0); this.mainLayoutPanel.Controls.Add(this.detailsLayoutPanel, 1, 0); this.mainLayoutPanel.Controls.Add(this.closePanel, 1, 1); - this.mainLayoutPanel.Controls.Add(this.settingsPanel, 0, 1); this.mainLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; this.mainLayoutPanel.Location = new System.Drawing.Point(0, 0); - this.mainLayoutPanel.Margin = new System.Windows.Forms.Padding(4); this.mainLayoutPanel.Name = "mainLayoutPanel"; this.mainLayoutPanel.RowCount = 2; this.mainLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.mainLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 62F)); - this.mainLayoutPanel.Size = new System.Drawing.Size(1072, 672); + this.mainLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 50F)); + this.mainLayoutPanel.Size = new System.Drawing.Size(944, 546); this.mainLayoutPanel.TabIndex = 0; // // packageViewLayoutPanel @@ -88,15 +84,15 @@ private void InitializeComponent() this.packageViewLayoutPanel.Controls.Add(this.pageSelectorPanel, 0, 2); this.packageViewLayoutPanel.Controls.Add(this.packageViewPanel, 0, 1); this.packageViewLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; - this.packageViewLayoutPanel.Location = new System.Drawing.Point(4, 4); - this.packageViewLayoutPanel.Margin = new System.Windows.Forms.Padding(4); + this.packageViewLayoutPanel.Location = new System.Drawing.Point(3, 3); this.packageViewLayoutPanel.Name = "packageViewLayoutPanel"; this.packageViewLayoutPanel.RowCount = 3; - this.packageViewLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); + this.mainLayoutPanel.SetRowSpan(this.packageViewLayoutPanel, 2); + this.packageViewLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F)); this.packageViewLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.packageViewLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 49F)); - this.packageViewLayoutPanel.Size = new System.Drawing.Size(727, 602); - this.packageViewLayoutPanel.TabIndex = 2; + this.packageViewLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 46F)); + this.packageViewLayoutPanel.Size = new System.Drawing.Size(638, 540); + this.packageViewLayoutPanel.TabIndex = 0; // // filterLayoutPanel // @@ -104,33 +100,43 @@ private void InitializeComponent() this.filterLayoutPanel.Controls.Add(this.refreshButton); this.filterLayoutPanel.Controls.Add(this.prereleaseCheckBox); this.filterLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; - this.filterLayoutPanel.Location = new System.Drawing.Point(4, 4); - this.filterLayoutPanel.Margin = new System.Windows.Forms.Padding(4); + this.filterLayoutPanel.Location = new System.Drawing.Point(3, 3); this.filterLayoutPanel.Name = "filterLayoutPanel"; - this.filterLayoutPanel.Size = new System.Drawing.Size(719, 29); - this.filterLayoutPanel.TabIndex = 1; + this.filterLayoutPanel.Size = new System.Drawing.Size(632, 24); + this.filterLayoutPanel.TabIndex = 0; + // + // searchComboBox + // + this.searchComboBox.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend; + this.searchComboBox.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems; + this.searchComboBox.CueBanner = null; + this.searchComboBox.FormattingEnabled = true; + this.searchComboBox.Location = new System.Drawing.Point(3, 3); + this.searchComboBox.Name = "searchComboBox"; + this.searchComboBox.Size = new System.Drawing.Size(226, 21); + this.searchComboBox.TabIndex = 0; // // refreshButton // this.refreshButton.FlatAppearance.BorderSize = 0; this.refreshButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.refreshButton.Image = global::Bonsai.NuGet.Design.Properties.Resources.RefreshImage; - this.refreshButton.Location = new System.Drawing.Point(311, 3); + this.refreshButton.Location = new System.Drawing.Point(234, 2); + this.refreshButton.Margin = new System.Windows.Forms.Padding(2); this.refreshButton.Name = "refreshButton"; - this.refreshButton.Size = new System.Drawing.Size(24, 23); - this.refreshButton.TabIndex = 5; - this.refreshButton.TabStop = false; + this.refreshButton.Size = new System.Drawing.Size(18, 19); + this.refreshButton.TabIndex = 1; this.refreshButton.UseVisualStyleBackColor = true; this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click); // // prereleaseCheckBox // this.prereleaseCheckBox.AutoSize = true; - this.prereleaseCheckBox.Location = new System.Drawing.Point(341, 3); - this.prereleaseCheckBox.Margin = new System.Windows.Forms.Padding(3, 6, 3, 3); + this.prereleaseCheckBox.Location = new System.Drawing.Point(256, 5); + this.prereleaseCheckBox.Margin = new System.Windows.Forms.Padding(2, 5, 2, 2); this.prereleaseCheckBox.Name = "prereleaseCheckBox"; - this.prereleaseCheckBox.Size = new System.Drawing.Size(147, 26); - this.prereleaseCheckBox.TabIndex = 3; + this.prereleaseCheckBox.Size = new System.Drawing.Size(113, 17); + this.prereleaseCheckBox.TabIndex = 2; this.prereleaseCheckBox.Text = "Include prerelease"; this.prereleaseCheckBox.UseVisualStyleBackColor = true; // @@ -138,11 +144,20 @@ private void InitializeComponent() // this.pageSelectorPanel.Controls.Add(this.packagePageSelector); this.pageSelectorPanel.Dock = System.Windows.Forms.DockStyle.Fill; - this.pageSelectorPanel.Location = new System.Drawing.Point(4, 557); - this.pageSelectorPanel.Margin = new System.Windows.Forms.Padding(4); + this.pageSelectorPanel.Location = new System.Drawing.Point(3, 497); this.pageSelectorPanel.Name = "pageSelectorPanel"; - this.pageSelectorPanel.Size = new System.Drawing.Size(719, 41); - this.pageSelectorPanel.TabIndex = 2; + this.pageSelectorPanel.Size = new System.Drawing.Size(632, 40); + this.pageSelectorPanel.TabIndex = 1; + // + // packagePageSelector + // + this.packagePageSelector.Location = new System.Drawing.Point(219, 7); + this.packagePageSelector.Margin = new System.Windows.Forms.Padding(4); + this.packagePageSelector.Name = "packagePageSelector"; + this.packagePageSelector.SelectedPage = 0; + this.packagePageSelector.ShowNext = false; + this.packagePageSelector.Size = new System.Drawing.Size(75, 27); + this.packagePageSelector.TabIndex = 0; // // packageViewPanel // @@ -151,14 +166,34 @@ private void InitializeComponent() this.packageViewPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.packageViewPanel.Controls.Add(this.packageView, 0, 1); this.packageViewPanel.Dock = System.Windows.Forms.DockStyle.Fill; - this.packageViewPanel.Location = new System.Drawing.Point(4, 41); - this.packageViewPanel.Margin = new System.Windows.Forms.Padding(4); + this.packageViewPanel.Location = new System.Drawing.Point(3, 33); this.packageViewPanel.Name = "packageViewPanel"; this.packageViewPanel.RowCount = 2; this.packageViewPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.packageViewPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.packageViewPanel.Size = new System.Drawing.Size(719, 508); - this.packageViewPanel.TabIndex = 3; + this.packageViewPanel.Size = new System.Drawing.Size(632, 458); + this.packageViewPanel.TabIndex = 2; + // + // packageView + // + this.packageView.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(246)))), ((int)(((byte)(246)))), ((int)(((byte)(246))))); + this.packageView.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.packageView.CanSelectNodes = false; + this.packageView.Dock = System.Windows.Forms.DockStyle.Fill; + this.packageView.FullRowSelect = true; + this.packageView.HotTracking = true; + this.packageView.ImageIndex = 0; + this.packageView.ImageList = this.packageIcons; + this.packageView.ItemHeight = 64; + this.packageView.Location = new System.Drawing.Point(3, 3); + this.packageView.Name = "packageView"; + this.packageView.Operation = Bonsai.NuGet.Design.PackageOperationType.Open; + this.packageView.SelectedImageIndex = 0; + this.packageView.ShowLines = false; + this.packageView.ShowRootLines = false; + this.packageView.Size = new System.Drawing.Size(626, 452); + this.packageView.TabIndex = 0; + this.packageView.OperationClick += new Bonsai.NuGet.Design.PackageViewEventHandler(this.packageView_OperationClick); // // packageIcons // @@ -173,159 +208,104 @@ private void InitializeComponent() this.detailsLayoutPanel.Controls.Add(this.searchLayoutPanel, 0, 0); this.detailsLayoutPanel.Controls.Add(this.packageDetails, 0, 1); this.detailsLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; - this.detailsLayoutPanel.Location = new System.Drawing.Point(739, 4); - this.detailsLayoutPanel.Margin = new System.Windows.Forms.Padding(4); + this.detailsLayoutPanel.Location = new System.Drawing.Point(647, 3); this.detailsLayoutPanel.Name = "detailsLayoutPanel"; this.detailsLayoutPanel.RowCount = 2; - this.detailsLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); + this.detailsLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F)); this.detailsLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.detailsLayoutPanel.Size = new System.Drawing.Size(329, 602); - this.detailsLayoutPanel.TabIndex = 3; + this.detailsLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.detailsLayoutPanel.Size = new System.Drawing.Size(294, 490); + this.detailsLayoutPanel.TabIndex = 1; // // searchLayoutPanel // - this.searchLayoutPanel.Controls.Add(this.sortLabel); - this.searchLayoutPanel.Controls.Add(this.packageSourceComboBox); this.searchLayoutPanel.Controls.Add(this.settingsButton); + this.searchLayoutPanel.Controls.Add(this.packageSourceComboBox); + this.searchLayoutPanel.Controls.Add(this.sortLabel); this.searchLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; - this.searchLayoutPanel.Location = new System.Drawing.Point(4, 4); - this.searchLayoutPanel.Margin = new System.Windows.Forms.Padding(4); + this.searchLayoutPanel.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; + this.searchLayoutPanel.Location = new System.Drawing.Point(3, 3); this.searchLayoutPanel.Name = "searchLayoutPanel"; - this.searchLayoutPanel.Size = new System.Drawing.Size(321, 29); + this.searchLayoutPanel.Size = new System.Drawing.Size(288, 24); this.searchLayoutPanel.TabIndex = 0; // - // sortLabel + // settingsButton // - this.sortLabel.Location = new System.Drawing.Point(4, 0); - this.sortLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); - this.sortLabel.Name = "sortLabel"; - this.sortLabel.Size = new System.Drawing.Size(120, 27); - this.sortLabel.TabIndex = 1; - this.sortLabel.Text = "Package source:"; - this.sortLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.settingsButton.FlatAppearance.BorderSize = 0; + this.settingsButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.settingsButton.Image = global::Bonsai.NuGet.Design.Properties.Resources.SettingsImage; + this.settingsButton.Location = new System.Drawing.Point(268, 2); + this.settingsButton.Margin = new System.Windows.Forms.Padding(2); + this.settingsButton.Name = "settingsButton"; + this.settingsButton.Size = new System.Drawing.Size(18, 19); + this.settingsButton.TabIndex = 2; + this.settingsButton.UseVisualStyleBackColor = true; + this.settingsButton.Click += new System.EventHandler(this.settingsButton_Click); // // packageSourceComboBox // this.packageSourceComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.packageSourceComboBox.FormattingEnabled = true; - this.packageSourceComboBox.Location = new System.Drawing.Point(132, 4); - this.packageSourceComboBox.Margin = new System.Windows.Forms.Padding(4); + this.packageSourceComboBox.Location = new System.Drawing.Point(149, 3); this.packageSourceComboBox.Name = "packageSourceComboBox"; - this.packageSourceComboBox.Size = new System.Drawing.Size(150, 24); - this.packageSourceComboBox.TabIndex = 0; + this.packageSourceComboBox.Size = new System.Drawing.Size(114, 21); + this.packageSourceComboBox.TabIndex = 1; this.packageSourceComboBox.SelectedIndexChanged += new System.EventHandler(this.refreshButton_Click); // - // settingsButton + // sortLabel // - this.settingsButton.FlatAppearance.BorderSize = 0; - this.settingsButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.settingsButton.Image = global::Bonsai.NuGet.Design.Properties.Resources.SettingsImage; - this.settingsButton.Location = new System.Drawing.Point(289, 3); - this.settingsButton.Name = "settingsButton"; - this.settingsButton.Size = new System.Drawing.Size(24, 23); - this.settingsButton.TabIndex = 5; - this.settingsButton.UseVisualStyleBackColor = true; - this.settingsButton.Click += new System.EventHandler(this.settingsButton_Click); + this.sortLabel.Location = new System.Drawing.Point(53, 0); + this.sortLabel.Name = "sortLabel"; + this.sortLabel.Size = new System.Drawing.Size(90, 22); + this.sortLabel.TabIndex = 0; + this.sortLabel.Text = "Package source:"; + this.sortLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // packageDetails + // + this.packageDetails.Dock = System.Windows.Forms.DockStyle.Fill; + this.packageDetails.Location = new System.Drawing.Point(4, 34); + this.packageDetails.Margin = new System.Windows.Forms.Padding(4); + this.packageDetails.Name = "packageDetails"; + this.packageDetails.Operation = Bonsai.NuGet.Design.PackageOperationType.Install; + this.packageDetails.Size = new System.Drawing.Size(286, 452); + this.packageDetails.TabIndex = 1; + this.packageDetails.OperationClick += new Bonsai.NuGet.Design.PackageViewEventHandler(this.packageView_OperationClick); // // closePanel // this.closePanel.Controls.Add(this.closeButton); this.closePanel.Dock = System.Windows.Forms.DockStyle.Fill; - this.closePanel.Location = new System.Drawing.Point(739, 614); - this.closePanel.Margin = new System.Windows.Forms.Padding(4); + this.closePanel.Location = new System.Drawing.Point(647, 499); this.closePanel.Name = "closePanel"; - this.closePanel.Size = new System.Drawing.Size(329, 54); - this.closePanel.TabIndex = 5; + this.closePanel.Size = new System.Drawing.Size(294, 44); + this.closePanel.TabIndex = 2; // // closeButton // - this.closeButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.closeButton.Anchor = System.Windows.Forms.AnchorStyles.Right; this.closeButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.closeButton.Location = new System.Drawing.Point(217, 15); - this.closeButton.Margin = new System.Windows.Forms.Padding(4); + this.closeButton.Location = new System.Drawing.Point(210, 12); this.closeButton.Name = "closeButton"; - this.closeButton.Size = new System.Drawing.Size(100, 28); + this.closeButton.Size = new System.Drawing.Size(75, 23); this.closeButton.TabIndex = 0; this.closeButton.Text = "Close"; this.closeButton.UseVisualStyleBackColor = true; this.closeButton.Click += new System.EventHandler(this.closeButton_Click); // - // settingsPanel - // - this.settingsPanel.Dock = System.Windows.Forms.DockStyle.Fill; - this.settingsPanel.Location = new System.Drawing.Point(4, 614); - this.settingsPanel.Margin = new System.Windows.Forms.Padding(4); - this.settingsPanel.Name = "settingsPanel"; - this.settingsPanel.Size = new System.Drawing.Size(727, 54); - this.settingsPanel.TabIndex = 6; - // - // searchComboBox - // - this.searchComboBox.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend; - this.searchComboBox.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems; - this.searchComboBox.CueBanner = null; - this.searchComboBox.FormattingEnabled = true; - this.searchComboBox.Location = new System.Drawing.Point(4, 4); - this.searchComboBox.Margin = new System.Windows.Forms.Padding(4); - this.searchComboBox.Name = "searchComboBox"; - this.searchComboBox.Size = new System.Drawing.Size(300, 24); - this.searchComboBox.TabIndex = 0; - // - // packagePageSelector - // - this.packagePageSelector.AutoSize = true; - this.packagePageSelector.Dock = System.Windows.Forms.DockStyle.Fill; - this.packagePageSelector.Location = new System.Drawing.Point(0, 0); - this.packagePageSelector.Margin = new System.Windows.Forms.Padding(5); - this.packagePageSelector.Name = "packagePageSelector"; - this.packagePageSelector.Size = new System.Drawing.Size(719, 41); - this.packagePageSelector.TabIndex = 0; - // - // packageView - // - this.packageView.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(246)))), ((int)(((byte)(246)))), ((int)(((byte)(246))))); - this.packageView.BorderStyle = System.Windows.Forms.BorderStyle.None; - this.packageView.CanSelectNodes = false; - this.packageView.Dock = System.Windows.Forms.DockStyle.Fill; - this.packageView.DrawMode = System.Windows.Forms.TreeViewDrawMode.OwnerDrawText; - this.packageView.FullRowSelect = true; - this.packageView.ImageIndex = 0; - this.packageView.ImageList = this.packageIcons; - this.packageView.ItemHeight = 64; - this.packageView.Location = new System.Drawing.Point(4, 4); - this.packageView.Margin = new System.Windows.Forms.Padding(4); - this.packageView.Name = "packageView"; - this.packageView.OperationText = null; - this.packageView.SelectedImageIndex = 0; - this.packageView.ShowLines = false; - this.packageView.ShowRootLines = false; - this.packageView.Size = new System.Drawing.Size(711, 500); - this.packageView.TabIndex = 1; - this.packageView.OperationClick += new System.Windows.Forms.TreeViewEventHandler(this.packageView_OperationClick); - // - // packageDetails - // - this.packageDetails.Dock = System.Windows.Forms.DockStyle.Fill; - this.packageDetails.Location = new System.Drawing.Point(5, 42); - this.packageDetails.Margin = new System.Windows.Forms.Padding(5); - this.packageDetails.Name = "packageDetails"; - this.packageDetails.Size = new System.Drawing.Size(319, 555); - this.packageDetails.TabIndex = 1; - // // saveFolderDialog // this.saveFolderDialog.FileName = ""; // // GalleryDialog // - this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.closeButton; - this.ClientSize = new System.Drawing.Size(1072, 672); + this.ClientSize = new System.Drawing.Size(944, 546); this.Controls.Add(this.mainLayoutPanel); this.KeyPreview = true; - this.Margin = new System.Windows.Forms.Padding(4); - this.MinimumSize = new System.Drawing.Size(1087, 709); + this.MinimumSize = new System.Drawing.Size(850, 583); this.Name = "GalleryDialog"; this.ShowIcon = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; @@ -333,8 +313,8 @@ private void InitializeComponent() this.mainLayoutPanel.ResumeLayout(false); this.packageViewLayoutPanel.ResumeLayout(false); this.filterLayoutPanel.ResumeLayout(false); + this.filterLayoutPanel.PerformLayout(); this.pageSelectorPanel.ResumeLayout(false); - this.pageSelectorPanel.PerformLayout(); this.packageViewPanel.ResumeLayout(false); this.detailsLayoutPanel.ResumeLayout(false); this.searchLayoutPanel.ResumeLayout(false); @@ -361,7 +341,6 @@ private void InitializeComponent() private System.Windows.Forms.Button closeButton; private Bonsai.NuGet.Design.TableLayoutPanel packageViewPanel; private PackageView packageView; - private System.Windows.Forms.Panel settingsPanel; private SaveFolderDialog saveFolderDialog; private System.Windows.Forms.CheckBox prereleaseCheckBox; private System.Windows.Forms.Button settingsButton; diff --git a/Bonsai.NuGet.Design/GalleryDialog.cs b/Bonsai.NuGet.Design/GalleryDialog.cs index 28c7678f8..e970822c7 100644 --- a/Bonsai.NuGet.Design/GalleryDialog.cs +++ b/Bonsai.NuGet.Design/GalleryDialog.cs @@ -33,9 +33,9 @@ public GalleryDialog(NuGetFramework projectFramework, string path) searchComboBox, prereleaseCheckBox, () => false, - value => { }, - new[] { Constants.GalleryPackageType }); + value => { }); packageViewController.SearchPrefix = $"tags:{Constants.GalleryDirectory} "; + packageViewController.PackageTypes = new[] { Constants.GalleryPackageType }; packageViewController.PackageManager.PackageManagerPlugins.Add(new GalleryPackagePlugin(this)); InitializePackageSourceItems(); } @@ -83,9 +83,9 @@ protected override void OnResizeEnd(EventArgs e) base.OnResizeEnd(e); } - private void packageView_OperationClick(object sender, TreeViewEventArgs e) + private void packageView_OperationClick(object sender, PackageViewEventArgs e) { - var package = (IPackageSearchMetadata)e.Node.Tag; + var package = e.Package; if (package != null) { if (!package.Tags.Contains(Constants.GalleryDirectory)) @@ -123,7 +123,7 @@ public override Task OnPackageInstallingAsync(PackageIdentity package, NuG { if (PackageIdentityComparer.Default.Equals(package, Owner.targetPackage)) { - Owner.InstallPath = PackageHelper.InstallExecutablePackage(package, projectFramework, packageReader, Owner.targetPath); + Owner.InstallPath = packageReader.InstallExecutablePackage(package, projectFramework, Owner.targetPath); Owner.DialogResult = DialogResult.OK; } @@ -159,7 +159,6 @@ private void UpdateSelectedRepository() } else packageViewController.SelectedRepository = null; - packageView.OperationText = Resources.OpenOperationName; searchComboBox.Text = string.Empty; packageViewController.UpdatePackageQuery(); } diff --git a/Bonsai.NuGet.Design/IconReader.cs b/Bonsai.NuGet.Design/IconReader.cs index ecae02aa2..20af3f5c6 100644 --- a/Bonsai.NuGet.Design/IconReader.cs +++ b/Bonsai.NuGet.Design/IconReader.cs @@ -1,12 +1,11 @@ using System; using System.Collections.Concurrent; using System.Drawing; -using System.Drawing.Drawing2D; -using System.Drawing.Imaging; using System.IO; using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Bonsai.NuGet.Design.Properties; using NuGet.Packaging; namespace Bonsai.NuGet.Design @@ -14,16 +13,15 @@ namespace Bonsai.NuGet.Design class IconReader { static readonly HttpClient httpClient = GetHttpClient(); - static readonly Uri PackageDefaultIconUrl = new("https://www.nuget.org/Content/Images/packageDefaultIcon.png"); - static readonly Image DefaultIconImage = new Bitmap(32, 32, PixelFormat.Format32bppArgb); readonly ConcurrentDictionary>> iconCache = new(); - readonly Task defaultIcon; + readonly Task defaultIconTask; readonly Size targetSize; public IconReader(Size size) { targetSize = size; - defaultIcon = GetAsync(PackageDefaultIconUrl); + DefaultIcon = Resources.PackageDefaultIcon.Resize(targetSize); + defaultIconTask = Task.FromResult(DefaultIcon); } static HttpClient GetHttpClient() @@ -39,16 +37,16 @@ public void ClearCache() iconCache.Clear(); } - public Task GetDefaultIconAsync() => defaultIcon; + public Image DefaultIcon { get; } public Task GetAsync(Uri iconUrl, CancellationToken cancellationToken = default) { - if (iconUrl == null) return defaultIcon; + if (iconUrl == null) return defaultIconTask; if (!iconCache.TryGetValue(iconUrl, out Lazy> result)) { var iconStream = new Lazy>(() => iconUrl.IsFile ? GetFileRequestAsync(iconUrl, cancellationToken) - : GetWebRequestAsync(iconUrl, defaultIcon, cancellationToken)); + : GetWebRequestAsync(iconUrl, cancellationToken)); result = iconCache.GetOrAdd(iconUrl, iconStream); } @@ -64,17 +62,17 @@ async Task GetFileRequestAsync(Uri requestUri, CancellationToken cancella using var packageReader = new PackageArchiveReader(requestUri.AbsolutePath); using var iconStream = await packageReader.GetStreamAsync(requestUri.Fragment.Substring(1), cancellationToken); using var image = Image.FromStream(iconStream); - return ResizeImage(image, targetSize); + return image.Resize(targetSize); } } catch (IOException) { } // fallback if error reading icon stream catch (ArgumentException) { } // fallback if invalid path or image data catch (UnauthorizedAccessException) { } // fallback if unauthorized access - return await defaultIcon; + return await defaultIconTask; } - async Task GetWebRequestAsync(Uri requestUri, Task fallbackImage = null, CancellationToken cancellationToken = default) + async Task GetWebRequestAsync(Uri requestUri, CancellationToken cancellationToken = default) { try { @@ -88,29 +86,14 @@ async Task GetWebRequestAsync(Uri requestUri, Task fallbackImage = try { using var image = Image.FromStream(responseStream); - return ResizeImage(image, targetSize); + return image.Resize(targetSize); } catch (ArgumentException) { } // fallback if decoding image fails } } } catch (HttpRequestException) { } // fallback if request fails - return fallbackImage != null - ? await fallbackImage - : DefaultIconImage; - } - - static Bitmap ResizeImage(Image image, Size newSize) - { - var result = new Bitmap(newSize.Width, newSize.Height); - using (var graphics = Graphics.FromImage(result)) - { - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - graphics.DrawImage(image, 0, 0, newSize.Width, newSize.Height); - } - return result; + return await defaultIconTask; } } } diff --git a/Bonsai.NuGet.Design/ImageLabel.cs b/Bonsai.NuGet.Design/ImageLabel.cs new file mode 100644 index 000000000..5608849e4 --- /dev/null +++ b/Bonsai.NuGet.Design/ImageLabel.cs @@ -0,0 +1,60 @@ +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace Bonsai.NuGet.Design +{ + internal class ImageLabel : Label + { + public ImageLabel() + { + ImageAlign = ContentAlignment.MiddleLeft; + TextAlign = ContentAlignment.MiddleRight; + } + + public new Image Image + { + get => base.Image; + set + { + base.Image = value; + if (AutoSize) + { + // force size calculation + AutoSize = false; + AutoSize = true; + } + } + } + + public new ImageList ImageList { get; set; } + + public new int ImageIndex { get; set; } + + public override Size GetPreferredSize(Size proposedSize) + { + var size = base.GetPreferredSize(proposedSize); + var image = ImageList != null ? ImageList.Images[ImageIndex] : Image; + if (image != null) + { + using var graphics = CreateGraphics(); + var imageSize = Size.Ceiling(graphics.GetImageSize(image)); + size.Width += imageSize.Width; + size.Height = Math.Max(size.Height, imageSize.Height); + } + return size; + } + + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + if (ImageList != null) + { + var rectangle = ClientRectangle; + var image = ImageList.Images[ImageIndex]; + var imageBounds = CalcImageRenderBounds(image, rectangle, ImageAlign); + ImageList.Draw(e.Graphics, imageBounds.Location, ImageIndex); + } + } + } +} diff --git a/Bonsai.NuGet.Design/ImageLinkLabel.cs b/Bonsai.NuGet.Design/ImageLinkLabel.cs new file mode 100644 index 000000000..5c93411c7 --- /dev/null +++ b/Bonsai.NuGet.Design/ImageLinkLabel.cs @@ -0,0 +1,43 @@ +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace Bonsai.NuGet.Design +{ + internal class ImageLinkLabel : LinkLabel + { + public ImageLinkLabel() + { + ImageAlign = ContentAlignment.MiddleLeft; + TextAlign = ContentAlignment.MiddleRight; + } + + public new Image Image + { + get => base.Image; + set + { + base.Image = value; + if (AutoSize) + { + // force size calculation + AutoSize = false; + AutoSize = true; + } + } + } + + public override Size GetPreferredSize(Size proposedSize) + { + var size = base.GetPreferredSize(proposedSize); + if (Image != null) + { + using var graphics = CreateGraphics(); + var imageSize = Size.Ceiling(graphics.GetImageSize(Image)); + size.Width += imageSize.Width; + size.Height = Math.Max(size.Height, imageSize.Height); + } + return size; + } + } +} diff --git a/Bonsai.NuGet.Design/LicenseAcceptanceDialog.cs b/Bonsai.NuGet.Design/LicenseAcceptanceDialog.cs index 214cac6d1..76939d75a 100644 --- a/Bonsai.NuGet.Design/LicenseAcceptanceDialog.cs +++ b/Bonsai.NuGet.Design/LicenseAcceptanceDialog.cs @@ -1,8 +1,6 @@ using Bonsai.NuGet.Design.Properties; -using NuGet.Protocol.Core.Types; using System; using System.Collections.Generic; -using System.Diagnostics; using System.Drawing; using System.Globalization; using System.Windows.Forms; @@ -11,7 +9,7 @@ namespace Bonsai.NuGet.Design { public partial class LicenseAcceptanceDialog : Form { - public LicenseAcceptanceDialog(IEnumerable licensePackages) + public LicenseAcceptanceDialog(IEnumerable licensePackages) { if (licensePackages == null) { @@ -21,12 +19,14 @@ public LicenseAcceptanceDialog(IEnumerable licensePackag InitializeComponent(); SuspendLayout(); var bold = new Font(Font, FontStyle.Bold); - foreach (var package in licensePackages) + foreach (var packageInfo in licensePackages) { + var package = packageInfo.Package; var titleAuthorshipPanel = new FlowLayoutPanel(); var titleLabel = new Label(); var authorshipLabel = new Label(); var viewLicenseLabel = new LinkLabel(); + viewLicenseLabel.AutoSize = true; viewLicenseLabel.LinkClicked += viewLicenseLabel_LinkClicked; titleLabel.Font = bold; @@ -36,7 +36,7 @@ public LicenseAcceptanceDialog(IEnumerable licensePackag var authorsText = string.Join(CultureInfo.CurrentCulture.TextInfo.ListSeparator, package.Authors); authorshipLabel.Text = string.Format(Resources.LicenseAuthorshipLabel, authorsText); viewLicenseLabel.Text = Resources.LicenseViewLicenseLabel; - SetLinkLabelUri(viewLicenseLabel, package.LicenseUrl); + LicenseHelper.SetLicenseLinkLabel(viewLicenseLabel, package, packageInfo.SourceRepository); titleAuthorshipPanel.Margin = new Padding(0, 3, 3, 3); titleAuthorshipPanel.AutoSize = true; titleAuthorshipPanel.Controls.Add(titleLabel); @@ -47,19 +47,9 @@ public LicenseAcceptanceDialog(IEnumerable licensePackag ResumeLayout(); } - static void SetLinkLabelUri(LinkLabel linkLabel, Uri uri) + async void viewLicenseLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { - linkLabel.Links[0].Description = uri != null && uri.IsAbsoluteUri ? uri.AbsoluteUri : null; - linkLabel.Links[0].LinkData = uri; - } - - void viewLicenseLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) - { - var linkUri = (Uri)e.Link.LinkData; - if (linkUri != null) - { - Process.Start(linkUri.AbsoluteUri); - } + await LicenseHelper.ShowLicenseAsync(e.Link, this); } } } diff --git a/Bonsai.NuGet.Design/LicenseFileDialog.Designer.cs b/Bonsai.NuGet.Design/LicenseFileDialog.Designer.cs new file mode 100644 index 000000000..af21ba847 --- /dev/null +++ b/Bonsai.NuGet.Design/LicenseFileDialog.Designer.cs @@ -0,0 +1,69 @@ +namespace Bonsai.NuGet.Design +{ + partial class LicenseFileDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.richTextBox = new System.Windows.Forms.RichTextBox(); + this.SuspendLayout(); + // + // richTextBox + // + this.richTextBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.richTextBox.Cursor = System.Windows.Forms.Cursors.Default; + this.richTextBox.DetectUrls = false; + this.richTextBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.richTextBox.Location = new System.Drawing.Point(0, 0); + this.richTextBox.Name = "richTextBox"; + this.richTextBox.ReadOnly = true; + this.richTextBox.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.Vertical; + this.richTextBox.Size = new System.Drawing.Size(404, 411); + this.richTextBox.TabIndex = 0; + this.richTextBox.TabStop = false; + this.richTextBox.Text = ""; + // + // LicenseFileDialog + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(404, 411); + this.Controls.Add(this.richTextBox); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "LicenseFileDialog"; + this.ShowIcon = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "RichTextDialog"; + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.RichTextBox richTextBox; + } +} \ No newline at end of file diff --git a/Bonsai.NuGet.Design/LicenseFileDialog.cs b/Bonsai.NuGet.Design/LicenseFileDialog.cs new file mode 100644 index 000000000..52756c303 --- /dev/null +++ b/Bonsai.NuGet.Design/LicenseFileDialog.cs @@ -0,0 +1,33 @@ +using System.Windows.Forms; + +namespace Bonsai.NuGet.Design +{ + public partial class LicenseFileDialog : Form + { + string licenseText; + + public LicenseFileDialog() + { + InitializeComponent(); + } + + public string LicenseText + { + get => licenseText; + set + { + licenseText = value; + FormatTextBox(); + } + } + + private void FormatTextBox() + { + richTextBox.Text = licenseText; + richTextBox.SelectAll(); + richTextBox.SelectionIndent += Margin.Left * 3; + richTextBox.SelectionRightIndent += Margin.Right * 3; + richTextBox.DeselectAll(); + } + } +} diff --git a/Bonsai.NuGet.Design/LicenseFileDialog.resx b/Bonsai.NuGet.Design/LicenseFileDialog.resx new file mode 100644 index 000000000..1af7de150 --- /dev/null +++ b/Bonsai.NuGet.Design/LicenseFileDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Bonsai.NuGet.Design/LicenseHelper.cs b/Bonsai.NuGet.Design/LicenseHelper.cs new file mode 100644 index 000000000..93c90ef5d --- /dev/null +++ b/Bonsai.NuGet.Design/LicenseHelper.cs @@ -0,0 +1,107 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Threading.Tasks; +using System.Windows.Forms; +using Bonsai.NuGet.Design.Properties; +using NuGet.Packaging; +using NuGet.Protocol; +using NuGet.Protocol.Core.Types; + +namespace Bonsai.NuGet.Design +{ + static class LicenseHelper + { + public static void SetLicenseLinkLabel(LinkLabel linkLabel, IPackageSearchMetadata package, SourceRepository sourceRepository) + { + var license = package.LicenseMetadata; + if (license != null && sourceRepository.PackageSource.IsLocal) + { + switch (license.Type) + { + case LicenseType.File: + var localPackage = sourceRepository.GetLocalPackage(package.Identity); + SetLinkLabelData(linkLabel, license.License, license, visible: true, localPackage); + break; + case LicenseType.Expression: + SetLinkLabelUri(linkLabel, license.LicenseUrl); + break; + default: break; + } + } + else SetLinkLabelUri(linkLabel, package.LicenseUrl); + } + + static void SetLinkLabelUri(LinkLabel linkLabel, Uri uri) + { + var description = uri != null && uri.IsAbsoluteUri ? uri.AbsoluteUri : null; + SetLinkLabelData(linkLabel, description, uri, visible: uri != null, tag: null); + } + + static void SetLinkLabelData( + LinkLabel linkLabel, + string description, + object linkData, + bool visible, + object tag) + { + linkLabel.Links[0].Description = description; + linkLabel.Links[0].LinkData = linkData; + linkLabel.Links[0].Tag = tag; + linkLabel.Visible = visible; + } + + public static async Task ShowLicenseAsync(LinkLabel.Link link, IWin32Window owner) + { + if (link.LinkData is Uri linkUri) + { + ShowExternal(linkUri.AbsoluteUri); + return; + } + + if (link.LinkData is LicenseMetadata license && + link.Tag is LocalPackageInfo localPackage) + { + await ShowLocalAsync(license, localPackage, owner); + } + } + + static async Task ShowLocalAsync( + LicenseMetadata licenseMetadata, + LocalPackageInfo localPackage, + IWin32Window owner) + { + try + { + using var stream = localPackage.GetReader().GetStream(licenseMetadata.License); + using var streamReader = new StreamReader(stream); + var licenseText = await streamReader.ReadToEndAsync(); + + using var dialog = new LicenseFileDialog(); + dialog.Text = $"{localPackage.Identity.Id} {Resources.LicenseLabel}"; + dialog.LicenseText = licenseText; + dialog.ShowDialog(owner); + } + catch { } //best effort + } + + static void ShowExternal(string uri) + { + var activeForm = Form.ActiveForm; + try + { + if (activeForm != null) activeForm.Cursor = Cursors.AppStarting; + if (NativeMethods.IsRunningOnMono && Environment.OSVersion.Platform == PlatformID.Unix) + { + Process.Start("xdg-open", uri); + } + else Process.Start(uri); + } + catch { } //best effort + finally + { + if (activeForm != null) activeForm.Cursor = null; + } + } + } +} diff --git a/Bonsai.NuGet.Design/PackageDetails.Designer.cs b/Bonsai.NuGet.Design/PackageDetails.Designer.cs index 6972169fb..8262b61fb 100644 --- a/Bonsai.NuGet.Design/PackageDetails.Designer.cs +++ b/Bonsai.NuGet.Design/PackageDetails.Designer.cs @@ -28,63 +28,85 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); this.detailsLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); + this.packageIdPanel = new System.Windows.Forms.FlowLayoutPanel(); + this.packageIdLabel = new Bonsai.NuGet.Design.ImageLabel(); + this.prefixReservedIcon = new System.Windows.Forms.PictureBox(); + this.installedVersionLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); + this.installedHeader = new System.Windows.Forms.Label(); + this.installedVersionTextBox = new System.Windows.Forms.TextBox(); + this.uninstallButton = new System.Windows.Forms.Button(); + this.versionLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); + this.versionHeader = new System.Windows.Forms.Label(); + this.versionComboBox = new System.Windows.Forms.ComboBox(); + this.operationButton = new System.Windows.Forms.Button(); + this.deprecationMetadataPanel = new System.Windows.Forms.FlowLayoutPanel(); + this.warningImageLabel = new Bonsai.NuGet.Design.ImageLabel(); + this.deprecationMetadataLabel = new System.Windows.Forms.Label(); + this.alternatePackagePanel = new System.Windows.Forms.FlowLayoutPanel(); + this.alternatePackageHeader = new System.Windows.Forms.Label(); + this.alternatePackageLinkLabel = new System.Windows.Forms.LinkLabel(); + this.descriptionLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); + this.descriptionHeader = new System.Windows.Forms.Label(); + this.descriptionLabel = new System.Windows.Forms.Label(); this.createdByLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); - this.label1 = new System.Windows.Forms.Label(); + this.createdByHeader = new System.Windows.Forms.Label(); this.createdByLabel = new System.Windows.Forms.Label(); - this.flowLayoutPanel2 = new System.Windows.Forms.FlowLayoutPanel(); - this.label3 = new System.Windows.Forms.Label(); - this.idLinkLabel = new System.Windows.Forms.LinkLabel(); - this.flowLayoutPanel3 = new System.Windows.Forms.FlowLayoutPanel(); - this.label2 = new System.Windows.Forms.Label(); - this.versionLabel = new System.Windows.Forms.Label(); - this.flowLayoutPanel4 = new System.Windows.Forms.FlowLayoutPanel(); - this.label4 = new System.Windows.Forms.Label(); + this.lastPublishedLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); + this.lastPublishedHeader = new System.Windows.Forms.Label(); this.lastPublishedLabel = new System.Windows.Forms.Label(); - this.flowLayoutPanel7 = new System.Windows.Forms.FlowLayoutPanel(); - this.label7 = new System.Windows.Forms.Label(); + this.downloadsLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); + this.downloadsHeader = new System.Windows.Forms.Label(); this.downloadsLabel = new System.Windows.Forms.Label(); - this.licenseLinkLabel = new System.Windows.Forms.LinkLabel(); - this.projectLinkLabel = new System.Windows.Forms.LinkLabel(); + this.detailsLinkLabel = new Bonsai.NuGet.Design.ImageLinkLabel(); + this.projectLinkLabel = new Bonsai.NuGet.Design.ImageLinkLabel(); + this.licenseLinkLabel = new Bonsai.NuGet.Design.ImageLinkLabel(); this.reportAbuseLinkLabel = new System.Windows.Forms.LinkLabel(); - this.flowLayoutPanel6 = new System.Windows.Forms.FlowLayoutPanel(); - this.label5 = new System.Windows.Forms.Label(); - this.descriptionLabel = new System.Windows.Forms.Label(); - this.flowLayoutPanel5 = new System.Windows.Forms.FlowLayoutPanel(); - this.label6 = new System.Windows.Forms.Label(); + this.tagsLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); + this.tagsHeader = new System.Windows.Forms.Label(); this.tagsLabel = new System.Windows.Forms.Label(); - this.flowLayoutPanel8 = new System.Windows.Forms.FlowLayoutPanel(); - this.label8 = new System.Windows.Forms.Label(); - this.flowLayoutPanel9 = new System.Windows.Forms.FlowLayoutPanel(); + this.dependenciesHeaderLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); + this.dependenciesHeader = new System.Windows.Forms.Label(); + this.dependenciesLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); this.dependenciesTextBox = new System.Windows.Forms.TextBox(); this.dependencyWarningLabel = new System.Windows.Forms.Label(); + this.toolTip = new System.Windows.Forms.ToolTip(this.components); this.detailsLayoutPanel.SuspendLayout(); + this.packageIdPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.prefixReservedIcon)).BeginInit(); + this.installedVersionLayoutPanel.SuspendLayout(); + this.versionLayoutPanel.SuspendLayout(); + this.deprecationMetadataPanel.SuspendLayout(); + this.alternatePackagePanel.SuspendLayout(); + this.descriptionLayoutPanel.SuspendLayout(); this.createdByLayoutPanel.SuspendLayout(); - this.flowLayoutPanel2.SuspendLayout(); - this.flowLayoutPanel3.SuspendLayout(); - this.flowLayoutPanel4.SuspendLayout(); - this.flowLayoutPanel7.SuspendLayout(); - this.flowLayoutPanel6.SuspendLayout(); - this.flowLayoutPanel5.SuspendLayout(); - this.flowLayoutPanel8.SuspendLayout(); - this.flowLayoutPanel9.SuspendLayout(); + this.lastPublishedLayoutPanel.SuspendLayout(); + this.downloadsLayoutPanel.SuspendLayout(); + this.tagsLayoutPanel.SuspendLayout(); + this.dependenciesHeaderLayoutPanel.SuspendLayout(); + this.dependenciesLayoutPanel.SuspendLayout(); this.SuspendLayout(); // // detailsLayoutPanel // this.detailsLayoutPanel.AutoScroll = true; + this.detailsLayoutPanel.Controls.Add(this.packageIdPanel); + this.detailsLayoutPanel.Controls.Add(this.installedVersionLayoutPanel); + this.detailsLayoutPanel.Controls.Add(this.versionLayoutPanel); + this.detailsLayoutPanel.Controls.Add(this.deprecationMetadataPanel); + this.detailsLayoutPanel.Controls.Add(this.alternatePackagePanel); + this.detailsLayoutPanel.Controls.Add(this.descriptionLayoutPanel); this.detailsLayoutPanel.Controls.Add(this.createdByLayoutPanel); - this.detailsLayoutPanel.Controls.Add(this.flowLayoutPanel2); - this.detailsLayoutPanel.Controls.Add(this.flowLayoutPanel3); - this.detailsLayoutPanel.Controls.Add(this.flowLayoutPanel4); - this.detailsLayoutPanel.Controls.Add(this.flowLayoutPanel7); - this.detailsLayoutPanel.Controls.Add(this.licenseLinkLabel); + this.detailsLayoutPanel.Controls.Add(this.lastPublishedLayoutPanel); + this.detailsLayoutPanel.Controls.Add(this.downloadsLayoutPanel); + this.detailsLayoutPanel.Controls.Add(this.detailsLinkLabel); this.detailsLayoutPanel.Controls.Add(this.projectLinkLabel); + this.detailsLayoutPanel.Controls.Add(this.licenseLinkLabel); this.detailsLayoutPanel.Controls.Add(this.reportAbuseLinkLabel); - this.detailsLayoutPanel.Controls.Add(this.flowLayoutPanel6); - this.detailsLayoutPanel.Controls.Add(this.flowLayoutPanel5); - this.detailsLayoutPanel.Controls.Add(this.flowLayoutPanel8); - this.detailsLayoutPanel.Controls.Add(this.flowLayoutPanel9); + this.detailsLayoutPanel.Controls.Add(this.tagsLayoutPanel); + this.detailsLayoutPanel.Controls.Add(this.dependenciesHeaderLayoutPanel); + this.detailsLayoutPanel.Controls.Add(this.dependenciesLayoutPanel); this.detailsLayoutPanel.Controls.Add(this.dependencyWarningLabel); this.detailsLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; this.detailsLayoutPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; @@ -94,274 +116,451 @@ private void InitializeComponent() this.detailsLayoutPanel.TabIndex = 0; this.detailsLayoutPanel.WrapContents = false; // + // packageIdPanel + // + this.packageIdPanel.AutoSize = true; + this.packageIdPanel.Controls.Add(this.packageIdLabel); + this.packageIdPanel.Controls.Add(this.prefixReservedIcon); + this.packageIdPanel.Location = new System.Drawing.Point(0, 0); + this.packageIdPanel.Margin = new System.Windows.Forms.Padding(0); + this.packageIdPanel.Name = "packageIdPanel"; + this.packageIdPanel.Size = new System.Drawing.Size(88, 24); + this.packageIdPanel.TabIndex = 0; + // + // packageIdLabel + // + this.packageIdLabel.AutoSize = true; + this.packageIdLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.packageIdLabel.Image = null; + this.packageIdLabel.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.packageIdLabel.ImageIndex = 0; + this.packageIdLabel.Location = new System.Drawing.Point(3, 3); + this.packageIdLabel.Margin = new System.Windows.Forms.Padding(3, 3, 0, 3); + this.packageIdLabel.Name = "packageIdLabel"; + this.packageIdLabel.Size = new System.Drawing.Size(66, 18); + this.packageIdLabel.TabIndex = 0; + this.packageIdLabel.Text = "Package"; + this.packageIdLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // prefixReservedIcon + // + this.prefixReservedIcon.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.prefixReservedIcon.Image = global::Bonsai.NuGet.Design.Properties.Resources.PrefixReservedImage; + this.prefixReservedIcon.Location = new System.Drawing.Point(69, 3); + this.prefixReservedIcon.Margin = new System.Windows.Forms.Padding(0, 3, 3, 3); + this.prefixReservedIcon.Name = "prefixReservedIcon"; + this.prefixReservedIcon.Size = new System.Drawing.Size(16, 18); + this.prefixReservedIcon.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage; + this.prefixReservedIcon.TabIndex = 13; + this.prefixReservedIcon.TabStop = false; + // + // installedVersionLayoutPanel + // + this.installedVersionLayoutPanel.AutoSize = true; + this.installedVersionLayoutPanel.Controls.Add(this.installedHeader); + this.installedVersionLayoutPanel.Controls.Add(this.installedVersionTextBox); + this.installedVersionLayoutPanel.Controls.Add(this.uninstallButton); + this.installedVersionLayoutPanel.Location = new System.Drawing.Point(3, 27); + this.installedVersionLayoutPanel.Margin = new System.Windows.Forms.Padding(3, 3, 3, 0); + this.installedVersionLayoutPanel.Name = "installedVersionLayoutPanel"; + this.installedVersionLayoutPanel.Size = new System.Drawing.Size(171, 49); + this.installedVersionLayoutPanel.TabIndex = 1; + // + // installedHeader + // + this.installedHeader.Anchor = System.Windows.Forms.AnchorStyles.None; + this.installedHeader.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.installedHeader.Location = new System.Drawing.Point(3, 6); + this.installedHeader.Name = "installedHeader"; + this.installedHeader.Size = new System.Drawing.Size(61, 13); + this.installedHeader.TabIndex = 0; + this.installedHeader.Text = "Installed:"; + // + // installedVersionTextBox + // + this.installedVersionTextBox.Cursor = System.Windows.Forms.Cursors.Default; + this.installedVersionTextBox.Location = new System.Drawing.Point(69, 3); + this.installedVersionTextBox.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3); + this.installedVersionTextBox.Name = "installedVersionTextBox"; + this.installedVersionTextBox.ReadOnly = true; + this.installedVersionTextBox.Size = new System.Drawing.Size(100, 20); + this.installedVersionTextBox.TabIndex = 1; + // + // uninstallButton + // + this.uninstallButton.Anchor = System.Windows.Forms.AnchorStyles.None; + this.uninstallButton.Location = new System.Drawing.Point(3, 26); + this.uninstallButton.Margin = new System.Windows.Forms.Padding(3, 0, 3, 0); + this.uninstallButton.Name = "uninstallButton"; + this.uninstallButton.Size = new System.Drawing.Size(100, 23); + this.uninstallButton.TabIndex = 2; + this.uninstallButton.Text = "Uninstall"; + this.uninstallButton.UseVisualStyleBackColor = true; + this.uninstallButton.Click += new System.EventHandler(this.uninstallButton_Click); + // + // versionLayoutPanel + // + this.versionLayoutPanel.AutoSize = true; + this.versionLayoutPanel.Controls.Add(this.versionHeader); + this.versionLayoutPanel.Controls.Add(this.versionComboBox); + this.versionLayoutPanel.Controls.Add(this.operationButton); + this.versionLayoutPanel.Location = new System.Drawing.Point(3, 76); + this.versionLayoutPanel.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3); + this.versionLayoutPanel.Name = "versionLayoutPanel"; + this.versionLayoutPanel.Size = new System.Drawing.Size(173, 50); + this.versionLayoutPanel.TabIndex = 2; + // + // versionHeader + // + this.versionHeader.Anchor = System.Windows.Forms.AnchorStyles.None; + this.versionHeader.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.versionHeader.Location = new System.Drawing.Point(3, 7); + this.versionHeader.Name = "versionHeader"; + this.versionHeader.Size = new System.Drawing.Size(61, 13); + this.versionHeader.TabIndex = 0; + this.versionHeader.Text = "Version:"; + // + // versionComboBox + // + this.versionComboBox.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.versionComboBox.FormattingEnabled = true; + this.versionComboBox.Location = new System.Drawing.Point(70, 3); + this.versionComboBox.Name = "versionComboBox"; + this.versionComboBox.Size = new System.Drawing.Size(100, 21); + this.versionComboBox.TabIndex = 1; + this.versionComboBox.SelectedIndexChanged += new System.EventHandler(this.versionComboBox_SelectedIndexChanged); + this.versionComboBox.TextChanged += new System.EventHandler(this.versionComboBox_TextChanged); + // + // operationButton + // + this.operationButton.Anchor = System.Windows.Forms.AnchorStyles.None; + this.operationButton.Location = new System.Drawing.Point(3, 27); + this.operationButton.Margin = new System.Windows.Forms.Padding(3, 0, 3, 0); + this.operationButton.Name = "operationButton"; + this.operationButton.Size = new System.Drawing.Size(100, 23); + this.operationButton.TabIndex = 2; + this.operationButton.Text = "Operation"; + this.operationButton.UseVisualStyleBackColor = true; + this.operationButton.Click += new System.EventHandler(this.operationButton_Click); + // + // deprecationMetadataPanel + // + this.deprecationMetadataPanel.AutoSize = true; + this.deprecationMetadataPanel.Controls.Add(this.warningImageLabel); + this.deprecationMetadataPanel.Controls.Add(this.deprecationMetadataLabel); + this.deprecationMetadataPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + this.deprecationMetadataPanel.Location = new System.Drawing.Point(3, 132); + this.deprecationMetadataPanel.Name = "deprecationMetadataPanel"; + this.deprecationMetadataPanel.Size = new System.Drawing.Size(96, 36); + this.deprecationMetadataPanel.TabIndex = 3; + // + // warningImageLabel + // + this.warningImageLabel.AutoSize = true; + this.warningImageLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.warningImageLabel.Image = global::Bonsai.NuGet.Design.Properties.Resources.WarningImage; + this.warningImageLabel.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.warningImageLabel.ImageIndex = 0; + this.warningImageLabel.Location = new System.Drawing.Point(3, 3); + this.warningImageLabel.Margin = new System.Windows.Forms.Padding(3); + this.warningImageLabel.Name = "warningImageLabel"; + this.warningImageLabel.Size = new System.Drawing.Size(90, 17); + this.warningImageLabel.TabIndex = 0; + this.warningImageLabel.Text = "Deprecated"; + this.warningImageLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // deprecationMetadataLabel + // + this.deprecationMetadataLabel.AutoSize = true; + this.deprecationMetadataLabel.Location = new System.Drawing.Point(3, 23); + this.deprecationMetadataLabel.Name = "deprecationMetadataLabel"; + this.deprecationMetadataLabel.Size = new System.Drawing.Size(84, 13); + this.deprecationMetadataLabel.TabIndex = 1; + this.deprecationMetadataLabel.Text = "deprecationText"; + // + // alternatePackagePanel + // + this.alternatePackagePanel.AutoSize = true; + this.alternatePackagePanel.Controls.Add(this.alternatePackageHeader); + this.alternatePackagePanel.Controls.Add(this.alternatePackageLinkLabel); + this.alternatePackagePanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + this.alternatePackagePanel.Location = new System.Drawing.Point(3, 174); + this.alternatePackagePanel.Name = "alternatePackagePanel"; + this.alternatePackagePanel.Size = new System.Drawing.Size(117, 38); + this.alternatePackagePanel.TabIndex = 4; + // + // alternatePackageHeader + // + this.alternatePackageHeader.AutoSize = true; + this.alternatePackageHeader.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.alternatePackageHeader.Location = new System.Drawing.Point(3, 3); + this.alternatePackageHeader.Margin = new System.Windows.Forms.Padding(3); + this.alternatePackageHeader.Name = "alternatePackageHeader"; + this.alternatePackageHeader.Size = new System.Drawing.Size(111, 13); + this.alternatePackageHeader.TabIndex = 0; + this.alternatePackageHeader.Text = "Alternate package"; + // + // alternatePackageLinkLabel + // + this.alternatePackageLinkLabel.AutoSize = true; + this.alternatePackageLinkLabel.ForeColor = System.Drawing.Color.Blue; + this.alternatePackageLinkLabel.LinkBehavior = System.Windows.Forms.LinkBehavior.HoverUnderline; + this.alternatePackageLinkLabel.Location = new System.Drawing.Point(3, 22); + this.alternatePackageLinkLabel.Margin = new System.Windows.Forms.Padding(3); + this.alternatePackageLinkLabel.Name = "alternatePackageLinkLabel"; + this.alternatePackageLinkLabel.Size = new System.Drawing.Size(100, 13); + this.alternatePackageLinkLabel.TabIndex = 1; + this.alternatePackageLinkLabel.TabStop = true; + this.alternatePackageLinkLabel.Text = "alternatePackageId"; + this.alternatePackageLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.alternatePackageLinkLabel_LinkClicked); + this.alternatePackageLinkLabel.MouseEnter += new System.EventHandler(this.linkLabel_MouseEnter); + this.alternatePackageLinkLabel.MouseLeave += new System.EventHandler(this.linkLabel_MouseLeave); + // + // descriptionLayoutPanel + // + this.descriptionLayoutPanel.AutoSize = true; + this.descriptionLayoutPanel.Controls.Add(this.descriptionHeader); + this.descriptionLayoutPanel.Controls.Add(this.descriptionLabel); + this.descriptionLayoutPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + this.descriptionLayoutPanel.Location = new System.Drawing.Point(3, 218); + this.descriptionLayoutPanel.Name = "descriptionLayoutPanel"; + this.descriptionLayoutPanel.Size = new System.Drawing.Size(85, 32); + this.descriptionLayoutPanel.TabIndex = 5; + // + // descriptionHeader + // + this.descriptionHeader.AutoSize = true; + this.descriptionHeader.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.descriptionHeader.Location = new System.Drawing.Point(3, 3); + this.descriptionHeader.Margin = new System.Windows.Forms.Padding(3); + this.descriptionHeader.Name = "descriptionHeader"; + this.descriptionHeader.Size = new System.Drawing.Size(71, 13); + this.descriptionHeader.TabIndex = 0; + this.descriptionHeader.Text = "Description"; + // + // descriptionLabel + // + this.descriptionLabel.AutoSize = true; + this.descriptionLabel.Location = new System.Drawing.Point(3, 19); + this.descriptionLabel.Name = "descriptionLabel"; + this.descriptionLabel.Size = new System.Drawing.Size(79, 13); + this.descriptionLabel.TabIndex = 1; + this.descriptionLabel.Text = "descriptionText"; + // // createdByLayoutPanel // this.createdByLayoutPanel.AutoSize = true; - this.createdByLayoutPanel.Controls.Add(this.label1); + this.createdByLayoutPanel.Controls.Add(this.createdByHeader); this.createdByLayoutPanel.Controls.Add(this.createdByLabel); - this.createdByLayoutPanel.Location = new System.Drawing.Point(3, 3); + this.createdByLayoutPanel.Location = new System.Drawing.Point(3, 256); this.createdByLayoutPanel.Name = "createdByLayoutPanel"; - this.createdByLayoutPanel.Size = new System.Drawing.Size(119, 13); - this.createdByLayoutPanel.TabIndex = 0; + this.createdByLayoutPanel.Size = new System.Drawing.Size(160, 13); + this.createdByLayoutPanel.TabIndex = 6; // - // label1 + // createdByHeader // - this.label1.AutoSize = true; - this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label1.Location = new System.Drawing.Point(3, 0); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(72, 13); - this.label1.TabIndex = 0; - this.label1.Text = "Created by:"; + this.createdByHeader.AutoSize = true; + this.createdByHeader.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.createdByHeader.Location = new System.Drawing.Point(3, 0); + this.createdByHeader.Name = "createdByHeader"; + this.createdByHeader.Size = new System.Drawing.Size(72, 13); + this.createdByHeader.TabIndex = 0; + this.createdByHeader.Text = "Created by:"; // // createdByLabel // this.createdByLabel.AutoSize = true; this.createdByLabel.Location = new System.Drawing.Point(81, 0); this.createdByLabel.Name = "createdByLabel"; - this.createdByLabel.Size = new System.Drawing.Size(35, 13); + this.createdByLabel.Size = new System.Drawing.Size(76, 13); this.createdByLabel.TabIndex = 1; - this.createdByLabel.Text = "label2"; - // - // flowLayoutPanel2 - // - this.flowLayoutPanel2.AutoSize = true; - this.flowLayoutPanel2.Controls.Add(this.label3); - this.flowLayoutPanel2.Controls.Add(this.idLinkLabel); - this.flowLayoutPanel2.Location = new System.Drawing.Point(3, 22); - this.flowLayoutPanel2.Name = "flowLayoutPanel2"; - this.flowLayoutPanel2.Size = new System.Drawing.Size(89, 13); - this.flowLayoutPanel2.TabIndex = 1; - // - // label3 - // - this.label3.AutoSize = true; - this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label3.Location = new System.Drawing.Point(3, 0); - this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(22, 13); - this.label3.TabIndex = 0; - this.label3.Text = "Id:"; - // - // idLinkLabel - // - this.idLinkLabel.AutoSize = true; - this.idLinkLabel.LinkBehavior = System.Windows.Forms.LinkBehavior.HoverUnderline; - this.idLinkLabel.Location = new System.Drawing.Point(31, 0); - this.idLinkLabel.Name = "idLinkLabel"; - this.idLinkLabel.Size = new System.Drawing.Size(55, 13); - this.idLinkLabel.TabIndex = 1; - this.idLinkLabel.TabStop = true; - this.idLinkLabel.Text = "linkLabel1"; - this.idLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel_LinkClicked); - // - // flowLayoutPanel3 - // - this.flowLayoutPanel3.AutoSize = true; - this.flowLayoutPanel3.Controls.Add(this.label2); - this.flowLayoutPanel3.Controls.Add(this.versionLabel); - this.flowLayoutPanel3.Location = new System.Drawing.Point(3, 41); - this.flowLayoutPanel3.Name = "flowLayoutPanel3"; - this.flowLayoutPanel3.Size = new System.Drawing.Size(100, 13); - this.flowLayoutPanel3.TabIndex = 2; - // - // label2 - // - this.label2.AutoSize = true; - this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label2.Location = new System.Drawing.Point(3, 0); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(53, 13); - this.label2.TabIndex = 0; - this.label2.Text = "Version:"; - // - // versionLabel - // - this.versionLabel.AutoSize = true; - this.versionLabel.Location = new System.Drawing.Point(62, 0); - this.versionLabel.Name = "versionLabel"; - this.versionLabel.Size = new System.Drawing.Size(35, 13); - this.versionLabel.TabIndex = 1; - this.versionLabel.Text = "label4"; - // - // flowLayoutPanel4 - // - this.flowLayoutPanel4.AutoSize = true; - this.flowLayoutPanel4.Controls.Add(this.label4); - this.flowLayoutPanel4.Controls.Add(this.lastPublishedLabel); - this.flowLayoutPanel4.Location = new System.Drawing.Point(3, 60); - this.flowLayoutPanel4.Name = "flowLayoutPanel4"; - this.flowLayoutPanel4.Size = new System.Drawing.Size(141, 13); - this.flowLayoutPanel4.TabIndex = 3; - // - // label4 - // - this.label4.AutoSize = true; - this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label4.Location = new System.Drawing.Point(3, 0); - this.label4.Name = "label4"; - this.label4.Size = new System.Drawing.Size(94, 13); - this.label4.TabIndex = 0; - this.label4.Text = "Last Published:"; + this.createdByLabel.Text = "createdByText"; + // + // lastPublishedLayoutPanel + // + this.lastPublishedLayoutPanel.AutoSize = true; + this.lastPublishedLayoutPanel.Controls.Add(this.lastPublishedHeader); + this.lastPublishedLayoutPanel.Controls.Add(this.lastPublishedLabel); + this.lastPublishedLayoutPanel.Location = new System.Drawing.Point(3, 275); + this.lastPublishedLayoutPanel.Name = "lastPublishedLayoutPanel"; + this.lastPublishedLayoutPanel.Size = new System.Drawing.Size(196, 13); + this.lastPublishedLayoutPanel.TabIndex = 7; + // + // lastPublishedHeader + // + this.lastPublishedHeader.AutoSize = true; + this.lastPublishedHeader.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lastPublishedHeader.Location = new System.Drawing.Point(3, 0); + this.lastPublishedHeader.Name = "lastPublishedHeader"; + this.lastPublishedHeader.Size = new System.Drawing.Size(94, 13); + this.lastPublishedHeader.TabIndex = 0; + this.lastPublishedHeader.Text = "Last Published:"; // // lastPublishedLabel // this.lastPublishedLabel.AutoSize = true; this.lastPublishedLabel.Location = new System.Drawing.Point(103, 0); this.lastPublishedLabel.Name = "lastPublishedLabel"; - this.lastPublishedLabel.Size = new System.Drawing.Size(35, 13); + this.lastPublishedLabel.Size = new System.Drawing.Size(90, 13); this.lastPublishedLabel.TabIndex = 1; - this.lastPublishedLabel.Text = "label5"; + this.lastPublishedLabel.Text = "lastPublishedText"; // - // flowLayoutPanel7 + // downloadsLayoutPanel // - this.flowLayoutPanel7.AutoSize = true; - this.flowLayoutPanel7.Controls.Add(this.label7); - this.flowLayoutPanel7.Controls.Add(this.downloadsLabel); - this.flowLayoutPanel7.Location = new System.Drawing.Point(3, 79); - this.flowLayoutPanel7.Name = "flowLayoutPanel7"; - this.flowLayoutPanel7.Size = new System.Drawing.Size(120, 13); - this.flowLayoutPanel7.TabIndex = 11; + this.downloadsLayoutPanel.AutoSize = true; + this.downloadsLayoutPanel.Controls.Add(this.downloadsHeader); + this.downloadsLayoutPanel.Controls.Add(this.downloadsLabel); + this.downloadsLayoutPanel.Location = new System.Drawing.Point(3, 294); + this.downloadsLayoutPanel.Name = "downloadsLayoutPanel"; + this.downloadsLayoutPanel.Size = new System.Drawing.Size(166, 13); + this.downloadsLayoutPanel.TabIndex = 8; // - // label7 + // downloadsHeader // - this.label7.AutoSize = true; - this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label7.Location = new System.Drawing.Point(3, 0); - this.label7.Name = "label7"; - this.label7.Size = new System.Drawing.Size(73, 13); - this.label7.TabIndex = 0; - this.label7.Text = "Downloads:"; + this.downloadsHeader.AutoSize = true; + this.downloadsHeader.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.downloadsHeader.Location = new System.Drawing.Point(3, 0); + this.downloadsHeader.Name = "downloadsHeader"; + this.downloadsHeader.Size = new System.Drawing.Size(73, 13); + this.downloadsHeader.TabIndex = 0; + this.downloadsHeader.Text = "Downloads:"; // // downloadsLabel // this.downloadsLabel.AutoSize = true; this.downloadsLabel.Location = new System.Drawing.Point(82, 0); this.downloadsLabel.Name = "downloadsLabel"; - this.downloadsLabel.Size = new System.Drawing.Size(35, 13); + this.downloadsLabel.Size = new System.Drawing.Size(81, 13); this.downloadsLabel.TabIndex = 1; - this.downloadsLabel.Text = "label8"; - // - // licenseLinkLabel - // - this.licenseLinkLabel.AutoSize = true; - this.licenseLinkLabel.LinkBehavior = System.Windows.Forms.LinkBehavior.HoverUnderline; - this.licenseLinkLabel.Location = new System.Drawing.Point(3, 95); - this.licenseLinkLabel.Name = "licenseLinkLabel"; - this.licenseLinkLabel.Size = new System.Drawing.Size(102, 13); - this.licenseLinkLabel.TabIndex = 4; - this.licenseLinkLabel.TabStop = true; - this.licenseLinkLabel.Text = "View License Terms"; - this.licenseLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel_LinkClicked); + this.downloadsLabel.Text = "downloadCount"; + // + // detailsLinkLabel + // + this.detailsLinkLabel.AutoSize = true; + this.detailsLinkLabel.ForeColor = System.Drawing.Color.Blue; + this.detailsLinkLabel.Image = global::Bonsai.NuGet.Design.Properties.Resources.PackageImage; + this.detailsLinkLabel.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.detailsLinkLabel.LinkBehavior = System.Windows.Forms.LinkBehavior.HoverUnderline; + this.detailsLinkLabel.Location = new System.Drawing.Point(6, 310); + this.detailsLinkLabel.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0); + this.detailsLinkLabel.Name = "detailsLinkLabel"; + this.detailsLinkLabel.Size = new System.Drawing.Size(102, 17); + this.detailsLinkLabel.TabIndex = 9; + this.detailsLinkLabel.TabStop = true; + this.detailsLinkLabel.Text = "Package Details"; + this.detailsLinkLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // projectLinkLabel // this.projectLinkLabel.AutoSize = true; + this.projectLinkLabel.ForeColor = System.Drawing.Color.Blue; + this.projectLinkLabel.Image = global::Bonsai.NuGet.Design.Properties.Resources.WebImage; + this.projectLinkLabel.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; this.projectLinkLabel.LinkBehavior = System.Windows.Forms.LinkBehavior.HoverUnderline; - this.projectLinkLabel.Location = new System.Drawing.Point(3, 108); + this.projectLinkLabel.Location = new System.Drawing.Point(6, 327); + this.projectLinkLabel.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0); this.projectLinkLabel.Name = "projectLinkLabel"; - this.projectLinkLabel.Size = new System.Drawing.Size(95, 13); - this.projectLinkLabel.TabIndex = 5; + this.projectLinkLabel.Size = new System.Drawing.Size(99, 17); + this.projectLinkLabel.TabIndex = 10; this.projectLinkLabel.TabStop = true; - this.projectLinkLabel.Text = "Project Information"; + this.projectLinkLabel.Text = "Project Website"; + this.projectLinkLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight; this.projectLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel_LinkClicked); + this.projectLinkLabel.MouseEnter += new System.EventHandler(this.linkLabel_MouseEnter); + this.projectLinkLabel.MouseLeave += new System.EventHandler(this.linkLabel_MouseLeave); + // + // licenseLinkLabel + // + this.licenseLinkLabel.AutoSize = true; + this.licenseLinkLabel.ForeColor = System.Drawing.Color.Blue; + this.licenseLinkLabel.Image = global::Bonsai.NuGet.Design.Properties.Resources.LicenseImage; + this.licenseLinkLabel.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.licenseLinkLabel.LinkBehavior = System.Windows.Forms.LinkBehavior.HoverUnderline; + this.licenseLinkLabel.Location = new System.Drawing.Point(6, 344); + this.licenseLinkLabel.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0); + this.licenseLinkLabel.Name = "licenseLinkLabel"; + this.licenseLinkLabel.Size = new System.Drawing.Size(82, 17); + this.licenseLinkLabel.TabIndex = 11; + this.licenseLinkLabel.TabStop = true; + this.licenseLinkLabel.Text = "License Info"; + this.licenseLinkLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + this.licenseLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.licenseLinkLabel_LinkClicked); + this.licenseLinkLabel.MouseEnter += new System.EventHandler(this.linkLabel_MouseEnter); + this.licenseLinkLabel.MouseLeave += new System.EventHandler(this.linkLabel_MouseLeave); // // reportAbuseLinkLabel // this.reportAbuseLinkLabel.AutoSize = true; + this.reportAbuseLinkLabel.ForeColor = System.Drawing.Color.Blue; this.reportAbuseLinkLabel.LinkBehavior = System.Windows.Forms.LinkBehavior.HoverUnderline; - this.reportAbuseLinkLabel.Location = new System.Drawing.Point(3, 121); + this.reportAbuseLinkLabel.Location = new System.Drawing.Point(6, 364); + this.reportAbuseLinkLabel.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); this.reportAbuseLinkLabel.Name = "reportAbuseLinkLabel"; this.reportAbuseLinkLabel.Size = new System.Drawing.Size(72, 13); - this.reportAbuseLinkLabel.TabIndex = 6; + this.reportAbuseLinkLabel.TabIndex = 12; this.reportAbuseLinkLabel.TabStop = true; this.reportAbuseLinkLabel.Text = "Report Abuse"; this.reportAbuseLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel_LinkClicked); + this.reportAbuseLinkLabel.MouseEnter += new System.EventHandler(this.linkLabel_MouseEnter); + this.reportAbuseLinkLabel.MouseLeave += new System.EventHandler(this.linkLabel_MouseLeave); // - // flowLayoutPanel6 - // - this.flowLayoutPanel6.AutoSize = true; - this.flowLayoutPanel6.Controls.Add(this.label5); - this.flowLayoutPanel6.Controls.Add(this.descriptionLabel); - this.flowLayoutPanel6.Location = new System.Drawing.Point(3, 137); - this.flowLayoutPanel6.Name = "flowLayoutPanel6"; - this.flowLayoutPanel6.Size = new System.Drawing.Size(87, 13); - this.flowLayoutPanel6.TabIndex = 10; - // - // label5 - // - this.label5.AutoSize = true; - this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label5.Location = new System.Drawing.Point(3, 0); - this.label5.Name = "label5"; - this.label5.Size = new System.Drawing.Size(75, 13); - this.label5.TabIndex = 7; - this.label5.Text = "Description:"; - // - // descriptionLabel - // - this.descriptionLabel.AutoSize = true; - this.descriptionLabel.Location = new System.Drawing.Point(84, 0); - this.descriptionLabel.Name = "descriptionLabel"; - this.descriptionLabel.Size = new System.Drawing.Size(0, 13); - this.descriptionLabel.TabIndex = 8; - // - // flowLayoutPanel5 + // tagsLayoutPanel // - this.flowLayoutPanel5.AutoSize = true; - this.flowLayoutPanel5.Controls.Add(this.label6); - this.flowLayoutPanel5.Controls.Add(this.tagsLabel); - this.flowLayoutPanel5.Location = new System.Drawing.Point(3, 156); - this.flowLayoutPanel5.Name = "flowLayoutPanel5"; - this.flowLayoutPanel5.Size = new System.Drawing.Size(86, 13); - this.flowLayoutPanel5.TabIndex = 9; + this.tagsLayoutPanel.AutoSize = true; + this.tagsLayoutPanel.Controls.Add(this.tagsHeader); + this.tagsLayoutPanel.Controls.Add(this.tagsLabel); + this.tagsLayoutPanel.Location = new System.Drawing.Point(3, 383); + this.tagsLayoutPanel.Name = "tagsLayoutPanel"; + this.tagsLayoutPanel.Size = new System.Drawing.Size(99, 13); + this.tagsLayoutPanel.TabIndex = 13; // - // label6 + // tagsHeader // - this.label6.AutoSize = true; - this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label6.Location = new System.Drawing.Point(3, 0); - this.label6.Name = "label6"; - this.label6.Size = new System.Drawing.Size(39, 13); - this.label6.TabIndex = 0; - this.label6.Text = "Tags:"; + this.tagsHeader.AutoSize = true; + this.tagsHeader.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tagsHeader.Location = new System.Drawing.Point(3, 0); + this.tagsHeader.Name = "tagsHeader"; + this.tagsHeader.Size = new System.Drawing.Size(39, 13); + this.tagsHeader.TabIndex = 0; + this.tagsHeader.Text = "Tags:"; // // tagsLabel // this.tagsLabel.AutoSize = true; this.tagsLabel.Location = new System.Drawing.Point(48, 0); this.tagsLabel.Name = "tagsLabel"; - this.tagsLabel.Size = new System.Drawing.Size(35, 13); + this.tagsLabel.Size = new System.Drawing.Size(48, 13); this.tagsLabel.TabIndex = 1; - this.tagsLabel.Text = "label7"; + this.tagsLabel.Text = "tagsText"; // - // flowLayoutPanel8 + // dependenciesHeaderLayoutPanel // - this.flowLayoutPanel8.AutoSize = true; - this.flowLayoutPanel8.Controls.Add(this.label8); - this.flowLayoutPanel8.Location = new System.Drawing.Point(3, 175); - this.flowLayoutPanel8.Name = "flowLayoutPanel8"; - this.flowLayoutPanel8.Size = new System.Drawing.Size(98, 13); - this.flowLayoutPanel8.TabIndex = 12; + this.dependenciesHeaderLayoutPanel.AutoSize = true; + this.dependenciesHeaderLayoutPanel.Controls.Add(this.dependenciesHeader); + this.dependenciesHeaderLayoutPanel.Location = new System.Drawing.Point(3, 402); + this.dependenciesHeaderLayoutPanel.Name = "dependenciesHeaderLayoutPanel"; + this.dependenciesHeaderLayoutPanel.Size = new System.Drawing.Size(94, 13); + this.dependenciesHeaderLayoutPanel.TabIndex = 14; // - // label8 + // dependenciesHeader // - this.label8.AutoSize = true; - this.label8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label8.Location = new System.Drawing.Point(3, 0); - this.label8.Name = "label8"; - this.label8.Size = new System.Drawing.Size(92, 13); - this.label8.TabIndex = 0; - this.label8.Text = "Dependencies:"; + this.dependenciesHeader.AutoSize = true; + this.dependenciesHeader.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.dependenciesHeader.Location = new System.Drawing.Point(3, 0); + this.dependenciesHeader.Name = "dependenciesHeader"; + this.dependenciesHeader.Size = new System.Drawing.Size(88, 13); + this.dependenciesHeader.TabIndex = 0; + this.dependenciesHeader.Text = "Dependencies"; // - // flowLayoutPanel9 + // dependenciesLayoutPanel // - this.flowLayoutPanel9.AutoSize = true; - this.flowLayoutPanel9.Controls.Add(this.dependenciesTextBox); - this.flowLayoutPanel9.Location = new System.Drawing.Point(3, 194); - this.flowLayoutPanel9.Name = "flowLayoutPanel9"; - this.flowLayoutPanel9.Size = new System.Drawing.Size(185, 26); - this.flowLayoutPanel9.TabIndex = 13; + this.dependenciesLayoutPanel.AutoSize = true; + this.dependenciesLayoutPanel.Controls.Add(this.dependenciesTextBox); + this.dependenciesLayoutPanel.Location = new System.Drawing.Point(3, 421); + this.dependenciesLayoutPanel.Name = "dependenciesLayoutPanel"; + this.dependenciesLayoutPanel.Size = new System.Drawing.Size(188, 26); + this.dependenciesLayoutPanel.TabIndex = 15; // // dependenciesTextBox // @@ -369,22 +568,24 @@ private void InitializeComponent() | System.Windows.Forms.AnchorStyles.Right))); this.dependenciesTextBox.BackColor = System.Drawing.SystemColors.Control; this.dependenciesTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None; - this.dependenciesTextBox.Location = new System.Drawing.Point(3, 3); + this.dependenciesTextBox.Location = new System.Drawing.Point(6, 3); + this.dependenciesTextBox.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); this.dependenciesTextBox.MinimumSize = new System.Drawing.Size(179, 20); this.dependenciesTextBox.Multiline = true; this.dependenciesTextBox.Name = "dependenciesTextBox"; this.dependenciesTextBox.Size = new System.Drawing.Size(179, 20); this.dependenciesTextBox.TabIndex = 0; + this.dependenciesTextBox.TabStop = false; this.dependenciesTextBox.TextChanged += new System.EventHandler(this.dependenciesTextBox_TextChanged); // // dependencyWarningLabel // this.dependencyWarningLabel.AutoSize = true; this.dependencyWarningLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.dependencyWarningLabel.Location = new System.Drawing.Point(3, 223); + this.dependencyWarningLabel.Location = new System.Drawing.Point(3, 450); this.dependencyWarningLabel.Name = "dependencyWarningLabel"; this.dependencyWarningLabel.Size = new System.Drawing.Size(0, 13); - this.dependencyWarningLabel.TabIndex = 14; + this.dependencyWarningLabel.TabIndex = 16; // // PackageDetails // @@ -395,24 +596,30 @@ private void InitializeComponent() this.Size = new System.Drawing.Size(240, 450); this.detailsLayoutPanel.ResumeLayout(false); this.detailsLayoutPanel.PerformLayout(); + this.packageIdPanel.ResumeLayout(false); + this.packageIdPanel.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.prefixReservedIcon)).EndInit(); + this.installedVersionLayoutPanel.ResumeLayout(false); + this.installedVersionLayoutPanel.PerformLayout(); + this.versionLayoutPanel.ResumeLayout(false); + this.deprecationMetadataPanel.ResumeLayout(false); + this.deprecationMetadataPanel.PerformLayout(); + this.alternatePackagePanel.ResumeLayout(false); + this.alternatePackagePanel.PerformLayout(); + this.descriptionLayoutPanel.ResumeLayout(false); + this.descriptionLayoutPanel.PerformLayout(); this.createdByLayoutPanel.ResumeLayout(false); this.createdByLayoutPanel.PerformLayout(); - this.flowLayoutPanel2.ResumeLayout(false); - this.flowLayoutPanel2.PerformLayout(); - this.flowLayoutPanel3.ResumeLayout(false); - this.flowLayoutPanel3.PerformLayout(); - this.flowLayoutPanel4.ResumeLayout(false); - this.flowLayoutPanel4.PerformLayout(); - this.flowLayoutPanel7.ResumeLayout(false); - this.flowLayoutPanel7.PerformLayout(); - this.flowLayoutPanel6.ResumeLayout(false); - this.flowLayoutPanel6.PerformLayout(); - this.flowLayoutPanel5.ResumeLayout(false); - this.flowLayoutPanel5.PerformLayout(); - this.flowLayoutPanel8.ResumeLayout(false); - this.flowLayoutPanel8.PerformLayout(); - this.flowLayoutPanel9.ResumeLayout(false); - this.flowLayoutPanel9.PerformLayout(); + this.lastPublishedLayoutPanel.ResumeLayout(false); + this.lastPublishedLayoutPanel.PerformLayout(); + this.downloadsLayoutPanel.ResumeLayout(false); + this.downloadsLayoutPanel.PerformLayout(); + this.tagsLayoutPanel.ResumeLayout(false); + this.tagsLayoutPanel.PerformLayout(); + this.dependenciesHeaderLayoutPanel.ResumeLayout(false); + this.dependenciesHeaderLayoutPanel.PerformLayout(); + this.dependenciesLayoutPanel.ResumeLayout(false); + this.dependenciesLayoutPanel.PerformLayout(); this.ResumeLayout(false); } @@ -421,33 +628,46 @@ private void InitializeComponent() private System.Windows.Forms.FlowLayoutPanel detailsLayoutPanel; private System.Windows.Forms.FlowLayoutPanel createdByLayoutPanel; - private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label createdByHeader; private System.Windows.Forms.Label createdByLabel; - private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel2; - private System.Windows.Forms.Label label3; - private System.Windows.Forms.LinkLabel idLinkLabel; - private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel3; - private System.Windows.Forms.Label label2; - private System.Windows.Forms.Label versionLabel; - private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel4; - private System.Windows.Forms.Label label4; + private System.Windows.Forms.FlowLayoutPanel lastPublishedLayoutPanel; + private System.Windows.Forms.Label lastPublishedHeader; private System.Windows.Forms.Label lastPublishedLabel; - private System.Windows.Forms.LinkLabel licenseLinkLabel; - private System.Windows.Forms.LinkLabel projectLinkLabel; private System.Windows.Forms.LinkLabel reportAbuseLinkLabel; - private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label descriptionHeader; private System.Windows.Forms.Label descriptionLabel; - private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel5; - private System.Windows.Forms.Label label6; + private System.Windows.Forms.FlowLayoutPanel tagsLayoutPanel; + private System.Windows.Forms.Label tagsHeader; private System.Windows.Forms.Label tagsLabel; - private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel6; - private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel7; - private System.Windows.Forms.Label label7; + private System.Windows.Forms.FlowLayoutPanel downloadsLayoutPanel; + private System.Windows.Forms.Label downloadsHeader; private System.Windows.Forms.Label downloadsLabel; - private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel8; - private System.Windows.Forms.Label label8; - private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel9; + private System.Windows.Forms.FlowLayoutPanel dependenciesHeaderLayoutPanel; + private System.Windows.Forms.Label dependenciesHeader; + private System.Windows.Forms.FlowLayoutPanel dependenciesLayoutPanel; private System.Windows.Forms.TextBox dependenciesTextBox; private System.Windows.Forms.Label dependencyWarningLabel; + private System.Windows.Forms.Label deprecationMetadataLabel; + private ImageLabel warningImageLabel; + private ImageLinkLabel projectLinkLabel; + private ImageLinkLabel licenseLinkLabel; + private ImageLabel packageIdLabel; + private ImageLinkLabel detailsLinkLabel; + private System.Windows.Forms.FlowLayoutPanel versionLayoutPanel; + private System.Windows.Forms.Label versionHeader; + private System.Windows.Forms.Button operationButton; + private System.Windows.Forms.ComboBox versionComboBox; + private System.Windows.Forms.FlowLayoutPanel installedVersionLayoutPanel; + private System.Windows.Forms.Label installedHeader; + private System.Windows.Forms.Button uninstallButton; + private System.Windows.Forms.TextBox installedVersionTextBox; + private System.Windows.Forms.FlowLayoutPanel deprecationMetadataPanel; + private System.Windows.Forms.FlowLayoutPanel descriptionLayoutPanel; + private System.Windows.Forms.FlowLayoutPanel alternatePackagePanel; + private System.Windows.Forms.Label alternatePackageHeader; + private System.Windows.Forms.LinkLabel alternatePackageLinkLabel; + private System.Windows.Forms.FlowLayoutPanel packageIdPanel; + private System.Windows.Forms.PictureBox prefixReservedIcon; + private System.Windows.Forms.ToolTip toolTip; } } diff --git a/Bonsai.NuGet.Design/PackageDetails.cs b/Bonsai.NuGet.Design/PackageDetails.cs index 0c6f7a1e7..27ecd756a 100644 --- a/Bonsai.NuGet.Design/PackageDetails.cs +++ b/Bonsai.NuGet.Design/PackageDetails.cs @@ -5,50 +5,158 @@ using System.Diagnostics; using Bonsai.NuGet.Design.Properties; using NuGet.Protocol.Core.Types; -using NuGet.Packaging; -using System.IO; using NuGet.Frameworks; using NuGet.Packaging.Core; +using System.ComponentModel; +using NuGet.Versioning; +using System.Drawing; namespace Bonsai.NuGet.Design { - public partial class PackageDetails : UserControl + internal partial class PackageDetails : UserControl { + PackageViewItem selectedItem; const int TextHeightMargin = 7; - static readonly Uri NugetPackageRepository = new Uri("https://packages.nuget.org/packages/"); + static readonly object OperationClickEvent = new(); + static readonly object PackageLinkClickedEvent = new(); public PackageDetails() { InitializeComponent(); ProjectFramework = NuGetFramework.AnyFramework; + versionComboBox.DisplayMember = nameof(VersionInfo.Version); + toolTip.SetToolTip(prefixReservedIcon, Resources.PackagePrefixReservedToolTip); SetPackage(null); } + public PackageOperationType Operation { get; set; } + + [Category("Action")] + public event PackageViewEventHandler OperationClick + { + add { Events.AddHandler(OperationClickEvent, value); } + remove { Events.RemoveHandler(OperationClickEvent, value); } + } + + [Category("Action")] + public event PackageSearchEventHandler PackageLinkClicked + { + add { Events.AddHandler(PackageLinkClickedEvent, value); } + remove { Events.RemoveHandler(PackageLinkClickedEvent, value); } + } + public NuGetFramework ProjectFramework { get; set; } - public PackagePathResolver PathResolver { get; set; } + private void OnOperationClick(PackageViewEventArgs e) + { + (Events[OperationClickEvent] as PackageViewEventHandler)?.Invoke(this, e); + } - public void SetPackage(IPackageSearchMetadata package) + private void OnPackageLinkClicked(PackageSearchEventArgs e) + { + (Events[PackageLinkClickedEvent] as PackageSearchEventHandler)?.Invoke(this, e); + } + + protected override void ScaleControl(SizeF factor, BoundsSpecified specified) + { + if (factor.Height > 1) + { + var image = Resources.PrefixReservedImage; + var targetSize = new Size( + (int)(image.Width * factor.Height), + (int)(image.Height * factor.Height)); + prefixReservedIcon.Image = image.Resize(targetSize); + } + base.ScaleControl(factor, specified); + } + + public void SetPackage(PackageViewItem item) { SuspendLayout(); - detailsLayoutPanel.Visible = package != null; - if (package == null) return; + selectedItem = item; + detailsLayoutPanel.Visible = item != null; + if (item == null) + { + packageIdLabel.ImageList = null; + packageIdLabel.ImageIndex = 0; + packageIdLabel.Text = string.Empty; + ResumeLayout(); + return; + } + + var package = item.SelectedPackage; + packageIdLabel.ImageList = item.ImageList; + packageIdLabel.ImageIndex = item.ImageIndex; + packageIdLabel.Text = package.Identity.Id; + prefixReservedIcon.Visible = package.PrefixReserved; + + installedVersionLayoutPanel.Visible = + (Operation == PackageOperationType.Install || + Operation == PackageOperationType.Update) && + selectedItem.LocalPackage != null; + if (installedVersionLayoutPanel.Visible) + { + installedVersionTextBox.Text = selectedItem.LocalPackage.Identity.Version.ToString(); + } + + var operation = Operation == PackageOperationType.Install && selectedItem.LocalPackage != null + ? PackageOperationType.Update + : Operation; + operationButton.Text = operation.ToString(); + + versionComboBox.Items.Clear(); + foreach (var version in item.PackageVersions + .OrderByDescending(v => v.Version, VersionComparer.VersionRelease)) + { + versionComboBox.Items.Add(version); + if (version.Version == package.Identity.Version) + { + version.PackageSearchMetadata = package; + versionComboBox.SelectedItem = version; + } + } + + var selectedVersion = (VersionInfo)versionComboBox.SelectedItem; + SetPackageVersion(selectedVersion); + ResumeLayout(); + } + void SetPackageVersion(VersionInfo versionInfo) + { + var package = versionInfo.PackageSearchMetadata; createdByLabel.Text = string.Join(CultureInfo.CurrentCulture.TextInfo.ListSeparator, package.Authors); - idLinkLabel.Text = package.Identity.Id; - var packageUri = package.PackageDetailsUrl ?? new Uri(NugetPackageRepository, package.Identity.Id + "/" + package.Identity.Version.ToString()); - SetLinkLabelUri(idLinkLabel, packageUri, false); - versionLabel.Text = string.Format( - "{0}{1}", - package.Identity.Version.ToString(), - package.Identity.Version.IsPrerelease ? Resources.PrereleaseLabel : string.Empty); + SetLinkLabelUri(detailsLinkLabel, package.PackageDetailsUrl, true); lastPublishedLabel.Text = package.Published.HasValue ? package.Published.Value.Date.ToShortDateString() : Resources.UnpublishedLabel; - downloadsLabel.Text = package.DownloadCount.ToString(); - SetLinkLabelLicense(licenseLinkLabel, package, true); + downloadsLabel.Text = versionInfo.DownloadCount.ToString(); + LicenseHelper.SetLicenseLinkLabel(licenseLinkLabel, package, selectedItem.SourceRepository); SetLinkLabelUri(projectLinkLabel, package.ProjectUrl, true); SetLinkLabelUri(reportAbuseLinkLabel, package.ReportAbuseUrl, false); descriptionLabel.Text = package.Description; tagsLabel.Text = package.Tags; + + var deprecationMetadata = package.GetDeprecationMetadataAsync().Result; + if (deprecationMetadata != null) + { + deprecationMetadataPanel.Visible = true; + deprecationMetadataLabel.Text = string.IsNullOrEmpty(deprecationMetadata.Message) + ? Resources.PackageDeprecationDefaultMessage + : deprecationMetadata.Message; + + alternatePackagePanel.Visible = deprecationMetadata.AlternatePackage != null; + if (alternatePackagePanel.Visible) + { + var alternatePackageId = deprecationMetadata.AlternatePackage.PackageId; + alternatePackageLinkLabel.Text = alternatePackageId; + alternatePackageLinkLabel.Links[0].LinkData = $"packageid:{alternatePackageId}"; + } + } + else + { + alternatePackagePanel.Visible = false; + deprecationMetadataPanel.Visible = false; + deprecationMetadataLabel.Text = string.Empty; + } + var nearestDependencyGroup = package.DependencySets.GetNearest(ProjectFramework); dependenciesTextBox.Lines = (from dependency in ((nearestDependencyGroup?.Packages) ?? Enumerable.Empty()) select dependency.ToString()).ToArray(); @@ -62,25 +170,6 @@ public void SetPackage(IPackageSearchMetadata package) dependenciesTextBox.Visible = false; dependencyWarningLabel.Text = Resources.NoDependenciesLabel; } - ResumeLayout(); - } - - void SetLinkLabelLicense(LinkLabel linkLabel, IPackageSearchMetadata package, bool hideEmptyLink) - { - var license = package.LicenseMetadata; - if (license != null && PathResolver != null) - { - switch (license.Type) - { - case LicenseType.File: - var licenseUri = new Uri(Path.Combine(PathResolver.GetInstallPath(package.Identity), license.License)); - SetLinkLabelUri(linkLabel, licenseUri, hideEmptyLink); - break; - case LicenseType.Expression: SetLinkLabelUri(linkLabel, license.LicenseUrl, hideEmptyLink); break; - default: break; - } - } - else SetLinkLabelUri(linkLabel, package.LicenseUrl, hideEmptyLink); } static void SetLinkLabelUri(LinkLabel linkLabel, Uri uri, bool hideEmptyLink) @@ -90,20 +179,80 @@ static void SetLinkLabelUri(LinkLabel linkLabel, Uri uri, bool hideEmptyLink) linkLabel.Visible = !hideEmptyLink || linkLabel.Links[0].LinkData != null; } + private async void licenseLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + await LicenseHelper.ShowLicenseAsync(e.Link, this); + } + private void linkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { - var linkUri = (Uri)e.Link.LinkData; - if (linkUri != null) + if (e.Link.LinkData is Uri linkUri) { Process.Start(linkUri.AbsoluteUri); } } + private void linkLabel_MouseEnter(object sender, EventArgs e) + { + var linkLabel = (LinkLabel)sender; + linkLabel.LinkColor = ControlPaint.Light(linkLabel.ForeColor); + } + + private void linkLabel_MouseLeave(object sender, EventArgs e) + { + var linkLabel = (LinkLabel)sender; + linkLabel.LinkColor = linkLabel.ForeColor; + } + private void dependenciesTextBox_TextChanged(object sender, EventArgs e) { var textSize = TextRenderer.MeasureText(dependenciesTextBox.Text, dependenciesTextBox.Font); textSize.Height += TextHeightMargin; dependenciesTextBox.Size = textSize; } + + private void operationButton_Click(object sender, EventArgs e) + { + var selectedVersion = (VersionInfo)versionComboBox.SelectedItem; + if (selectedVersion != null) + { + OnOperationClick(new PackageViewEventArgs(selectedVersion.PackageSearchMetadata, Operation)); + } + } + + private void uninstallButton_Click(object sender, EventArgs e) + { + var metadataBuilder = PackageSearchMetadataBuilder.FromIdentity(selectedItem.LocalPackage.Identity); + OnOperationClick(new PackageViewEventArgs(metadataBuilder.Build(), PackageOperationType.Uninstall)); + } + + private void alternatePackageLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + var searchTerm = (string)e.Link.LinkData; + OnPackageLinkClicked(new PackageSearchEventArgs(searchTerm)); + } + + private async void versionComboBox_SelectedIndexChanged(object sender, EventArgs e) + { + var currentPackage = selectedItem; + var selectedVersion = (VersionInfo)versionComboBox.SelectedItem; + selectedVersion.PackageSearchMetadata ??= await selectedItem.GetVersionMetadataAsync(selectedVersion); + + // cancel the update if selected package changes before version metadata is extracted + if (currentPackage != selectedItem || selectedVersion.PackageSearchMetadata is null) + return; + + SuspendLayout(); + SetPackageVersion(selectedVersion); + ResumeLayout(); + } + + private void versionComboBox_TextChanged(object sender, EventArgs e) + { + if (versionComboBox.SelectedIndex < 0) + { + operationButton.Enabled = false; + } + } } } diff --git a/Bonsai.NuGet.Design/PackageDetails.resx b/Bonsai.NuGet.Design/PackageDetails.resx index 1af7de150..8766f2983 100644 --- a/Bonsai.NuGet.Design/PackageDetails.resx +++ b/Bonsai.NuGet.Design/PackageDetails.resx @@ -117,4 +117,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 17, 17 + \ No newline at end of file diff --git a/Bonsai.NuGet.Design/PackageManagerDialog.Designer.cs b/Bonsai.NuGet.Design/PackageManagerDialog.Designer.cs index c51cf8a4f..6a546857d 100644 --- a/Bonsai.NuGet.Design/PackageManagerDialog.Designer.cs +++ b/Bonsai.NuGet.Design/PackageManagerDialog.Designer.cs @@ -29,186 +29,306 @@ protected override void Dispose(bool disposing) private void InitializeComponent() { this.components = new System.ComponentModel.Container(); + this.packageIcons = new System.Windows.Forms.ImageList(this.components); + this.closeButton = new System.Windows.Forms.Button(); this.mainLayoutPanel = new Bonsai.NuGet.Design.TableLayoutPanel(); + this.operationLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); + this.browseButton = new System.Windows.Forms.RadioButton(); + this.installedButton = new System.Windows.Forms.RadioButton(); + this.updatesButton = new System.Windows.Forms.RadioButton(); this.packageViewLayoutPanel = new Bonsai.NuGet.Design.TableLayoutPanel(); this.filterLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); + this.searchComboBox = new Bonsai.NuGet.Design.CueBannerComboBox(); this.refreshButton = new System.Windows.Forms.Button(); this.prereleaseCheckBox = new System.Windows.Forms.CheckBox(); - this.pageSelectorPanel = new System.Windows.Forms.Panel(); + this.dependencyCheckBox = new System.Windows.Forms.CheckBox(); this.packageViewPanel = new Bonsai.NuGet.Design.TableLayoutPanel(); - this.packageIcons = new System.Windows.Forms.ImageList(this.components); this.multiOperationPanel = new System.Windows.Forms.Panel(); - this.multiOperationButton = new System.Windows.Forms.Button(); this.multiOperationLabel = new System.Windows.Forms.Label(); + this.multiOperationButton = new System.Windows.Forms.Button(); + this.packageView = new Bonsai.NuGet.Design.PackageView(); + this.pageSelectorPanel = new System.Windows.Forms.Panel(); + this.packagePageSelector = new Bonsai.NuGet.Design.PackagePageSelector(); this.detailsLayoutPanel = new Bonsai.NuGet.Design.TableLayoutPanel(); this.searchLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); - this.packageSourceLabel = new System.Windows.Forms.Label(); - this.packageSourceComboBox = new System.Windows.Forms.ComboBox(); this.settingsButton = new System.Windows.Forms.Button(); - this.closePanel = new System.Windows.Forms.Panel(); - this.closeButton = new System.Windows.Forms.Button(); - this.operationLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); - this.browseButton = new System.Windows.Forms.RadioButton(); - this.installedButton = new System.Windows.Forms.RadioButton(); - this.updatesButton = new System.Windows.Forms.RadioButton(); - this.searchComboBox = new Bonsai.NuGet.Design.CueBannerComboBox(); - this.packagePageSelector = new Bonsai.NuGet.Design.PackagePageSelector(); - this.packageView = new Bonsai.NuGet.Design.PackageView(); + this.packageSourceComboBox = new System.Windows.Forms.ComboBox(); + this.packageSourceLabel = new System.Windows.Forms.Label(); this.packageDetails = new Bonsai.NuGet.Design.PackageDetails(); + this.closePanel = new System.Windows.Forms.Panel(); this.saveFolderDialog = new Bonsai.NuGet.Design.SaveFolderDialog(); this.mainLayoutPanel.SuspendLayout(); + this.operationLayoutPanel.SuspendLayout(); this.packageViewLayoutPanel.SuspendLayout(); this.filterLayoutPanel.SuspendLayout(); - this.pageSelectorPanel.SuspendLayout(); this.packageViewPanel.SuspendLayout(); this.multiOperationPanel.SuspendLayout(); + this.pageSelectorPanel.SuspendLayout(); this.detailsLayoutPanel.SuspendLayout(); this.searchLayoutPanel.SuspendLayout(); this.closePanel.SuspendLayout(); - this.operationLayoutPanel.SuspendLayout(); this.SuspendLayout(); // + // packageIcons + // + this.packageIcons.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit; + this.packageIcons.ImageSize = new System.Drawing.Size(32, 32); + this.packageIcons.TransparentColor = System.Drawing.Color.Violet; + // + // closeButton + // + this.closeButton.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.closeButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.closeButton.Location = new System.Drawing.Point(210, 12); + this.closeButton.Name = "closeButton"; + this.closeButton.Size = new System.Drawing.Size(75, 23); + this.closeButton.TabIndex = 6; + this.closeButton.Text = "Close"; + this.closeButton.UseVisualStyleBackColor = true; + this.closeButton.Click += new System.EventHandler(this.closeButton_Click); + // // mainLayoutPanel // this.mainLayoutPanel.ColumnCount = 2; this.mainLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.mainLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 337F)); + this.mainLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 300F)); + this.mainLayoutPanel.Controls.Add(this.operationLayoutPanel, 0, 0); this.mainLayoutPanel.Controls.Add(this.packageViewLayoutPanel, 0, 1); this.mainLayoutPanel.Controls.Add(this.detailsLayoutPanel, 1, 1); this.mainLayoutPanel.Controls.Add(this.closePanel, 1, 2); - this.mainLayoutPanel.Controls.Add(this.operationLayoutPanel, 0, 0); this.mainLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; this.mainLayoutPanel.Location = new System.Drawing.Point(0, 0); - this.mainLayoutPanel.Margin = new System.Windows.Forms.Padding(4); this.mainLayoutPanel.Name = "mainLayoutPanel"; this.mainLayoutPanel.RowCount = 3; - this.mainLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.mainLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.mainLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.mainLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 62F)); - this.mainLayoutPanel.Size = new System.Drawing.Size(1072, 672); + this.mainLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 50F)); + this.mainLayoutPanel.Size = new System.Drawing.Size(944, 546); this.mainLayoutPanel.TabIndex = 0; // + // operationLayoutPanel + // + this.operationLayoutPanel.Controls.Add(this.browseButton); + this.operationLayoutPanel.Controls.Add(this.installedButton); + this.operationLayoutPanel.Controls.Add(this.updatesButton); + this.operationLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.operationLayoutPanel.Location = new System.Drawing.Point(2, 2); + this.operationLayoutPanel.Margin = new System.Windows.Forms.Padding(2); + this.operationLayoutPanel.Name = "operationLayoutPanel"; + this.operationLayoutPanel.Size = new System.Drawing.Size(640, 28); + this.operationLayoutPanel.TabIndex = 0; + this.operationLayoutPanel.TabStop = true; + // + // browseButton + // + this.browseButton.Appearance = System.Windows.Forms.Appearance.Button; + this.browseButton.AutoSize = true; + this.browseButton.FlatAppearance.BorderSize = 0; + this.browseButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.browseButton.Location = new System.Drawing.Point(2, 2); + this.browseButton.Margin = new System.Windows.Forms.Padding(2); + this.browseButton.Name = "browseButton"; + this.browseButton.Size = new System.Drawing.Size(52, 23); + this.browseButton.TabIndex = 0; + this.browseButton.Text = "Browse"; + this.browseButton.UseVisualStyleBackColor = true; + this.browseButton.CheckedChanged += new System.EventHandler(this.refreshButton_Click); + // + // installedButton + // + this.installedButton.Appearance = System.Windows.Forms.Appearance.Button; + this.installedButton.AutoSize = true; + this.installedButton.FlatAppearance.BorderSize = 0; + this.installedButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.installedButton.Location = new System.Drawing.Point(58, 2); + this.installedButton.Margin = new System.Windows.Forms.Padding(2); + this.installedButton.Name = "installedButton"; + this.installedButton.Size = new System.Drawing.Size(56, 23); + this.installedButton.TabIndex = 1; + this.installedButton.Text = "Installed"; + this.installedButton.UseVisualStyleBackColor = true; + this.installedButton.CheckedChanged += new System.EventHandler(this.refreshButton_Click); + // + // updatesButton + // + this.updatesButton.Appearance = System.Windows.Forms.Appearance.Button; + this.updatesButton.AutoSize = true; + this.updatesButton.FlatAppearance.BorderSize = 0; + this.updatesButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.updatesButton.Location = new System.Drawing.Point(118, 2); + this.updatesButton.Margin = new System.Windows.Forms.Padding(2); + this.updatesButton.Name = "updatesButton"; + this.updatesButton.Size = new System.Drawing.Size(57, 23); + this.updatesButton.TabIndex = 2; + this.updatesButton.Text = "Updates"; + this.updatesButton.UseVisualStyleBackColor = true; + this.updatesButton.CheckedChanged += new System.EventHandler(this.refreshButton_Click); + // // packageViewLayoutPanel // this.packageViewLayoutPanel.ColumnCount = 1; this.packageViewLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.packageViewLayoutPanel.Controls.Add(this.filterLayoutPanel, 0, 0); - this.packageViewLayoutPanel.Controls.Add(this.pageSelectorPanel, 0, 2); this.packageViewLayoutPanel.Controls.Add(this.packageViewPanel, 0, 1); + this.packageViewLayoutPanel.Controls.Add(this.pageSelectorPanel, 0, 2); this.packageViewLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; - this.packageViewLayoutPanel.Location = new System.Drawing.Point(4, 44); - this.packageViewLayoutPanel.Margin = new System.Windows.Forms.Padding(4); + this.packageViewLayoutPanel.Location = new System.Drawing.Point(3, 35); this.packageViewLayoutPanel.Name = "packageViewLayoutPanel"; this.packageViewLayoutPanel.RowCount = 3; - this.packageViewLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); + this.mainLayoutPanel.SetRowSpan(this.packageViewLayoutPanel, 2); + this.packageViewLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F)); this.packageViewLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.packageViewLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 49F)); - this.packageViewLayoutPanel.Size = new System.Drawing.Size(727, 562); - this.packageViewLayoutPanel.TabIndex = 2; + this.packageViewLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 46F)); + this.packageViewLayoutPanel.Size = new System.Drawing.Size(638, 508); + this.packageViewLayoutPanel.TabIndex = 1; // // filterLayoutPanel // this.filterLayoutPanel.Controls.Add(this.searchComboBox); this.filterLayoutPanel.Controls.Add(this.refreshButton); this.filterLayoutPanel.Controls.Add(this.prereleaseCheckBox); + this.filterLayoutPanel.Controls.Add(this.dependencyCheckBox); this.filterLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; - this.filterLayoutPanel.Location = new System.Drawing.Point(4, 4); - this.filterLayoutPanel.Margin = new System.Windows.Forms.Padding(4); + this.filterLayoutPanel.Location = new System.Drawing.Point(3, 3); this.filterLayoutPanel.Name = "filterLayoutPanel"; - this.filterLayoutPanel.Size = new System.Drawing.Size(719, 29); - this.filterLayoutPanel.TabIndex = 1; + this.filterLayoutPanel.Size = new System.Drawing.Size(632, 24); + this.filterLayoutPanel.TabIndex = 0; + // + // searchComboBox + // + this.searchComboBox.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend; + this.searchComboBox.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems; + this.searchComboBox.CueBanner = null; + this.searchComboBox.FormattingEnabled = true; + this.searchComboBox.Location = new System.Drawing.Point(3, 3); + this.searchComboBox.Name = "searchComboBox"; + this.searchComboBox.Size = new System.Drawing.Size(226, 21); + this.searchComboBox.TabIndex = 0; // // refreshButton // this.refreshButton.FlatAppearance.BorderSize = 0; this.refreshButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.refreshButton.Image = global::Bonsai.NuGet.Design.Properties.Resources.RefreshImage; - this.refreshButton.Location = new System.Drawing.Point(311, 3); + this.refreshButton.Location = new System.Drawing.Point(234, 2); + this.refreshButton.Margin = new System.Windows.Forms.Padding(2); this.refreshButton.Name = "refreshButton"; - this.refreshButton.Size = new System.Drawing.Size(24, 23); - this.refreshButton.TabIndex = 3; - this.refreshButton.TabStop = false; + this.refreshButton.Size = new System.Drawing.Size(18, 19); + this.refreshButton.TabIndex = 1; this.refreshButton.UseVisualStyleBackColor = true; this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click); // // prereleaseCheckBox // this.prereleaseCheckBox.AutoSize = true; - this.prereleaseCheckBox.Location = new System.Drawing.Point(341, 6); - this.prereleaseCheckBox.Margin = new System.Windows.Forms.Padding(3, 6, 3, 3); + this.prereleaseCheckBox.Location = new System.Drawing.Point(256, 5); + this.prereleaseCheckBox.Margin = new System.Windows.Forms.Padding(2, 5, 2, 2); this.prereleaseCheckBox.Name = "prereleaseCheckBox"; - this.prereleaseCheckBox.Size = new System.Drawing.Size(147, 21); - this.prereleaseCheckBox.TabIndex = 4; + this.prereleaseCheckBox.Size = new System.Drawing.Size(113, 17); + this.prereleaseCheckBox.TabIndex = 2; this.prereleaseCheckBox.Text = "Include prerelease"; this.prereleaseCheckBox.UseVisualStyleBackColor = true; // - // pageSelectorPanel + // dependencyCheckBox // - this.pageSelectorPanel.Controls.Add(this.packagePageSelector); - this.pageSelectorPanel.Dock = System.Windows.Forms.DockStyle.Fill; - this.pageSelectorPanel.Location = new System.Drawing.Point(4, 517); - this.pageSelectorPanel.Margin = new System.Windows.Forms.Padding(4); - this.pageSelectorPanel.Name = "pageSelectorPanel"; - this.pageSelectorPanel.Size = new System.Drawing.Size(719, 41); - this.pageSelectorPanel.TabIndex = 2; + this.dependencyCheckBox.AutoSize = true; + this.dependencyCheckBox.Location = new System.Drawing.Point(373, 5); + this.dependencyCheckBox.Margin = new System.Windows.Forms.Padding(2, 5, 2, 2); + this.dependencyCheckBox.Name = "dependencyCheckBox"; + this.dependencyCheckBox.Size = new System.Drawing.Size(123, 17); + this.dependencyCheckBox.TabIndex = 3; + this.dependencyCheckBox.Text = "Show dependencies"; + this.dependencyCheckBox.UseVisualStyleBackColor = true; + this.dependencyCheckBox.CheckedChanged += new System.EventHandler(this.dependencyCheckBox_CheckedChanged); // // packageViewPanel // this.packageViewPanel.BackColor = System.Drawing.SystemColors.Control; this.packageViewPanel.ColumnCount = 1; this.packageViewPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.packageViewPanel.Controls.Add(this.packageView, 0, 1); this.packageViewPanel.Controls.Add(this.multiOperationPanel, 0, 0); + this.packageViewPanel.Controls.Add(this.packageView, 0, 1); this.packageViewPanel.Dock = System.Windows.Forms.DockStyle.Fill; - this.packageViewPanel.Location = new System.Drawing.Point(4, 41); - this.packageViewPanel.Margin = new System.Windows.Forms.Padding(4); + this.packageViewPanel.Location = new System.Drawing.Point(3, 33); this.packageViewPanel.Name = "packageViewPanel"; this.packageViewPanel.RowCount = 2; this.packageViewPanel.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.packageViewPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.packageViewPanel.Size = new System.Drawing.Size(719, 468); - this.packageViewPanel.TabIndex = 3; - // - // packageIcons - // - this.packageIcons.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit; - this.packageIcons.ImageSize = new System.Drawing.Size(32, 32); - this.packageIcons.TransparentColor = System.Drawing.Color.Violet; + this.packageViewPanel.Size = new System.Drawing.Size(632, 426); + this.packageViewPanel.TabIndex = 1; // // multiOperationPanel // this.multiOperationPanel.AutoSize = true; this.multiOperationPanel.BackColor = System.Drawing.SystemColors.Control; - this.multiOperationPanel.Controls.Add(this.multiOperationButton); this.multiOperationPanel.Controls.Add(this.multiOperationLabel); + this.multiOperationPanel.Controls.Add(this.multiOperationButton); this.multiOperationPanel.Dock = System.Windows.Forms.DockStyle.Fill; this.multiOperationPanel.Location = new System.Drawing.Point(0, 0); this.multiOperationPanel.Margin = new System.Windows.Forms.Padding(0); this.multiOperationPanel.Name = "multiOperationPanel"; - this.multiOperationPanel.Size = new System.Drawing.Size(719, 36); + this.multiOperationPanel.Size = new System.Drawing.Size(632, 29); this.multiOperationPanel.TabIndex = 0; // + // multiOperationLabel + // + this.multiOperationLabel.AutoSize = true; + this.multiOperationLabel.Location = new System.Drawing.Point(3, 8); + this.multiOperationLabel.Name = "multiOperationLabel"; + this.multiOperationLabel.Size = new System.Drawing.Size(79, 13); + this.multiOperationLabel.TabIndex = 0; + this.multiOperationLabel.Text = "OperationLabel"; + // // multiOperationButton // this.multiOperationButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.multiOperationButton.Location = new System.Drawing.Point(579, 4); - this.multiOperationButton.Margin = new System.Windows.Forms.Padding(4); + this.multiOperationButton.Location = new System.Drawing.Point(527, 3); this.multiOperationButton.Name = "multiOperationButton"; - this.multiOperationButton.Size = new System.Drawing.Size(100, 28); + this.multiOperationButton.Size = new System.Drawing.Size(75, 23); this.multiOperationButton.TabIndex = 1; this.multiOperationButton.Text = "Operation"; this.multiOperationButton.UseVisualStyleBackColor = true; this.multiOperationButton.Click += new System.EventHandler(this.multiOperationButton_Click); // - // multiOperationLabel + // packageView // - this.multiOperationLabel.AutoSize = true; - this.multiOperationLabel.Location = new System.Drawing.Point(4, 10); - this.multiOperationLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); - this.multiOperationLabel.Name = "multiOperationLabel"; - this.multiOperationLabel.Size = new System.Drawing.Size(106, 17); - this.multiOperationLabel.TabIndex = 2; - this.multiOperationLabel.Text = "OperationLabel"; + this.packageView.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(246)))), ((int)(((byte)(246)))), ((int)(((byte)(246))))); + this.packageView.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.packageView.CanSelectNodes = false; + this.packageView.Dock = System.Windows.Forms.DockStyle.Fill; + this.packageView.FullRowSelect = true; + this.packageView.HotTracking = true; + this.packageView.ImageIndex = 0; + this.packageView.ImageList = this.packageIcons; + this.packageView.ItemHeight = 64; + this.packageView.Location = new System.Drawing.Point(3, 32); + this.packageView.Name = "packageView"; + this.packageView.Operation = Bonsai.NuGet.Design.PackageOperationType.Install; + this.packageView.SelectedImageIndex = 0; + this.packageView.ShowLines = false; + this.packageView.ShowRootLines = false; + this.packageView.Size = new System.Drawing.Size(626, 391); + this.packageView.TabIndex = 1; + this.packageView.OperationClick += new Bonsai.NuGet.Design.PackageViewEventHandler(this.packageView_OperationClick); + // + // pageSelectorPanel + // + this.pageSelectorPanel.Anchor = System.Windows.Forms.AnchorStyles.None; + this.pageSelectorPanel.Controls.Add(this.packagePageSelector); + this.pageSelectorPanel.Location = new System.Drawing.Point(3, 465); + this.pageSelectorPanel.Name = "pageSelectorPanel"; + this.pageSelectorPanel.Size = new System.Drawing.Size(632, 40); + this.pageSelectorPanel.TabIndex = 2; + // + // packagePageSelector + // + this.packagePageSelector.Location = new System.Drawing.Point(219, 7); + this.packagePageSelector.Margin = new System.Windows.Forms.Padding(4); + this.packagePageSelector.Name = "packagePageSelector"; + this.packagePageSelector.SelectedPage = 0; + this.packagePageSelector.ShowNext = false; + this.packagePageSelector.Size = new System.Drawing.Size(75, 27); + this.packagePageSelector.TabIndex = 0; // // detailsLayoutPanel // @@ -217,189 +337,78 @@ private void InitializeComponent() this.detailsLayoutPanel.Controls.Add(this.searchLayoutPanel, 0, 0); this.detailsLayoutPanel.Controls.Add(this.packageDetails, 0, 1); this.detailsLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; - this.detailsLayoutPanel.Location = new System.Drawing.Point(739, 44); - this.detailsLayoutPanel.Margin = new System.Windows.Forms.Padding(4); + this.detailsLayoutPanel.Location = new System.Drawing.Point(647, 35); this.detailsLayoutPanel.Name = "detailsLayoutPanel"; this.detailsLayoutPanel.RowCount = 2; - this.detailsLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); + this.detailsLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F)); this.detailsLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.detailsLayoutPanel.Size = new System.Drawing.Size(329, 562); - this.detailsLayoutPanel.TabIndex = 3; + this.detailsLayoutPanel.Size = new System.Drawing.Size(294, 458); + this.detailsLayoutPanel.TabIndex = 2; // // searchLayoutPanel // - this.searchLayoutPanel.Controls.Add(this.packageSourceLabel); - this.searchLayoutPanel.Controls.Add(this.packageSourceComboBox); this.searchLayoutPanel.Controls.Add(this.settingsButton); + this.searchLayoutPanel.Controls.Add(this.packageSourceComboBox); + this.searchLayoutPanel.Controls.Add(this.packageSourceLabel); this.searchLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; - this.searchLayoutPanel.Location = new System.Drawing.Point(4, 4); - this.searchLayoutPanel.Margin = new System.Windows.Forms.Padding(4); + this.searchLayoutPanel.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; + this.searchLayoutPanel.Location = new System.Drawing.Point(3, 3); this.searchLayoutPanel.Name = "searchLayoutPanel"; - this.searchLayoutPanel.Size = new System.Drawing.Size(321, 29); + this.searchLayoutPanel.Size = new System.Drawing.Size(288, 24); this.searchLayoutPanel.TabIndex = 0; // - // packageSourceLabel - // - this.packageSourceLabel.Location = new System.Drawing.Point(4, 0); - this.packageSourceLabel.Margin = new System.Windows.Forms.Padding(4, 0, 0, 0); - this.packageSourceLabel.Name = "packageSourceLabel"; - this.packageSourceLabel.Size = new System.Drawing.Size(120, 27); - this.packageSourceLabel.TabIndex = 3; - this.packageSourceLabel.Text = "Package source:"; - this.packageSourceLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; - // - // packageSourceComboBox - // - this.packageSourceComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.packageSourceComboBox.FormattingEnabled = true; - this.packageSourceComboBox.Location = new System.Drawing.Point(132, 4); - this.packageSourceComboBox.Margin = new System.Windows.Forms.Padding(4); - this.packageSourceComboBox.Name = "packageSourceComboBox"; - this.packageSourceComboBox.Size = new System.Drawing.Size(150, 24); - this.packageSourceComboBox.TabIndex = 5; - this.packageSourceComboBox.SelectedIndexChanged += new System.EventHandler(this.refreshButton_Click); - // // settingsButton // this.settingsButton.FlatAppearance.BorderSize = 0; this.settingsButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.settingsButton.Image = global::Bonsai.NuGet.Design.Properties.Resources.SettingsImage; - this.settingsButton.Location = new System.Drawing.Point(289, 3); + this.settingsButton.Location = new System.Drawing.Point(268, 2); + this.settingsButton.Margin = new System.Windows.Forms.Padding(2); this.settingsButton.Name = "settingsButton"; - this.settingsButton.Size = new System.Drawing.Size(24, 23); - this.settingsButton.TabIndex = 6; + this.settingsButton.Size = new System.Drawing.Size(18, 19); + this.settingsButton.TabIndex = 2; this.settingsButton.UseVisualStyleBackColor = true; this.settingsButton.Click += new System.EventHandler(this.settingsButton_Click); // - // closePanel - // - this.closePanel.Controls.Add(this.closeButton); - this.closePanel.Dock = System.Windows.Forms.DockStyle.Fill; - this.closePanel.Location = new System.Drawing.Point(739, 614); - this.closePanel.Margin = new System.Windows.Forms.Padding(4); - this.closePanel.Name = "closePanel"; - this.closePanel.Size = new System.Drawing.Size(329, 54); - this.closePanel.TabIndex = 5; - // - // closeButton - // - this.closeButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.closeButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.closeButton.Location = new System.Drawing.Point(217, 15); - this.closeButton.Margin = new System.Windows.Forms.Padding(4); - this.closeButton.Name = "closeButton"; - this.closeButton.Size = new System.Drawing.Size(100, 28); - this.closeButton.TabIndex = 6; - this.closeButton.Text = "Close"; - this.closeButton.UseVisualStyleBackColor = true; - this.closeButton.Click += new System.EventHandler(this.closeButton_Click); - // - // operationLayoutPanel - // - this.operationLayoutPanel.Controls.Add(this.browseButton); - this.operationLayoutPanel.Controls.Add(this.installedButton); - this.operationLayoutPanel.Controls.Add(this.updatesButton); - this.operationLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; - this.operationLayoutPanel.Location = new System.Drawing.Point(3, 3); - this.operationLayoutPanel.Name = "operationLayoutPanel"; - this.operationLayoutPanel.Size = new System.Drawing.Size(729, 34); - this.operationLayoutPanel.TabIndex = 0; - this.operationLayoutPanel.TabStop = true; - // - // browseButton - // - this.browseButton.Appearance = System.Windows.Forms.Appearance.Button; - this.browseButton.AutoSize = true; - this.browseButton.FlatAppearance.BorderSize = 0; - this.browseButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.browseButton.Location = new System.Drawing.Point(3, 3); - this.browseButton.Name = "browseButton"; - this.browseButton.Size = new System.Drawing.Size(64, 27); - this.browseButton.TabIndex = 0; - this.browseButton.Text = "Browse"; - this.browseButton.UseVisualStyleBackColor = true; - this.browseButton.CheckedChanged += new System.EventHandler(this.refreshButton_Click); - // - // installedButton - // - this.installedButton.Appearance = System.Windows.Forms.Appearance.Button; - this.installedButton.AutoSize = true; - this.installedButton.FlatAppearance.BorderSize = 0; - this.installedButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.installedButton.Location = new System.Drawing.Point(73, 3); - this.installedButton.Name = "installedButton"; - this.installedButton.Size = new System.Drawing.Size(70, 27); - this.installedButton.TabIndex = 1; - this.installedButton.Text = "Installed"; - this.installedButton.UseVisualStyleBackColor = true; - this.installedButton.CheckedChanged += new System.EventHandler(this.refreshButton_Click); - // - // updatesButton - // - this.updatesButton.Appearance = System.Windows.Forms.Appearance.Button; - this.updatesButton.AutoSize = true; - this.updatesButton.FlatAppearance.BorderSize = 0; - this.updatesButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.updatesButton.Location = new System.Drawing.Point(149, 3); - this.updatesButton.Name = "updatesButton"; - this.updatesButton.Size = new System.Drawing.Size(71, 27); - this.updatesButton.TabIndex = 2; - this.updatesButton.Text = "Updates"; - this.updatesButton.UseVisualStyleBackColor = true; - this.updatesButton.CheckedChanged += new System.EventHandler(this.refreshButton_Click); - // - // searchComboBox - // - this.searchComboBox.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend; - this.searchComboBox.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems; - this.searchComboBox.CueBanner = null; - this.searchComboBox.FormattingEnabled = true; - this.searchComboBox.Location = new System.Drawing.Point(4, 4); - this.searchComboBox.Margin = new System.Windows.Forms.Padding(4); - this.searchComboBox.Name = "searchComboBox"; - this.searchComboBox.Size = new System.Drawing.Size(300, 24); - this.searchComboBox.TabIndex = 1; - // - // packagePageSelector + // packageSourceComboBox // - this.packagePageSelector.AutoSize = true; - this.packagePageSelector.Dock = System.Windows.Forms.DockStyle.Fill; - this.packagePageSelector.Location = new System.Drawing.Point(0, 0); - this.packagePageSelector.Margin = new System.Windows.Forms.Padding(5); - this.packagePageSelector.Name = "packagePageSelector"; - this.packagePageSelector.Size = new System.Drawing.Size(719, 41); - this.packagePageSelector.TabIndex = 3; + this.packageSourceComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.packageSourceComboBox.FormattingEnabled = true; + this.packageSourceComboBox.Location = new System.Drawing.Point(149, 3); + this.packageSourceComboBox.Name = "packageSourceComboBox"; + this.packageSourceComboBox.Size = new System.Drawing.Size(114, 21); + this.packageSourceComboBox.TabIndex = 1; + this.packageSourceComboBox.SelectedIndexChanged += new System.EventHandler(this.refreshButton_Click); // - // packageView + // packageSourceLabel // - this.packageView.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(246)))), ((int)(((byte)(246)))), ((int)(((byte)(246))))); - this.packageView.BorderStyle = System.Windows.Forms.BorderStyle.None; - this.packageView.CanSelectNodes = false; - this.packageView.Dock = System.Windows.Forms.DockStyle.Fill; - this.packageView.DrawMode = System.Windows.Forms.TreeViewDrawMode.OwnerDrawText; - this.packageView.FullRowSelect = true; - this.packageView.ImageIndex = 0; - this.packageView.ImageList = this.packageIcons; - this.packageView.ItemHeight = 64; - this.packageView.Location = new System.Drawing.Point(4, 40); - this.packageView.Margin = new System.Windows.Forms.Padding(4); - this.packageView.Name = "packageView"; - this.packageView.OperationText = null; - this.packageView.SelectedImageIndex = 0; - this.packageView.ShowLines = false; - this.packageView.ShowRootLines = false; - this.packageView.Size = new System.Drawing.Size(711, 424); - this.packageView.TabIndex = 2; - this.packageView.OperationClick += new System.Windows.Forms.TreeViewEventHandler(this.packageView_OperationClick); + this.packageSourceLabel.Location = new System.Drawing.Point(56, 0); + this.packageSourceLabel.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0); + this.packageSourceLabel.Name = "packageSourceLabel"; + this.packageSourceLabel.Size = new System.Drawing.Size(90, 22); + this.packageSourceLabel.TabIndex = 0; + this.packageSourceLabel.Text = "Package source:"; + this.packageSourceLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // packageDetails // this.packageDetails.Dock = System.Windows.Forms.DockStyle.Fill; - this.packageDetails.Location = new System.Drawing.Point(5, 42); - this.packageDetails.Margin = new System.Windows.Forms.Padding(5); + this.packageDetails.Location = new System.Drawing.Point(0, 34); + this.packageDetails.Margin = new System.Windows.Forms.Padding(0, 4, 4, 4); this.packageDetails.Name = "packageDetails"; - this.packageDetails.Size = new System.Drawing.Size(319, 515); + this.packageDetails.Operation = Bonsai.NuGet.Design.PackageOperationType.Install; + this.packageDetails.Size = new System.Drawing.Size(290, 420); this.packageDetails.TabIndex = 1; + this.packageDetails.OperationClick += new Bonsai.NuGet.Design.PackageViewEventHandler(this.packageView_OperationClick); + // + // closePanel + // + this.closePanel.Controls.Add(this.closeButton); + this.closePanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.closePanel.Location = new System.Drawing.Point(647, 499); + this.closePanel.Name = "closePanel"; + this.closePanel.Size = new System.Drawing.Size(294, 44); + this.closePanel.TabIndex = 3; // // saveFolderDialog // @@ -407,33 +416,31 @@ private void InitializeComponent() // // PackageManagerDialog // - this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.closeButton; - this.ClientSize = new System.Drawing.Size(1072, 672); + this.ClientSize = new System.Drawing.Size(944, 546); this.Controls.Add(this.mainLayoutPanel); this.KeyPreview = true; - this.Margin = new System.Windows.Forms.Padding(4); - this.MinimumSize = new System.Drawing.Size(1087, 709); + this.MinimumSize = new System.Drawing.Size(850, 583); this.Name = "PackageManagerDialog"; this.ShowIcon = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "Bonsai - Manage Packages"; this.mainLayoutPanel.ResumeLayout(false); + this.operationLayoutPanel.ResumeLayout(false); + this.operationLayoutPanel.PerformLayout(); this.packageViewLayoutPanel.ResumeLayout(false); this.filterLayoutPanel.ResumeLayout(false); this.filterLayoutPanel.PerformLayout(); - this.pageSelectorPanel.ResumeLayout(false); - this.pageSelectorPanel.PerformLayout(); this.packageViewPanel.ResumeLayout(false); this.packageViewPanel.PerformLayout(); this.multiOperationPanel.ResumeLayout(false); this.multiOperationPanel.PerformLayout(); + this.pageSelectorPanel.ResumeLayout(false); this.detailsLayoutPanel.ResumeLayout(false); this.searchLayoutPanel.ResumeLayout(false); this.closePanel.ResumeLayout(false); - this.operationLayoutPanel.ResumeLayout(false); - this.operationLayoutPanel.PerformLayout(); this.ResumeLayout(false); } @@ -467,5 +474,6 @@ private void InitializeComponent() private System.Windows.Forms.RadioButton installedButton; private System.Windows.Forms.RadioButton updatesButton; private System.Windows.Forms.Button settingsButton; + private System.Windows.Forms.CheckBox dependencyCheckBox; } } diff --git a/Bonsai.NuGet.Design/PackageManagerDialog.cs b/Bonsai.NuGet.Design/PackageManagerDialog.cs index 3abd5147e..db6af30ce 100644 --- a/Bonsai.NuGet.Design/PackageManagerDialog.cs +++ b/Bonsai.NuGet.Design/PackageManagerDialog.cs @@ -32,8 +32,8 @@ public PackageManagerDialog(NuGetFramework projectFramework, string path) searchComboBox, prereleaseCheckBox, () => updatesButton.Checked, - value => multiOperationPanel.Visible = value, - Enumerable.Empty()); + value => multiOperationPanel.Visible = value); + packageViewController.PackageTypes = new[] { Constants.LibraryPackageType }; packageViewController.PackageManager.PackageManagerPlugins.Add(new ExecutablePackagePlugin(this)); InitializePackageSourceItems(); multiOperationPanel.Visible = false; @@ -73,7 +73,7 @@ private void UpdateSelectedRepository() packageViewController.ClearActiveRequests(); if (installedButton.Checked) { - packageView.OperationText = Resources.UninstallOperationName; + packageViewController.Operation = PackageOperationType.Uninstall; packageViewController.SelectedRepository = PackageManager.LocalRepository; } else @@ -87,9 +87,9 @@ private void UpdateSelectedRepository() if (updatesButton.Checked) { - packageView.OperationText = Resources.UpdateOperationName; + packageViewController.Operation = PackageOperationType.Update; } - else packageView.OperationText = Resources.InstallOperationName; + else packageViewController.Operation = PackageOperationType.Install; } searchComboBox.Text = string.Empty; @@ -138,7 +138,7 @@ protected override void OnResizeEnd(EventArgs e) private void multiOperationButton_Click(object sender, EventArgs e) { - if (packageView.OperationText == Resources.UpdateOperationName) + if (packageViewController.Operation == PackageOperationType.Update) { var packages = packageView.Nodes.Cast() .Select(node => node.Tag as IPackageSearchMetadata) @@ -148,10 +148,10 @@ private void multiOperationButton_Click(object sender, EventArgs e) } } - private void packageView_OperationClick(object sender, TreeViewEventArgs e) + private void packageView_OperationClick(object sender, PackageViewEventArgs e) { bool handleDependencies = true; - var package = (IPackageSearchMetadata)e.Node.Tag; + var package = e.Package; if (package != null) { if (packageViewController.SelectedRepository == PackageManager.LocalRepository) @@ -192,7 +192,7 @@ public ExecutablePackagePlugin(PackageManagerDialog owner) public override Task OnPackageInstallingAsync(PackageIdentity package, NuGetFramework projectFramework, PackageReaderBase packageReader, string installPath) { - if (PackageHelper.IsExecutablePackage(package, projectFramework, packageReader)) + if (packageReader.IsExecutablePackage(package, projectFramework)) { Owner.Invoke((Action)(() => { @@ -204,7 +204,7 @@ public override Task OnPackageInstallingAsync(PackageIdentity package, NuG if (Owner.saveFolderDialog.ShowDialog(Owner) == DialogResult.OK) { var targetPath = Owner.saveFolderDialog.FileName; - Owner.InstallPath = PackageHelper.InstallExecutablePackage(package, projectFramework, packageReader, targetPath); + Owner.InstallPath = packageReader.InstallExecutablePackage(package, projectFramework, targetPath); Owner.DialogResult = DialogResult.OK; } } @@ -234,6 +234,15 @@ private void closeButton_Click(object sender, EventArgs e) { Close(); } + + private void dependencyCheckBox_CheckedChanged(object sender, EventArgs e) + { + var packageType = dependencyCheckBox.Checked + ? PackageType.Dependency.Name + : Constants.LibraryPackageType; + packageViewController.PackageTypes = new[] { packageType }; + packageViewController.UpdatePackageQuery(); + } } public enum PackageManagerTab diff --git a/Bonsai.NuGet.Design/PackageOperationType.cs b/Bonsai.NuGet.Design/PackageOperationType.cs new file mode 100644 index 000000000..4881b646c --- /dev/null +++ b/Bonsai.NuGet.Design/PackageOperationType.cs @@ -0,0 +1,10 @@ +namespace Bonsai.NuGet.Design +{ + internal enum PackageOperationType + { + Install, + Uninstall, + Update, + Open + } +} diff --git a/Bonsai.NuGet.Design/PackagePageSelector.Designer.cs b/Bonsai.NuGet.Design/PackagePageSelector.Designer.cs index 8900d10a0..c8b3ac572 100644 --- a/Bonsai.NuGet.Design/PackagePageSelector.Designer.cs +++ b/Bonsai.NuGet.Design/PackagePageSelector.Designer.cs @@ -28,36 +28,24 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - this.flowLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); this.currentButton = new System.Windows.Forms.Button(); this.previousButton = new System.Windows.Forms.Button(); this.nextButton = new System.Windows.Forms.Button(); this.tableLayoutPanel = new Bonsai.NuGet.Design.TableLayoutPanel(); - this.flowLayoutPanel.SuspendLayout(); this.tableLayoutPanel.SuspendLayout(); this.SuspendLayout(); // - // flowLayoutPanel - // - this.flowLayoutPanel.AutoSize = true; - this.flowLayoutPanel.Controls.Add(this.currentButton); - this.flowLayoutPanel.Location = new System.Drawing.Point(26, 0); - this.flowLayoutPanel.Margin = new System.Windows.Forms.Padding(0); - this.flowLayoutPanel.Name = "flowLayoutPanel"; - this.flowLayoutPanel.Size = new System.Drawing.Size(115, 27); - this.flowLayoutPanel.TabIndex = 0; - // - // button1 + // currentButton // this.currentButton.AutoSize = true; this.currentButton.FlatAppearance.BorderColor = System.Drawing.SystemColors.Control; this.currentButton.FlatAppearance.BorderSize = 0; this.currentButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.currentButton.Location = new System.Drawing.Point(0, 2); + this.currentButton.Location = new System.Drawing.Point(25, 2); this.currentButton.Margin = new System.Windows.Forms.Padding(0, 2, 0, 2); this.currentButton.Name = "currentButton"; this.currentButton.Size = new System.Drawing.Size(23, 23); - this.currentButton.TabIndex = 2; + this.currentButton.TabIndex = 1; this.currentButton.Text = "1"; this.currentButton.UseVisualStyleBackColor = true; // @@ -69,7 +57,7 @@ private void InitializeComponent() this.previousButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.previousButton.Location = new System.Drawing.Point(3, 3); this.previousButton.Name = "previousButton"; - this.previousButton.Size = new System.Drawing.Size(20, 20); + this.previousButton.Size = new System.Drawing.Size(19, 20); this.previousButton.TabIndex = 0; this.previousButton.Text = "<"; this.previousButton.UseVisualStyleBackColor = true; @@ -80,31 +68,28 @@ private void InitializeComponent() this.nextButton.FlatAppearance.BorderColor = System.Drawing.SystemColors.Control; this.nextButton.FlatAppearance.BorderSize = 0; this.nextButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.nextButton.Location = new System.Drawing.Point(144, 3); + this.nextButton.Location = new System.Drawing.Point(53, 3); this.nextButton.Name = "nextButton"; - this.nextButton.Size = new System.Drawing.Size(20, 20); - this.nextButton.TabIndex = 1; + this.nextButton.Size = new System.Drawing.Size(19, 20); + this.nextButton.TabIndex = 2; this.nextButton.Text = ">"; this.nextButton.UseVisualStyleBackColor = true; // // tableLayoutPanel // - this.tableLayoutPanel.Anchor = System.Windows.Forms.AnchorStyles.None; - this.tableLayoutPanel.AutoSize = true; - this.tableLayoutPanel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; this.tableLayoutPanel.ColumnCount = 3; - this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 26F)); - this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); - this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 26F)); + this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); + this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); + this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); this.tableLayoutPanel.Controls.Add(this.previousButton, 0, 0); - this.tableLayoutPanel.Controls.Add(this.flowLayoutPanel, 1, 0); + this.tableLayoutPanel.Controls.Add(this.currentButton, 1, 0); this.tableLayoutPanel.Controls.Add(this.nextButton, 2, 0); this.tableLayoutPanel.Location = new System.Drawing.Point(0, 0); this.tableLayoutPanel.Name = "tableLayoutPanel"; this.tableLayoutPanel.RowCount = 1; this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tableLayoutPanel.Size = new System.Drawing.Size(167, 27); - this.tableLayoutPanel.TabIndex = 1; + this.tableLayoutPanel.Size = new System.Drawing.Size(75, 27); + this.tableLayoutPanel.TabIndex = 0; // // PackagePageSelector // @@ -112,23 +97,17 @@ private void InitializeComponent() this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.tableLayoutPanel); this.Name = "PackagePageSelector"; - this.Size = new System.Drawing.Size(168, 26); - this.flowLayoutPanel.ResumeLayout(false); - this.flowLayoutPanel.PerformLayout(); + this.Size = new System.Drawing.Size(75, 27); this.tableLayoutPanel.ResumeLayout(false); this.tableLayoutPanel.PerformLayout(); this.ResumeLayout(false); - this.PerformLayout(); } #endregion - - private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel; private System.Windows.Forms.Button previousButton; private System.Windows.Forms.Button nextButton; private System.Windows.Forms.Button currentButton; - private Bonsai.NuGet.Design.TableLayoutPanel tableLayoutPanel; - + private TableLayoutPanel tableLayoutPanel; } } diff --git a/Bonsai.NuGet.Design/PackageSearchEventArgs.cs b/Bonsai.NuGet.Design/PackageSearchEventArgs.cs new file mode 100644 index 000000000..b22deab98 --- /dev/null +++ b/Bonsai.NuGet.Design/PackageSearchEventArgs.cs @@ -0,0 +1,16 @@ +using System; + +namespace Bonsai.NuGet.Design +{ + internal delegate void PackageSearchEventHandler(object sender, PackageSearchEventArgs e); + + internal class PackageSearchEventArgs : EventArgs + { + public PackageSearchEventArgs(string searchTerm) + { + SearchTerm = searchTerm; + } + + public string SearchTerm { get; } + } +} diff --git a/Bonsai.NuGet.Design/PackageView.cs b/Bonsai.NuGet.Design/PackageView.cs index 1933d2953..9eb6f842f 100644 --- a/Bonsai.NuGet.Design/PackageView.cs +++ b/Bonsai.NuGet.Design/PackageView.cs @@ -1,18 +1,15 @@ using Bonsai.NuGet.Design.Properties; +using NuGet.Protocol; +using NuGet.Protocol.Core.Types; using System; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; -using System.Windows.Forms.VisualStyles; namespace Bonsai.NuGet.Design { class PackageView : TreeView { - const int WM_NCMOUSEHOVER = 0x02a0; - const int WM_MOUSEHOVER = 0x02a1; - const int WM_NCMOUSELEAVE = 0x02a2; - const int WM_MOUSELEAVE = 0x02a3; const int WM_NOTIFY = 0x004e; const int WM_LBUTTONDOWN = 0x0201; const int WM_LBUTTONDBLCLK = 0x0203; @@ -23,44 +20,70 @@ class PackageView : TreeView const int TVS_EX_DOUBLEBUFFER = 0x0004; Font boldFont; + Font titleFont; + Font hyperlinkFont; + Font iconFont; + Brush nodeHighlight; int boundsMargin; + SizeF buttonSize; int verticalScrollBarWidth; - Rectangle operationButtonBounds; - readonly Image packageViewNodeCheckedImage; - static readonly Rectangle DefaultOperationButtonBounds = new Rectangle(10, 2, 75, 23); - const int DefaultBoundsMargin = 5; + PackageOperationType operation; + TreeNode operationHoverNode; + bool operationButtonState; + const int DefaultBoundsMargin = 6; + static readonly object OperationClickEvent = new(); public PackageView() { ShowLines = false; + HotTracking = true; FullRowSelect = true; - DrawMode = TreeViewDrawMode.OwnerDrawText; - packageViewNodeCheckedImage = Resources.PackageViewNodeCheckedImage; + DrawMode = TreeViewDrawMode.OwnerDrawAll; boundsMargin = DefaultBoundsMargin; verticalScrollBarWidth = SystemInformation.VerticalScrollBarWidth; - operationButtonBounds = DefaultOperationButtonBounds; + nodeHighlight = new SolidBrush(ControlPaint.LightLight(SystemColors.Highlight)); + SetStyle(ControlStyles.ResizeRedraw, false); } - public override Font Font + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public new TreeViewDrawMode DrawMode { - get { return base.Font; } - set - { - base.Font = value; - boldFont = new Font(value, FontStyle.Bold); - } + get => base.DrawMode; + set => base.DrawMode = value; } [Category("Action")] - public event TreeViewEventHandler OperationClick; + public event PackageViewEventHandler OperationClick + { + add { Events.AddHandler(OperationClickEvent, value); } + remove { Events.RemoveHandler(OperationClickEvent, value); } + } - public string OperationText { get; set; } + private Image OperationImage { get; set; } + + public PackageOperationType Operation + { + get => operation; + set + { + operation = value; + OperationImage = operation switch + { + PackageOperationType.Open => Resources.OpenImage, + PackageOperationType.Update => Resources.PackageUpdateImage, + PackageOperationType.Uninstall => Resources.PackageRemoveImage, + PackageOperationType.Install or _ => Resources.DownloadImage + }; + } + } public bool CanSelectNodes { get; set; } - private void OnOperationClick(TreeViewEventArgs e) + private void OnOperationClick(PackageViewEventArgs e) { - OperationClick?.Invoke(this, e); + (Events[OperationClickEvent] as PackageViewEventHandler)?.Invoke(this, e); } protected override CreateParams CreateParams @@ -79,11 +102,8 @@ protected override void ScaleControl(SizeF factor, BoundsSpecified specified) var widthScaleFactor = factor.Height * 0.5f + 0.5f; verticalScrollBarWidth = (int)(SystemInformation.VerticalScrollBarWidth * widthScaleFactor); boundsMargin = (int)(DefaultBoundsMargin * factor.Height); - operationButtonBounds = new Rectangle( - (int)(DefaultOperationButtonBounds.X * factor.Height), - (int)(DefaultOperationButtonBounds.Y * factor.Height), - (int)(DefaultOperationButtonBounds.Width * factor.Height), - (int)(DefaultOperationButtonBounds.Height * factor.Height)); + using var graphics = CreateGraphics(); + buttonSize = graphics.GetImageSize(OperationImage); base.ScaleControl(factor, specified); } @@ -94,6 +114,9 @@ protected override void OnHandleCreated(EventArgs e) NativeMethods.SendMessage(Handle, TVM_SETEXTENDEDSTYLE, (IntPtr)TVS_EX_DOUBLEBUFFER, (IntPtr)TVS_EX_DOUBLEBUFFER); } boldFont = new Font(Font, FontStyle.Bold); + titleFont = new Font(Font.FontFamily, Font.Size + 1, FontStyle.Bold); + hyperlinkFont = new Font(Font, FontStyle.Bold | FontStyle.Underline); + iconFont = new Font(Font.FontFamily, Font.Size - 1.5f); base.OnHandleCreated(e); } @@ -101,10 +124,6 @@ protected override void WndProc(ref Message m) { switch (m.Msg) { - case WM_MOUSELEAVE: - case WM_NCMOUSELEAVE: - case WM_MOUSEHOVER: - case WM_NCMOUSEHOVER: case WM_NOTIFY: return; case WM_LBUTTONDOWN: @@ -131,19 +150,46 @@ protected override void OnMouseDown(MouseEventArgs e) base.OnMouseDown(e); } + protected override void OnMouseMove(MouseEventArgs e) + { + var node = GetNodeAt(e.Location); + if (operationHoverNode != node) + operationButtonState = false; + + if (node != null) + { + var buttonBounds = GetOperationButtonBounds(node.Bounds); + var hoverState = buttonBounds.Contains(e.Location); + if (operationButtonState != hoverState) + { + var nodeBounds = node.Bounds; + nodeBounds.Width = Width - nodeBounds.X; + Invalidate(nodeBounds); + } + + operationButtonState = hoverState; + } + + operationHoverNode = node; + base.OnMouseMove(e); + } + protected override void OnMouseClick(MouseEventArgs e) { - if (OperationHitTest(e.Location) && e.Button == MouseButtons.Left) + if (e.Button == MouseButtons.Left && + OperationHitTest(e.Location, out TreeViewHitTestInfo hitTestInfo)) { - OnOperationClick(new TreeViewEventArgs(SelectedNode, TreeViewAction.ByMouse)); + OnOperationClick(new PackageViewEventArgs( + (IPackageSearchMetadata)hitTestInfo.Node.Tag, + Operation)); } base.OnMouseClick(e); } - private bool OperationHitTest(Point pt) + private bool OperationHitTest(Point pt, out TreeViewHitTestInfo hitTestInfo) { - var hitTestInfo = HitTest(pt); - if (hitTestInfo.Node != null && !hitTestInfo.Node.Checked && hitTestInfo.Node == SelectedNode) + hitTestInfo = HitTest(pt); + if (hitTestInfo.Node != null && !hitTestInfo.Node.Checked) { if (hitTestInfo.Node.Tag == null) return false; var buttonBounds = GetOperationButtonBounds(hitTestInfo.Node.Bounds); @@ -158,16 +204,69 @@ private int RightMargin get { return Width - verticalScrollBarWidth; } } - private Rectangle GetOperationButtonBounds(Rectangle nodeBounds) + private RectangleF GetOperationButtonBounds(Rectangle nodeBounds) + { + // Node bounds span the full text area but we need to account for scroll bar + return new( + x: RightMargin - buttonSize.Width - boundsMargin, + y: nodeBounds.Y + boundsMargin, + width: buttonSize.Width, + height: buttonSize.Height); + } + + private void FillImageBounds(Graphics graphics, Brush brush, Image image, ref Rectangle bounds) + { + var imageSize = graphics.GetImageSize(image); + RectangleF imageBounds = new( + x: bounds.Right - imageSize.Width, + y: bounds.Y, + width: imageSize.Width, + height: imageSize.Height); + graphics.FillRectangle(brush, imageBounds); + } + + private void DrawImageOverlay(Graphics graphics, Image overlay, float imageX, float imageY) + { + var imageSize = graphics.GetImageSize(overlay); + var overlayX = imageX + ImageList.ImageSize.Width - imageSize.Width / 2 - Margin.Horizontal; + var overlayY = imageY + ImageList.ImageSize.Height - imageSize.Height / 2 - Margin.Vertical; + graphics.DrawImage(overlay, overlayX, overlayY); + } + + private void DrawInlineImage(Graphics graphics, Image image, ref Rectangle bounds) + { + float imageX; + var imageSize = Size.Round(graphics.GetImageSize(image)); + imageX = bounds.Right - imageSize.Width; + graphics.DrawImage(image, imageX, bounds.Y); + bounds.Width -= imageSize.Width + boundsMargin; + } + + private void DrawInlineText( + Graphics graphics, + string text, + Font font, + Color color, + ref Rectangle bounds) { - nodeBounds.X = RightMargin - operationButtonBounds.Width - operationButtonBounds.X; - nodeBounds.Y += operationButtonBounds.Y; - nodeBounds.Size = operationButtonBounds.Size; - return nodeBounds; + if (bounds.Width <= 0) + return; + + var textFormatFlags = TextFormatFlags.NoPadding; + var textSize = TextRenderer.MeasureText(graphics, text, font, bounds.Size, textFormatFlags); + if (textSize.Width > bounds.Width) + { + textFormatFlags |= TextFormatFlags.WordEllipsis; + } + + TextRenderer.DrawText(graphics, text, font, bounds, color, textFormatFlags); + bounds.X += textSize.Width; + bounds.Width -= textSize.Width; } protected override void OnDrawNode(DrawTreeNodeEventArgs e) { + e.DrawDefault = false; var bounds = e.Bounds; bounds.Width = RightMargin - bounds.X; var color = (e.State & TreeNodeStates.Selected) != 0 @@ -180,50 +279,175 @@ protected override void OnDrawNode(DrawTreeNodeEventArgs e) } else { - if (NativeMethods.IsRunningOnMono && (e.State & TreeNodeStates.Selected) != 0) + var nodeHot = (e.State & TreeNodeStates.Hot) != 0; + var nodeSelected = (e.State & TreeNodeStates.Selected) != 0; + if (nodeSelected) { e.Graphics.FillRectangle(SystemBrushes.Highlight, bounds); } - if (e.Node.Checked) + if (!nodeSelected && nodeHot) { - var checkedImageX = RightMargin - packageViewNodeCheckedImage.Width - boundsMargin; - var checkedImageY = bounds.Y + operationButtonBounds.Y; - e.Graphics.DrawImage(packageViewNodeCheckedImage, checkedImageX, checkedImageY); - bounds.Width -= packageViewNodeCheckedImage.Width; + e.Graphics.FillRectangle(nodeHighlight, bounds); } - else if ((e.State & TreeNodeStates.Selected) != 0) + + // Get source and local package info + var packageMetadata = (IPackageSearchMetadata)e.Node.Tag; + var localPackageNode = e.Node.Nodes.Count > 0 ? e.Node.Nodes[Resources.UpdatesNodeName] : null; + var localPackageMetadata = (LocalPackageInfo)localPackageNode?.Tag; + + // Draw package icon + var iconText = Resources.PrereleaseLabel; + var iconTextFlags = TextFormatFlags.NoPadding; + var iconTextSize = TextRenderer.MeasureText(e.Graphics, iconText, iconFont, bounds.Size, iconTextFlags); + var iconImageX = e.Bounds.X + Margin.Left; + var iconImageY = e.Bounds.Top + (e.Bounds.Height - ImageList.ImageSize.Height - iconTextSize.Height) / 2; + var iconImageIndex = ImageList.Images.IndexOfKey(e.Node.ImageKey); + if (iconImageIndex >= 0) { - var font = Font; - var buttonBounds = GetOperationButtonBounds(bounds); - bounds.Width -= buttonBounds.Width + boundsMargin * 2; + ImageList.Draw(e.Graphics, iconImageX, iconImageY, iconImageIndex); + if (localPackageMetadata != null) + { + var iconOverlay = localPackageMetadata.Identity.Version < packageMetadata.Identity.Version + ? Resources.PackageUpdateImage + : Resources.PackageInstalledImage; + DrawImageOverlay(e.Graphics, iconOverlay, iconImageX, iconImageY); + } - if (VisualStyleRenderer.IsSupported) + if (packageMetadata.Identity.Version.IsPrerelease) { - ButtonRenderer.DrawButton(e.Graphics, buttonBounds, OperationText, font, false, PushButtonState.Normal); + var iconTextPosition = new Point( + x: iconImageX, + y: iconImageY + ImageList.ImageSize.Height); + TextRenderer.DrawText(e.Graphics, iconText, iconFont, iconTextPosition, color, iconTextFlags); } - else + } + bounds.X += iconTextSize.Width + Margin.Horizontal; + bounds.Width -= iconTextSize.Width + Margin.Horizontal; + + // Add spacing between text boxes + bounds.Y += boundsMargin; + bounds.Height -= boundsMargin; + + // Draw operation image + bounds.Width -= boundsMargin; + if (nodeHot && OperationImage != null) + { + var mousePosition = PointToClient(MousePosition); + var buttonBounds = GetOperationButtonBounds(e.Node.Bounds); + if (buttonBounds.Contains(mousePosition)) { - var buttonTextSize = TextRenderer.MeasureText(OperationText, font); - var buttonTextOffset = new Point( - buttonBounds.Location.X + (buttonBounds.Size.Width - buttonTextSize.Width) / 2, - buttonBounds.Location.Y + (buttonBounds.Size.Height - buttonTextSize.Height) / 2); - ControlPaint.DrawButton(e.Graphics, buttonBounds, ButtonState.Normal); - TextRenderer.DrawText(e.Graphics, OperationText, font, buttonTextOffset, SystemColors.ControlText); + FillImageBounds( + e.Graphics, + SystemBrushes.ButtonHighlight, + OperationImage, + ref bounds); } + DrawInlineImage(e.Graphics, OperationImage, ref bounds); } + else + bounds.Width -= Size.Round(e.Graphics.GetImageSize(OperationImage)).Width + boundsMargin; + + // Draw package version + var packageVersion = packageMetadata.Identity.Version.ToString(); + var textSize = TextRenderer.MeasureText(e.Graphics, packageVersion, Font); + var textPosition = new Point(bounds.Right - textSize.Width - boundsMargin, bounds.Y); + TextRenderer.DrawText(e.Graphics, packageVersion, Font, textPosition, color); + bounds.Width -= textSize.Width + boundsMargin; + // Draw package warnings + var warningNode = e.Node.Nodes.Count > 0 ? e.Node.Nodes[Resources.PackageWarningKey] : null; + if (warningNode != null) + DrawInlineImage(e.Graphics, Resources.WarningImage, ref bounds); + + // Draw package title + var titleBounds = bounds; + var titleTextFlags = TextFormatFlags.Default; var lines = e.Node.Text.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); - TextRenderer.DrawText(e.Graphics, lines[0], boldFont, bounds, color, TextFormatFlags.WordEllipsis); + var titleSize = TextRenderer.MeasureText(e.Graphics, lines[0], titleFont, bounds.Size, titleTextFlags); + TextRenderer.DrawText(e.Graphics, lines[0], titleFont, titleBounds, color, titleTextFlags); + titleBounds.X += titleSize.Width; + titleBounds.Width -= titleSize.Width; + if (packageMetadata.PrefixReserved) + { + var image = Resources.PrefixReservedMediumImage; + var imageSize = Size.Round(e.Graphics.GetImageSize(image)); + var imagePadding = imageSize.Width / 8; + titleBounds.X -= imagePadding; + var imageY = titleBounds.Y + titleFont.GetHeight(e.Graphics) - imageSize.Height + imagePadding; + e.Graphics.DrawImage(image, titleBounds.X, imageY); + titleBounds.X += imageSize.Width + imagePadding; + titleBounds.Width -= imageSize.Width; + } + + // Draw package authors and download count + titleBounds.Y += titleSize.Height - textSize.Height; + var hasDownloadCount = packageMetadata.DownloadCount.HasValue; + var downloadSeparator = hasDownloadCount ? ", " : string.Empty; + DrawInlineText(e.Graphics, $"by {packageMetadata.Authors}{downloadSeparator}", Font, color, ref titleBounds); + if (hasDownloadCount) + { + var downloadCount = packageMetadata.DownloadCount.GetValueOrDefault(); + DrawInlineText(e.Graphics, FormatHelper.ToLargeSuffix(downloadCount), boldFont, color, ref titleBounds); + DrawInlineText(e.Graphics, " downloads", Font, color, ref titleBounds); + } + + // Line break after title + bounds.Y += titleSize.Height; + bounds.Height -= titleSize.Height; + // Measure package deprecation notice + var deprecationSize = Size.Empty; + var deprecationMetadata = warningNode?.Tag as PackageDeprecationMetadata; + if (deprecationMetadata != null) + { + var notice = Resources.PackageDeprecationNotice; + deprecationSize = TextRenderer.MeasureText(notice, boldFont, bounds.Size, TextFormatFlags.WordEllipsis); + bounds.Height -= deprecationSize.Height; + } + + // Draw package description if (lines.Length > 1) { - bounds.Y += TextRenderer.MeasureText(lines[0], boldFont, bounds.Size, TextFormatFlags.WordEllipsis).Height; - TextRenderer.DrawText(e.Graphics, lines[1], Font, bounds, color, TextFormatFlags.WordBreak); + const TextFormatFlags DescriptionFlags = + TextFormatFlags.TextBoxControl + | TextFormatFlags.WordBreak + | TextFormatFlags.EndEllipsis; + var descriptionSize = TextRenderer.MeasureText(e.Graphics, lines[1], Font, bounds.Size, DescriptionFlags); + TextRenderer.DrawText(e.Graphics, lines[1], Font, bounds, color, DescriptionFlags); + bounds.Y += descriptionSize.Height; + bounds.Height -= descriptionSize.Height; + } + + // Draw package deprecation message + if (deprecationMetadata != null) + { + bounds.Height += deprecationSize.Height; + var notice = Resources.PackageDeprecationNotice; + DrawInlineText(e.Graphics, notice, boldFont, color, ref bounds); + if (deprecationMetadata.AlternatePackage != null && bounds.Width > 0) + { + var alternatePackageId = deprecationMetadata.AlternatePackage.PackageId; + var alternateNoticeParts = Resources.PackageDeprecationAlternateNotice.Split('|'); + DrawInlineText(e.Graphics, alternateNoticeParts[0], boldFont, color, ref bounds); + DrawInlineText(e.Graphics, alternatePackageId, hyperlinkFont, color, ref bounds); + DrawInlineText(e.Graphics, alternateNoticeParts[1], boldFont, color, ref bounds); + } } } base.OnDrawNode(e); } + + protected override void Dispose(bool disposing) + { + if (disposing && nodeHighlight != null) + { + nodeHighlight.Dispose(); + nodeHighlight = null; + } + + base.Dispose(disposing); + } } } diff --git a/Bonsai.NuGet.Design/PackageViewController.cs b/Bonsai.NuGet.Design/PackageViewController.cs index a2c817445..059f29c58 100644 --- a/Bonsai.NuGet.Design/PackageViewController.cs +++ b/Bonsai.NuGet.Design/PackageViewController.cs @@ -2,11 +2,11 @@ using Bonsai.NuGet.Design.Properties; using NuGet.Configuration; using NuGet.Frameworks; +using NuGet.Protocol; using NuGet.Protocol.Core.Types; using System; using System.Collections.Generic; using System.Drawing; -using System.Drawing.Drawing2D; using System.Linq; using System.Reactive; using System.Reactive.Concurrency; @@ -23,7 +23,6 @@ class PackageViewController bool loaded; readonly string packageManagerPath; - readonly IEnumerable packageTypes; readonly PackageSourceProvider packageSourceProvider; readonly List activeRequests; PackageQuery packageQuery; @@ -52,8 +51,7 @@ public PackageViewController( CueBannerComboBox search, CheckBox prerelease, Func updateFeed, - Action multiOperationVisible, - IEnumerable packageTypeFilter) + Action multiOperationVisible) { ProjectFramework = projectFramework ?? throw new ArgumentNullException(nameof(projectFramework)); control = owner ?? throw new ArgumentNullException(nameof(owner)); @@ -65,14 +63,18 @@ public PackageViewController( prereleaseCheckBox = prerelease ?? throw new ArgumentNullException(nameof(prerelease)); getUpdateFeed = updateFeed ?? throw new ArgumentNullException(nameof(updateFeed)); setMultiOperationVisible = multiOperationVisible ?? throw new ArgumentNullException(nameof(multiOperationVisible)); - packageTypes = packageTypeFilter; control.KeyDown += control_KeyDown; + packageDetails.PackageLinkClicked += packageDetails_PackageLinkClicked; prereleaseCheckBox.CheckedChanged += prereleaseFilterCheckBox_CheckedChanged; packagePageSelector.SelectedIndexChanged += packagePageSelector_SelectedIndexChanged; packagePageSelector.Visible = false; packageManagerPath = path; iconReader = new IconReader(packageIcons.ImageSize); + if (packageIcons.Images.Count == 0) + { + packageIcons.Images.Add(iconReader.DefaultIcon); + } activeRequests = new List(); var machineWideSettings = new BonsaiMachineWideSettings(); @@ -81,17 +83,25 @@ public PackageViewController( PackageManager = CreatePackageManager(packageSourceProvider, Enumerable.Empty()); searchComboBox.CueBanner = Resources.SearchCueBanner; packageDetails.ProjectFramework = ProjectFramework; - packageDetails.PathResolver = PackageManager.PathResolver; + Operation = packageView.Operation; } public string SearchPrefix { get; set; } + public IEnumerable PackageTypes { get; set; } + public SourceRepository SelectedRepository { get; set; } public NuGetFramework ProjectFramework { get; private set; } public LicenseAwarePackageManager PackageManager { get; private set; } + public PackageOperationType Operation + { + get => packageView.Operation; + set => packageView.Operation = packageDetails.Operation = value; + } + public void ClearActiveRequests() { activeRequests.RemoveAll(request => @@ -138,7 +148,7 @@ public void OnLoad(EventArgs e) { var selectHandler = SelectPackageDetails() .ObserveOn(control) - .Do(package => packageDetails.SetPackage(package)) + .Do(item => packageDetails.SetPackage(item)) .Select(result => Unit.Default); var searchHandler = Observable.FromEventPattern( handler => searchComboBox.TextChanged += new EventHandler(handler), @@ -200,8 +210,8 @@ QueryContinuation> GetPackageQuery(string se QueryContinuation> GetPackageQuery(SourceRepository repository, string searchTerm, int pageSize, bool includePrerelease, bool updateFeed) { return updateFeed - ? new UpdateQuery(repository, PackageManager.LocalRepository, searchTerm, includePrerelease, packageTypes) - : new SearchQuery(repository, searchTerm, pageSize, includePrerelease, packageTypes); + ? new UpdateQuery(repository, PackageManager.LocalRepository, searchTerm, includePrerelease, PackageTypes) + : new SearchQuery(repository, searchTerm, pageSize, includePrerelease, PackageTypes); } IObservable GetPackageIcon(Uri iconUrl) @@ -233,7 +243,7 @@ private void AddPackageRange(IList packages) if (packages.Count > 0) { if (packages.Count > 1 && packagePageSelector.SelectedPage == 0 && - packageView.OperationText == Resources.UpdateOperationName) + packageView.Operation == PackageOperationType.Update) { setMultiOperationVisible(true); } @@ -252,32 +262,37 @@ private void AddPackageRange(IList packages) private void AddPackage(IPackageSearchMetadata package) { - var installCheck = false; - if (SelectedRepository != PackageManager.LocalRepository && - packageView.OperationText != Resources.UpdateOperationName) + LocalPackageInfo installedPackage = null; + if (SelectedRepository != PackageManager.LocalRepository) { - var installedPackage = PackageManager.LocalRepository.FindLocalPackage(package.Identity.Id); - installCheck = installedPackage != null && installedPackage.Identity.Version >= package.Identity.Version; + installedPackage = PackageManager.LocalRepository.FindLocalPackage(package.Identity.Id); } - var nodeTitle = !string.IsNullOrWhiteSpace(package.Title) ? package.Title : package.Identity.Id; + var nodeTitle = package.Identity.Id; var nodeText = string.Join( Environment.NewLine, nodeTitle, package.Summary ?? package.Description.Split( new[] { Environment.NewLine, "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault()); var node = packageView.Nodes.Add(package.Identity.Id, nodeText); - node.Checked = installCheck; node.Tag = package; + if (installedPackage != null) + { + var installedPackageNode = node.Nodes.Add(Resources.UpdatesNodeName, installedPackage.Identity.Version.ToString()); + installedPackageNode.Tag = installedPackage; + } + + var deprecationMetadata = package.GetDeprecationMetadataAsync().Result; + if (deprecationMetadata != null) + { + var deprecationMetadataNode = node.Nodes.Add(Resources.PackageWarningKey, deprecationMetadata.Message); + deprecationMetadataNode.Tag = deprecationMetadata; + } + var requestIcon = GetPackageIcon(package.IconUrl); var iconRequest = requestIcon.ObserveOn(control).Subscribe(image => { - if (packageIcons.Images.Count == 0) - { - var defaultImage = iconReader.GetDefaultIconAsync().Result; - packageIcons.Images.Add(defaultImage); - } packageIcons.Images.Add(package.Identity.Id, image); node.ImageKey = package.Identity.Id; node.SelectedImageKey = package.Identity.Id; @@ -318,7 +333,7 @@ public void UpdatePackagePage(int pageIndex = 0) if (packageCount == 0) { if (feedExceptionMessage != null) SetPackageViewStatus(feedExceptionMessage); - else if (packageView.OperationText == Resources.UpdateOperationName) + else if (packageView.Operation == PackageOperationType.Update) { SetPackageViewStatus(Resources.NoUpdatesAvailableLabel); } @@ -338,7 +353,7 @@ public void RunPackageOperation(IEnumerable packages, bo IObservable operation; var uninstallOperation = SelectedRepository == PackageManager.LocalRepository; - var update = packageView.OperationText == Resources.UpdateOperationName; + var update = packageView.Operation == PackageOperationType.Update; if (uninstallOperation) { operation = Observable.FromAsync(async token => @@ -404,27 +419,48 @@ private void control_KeyDown(object sender, KeyEventArgs e) } } - private IObservable SelectPackageDetails() + private void packageDetails_PackageLinkClicked(object sender, PackageSearchEventArgs e) + { + searchComboBox.Text = e.SearchTerm; + } + + private IObservable SelectPackageDetails() { return Observable.FromEventPattern( handler => packageView.AfterSelect += handler, handler => packageView.AfterSelect -= handler) .Select(evt => Observable.StartAsync(async token => { + var selectedNode = evt.EventArgs.Node; var selectedRepository = SelectedRepository; - var package = (IPackageSearchMetadata)evt.EventArgs.Node.Tag; - if (package == null) return null; + var selectedPackage = (IPackageSearchMetadata)selectedNode.Tag; + if (selectedPackage == null) return null; + + var localPackageNode = selectedNode.Nodes.Count > 0 ? selectedNode.Nodes[Resources.UpdatesNodeName] : null; + var localPackage = (LocalPackageInfo)localPackageNode?.Tag; + var repositories = selectedRepository == null ? PackageManager.SourceRepositoryProvider.GetRepositories() : new[] { selectedRepository }; using (var cacheContext = new SourceCacheContext()) { foreach (var repository in repositories) { - var metadata = await repository.GetMetadataAsync(package.Identity, cacheContext); + var metadata = await repository.GetMetadataAsync(selectedPackage.Identity, cacheContext); if (metadata != null) { + var imageIndex = packageView.ImageList.Images.IndexOfKey(selectedNode.ImageKey); var result = (PackageSearchMetadataBuilder.ClonedPackageSearchMetadata)PackageSearchMetadataBuilder.FromMetadata(metadata).Build(); - result.DownloadCount = package.DownloadCount; - return result; + var packageVersions = await selectedPackage.GetVersionsAsync(); + var deprecationMetadata = await result.GetDeprecationMetadataAsync(); + result.PrefixReserved = selectedPackage.PrefixReserved; + result.DownloadCount = selectedPackage.DownloadCount; + return new PackageViewItem( + selectedPackage: result, + packageVersions, + deprecationMetadata, + repository, + localPackage, + packageView.ImageList, + imageIndex); } } diff --git a/Bonsai.NuGet.Design/PackageViewEventArgs.cs b/Bonsai.NuGet.Design/PackageViewEventArgs.cs new file mode 100644 index 000000000..b1adacdcf --- /dev/null +++ b/Bonsai.NuGet.Design/PackageViewEventArgs.cs @@ -0,0 +1,20 @@ +using System; +using NuGet.Protocol.Core.Types; + +namespace Bonsai.NuGet.Design +{ + internal delegate void PackageViewEventHandler(object sender, PackageViewEventArgs e); + + internal class PackageViewEventArgs : EventArgs + { + public PackageViewEventArgs(IPackageSearchMetadata package, PackageOperationType operation) + { + Package = package; + Operation = operation; + } + + public IPackageSearchMetadata Package { get; } + + public PackageOperationType Operation { get; } + } +} diff --git a/Bonsai.NuGet.Design/PackageViewItem.cs b/Bonsai.NuGet.Design/PackageViewItem.cs new file mode 100644 index 000000000..6c41b4da7 --- /dev/null +++ b/Bonsai.NuGet.Design/PackageViewItem.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; +using NuGet.Packaging.Core; +using NuGet.Protocol; +using NuGet.Protocol.Core.Types; + +namespace Bonsai.NuGet.Design +{ + internal class PackageViewItem + { + public PackageViewItem( + IPackageSearchMetadata selectedPackage, + IEnumerable packageVersions, + PackageDeprecationMetadata deprecationMetadata, + SourceRepository sourceRepository, + LocalPackageInfo localPackage, + ImageList imageList, + int imageIndex) + { + SelectedPackage = selectedPackage; + PackageVersions = packageVersions; + DeprecationMetadata = deprecationMetadata; + SourceRepository = sourceRepository; + LocalPackage = localPackage; + ImageList = imageList; + ImageIndex = imageIndex; + } + + public IPackageSearchMetadata SelectedPackage { get; } + + public IEnumerable PackageVersions { get; } + + public PackageDeprecationMetadata DeprecationMetadata { get; } + + public SourceRepository SourceRepository { get; } + + public LocalPackageInfo LocalPackage { get; } + + public ImageList ImageList { get; } + + public int ImageIndex { get; } + + public async Task GetVersionMetadataAsync(VersionInfo versionInfo, CancellationToken cancellationToken = default) + { + using var cacheContext = new SourceCacheContext(); + var identity = new PackageIdentity(SelectedPackage.Identity.Id, versionInfo.Version); + return await SourceRepository.GetMetadataAsync(identity, cacheContext, cancellationToken); + } + } +} diff --git a/Bonsai.NuGet.Design/Properties/Resources.Designer.cs b/Bonsai.NuGet.Design/Properties/Resources.Designer.cs index cdca4e43e..9f8f5ab2c 100644 --- a/Bonsai.NuGet.Design/Properties/Resources.Designer.cs +++ b/Bonsai.NuGet.Design/Properties/Resources.Designer.cs @@ -97,6 +97,16 @@ internal static string DependencyLicenseWarningLabel { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap DownloadImage { + get { + object obj = ResourceManager.GetObject("DownloadImage", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized string similar to Exporting.... /// @@ -151,15 +161,6 @@ internal static string InstallOperationLabel { } } - /// - /// Looks up a localized string similar to Install. - /// - internal static string InstallOperationName { - get { - return ResourceManager.GetString("InstallOperationName", resourceCulture); - } - } - /// /// Looks up a localized string similar to Installing '{0}'.. /// @@ -196,6 +197,25 @@ internal static string LicenseAuthorshipLabel { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap LicenseImage { + get { + object obj = ResourceManager.GetObject("LicenseImage", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized string similar to License. + /// + internal static string LicenseLabel { + get { + return ResourceManager.GetString("LicenseLabel", resourceCulture); + } + } + /// /// Looks up a localized string similar to View License. /// @@ -287,11 +307,12 @@ internal static string OnlineNodeName { } /// - /// Looks up a localized string similar to Open. + /// Looks up a localized resource of type System.Drawing.Bitmap. /// - internal static string OpenOperationName { + internal static System.Drawing.Bitmap OpenImage { get { - return ResourceManager.GetString("OpenOperationName", resourceCulture); + object obj = ResourceManager.GetObject("OpenImage", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); } } @@ -304,6 +325,16 @@ internal static string PackageAlreadyInstalled { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap PackageDefaultIcon { + get { + object obj = ResourceManager.GetObject("PackageDefaultIcon", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized string similar to Package '{0}' depends on the following packages. Do you want to uninstall them too? ///. @@ -314,6 +345,33 @@ internal static string PackageDependencyNotice { } } + /// + /// Looks up a localized string similar to Use | instead.. + /// + internal static string PackageDeprecationAlternateNotice { + get { + return ResourceManager.GetString("PackageDeprecationAlternateNotice", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This package has been deprecated as it is legacy and is no longer maintained.. + /// + internal static string PackageDeprecationDefaultMessage { + get { + return ResourceManager.GetString("PackageDeprecationDefaultMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This package version is deprecated.. + /// + internal static string PackageDeprecationNotice { + get { + return ResourceManager.GetString("PackageDeprecationNotice", resourceCulture); + } + } + /// /// Looks up a localized string similar to Package '{0} {1}' exported successfully.. /// @@ -326,15 +384,83 @@ internal static string PackageExported { /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// - internal static System.Drawing.Bitmap PackageViewNodeCheckedImage { + internal static System.Drawing.Bitmap PackageImage { + get { + object obj = ResourceManager.GetObject("PackageImage", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap PackageInstalledImage { + get { + object obj = ResourceManager.GetObject("PackageInstalledImage", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized string similar to The ID prefix of this package has been reserved for the owner of the package by the currently selected feed. + /// + internal static string PackagePrefixReservedToolTip { + get { + return ResourceManager.GetString("PackagePrefixReservedToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap PackageRemoveImage { + get { + object obj = ResourceManager.GetObject("PackageRemoveImage", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap PackageUpdateImage { get { - object obj = ResourceManager.GetObject("PackageViewNodeCheckedImage", resourceCulture); + object obj = ResourceManager.GetObject("PackageUpdateImage", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// - /// Looks up a localized string similar to (Prerelease). + /// Looks up a localized string similar to Warning. + /// + internal static string PackageWarningKey { + get { + return ResourceManager.GetString("PackageWarningKey", resourceCulture); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap PrefixReservedImage { + get { + object obj = ResourceManager.GetObject("PrefixReservedImage", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap PrefixReservedMediumImage { + get { + object obj = ResourceManager.GetObject("PrefixReservedMediumImage", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized string similar to Prerelease. /// internal static string PrereleaseLabel { get { @@ -435,15 +561,6 @@ internal static string UninstallOperationLabel { } } - /// - /// Looks up a localized string similar to Uninstall. - /// - internal static string UninstallOperationName { - get { - return ResourceManager.GetString("UninstallOperationName", resourceCulture); - } - } - /// /// Looks up a localized string similar to (unpublished). /// @@ -463,29 +580,40 @@ internal static string UpdateOperationLabel { } /// - /// Looks up a localized string similar to Update. + /// Looks up a localized string similar to Updates. /// - internal static string UpdateOperationName { + internal static string UpdatesNodeName { get { - return ResourceManager.GetString("UpdateOperationName", resourceCulture); + return ResourceManager.GetString("UpdatesNodeName", resourceCulture); } } /// - /// Looks up a localized string similar to Updates. + /// Looks up a localized resource of type System.Drawing.Bitmap. /// - internal static string UpdatesNodeName { + internal static System.Drawing.Bitmap WaitImage { get { - return ResourceManager.GetString("UpdatesNodeName", resourceCulture); + object obj = ResourceManager.GetObject("WaitImage", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// - internal static System.Drawing.Bitmap WaitImage { + internal static System.Drawing.Bitmap WarningImage { get { - object obj = ResourceManager.GetObject("WaitImage", resourceCulture); + object obj = ResourceManager.GetObject("WarningImage", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap WebImage { + get { + object obj = ResourceManager.GetObject("WebImage", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } diff --git a/Bonsai.NuGet.Design/Properties/Resources.resx b/Bonsai.NuGet.Design/Properties/Resources.resx index da2bf1d0a..f284aaa67 100644 --- a/Bonsai.NuGet.Design/Properties/Resources.resx +++ b/Bonsai.NuGet.Design/Properties/Resources.resx @@ -129,18 +129,12 @@ Installing... - - Install - Online Uninstalling... - - Uninstall - Updates @@ -158,7 +152,7 @@ - (Prerelease) + Prerelease (unpublished) @@ -166,14 +160,11 @@ Updating... - - Update - - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO - wQAADsEBuJFr7QAAANdJREFUOE+dU7kNwzAM1CjZwQtpBA/gwqVX8CKpAggI4FojuHNrwIVahZeQhh7a + vAAADrwBlbxySQAAANdJREFUOE+dU7kNwzAM1CjZwQtpBA/gwqVX8CKpAggI4FojuHNrwIVahZeQhh7a cXLAFSJ5Rz2UKRFCsERHjAURs1xWg5I3op/mKbb3NjZjkxEx5FCDWpZ9wOK1e3SVsOTwHGCyZia08Eho Ao1s4kVssTWtUNi7PgLLtuwxPo6FgdPOnBJCAEYSgwZaGGTFJbXuQmi/GmjdhapBKjjrDqoGqeisOygG 1SWKEDjqnl5i9YyyC+Co+/6MPAv+yhQKs0ECaPEe5SvTqI4ywCb/faYUlPzhOxvzAkO1WA01cJaNAAAA @@ -186,6 +177,9 @@ View License + + License + '{0}' already installed. @@ -198,7 +192,7 @@ iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO - wAAADsABataJCQAAABp0RVh0U29mdHdhcmUAUGFpbnQuTkVUIHYzLjUuMTFH80I3AAABzElEQVRYR82X + vAAADrwBlbxySQAAABp0RVh0U29mdHdhcmUAUGFpbnQuTkVUIHYzLjUuMTFH80I3AAABzElEQVRYR82X y23DMAyGM0JH6QhF7hkgm6RZwsgMzQa5uyPk3Pba9uoWyOOo6gtElZEpy3aBuAQ+IBHJn4xedmbOuUkx B2/J74eCHQ6H+9PpVHn2HmfAeEVcSMnaoAbO5/PD8Xh8Top1Qjx5QaJlvRvwYvxis0hPqiB1ZcUGmqa5 88mtqd7tdm69Xrv5fN6CcfxpjmePXpC+WLEBkrRIXdduuVyahVOII17ne/ZB+mKdDfjgq2nfbDZmoRLk @@ -221,9 +215,6 @@ Exporting... - - Open - Unable to open gallery package. No '{0}' was found in package contents. @@ -276,7 +267,7 @@ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO - wwAADsMBx2+oZAAAANhJREFUOE+lU7ERgzAMZBRmoad3SeGx6BkjTShcOSN4A5KG1tErep9DihxYd39n + vAAADrwBlbxySQAAANhJREFUOE+lU7ERgzAMZBRmoad3SeGx6BkjTShcOSN4A5KG1tErep9DihxYd39n Sf9vLOzuGPu+T4KbIB+A2mS035BmL4hGzimlPAyDAmvWjdOb7BMoCLZlWXIIQYlY02CeZ62hhzq40Jhc DWIt8N7ncRxLjjVqzM0kUowzqzsJ/8CvhBYGZWDcBTuu9zU/t5cCa34ROORDCwMdEs5JEsUxPhQ0oTm4 HKwaoFGDYuY0qTnAaQPn3BevGLQeoXmIbb/R7sL1i4SQpO0qI1AQXHtMdUjzxHPuujckkIG3YzbPFgAA @@ -289,7 +280,7 @@ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO - wQAADsEBuJFr7QAAAJpJREFUOE+lzgEJwCAQhWFT2MEIhjCDMWxgB2uYwwoWsMSNd+xExpQ5fxDm2H07 + vAAADrwBlbxySQAAAJpJREFUOE+lzgEJwCAQhWFT2MEIhjCDMWxgB2uYwwoWsMSNd+xExpQ5fxDm2H07 RYcxEELYPlIHdvoFxBip1srP20DOmZRSpLXm+xbQWuNBANgCLQF8VEq5b0TOOR42xtxvFkBKqa8KRFbH GdEpgHXxJxl6ri5NAQTEWtuRcXVpCUjeewbG1aVPAHobRp+BWa/A7pEYOOkQILoAJ5VmTOFnoT4AAAAA SUVORK5CYII= @@ -298,8 +289,170 @@ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO - wQAADsEBuJFr7QAAAC1JREFUOE9j+E8hABtQXl5OMoYBuAGkgFEDRg0AgUFqAKkYBsAGUAIoNOD/fwB6 + vAAADrwBlbxySQAAAC1JREFUOE9j+E8hABtQXl5OMoYBuAGkgFEDRg0AgUFqAKkYBsAGUAIoNOD/fwB6 Pq+umpazgQAAAABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1 + MAAA6mAAADqYAAAXb5JfxUYAAAAJcEhZcwAADrwAAA68AZW8ckkAAACgSURBVDhPpZCBEcMgCEUd1YG7 + QwfIEJRPkAMBm2u9e7mI+vg6iOgv7k8z6DXeQKdpHAV8cDKkTC2H8U2A7ktQpmgF6OgOL1KKk0C64xeo + IKUoBeikB3YBCCk6gd0dU7DmWJNNOpKAN4S7owR8jbEUlcC/fCewFEHAC9XLd0iKXRC6A5Rl31ZnJIUJ + uFB2PwjA9ILU/RFOcKXFJ5jgZ2h8ADPx6vIlyTDgAAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + vAAADrwBlbxySQAAAKFJREFUOE+lkUEKwzAMBPM2P87f6C99dbPCKrOKS1K6IGI5O0NpjjnnX+MLMsaY + nHUdMcYWRFBrLeax4Cy+VuerQB0yVaBySHYCvdOZzE4QEj0pyDsNmYug955AFXzekbkIBGSRAt3lmcxW + kJLd+VbAcp38ZWSqIP6onSRhdciYgJ+KEsLqGGPLCiUVVoyxBUlJhRVjbClZEoMVY7j8PvN4A80ziomU + 990nAAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + vAAADrwBlbxySQAAAHxJREFUOE/NjLENgDAMBLMAG1CzAAMjGoahYQs2cA16FAfHeiMQBZxkQV73n/6H + iAy4/CxEeQWEdZk3nJWjvEKlqW+PUznKc+3EimPXFJlldACwkdtlxY88KoNXA76Mr/2/HGFlvFlGR+yA + FaM812pU9lKUUyAwKcq/JqUdDDg6HDyy1HgAAAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + vAAADrwBlbxySQAAARRJREFUOE+dk09qAjEYxecEXkBBcNcLzMazeRF3ooOrXqFLS8G1OxH8QymKKzuF + 2cT8xu9JkqZCffAgyffem3yZpEhR13XpOfbceDZGxqyVJvsNX+x4VuvTxY3ej274enD96bYlY9aooUFr + thvM/DFbnd1gtnPdyTZLamjQRiF+UlFIDW+7b7f8+nG9ZN1CKplLtpZ+GbOQhqC1dkoCxvT3l1lIQ/Dg + JWDDIT0yC4RIhwcvAQ0nrYKAWFh81jZydx0evNkAbVd4me/vO8sFRC3wNfUqEMCcEOnCFqJDDA9KUEDI + 8BCzvxEKaUD0G+0uVNPMRRLSALR4WjPwk/YqU8jtRKRm5vgqAwt57jGF8MV/POeiuAIJP8MXc9uPMgAA + AABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + vAAADrwBlbxySQAAAQJJREFUOE9jIBZ8+/aNA8okHQA1VwDxZSAWhQoRD0CaL99/+b9v44X/JBsCVJz8 + /M2H/1Ipy/4zBM/537TyDHGGABVwArEAEKsC8R2QRpABeA0BCmgC8VIgfg3EIEUoGK8hQEbwi7cfv+fP + PfpfInkpXCE+DDMEbDNIs07BGqwKcWFkA5aCbAYJgmxff/zOf1DAoePi+ccwNAOxKMiA1zBngzQD+V1A + zAP1migQXybkf7gkyCYgXwIsAQRANuFoBHKwuUAEiDmh8vgTElAAIwzefvgMUjwBqgRsCFbNIAAUxIiF + 7FmHUQwAASAfd2YCSqKkA2wGEAQglwAxckokwgAGBgCkhsE8CrHMLAAAAABJRU5ErkJggg== + + + + Warning + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6 + JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAA68AAAOvAGVvHJJAAAAwElE + QVQ4T62TTQrCMBBGcwcFTQL5WbjxFvV8XXWh9Aa9Rak38BJuVMStm/p9EGkQlWl14JFJJvOalkb9LWKM + DWiFNKltCBaMMTMJ3JvahsgFzjmNeR1CuGG8Ytx575diARq2oLXWFmgsUOu4NkZwYSNOsk5ssHYeIzi9 + ERzFAuQV6JKEzXtQigX8YJj3PDZhDhZiQdrUP1+B+UtNJsDTD2SSAI13rNWEeV4TCfAPrLTWc8I8r4kE + 3/gk+O0yTQulHhgvf0hB5dCZAAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + vAAADrwBlbxySQAAAThJREFUOE+dk01ugzAQhTlBj+MDIJC4A1yEa7DMnlWaSiAu0ZI7BNi0XaRpF7Bg + 4/pzbMsQ0qod6SX2zHvPP4yDdUzTJBR2CieF2YAxOWFot6GKDwrlMAyyKAqZZZkMw1AjTVOdowYHrpFd + w4if67p2os/L1wI2DwfuwkRNSitu2+NdA1szJqUVC7ZGYb9/dOQtA/6bptFjcxyBwY7zkfwL0KDF4ESC + 7bHCx/miCf7qYJ1jjjYYx3Fm0nfDXfJWzhjMzuBweJJvr++b5K1dOQN7hP9AH0H96EvM83zh7q+2zsH1 + L1H0fS+TJHEX+ZsBXDRobS/oRorjWB5/aqSXVkZRJKuqQnxtJEJNXCvjTu9D9kGOmhEvW5kwJiVb43wI + WBXYx9R1nV75RuyHKrrnzCcGjE1u9ZyD4BugoZigQ9xrngAAAABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + vAAADrwBlbxySQAAAJ1JREFUOE+1j7ENxCAMRTPCjXCz0LMFg6SivQXY5ZZgCwouDa3j78MKikBRFIH0 + JGP7P8kLEQn6SikrQ0xmXhXU6K117chpwcM3840xknOOQggIfABq9DDDDnZ7goxFY4xgraWUkoBa+1Wc + ewIZ6iLw3gttrwo40hEAPaENgeaEseAcGjFf8MubMPrPF1wxX/D4hMeCK+YJ7vDP0bIDFVes7Wn8RVwA + AAAASUVORK5CYII= + + + + Use | instead. + + + This package version is deprecated. + + + This package has been deprecated as it is legacy and is no longer maintained. + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + vgAADr4B6kKxwAAAAMtJREFUOE/NkssNwjAQRFNKSnAjnOmCWujAB0rgwpEOuNEDCLAUyVIsIZl90Zo4 + zucABxhplM16Z3b9qf4PIYRaaIVOGJXE5Gotm4YUmIdv3e7cxPXhFlf7a0dicmpktHwI3CnYHO9vYUnW + 1GQ8iSQtXaaEF//sSKyTWJX1wDkfO3F7aiLgyz811Kqsh/PtSAzz7oliEFXWIzdIorL7okG+hSQEZffZ + LUhycIgIQdl96RC/u0YgC4YCuuQ3Qqyd5x9SAu7Cz57yD1BVL1pyspuCvJmtAAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + vwAADr8BOAVTJAAAASlJREFUOE+lk8tNw0AQhl0B3bgMECDRAtRBGVgc3QFYcEHckHKOOJtTRGKEWCBK + bBDDfM7Y2k3W5sBIvx878/32PibZjrquU1WmKtd10yCebSy1st3Q5J4qn7mVXEzf5fTuRQ6LqhXPjJGj + hlrDNmHw5Lb8lGMF9q8WUZGjhtrARF9yEjEoJjPJOzjl18a+jI6KhTzMVnJyU7W1Np0Ug4z5xaBOwM/L + byHmemcMBhaDkkXahjr5MHH5+NGOw8AmbBMr3QFn969/wghGDZrAoCiX8mOFY3Bg4E+hdF9WLuLWwzDq + p6CXfhEPrit5co1hm4jBKJu6fhGDbfRNhuBgG+0sBAcJk/PJWwD5Cg4SoS//O8qEmQw2E3O2395tJj80 + 2bezilYeaeck+QVT79qQP637QAAAAABJRU5ErkJggg== + + + + The ID prefix of this package has been reserved for the owner of the package by the currently selected feed + + + + iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABGdBTUEAALGPC/xhBQAACmJJREFUeF7t + nXnsJEUVx0s8IAoqHqCgICLeiKioMd5R7OqZ+bk/8GcgSlyPoARNPPgDNZp+3fO7XASzYXEXNOBCoqya + eICoiGt24wEerKLZRRAk6mpwUeKurq7i+l5NLepv3vRMn1PV/b7JJzO/ya+rX1W9vqqr3lMikUg0Rvv3 + P0BF0UHmU9QSdZNXqjBapwK4A/m3CqL9A2AnslHpZJWK9h9k/1vUGHXjk7CjN/+3w1PQ0S2qk7zGbiny + XiGcqTT8je3skeDZQccfsiWIvFUYv/H/T/UZ0fFHbUki79SZf3b2I38ldCaAni1R5JV09A2+UzOi4XZ1 + 9oYH21JFXqjTfwnbmXkJ43fYkkVeKICL2Y7Mi45usCWLvFAA29iOzM9eGTDySUG0e0UHFkfuAzyShn+x + nVgEvfZgW7rIeQXRPUMdWJQoepAtXeS8gui7Qx1YBA1/siWLvFAQJUOdWAQNX7Ali7xQNzmh1PuADnRs + ySJvpOFStjOzoqOttkSRV5pdcwR23m/YTp0Y2KOC+efYEkXeSccnY0fmGxMwlxCYtSWJvJV5Kxj9iu3k + UdBdfwivtSWIvNfc0iPwaF5AR/gr2+EHoKM+hE+pbv9ou6WoUSJHoHl/OroILw9fRm7A79dix1+CvEmF + H3uc/U+RSCRyUTPLR5mb2078MhUkIV6yTsez2hucoJOcpjrwOjMfI4xPVJ3Fw63VolwK+8diQ67Gy9MV + yI+Qv7D3Li6j4W68zG7Fz7XmqWruwkfZ2olYzSw+BY/qCBvsl2yDeg/Ns4y+j3U8V80uPNrWWqTCpGuO + FLbRmgrsw8+rVTj/XNsKLdTgqeSnw43TMszTGN7btEb0wiqIrhtqiDYzGH29WK266JG2lRqqMH4/Vvbv + bCMI5Ah/ME8TjRPd9Ojoq2ylhRWYRTlLKtrckJlY5pRPq5K5ygopXK9mlg+zreipuv0X4pH/R6ZywiRo + +ImamT/StqZnCuMXYSXKn6beNnR0q9ILj7Wt6ok6yTPwtL+LrZCQhx/7czmgU1bhGUkCw/VqbtMDbSs7 + Kgozo+FbjPFCOSS2pR0VjeXzhgulQI+Iyam2tR0TjWsXmZJuglrANfh5AfIB/P52FcTn4/e1eEnZitzH + btc2NPxWza071La6I6IVxPSmizN4PDdiZ8+q3oaH2tJ49S54jNLx2dgAv2bKaBsft63iiMzRyhqaAuw0 + kzqyihar0pAyLV1ny20F/1S9+Fm2RaYsWj6e/ai80czwKSIaZAqi360ot01cbVtiytLx2xjjUoAtavXl + h9iti0kvPwHLbKcT0D1Rb+HptiWmqAB2sAaywB3mWl6mwv4L8AxUMCqap+jo07YVpqSsQano/6tQAO9j + 99d8do+9ea5UAaxnjOKpcqn53KaH4NGQbRVUY4jfbFuhZtGwJC0lY41ioKVqVYpC2nH7bTo0pWwqCuJT + WINY8D6haplJJyXGRvAG2DOdQF40SscaxKCjZbtVtdLwHXb/jaf/UtsCNSrLxM4QzrJbVauyA2X6wlQi + vOvoLtYYDp282m5VraghuP03HQ1X2RaoSfTokSUcfV0LIML4Xez+mw4tn6tVFD6GM2QUdQWd0PARdv9N + h9ZO1qoOvIo1ZCTwFrtltdLRJ/n9t4BanwQ6yQxrxCgoMEUdyv9K2n9qXZJOUUU4I0Zzp92yOp02//hM + 9yVNo7d0jG2JGhTCW1kjUolPsVtXIw3v4ffbEmh5fW3K4wCUyqYqDZ5KdrL7bQvOOwBR1cJHynLG7a9N + eOEAtPr19f0n2lLKEQ0y0RQpbn9twgsHMMA2E7auDNEKpCxvJJuMPw5AwA4VLjzVlpZPdDnR0Z/58luI + Xw5AwL149J6TeR08RdGgt4utfPWbgn8OcD+3YXnvHButlCZADoZ6y0+L0wQ8dgCLiYxxE35uNEd4CB9E + LsTfPod/38pvI9yP/w4gFEIcoOWIA7QccYCWIw7QcsQB6gT24ZPJXfiE8gPkK/j3Fvy8HX9Lz8xSJeIA + FWLC1cMm/BxkV0nLqG7S9CQhOsMG3Ka+N5TiABVARzglz6SlZ3lEjkLh8mhZHFd+mYgDlAnsMFlAytQg + nsHm4X2VhDhACdCaew0frjROr0kzU0EGFHGAglCnaOjZGlYrCu9S9mpmcYACUGfohWfa2tUjyiEUwLdZ + e/IgDpCbe+ptvP/RWWsehs53M2NTdsQB8oDP87TIZZqiKXI0VY61LwPiADmgeQguSPdfjE5QLJOKOEBG + KKaxS9LRPGvnpIgDZIEmnziWqUuvfTg6Qf7kGuIAGQijK21N3FKR1U3iAJOCR38vOc7WxC2ZqKs5cy2I + A0wIje+7LJNrmLF7HOIAkxKfb2vhpgYrnRi7xyAOMCFOxNdNEb2HyDP1XRxgAmgShw/S0edZ+9MQB5gE + 2GJr4LYoawprfwriABNAi0x80CAhBl+HUYgDTACtNPJBQXwGa38aNTvAmawRzuP4E8AB5XkSKDvuQqrC + pMsa4TqUldsHURItzv40aNV0bdLxK1gjnAc22hq4rRDO5e1PodasojR7hjPCdVx7AzhKASyw9o+CoqTU + Kkrd5meAhu22Bm5LwxWM7aOhAJm1i1bCcMa4DM36nV1zhK2Bu8o6YZQcpnYF8CXWGNeh1DIuK4xPZO1O + Q8fn2a1rlI7fyxrjPHCNrYGbyhPzkFLn1a6sIeNdgebezSwfZmvhnrLOEqZIaZS2v3YNEkb/njXKdSjN + vYvSoFl706gyHd9YURZrzijngT1jo5LVLTqKdXQLb28aMGtLmIJ8vQwYYL2thRvKnIOZgF25Vy6XJspX + wxrnODSOQfPxXdDM/JHYmdljCWj4hC1higrhdNY4H6gieHVWmUG1HJlOdPSPqdtuNLgZ/AVrpA/QXTet + 0ZuWKOUbZ9c4NFxqS3BAFEyBM9IXKP9u3U5gDhxYYu0ZB6XLX7X4JFuSI9LwNdZYb4BtKuwfa2tTrcjZ + guiLwzZMCAWxcE6dxSejcXuHjPUJDXejE7zc1qga0ZGr4Wfs/idj+/Tv/EeJwr/zRvvD4C3nZarbP9rW + qhzNrTvUDPMWCQ9DN35TGfbNIpp4yRnvG3SdpWt00Xx8dLTSBI8yYgDo+N22VIdF4+y5RrQchY66AL6O + HXjOxGcFEyswPgO3/Sxuey9bblZqTxBdRDPLR6HRdw5Vohlsx874JnbuZ1QYLZop3Bpi/P0y/O1a5Gbs + 9H0rtikIOmCtqWHLUDc5oZTTXuuB7011nKKQdHI8VuK24UoJE3Kdv51/QDQFS0c/ZConpEGXmCoDVtYq + Gu8Oo3VsRYWV7DU3nI2UeXEEu5hKCwb4uerGJ9nWaqgGETLX4ynuPr4RWslufMY/z787/SLqxM/HUx0l + W2hvzv/B6f6S0kcdvRJNg6aoXTTyxjdS86B3DiY3omPT0qYqGkGkZec0yOL7SyUOWr5FI4OUgKIxd/dV + afXlh5gYvjR7l16bDoaWPXIKkxf5JuSqwUhhfPJ0pm43SdSA9GKmt3SMWZxK9xD0ZswFevPPUzPJ08y1 + nCJ+ikQiUc1S6j/KIYhc3IWgSwAAAABJRU5ErkJggg== \ No newline at end of file diff --git a/Bonsai.NuGet/Constants.cs b/Bonsai.NuGet/Constants.cs index 274c4283f..0deb1ebc7 100644 --- a/Bonsai.NuGet/Constants.cs +++ b/Bonsai.NuGet/Constants.cs @@ -4,8 +4,10 @@ public static class Constants { public const string BonsaiDirectory = "Bonsai"; public const string GalleryDirectory = "Gallery"; + public const string ExtensionsDirectory = "Extensions"; public const string BonsaiExtension = ".bonsai"; public const string LayoutExtension = ".layout"; + public const string LibraryPackageType = "BonsaiLibrary"; public const string GalleryPackageType = "BonsaiGallery"; } } diff --git a/Bonsai.NuGet/LicenseAwarePackageManager.cs b/Bonsai.NuGet/LicenseAwarePackageManager.cs index b845f6154..34b2aefe6 100644 --- a/Bonsai.NuGet/LicenseAwarePackageManager.cs +++ b/Bonsai.NuGet/LicenseAwarePackageManager.cs @@ -1,5 +1,4 @@ using NuGet.Configuration; -using NuGet.Protocol.Core.Types; using System; using System.Collections.Generic; @@ -24,7 +23,7 @@ public LicenseAwarePackageManager(ISettings settings, IPackageSourceProvider pac public event EventHandler RequiringLicenseAcceptance; - protected override bool AcceptLicenseAgreement(IEnumerable licensePackages) + protected override bool AcceptLicenseAgreement(IEnumerable licensePackages) { var eventArgs = new RequiringLicenseAcceptanceEventArgs(licensePackages); RequiringLicenseAcceptance?.Invoke(this, eventArgs); diff --git a/Bonsai.NuGet/PackageHelper.cs b/Bonsai.NuGet/PackageExtensions.cs similarity index 52% rename from Bonsai.NuGet/PackageHelper.cs rename to Bonsai.NuGet/PackageExtensions.cs index 30bb95611..a43098e4f 100644 --- a/Bonsai.NuGet/PackageHelper.cs +++ b/Bonsai.NuGet/PackageExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; @@ -9,23 +10,63 @@ using NuGet.Frameworks; using NuGet.Packaging; using NuGet.Packaging.Core; +using NuGet.Protocol; using NuGet.Versioning; namespace Bonsai.NuGet { - public static class PackageHelper + public static class PackageExtensions { + const string PackageTagFilter = "Bonsai"; public static readonly string ContentFolder = PathUtility.EnsureTrailingSlash(PackagingConstants.Folders.Content); - public static bool IsExecutablePackage(PackageIdentity package, NuGetFramework projectFramework, PackageReaderBase packageReader) + public static bool IsPackageType(this LocalPackageInfo packageInfo, string typeName) { - var entryPoint = package.Id + Constants.BonsaiExtension; + return packageInfo.Nuspec.IsPackageType(typeName); + } + + public static bool IsPackageType(this PackageReaderBase packageReader, string typeName) + { + return packageReader.NuspecReader.IsPackageType(typeName); + } + + public static bool IsPackageType(this NuspecReader reader, string typeName) + { + return reader.GetPackageTypes().IsPackageType(typeName); + } + + public static bool IsPackageType(this IReadOnlyList packageTypes, string typeName) + { + if (packageTypes.Count == 0 + && PackageType.PackageTypeNameComparer.Equals(typeName, PackageType.Dependency.Name)) + { + return true; + } + + return packageTypes.Any(type => PackageType.PackageTypeNameComparer.Equals(type.Name, typeName)); + } + + public static bool IsLibraryPackage(this PackageReaderBase packageReader) + { + return packageReader.IsPackageType(Constants.LibraryPackageType) + || packageReader.NuspecReader.GetTags()?.Contains(PackageTagFilter) is true; + } + + public static bool IsGalleryPackage(this PackageReaderBase packageReader) + { + return packageReader.IsPackageType(Constants.GalleryPackageType) + || packageReader.NuspecReader.GetTags()?.Contains(PackageTagFilter) is true; + } + + public static bool IsExecutablePackage(this PackageReaderBase packageReader, PackageIdentity identity, NuGetFramework projectFramework) + { + var entryPoint = identity.Id + Constants.BonsaiExtension; var nearestFrameworkGroup = packageReader.GetContentItems().GetNearest(projectFramework); - var executablePackage = nearestFrameworkGroup?.Items.Any(file => PathUtility.GetRelativePath(PackageHelper.ContentFolder, file) == entryPoint); - return executablePackage.GetValueOrDefault(); + var executablePackage = nearestFrameworkGroup?.Items.Any(file => PathUtility.GetRelativePath(ContentFolder, file) == entryPoint); + return IsGalleryPackage(packageReader) && executablePackage.GetValueOrDefault(); } - public static string InstallExecutablePackage(PackageIdentity package, NuGetFramework projectFramework, PackageReaderBase packageReader, string targetPath) + public static string InstallExecutablePackage(this PackageReaderBase packageReader, PackageIdentity package, NuGetFramework projectFramework, string targetPath) { var targetId = Path.GetFileName(targetPath); var targetEntryPoint = targetId + Constants.BonsaiExtension; @@ -71,30 +112,29 @@ public static string InstallExecutablePackage(PackageIdentity package, NuGetFram return effectiveEntryPoint; } - public static async Task StartInstallPackage(this IPackageManager packageManager, PackageIdentity package, NuGetFramework projectFramework) - { - if (package == null) - { - throw new ArgumentNullException(nameof(package)); - } - - packageManager.Logger.LogInformation(string.Format(Resources.InstallPackageVersion, package.Id, package.Version)); - await packageManager.InstallPackageAsync(package, projectFramework, ignoreDependencies: false, CancellationToken.None); - } - - public static async Task StartInstallPackage(this IPackageManager packageManager, string packageId, NuGetVersion version, NuGetFramework projectFramework) + public static async Task InstallPackageAsync( + this IPackageManager packageManager, + string packageId, + NuGetVersion version, + NuGetFramework projectFramework, + CancellationToken cancellationToken = default) { - var logMessage = version == null ? Resources.InstallPackageLatestVersion : Resources.InstallPackageVersion; - packageManager.Logger.LogInformation(string.Format(logMessage, packageId, version)); var package = new PackageIdentity(packageId, version); - return await packageManager.InstallPackageAsync(package, projectFramework, ignoreDependencies: false, CancellationToken.None); + var logMessage = package.Version == null ? Resources.InstallPackageLatestVersion : Resources.InstallPackageVersion; + packageManager.Logger.LogInformation(string.Format(logMessage, package.Id, package.Version)); + return await packageManager.InstallPackageAsync(package, projectFramework, ignoreDependencies: false, cancellationToken); } - public static async Task StartRestorePackage(this IPackageManager packageManager, string packageId, NuGetVersion version, NuGetFramework projectFramework) + public static async Task RestorePackageAsync( + this IPackageManager packageManager, + string packageId, + NuGetVersion version, + NuGetFramework projectFramework, + CancellationToken cancellationToken = default) { - packageManager.Logger.LogInformation(string.Format(Resources.RestorePackageVersion, packageId, version)); var package = new PackageIdentity(packageId, version); - return await packageManager.InstallPackageAsync(package, projectFramework, ignoreDependencies: true, CancellationToken.None); + packageManager.Logger.LogInformation(string.Format(Resources.RestorePackageVersion, packageId, version)); + return await packageManager.InstallPackageAsync(package, projectFramework, ignoreDependencies: true, cancellationToken); } } } diff --git a/Bonsai.NuGet/PackageManager.cs b/Bonsai.NuGet/PackageManager.cs index dfe1778fb..3c407e270 100644 --- a/Bonsai.NuGet/PackageManager.cs +++ b/Bonsai.NuGet/PackageManager.cs @@ -33,7 +33,7 @@ public PackageManager(ISettings settings, IPackageSourceProvider packageSourcePr { if (packageSourceProvider == null) throw new ArgumentNullException(nameof(packageSourceProvider)); if (localRepository == null) throw new ArgumentNullException(nameof(localRepository)); - SourceRepositoryProvider = new SourceRepositoryProvider(packageSourceProvider, Repository.Provider.GetCoreV3()); + SourceRepositoryProvider = new SourceRepositoryProvider(packageSourceProvider, Search.Repository.Provider.GetCoreV3()); PathResolver = pathResolver ?? new PackagePathResolver(localRepository.Source); LocalRepository = SourceRepositoryProvider.CreateRepository(localRepository); Settings = settings ?? throw new ArgumentNullException(nameof(settings)); @@ -59,7 +59,7 @@ public PackageManager(ISettings settings, IPackageSourceProvider packageSourcePr public PackageSaveMode PackageSaveMode { get; set; } - protected virtual bool AcceptLicenseAgreement(IEnumerable licensePackages) + protected virtual bool AcceptLicenseAgreement(IEnumerable licensePackages) { return true; } @@ -198,7 +198,7 @@ public async Task InstallPackageAsync(PackageIdentity package var resolver = new PackageResolver(); var installOperations = resolver.Resolve(resolverContext, token); var packagesToRemove = new List(); - var licensePackages = new List(); + var licensePackages = new List(); var findLocalPackageResource = await LocalRepository.GetResourceAsync(token); foreach (var identity in installOperations) { @@ -208,7 +208,11 @@ public async Task InstallPackageAsync(PackageIdentity package var packageInfo = sourcePackages[identity]; var packageMetadataResource = await packageInfo.Source.GetResourceAsync(token); var packageMetadata = await packageMetadataResource.GetMetadataAsync(identity, cacheContext, NullLogger.Instance, token); - if (packageMetadata.RequireLicenseAcceptance) licensePackages.Add(packageMetadata); + if (packageMetadata.RequireLicenseAcceptance) + { + licensePackages.Add(new RequiringLicenseAcceptancePackageInfo(packageMetadata, packageInfo.Source)); + } + try { var existingPackages = await findLocalPackageResource.GetAllVersionsAsync(identity.Id, cacheContext, NullLogger.Instance, token); @@ -226,7 +230,8 @@ public async Task InstallPackageAsync(PackageIdentity package { token.ThrowIfCancellationRequested(); var pluralSuffix = licensePackages.Count == 1 ? "s" : ""; - var message = $"Unable to install package '{package}' because '{string.Join(", ", licensePackages.Select(x => x.Identity))}' require{pluralSuffix} license acceptance."; + var requiringPackages = string.Join(", ", licensePackages.Select(info => info.Package.Identity)); + var message = $"Unable to install package '{package}' because '{requiringPackages}' require{pluralSuffix} license acceptance."; throw new InvalidOperationException(message); } diff --git a/Bonsai.NuGet/RequiringLicenseAcceptanceEventArgs.cs b/Bonsai.NuGet/RequiringLicenseAcceptanceEventArgs.cs index 76eb598df..120cf6ce0 100644 --- a/Bonsai.NuGet/RequiringLicenseAcceptanceEventArgs.cs +++ b/Bonsai.NuGet/RequiringLicenseAcceptanceEventArgs.cs @@ -1,17 +1,16 @@ -using NuGet.Protocol.Core.Types; -using System; +using System; using System.Collections.Generic; namespace Bonsai.NuGet { public class RequiringLicenseAcceptanceEventArgs : EventArgs { - public RequiringLicenseAcceptanceEventArgs(IEnumerable licensePackages) + public RequiringLicenseAcceptanceEventArgs(IEnumerable licensePackages) { LicensePackages = licensePackages ?? throw new ArgumentNullException(nameof(licensePackages)); } - public IEnumerable LicensePackages { get; private set; } + public IEnumerable LicensePackages { get; private set; } public bool LicenseAccepted { get; set; } } diff --git a/Bonsai.NuGet/RequiringLicenseAcceptancePackageInfo.cs b/Bonsai.NuGet/RequiringLicenseAcceptancePackageInfo.cs new file mode 100644 index 000000000..eae03a7ed --- /dev/null +++ b/Bonsai.NuGet/RequiringLicenseAcceptancePackageInfo.cs @@ -0,0 +1,17 @@ +using NuGet.Protocol.Core.Types; + +namespace Bonsai.NuGet +{ + public sealed class RequiringLicenseAcceptancePackageInfo + { + public RequiringLicenseAcceptancePackageInfo(IPackageSearchMetadata package, SourceRepository sourceRepository) + { + Package = package; + SourceRepository = sourceRepository; + } + + public IPackageSearchMetadata Package { get; } + + public SourceRepository SourceRepository { get; } + } +} diff --git a/Bonsai.NuGet/Search/LocalPackageTypeSearchResource.cs b/Bonsai.NuGet/Search/LocalPackageTypeSearchResource.cs new file mode 100644 index 000000000..1bb6b3d66 --- /dev/null +++ b/Bonsai.NuGet/Search/LocalPackageTypeSearchResource.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Common; +using NuGet.Packaging.Core; +using NuGet.Protocol; +using NuGet.Protocol.Core.Types; + +namespace Bonsai.NuGet.Search +{ + internal class LocalPackageTypeSearchResource : PackageSearchResource + { + readonly FindLocalPackagesResource findLocalPackagesResource; + readonly Lazy defaultSearchResource; + + public LocalPackageTypeSearchResource(FindLocalPackagesResource localResource) + { + findLocalPackagesResource = localResource ?? throw new ArgumentNullException(nameof(localResource)); + defaultSearchResource = new Lazy( + () => new LocalPackageSearchResource(findLocalPackagesResource)); + } + + public override async Task> SearchAsync( + string searchTerm, + SearchFilter filters, + int skip, + int take, + ILogger log, + CancellationToken cancellationToken) + { + LocalPackageSearchResource searchResource; + if (filters?.PackageTypes != null + && filters.PackageTypes.SingleOrDefault() is string packageType) + { + var wrapperResource = new FindLocalPackageWrapperResource(findLocalPackagesResource, packageType); + searchResource = new LocalPackageSearchResource(wrapperResource); + } + else searchResource = defaultSearchResource.Value; + + return await searchResource.SearchAsync(searchTerm, filters, skip, take, log, cancellationToken); + } + + class FindLocalPackageWrapperResource : FindLocalPackagesResource + { + readonly FindLocalPackagesResource baseLocalResource; + + public FindLocalPackageWrapperResource(FindLocalPackagesResource localResource, string packageType) + { + baseLocalResource = localResource ?? throw new ArgumentNullException(nameof(localResource)); + Root = baseLocalResource.Root; + PackageTypeFilter = packageType; + } + + public string PackageTypeFilter { get; } + + public override IEnumerable FindPackagesById(string id, ILogger logger, CancellationToken token) + { + return baseLocalResource.FindPackagesById(id, logger, token); + } + + public override LocalPackageInfo GetPackage(Uri path, ILogger logger, CancellationToken token) + { + throw new NotSupportedException(); + } + + public override LocalPackageInfo GetPackage(PackageIdentity identity, ILogger logger, CancellationToken token) + { + throw new NotSupportedException(); + } + + public override IEnumerable GetPackages(ILogger logger, CancellationToken token) + { + var result = baseLocalResource.GetPackages(logger, token); + if (!string.IsNullOrEmpty(PackageTypeFilter)) + { + result = result.Where(package => package.IsPackageType(PackageTypeFilter)); + } + return result; + } + } + } +} diff --git a/Bonsai.NuGet/Search/LocalPackageTypeSearchResourceProvider.cs b/Bonsai.NuGet/Search/LocalPackageTypeSearchResourceProvider.cs new file mode 100644 index 000000000..26a9d5db3 --- /dev/null +++ b/Bonsai.NuGet/Search/LocalPackageTypeSearchResourceProvider.cs @@ -0,0 +1,28 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Protocol; +using NuGet.Protocol.Core.Types; + +namespace Bonsai.NuGet.Search +{ + internal class LocalPackageTypeSearchResourceProvider : ResourceProvider + { + public LocalPackageTypeSearchResourceProvider() + : base(typeof(PackageSearchResource), nameof(LocalPackageTypeSearchResourceProvider), nameof(LocalPackageSearchResourceProvider)) + { + } + + public override async Task> TryCreate(SourceRepository source, CancellationToken token) + { + INuGetResource resource = null; + var localResource = await source.GetResourceAsync(token); + if (localResource != null) + { + resource = new LocalPackageTypeSearchResource(localResource); + } + + return new Tuple(resource != null, resource); + } + } +} diff --git a/Bonsai.NuGet/Search/PackageTypeSearchResourceV3.cs b/Bonsai.NuGet/Search/PackageTypeSearchResourceV3.cs new file mode 100644 index 000000000..2b6416c73 --- /dev/null +++ b/Bonsai.NuGet/Search/PackageTypeSearchResourceV3.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Common; +using NuGet.Protocol; +using NuGet.Protocol.Core.Types; + +namespace Bonsai.NuGet.Search +{ + internal class PackageTypeSearchResourceV3 : PackageSearchResource + { + readonly PackageSearchResourceV3 packageSearchResource; + + public PackageTypeSearchResourceV3(PackageSearchResourceV3 searchResource) + { + packageSearchResource = searchResource ?? throw new ArgumentNullException(nameof(searchResource)); + } + + public override Task> SearchAsync(string searchTerm, SearchFilter filters, int skip, int take, ILogger log, CancellationToken cancellationToken) + { + if (filters?.PackageTypes != null + && filters.PackageTypes.SingleOrDefault() is string packageType) + { + searchTerm += "&packageType=" + packageType; + } + + return packageSearchResource.SearchAsync(searchTerm, filters, skip, take, log, cancellationToken); + } + } +} diff --git a/Bonsai.NuGet/Search/PackageTypeSearchResourceV3Provider.cs b/Bonsai.NuGet/Search/PackageTypeSearchResourceV3Provider.cs new file mode 100644 index 000000000..df4e56a23 --- /dev/null +++ b/Bonsai.NuGet/Search/PackageTypeSearchResourceV3Provider.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Protocol; +using NuGet.Protocol.Core.Types; + +namespace Bonsai.NuGet.Search +{ + internal class PackageTypeSearchResourceV3Provider : ResourceProvider + { + readonly PackageSearchResourceV3Provider searchResourceV3Provider = new(); + + public PackageTypeSearchResourceV3Provider() + : base(typeof(PackageSearchResource), nameof(PackageTypeSearchResourceV3Provider), nameof(PackageSearchResourceV3Provider)) + { + } + + public override async Task> TryCreate(SourceRepository source, CancellationToken token) + { + INuGetResource resource = null; + var result = await searchResourceV3Provider.TryCreate(source, token); + if (result.Item1) + { + var searchResource = (PackageSearchResourceV3)result.Item2; + resource = new PackageTypeSearchResourceV3(searchResource); + } + + return new Tuple(resource != null, resource); + } + } +} diff --git a/Bonsai.NuGet/Search/Repository.cs b/Bonsai.NuGet/Search/Repository.cs new file mode 100644 index 000000000..2fdfd75bf --- /dev/null +++ b/Bonsai.NuGet/Search/Repository.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using NuGet.Protocol.Core.Types; + +namespace Bonsai.NuGet.Search +{ + internal static class Repository + { + public static ProviderFactory Provider => ProviderFactory.Instance; + + public class ProviderFactory : global::NuGet.Protocol.Core.Types.Repository.ProviderFactory + { + internal static readonly ProviderFactory Instance = new(); + + public override IEnumerable> GetCoreV3() + { + yield return new Lazy(() => new PackageTypeSearchResourceV3Provider()); + yield return new Lazy(() => new LocalPackageTypeSearchResourceProvider()); + foreach (var provider in base.GetCoreV3()) + { + yield return provider; + } + } + } + } +} diff --git a/Bonsai.NuGet/SourceRepositoryExtensions.cs b/Bonsai.NuGet/SourceRepositoryExtensions.cs index 131811202..984da279b 100644 --- a/Bonsai.NuGet/SourceRepositoryExtensions.cs +++ b/Bonsai.NuGet/SourceRepositoryExtensions.cs @@ -90,11 +90,20 @@ public static async Task GetMetadataAsync(this SourceRep public static async Task GetLatestMetadataAsync(this SourceRepository repository, string id, VersionRange version, bool includePrerelease, SourceCacheContext cacheContext, CancellationToken token = default) { var packageMetadataResource = await repository.GetResourceAsync(token); - var packages = await packageMetadataResource.GetMetadataAsync(id, includePrerelease, includeUnlisted: false, cacheContext, NullLogger.Instance, token); - return packages + var packageMetadata = await packageMetadataResource.GetMetadataAsync(id, includePrerelease, includeUnlisted: false, cacheContext, NullLogger.Instance, token); + var packageVersions = packageMetadata .Where(package => version.Satisfies(package.Identity.Version)) .OrderByDescending(package => package.Identity.Version, VersionComparer.VersionRelease) - .FirstOrDefault(); + .ToArray(); + return packageVersions.Length > 0 + ? PackageSearchMetadataBuilder + .FromMetadata(packageVersions[0]) + .WithVersions(AsyncLazy.New(packageVersions + .Select(metadata => new VersionInfo(metadata.Identity.Version, metadata.DownloadCount) + { + PackageSearchMetadata = metadata + }))).Build() + : null; } } } diff --git a/Bonsai.Setup.Bootstrapper/License.rtf b/Bonsai.Setup.Bootstrapper/License.rtf index c904e7c72..06195bc76 100644 --- a/Bonsai.Setup.Bootstrapper/License.rtf +++ b/Bonsai.Setup.Bootstrapper/License.rtf @@ -1,6 +1,6 @@ {\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff0\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi0\deflang2057\deflangfe1041\themelang2070\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f2\fbidi \fmodern\fcharset0\fprq1{\*\panose 02070309020205020404}Courier New;} {\f3\fbidi \froman\fcharset2\fprq2{\*\panose 05050102010706020507}Symbol;}{\f10\fbidi \fnil\fcharset2\fprq2{\*\panose 05000000000000000000}Wingdings;}{\f34\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria Math;} -{\f37\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\f47\fbidi \fmodern\fcharset0\fprq1{\*\panose 00000000000000000000}Consolas;}{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\f37\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\f47\fbidi \fmodern\fcharset0\fprq1{\*\panose 020b0609020204030204}Consolas;}{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} {\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhimajor\f31502\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria;} {\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} {\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;} @@ -52,12 +52,12 @@ Hyperlink;}{\*\cs17 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \cf20\chshdng0\chcfpa \fi-360\li3960\lin3960 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid135659521\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li4680\lin4680 }{\listlevel \levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid135659523\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5400\lin5400 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 \levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid135659525\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6120\lin6120 }{\listname ;}\listid563031358}}{\*\listoverridetable{\listoverride\listid563031358 -\listoverridecount0\ls1}}{\*\pgptbl {\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}}{\*\rsidtbl \rsid3018\rsid883364\rsid932160\rsid1779335\rsid2687205\rsid4552376\rsid4988138\rsid5898739\rsid6040131\rsid6308307\rsid7026644\rsid7561066\rsid8196620\rsid9073335 -\rsid9132714\rsid9449547\rsid9917251\rsid10240754\rsid10776296\rsid11675940\rsid11927560\rsid12270564\rsid13566957\rsid13788254\rsid15890540}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440 -\mintLim0\mnaryLim1}{\info{\operator Gon\'e7alo Lopes}{\creatim\yr2013\mo3\dy27\hr22\min26}{\revtim\yr2024\mo3\dy1\hr3\min14}{\version23}{\edmins24}{\nofpages1}{\nofwords317}{\nofchars1811}{\nofcharsws2124}{\vern89}}{\*\userprops {\propname Docear4Word_St -yleTitle}\proptype30{\staticval Public Library of Science}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}\paperw12240\paperh15840\margl1701\margr1701\margt1417\margb1417\gutter0\ltrsect +\listoverridecount0\ls1}}{\*\pgptbl {\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}}{\*\rsidtbl \rsid3018\rsid883364\rsid932160\rsid1779335\rsid2687205\rsid4265130\rsid4552376\rsid4988138\rsid5898739\rsid6040131\rsid6308307\rsid7026644\rsid7237901\rsid7561066 +\rsid8196620\rsid9073335\rsid9132714\rsid9449547\rsid9917251\rsid10240754\rsid10776296\rsid11675940\rsid11927560\rsid12270564\rsid13566957\rsid13788254\rsid15890540}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0 +\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\operator Gon\'e7alo Lopes}{\creatim\yr2013\mo3\dy27\hr22\min26}{\revtim\yr2024\mo8\dy27\hr8\min39}{\version25}{\edmins29}{\nofpages1}{\nofwords335}{\nofchars1911}{\nofcharsws2242}{\vern101}} +{\*\userprops {\propname Docear4Word_StyleTitle}\proptype30{\staticval Public Library of Science}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}\paperw12240\paperh15840\margl1701\margr1701\margt1417\margb1417\gutter0\ltrsect \widowctrl\ftnbj\aenddoc\hyphhotz425\trackmoves0\trackformatting1\donotembedsysfont0\relyonvml0\donotembedlingdata1\grfdocevents0\validatexml0\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors0\horzdoc\dghspace120\dgvspace120 -\dghorigin1701\dgvorigin1984\dghshow0\dgvshow3\jcompress\viewkind1\viewscale120\rsidroot10776296 \fet0{\*\wgrffmtfilter 2450}\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}} +\dghorigin1701\dgvorigin1984\dghshow0\dgvshow3\jcompress\viewkind1\viewscale110\rsidroot10776296 \fet0{\*\wgrffmtfilter 2450}\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}} {\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}} {\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9 \pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 @@ -88,8 +88,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI \par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f3\fs19\lang1033\langfe1041\langnp1033\insrsid10776296\charrsid9449547 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}}\pard \ltrpar \ql \fi-360\li360\ri0\nowidctlpar\wrapdefault\faauto\ls1\rin0\lin360\itap0\pararsid1779335 {\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid10776296\charrsid9449547 \hich\af47\dbch\af31505\loch\f47 The Bonsai }{ \rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid9917251 \hich\af47\dbch\af31505\loch\f47 bootstrapper}{\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid10776296\charrsid9449547 -\hich\af47\dbch\af31505\loch\f47 }{\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid883364\charrsid9449547 \hich\af47\dbch\af31505\loch\f47 includes software from the}{\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 -\f47\fs19\lang1033\langfe1041\langnp1033\insrsid9449547 \hich\af47\dbch\af31505\loch\f47 Reactive Ext\hich\af47\dbch\af31505\loch\f47 ensions for .NET and the}{\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 +\hich\af47\dbch\af31505\loch\f47 }{\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid883364\charrsid9449547 \hich\af47\dbch\af31505\loch\f47 includes software\hich\af47\dbch\af31505\loch\f47 from the}{\rtlch\fcs1 +\af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid9449547 \hich\af47\dbch\af31505\loch\f47 Reactive Extensions for .NET and the}{\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid883364\charrsid9449547 \hich\af47\dbch\af31505\loch\f47 NuGet }{\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid9449547 \hich\af47\dbch\af31505\loch\f47 p}{\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid883364\charrsid9449547 \hich\af47\dbch\af31505\loch\f47 roject}{\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid9449547 \hich\af47\dbch\af31505\loch\f47 s}{\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid883364\charrsid9449547 \hich\af47\dbch\af31505\loch\f47 developed at the }{\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 @@ -97,7 +97,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI Foundation (}{\field{\*\fldinst {\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid9449547\charrsid9449547 \hich\af47\dbch\af31505\loch\f47 HYPERLINK "https://dotnetfoundation.org/" }{\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid2687205\charrsid9449547 {\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b54000000680074007400700073003a002f002f0064006f0074006e006500740066006f0075006e0064006100740069006f006e002e006f00720067002f000000795881f43b1d7f48af2c825dc485276300000000a5ab -00030074006c7f33000063}}}{\fldrslt {\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \cs16\f47\fs19\ul\cf19\lang1033\langfe1041\langnp1033\insrsid9449547\charrsid9449547 \hich\af47\dbch\af31505\loch\f47 https://dotnetfoundation.org/}}}\sectd \ltrsect +00030074006c7f330000636f}}}{\fldrslt {\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \cs16\f47\fs19\ul\cf19\lang1033\langfe1041\langnp1033\insrsid9449547\charrsid9449547 \hich\af47\dbch\af31505\loch\f47 https://dotnetfoundation.org/}}}\sectd \ltrsect \linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid883364\charrsid9449547 \hich\af47\dbch\af31505\loch\f47 ).}{\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid13566957\charrsid9449547 \par }\pard \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0\pararsid883364 {\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid883364 @@ -113,12 +113,16 @@ You may obtain a copy of the License at \par \hich\af47\dbch\af31505\loch\f47 }{\field{\*\fldinst {\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid9073335 \hich\af47\dbch\af31505\loch\f47 HYPERLINK "http://www.apache.org/licenses/LICENSE-2.0" }{\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid9073335 {\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b6e00000068007400740070003a002f002f007700770077002e006100700061006300680065002e006f00720067002f006c006900630065006e007300650073002f004c004900430045004e00530045002d0032002e00 -30000000795881f43b1d7f48af2c825dc485276300000000a5ab0000005000000000000000000200330013660041}}}{\fldrslt {\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \cs16\f47\fs19\ul\cf19\lang1033\langfe1041\langnp1033\insrsid883364\charrsid9073335 +30000000795881f43b1d7f48af2c825dc485276300000000a5ab000000500000000000000000020033001366004134}}}{\fldrslt {\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \cs16\f47\fs19\ul\cf19\lang1033\langfe1041\langnp1033\insrsid883364\charrsid9073335 \hich\af47\dbch\af31505\loch\f47 http://www.apache.org/licenses/LICENSE-2.0}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid883364\charrsid883364 \par \par \hich\af47\dbch\af31505\loch\f47 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language govern -\hich\af47\dbch\af31505\loch\f47 ing permissions and limitations under the License. +\hich\af47\dbch\af31505\loch\f47 ing permissions and limitations under the License.}{\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid883364 +\par }{\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid7237901 +\par }\pard \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0\pararsid7237901 {\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid7237901 \hich\af47\dbch\af31505\loch\f47 T\hich\af47\dbch\af31505\loch\f47 +he NuGet logo is part of the NuGet project and license\hich\af47\dbch\af31505\loch\f47 d under\hich\af47\dbch\af31505\loch\f47 the\hich\af47\dbch\af31505\loch\f47 Creative Commons\hich\af47\dbch\af31505\loch\f47 \hich\af47\dbch\af31505\loch\f47 A +\hich\af47\dbch\af31505\loch\f47 ttribution\hich\af47\dbch\af31505\loch\f47 2.0\hich\af47\dbch\af31505\loch\f47 Generic license.}{\rtlch\fcs1 \af47\afs19 \ltrch\fcs0 \f47\fs19\lang1033\langfe1041\langnp1033\insrsid7237901\charrsid883364 \par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a 9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad 5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 @@ -238,8 +242,8 @@ fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000500d -1687866bda01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 +ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e500000000000000000000000020d9 +984454f8da01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000105000000000000}} \ No newline at end of file diff --git a/Bonsai.StarterPack/Properties/launchSettings.json b/Bonsai.StarterPack/Properties/launchSettings.json new file mode 100644 index 000000000..4af4f4685 --- /dev/null +++ b/Bonsai.StarterPack/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "Bonsai": { + "commandName": "Executable", + "executablePath": "$(BonsaiExecutablePath)", + "commandLineArgs": "--lib:\"$(TargetDir).\"", + "nativeDebugging": true + } + } +} \ No newline at end of file diff --git a/Bonsai/EditorBootstrapper.cs b/Bonsai/EditorBootstrapper.cs index efa049f83..10d34d93d 100644 --- a/Bonsai/EditorBootstrapper.cs +++ b/Bonsai/EditorBootstrapper.cs @@ -2,6 +2,7 @@ using Bonsai.NuGet; using Bonsai.NuGet.Design; using System; +using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; @@ -30,7 +31,7 @@ public EditorBootstrapper(string path) PackageManager.Logger = new EventLogger(); } - protected override Task RunPackageOperationAsync(Func operationFactory) + protected override Task RunPackageOperationAsync(Func operationFactory, CancellationToken cancellationToken) { EnableVisualStyles(); EventHandler requiringLicenseHandler = null; @@ -49,7 +50,7 @@ protected override Task RunPackageOperationAsync(Func operationFactory) PackageManager.RequiringLicenseAcceptance += requiringLicenseHandler; try { - var operation = operationFactory(); + var operation = operationFactory(cancellationToken); operation.ContinueWith(task => { if (task.IsFaulted) @@ -60,7 +61,7 @@ protected override Task RunPackageOperationAsync(Func operationFactory) } } else dialog.BeginInvoke((Action)dialog.Close); - }); + }, cancellationToken); dialog.ShowDialog(); return operation; } diff --git a/tooling/Common.csproj.props b/tooling/Common.csproj.props index 11057cad5..0a1162f93 100644 --- a/tooling/Common.csproj.props +++ b/tooling/Common.csproj.props @@ -11,6 +11,7 @@ https://bonsai-rx.org https://bonsai-rx.org/license https://bonsai-rx.org/assets/images/bonsai.png + Dependency;BonsaiLibrary true