From 1b26cbc33bda4d52ef795698f130d40affe7fbe9 Mon Sep 17 00:00:00 2001 From: Martin Calander Date: Sun, 22 Feb 2026 20:41:07 +0100 Subject: [PATCH 01/10] Menu Items in Proper place (imo) I feel like this is better place to store the menuitems.. --- src/NuGetForUnity/Editor/Ui/DependencyTreeViewer.cs | 2 +- src/NuGetForUnity/Editor/Ui/NuGetForUnityUpdater.cs | 4 ++-- src/NuGetForUnity/Editor/Ui/NugetWindow.cs | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/NuGetForUnity/Editor/Ui/DependencyTreeViewer.cs b/src/NuGetForUnity/Editor/Ui/DependencyTreeViewer.cs index 2423c037..af35b9a4 100644 --- a/src/NuGetForUnity/Editor/Ui/DependencyTreeViewer.cs +++ b/src/NuGetForUnity/Editor/Ui/DependencyTreeViewer.cs @@ -49,7 +49,7 @@ public class DependencyTreeViewer : EditorWindow /// /// Opens the NuGet Package Manager Window. /// - [MenuItem("NuGet/Show Dependency Tree", false, 5)] + [MenuItem("Window/Package Management/NuGet/Show Dependency Tree", false, 5)] protected static void DisplayDependencyTree() { GetWindow(); diff --git a/src/NuGetForUnity/Editor/Ui/NuGetForUnityUpdater.cs b/src/NuGetForUnity/Editor/Ui/NuGetForUnityUpdater.cs index d3ae7273..aa3a4a76 100644 --- a/src/NuGetForUnity/Editor/Ui/NuGetForUnityUpdater.cs +++ b/src/NuGetForUnity/Editor/Ui/NuGetForUnityUpdater.cs @@ -30,7 +30,7 @@ internal static class NuGetForUnityUpdater /// /// Opens release notes for the current version. /// - [MenuItem("NuGet/Version " + NugetPreferences.NuGetForUnityVersion + " \uD83D\uDD17", false, 10)] + [MenuItem("Window/Package Management/NuGet/Version " + NugetPreferences.NuGetForUnityVersion + " \uD83D\uDD17", false, 10)] public static void DisplayVersion() { Application.OpenURL($"{GitHubReleasesPageUrl}/tag/v{NugetPreferences.NuGetForUnityVersion}"); @@ -39,7 +39,7 @@ public static void DisplayVersion() /// /// Checks/launches the Releases page to update NuGetForUnity with a new version. /// - [MenuItem("NuGet/Check for Updates...", false, 11)] + [MenuItem("Window/Package Management/NuGet/Check for Updates...", false, 11)] public static void CheckForUpdates() { var request = UnityWebRequest.Get(GitHubReleasesApiUrl); diff --git a/src/NuGetForUnity/Editor/Ui/NugetWindow.cs b/src/NuGetForUnity/Editor/Ui/NugetWindow.cs index 9e4b621b..bdaee030 100644 --- a/src/NuGetForUnity/Editor/Ui/NugetWindow.cs +++ b/src/NuGetForUnity/Editor/Ui/NugetWindow.cs @@ -290,7 +290,7 @@ public void OnAfterDeserialize() /// /// Opens the NuGet Package Manager Window. /// - [MenuItem("NuGet/Manage NuGet Packages", false, 0)] + [MenuItem("Window/Package Management/NuGet/Manage NuGet Packages", false, 0)] protected static void DisplayNugetWindow() { GetWindow(); @@ -299,7 +299,7 @@ protected static void DisplayNugetWindow() /// /// Restores all packages defined in packages.config. /// - [MenuItem("NuGet/Restore Packages", false, 1)] + [MenuItem("Window/Package Management/NuGet/Restore Packages", false, 1)] protected static void RestorePackages() { PackageRestorer.Restore(false); @@ -312,7 +312,7 @@ protected static void RestorePackages() /// /// Opens the preferences window. /// - [MenuItem("NuGet/Preferences", false, 9)] + [MenuItem("Window/Package Management/NuGet/Preferences", false, 9)] protected static void DisplayPreferences() { SettingsService.OpenProjectSettings(NugetPreferences.MenuItemLocation); From 94f89285b02fb1ed512d7d2eb7914f0688d7e2ec Mon Sep 17 00:00:00 2001 From: Martin Calander Date: Sun, 22 Feb 2026 20:47:26 +0100 Subject: [PATCH 02/10] Update package.json --- src/NuGetForUnity/package.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/NuGetForUnity/package.json b/src/NuGetForUnity/package.json index 9e2d7866..b2517cc7 100644 --- a/src/NuGetForUnity/package.json +++ b/src/NuGetForUnity/package.json @@ -8,8 +8,12 @@ "nuget", "unity" ], + "author": { + "name": "GlitchEnzo", + "url": "https://glitchenzo.github.io/" + }, "license": "MIT", "licensesUrl": "https://github.com/GlitchEnzo/NuGetForUnity/blob/master/LICENSE", "changelogUrl": "https://github.com/GlitchEnzo/NuGetForUnity/releases", "documentationUrl": "https://github.com/GlitchEnzo/NuGetForUnity" -} +} \ No newline at end of file From eb055e7a3a86900acd0cd45d6af12891514418f0 Mon Sep 17 00:00:00 2001 From: Martin Calander Date: Wed, 25 Feb 2026 15:00:07 +0100 Subject: [PATCH 03/10] Make NuGet menu root configurable --- .../Assets/Tests/Editor/NuGetTests.cs | 45 +++++ .../Editor/Configuration/NugetConfigFile.cs | 55 ++++++ .../Editor/Ui/DependencyTreeViewer.cs | 3 +- .../Editor/Ui/NuGetForUnityUpdater.cs | 2 - src/NuGetForUnity/Editor/Ui/NugetMenu.cs | 172 ++++++++++++++++++ src/NuGetForUnity/Editor/Ui/NugetMenu.cs.meta | 11 ++ .../Editor/Ui/NugetPreferences.cs | 19 ++ src/NuGetForUnity/Editor/Ui/NugetWindow.cs | 9 +- 8 files changed, 306 insertions(+), 10 deletions(-) create mode 100644 src/NuGetForUnity/Editor/Ui/NugetMenu.cs create mode 100644 src/NuGetForUnity/Editor/Ui/NugetMenu.cs.meta diff --git a/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs b/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs index 33680a7e..2d5d975d 100644 --- a/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs +++ b/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs @@ -722,6 +722,51 @@ public void PackageSourceCredentialsTest(string name) Assert.That(parsedSource.SavedPassword, Is.EqualTo(password)); } + [Test] + public void MenuRootConfigRoundtripTest() + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}_{NugetConfigFile.FileName}"); + + try + { + var file = NugetConfigFile.CreateDefaultFile(path); + Assert.That(file.MenuRoot, Is.EqualTo(NugetConfigFile.DefaultMenuRoot)); + + file.MenuRoot = "Window//Package Management\\NuGet/"; + file.Save(path); + + var loaded = NugetConfigFile.Load(path); + Assert.That(loaded.MenuRoot, Is.EqualTo("Window/Package Management/NuGet")); + } + finally + { + if (File.Exists(path)) + { + File.Delete(path); + } + } + } + + [Test] + public void MenuRootFallsBackToDefaultWhenEmptyTest() + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}_{NugetConfigFile.FileName}"); + + try + { + var file = NugetConfigFile.CreateDefaultFile(path); + file.MenuRoot = " /// "; + Assert.That(file.MenuRoot, Is.EqualTo(NugetConfigFile.DefaultMenuRoot)); + } + finally + { + if (File.Exists(path)) + { + File.Delete(path); + } + } + } + [Test] [TestCase("2018.4.30f1", false, false, false)] [TestCase("2018.4.30f1", true, false, false)] diff --git a/src/NuGetForUnity/Editor/Configuration/NugetConfigFile.cs b/src/NuGetForUnity/Editor/Configuration/NugetConfigFile.cs index 7baa678f..0c18ae35 100644 --- a/src/NuGetForUnity/Editor/Configuration/NugetConfigFile.cs +++ b/src/NuGetForUnity/Editor/Configuration/NugetConfigFile.cs @@ -37,6 +37,11 @@ public class NugetConfigFile /// public const string FileName = "NuGet.config"; + /// + /// The default root menu path where NuGet menu items are created. + /// + public const string DefaultMenuRoot = "NuGet"; + /// /// The name of the attribute that is used to configure of /// . @@ -56,6 +61,8 @@ public class NugetConfigFile private const string KeepingPdbFilesConfigKey = "KeepingPdbFiles"; + private const string MenuRootConfigKey = "MenuRoot"; + private const string ProtocolVersionAttributeName = "protocolVersion"; private const string EnableCredentialProviderAttributeName = "enableCredentialProvider"; @@ -78,6 +85,9 @@ public class NugetConfigFile [NotNull] private string repositoryPath = Path.GetFullPath(Path.Combine(Application.dataPath, "Packages")); + [NotNull] + private string menuRoot = DefaultMenuRoot; + /// /// Gets the list of package sources that are defined in the NuGet.config file. /// @@ -202,6 +212,16 @@ public string RelativePackagesConfigDirectoryPath /// internal bool KeepingPdbFiles { get; set; } + /// + /// Gets or sets the root menu path used for NuGet menu items. + /// + [NotNull] + public string MenuRoot + { + get => menuRoot; + set => menuRoot = NormalizeMenuRoot(value); + } + /// /// Gets the value that tells the system how to determine where the packages are to be installed and configurations are to be stored. /// @@ -382,6 +402,10 @@ public static NugetConfigFile Load([NotNull] string filePath) { configFile.KeepingPdbFiles = bool.Parse(value); } + else if (string.Equals(key, MenuRootConfigKey, StringComparison.OrdinalIgnoreCase)) + { + configFile.MenuRoot = value; + } } return configFile; @@ -587,6 +611,14 @@ public void Save([NotNull] string filePath) config.Add(addElement); } + if (!string.Equals(MenuRoot, DefaultMenuRoot, StringComparison.Ordinal)) + { + addElement = new XElement("add"); + addElement.Add(new XAttribute("key", MenuRootConfigKey)); + addElement.Add(new XAttribute("value", MenuRoot)); + config.Add(addElement); + } + var configuration = new XElement("configuration"); configuration.Add(packageSources); configuration.Add(disabledPackageSources); @@ -673,6 +705,29 @@ internal void FillMissingPackageCredentialsFromExternalFile([NotNull] string fil } } + /// + /// Normalizes a user configured menu root path. + /// + /// Configured menu root path. + /// Normalized menu root path. + [NotNull] + public static string NormalizeMenuRoot([CanBeNull] string menuRootPath) + { + if (string.IsNullOrWhiteSpace(menuRootPath)) + { + return DefaultMenuRoot; + } + + var normalized = menuRootPath.Trim().Replace('\\', '/'); + while (normalized.Contains("//")) + { + normalized = normalized.Replace("//", "/"); + } + + normalized = normalized.Trim('/'); + return string.IsNullOrWhiteSpace(normalized) ? DefaultMenuRoot : normalized; + } + private void FillPackageSourceCredentialsFromConfig(XDocument file, bool overwriteMissingFromExternal) { var packageSourceCredentials = file.Root?.Element("packageSourceCredentials"); diff --git a/src/NuGetForUnity/Editor/Ui/DependencyTreeViewer.cs b/src/NuGetForUnity/Editor/Ui/DependencyTreeViewer.cs index af35b9a4..1e22650d 100644 --- a/src/NuGetForUnity/Editor/Ui/DependencyTreeViewer.cs +++ b/src/NuGetForUnity/Editor/Ui/DependencyTreeViewer.cs @@ -49,8 +49,7 @@ public class DependencyTreeViewer : EditorWindow /// /// Opens the NuGet Package Manager Window. /// - [MenuItem("Window/Package Management/NuGet/Show Dependency Tree", false, 5)] - protected static void DisplayDependencyTree() + internal static void DisplayDependencyTree() { GetWindow(); } diff --git a/src/NuGetForUnity/Editor/Ui/NuGetForUnityUpdater.cs b/src/NuGetForUnity/Editor/Ui/NuGetForUnityUpdater.cs index aa3a4a76..081a89dd 100644 --- a/src/NuGetForUnity/Editor/Ui/NuGetForUnityUpdater.cs +++ b/src/NuGetForUnity/Editor/Ui/NuGetForUnityUpdater.cs @@ -30,7 +30,6 @@ internal static class NuGetForUnityUpdater /// /// Opens release notes for the current version. /// - [MenuItem("Window/Package Management/NuGet/Version " + NugetPreferences.NuGetForUnityVersion + " \uD83D\uDD17", false, 10)] public static void DisplayVersion() { Application.OpenURL($"{GitHubReleasesPageUrl}/tag/v{NugetPreferences.NuGetForUnityVersion}"); @@ -39,7 +38,6 @@ public static void DisplayVersion() /// /// Checks/launches the Releases page to update NuGetForUnity with a new version. /// - [MenuItem("Window/Package Management/NuGet/Check for Updates...", false, 11)] public static void CheckForUpdates() { var request = UnityWebRequest.Get(GitHubReleasesApiUrl); diff --git a/src/NuGetForUnity/Editor/Ui/NugetMenu.cs b/src/NuGetForUnity/Editor/Ui/NugetMenu.cs new file mode 100644 index 00000000..cdb0daa7 --- /dev/null +++ b/src/NuGetForUnity/Editor/Ui/NugetMenu.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using JetBrains.Annotations; +using NugetForUnity.Configuration; +using UnityEditor; +using UnityEngine; + +namespace NugetForUnity.Ui +{ + /// + /// Dynamically registers NuGet menu items so users can configure the menu root path. + /// + [InitializeOnLoad] + internal static class NugetMenu + { + private static readonly List RegisteredMenuItemPaths = new List(); + + [CanBeNull] + private static MethodInfo addMenuItemMethod; + + private static bool loggedMenuApiWarning; + + [CanBeNull] + private static MethodInfo removeMenuItemMethod; + + static NugetMenu() + { + EditorApplication.delayCall += Refresh; + } + + /// + /// Re-registers NuGet menu items at the configured menu root. + /// + public static void Refresh() + { + if (!TryInitializeMenuMethods()) + { + return; + } + + RemoveRegisteredMenuItems(); + + var menuRoot = ConfigurationManager.NugetConfigFile.MenuRoot; + foreach (var menuItem in GetMenuItems()) + { + var menuPath = $"{menuRoot}/{menuItem.Name}"; + + try + { + RemoveMenuItem(menuPath); + addMenuItemMethod.Invoke(null, new object[] { menuPath, string.Empty, false, menuItem.Priority, menuItem.Execute, null }); + RegisteredMenuItemPaths.Add(menuPath); + } + catch (Exception exception) + { + Debug.LogError($"Unable to add NuGet menu item '{menuPath}'. Error: {exception}"); + } + } + } + + [NotNull] + private static IEnumerable GetMenuItems() + { + yield return new MenuItemDefinition("Manage NuGet Packages", 0, NugetWindow.DisplayNugetWindow); + yield return new MenuItemDefinition("Restore Packages", 1, NugetWindow.RestorePackages); + yield return new MenuItemDefinition("Show Dependency Tree", 5, DependencyTreeViewer.DisplayDependencyTree); + yield return new MenuItemDefinition("Preferences", 9, NugetWindow.DisplayPreferences); + yield return new MenuItemDefinition($"Version {NugetPreferences.NuGetForUnityVersion} \uD83D\uDD17", 10, NuGetForUnityUpdater.DisplayVersion); + yield return new MenuItemDefinition("Check for Updates...", 11, NuGetForUnityUpdater.CheckForUpdates); + } + + private static void RemoveRegisteredMenuItems() + { + if (removeMenuItemMethod == null) + { + RegisteredMenuItemPaths.Clear(); + return; + } + + foreach (var menuPath in RegisteredMenuItemPaths) + { + RemoveMenuItem(menuPath); + } + + RegisteredMenuItemPaths.Clear(); + } + + private static bool TryInitializeMenuMethods() + { + if (addMenuItemMethod != null && removeMenuItemMethod != null) + { + return true; + } + + var menuType = typeof(EditorApplication).Assembly.GetType("UnityEditor.Menu"); + if (menuType == null) + { + LogMenuApiWarning("Unable to find type 'UnityEditor.Menu'. NuGet menu root configuration will not be applied."); + return false; + } + + addMenuItemMethod = menuType.GetMethod( + "AddMenuItem", + BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, + null, + new[] { typeof(string), typeof(string), typeof(bool), typeof(int), typeof(Action), typeof(Func) }, + null); + removeMenuItemMethod = menuType.GetMethod( + "RemoveMenuItem", + BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, + null, + new[] { typeof(string) }, + null); + + if (addMenuItemMethod == null || removeMenuItemMethod == null) + { + LogMenuApiWarning( + "Unable to resolve UnityEditor.Menu.AddMenuItem/RemoveMenuItem methods. NuGet menu root configuration will not be applied."); + return false; + } + + return true; + } + + private static void LogMenuApiWarning([NotNull] string message) + { + if (loggedMenuApiWarning) + { + return; + } + + loggedMenuApiWarning = true; + Debug.LogWarning(message); + } + + private static void RemoveMenuItem([NotNull] string menuPath) + { + if (removeMenuItemMethod == null) + { + return; + } + + try + { + removeMenuItemMethod.Invoke(null, new object[] { menuPath }); + } + catch (Exception) + { + // Ignore, this is a best-effort cleanup of existing menu entries. + } + } + + private sealed class MenuItemDefinition + { + public MenuItemDefinition([NotNull] string name, int priority, [NotNull] Action execute) + { + Name = name; + Priority = priority; + Execute = execute; + } + + [NotNull] + public string Name { get; } + + public int Priority { get; } + + [NotNull] + public Action Execute { get; } + } + } +} diff --git a/src/NuGetForUnity/Editor/Ui/NugetMenu.cs.meta b/src/NuGetForUnity/Editor/Ui/NugetMenu.cs.meta new file mode 100644 index 00000000..dfc657a0 --- /dev/null +++ b/src/NuGetForUnity/Editor/Ui/NugetMenu.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 48d1f91bca5c4900946a1853927aa4d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs b/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs index 6f971386..b4bb3c32 100644 --- a/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs +++ b/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs @@ -121,6 +121,7 @@ public override void OnGUI([CanBeNull] string searchContext) new GUIStyle(GUI.skin.toggle) { onNormal = { textColor = Color.red }, onHover = { textColor = Color.red } }; var preferencesChangedThisFrame = false; + var menuRootChangedThisFrame = false; var sourcePathChangedThisFrame = false; var needsAssetRefresh = false; @@ -128,6 +129,18 @@ public override void OnGUI([CanBeNull] string searchContext) EditorGUIUtility.labelWidth = biggestLabelSize; EditorGUILayout.LabelField($"Version: {NuGetForUnityVersion}"); + var menuRoot = EditorGUILayout.TextField( + new GUIContent( + "Menu Root Path", + "Root menu path for NuGet menu items. Examples: 'NuGet', 'Tools/NuGet', 'Window/Package Management/NuGet'."), + ConfigurationManager.NugetConfigFile.MenuRoot); + if (menuRoot != ConfigurationManager.NugetConfigFile.MenuRoot) + { + preferencesChangedThisFrame = true; + menuRootChangedThisFrame = true; + ConfigurationManager.NugetConfigFile.MenuRoot = menuRoot; + } + var installFromCache = EditorGUILayout.Toggle("Install From the Cache", ConfigurationManager.NugetConfigFile.InstallFromCache); if (installFromCache != ConfigurationManager.NugetConfigFile.InstallFromCache) { @@ -607,6 +620,7 @@ public override void OnGUI([CanBeNull] string searchContext) NugetConfigFile.CreateDefaultFile(ConfigurationManager.NugetConfigFilePath); ConfigurationManager.LoadNugetConfigFile(); preferencesChangedThisFrame = true; + menuRootChangedThisFrame = true; } if (!preferencesChangedThisFrame) @@ -623,6 +637,11 @@ public override void OnGUI([CanBeNull] string searchContext) ConfigurationManager.LoadNugetConfigFile(); } + if (menuRootChangedThisFrame) + { + NugetMenu.Refresh(); + } + if (needsAssetRefresh) { // AssetDatabase.Refresh(); doesn't work when we move the files from Assets to Packages so we use this instead: diff --git a/src/NuGetForUnity/Editor/Ui/NugetWindow.cs b/src/NuGetForUnity/Editor/Ui/NugetWindow.cs index bdaee030..ba2d3609 100644 --- a/src/NuGetForUnity/Editor/Ui/NugetWindow.cs +++ b/src/NuGetForUnity/Editor/Ui/NugetWindow.cs @@ -290,8 +290,7 @@ public void OnAfterDeserialize() /// /// Opens the NuGet Package Manager Window. /// - [MenuItem("Window/Package Management/NuGet/Manage NuGet Packages", false, 0)] - protected static void DisplayNugetWindow() + internal static void DisplayNugetWindow() { GetWindow(); } @@ -299,8 +298,7 @@ protected static void DisplayNugetWindow() /// /// Restores all packages defined in packages.config. /// - [MenuItem("Window/Package Management/NuGet/Restore Packages", false, 1)] - protected static void RestorePackages() + internal static void RestorePackages() { PackageRestorer.Restore(false); foreach (var nugetWindow in Resources.FindObjectsOfTypeAll()) @@ -312,8 +310,7 @@ protected static void RestorePackages() /// /// Opens the preferences window. /// - [MenuItem("Window/Package Management/NuGet/Preferences", false, 9)] - protected static void DisplayPreferences() + internal static void DisplayPreferences() { SettingsService.OpenProjectSettings(NugetPreferences.MenuItemLocation); } From b104d0579c6208404adb8a589a10a880a872637d Mon Sep 17 00:00:00 2001 From: Martin Calander Date: Tue, 3 Mar 2026 00:00:12 +0100 Subject: [PATCH 04/10] Revert "Make NuGet menu root configurable" This reverts commit eb055e7a3a86900acd0cd45d6af12891514418f0. --- .../Assets/Tests/Editor/NuGetTests.cs | 45 ----- .../Editor/Configuration/NugetConfigFile.cs | 55 ------ .../Editor/Ui/DependencyTreeViewer.cs | 3 +- .../Editor/Ui/NuGetForUnityUpdater.cs | 2 + src/NuGetForUnity/Editor/Ui/NugetMenu.cs | 172 ------------------ src/NuGetForUnity/Editor/Ui/NugetMenu.cs.meta | 11 -- .../Editor/Ui/NugetPreferences.cs | 19 -- src/NuGetForUnity/Editor/Ui/NugetWindow.cs | 9 +- 8 files changed, 10 insertions(+), 306 deletions(-) delete mode 100644 src/NuGetForUnity/Editor/Ui/NugetMenu.cs delete mode 100644 src/NuGetForUnity/Editor/Ui/NugetMenu.cs.meta diff --git a/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs b/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs index 2d5d975d..33680a7e 100644 --- a/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs +++ b/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs @@ -722,51 +722,6 @@ public void PackageSourceCredentialsTest(string name) Assert.That(parsedSource.SavedPassword, Is.EqualTo(password)); } - [Test] - public void MenuRootConfigRoundtripTest() - { - var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}_{NugetConfigFile.FileName}"); - - try - { - var file = NugetConfigFile.CreateDefaultFile(path); - Assert.That(file.MenuRoot, Is.EqualTo(NugetConfigFile.DefaultMenuRoot)); - - file.MenuRoot = "Window//Package Management\\NuGet/"; - file.Save(path); - - var loaded = NugetConfigFile.Load(path); - Assert.That(loaded.MenuRoot, Is.EqualTo("Window/Package Management/NuGet")); - } - finally - { - if (File.Exists(path)) - { - File.Delete(path); - } - } - } - - [Test] - public void MenuRootFallsBackToDefaultWhenEmptyTest() - { - var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}_{NugetConfigFile.FileName}"); - - try - { - var file = NugetConfigFile.CreateDefaultFile(path); - file.MenuRoot = " /// "; - Assert.That(file.MenuRoot, Is.EqualTo(NugetConfigFile.DefaultMenuRoot)); - } - finally - { - if (File.Exists(path)) - { - File.Delete(path); - } - } - } - [Test] [TestCase("2018.4.30f1", false, false, false)] [TestCase("2018.4.30f1", true, false, false)] diff --git a/src/NuGetForUnity/Editor/Configuration/NugetConfigFile.cs b/src/NuGetForUnity/Editor/Configuration/NugetConfigFile.cs index 0c18ae35..7baa678f 100644 --- a/src/NuGetForUnity/Editor/Configuration/NugetConfigFile.cs +++ b/src/NuGetForUnity/Editor/Configuration/NugetConfigFile.cs @@ -37,11 +37,6 @@ public class NugetConfigFile /// public const string FileName = "NuGet.config"; - /// - /// The default root menu path where NuGet menu items are created. - /// - public const string DefaultMenuRoot = "NuGet"; - /// /// The name of the attribute that is used to configure of /// . @@ -61,8 +56,6 @@ public class NugetConfigFile private const string KeepingPdbFilesConfigKey = "KeepingPdbFiles"; - private const string MenuRootConfigKey = "MenuRoot"; - private const string ProtocolVersionAttributeName = "protocolVersion"; private const string EnableCredentialProviderAttributeName = "enableCredentialProvider"; @@ -85,9 +78,6 @@ public class NugetConfigFile [NotNull] private string repositoryPath = Path.GetFullPath(Path.Combine(Application.dataPath, "Packages")); - [NotNull] - private string menuRoot = DefaultMenuRoot; - /// /// Gets the list of package sources that are defined in the NuGet.config file. /// @@ -212,16 +202,6 @@ public string RelativePackagesConfigDirectoryPath /// internal bool KeepingPdbFiles { get; set; } - /// - /// Gets or sets the root menu path used for NuGet menu items. - /// - [NotNull] - public string MenuRoot - { - get => menuRoot; - set => menuRoot = NormalizeMenuRoot(value); - } - /// /// Gets the value that tells the system how to determine where the packages are to be installed and configurations are to be stored. /// @@ -402,10 +382,6 @@ public static NugetConfigFile Load([NotNull] string filePath) { configFile.KeepingPdbFiles = bool.Parse(value); } - else if (string.Equals(key, MenuRootConfigKey, StringComparison.OrdinalIgnoreCase)) - { - configFile.MenuRoot = value; - } } return configFile; @@ -611,14 +587,6 @@ public void Save([NotNull] string filePath) config.Add(addElement); } - if (!string.Equals(MenuRoot, DefaultMenuRoot, StringComparison.Ordinal)) - { - addElement = new XElement("add"); - addElement.Add(new XAttribute("key", MenuRootConfigKey)); - addElement.Add(new XAttribute("value", MenuRoot)); - config.Add(addElement); - } - var configuration = new XElement("configuration"); configuration.Add(packageSources); configuration.Add(disabledPackageSources); @@ -705,29 +673,6 @@ internal void FillMissingPackageCredentialsFromExternalFile([NotNull] string fil } } - /// - /// Normalizes a user configured menu root path. - /// - /// Configured menu root path. - /// Normalized menu root path. - [NotNull] - public static string NormalizeMenuRoot([CanBeNull] string menuRootPath) - { - if (string.IsNullOrWhiteSpace(menuRootPath)) - { - return DefaultMenuRoot; - } - - var normalized = menuRootPath.Trim().Replace('\\', '/'); - while (normalized.Contains("//")) - { - normalized = normalized.Replace("//", "/"); - } - - normalized = normalized.Trim('/'); - return string.IsNullOrWhiteSpace(normalized) ? DefaultMenuRoot : normalized; - } - private void FillPackageSourceCredentialsFromConfig(XDocument file, bool overwriteMissingFromExternal) { var packageSourceCredentials = file.Root?.Element("packageSourceCredentials"); diff --git a/src/NuGetForUnity/Editor/Ui/DependencyTreeViewer.cs b/src/NuGetForUnity/Editor/Ui/DependencyTreeViewer.cs index 1e22650d..af35b9a4 100644 --- a/src/NuGetForUnity/Editor/Ui/DependencyTreeViewer.cs +++ b/src/NuGetForUnity/Editor/Ui/DependencyTreeViewer.cs @@ -49,7 +49,8 @@ public class DependencyTreeViewer : EditorWindow /// /// Opens the NuGet Package Manager Window. /// - internal static void DisplayDependencyTree() + [MenuItem("Window/Package Management/NuGet/Show Dependency Tree", false, 5)] + protected static void DisplayDependencyTree() { GetWindow(); } diff --git a/src/NuGetForUnity/Editor/Ui/NuGetForUnityUpdater.cs b/src/NuGetForUnity/Editor/Ui/NuGetForUnityUpdater.cs index 081a89dd..aa3a4a76 100644 --- a/src/NuGetForUnity/Editor/Ui/NuGetForUnityUpdater.cs +++ b/src/NuGetForUnity/Editor/Ui/NuGetForUnityUpdater.cs @@ -30,6 +30,7 @@ internal static class NuGetForUnityUpdater /// /// Opens release notes for the current version. /// + [MenuItem("Window/Package Management/NuGet/Version " + NugetPreferences.NuGetForUnityVersion + " \uD83D\uDD17", false, 10)] public static void DisplayVersion() { Application.OpenURL($"{GitHubReleasesPageUrl}/tag/v{NugetPreferences.NuGetForUnityVersion}"); @@ -38,6 +39,7 @@ public static void DisplayVersion() /// /// Checks/launches the Releases page to update NuGetForUnity with a new version. /// + [MenuItem("Window/Package Management/NuGet/Check for Updates...", false, 11)] public static void CheckForUpdates() { var request = UnityWebRequest.Get(GitHubReleasesApiUrl); diff --git a/src/NuGetForUnity/Editor/Ui/NugetMenu.cs b/src/NuGetForUnity/Editor/Ui/NugetMenu.cs deleted file mode 100644 index cdb0daa7..00000000 --- a/src/NuGetForUnity/Editor/Ui/NugetMenu.cs +++ /dev/null @@ -1,172 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using JetBrains.Annotations; -using NugetForUnity.Configuration; -using UnityEditor; -using UnityEngine; - -namespace NugetForUnity.Ui -{ - /// - /// Dynamically registers NuGet menu items so users can configure the menu root path. - /// - [InitializeOnLoad] - internal static class NugetMenu - { - private static readonly List RegisteredMenuItemPaths = new List(); - - [CanBeNull] - private static MethodInfo addMenuItemMethod; - - private static bool loggedMenuApiWarning; - - [CanBeNull] - private static MethodInfo removeMenuItemMethod; - - static NugetMenu() - { - EditorApplication.delayCall += Refresh; - } - - /// - /// Re-registers NuGet menu items at the configured menu root. - /// - public static void Refresh() - { - if (!TryInitializeMenuMethods()) - { - return; - } - - RemoveRegisteredMenuItems(); - - var menuRoot = ConfigurationManager.NugetConfigFile.MenuRoot; - foreach (var menuItem in GetMenuItems()) - { - var menuPath = $"{menuRoot}/{menuItem.Name}"; - - try - { - RemoveMenuItem(menuPath); - addMenuItemMethod.Invoke(null, new object[] { menuPath, string.Empty, false, menuItem.Priority, menuItem.Execute, null }); - RegisteredMenuItemPaths.Add(menuPath); - } - catch (Exception exception) - { - Debug.LogError($"Unable to add NuGet menu item '{menuPath}'. Error: {exception}"); - } - } - } - - [NotNull] - private static IEnumerable GetMenuItems() - { - yield return new MenuItemDefinition("Manage NuGet Packages", 0, NugetWindow.DisplayNugetWindow); - yield return new MenuItemDefinition("Restore Packages", 1, NugetWindow.RestorePackages); - yield return new MenuItemDefinition("Show Dependency Tree", 5, DependencyTreeViewer.DisplayDependencyTree); - yield return new MenuItemDefinition("Preferences", 9, NugetWindow.DisplayPreferences); - yield return new MenuItemDefinition($"Version {NugetPreferences.NuGetForUnityVersion} \uD83D\uDD17", 10, NuGetForUnityUpdater.DisplayVersion); - yield return new MenuItemDefinition("Check for Updates...", 11, NuGetForUnityUpdater.CheckForUpdates); - } - - private static void RemoveRegisteredMenuItems() - { - if (removeMenuItemMethod == null) - { - RegisteredMenuItemPaths.Clear(); - return; - } - - foreach (var menuPath in RegisteredMenuItemPaths) - { - RemoveMenuItem(menuPath); - } - - RegisteredMenuItemPaths.Clear(); - } - - private static bool TryInitializeMenuMethods() - { - if (addMenuItemMethod != null && removeMenuItemMethod != null) - { - return true; - } - - var menuType = typeof(EditorApplication).Assembly.GetType("UnityEditor.Menu"); - if (menuType == null) - { - LogMenuApiWarning("Unable to find type 'UnityEditor.Menu'. NuGet menu root configuration will not be applied."); - return false; - } - - addMenuItemMethod = menuType.GetMethod( - "AddMenuItem", - BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, - null, - new[] { typeof(string), typeof(string), typeof(bool), typeof(int), typeof(Action), typeof(Func) }, - null); - removeMenuItemMethod = menuType.GetMethod( - "RemoveMenuItem", - BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, - null, - new[] { typeof(string) }, - null); - - if (addMenuItemMethod == null || removeMenuItemMethod == null) - { - LogMenuApiWarning( - "Unable to resolve UnityEditor.Menu.AddMenuItem/RemoveMenuItem methods. NuGet menu root configuration will not be applied."); - return false; - } - - return true; - } - - private static void LogMenuApiWarning([NotNull] string message) - { - if (loggedMenuApiWarning) - { - return; - } - - loggedMenuApiWarning = true; - Debug.LogWarning(message); - } - - private static void RemoveMenuItem([NotNull] string menuPath) - { - if (removeMenuItemMethod == null) - { - return; - } - - try - { - removeMenuItemMethod.Invoke(null, new object[] { menuPath }); - } - catch (Exception) - { - // Ignore, this is a best-effort cleanup of existing menu entries. - } - } - - private sealed class MenuItemDefinition - { - public MenuItemDefinition([NotNull] string name, int priority, [NotNull] Action execute) - { - Name = name; - Priority = priority; - Execute = execute; - } - - [NotNull] - public string Name { get; } - - public int Priority { get; } - - [NotNull] - public Action Execute { get; } - } - } -} diff --git a/src/NuGetForUnity/Editor/Ui/NugetMenu.cs.meta b/src/NuGetForUnity/Editor/Ui/NugetMenu.cs.meta deleted file mode 100644 index dfc657a0..00000000 --- a/src/NuGetForUnity/Editor/Ui/NugetMenu.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 48d1f91bca5c4900946a1853927aa4d8 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs b/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs index b4bb3c32..6f971386 100644 --- a/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs +++ b/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs @@ -121,7 +121,6 @@ public override void OnGUI([CanBeNull] string searchContext) new GUIStyle(GUI.skin.toggle) { onNormal = { textColor = Color.red }, onHover = { textColor = Color.red } }; var preferencesChangedThisFrame = false; - var menuRootChangedThisFrame = false; var sourcePathChangedThisFrame = false; var needsAssetRefresh = false; @@ -129,18 +128,6 @@ public override void OnGUI([CanBeNull] string searchContext) EditorGUIUtility.labelWidth = biggestLabelSize; EditorGUILayout.LabelField($"Version: {NuGetForUnityVersion}"); - var menuRoot = EditorGUILayout.TextField( - new GUIContent( - "Menu Root Path", - "Root menu path for NuGet menu items. Examples: 'NuGet', 'Tools/NuGet', 'Window/Package Management/NuGet'."), - ConfigurationManager.NugetConfigFile.MenuRoot); - if (menuRoot != ConfigurationManager.NugetConfigFile.MenuRoot) - { - preferencesChangedThisFrame = true; - menuRootChangedThisFrame = true; - ConfigurationManager.NugetConfigFile.MenuRoot = menuRoot; - } - var installFromCache = EditorGUILayout.Toggle("Install From the Cache", ConfigurationManager.NugetConfigFile.InstallFromCache); if (installFromCache != ConfigurationManager.NugetConfigFile.InstallFromCache) { @@ -620,7 +607,6 @@ public override void OnGUI([CanBeNull] string searchContext) NugetConfigFile.CreateDefaultFile(ConfigurationManager.NugetConfigFilePath); ConfigurationManager.LoadNugetConfigFile(); preferencesChangedThisFrame = true; - menuRootChangedThisFrame = true; } if (!preferencesChangedThisFrame) @@ -637,11 +623,6 @@ public override void OnGUI([CanBeNull] string searchContext) ConfigurationManager.LoadNugetConfigFile(); } - if (menuRootChangedThisFrame) - { - NugetMenu.Refresh(); - } - if (needsAssetRefresh) { // AssetDatabase.Refresh(); doesn't work when we move the files from Assets to Packages so we use this instead: diff --git a/src/NuGetForUnity/Editor/Ui/NugetWindow.cs b/src/NuGetForUnity/Editor/Ui/NugetWindow.cs index ba2d3609..bdaee030 100644 --- a/src/NuGetForUnity/Editor/Ui/NugetWindow.cs +++ b/src/NuGetForUnity/Editor/Ui/NugetWindow.cs @@ -290,7 +290,8 @@ public void OnAfterDeserialize() /// /// Opens the NuGet Package Manager Window. /// - internal static void DisplayNugetWindow() + [MenuItem("Window/Package Management/NuGet/Manage NuGet Packages", false, 0)] + protected static void DisplayNugetWindow() { GetWindow(); } @@ -298,7 +299,8 @@ internal static void DisplayNugetWindow() /// /// Restores all packages defined in packages.config. /// - internal static void RestorePackages() + [MenuItem("Window/Package Management/NuGet/Restore Packages", false, 1)] + protected static void RestorePackages() { PackageRestorer.Restore(false); foreach (var nugetWindow in Resources.FindObjectsOfTypeAll()) @@ -310,7 +312,8 @@ internal static void RestorePackages() /// /// Opens the preferences window. /// - internal static void DisplayPreferences() + [MenuItem("Window/Package Management/NuGet/Preferences", false, 9)] + protected static void DisplayPreferences() { SettingsService.OpenProjectSettings(NugetPreferences.MenuItemLocation); } From aeb64de1cb5c269c2a269a1615d315186b96fc17 Mon Sep 17 00:00:00 2001 From: Martin Calander Date: Thu, 25 Jun 2026 21:43:02 +0200 Subject: [PATCH 05/10] Make NuGet menu location configurable --- .../Assets/Tests/Editor/NuGetTests.cs | 36 +++ .../Editor/Configuration/NugetConfigFile.cs | 55 ++++ .../Editor/Ui/DependencyTreeViewer.cs | 3 +- .../Editor/Ui/NuGetForUnityUpdater.cs | 2 - src/NuGetForUnity/Editor/Ui/NugetMenu.cs | 262 ++++++++++++++++++ src/NuGetForUnity/Editor/Ui/NugetMenu.cs.meta | 2 + .../Editor/Ui/NugetPreferences.cs | 87 +++++- src/NuGetForUnity/Editor/Ui/NugetWindow.cs | 9 +- 8 files changed, 445 insertions(+), 11 deletions(-) create mode 100644 src/NuGetForUnity/Editor/Ui/NugetMenu.cs create mode 100644 src/NuGetForUnity/Editor/Ui/NugetMenu.cs.meta diff --git a/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs b/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs index 33680a7e..091f8047 100644 --- a/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs +++ b/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs @@ -722,6 +722,42 @@ public void PackageSourceCredentialsTest(string name) Assert.That(parsedSource.SavedPassword, Is.EqualTo(password)); } + [Test] + public void MenuRootConfigRoundtripTest() + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}_{NugetConfigFile.FileName}"); + + try + { + var file = NugetConfigFile.CreateDefaultFile(path); + Assert.That(file.MenuRoot, Is.EqualTo(NugetConfigFile.DefaultMenuRoot)); + + file.MenuRoot = "Tools//Package Management\\NuGet/"; + file.Save(path); + + var loaded = NugetConfigFile.Load(path); + Assert.That(loaded.MenuRoot, Is.EqualTo("Tools/Package Management/NuGet")); + } + finally + { + if (File.Exists(path)) + { + File.Delete(path); + } + } + } + + [Test] + public void MenuRootFallsBackToDefaultWhenEmptyTest() + { + var file = new NugetConfigFile + { + MenuRoot = " /// ", + }; + + Assert.That(file.MenuRoot, Is.EqualTo(NugetConfigFile.DefaultMenuRoot)); + } + [Test] [TestCase("2018.4.30f1", false, false, false)] [TestCase("2018.4.30f1", true, false, false)] diff --git a/src/NuGetForUnity/Editor/Configuration/NugetConfigFile.cs b/src/NuGetForUnity/Editor/Configuration/NugetConfigFile.cs index 7baa678f..0c18ae35 100644 --- a/src/NuGetForUnity/Editor/Configuration/NugetConfigFile.cs +++ b/src/NuGetForUnity/Editor/Configuration/NugetConfigFile.cs @@ -37,6 +37,11 @@ public class NugetConfigFile /// public const string FileName = "NuGet.config"; + /// + /// The default root menu path where NuGet menu items are created. + /// + public const string DefaultMenuRoot = "NuGet"; + /// /// The name of the attribute that is used to configure of /// . @@ -56,6 +61,8 @@ public class NugetConfigFile private const string KeepingPdbFilesConfigKey = "KeepingPdbFiles"; + private const string MenuRootConfigKey = "MenuRoot"; + private const string ProtocolVersionAttributeName = "protocolVersion"; private const string EnableCredentialProviderAttributeName = "enableCredentialProvider"; @@ -78,6 +85,9 @@ public class NugetConfigFile [NotNull] private string repositoryPath = Path.GetFullPath(Path.Combine(Application.dataPath, "Packages")); + [NotNull] + private string menuRoot = DefaultMenuRoot; + /// /// Gets the list of package sources that are defined in the NuGet.config file. /// @@ -202,6 +212,16 @@ public string RelativePackagesConfigDirectoryPath /// internal bool KeepingPdbFiles { get; set; } + /// + /// Gets or sets the root menu path used for NuGet menu items. + /// + [NotNull] + public string MenuRoot + { + get => menuRoot; + set => menuRoot = NormalizeMenuRoot(value); + } + /// /// Gets the value that tells the system how to determine where the packages are to be installed and configurations are to be stored. /// @@ -382,6 +402,10 @@ public static NugetConfigFile Load([NotNull] string filePath) { configFile.KeepingPdbFiles = bool.Parse(value); } + else if (string.Equals(key, MenuRootConfigKey, StringComparison.OrdinalIgnoreCase)) + { + configFile.MenuRoot = value; + } } return configFile; @@ -587,6 +611,14 @@ public void Save([NotNull] string filePath) config.Add(addElement); } + if (!string.Equals(MenuRoot, DefaultMenuRoot, StringComparison.Ordinal)) + { + addElement = new XElement("add"); + addElement.Add(new XAttribute("key", MenuRootConfigKey)); + addElement.Add(new XAttribute("value", MenuRoot)); + config.Add(addElement); + } + var configuration = new XElement("configuration"); configuration.Add(packageSources); configuration.Add(disabledPackageSources); @@ -673,6 +705,29 @@ internal void FillMissingPackageCredentialsFromExternalFile([NotNull] string fil } } + /// + /// Normalizes a user configured menu root path. + /// + /// Configured menu root path. + /// Normalized menu root path. + [NotNull] + public static string NormalizeMenuRoot([CanBeNull] string menuRootPath) + { + if (string.IsNullOrWhiteSpace(menuRootPath)) + { + return DefaultMenuRoot; + } + + var normalized = menuRootPath.Trim().Replace('\\', '/'); + while (normalized.Contains("//")) + { + normalized = normalized.Replace("//", "/"); + } + + normalized = normalized.Trim('/'); + return string.IsNullOrWhiteSpace(normalized) ? DefaultMenuRoot : normalized; + } + private void FillPackageSourceCredentialsFromConfig(XDocument file, bool overwriteMissingFromExternal) { var packageSourceCredentials = file.Root?.Element("packageSourceCredentials"); diff --git a/src/NuGetForUnity/Editor/Ui/DependencyTreeViewer.cs b/src/NuGetForUnity/Editor/Ui/DependencyTreeViewer.cs index af35b9a4..1e22650d 100644 --- a/src/NuGetForUnity/Editor/Ui/DependencyTreeViewer.cs +++ b/src/NuGetForUnity/Editor/Ui/DependencyTreeViewer.cs @@ -49,8 +49,7 @@ public class DependencyTreeViewer : EditorWindow /// /// Opens the NuGet Package Manager Window. /// - [MenuItem("Window/Package Management/NuGet/Show Dependency Tree", false, 5)] - protected static void DisplayDependencyTree() + internal static void DisplayDependencyTree() { GetWindow(); } diff --git a/src/NuGetForUnity/Editor/Ui/NuGetForUnityUpdater.cs b/src/NuGetForUnity/Editor/Ui/NuGetForUnityUpdater.cs index aa3a4a76..081a89dd 100644 --- a/src/NuGetForUnity/Editor/Ui/NuGetForUnityUpdater.cs +++ b/src/NuGetForUnity/Editor/Ui/NuGetForUnityUpdater.cs @@ -30,7 +30,6 @@ internal static class NuGetForUnityUpdater /// /// Opens release notes for the current version. /// - [MenuItem("Window/Package Management/NuGet/Version " + NugetPreferences.NuGetForUnityVersion + " \uD83D\uDD17", false, 10)] public static void DisplayVersion() { Application.OpenURL($"{GitHubReleasesPageUrl}/tag/v{NugetPreferences.NuGetForUnityVersion}"); @@ -39,7 +38,6 @@ public static void DisplayVersion() /// /// Checks/launches the Releases page to update NuGetForUnity with a new version. /// - [MenuItem("Window/Package Management/NuGet/Check for Updates...", false, 11)] public static void CheckForUpdates() { var request = UnityWebRequest.Get(GitHubReleasesApiUrl); diff --git a/src/NuGetForUnity/Editor/Ui/NugetMenu.cs b/src/NuGetForUnity/Editor/Ui/NugetMenu.cs new file mode 100644 index 00000000..76860126 --- /dev/null +++ b/src/NuGetForUnity/Editor/Ui/NugetMenu.cs @@ -0,0 +1,262 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using JetBrains.Annotations; +using NugetForUnity.Configuration; +using UnityEditor; +using UnityEngine; + +namespace NugetForUnity.Ui +{ + /// + /// Dynamically registers NuGet menu items so users can configure the menu root path. + /// + [InitializeOnLoad] + internal static class NugetMenu + { + private const string RegisteredMenuRootSessionStateKey = "NuGetForUnity.RegisteredMenuRoot"; + + private static readonly string[] KnownMenuRoots = + { + NugetConfigFile.DefaultMenuRoot, + "Tools/NuGet", + "Window/Package Management/NuGet", + }; + + private static readonly List RegisteredMenuItemPaths = new List(); + + [CanBeNull] + private static MethodInfo addMenuItemMethod; + + private static bool loggedMenuApiWarning; + + [CanBeNull] + private static MethodInfo removeMenuItemMethod; + + static NugetMenu() + { + EditorApplication.delayCall += Refresh; + } + + [MenuItem(NugetConfigFile.DefaultMenuRoot + "/Manage NuGet Packages", false, 0)] + private static void DisplayNugetWindow() + { + NugetWindow.DisplayNugetWindow(); + } + + [MenuItem(NugetConfigFile.DefaultMenuRoot + "/Restore Packages", false, 1)] + private static void RestorePackages() + { + NugetWindow.RestorePackages(); + } + + [MenuItem(NugetConfigFile.DefaultMenuRoot + "/Show Dependency Tree", false, 5)] + private static void DisplayDependencyTree() + { + DependencyTreeViewer.DisplayDependencyTree(); + } + + [MenuItem(NugetConfigFile.DefaultMenuRoot + "/Preferences", false, 9)] + private static void DisplayPreferences() + { + NugetWindow.DisplayPreferences(); + } + + [MenuItem(NugetConfigFile.DefaultMenuRoot + "/Version " + NugetPreferences.NuGetForUnityVersion + " \uD83D\uDD17", false, 10)] + private static void DisplayVersion() + { + NuGetForUnityUpdater.DisplayVersion(); + } + + [MenuItem(NugetConfigFile.DefaultMenuRoot + "/Check for Updates...", false, 11)] + private static void CheckForUpdates() + { + NuGetForUnityUpdater.CheckForUpdates(); + } + + /// + /// Re-registers NuGet menu items at the configured menu root. + /// + public static void Refresh() + { + if (!TryInitializeMenuMethods()) + { + return; + } + + RemoveRegisteredMenuItems(); + + var menuRoot = GetConfiguredMenuRoot(); + foreach (var menuItem in GetMenuItems()) + { + var menuPath = $"{menuRoot}/{menuItem.Name}"; + + try + { + RemoveMenuItem(menuPath); + addMenuItemMethod.Invoke(null, new object[] { menuPath, string.Empty, false, menuItem.Priority, menuItem.Execute, menuItem.Validate }); + RegisteredMenuItemPaths.Add(menuPath); + } + catch (Exception exception) + { + Debug.LogError($"Unable to add NuGet menu item '{menuPath}'. Error: {exception}"); + } + } + + SessionState.SetString(RegisteredMenuRootSessionStateKey, menuRoot); + } + + [NotNull] + private static string GetConfiguredMenuRoot() + { + try + { + if (!File.Exists(ConfigurationManager.NugetConfigFilePath)) + { + return NugetConfigFile.DefaultMenuRoot; + } + + return ConfigurationManager.NugetConfigFile.MenuRoot; + } + catch (Exception exception) + { + Debug.LogWarning($"Unable to load NuGet menu root setting. Falling back to '{NugetConfigFile.DefaultMenuRoot}'. Error: {exception}"); + return NugetConfigFile.DefaultMenuRoot; + } + } + + [NotNull] + private static IEnumerable GetMenuItems() + { + yield return new MenuItemDefinition("Manage NuGet Packages", 0, NugetWindow.DisplayNugetWindow); + yield return new MenuItemDefinition("Restore Packages", 1, NugetWindow.RestorePackages); + yield return new MenuItemDefinition("Show Dependency Tree", 5, DependencyTreeViewer.DisplayDependencyTree); + yield return new MenuItemDefinition("Preferences", 9, NugetWindow.DisplayPreferences); + yield return new MenuItemDefinition($"Version {NugetPreferences.NuGetForUnityVersion} \uD83D\uDD17", 10, NuGetForUnityUpdater.DisplayVersion); + yield return new MenuItemDefinition("Check for Updates...", 11, NuGetForUnityUpdater.CheckForUpdates); + } + + private static void RemoveRegisteredMenuItems() + { + foreach (var menuPath in RegisteredMenuItemPaths) + { + RemoveMenuItem(menuPath); + } + + RegisteredMenuItemPaths.Clear(); + + var menuRootsToClear = new HashSet(StringComparer.Ordinal); + foreach (var menuRoot in KnownMenuRoots) + { + menuRootsToClear.Add(menuRoot); + } + + var sessionMenuRoot = SessionState.GetString(RegisteredMenuRootSessionStateKey, string.Empty); + if (!string.IsNullOrEmpty(sessionMenuRoot)) + { + menuRootsToClear.Add(sessionMenuRoot); + } + + foreach (var menuRoot in menuRootsToClear) + { + RemoveMenuItems(menuRoot); + } + } + + private static void RemoveMenuItems([NotNull] string menuRoot) + { + foreach (var menuItem in GetMenuItems()) + { + RemoveMenuItem($"{menuRoot}/{menuItem.Name}"); + } + } + + private static bool TryInitializeMenuMethods() + { + if (addMenuItemMethod != null && removeMenuItemMethod != null) + { + return true; + } + + var menuType = typeof(EditorApplication).Assembly.GetType("UnityEditor.Menu"); + if (menuType == null) + { + LogMenuApiWarning("Unable to find type 'UnityEditor.Menu'. NuGet menu root configuration will not be applied."); + return false; + } + + addMenuItemMethod = menuType.GetMethod( + "AddMenuItem", + BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, + null, + new[] { typeof(string), typeof(string), typeof(bool), typeof(int), typeof(Action), typeof(Func) }, + null); + removeMenuItemMethod = menuType.GetMethod( + "RemoveMenuItem", + BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, + null, + new[] { typeof(string) }, + null); + + if (addMenuItemMethod == null || removeMenuItemMethod == null) + { + LogMenuApiWarning( + "Unable to resolve UnityEditor.Menu.AddMenuItem/RemoveMenuItem methods. NuGet menu root configuration will not be applied."); + return false; + } + + return true; + } + + private static void LogMenuApiWarning([NotNull] string message) + { + if (loggedMenuApiWarning) + { + return; + } + + loggedMenuApiWarning = true; + Debug.LogWarning(message); + } + + private static void RemoveMenuItem([NotNull] string menuPath) + { + if (removeMenuItemMethod == null) + { + return; + } + + try + { + removeMenuItemMethod.Invoke(null, new object[] { menuPath }); + } + catch (Exception) + { + // Menu cleanup is best-effort; missing menu items are harmless. + } + } + + private sealed class MenuItemDefinition + { + public MenuItemDefinition([NotNull] string name, int priority, [NotNull] Action execute, [CanBeNull] Func validate = null) + { + Name = name; + Priority = priority; + Execute = execute; + Validate = validate; + } + + [NotNull] + public string Name { get; } + + public int Priority { get; } + + [NotNull] + public Action Execute { get; } + + [CanBeNull] + public Func Validate { get; } + } + } +} diff --git a/src/NuGetForUnity/Editor/Ui/NugetMenu.cs.meta b/src/NuGetForUnity/Editor/Ui/NugetMenu.cs.meta new file mode 100644 index 00000000..c3f5216a --- /dev/null +++ b/src/NuGetForUnity/Editor/Ui/NugetMenu.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 303d73d13c81749ff81dea424bf5bd9e \ No newline at end of file diff --git a/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs b/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs index 6f971386..b88c22a4 100644 --- a/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs +++ b/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs @@ -44,7 +44,24 @@ public class NugetPreferences : SettingsProvider private const float LabelPading = 5; - private static readonly string[] SearchKeywords = { "NuGet", "Packages" }; + private static readonly string[] SearchKeywords = { "NuGet", "Packages", "Menu" }; + + private static readonly string[] MenuRootPresetPaths = + { + NugetConfigFile.DefaultMenuRoot, + "Tools/NuGet", + "Window/Package Management/NuGet", + }; + + private static readonly GUIContent[] MenuRootPresetLabels = + { + new GUIContent("NuGet"), + new GUIContent("Tools/NuGet"), + new GUIContent("Window/Package Management/NuGet"), + new GUIContent("Custom"), + }; + + private const int CustomMenuRootPresetIndex = 3; private readonly GUIContent deleteX = new GUIContent("\u2716"); @@ -60,6 +77,8 @@ public class NugetPreferences : SettingsProvider private float? biggestPackageSourceSectionLabelSize; + private bool customMenuRootSelected; + /// /// The current position of the scroll bar in the GUI for the list of plugins. /// @@ -121,6 +140,7 @@ public override void OnGUI([CanBeNull] string searchContext) new GUIStyle(GUI.skin.toggle) { onNormal = { textColor = Color.red }, onHover = { textColor = Color.red } }; var preferencesChangedThisFrame = false; + var menuRootChangedThisFrame = false; var sourcePathChangedThisFrame = false; var needsAssetRefresh = false; @@ -128,6 +148,52 @@ public override void OnGUI([CanBeNull] string searchContext) EditorGUIUtility.labelWidth = biggestLabelSize; EditorGUILayout.LabelField($"Version: {NuGetForUnityVersion}"); + var menuRoot = ConfigurationManager.NugetConfigFile.MenuRoot; + var currentMenuRootPresetIndex = GetMenuRootPresetIndex(menuRoot); + if (currentMenuRootPresetIndex == CustomMenuRootPresetIndex) + { + customMenuRootSelected = true; + } + + var displayedMenuRootPresetIndex = customMenuRootSelected ? CustomMenuRootPresetIndex : currentMenuRootPresetIndex; + var selectedMenuRootPresetIndex = EditorGUILayout.Popup( + new GUIContent( + "NuGet Menu Location", + "Root menu path for NuGet menu items."), + displayedMenuRootPresetIndex, + MenuRootPresetLabels); + + if (selectedMenuRootPresetIndex != displayedMenuRootPresetIndex) + { + customMenuRootSelected = selectedMenuRootPresetIndex == CustomMenuRootPresetIndex; + if (!customMenuRootSelected && + selectedMenuRootPresetIndex >= 0 && + selectedMenuRootPresetIndex < MenuRootPresetPaths.Length && + MenuRootPresetPaths[selectedMenuRootPresetIndex] != ConfigurationManager.NugetConfigFile.MenuRoot) + { + preferencesChangedThisFrame = true; + menuRootChangedThisFrame = true; + ConfigurationManager.NugetConfigFile.MenuRoot = MenuRootPresetPaths[selectedMenuRootPresetIndex]; + } + } + + if (customMenuRootSelected) + { + var customMenuRoot = EditorGUILayout.DelayedTextField( + new GUIContent( + "Custom Menu Path", + "Examples: 'NuGet', 'Tools/NuGet', 'Window/Package Management/NuGet'."), + menuRoot); + customMenuRoot = NugetConfigFile.NormalizeMenuRoot(customMenuRoot); + if (customMenuRoot != ConfigurationManager.NugetConfigFile.MenuRoot) + { + preferencesChangedThisFrame = true; + menuRootChangedThisFrame = true; + ConfigurationManager.NugetConfigFile.MenuRoot = customMenuRoot; + customMenuRootSelected = GetMenuRootPresetIndex(customMenuRoot) == CustomMenuRootPresetIndex; + } + } + var installFromCache = EditorGUILayout.Toggle("Install From the Cache", ConfigurationManager.NugetConfigFile.InstallFromCache); if (installFromCache != ConfigurationManager.NugetConfigFile.InstallFromCache) { @@ -607,6 +673,7 @@ public override void OnGUI([CanBeNull] string searchContext) NugetConfigFile.CreateDefaultFile(ConfigurationManager.NugetConfigFilePath); ConfigurationManager.LoadNugetConfigFile(); preferencesChangedThisFrame = true; + menuRootChangedThisFrame = true; } if (!preferencesChangedThisFrame) @@ -623,6 +690,11 @@ public override void OnGUI([CanBeNull] string searchContext) ConfigurationManager.LoadNugetConfigFile(); } + if (menuRootChangedThisFrame) + { + NugetMenu.Refresh(); + } + if (needsAssetRefresh) { // AssetDatabase.Refresh(); doesn't work when we move the files from Assets to Packages so we use this instead: @@ -630,6 +702,19 @@ public override void OnGUI([CanBeNull] string searchContext) } } + private static int GetMenuRootPresetIndex([NotNull] string menuRoot) + { + for (var i = 0; i < MenuRootPresetPaths.Length; i++) + { + if (string.Equals(menuRoot, MenuRootPresetPaths[i], StringComparison.Ordinal)) + { + return i; + } + } + + return CustomMenuRootPresetIndex; + } + private sealed class NugetPlugin { public NugetPlugin([NotNull] Assembly assembly, bool enabled) diff --git a/src/NuGetForUnity/Editor/Ui/NugetWindow.cs b/src/NuGetForUnity/Editor/Ui/NugetWindow.cs index cbbbc61c..39bea7dd 100644 --- a/src/NuGetForUnity/Editor/Ui/NugetWindow.cs +++ b/src/NuGetForUnity/Editor/Ui/NugetWindow.cs @@ -290,8 +290,7 @@ public void OnAfterDeserialize() /// /// Opens the NuGet Package Manager Window. /// - [MenuItem("Window/Package Management/NuGet/Manage NuGet Packages", false, 0)] - protected static void DisplayNugetWindow() + internal static void DisplayNugetWindow() { GetWindow(); } @@ -299,8 +298,7 @@ protected static void DisplayNugetWindow() /// /// Restores all packages defined in packages.config. /// - [MenuItem("Window/Package Management/NuGet/Restore Packages", false, 1)] - protected static void RestorePackages() + internal static void RestorePackages() { PackageRestorer.Restore(false); foreach (var nugetWindow in Resources.FindObjectsOfTypeAll()) @@ -312,8 +310,7 @@ protected static void RestorePackages() /// /// Opens the preferences window. /// - [MenuItem("Window/Package Management/NuGet/Preferences", false, 9)] - protected static void DisplayPreferences() + internal static void DisplayPreferences() { SettingsService.OpenProjectSettings(NugetPreferences.MenuItemLocation); } From 830405e2a56d75d6548b4282f0c85881bf586628 Mon Sep 17 00:00:00 2001 From: Martin Calander Date: Sat, 27 Jun 2026 13:19:11 +0200 Subject: [PATCH 06/10] Use script defines for NuGet menu path --- README.md | 2 + .../Assets/Tests/Editor/NuGetTests.cs | 40 +-- .../Editor/Configuration/NugetConfigFile.cs | 50 ---- src/NuGetForUnity/Editor/Ui/NugetMenu.cs | 239 ++---------------- .../Editor/Ui/NugetPreferences.cs | 86 +------ 5 files changed, 51 insertions(+), 366 deletions(-) diff --git a/README.md b/README.md index 0e7813e9..aae996bf 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,8 @@ Download the `*.unitypackage` file. Right-click on it in File Explorer and choos To launch, select **NuGet → Manage NuGet Packages** +The menu root is selected at compile time. Without a scripting define symbol it appears under **NuGet**. Define `NUGETFORUNITY_MENU_TOOLS` to use **Tools → NuGet**, or `NUGETFORUNITY_MENU_WINDOW_PACKAGE_MANAGER` to use **Window → Package Manager → NuGet**. Define only one menu location symbol. + Menu Items After several seconds (it can take some time to query the server for packages), you should see a window like this: diff --git a/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs b/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs index 091f8047..ad4c078b 100644 --- a/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs +++ b/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs @@ -723,20 +723,35 @@ public void PackageSourceCredentialsTest(string name) } [Test] - public void MenuRootConfigRoundtripTest() + public void MenuRootMatchesScriptDefineSymbolsTest() + { +#if NUGETFORUNITY_MENU_TOOLS + Assert.That(NugetMenu.MenuRoot, Is.EqualTo("Tools/NuGet")); +#elif NUGETFORUNITY_MENU_WINDOW_PACKAGE_MANAGER + Assert.That(NugetMenu.MenuRoot, Is.EqualTo("Window/Package Manager/NuGet")); +#else + Assert.That(NugetMenu.MenuRoot, Is.EqualTo(NugetConfigFile.DefaultMenuRoot)); +#endif + } + + [Test] + public void MenuRootConfigIsNotSavedTest() { var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}_{NugetConfigFile.FileName}"); try { - var file = NugetConfigFile.CreateDefaultFile(path); - Assert.That(file.MenuRoot, Is.EqualTo(NugetConfigFile.DefaultMenuRoot)); - - file.MenuRoot = "Tools//Package Management\\NuGet/"; + NugetConfigFile.CreateDefaultFile(path); + File.WriteAllText( + path, + File.ReadAllText(path).Replace( + " ", + " \n ")); + + var file = NugetConfigFile.Load(path); file.Save(path); - var loaded = NugetConfigFile.Load(path); - Assert.That(loaded.MenuRoot, Is.EqualTo("Tools/Package Management/NuGet")); + Assert.That(File.ReadAllText(path), Does.Not.Contain("MenuRoot")); } finally { @@ -747,17 +762,6 @@ public void MenuRootConfigRoundtripTest() } } - [Test] - public void MenuRootFallsBackToDefaultWhenEmptyTest() - { - var file = new NugetConfigFile - { - MenuRoot = " /// ", - }; - - Assert.That(file.MenuRoot, Is.EqualTo(NugetConfigFile.DefaultMenuRoot)); - } - [Test] [TestCase("2018.4.30f1", false, false, false)] [TestCase("2018.4.30f1", true, false, false)] diff --git a/src/NuGetForUnity/Editor/Configuration/NugetConfigFile.cs b/src/NuGetForUnity/Editor/Configuration/NugetConfigFile.cs index 0c18ae35..072043d3 100644 --- a/src/NuGetForUnity/Editor/Configuration/NugetConfigFile.cs +++ b/src/NuGetForUnity/Editor/Configuration/NugetConfigFile.cs @@ -61,8 +61,6 @@ public class NugetConfigFile private const string KeepingPdbFilesConfigKey = "KeepingPdbFiles"; - private const string MenuRootConfigKey = "MenuRoot"; - private const string ProtocolVersionAttributeName = "protocolVersion"; private const string EnableCredentialProviderAttributeName = "enableCredentialProvider"; @@ -85,9 +83,6 @@ public class NugetConfigFile [NotNull] private string repositoryPath = Path.GetFullPath(Path.Combine(Application.dataPath, "Packages")); - [NotNull] - private string menuRoot = DefaultMenuRoot; - /// /// Gets the list of package sources that are defined in the NuGet.config file. /// @@ -212,16 +207,6 @@ public string RelativePackagesConfigDirectoryPath /// internal bool KeepingPdbFiles { get; set; } - /// - /// Gets or sets the root menu path used for NuGet menu items. - /// - [NotNull] - public string MenuRoot - { - get => menuRoot; - set => menuRoot = NormalizeMenuRoot(value); - } - /// /// Gets the value that tells the system how to determine where the packages are to be installed and configurations are to be stored. /// @@ -402,10 +387,6 @@ public static NugetConfigFile Load([NotNull] string filePath) { configFile.KeepingPdbFiles = bool.Parse(value); } - else if (string.Equals(key, MenuRootConfigKey, StringComparison.OrdinalIgnoreCase)) - { - configFile.MenuRoot = value; - } } return configFile; @@ -611,14 +592,6 @@ public void Save([NotNull] string filePath) config.Add(addElement); } - if (!string.Equals(MenuRoot, DefaultMenuRoot, StringComparison.Ordinal)) - { - addElement = new XElement("add"); - addElement.Add(new XAttribute("key", MenuRootConfigKey)); - addElement.Add(new XAttribute("value", MenuRoot)); - config.Add(addElement); - } - var configuration = new XElement("configuration"); configuration.Add(packageSources); configuration.Add(disabledPackageSources); @@ -705,29 +678,6 @@ internal void FillMissingPackageCredentialsFromExternalFile([NotNull] string fil } } - /// - /// Normalizes a user configured menu root path. - /// - /// Configured menu root path. - /// Normalized menu root path. - [NotNull] - public static string NormalizeMenuRoot([CanBeNull] string menuRootPath) - { - if (string.IsNullOrWhiteSpace(menuRootPath)) - { - return DefaultMenuRoot; - } - - var normalized = menuRootPath.Trim().Replace('\\', '/'); - while (normalized.Contains("//")) - { - normalized = normalized.Replace("//", "/"); - } - - normalized = normalized.Trim('/'); - return string.IsNullOrWhiteSpace(normalized) ? DefaultMenuRoot : normalized; - } - private void FillPackageSourceCredentialsFromConfig(XDocument file, bool overwriteMissingFromExternal) { var packageSourceCredentials = file.Root?.Element("packageSourceCredentials"); diff --git a/src/NuGetForUnity/Editor/Ui/NugetMenu.cs b/src/NuGetForUnity/Editor/Ui/NugetMenu.cs index 76860126..5672869e 100644 --- a/src/NuGetForUnity/Editor/Ui/NugetMenu.cs +++ b/src/NuGetForUnity/Editor/Ui/NugetMenu.cs @@ -1,262 +1,71 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using JetBrains.Annotations; using NugetForUnity.Configuration; using UnityEditor; -using UnityEngine; namespace NugetForUnity.Ui { +#if NUGETFORUNITY_MENU_TOOLS && NUGETFORUNITY_MENU_WINDOW_PACKAGE_MANAGER +#error Define only one NuGetForUnity menu location symbol. +#endif + /// - /// Dynamically registers NuGet menu items so users can configure the menu root path. + /// Registers NuGet menu items at the compile-time selected root path. /// - [InitializeOnLoad] internal static class NugetMenu { - private const string RegisteredMenuRootSessionStateKey = "NuGetForUnity.RegisteredMenuRoot"; +#if NUGETFORUNITY_MENU_TOOLS + internal const string MenuRoot = "Tools/NuGet"; +#elif NUGETFORUNITY_MENU_WINDOW_PACKAGE_MANAGER + internal const string MenuRoot = "Window/Package Manager/NuGet"; +#else + internal const string MenuRoot = NugetConfigFile.DefaultMenuRoot; +#endif - private static readonly string[] KnownMenuRoots = - { - NugetConfigFile.DefaultMenuRoot, - "Tools/NuGet", - "Window/Package Management/NuGet", - }; + internal const string ManagePackagesMenuItem = MenuRoot + "/Manage NuGet Packages"; - private static readonly List RegisteredMenuItemPaths = new List(); + internal const string RestorePackagesMenuItem = MenuRoot + "/Restore Packages"; - [CanBeNull] - private static MethodInfo addMenuItemMethod; + internal const string DependencyTreeMenuItem = MenuRoot + "/Show Dependency Tree"; - private static bool loggedMenuApiWarning; + internal const string PreferencesMenuItem = MenuRoot + "/Preferences"; - [CanBeNull] - private static MethodInfo removeMenuItemMethod; + internal const string VersionMenuItem = MenuRoot + "/Version " + NugetPreferences.NuGetForUnityVersion + " \uD83D\uDD17"; - static NugetMenu() - { - EditorApplication.delayCall += Refresh; - } + internal const string CheckForUpdatesMenuItem = MenuRoot + "/Check for Updates..."; - [MenuItem(NugetConfigFile.DefaultMenuRoot + "/Manage NuGet Packages", false, 0)] + [MenuItem(ManagePackagesMenuItem, false, 0)] private static void DisplayNugetWindow() { NugetWindow.DisplayNugetWindow(); } - [MenuItem(NugetConfigFile.DefaultMenuRoot + "/Restore Packages", false, 1)] + [MenuItem(RestorePackagesMenuItem, false, 1)] private static void RestorePackages() { NugetWindow.RestorePackages(); } - [MenuItem(NugetConfigFile.DefaultMenuRoot + "/Show Dependency Tree", false, 5)] + [MenuItem(DependencyTreeMenuItem, false, 5)] private static void DisplayDependencyTree() { DependencyTreeViewer.DisplayDependencyTree(); } - [MenuItem(NugetConfigFile.DefaultMenuRoot + "/Preferences", false, 9)] + [MenuItem(PreferencesMenuItem, false, 9)] private static void DisplayPreferences() { NugetWindow.DisplayPreferences(); } - [MenuItem(NugetConfigFile.DefaultMenuRoot + "/Version " + NugetPreferences.NuGetForUnityVersion + " \uD83D\uDD17", false, 10)] + [MenuItem(VersionMenuItem, false, 10)] private static void DisplayVersion() { NuGetForUnityUpdater.DisplayVersion(); } - [MenuItem(NugetConfigFile.DefaultMenuRoot + "/Check for Updates...", false, 11)] + [MenuItem(CheckForUpdatesMenuItem, false, 11)] private static void CheckForUpdates() { NuGetForUnityUpdater.CheckForUpdates(); } - - /// - /// Re-registers NuGet menu items at the configured menu root. - /// - public static void Refresh() - { - if (!TryInitializeMenuMethods()) - { - return; - } - - RemoveRegisteredMenuItems(); - - var menuRoot = GetConfiguredMenuRoot(); - foreach (var menuItem in GetMenuItems()) - { - var menuPath = $"{menuRoot}/{menuItem.Name}"; - - try - { - RemoveMenuItem(menuPath); - addMenuItemMethod.Invoke(null, new object[] { menuPath, string.Empty, false, menuItem.Priority, menuItem.Execute, menuItem.Validate }); - RegisteredMenuItemPaths.Add(menuPath); - } - catch (Exception exception) - { - Debug.LogError($"Unable to add NuGet menu item '{menuPath}'. Error: {exception}"); - } - } - - SessionState.SetString(RegisteredMenuRootSessionStateKey, menuRoot); - } - - [NotNull] - private static string GetConfiguredMenuRoot() - { - try - { - if (!File.Exists(ConfigurationManager.NugetConfigFilePath)) - { - return NugetConfigFile.DefaultMenuRoot; - } - - return ConfigurationManager.NugetConfigFile.MenuRoot; - } - catch (Exception exception) - { - Debug.LogWarning($"Unable to load NuGet menu root setting. Falling back to '{NugetConfigFile.DefaultMenuRoot}'. Error: {exception}"); - return NugetConfigFile.DefaultMenuRoot; - } - } - - [NotNull] - private static IEnumerable GetMenuItems() - { - yield return new MenuItemDefinition("Manage NuGet Packages", 0, NugetWindow.DisplayNugetWindow); - yield return new MenuItemDefinition("Restore Packages", 1, NugetWindow.RestorePackages); - yield return new MenuItemDefinition("Show Dependency Tree", 5, DependencyTreeViewer.DisplayDependencyTree); - yield return new MenuItemDefinition("Preferences", 9, NugetWindow.DisplayPreferences); - yield return new MenuItemDefinition($"Version {NugetPreferences.NuGetForUnityVersion} \uD83D\uDD17", 10, NuGetForUnityUpdater.DisplayVersion); - yield return new MenuItemDefinition("Check for Updates...", 11, NuGetForUnityUpdater.CheckForUpdates); - } - - private static void RemoveRegisteredMenuItems() - { - foreach (var menuPath in RegisteredMenuItemPaths) - { - RemoveMenuItem(menuPath); - } - - RegisteredMenuItemPaths.Clear(); - - var menuRootsToClear = new HashSet(StringComparer.Ordinal); - foreach (var menuRoot in KnownMenuRoots) - { - menuRootsToClear.Add(menuRoot); - } - - var sessionMenuRoot = SessionState.GetString(RegisteredMenuRootSessionStateKey, string.Empty); - if (!string.IsNullOrEmpty(sessionMenuRoot)) - { - menuRootsToClear.Add(sessionMenuRoot); - } - - foreach (var menuRoot in menuRootsToClear) - { - RemoveMenuItems(menuRoot); - } - } - - private static void RemoveMenuItems([NotNull] string menuRoot) - { - foreach (var menuItem in GetMenuItems()) - { - RemoveMenuItem($"{menuRoot}/{menuItem.Name}"); - } - } - - private static bool TryInitializeMenuMethods() - { - if (addMenuItemMethod != null && removeMenuItemMethod != null) - { - return true; - } - - var menuType = typeof(EditorApplication).Assembly.GetType("UnityEditor.Menu"); - if (menuType == null) - { - LogMenuApiWarning("Unable to find type 'UnityEditor.Menu'. NuGet menu root configuration will not be applied."); - return false; - } - - addMenuItemMethod = menuType.GetMethod( - "AddMenuItem", - BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, - null, - new[] { typeof(string), typeof(string), typeof(bool), typeof(int), typeof(Action), typeof(Func) }, - null); - removeMenuItemMethod = menuType.GetMethod( - "RemoveMenuItem", - BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, - null, - new[] { typeof(string) }, - null); - - if (addMenuItemMethod == null || removeMenuItemMethod == null) - { - LogMenuApiWarning( - "Unable to resolve UnityEditor.Menu.AddMenuItem/RemoveMenuItem methods. NuGet menu root configuration will not be applied."); - return false; - } - - return true; - } - - private static void LogMenuApiWarning([NotNull] string message) - { - if (loggedMenuApiWarning) - { - return; - } - - loggedMenuApiWarning = true; - Debug.LogWarning(message); - } - - private static void RemoveMenuItem([NotNull] string menuPath) - { - if (removeMenuItemMethod == null) - { - return; - } - - try - { - removeMenuItemMethod.Invoke(null, new object[] { menuPath }); - } - catch (Exception) - { - // Menu cleanup is best-effort; missing menu items are harmless. - } - } - - private sealed class MenuItemDefinition - { - public MenuItemDefinition([NotNull] string name, int priority, [NotNull] Action execute, [CanBeNull] Func validate = null) - { - Name = name; - Priority = priority; - Execute = execute; - Validate = validate; - } - - [NotNull] - public string Name { get; } - - public int Priority { get; } - - [NotNull] - public Action Execute { get; } - - [CanBeNull] - public Func Validate { get; } - } } } diff --git a/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs b/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs index b88c22a4..6795524f 100644 --- a/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs +++ b/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs @@ -46,23 +46,6 @@ public class NugetPreferences : SettingsProvider private static readonly string[] SearchKeywords = { "NuGet", "Packages", "Menu" }; - private static readonly string[] MenuRootPresetPaths = - { - NugetConfigFile.DefaultMenuRoot, - "Tools/NuGet", - "Window/Package Management/NuGet", - }; - - private static readonly GUIContent[] MenuRootPresetLabels = - { - new GUIContent("NuGet"), - new GUIContent("Tools/NuGet"), - new GUIContent("Window/Package Management/NuGet"), - new GUIContent("Custom"), - }; - - private const int CustomMenuRootPresetIndex = 3; - private readonly GUIContent deleteX = new GUIContent("\u2716"); private readonly GUIContent downArrow = new GUIContent("\u25bc"); @@ -77,8 +60,6 @@ public class NugetPreferences : SettingsProvider private float? biggestPackageSourceSectionLabelSize; - private bool customMenuRootSelected; - /// /// The current position of the scroll bar in the GUI for the list of plugins. /// @@ -140,59 +121,17 @@ public override void OnGUI([CanBeNull] string searchContext) new GUIStyle(GUI.skin.toggle) { onNormal = { textColor = Color.red }, onHover = { textColor = Color.red } }; var preferencesChangedThisFrame = false; - var menuRootChangedThisFrame = false; var sourcePathChangedThisFrame = false; var needsAssetRefresh = false; var biggestLabelSize = EditorStyles.label.CalcSize(new GUIContent("Prefer .NET Standard dependencies over .NET Framework")).x; EditorGUIUtility.labelWidth = biggestLabelSize; EditorGUILayout.LabelField($"Version: {NuGetForUnityVersion}"); - - var menuRoot = ConfigurationManager.NugetConfigFile.MenuRoot; - var currentMenuRootPresetIndex = GetMenuRootPresetIndex(menuRoot); - if (currentMenuRootPresetIndex == CustomMenuRootPresetIndex) - { - customMenuRootSelected = true; - } - - var displayedMenuRootPresetIndex = customMenuRootSelected ? CustomMenuRootPresetIndex : currentMenuRootPresetIndex; - var selectedMenuRootPresetIndex = EditorGUILayout.Popup( + EditorGUILayout.LabelField( new GUIContent( "NuGet Menu Location", - "Root menu path for NuGet menu items."), - displayedMenuRootPresetIndex, - MenuRootPresetLabels); - - if (selectedMenuRootPresetIndex != displayedMenuRootPresetIndex) - { - customMenuRootSelected = selectedMenuRootPresetIndex == CustomMenuRootPresetIndex; - if (!customMenuRootSelected && - selectedMenuRootPresetIndex >= 0 && - selectedMenuRootPresetIndex < MenuRootPresetPaths.Length && - MenuRootPresetPaths[selectedMenuRootPresetIndex] != ConfigurationManager.NugetConfigFile.MenuRoot) - { - preferencesChangedThisFrame = true; - menuRootChangedThisFrame = true; - ConfigurationManager.NugetConfigFile.MenuRoot = MenuRootPresetPaths[selectedMenuRootPresetIndex]; - } - } - - if (customMenuRootSelected) - { - var customMenuRoot = EditorGUILayout.DelayedTextField( - new GUIContent( - "Custom Menu Path", - "Examples: 'NuGet', 'Tools/NuGet', 'Window/Package Management/NuGet'."), - menuRoot); - customMenuRoot = NugetConfigFile.NormalizeMenuRoot(customMenuRoot); - if (customMenuRoot != ConfigurationManager.NugetConfigFile.MenuRoot) - { - preferencesChangedThisFrame = true; - menuRootChangedThisFrame = true; - ConfigurationManager.NugetConfigFile.MenuRoot = customMenuRoot; - customMenuRootSelected = GetMenuRootPresetIndex(customMenuRoot) == CustomMenuRootPresetIndex; - } - } + "Set NUGETFORUNITY_MENU_TOOLS or NUGETFORUNITY_MENU_WINDOW_PACKAGE_MANAGER in scripting define symbols to change this path."), + NugetMenu.MenuRoot); var installFromCache = EditorGUILayout.Toggle("Install From the Cache", ConfigurationManager.NugetConfigFile.InstallFromCache); if (installFromCache != ConfigurationManager.NugetConfigFile.InstallFromCache) @@ -673,7 +612,6 @@ public override void OnGUI([CanBeNull] string searchContext) NugetConfigFile.CreateDefaultFile(ConfigurationManager.NugetConfigFilePath); ConfigurationManager.LoadNugetConfigFile(); preferencesChangedThisFrame = true; - menuRootChangedThisFrame = true; } if (!preferencesChangedThisFrame) @@ -690,11 +628,6 @@ public override void OnGUI([CanBeNull] string searchContext) ConfigurationManager.LoadNugetConfigFile(); } - if (menuRootChangedThisFrame) - { - NugetMenu.Refresh(); - } - if (needsAssetRefresh) { // AssetDatabase.Refresh(); doesn't work when we move the files from Assets to Packages so we use this instead: @@ -702,19 +635,6 @@ public override void OnGUI([CanBeNull] string searchContext) } } - private static int GetMenuRootPresetIndex([NotNull] string menuRoot) - { - for (var i = 0; i < MenuRootPresetPaths.Length; i++) - { - if (string.Equals(menuRoot, MenuRootPresetPaths[i], StringComparison.Ordinal)) - { - return i; - } - } - - return CustomMenuRootPresetIndex; - } - private sealed class NugetPlugin { public NugetPlugin([NotNull] Assembly assembly, bool enabled) From 08004d35c2869219b4bf4d60ef436bbc63ae5abc Mon Sep 17 00:00:00 2001 From: Martin Calander Date: Sat, 27 Jun 2026 15:22:21 +0200 Subject: [PATCH 07/10] Fix NuGet window menu path --- README.md | 2 +- src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs | 2 +- src/NuGetForUnity/Editor/Ui/NugetMenu.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index aae996bf..e302ea23 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Download the `*.unitypackage` file. Right-click on it in File Explorer and choos To launch, select **NuGet → Manage NuGet Packages** -The menu root is selected at compile time. Without a scripting define symbol it appears under **NuGet**. Define `NUGETFORUNITY_MENU_TOOLS` to use **Tools → NuGet**, or `NUGETFORUNITY_MENU_WINDOW_PACKAGE_MANAGER` to use **Window → Package Manager → NuGet**. Define only one menu location symbol. +The menu root is selected at compile time. Without a scripting define symbol it appears under **NuGet**. Define `NUGETFORUNITY_MENU_TOOLS` to use **Tools → NuGet**, or `NUGETFORUNITY_MENU_WINDOW_PACKAGE_MANAGER` to use **Window → Package Management → NuGet**. Define only one menu location symbol. Menu Items diff --git a/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs b/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs index ad4c078b..87f7a341 100644 --- a/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs +++ b/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs @@ -728,7 +728,7 @@ public void MenuRootMatchesScriptDefineSymbolsTest() #if NUGETFORUNITY_MENU_TOOLS Assert.That(NugetMenu.MenuRoot, Is.EqualTo("Tools/NuGet")); #elif NUGETFORUNITY_MENU_WINDOW_PACKAGE_MANAGER - Assert.That(NugetMenu.MenuRoot, Is.EqualTo("Window/Package Manager/NuGet")); + Assert.That(NugetMenu.MenuRoot, Is.EqualTo("Window/Package Management/NuGet")); #else Assert.That(NugetMenu.MenuRoot, Is.EqualTo(NugetConfigFile.DefaultMenuRoot)); #endif diff --git a/src/NuGetForUnity/Editor/Ui/NugetMenu.cs b/src/NuGetForUnity/Editor/Ui/NugetMenu.cs index 5672869e..ee10b363 100644 --- a/src/NuGetForUnity/Editor/Ui/NugetMenu.cs +++ b/src/NuGetForUnity/Editor/Ui/NugetMenu.cs @@ -15,7 +15,7 @@ internal static class NugetMenu #if NUGETFORUNITY_MENU_TOOLS internal const string MenuRoot = "Tools/NuGet"; #elif NUGETFORUNITY_MENU_WINDOW_PACKAGE_MANAGER - internal const string MenuRoot = "Window/Package Manager/NuGet"; + internal const string MenuRoot = "Window/Package Management/NuGet"; #else internal const string MenuRoot = NugetConfigFile.DefaultMenuRoot; #endif From 28fba07ca35d0024da60f0f053417ea1d1e0ba25 Mon Sep 17 00:00:00 2001 From: Martin Calander Date: Sat, 27 Jun 2026 15:53:53 +0200 Subject: [PATCH 08/10] Add NuGet menu location dropdown --- .../Editor/Ui/NugetPreferences.cs | 89 ++++++++++++++++++- 1 file changed, 85 insertions(+), 4 deletions(-) diff --git a/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs b/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs index 6795524f..188e416c 100644 --- a/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs +++ b/src/NuGetForUnity/Editor/Ui/NugetPreferences.cs @@ -44,8 +44,19 @@ public class NugetPreferences : SettingsProvider private const float LabelPading = 5; + private const string ToolsMenuDefineSymbol = "NUGETFORUNITY_MENU_TOOLS"; + + private const string WindowPackageManagementMenuDefineSymbol = "NUGETFORUNITY_MENU_WINDOW_PACKAGE_MANAGER"; + private static readonly string[] SearchKeywords = { "NuGet", "Packages", "Menu" }; + private static readonly string[] MenuLocationLabels = + { + "NuGet", + "Tools - NuGet", + "Window Package Management - NuGet", + }; + private readonly GUIContent deleteX = new GUIContent("\u2716"); private readonly GUIContent downArrow = new GUIContent("\u25bc"); @@ -127,11 +138,17 @@ public override void OnGUI([CanBeNull] string searchContext) var biggestLabelSize = EditorStyles.label.CalcSize(new GUIContent("Prefer .NET Standard dependencies over .NET Framework")).x; EditorGUIUtility.labelWidth = biggestLabelSize; EditorGUILayout.LabelField($"Version: {NuGetForUnityVersion}"); - EditorGUILayout.LabelField( + var menuLocation = GetMenuLocationFromScriptingDefineSymbols(); + var selectedMenuLocation = (NugetMenuLocation)EditorGUILayout.Popup( new GUIContent( - "NuGet Menu Location", - "Set NUGETFORUNITY_MENU_TOOLS or NUGETFORUNITY_MENU_WINDOW_PACKAGE_MANAGER in scripting define symbols to change this path."), - NugetMenu.MenuRoot); + "Menu Location", + "Changes the scripting define symbol used to compile the NuGet menu path."), + (int)menuLocation, + MenuLocationLabels); + if (selectedMenuLocation != menuLocation) + { + SetMenuLocationScriptingDefineSymbols(selectedMenuLocation); + } var installFromCache = EditorGUILayout.Toggle("Install From the Cache", ConfigurationManager.NugetConfigFile.InstallFromCache); if (installFromCache != ConfigurationManager.NugetConfigFile.InstallFromCache) @@ -635,6 +652,70 @@ public override void OnGUI([CanBeNull] string searchContext) } } + private static NugetMenuLocation GetMenuLocationFromScriptingDefineSymbols() + { + var defineSymbols = GetCurrentBuildTargetDefineSymbols(); + var symbols = defineSymbols.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries) + .Select(symbol => symbol.Trim()) + .ToList(); + + if (symbols.Contains(WindowPackageManagementMenuDefineSymbol)) + { + return NugetMenuLocation.WindowPackageManagementNuGet; + } + + return symbols.Contains(ToolsMenuDefineSymbol) ? NugetMenuLocation.ToolsNuGet : NugetMenuLocation.NuGet; + } + + private static void SetMenuLocationScriptingDefineSymbols(NugetMenuLocation menuLocation) + { + var defineSymbols = GetCurrentBuildTargetDefineSymbols(); + var symbols = defineSymbols.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries) + .Select(symbol => symbol.Trim()) + .Where(symbol => !string.IsNullOrEmpty(symbol)) + .ToList(); + + symbols.RemoveAll(symbol => symbol == ToolsMenuDefineSymbol || symbol == WindowPackageManagementMenuDefineSymbol); + + if (menuLocation == NugetMenuLocation.ToolsNuGet) + { + symbols.Add(ToolsMenuDefineSymbol); + } + else if (menuLocation == NugetMenuLocation.WindowPackageManagementNuGet) + { + symbols.Add(WindowPackageManagementMenuDefineSymbol); + } + + SetCurrentBuildTargetDefineSymbols(string.Join(";", symbols.ToArray())); + } + + private static string GetCurrentBuildTargetDefineSymbols() + { +#if UNITY_2021_2_OR_NEWER + return PlayerSettings.GetScriptingDefineSymbols(UnityEditor.Build.NamedBuildTarget.FromBuildTargetGroup(EditorUserBuildSettings.selectedBuildTargetGroup)); +#else + return PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup); +#endif + } + + private static void SetCurrentBuildTargetDefineSymbols(string defineSymbols) + { +#if UNITY_2021_2_OR_NEWER + PlayerSettings.SetScriptingDefineSymbols(UnityEditor.Build.NamedBuildTarget.FromBuildTargetGroup(EditorUserBuildSettings.selectedBuildTargetGroup), defineSymbols); +#else + PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, defineSymbols); +#endif + } + + private enum NugetMenuLocation + { + NuGet, + + ToolsNuGet, + + WindowPackageManagementNuGet, + } + private sealed class NugetPlugin { public NugetPlugin([NotNull] Assembly assembly, bool enabled) From 5edbf922964aee3b898fa9383b8fa64cb4f3a0e7 Mon Sep 17 00:00:00 2001 From: Martin Calander Date: Sun, 28 Jun 2026 15:30:46 +0200 Subject: [PATCH 09/10] Add NuGetForUnity package icon --- src/NuGetForUnity/NuGetForUnityIcon.png | Bin 0 -> 11151 bytes src/NuGetForUnity/NuGetForUnityIcon.png.meta | 169 +++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 src/NuGetForUnity/NuGetForUnityIcon.png create mode 100644 src/NuGetForUnity/NuGetForUnityIcon.png.meta diff --git a/src/NuGetForUnity/NuGetForUnityIcon.png b/src/NuGetForUnity/NuGetForUnityIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..eb637c3428d982be88906920cf4cb254177f1f4c GIT binary patch literal 11151 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 literal 0 HcmV?d00001 diff --git a/src/NuGetForUnity/NuGetForUnityIcon.png.meta b/src/NuGetForUnity/NuGetForUnityIcon.png.meta new file mode 100644 index 00000000..0874fc0d --- /dev/null +++ b/src/NuGetForUnity/NuGetForUnityIcon.png.meta @@ -0,0 +1,169 @@ +fileFormatVersion: 2 +guid: 7acff9de9ad274c3d900d7b06367bbbe +TextureImporter: + internalIDToNameTable: + - first: + 213: 5957285066381558381 + second: NuGetForUnityIcon_0 + - first: + 213: -1823813202051018648 + second: NuGetForUnityIcon_1 + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: + - serializedVersion: 2 + name: NuGetForUnityIcon_0 + rect: + serializedVersion: 2 + x: 43 + y: 459 + width: 87 + height: 87 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: d62896f64378ca250800000000000000 + internalID: 5957285066381558381 + vertices: [] + indices: + edges: [] + weights: [] + - serializedVersion: 2 + name: NuGetForUnityIcon_1 + rect: + serializedVersion: 2 + x: 149 + y: 54 + width: 405 + height: 401 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: 860d4134e8380b6e0800000000000000 + internalID: -1823813202051018648 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + NuGetForUnityIcon_0: 5957285066381558381 + NuGetForUnityIcon_1: -1823813202051018648 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: From a8ee59b1c30352422a24f08043d92f0630efe1fb Mon Sep 17 00:00:00 2001 From: Martin Calander Date: Mon, 6 Jul 2026 17:03:51 +0200 Subject: [PATCH 10/10] Ignore Unity build output --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7b9d911a..ebcf6ee1 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ **/.idea src/NuGetForUnity/Editor/NuGetForUnity.csproj* +Builds/