diff --git a/Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/BeeBuildProgramCommon.Data.gen.csproj b/Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/BeeBuildProgramCommon.Data.gen.csproj
index d1a17e8a7b..fa4645ac60 100644
--- a/Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/BeeBuildProgramCommon.Data.gen.csproj
+++ b/Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/BeeBuildProgramCommon.Data.gen.csproj
@@ -30,6 +30,6 @@
-
+
diff --git a/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/Data.cs b/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/Data.cs
index 02db58f293..34f3b14ca6 100644
--- a/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/Data.cs
+++ b/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/Data.cs
@@ -69,6 +69,8 @@ public class LinkerConfig
public string[] LinkXmlFiles = new string[0];
public string[] AssembliesToProcess = new string[0];
public string EditorToLinkerData;
+ // Path to the ManagedCapture config file, or null when no config is supplied.
+ public string ManagedCaptureConfigFile;
public string Runtime;
public string Profile;
public string Ruleset;
diff --git a/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/PlayerBuildProgramLibrary.Data.gen.csproj b/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/PlayerBuildProgramLibrary.Data.gen.csproj
index 598d01fe47..f30313eac0 100644
--- a/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/PlayerBuildProgramLibrary.Data.gen.csproj
+++ b/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/PlayerBuildProgramLibrary.Data.gen.csproj
@@ -32,6 +32,6 @@
-
+
diff --git a/Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/ScriptCompilationBuildProgram.Data.gen.csproj b/Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/ScriptCompilationBuildProgram.Data.gen.csproj
index 058e7f39a9..d8788ba44a 100644
--- a/Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/ScriptCompilationBuildProgram.Data.gen.csproj
+++ b/Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/ScriptCompilationBuildProgram.Data.gen.csproj
@@ -31,6 +31,6 @@
-
+
diff --git a/Editor/Mono/2D/Common/TexturePlatformSettingsView.cs b/Editor/Mono/2D/Common/TexturePlatformSettingsView.cs
index b727a7fa50..da81d0b21b 100644
--- a/Editor/Mono/2D/Common/TexturePlatformSettingsView.cs
+++ b/Editor/Mono/2D/Common/TexturePlatformSettingsView.cs
@@ -13,23 +13,23 @@ internal class TexturePlatformSettingsView : ITexturePlatformSettingsView
{
class Styles
{
- public readonly GUIContent textureFormatLabel = EditorGUIUtility.TrTextContent("Format");
- public readonly GUIContent maxTextureSizeLabel = EditorGUIUtility.TrTextContent("Max Texture Size", "Maximum size of the packed texture.");
- public readonly GUIContent compressionLabel = EditorGUIUtility.TrTextContent("Compression", "How will this texture be compressed?");
- public readonly GUIContent useCrunchedCompressionLabel = EditorGUIUtility.TrTextContent("Use Crunch Compression", "Texture is crunch-compressed to save space on disk when applicable.");
- public readonly GUIContent useAlphaSplitLabel = EditorGUIUtility.TrTextContent("Split Alpha Channel", "Alpha for this texture will be preserved by splitting the alpha channel to another texture, and both resulting textures will be compressed using ETC1.");
- public readonly GUIContent compressionQualityLabel = EditorGUIUtility.TrTextContent("Compressor Quality");
- public readonly GUIContent compressionQualitySliderLabel = EditorGUIUtility.TrTextContent("Compressor Quality", "Use the slider to adjust compression quality from 0 (Fastest) to 100 (Best)");
+ public readonly GUIContent textureFormatLabel = L10n.TextContent("Format", null, null, null);
+ public readonly GUIContent maxTextureSizeLabel = L10n.TextContent("Max Texture Size", "Maximum size of the packed texture.", null, null);
+ public readonly GUIContent compressionLabel = L10n.TextContent("Compression", "How will this texture be compressed?", null, null);
+ public readonly GUIContent useCrunchedCompressionLabel = L10n.TextContent("Use Crunch Compression", "Texture is crunch-compressed to save space on disk when applicable.", null, null);
+ public readonly GUIContent useAlphaSplitLabel = L10n.TextContent("Split Alpha Channel", "Alpha for this texture will be preserved by splitting the alpha channel to another texture, and both resulting textures will be compressed using ETC1.", null, null);
+ public readonly GUIContent compressionQualityLabel = L10n.TextContent("Compressor Quality", null, null, null);
+ public readonly GUIContent compressionQualitySliderLabel = L10n.TextContent("Compressor Quality", "Use the slider to adjust compression quality from 0 (Fastest) to 100 (Best)", null, null);
public readonly int[] kMaxTextureSizeValues = { 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384 };
public readonly GUIContent[] kMaxTextureSizeStrings;
public readonly GUIContent[] kTextureCompressionOptions =
{
- EditorGUIUtility.TrTextContent("None", "Texture is not compressed."),
- EditorGUIUtility.TrTextContent("Low Quality", "Texture compressed with low quality but high performance, high compression format."),
- EditorGUIUtility.TrTextContent("Normal Quality", "Texture is compressed with a standard format."),
- EditorGUIUtility.TrTextContent("High Quality", "Texture compressed with a high quality format."),
+ L10n.TextContent("None", "Texture is not compressed.", null, null),
+ L10n.TextContent("Low Quality", "Texture compressed with low quality but high performance, high compression format.", null, null),
+ L10n.TextContent("Normal Quality", "Texture is compressed with a standard format.", null, null),
+ L10n.TextContent("High Quality", "Texture compressed with a high quality format.", null, null),
};
public readonly int[] kTextureCompressionValues =
@@ -42,9 +42,9 @@ class Styles
public readonly GUIContent[] kMobileCompressionQualityOptions =
{
- EditorGUIUtility.TrTextContent("Fast"),
- EditorGUIUtility.TrTextContent("Normal"),
- EditorGUIUtility.TrTextContent("Best")
+ L10n.TextContent("Fast", null, null, null),
+ L10n.TextContent("Normal", null, null, null),
+ L10n.TextContent("Best", null, null, null)
};
public Styles()
diff --git a/Editor/Mono/2D/SpriteAtlas/SpriteAtlasImporterInspector.cs b/Editor/Mono/2D/SpriteAtlas/SpriteAtlasImporterInspector.cs
index 296b4a72a7..65340eafd4 100644
--- a/Editor/Mono/2D/SpriteAtlas/SpriteAtlasImporterInspector.cs
+++ b/Editor/Mono/2D/SpriteAtlas/SpriteAtlasImporterInspector.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: _2D not yet converted
using System.Collections.Generic;
using System.IO;
using Unity.Jobs;
@@ -50,43 +49,47 @@ class Styles
public readonly GUIStyle previewSliderThumb = "preSliderThumb";
public readonly GUIStyle previewLabel = "preLabel";
- public readonly GUIContent textureSettingLabel = EditorGUIUtility.TrTextContent("Texture");
- public readonly GUIContent variantSettingLabel = EditorGUIUtility.TrTextContent("Variant");
- public readonly GUIContent packingParametersLabel = EditorGUIUtility.TrTextContent("Packing");
- public readonly GUIContent atlasTypeLabel = EditorGUIUtility.TrTextContent("Type");
- public readonly GUIContent defaultPlatformLabel = EditorGUIUtility.TrTextContent("Default");
- public readonly GUIContent masterAtlasLabel = EditorGUIUtility.TrTextContent("Master Atlas", "Assigning another Sprite Atlas asset will make this atlas a variant of it.");
- public readonly GUIContent packerLabel = EditorGUIUtility.TrTextContent("Scriptable Packer", "Scriptable Object that implements custom packing for Sprite-Atlas.");
- public readonly GUIContent bindAsDefaultLabel = EditorGUIUtility.TrTextContent("Include in Build", "Packed textures will be included in the build by default.");
- public readonly GUIContent enableRotationLabel = EditorGUIUtility.TrTextContent("Allow Rotation", "Try rotating the sprite to fit better during packing.");
- public readonly GUIContent enableTightPackingLabel = EditorGUIUtility.TrTextContent("Tight Packing", "Use the mesh outline to fit instead of the whole texture rect during packing.");
- public readonly GUIContent enableAlphaDilationLabel = EditorGUIUtility.TrTextContent("Alpha Dilation", "Enable Alpha Dilation for SpriteAtlas padding pixels.");
- public readonly GUIContent paddingLabel = EditorGUIUtility.TrTextContent("Padding", "The amount of extra padding between packed sprites.");
-
- public readonly GUIContent generateMipMapLabel = EditorGUIUtility.TrTextContent("Generate Mip Maps");
- public readonly GUIContent packPreviewLabel = EditorGUIUtility.TrTextContent("Pack Preview", "Pack and preview Sprite Atlas textures when Enabled for Builds. Previews shown may not be upto-date.");
- public readonly GUIContent sRGBLabel = EditorGUIUtility.TrTextContent("sRGB", "Texture content is stored in gamma space.");
- public readonly GUIContent readWrite = EditorGUIUtility.TrTextContent("Read/Write", "Enable to be able to access the raw pixel data from code.");
- public readonly GUIContent variantMultiplierLabel = EditorGUIUtility.TrTextContent("Scale", "Down scale ratio.");
- public readonly GUIContent copyMasterButton = EditorGUIUtility.TrTextContent("Copy Master's Settings", "Copy all master's settings into this variant.");
+ public readonly GUIContent textureSettingLabel = L10n.TextContent("Texture", null, null, null);
+ public readonly GUIContent variantSettingLabel = L10n.TextContent("Variant", null, null, null);
+ public readonly GUIContent packingParametersLabel = L10n.TextContent("Packing", null, null, null);
+ public readonly GUIContent atlasTypeLabel = L10n.TextContent("Type", null, null, null);
+ public readonly GUIContent defaultPlatformLabel = L10n.TextContent("Default", null, null, null);
+ public readonly GUIContent masterAtlasLabel = L10n.TextContent("Master Atlas", "Assigning another Sprite Atlas asset will make this atlas a variant of it.", null, null);
+ public readonly GUIContent packerLabel = L10n.TextContent("Scriptable Packer", "Scriptable Object that implements custom packing for Sprite-Atlas.", null, null);
+ public readonly GUIContent bindAsDefaultLabel = L10n.TextContent("Include in Build", "Packed textures will be included in the build by default.", null, null);
+ public readonly GUIContent enableRotationLabel = L10n.TextContent("Allow Rotation", "Try rotating the sprite to fit better during packing.", null, null);
+ public readonly GUIContent enableTightPackingLabel = L10n.TextContent("Tight Packing", "Use the mesh outline to fit instead of the whole texture rect during packing.", null, null);
+ public readonly GUIContent enableAlphaDilationLabel = L10n.TextContent("Alpha Dilation", "Enable Alpha Dilation for SpriteAtlas padding pixels.", null, null);
+ public readonly GUIContent paddingLabel = L10n.TextContent("Padding", "The amount of extra padding between packed sprites.", null, null);
+
+ public readonly GUIContent generateMipMapLabel = L10n.TextContent("Generate Mip Maps", null, null, null);
+ public readonly GUIContent packPreviewLabel = L10n.TextContent("Pack Preview", "Pack and preview Sprite Atlas textures when Enabled for Builds. Previews shown may not be upto-date.", null, null);
+ public readonly GUIContent sRGBLabel = L10n.TextContent("sRGB", "Texture content is stored in gamma space.", null, null);
+ public readonly GUIContent readWrite = L10n.TextContent("Read/Write", "Enable to be able to access the raw pixel data from code.", null, null);
+ public readonly GUIContent variantMultiplierLabel = L10n.TextContent("Scale", "Down scale ratio.", null, null);
+ public readonly GUIContent copyMasterButton = L10n.TextContent("Copy Master's Settings", "Copy all master's settings into this variant.", null, null);
public readonly GUIContent disabledPackLabel = EditorGUIUtility.TrTextContent("Sprite Atlas packing is disabled. Enable it in Edit > Project Settings > Editor.", null, EditorGUIUtility.GetHelpIcon(MessageType.Info));
- public readonly GUIContent packableListLabel = EditorGUIUtility.TrTextContent("Objects for Packing", "Only accepts Folders, Sprite Sheet (Texture) and Sprite.");
+ public readonly GUIContent packableListLabel = L10n.TextContent("Objects for Packing", "Only accepts Folders, Sprite Sheet (Texture) and Sprite.", null, null);
- public readonly GUIContent notPowerOfTwoWarning = EditorGUIUtility.TrTextContent("This scale will produce a Variant Sprite Atlas with a packed Texture that is NPOT (non - power of two). This may cause visual artifacts in certain compression/Texture formats.");
- public readonly GUIContent secondaryTextureNameLabel = EditorGUIUtility.TrTextContent("Secondary Texture Name", "The name of the Secondary Texture to apply the following settings to.");
- public readonly GUIContent platformSettingsDropDownLabel = EditorGUIUtility.TrTextContent("Show Platform Settings For");
+ public readonly GUIContent notPowerOfTwoWarning = L10n.TextContent("This scale will produce a Variant Sprite Atlas with a packed Texture that is NPOT (non - power of two). This may cause visual artifacts in certain compression/Texture formats.", null, null, null);
+ public readonly GUIContent secondaryTextureNameLabel = L10n.TextContent("Secondary Texture Name", "The name of the Secondary Texture to apply the following settings to.", null, null);
+ public readonly GUIContent platformSettingsDropDownLabel = L10n.TextContent("Show Platform Settings For", null, null, null);
public readonly GUIContent smallZoom = EditorGUIUtility.IconContent("PreTextureMipMapLow");
public readonly GUIContent largeZoom = EditorGUIUtility.IconContent("PreTextureMipMapHigh");
public readonly GUIContent alphaIcon = EditorGUIUtility.IconContent("PreTextureAlpha");
public readonly GUIContent RGBIcon = EditorGUIUtility.IconContent("PreTextureRGB");
- public readonly GUIContent trashIcon = EditorGUIUtility.TrIconContent("TreeEditor.Trash", "Delete currently selected settings.");
+ public readonly GUIContent trashIcon = L10n.IconContent("TreeEditor.Trash", "Delete currently selected settings.", null);
public readonly int packableElementHash = "PackableElement".GetHashCode();
public readonly int packableSelectorHash = "PackableSelector".GetHashCode();
public readonly string swapObjectRegisterUndo = L10n.Tr("Swap Packable", null);
+ public readonly string platformSettingsRegisterUndo = L10n.Tr("Sprite Atlas Platform Settings", null);
+ public readonly string secondaryColorSpaceRegisterUndo = L10n.Tr("Secondary Texture sRGB", null);
+ public readonly string deleteSecondarySettingsRegisterUndo = L10n.Tr("Delete Secondary Texture Settings", null);
+ public readonly string renameSecondarySettingsRegisterUndo = L10n.Tr("Rename Secondary Texture Settings", null);
public readonly string secondaryTextureNameTextControlName = "secondary_texture_name_text_field";
public readonly string defaultTextForSecondaryTextureName = L10n.Tr("(Matches the names of the Secondary Textures in your Sprites.)", null);
public readonly string nameUniquenessWarning = L10n.Tr("Secondary Texture names must be unique within a Sprite or Sprite Atlas.", null);
@@ -94,8 +97,8 @@ class Styles
public readonly int[] atlasTypeValues = { 0, 1 };
public readonly GUIContent[] atlasTypeOptions =
{
- EditorGUIUtility.TrTextContent("Master"),
- EditorGUIUtility.TrTextContent("Variant"),
+ L10n.TextContent("Master", null, null, null),
+ L10n.TextContent("Variant", null, null, null),
};
public readonly int[] paddingValues = { 2, 4, 8 };
@@ -320,6 +323,7 @@ private SerializedObject GetSerializedAssetObject()
public override void OnEnable()
{
base.OnEnable();
+ Undo.undoRedoPerformed += OnUndoRedoPerformed;
if (!AreImporterTargetsValid()) // asset gone: base already logged and bailed
return;
@@ -354,6 +358,36 @@ public override void OnEnable()
UpdateSpriteAtlasAssetSerializedObject();
}
+ public override void OnDisable()
+ {
+ Undo.undoRedoPerformed -= OnUndoRedoPerformed;
+ base.OnDisable();
+ }
+
+ // Platform settings are stored on the importer's native object and are not edited through
+ // SerializedProperties, so an Undo/Redo of them has to be picked up explicitly: the cached
+ // copies in m_TempPlatformSettings and the secondary texture name list are both stale by then.
+ private void OnUndoRedoPerformed()
+ {
+ if (!AreImporterTargetsValid())
+ return;
+
+ serializedObject.UpdateIfRequiredOrScript();
+
+ // Keep the dropdown on the same secondary texture if it survived the Undo/Redo.
+ var selectedName = secondaryTextureSelected ? m_PlatformSettingsOptions[m_SelectedPlatformSettings] : null;
+ PopulatePlatformSettingsOptions();
+ if (selectedName != null)
+ {
+ var index = m_PlatformSettingsOptions.IndexOf(selectedName);
+ if (index != -1)
+ m_SelectedPlatformSettings = index;
+ }
+
+ SyncPlatformSettings();
+ Repaint();
+ }
+
private void UpdateSpriteAtlasAssetSerializedObject()
{
m_MasterAtlas = serializedAssetObject.FindProperty("m_MasterAtlas");
@@ -413,8 +447,19 @@ void SyncPlatformSettings()
}
}
+ // Platform settings are written straight to the importer's native object instead of through
+ // SerializedProperties, so each such write has to record its own Undo entry. Pending property
+ // edits are flushed first so the snapshot does not roll them back along with the settings.
+ void RegisterPlatformSettingsUndo(string undoName)
+ {
+ serializedObject.ApplyModifiedProperties();
+ Undo.RegisterCompleteObjectUndo(spriteAtlasImporter, undoName);
+ }
+
void RenameSecondaryPlatformSettings(string oldName, string newName)
{
+ RegisterPlatformSettingsUndo(styles.renameSecondarySettingsRegisterUndo);
+
spriteAtlasImporter.DeleteSecondaryPlatformSettings(oldName);
var defaultPlatformSettings = m_TempPlatformSettings[TextureImporterInspector.s_DefaultPlatformName];
@@ -453,17 +498,18 @@ void DrawPackableElement(Rect rect, int index, bool selected, bool focused)
protected override void Apply()
{
- if (HasModified())
+ var hasModified = HasModified();
+ if (hasModified && spriteAtlasAsset)
{
- if (spriteAtlasAsset)
- {
- SpriteAtlasAsset.Save(spriteAtlasAsset, m_AssetPath);
- AssetDatabase.ImportAsset(m_AssetPath);
- }
-
- m_ContentHash = GetInspectorHash();
+ SpriteAtlasAsset.Save(spriteAtlasAsset, m_AssetPath);
+ AssetDatabase.ImportAsset(m_AssetPath);
}
+
base.Apply();
+
+ // Snapshot after base.Apply(), which flushes the importer's serialized properties.
+ if (hasModified)
+ UpdateContentHash();
}
protected override bool useAssetDrawPreview { get { return false; } }
@@ -502,7 +548,30 @@ private bool IsValidAtlas()
public override bool HasModified()
{
- return (base.HasModified() || m_ContentHash != GetInspectorHash());
+ // base.HasModified() flushes pending property edits, so it has to run before the Update()
+ // calls below. Both SerializedObjects must then be in sync with their native objects
+ // before hashing: HasModified() is also invoked from native (UpdateUnsavedChangesState,
+ // fired by RevertObject/UpdateSavedData) at a point where neither has been Update()d yet,
+ // and hashing the stale caches there reports the inspector as still dirty.
+ var baseModified = base.HasModified();
+ serializedObject.Update();
+ m_SerializedAssetObject?.Update();
+ return (baseModified || m_ContentHash != GetInspectorHash());
+ }
+
+ // Re-baselines the content hash and pushes the resulting state to hasUnsavedChanges. The
+ // explicit refresh is needed because the native UpdateSavedData/RevertObject callbacks
+ // evaluate HasModified() while m_ContentHash still describes the pre-apply/pre-revert state,
+ // so the flag they leave behind has to be recomputed once the baseline is up to date.
+ private void UpdateContentHash()
+ {
+ // Callers run after an apply/revert has already flushed pending edits, so syncing with
+ // the native objects here cannot drop user input, and the baseline must be taken from
+ // the synced state to match what HasModified() will hash.
+ serializedObject.Update();
+ m_SerializedAssetObject?.Update();
+ m_ContentHash = GetInspectorHash();
+ hasUnsavedChanges = HasModified();
}
private void ValidateMasterAtlas()
@@ -686,6 +755,7 @@ private void HandleTextureSettingUI()
{
EditorGUI.EndEditingActiveTextField();
+ RegisterPlatformSettingsUndo(styles.deleteSecondarySettingsRegisterUndo);
spriteAtlasImporter.DeleteSecondaryPlatformSettings(m_PlatformSettingsOptions[m_SelectedPlatformSettings]);
m_PlatformSettingsOptions.RemoveAt(m_SelectedPlatformSettings);
@@ -740,7 +810,10 @@ private void HandleTextureSettingUI()
EditorGUI.BeginChangeCheck();
bool value = EditorGUILayout.Toggle(s_Styles.sRGBLabel, spriteAtlasImporter.GetSecondaryColorSpace(secondaryTextureName));
if (EditorGUI.EndChangeCheck())
+ {
+ RegisterPlatformSettingsUndo(styles.secondaryColorSpaceRegisterUndo);
spriteAtlasImporter.SetSecondaryColorSpace(secondaryTextureName, value);
+ }
HandlePlatformSettingUI(textFieldText);
}
@@ -760,6 +833,7 @@ private void HandlePlatformSettingUI(string secondaryTextureName)
{
if (m_TexturePlatformSettingsController.HandleDefaultSettings(defaultPlatformSettings, view, m_TexturePlatformSettingTextureHelper))
{
+ RegisterPlatformSettingsUndo(styles.platformSettingsRegisterUndo);
for (var i = 0; i < defaultPlatformSettings.Count; ++i)
{
if (isSecondary)
@@ -798,6 +872,7 @@ private void HandlePlatformSettingUI(string secondaryTextureName)
m_TexturePlatformSettingsView.buildPlatformTitle = buildPlatform.title.text;
if (m_TexturePlatformSettingsController.HandlePlatformSettings(buildPlatform.defaultTarget, platformSettings, view, m_TexturePlatformSettingTextureHelper))
{
+ RegisterPlatformSettingsUndo(styles.platformSettingsRegisterUndo);
for (var i = 0; i < platformSettings.Count; ++i)
{
if (isSecondary)
@@ -892,7 +967,7 @@ private void HandlePackableListUI()
public override void SaveChanges()
{
base.SaveChanges();
- m_ContentHash = GetInspectorHash();
+ UpdateContentHash();
}
public override void DiscardChanges()
@@ -903,8 +978,12 @@ public override void DiscardChanges()
UpdateSpriteAtlasAssetSerializedObject();
if (EditorSettings.spritePackerMode == SpritePackerMode.SpriteAtlasV2)
m_PreviewStatus = PreviewStatus.Unknown;
- m_ContentHash = GetInspectorHash();
+
+ // base.DiscardChanges() reverts the importer's serialized data, so the hash has to be
+ // snapshot after it. Taking it before leaves m_ContentHash describing the discarded state,
+ // which makes HasModified() report the inspector as dirty again right after a discard.
base.DiscardChanges();
+ UpdateContentHash();
}
void UpdatePages()
@@ -1080,4 +1159,3 @@ public override void OnPreviewGUI(Rect r, GUIStyle background)
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/2D/SpriteAtlas/SpriteAtlasInspector.cs b/Editor/Mono/2D/SpriteAtlas/SpriteAtlasInspector.cs
index 3fbfbeaac7..011a356838 100644
--- a/Editor/Mono/2D/SpriteAtlas/SpriteAtlasInspector.cs
+++ b/Editor/Mono/2D/SpriteAtlas/SpriteAtlasInspector.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: _2D not yet converted
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
@@ -46,36 +45,36 @@ class Styles
public readonly GUIStyle previewSliderThumb = "preSliderThumb";
public readonly GUIStyle previewLabel = "preLabel";
- public readonly GUIContent textureSettingLabel = EditorGUIUtility.TrTextContent("Texture");
- public readonly GUIContent variantSettingLabel = EditorGUIUtility.TrTextContent("Variant");
- public readonly GUIContent packingParametersLabel = EditorGUIUtility.TrTextContent("Packing");
- public readonly GUIContent atlasTypeLabel = EditorGUIUtility.TrTextContent("Type");
- public readonly GUIContent defaultPlatformLabel = EditorGUIUtility.TrTextContent("Default");
- public readonly GUIContent masterAtlasLabel = EditorGUIUtility.TrTextContent("Master Atlas", "Assigning another Sprite Atlas asset will make this atlas a variant of it.");
- public readonly GUIContent bindAsDefaultLabel = EditorGUIUtility.TrTextContent("Include in Build", "Packed textures will be included in the build by default.");
- public readonly GUIContent enableRotationLabel = EditorGUIUtility.TrTextContent("Allow Rotation", "Try rotating the sprite to fit better during packing.");
- public readonly GUIContent enableTightPackingLabel = EditorGUIUtility.TrTextContent("Tight Packing", "Use the mesh outline to fit instead of the whole texture rect during packing.");
- public readonly GUIContent enableAlphaDilationLabel = EditorGUIUtility.TrTextContent("Alpha Dilation", "Enable Alpha Dilation for SpriteAtlas padding pixels.");
- public readonly GUIContent paddingLabel = EditorGUIUtility.TrTextContent("Padding", "The amount of extra padding between packed sprites.");
-
- public readonly GUIContent generateMipMapLabel = EditorGUIUtility.TrTextContent("Generate Mip Maps");
- public readonly GUIContent sRGBLabel = EditorGUIUtility.TrTextContent("sRGB", "Texture content is stored in gamma space.");
- public readonly GUIContent readWrite = EditorGUIUtility.TrTextContent("Read/Write", "Enable to be able to access the raw pixel data from code.");
- public readonly GUIContent variantMultiplierLabel = EditorGUIUtility.TrTextContent("Scale", "Down scale ratio.");
- public readonly GUIContent packButton = EditorGUIUtility.TrTextContent("Pack Preview", "Pack this atlas.");
+ public readonly GUIContent textureSettingLabel = L10n.TextContent("Texture", null, null, null);
+ public readonly GUIContent variantSettingLabel = L10n.TextContent("Variant", null, null, null);
+ public readonly GUIContent packingParametersLabel = L10n.TextContent("Packing", null, null, null);
+ public readonly GUIContent atlasTypeLabel = L10n.TextContent("Type", null, null, null);
+ public readonly GUIContent defaultPlatformLabel = L10n.TextContent("Default", null, null, null);
+ public readonly GUIContent masterAtlasLabel = L10n.TextContent("Master Atlas", "Assigning another Sprite Atlas asset will make this atlas a variant of it.", null, null);
+ public readonly GUIContent bindAsDefaultLabel = L10n.TextContent("Include in Build", "Packed textures will be included in the build by default.", null, null);
+ public readonly GUIContent enableRotationLabel = L10n.TextContent("Allow Rotation", "Try rotating the sprite to fit better during packing.", null, null);
+ public readonly GUIContent enableTightPackingLabel = L10n.TextContent("Tight Packing", "Use the mesh outline to fit instead of the whole texture rect during packing.", null, null);
+ public readonly GUIContent enableAlphaDilationLabel = L10n.TextContent("Alpha Dilation", "Enable Alpha Dilation for SpriteAtlas padding pixels.", null, null);
+ public readonly GUIContent paddingLabel = L10n.TextContent("Padding", "The amount of extra padding between packed sprites.", null, null);
+
+ public readonly GUIContent generateMipMapLabel = L10n.TextContent("Generate Mip Maps", null, null, null);
+ public readonly GUIContent sRGBLabel = L10n.TextContent("sRGB", "Texture content is stored in gamma space.", null, null);
+ public readonly GUIContent readWrite = L10n.TextContent("Read/Write", "Enable to be able to access the raw pixel data from code.", null, null);
+ public readonly GUIContent variantMultiplierLabel = L10n.TextContent("Scale", "Down scale ratio.", null, null);
+ public readonly GUIContent packButton = L10n.TextContent("Pack Preview", "Pack this atlas.", null, null);
public readonly GUIContent disabledPackLabel = EditorGUIUtility.TrTextContent("Sprite Atlas packing is disabled. Enable it in Edit > Project Settings > Editor.", null, EditorGUIUtility.GetHelpIcon(MessageType.Info));
- public readonly GUIContent packableListLabel = EditorGUIUtility.TrTextContent("Objects for Packing", "Only accepts Folder, Sprite Sheet (Texture) and Sprite.");
+ public readonly GUIContent packableListLabel = L10n.TextContent("Objects for Packing", "Only accepts Folder, Sprite Sheet (Texture) and Sprite.", null, null);
- public readonly GUIContent notPowerOfTwoWarning = EditorGUIUtility.TrTextContent("This scale will produce a Variant Sprite Atlas with a packed Texture that is NPOT (non - power of two). This may cause visual artifacts in certain compression/Texture formats.");
- public readonly GUIContent secondaryTextureNameLabel = EditorGUIUtility.TrTextContent("Secondary Texture Name", "The name of the Secondary Texture to apply the following settings to.");
- public readonly GUIContent platformSettingsDropDownLabel = EditorGUIUtility.TrTextContent("Show Platform Settings For");
+ public readonly GUIContent notPowerOfTwoWarning = L10n.TextContent("This scale will produce a Variant Sprite Atlas with a packed Texture that is NPOT (non - power of two). This may cause visual artifacts in certain compression/Texture formats.", null, null, null);
+ public readonly GUIContent secondaryTextureNameLabel = L10n.TextContent("Secondary Texture Name", "The name of the Secondary Texture to apply the following settings to.", null, null);
+ public readonly GUIContent platformSettingsDropDownLabel = L10n.TextContent("Show Platform Settings For", null, null, null);
public readonly GUIContent smallZoom = EditorGUIUtility.IconContent("PreTextureMipMapLow");
public readonly GUIContent largeZoom = EditorGUIUtility.IconContent("PreTextureMipMapHigh");
public readonly GUIContent alphaIcon = EditorGUIUtility.IconContent("PreTextureAlpha");
public readonly GUIContent RGBIcon = EditorGUIUtility.IconContent("PreTextureRGB");
- public readonly GUIContent trashIcon = EditorGUIUtility.TrIconContent("TreeEditor.Trash", "Delete currently selected settings.");
+ public readonly GUIContent trashIcon = L10n.IconContent("TreeEditor.Trash", "Delete currently selected settings.", null);
public readonly int packableElementHash = "PackableElement".GetHashCode();
public readonly int packableSelectorHash = "PackableSelector".GetHashCode();
@@ -88,8 +87,8 @@ class Styles
public readonly int[] atlasTypeValues = { 0, 1 };
public readonly GUIContent[] atlasTypeOptions =
{
- EditorGUIUtility.TrTextContent("Master"),
- EditorGUIUtility.TrTextContent("Variant"),
+ L10n.TextContent("Master", null, null, null),
+ L10n.TextContent("Variant", null, null, null),
};
public readonly int[] paddingValues = { 2, 4, 8 };
@@ -848,4 +847,3 @@ public override void OnPreviewGUI(Rect r, GUIStyle background)
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/Accessibility/UserAccessibilitySettings.cs b/Editor/Mono/Accessibility/UserAccessibilitySettings.cs
index d3c4f6f28c..5067d276c4 100644
--- a/Editor/Mono/Accessibility/UserAccessibilitySettings.cs
+++ b/Editor/Mono/Accessibility/UserAccessibilitySettings.cs
@@ -46,6 +46,9 @@ public static ColorBlindCondition colorBlindCondition
private static ColorBlindCondition s_ColorBlindCondition;
[AutoStaticsCleanupOnCodeReload]
+ // Subscribers attach through their own lifecycle and re-subscribe after a code reload, so the
+ // cleared invocation list refills itself.
+ [IgnoreForUAL0015("Event whose subscribers re-register through their own lifecycle after a code reload")]
public static Action colorBlindConditionChanged;
}
}
diff --git a/Editor/Mono/Animation/AnimationEventWrapperInspector.cs b/Editor/Mono/Animation/AnimationEventWrapperInspector.cs
index 944e19023e..068b465ca1 100644
--- a/Editor/Mono/Animation/AnimationEventWrapperInspector.cs
+++ b/Editor/Mono/Animation/AnimationEventWrapperInspector.cs
@@ -15,9 +15,9 @@ namespace UnityEditor
[CanEditMultipleObjects]
internal partial class AnimationEventWrapperInspector : UnityEditor.Editor
{
- public static readonly GUIContent s_OverloadWarning = EditorGUIUtility.TrTextContent("Some functions were overloaded in MonoBehaviour components and may not work as intended if used with Animation Events!");
- public static readonly GUIContent s_DuplicatesWarning = EditorGUIUtility.TrTextContent("Some functions have the same name across several MonoBehaviour components and may not work as intended if used with Animation Events!");
- public static readonly GUIContent s_RequireMethod = EditorGUIUtility.TrTextContent("Require Receiver", "When enabled, an error will be emitted if there is no matching method on the GameObject.");
+ public static readonly GUIContent s_OverloadWarning = L10n.TextContent("Some functions were overloaded in MonoBehaviour components and may not work as intended if used with Animation Events!", null, null, null);
+ public static readonly GUIContent s_DuplicatesWarning = L10n.TextContent("Some functions have the same name across several MonoBehaviour components and may not work as intended if used with Animation Events!", null, null, null);
+ public static readonly GUIContent s_RequireMethod = L10n.TextContent("Require Receiver", "When enabled, an error will be emitted if there is no matching method on the GameObject.", null, null);
const string kNotSupportedPostFix = " (Function Not Supported)";
const string kNoneSelected = "(No Function Selected)";
@@ -143,7 +143,7 @@ public static void OnEditAnimationEvents(AnimationEventWrapper[] awEvents, Anima
if (singleFunctionName && selectedParameter != null)
{
EditorGUILayout.Space();
- if (selectedParameter == typeof(AnimationEvent))
+ if (IsEventDataType(selectedParameter))
EditorGUILayout.PrefixLabel("Event Data");
else
EditorGUILayout.PrefixLabel("Parameters");
@@ -180,7 +180,7 @@ public static void OnEditAnimationEvents(AnimationEventWrapper[] awEvents, Anima
{
EditorGUI.showMixedValue = !singleFunctionName;
string oldFunctionName = singleFunctionName ? firstEvent.functionName : "";
- string functionName = EditorGUILayout.TextField(EditorGUIUtility.TrTextContent("Function"), oldFunctionName).Replace(" ", "");
+ string functionName = EditorGUILayout.TextField(L10n.TextContent("Function", null, null, null), oldFunctionName).Replace(" ", "");
if (functionName != oldFunctionName)
{
foreach (var evt in data.selectedEvents)
@@ -267,7 +267,7 @@ public static void OnDisabledAnimationEvent()
using (new EditorGUI.DisabledScope(true))
{
- dummyEvent.functionName = EditorGUILayout.TextField(EditorGUIUtility.TrTextContent("Function"), dummyEvent.functionName);
+ dummyEvent.functionName = EditorGUILayout.TextField(L10n.TextContent("Function", null, null, null), dummyEvent.functionName);
EditorGUI.indentLevel++;
dummyEvent.m_MessageOptions = EditorGUILayout.Toggle(s_RequireMethod, dummyEvent.messageOptions == SendMessageOptions.RequireReceiver) ? 0 : 1;
EditorGUI.indentLevel--;
@@ -278,7 +278,7 @@ public static void OnDisabledAnimationEvent()
[AutoStaticsCleanupOnCodeReload]
static Dictionary> s_TypeAnimationMethodMapCache = new Dictionary>();
- static void CollectSupportedMethods(GameObject gameObject, List supportedMethods, List overloadedMethods, List duplicatedMethods)
+ internal static void CollectSupportedMethods(GameObject gameObject, List supportedMethods, List overloadedMethods, List duplicatedMethods)
{
if (gameObject == null)
return;
@@ -317,7 +317,7 @@ static void CollectSupportedMethods(GameObject gameObject, List
///
- struct AnimationMethodMap
+ internal struct AnimationMethodMap
{
public Object sourceBehaviour;
public MethodInfo methodInfo;
@@ -428,8 +428,9 @@ public static string FormatEvent(GameObject root, AnimationEvent evt)
private static void DoEditRegularParameters(AnimationEvent[] events, Type selectedParameter)
{
AnimationEvent firstEvent = events[0];
+ bool isEventData = IsEventDataType(selectedParameter);
- if (selectedParameter == typeof(AnimationEvent) || selectedParameter == typeof(float))
+ if (isEventData || selectedParameter == typeof(float))
{
bool singleParamValue = Array.TrueForAll(events, evt => evt.floatParameter == firstEvent.floatParameter);
@@ -445,7 +446,7 @@ private static void DoEditRegularParameters(AnimationEvent[] events, Type select
}
}
- if (selectedParameter == typeof(AnimationEvent) || selectedParameter == typeof(int) || selectedParameter.IsEnum)
+ if (isEventData || selectedParameter == typeof(int) || selectedParameter.IsEnum)
{
bool singleParamValue = Array.TrueForAll(events, evt => evt.intParameter == firstEvent.intParameter);
@@ -465,7 +466,7 @@ private static void DoEditRegularParameters(AnimationEvent[] events, Type select
}
}
- if (selectedParameter == typeof(AnimationEvent) || selectedParameter == typeof(string))
+ if (isEventData || selectedParameter == typeof(string))
{
bool singleParamValue = Array.TrueForAll(events, evt => evt.stringParameter == firstEvent.stringParameter);
@@ -481,13 +482,13 @@ private static void DoEditRegularParameters(AnimationEvent[] events, Type select
}
}
- if (selectedParameter == typeof(AnimationEvent) || selectedParameter.IsSubclassOf(typeof(UnityEngine.Object)) || selectedParameter == typeof(UnityEngine.Object))
+ if (isEventData || selectedParameter.IsSubclassOf(typeof(UnityEngine.Object)) || selectedParameter == typeof(UnityEngine.Object))
{
bool singleParamValue = Array.TrueForAll(events, evt => evt.objectReferenceParameter == firstEvent.objectReferenceParameter);
EditorGUI.BeginChangeCheck();
Type type = typeof(UnityEngine.Object);
- if (selectedParameter != typeof(AnimationEvent))
+ if (!isEventData)
type = selectedParameter;
EditorGUI.showMixedValue = !singleParamValue;
@@ -522,6 +523,11 @@ private static int EnumPopup(string label, Type enumType, int selected)
}
}
+ private static bool IsEventDataType(Type type)
+ {
+ return type == typeof(AnimationEvent) || type == typeof(AnimationEventInfo);
+ }
+
private static bool IsSupportedMethodName(string name)
{
return name != "Main" && name != "Start" && name != "Awake" && name != "Update";
@@ -549,7 +555,7 @@ private static string FormatEventArguments(ParameterInfo[] paramTypes, Animation
if (paramType.IsEnum)
return " ( " + paramType.Name + "." + Enum.GetName(paramType, evt.intParameter) + " )";
- if (paramType == typeof(AnimationEvent))
+ if (IsEventDataType(paramType))
return " ( "
+ evt.floatParameter + " / "
+ evt.intParameter + " / \""
diff --git a/Editor/Mono/Animation/AnimationUtility.bindings.cs b/Editor/Mono/Animation/AnimationUtility.bindings.cs
index bc1ce4f8b0..e2e69c493b 100644
--- a/Editor/Mono/Animation/AnimationUtility.bindings.cs
+++ b/Editor/Mono/Animation/AnimationUtility.bindings.cs
@@ -432,7 +432,7 @@ public static AnimationCurve GetEditorCurve(AnimationClip clip, string relativeP
extern public static void SetAnimationClipSettings([NotNull] AnimationClip clip, AnimationClipSettings srcClipInfo);
extern internal static void SetAnimationClipSettingsNoDirty([NotNull] AnimationClip clip, AnimationClipSettings srcClipInfo);
- extern public static void SetAdditiveReferencePose(AnimationClip clip, AnimationClip referenceClip, float time);
+ extern public static void SetAdditiveReferencePose([NotNull] AnimationClip clip, [NotNull] AnimationClip referenceClip, float time);
extern internal static bool IsValidOptimizedPolynomialCurve(AnimationCurve curve);
extern public static void ConstrainToPolynomialCurve(AnimationCurve curve);
diff --git a/Editor/Mono/Animation/CurveEditor/CurveEditor.cs b/Editor/Mono/Animation/CurveEditor/CurveEditor.cs
index 65b5f2d640..867add1104 100644
--- a/Editor/Mono/Animation/CurveEditor/CurveEditor.cs
+++ b/Editor/Mono/Animation/CurveEditor/CurveEditor.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: MecanimAnimation not yet converted
using UnityEngine;
using UnityEngine.Bindings;
using UnityEditor;
@@ -2386,9 +2385,9 @@ void SelectPoints()
{
GenericMenu menu = new GenericMenu();
if (m_AnimationCurves[curveIndex].animationIsEditable)
- menu.AddItem(EditorGUIUtility.TrTextContent("Add Key"), false, CreateKeyFromClick, Event.current.mousePosition);
+ menu.AddItem(L10n.TextContent("Add Key", null, null, null), false, CreateKeyFromClick, Event.current.mousePosition);
else
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Add Key"));
+ menu.AddDisabledItem(L10n.TextContent("Add Key", null, null, null));
menu.ShowAsContext();
Event.current.Use();
@@ -2770,6 +2769,14 @@ public void CancelTimeRangeSelection()
const string kPointTimeFieldName = "pointTimeField";
string m_FocusedPointField = null;
Vector2 m_PointEditingFieldPosition;
+ Rect m_PointEditingFieldsRect;
+
+ // Keep the keyboard control on an open "Edit Key..." field when it is the thing being clicked,
+ // otherwise the click counts as an initial one and re-selects all of its text. (UUM-140166)
+ protected override bool captureKeyboardControlOnMouseDown
+ {
+ get { return !(m_EditingPoints && m_PointEditingFieldsRect.Contains(Event.current.mousePosition)); }
+ }
Vector2 GetPointEditionFieldPosition()
{
@@ -2802,6 +2809,7 @@ void StartEditingSelectedPoints()
void StartEditingSelectedPoints(Vector2 fieldPosition)
{
m_PointEditingFieldPosition = fieldPosition;
+ m_PointEditingFieldsRect = Rect.zero;
m_FocusedPointField = kPointValueFieldName;
m_TimeWasEdited = false;
m_ValueWasEdited = false;
@@ -2871,6 +2879,9 @@ void EditSelectedPoints()
fieldPosition.x = Mathf.Clamp(fieldPosition.x, drawAreaInMargins.xMin, drawAreaInMargins.xMax - kFieldWidth);
fieldPosition.y = Mathf.Clamp(fieldPosition.y, drawAreaInMargins.yMin, drawAreaInMargins.yMax - kFieldHeight * 2);
+ // Covers both fields, in drawRect space, which is also the space the zoomable area handles its events in.
+ m_PointEditingFieldsRect = new Rect(fieldPosition.x, fieldPosition.y, kFieldWidth, kFieldHeight * 2);
+
EditorGUI.BeginChangeCheck();
GUI.SetNextControlName(kPointTimeFieldName);
CurveWrapper curveWrapper = GetCurveWrapperFromSelection(selectedCurves[0]);
@@ -4225,4 +4236,3 @@ public void GridGUI()
}
}
} // namespace
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/Animation/CurveEditor/CurveEditorWindow.cs b/Editor/Mono/Animation/CurveEditor/CurveEditorWindow.cs
index e5f5f0f976..d62f832007 100644
--- a/Editor/Mono/Animation/CurveEditor/CurveEditorWindow.cs
+++ b/Editor/Mono/Animation/CurveEditor/CurveEditorWindow.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: MecanimAnimation not yet converted
using System;
using UnityEditor.ShortcutManagement;
using UnityEngine;
@@ -22,10 +21,6 @@ internal enum WrapModeFixedCurve
[Serializable]
internal partial class CurveEditorWindow : EditorWindow
{
- #pragma warning disable UAL0015 // this side effect does not outlive the current call (global trigger / lazily-loaded asset re-fetched on next access); a stale reference is harmlessly replaced
- internal CurveEditorWindow() {}
- #pragma warning restore UAL0015
-
public enum NormalizationMode
{
None = 0,
@@ -37,6 +32,9 @@ public enum NormalizationMode
const int kPresetsHeight = 50;
[AutoStaticsCleanupOnCodeReload]
+ // Shared window cache: the accessor creates a CurveEditorWindow when this is null and OnEnable assigns
+ // it back, so it returns on the next access.
+ [IgnoreForUAL0015("Shared window cache recreated on demand and reassigned by OnEnable")]
static CurveEditorWindow s_SharedCurveEditor;
internal CurveEditor m_CurveEditor;
@@ -159,7 +157,7 @@ void Init(CurveEditorSettings settings)
m_CurveEditor.FrameSelected(frameH, frameV);
- titleContent = EditorGUIUtility.TrTextContent("Curve");
+ titleContent = L10n.TextContent("Curve", null, null, null);
// deal with window size
minSize = new Vector2(240, 240 + kPresetsHeight);
@@ -633,4 +631,3 @@ static void FrameClip(ShortcutArguments args)
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/Animation/CurveEditor/CurveMenuManager.cs b/Editor/Mono/Animation/CurveEditor/CurveMenuManager.cs
index d56c29fe14..6c6e386714 100644
--- a/Editor/Mono/Animation/CurveEditor/CurveMenuManager.cs
+++ b/Editor/Mono/Animation/CurveEditor/CurveMenuManager.cs
@@ -116,45 +116,45 @@ public void AddTangentMenuItems(GenericMenu menu, List keyList)
}
if (anyKeys)
{
- menu.AddItem(EditorGUIUtility.TrTextContent("Clamped Auto"), allClampedAuto, SetClampedAuto, keyList);
- menu.AddItem(EditorGUIUtility.TrTextContent("Auto"), allAuto, SetAuto, keyList);
- menu.AddItem(EditorGUIUtility.TrTextContent("Free Smooth"), allFreeSmooth, SetEditable, keyList);
- menu.AddItem(EditorGUIUtility.TrTextContent("Flat"), allFlat, SetFlat, keyList);
- menu.AddItem(EditorGUIUtility.TrTextContent("Broken"), allBroken, SetBroken, keyList);
+ menu.AddItem(L10n.TextContent("Clamped Auto", null, null, null), allClampedAuto, SetClampedAuto, keyList);
+ menu.AddItem(L10n.TextContent("Auto", null, null, null), allAuto, SetAuto, keyList);
+ menu.AddItem(L10n.TextContent("Free Smooth", null, null, null), allFreeSmooth, SetEditable, keyList);
+ menu.AddItem(L10n.TextContent("Flat", null, null, null), allFlat, SetFlat, keyList);
+ menu.AddItem(L10n.TextContent("Broken", null, null, null), allBroken, SetBroken, keyList);
menu.AddSeparator("");
- menu.AddItem(EditorGUIUtility.TrTextContent("Left Tangent/Free"), allLeftFree, SetLeftEditable, keyList);
- menu.AddItem(EditorGUIUtility.TrTextContent("Left Tangent/Linear"), allLeftLinear, SetLeftLinear, keyList);
- menu.AddItem(EditorGUIUtility.TrTextContent("Left Tangent/Constant"), allLeftConstant, SetLeftConstant, keyList);
- menu.AddItem(EditorGUIUtility.TrTextContent("Left Tangent/Weighted"), allLeftWeighted, ToggleLeftWeighted, keyList);
- menu.AddItem(EditorGUIUtility.TrTextContent("Right Tangent/Free"), allRightFree, SetRightEditable, keyList);
- menu.AddItem(EditorGUIUtility.TrTextContent("Right Tangent/Linear"), allRightLinear, SetRightLinear, keyList);
- menu.AddItem(EditorGUIUtility.TrTextContent("Right Tangent/Constant"), allRightConstant, SetRightConstant, keyList);
- menu.AddItem(EditorGUIUtility.TrTextContent("Right Tangent/Weighted"), allRightWeighted, ToggleRightWeighted, keyList);
- menu.AddItem(EditorGUIUtility.TrTextContent("Both Tangents/Free"), allRightFree && allLeftFree, SetBothEditable, keyList);
- menu.AddItem(EditorGUIUtility.TrTextContent("Both Tangents/Linear"), allRightLinear && allLeftLinear, SetBothLinear, keyList);
- menu.AddItem(EditorGUIUtility.TrTextContent("Both Tangents/Constant"), allRightConstant && allLeftConstant, SetBothConstant, keyList);
- menu.AddItem(EditorGUIUtility.TrTextContent("Both Tangents/Weighted"), allRightWeighted && allLeftWeighted, ToggleBothWeighted, keyList);
+ menu.AddItem(L10n.TextContent("Left Tangent/Free", null, null, null), allLeftFree, SetLeftEditable, keyList);
+ menu.AddItem(L10n.TextContent("Left Tangent/Linear", null, null, null), allLeftLinear, SetLeftLinear, keyList);
+ menu.AddItem(L10n.TextContent("Left Tangent/Constant", null, null, null), allLeftConstant, SetLeftConstant, keyList);
+ menu.AddItem(L10n.TextContent("Left Tangent/Weighted", null, null, null), allLeftWeighted, ToggleLeftWeighted, keyList);
+ menu.AddItem(L10n.TextContent("Right Tangent/Free", null, null, null), allRightFree, SetRightEditable, keyList);
+ menu.AddItem(L10n.TextContent("Right Tangent/Linear", null, null, null), allRightLinear, SetRightLinear, keyList);
+ menu.AddItem(L10n.TextContent("Right Tangent/Constant", null, null, null), allRightConstant, SetRightConstant, keyList);
+ menu.AddItem(L10n.TextContent("Right Tangent/Weighted", null, null, null), allRightWeighted, ToggleRightWeighted, keyList);
+ menu.AddItem(L10n.TextContent("Both Tangents/Free", null, null, null), allRightFree && allLeftFree, SetBothEditable, keyList);
+ menu.AddItem(L10n.TextContent("Both Tangents/Linear", null, null, null), allRightLinear && allLeftLinear, SetBothLinear, keyList);
+ menu.AddItem(L10n.TextContent("Both Tangents/Constant", null, null, null), allRightConstant && allLeftConstant, SetBothConstant, keyList);
+ menu.AddItem(L10n.TextContent("Both Tangents/Weighted", null, null, null), allRightWeighted && allLeftWeighted, ToggleBothWeighted, keyList);
}
else
{
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Weighted"));
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Auto"));
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Free Smooth"));
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Flat"));
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Broken"));
+ menu.AddDisabledItem(L10n.TextContent("Weighted", null, null, null));
+ menu.AddDisabledItem(L10n.TextContent("Auto", null, null, null));
+ menu.AddDisabledItem(L10n.TextContent("Free Smooth", null, null, null));
+ menu.AddDisabledItem(L10n.TextContent("Flat", null, null, null));
+ menu.AddDisabledItem(L10n.TextContent("Broken", null, null, null));
menu.AddSeparator("");
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Left Tangent/Free"));
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Left Tangent/Linear"));
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Left Tangent/Constant"));
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Left Tangent/Weighted"));
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Right Tangent/Free"));
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Right Tangent/Linear"));
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Right Tangent/Constant"));
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Right Tangent/Weighted"));
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Both Tangents/Free"));
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Both Tangents/Linear"));
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Both Tangents/Constant"));
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Both Tangents/Weighted"));
+ menu.AddDisabledItem(L10n.TextContent("Left Tangent/Free", null, null, null));
+ menu.AddDisabledItem(L10n.TextContent("Left Tangent/Linear", null, null, null));
+ menu.AddDisabledItem(L10n.TextContent("Left Tangent/Constant", null, null, null));
+ menu.AddDisabledItem(L10n.TextContent("Left Tangent/Weighted", null, null, null));
+ menu.AddDisabledItem(L10n.TextContent("Right Tangent/Free", null, null, null));
+ menu.AddDisabledItem(L10n.TextContent("Right Tangent/Linear", null, null, null));
+ menu.AddDisabledItem(L10n.TextContent("Right Tangent/Constant", null, null, null));
+ menu.AddDisabledItem(L10n.TextContent("Right Tangent/Weighted", null, null, null));
+ menu.AddDisabledItem(L10n.TextContent("Both Tangents/Free", null, null, null));
+ menu.AddDisabledItem(L10n.TextContent("Both Tangents/Linear", null, null, null));
+ menu.AddDisabledItem(L10n.TextContent("Both Tangents/Constant", null, null, null));
+ menu.AddDisabledItem(L10n.TextContent("Both Tangents/Weighted", null, null, null));
}
}
diff --git a/Editor/Mono/Animation/CurveEditor/MinMaxCurveEditorWindow.cs b/Editor/Mono/Animation/CurveEditor/MinMaxCurveEditorWindow.cs
index fbe211a558..51ad694550 100644
--- a/Editor/Mono/Animation/CurveEditor/MinMaxCurveEditorWindow.cs
+++ b/Editor/Mono/Animation/CurveEditor/MinMaxCurveEditorWindow.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: MecanimAnimation not yet converted
using System;
using UnityEngine;
using Unity.Scripting.LifecycleManagement;
@@ -11,10 +10,6 @@ namespace UnityEditor
{
internal partial class MinMaxCurveEditorWindow : EditorWindow
{
- #pragma warning disable UAL0015 // this side effect does not outlive the current call (global trigger / lazily-loaded asset re-fetched on next access); a stale reference is harmlessly replaced
- internal MinMaxCurveEditorWindow() {}
- #pragma warning restore UAL0015
-
const int k_PresetsHeight = 46;
const float k_WindowMinSize = 240;
const float k_WindowMaxSize = 10000;
@@ -277,7 +272,7 @@ public void Show(GUIView viewToUpdate, CurveEditorSettings settings)
delegateView = viewToUpdate;
Init(settings);
ShowAuxWindow();
- titleContent = EditorGUIUtility.TrTextContent("Curve Editor");
+ titleContent = L10n.TextContent("Curve Editor", null, null, null);
// deal with window size
minSize = new Vector2(k_WindowMinSize, k_WindowMinSize + k_PresetsHeight);
@@ -424,4 +419,3 @@ void SendEvent(string eventName, bool exitGUI)
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/Animation/ZoomableArea.cs b/Editor/Mono/Animation/ZoomableArea.cs
index 2ba899842f..0c298ad3d3 100644
--- a/Editor/Mono/Animation/ZoomableArea.cs
+++ b/Editor/Mono/Animation/ZoomableArea.cs
@@ -170,6 +170,10 @@ public float vScaleMax
protected bool allowSliderZoomHorizontal { get { return m_EnableSliderZoomHorizontal && m_HScaleMin < m_HScaleMax; } }
protected bool allowSliderZoomVertical { get { return m_EnableSliderZoomVertical && m_VScaleMin < m_VScaleMax; } }
+ // Controls drawn over the area receive a mouse down only after BeginViewGUI has taken the keyboard
+ // control, which ends any editing session they had. Return false to skip that. (UUM-140166)
+ protected virtual bool captureKeyboardControlOnMouseDown { get { return true; } }
+
public bool m_UniformScale;
public bool uniformScale { get { return m_UniformScale; } set { m_UniformScale = value; } }
@@ -678,7 +682,8 @@ public void HandleZoomAndPanEvents(Rect area)
{
// Catch keyboard control when clicked inside zoomable area
// (used to restrict scrollwheel)
- GUIUtility.keyboardControl = id;
+ if (captureKeyboardControlOnMouseDown)
+ GUIUtility.keyboardControl = id;
if (IsZoomEvent() || IsPanEvent())
{
diff --git a/Editor/Mono/AnimatorController.bindings.cs b/Editor/Mono/AnimatorController.bindings.cs
index d676c896de..5fd33feac2 100644
--- a/Editor/Mono/AnimatorController.bindings.cs
+++ b/Editor/Mono/AnimatorController.bindings.cs
@@ -76,7 +76,7 @@ static public StateMachineBehaviourContext[] FindStateMachineBehaviourContext(St
extern internal static StateMachineBehaviourContext[] Internal_FindStateMachineBehaviourContext(ScriptableObject behaviour);
[FreeFunction("AnimatorControllerBindings::Internal_CreateNewStateMachineBehaviour")]
- extern public static EntityId CreateNewStateMachineBehaviour(MonoScript script);
+ extern public static EntityId CreateNewStateMachineBehaviour([NotNull] MonoScript script);
[Obsolete("CreateStateMachineBehaviour is deprecated. Use CreateNewStateMachineBehaviour instead.", true)]
public static int CreateStateMachineBehaviour(MonoScript script) => (int)CreateNewStateMachineBehaviour(script);
diff --git a/Editor/Mono/Annotation/AnnotationWindow.cs b/Editor/Mono/Annotation/AnnotationWindow.cs
index 59011fd4a7..0679bffc33 100644
--- a/Editor/Mono/Annotation/AnnotationWindow.cs
+++ b/Editor/Mono/Annotation/AnnotationWindow.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: SceneView not yet converted
using UnityEngine;
using System.Collections.Generic;
using UnityEditor.ShortcutManagement;
@@ -66,6 +65,9 @@ private enum EnabledState
float iconTextRightAlign;
[AutoStaticsCleanupOnCodeReload]
+ // Cache of the open gizmos popup, which a code reload closes anyway; ShowAtPosition creates a
+ // new instance whenever this is null.
+ [IgnoreForUAL0015("Popup instance recreated by ShowAtPosition when the cached one is null")]
static AnnotationWindow s_AnnotationWindow = null;
[NoAutoStaticsCleanup] // safe: timestamp guard, intentionally persisted across reloads
static long s_LastClosedTime;
@@ -84,19 +86,19 @@ private enum EnabledState
const int maxShowRecent = 5;
readonly string textGizmoVisible = L10n.Tr("Show/Hide Gizmo", null);
- GUIContent generalContent = EditorGUIUtility.TrTextContent("General");
- GUIContent iconToggleContent = EditorGUIUtility.TrTextContent("", "Show/Hide Icon");
- GUIContent iconSelectContent = EditorGUIUtility.TrTextContent("", "Select Icon");
- GUIContent icon3dGizmoContent = EditorGUIUtility.TrTextContent("3D Icons");
- GUIContent terrainDebugWarnings = EditorGUIUtility.TrTextContent("Terrain Debug Warnings");
- GUIContent showOutlineContent = EditorGUIUtility.TrTextContent("Selection Outline");
- GUIContent showWireframeContent = EditorGUIUtility.TrTextContent("Selection Wire");
- GUIContent showLODLabelContent = EditorGUIUtility.TrTextContent("LOD Labels", "Show or Hide LOD Labels for Mesh LOD and LODGroup. Disabled if MeshRenderer, SkinnedMeshRenderer or LODGroup Gizmos are are hidden");
- GUIContent fadeGizmosContent = EditorGUIUtility.TrTextContent("Fade Gizmos", "Fade out and stop rendering gizmos that are small on screen");
- GUIContent lightProbeVisualizationContent = EditorGUIUtility.TrTextContent("Light Probe Visualization");
- GUIContent displayWeightsContent = EditorGUIUtility.TrTextContent("Display Weights");
- GUIContent displayOcclusionContent = EditorGUIUtility.TrTextContent("Display Occlusion");
- GUIContent highlightInvalidCellsContent = EditorGUIUtility.TrTextContent("Highlight Invalid Cells", "Highlight the invalid cells that cannot be used for probe interpolation.");
+ GUIContent generalContent = L10n.TextContent("General", null, null, null);
+ GUIContent iconToggleContent = L10n.TextContent("", "Show/Hide Icon", null, null);
+ GUIContent iconSelectContent = L10n.TextContent("", "Select Icon", null, null);
+ GUIContent icon3dGizmoContent = L10n.TextContent("3D Icons", null, null, null);
+ GUIContent terrainDebugWarnings = L10n.TextContent("Terrain Debug Warnings", null, null, null);
+ GUIContent showOutlineContent = L10n.TextContent("Selection Outline", null, null, null);
+ GUIContent showWireframeContent = L10n.TextContent("Selection Wire", null, null, null);
+ GUIContent showLODLabelContent = L10n.TextContent("LOD Labels", "Show or Hide LOD Labels for Mesh LOD and LODGroup. Disabled if MeshRenderer, SkinnedMeshRenderer or LODGroup Gizmos are are hidden", null, null);
+ GUIContent fadeGizmosContent = L10n.TextContent("Fade Gizmos", "Fade out and stop rendering gizmos that are small on screen", null, null);
+ GUIContent lightProbeVisualizationContent = L10n.TextContent("Light Probe Visualization", null, null, null);
+ GUIContent displayWeightsContent = L10n.TextContent("Display Weights", null, null, null);
+ GUIContent displayOcclusionContent = L10n.TextContent("Display Occlusion", null, null, null);
+ GUIContent highlightInvalidCellsContent = L10n.TextContent("Highlight Invalid Cells", "Highlight the invalid cells that cannot be used for probe interpolation.", null, null);
private bool m_IsGameView;
string m_SearchFilter = string.Empty;
@@ -786,4 +788,3 @@ static void ToggleLODLabels()
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/Annotation/LayerVisibilityWindow.cs b/Editor/Mono/Annotation/LayerVisibilityWindow.cs
index 2856874e7a..175c25b95d 100644
--- a/Editor/Mono/Annotation/LayerVisibilityWindow.cs
+++ b/Editor/Mono/Annotation/LayerVisibilityWindow.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: SceneView not yet converted
using UnityEngine;
using UnityEditorInternal;
using System.Collections.Generic;
@@ -25,7 +24,7 @@ private class Styles
public readonly Texture2D visibleOff = EditorGUIUtility.LoadIcon("animationvisibilitytoggleoff");
public readonly Texture2D pickable = EditorGUIUtility.LoadIcon("scenepicking_pickable");
public readonly Texture2D notpickable = EditorGUIUtility.LoadIcon("scenepicking_notpickable");
- public readonly GUIContent editLayers = EditorGUIUtility.TrTextContent("Edit Layers...");
+ public readonly GUIContent editLayers = L10n.TextContent("Edit Layers...", null, null, null);
public Styles()
{
listTextStyle = new GUIStyle(EditorStyles.label);
@@ -45,6 +44,9 @@ public Styles()
const string k_LayerPickable = "Toggle Pickable status this Layer. Non-Pickable items cannot be selected in the Scene View.";
[AutoStaticsCleanupOnCodeReload]
+ // ShowAtPosition recreates the popup window the next time it is opened, and OnDisable already nulls
+ // this slot as part of normal operation.
+ [IgnoreForUAL0015("Popup window slot recreated by ShowAtPosition; OnDisable already nulls it")]
private static LayerVisibilityWindow s_LayerVisibilityWindow;
[NoAutoStaticsCleanup] // safe: timestamp guard, intentionally persisted across reloads
private static long s_LastClosedTime;
@@ -318,4 +320,3 @@ static void RepaintAllSceneViews()
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/Annotation/SceneRenderModeWindow.cs b/Editor/Mono/Annotation/SceneRenderModeWindow.cs
index 532e89e534..be4d8243c4 100644
--- a/Editor/Mono/Annotation/SceneRenderModeWindow.cs
+++ b/Editor/Mono/Annotation/SceneRenderModeWindow.cs
@@ -89,7 +89,7 @@ static class Styles
private static GUIStyle menuItem;
[NoAutoStaticsCleanup] // safe: lazily rebuilt GUI style cache, recreated on demand
private static GUIStyle separator;
- private static readonly GUIContent debuggerLabel = EditorGUIUtility.TrTextContent("Rendering Debugger...");
+ private static readonly GUIContent debuggerLabel = L10n.TextContent("Rendering Debugger...", null, null, null);
public static GUIStyle s_MenuItem => menuItem ?? (menuItem = "MenuItem");
public static GUIStyle s_Separator => separator ?? (separator = "sv_iconselector_sep");
diff --git a/Editor/Mono/Annotation/SceneViewCameraWindow.cs b/Editor/Mono/Annotation/SceneViewCameraWindow.cs
index 2404bf9bb1..4c748b84de 100644
--- a/Editor/Mono/Annotation/SceneViewCameraWindow.cs
+++ b/Editor/Mono/Annotation/SceneViewCameraWindow.cs
@@ -21,11 +21,11 @@ static class Styles
public static readonly string maxTooltips = L10n.Tr($"The maximum speed of the camera in the Scene view. Valid values are between [{SceneView.CameraSettings.kAbsoluteSpeedMin + .0001f}, {SceneView.CameraSettings.kAbsoluteSpeedMax}].", null);
// Menu labels
- public static readonly GUIContent copyPlacementLabel = EditorGUIUtility.TrTextContent("Copy Placement");
- public static readonly GUIContent pastePlacementLabel = EditorGUIUtility.TrTextContent("Paste Placement");
- public static readonly GUIContent copySettingsLabel = EditorGUIUtility.TrTextContent("Copy Settings");
- public static readonly GUIContent pasteSettingsLabel = EditorGUIUtility.TrTextContent("Paste Settings");
- public static readonly GUIContent resetSettingsLabel = EditorGUIUtility.TrTextContent("Reset Settings");
+ public static readonly GUIContent copyPlacementLabel = L10n.TextContent("Copy Placement", null, null, null);
+ public static readonly GUIContent pastePlacementLabel = L10n.TextContent("Paste Placement", null, null, null);
+ public static readonly GUIContent copySettingsLabel = L10n.TextContent("Copy Settings", null, null, null);
+ public static readonly GUIContent pasteSettingsLabel = L10n.TextContent("Paste Settings", null, null, null);
+ public static readonly GUIContent resetSettingsLabel = L10n.TextContent("Reset Settings", null, null, null);
// Layout
public const int windowWidth = 290;
@@ -105,7 +105,13 @@ public override Vector2 GetWindowSize()
=> m_WindowSize;
public SceneViewCameraWindow(SceneView sceneView)
- => m_SceneView = sceneView;
+ {
+ m_SceneView = sceneView;
+
+ // The Occlusion Culling row is hidden when legacy Umbra is disabled, so the window is one line shorter.
+ if (!EditorSettings.enableLegacyUmbraCulling)
+ m_WindowSize.y -= (int)EditorGUI.kSingleLineHeight;
+ }
public override VisualElement CreateGUI()
{
@@ -191,9 +197,13 @@ void LinkElements(SceneView.CameraSettings settings)
// Occlusion culling
m_OcclusionCulling = m_Root.MandatoryQ("OcclusionCulling");
+ if (!EditorSettings.enableLegacyUmbraCulling)
+ m_OcclusionCulling.style.display = DisplayStyle.None;
m_OcclusionCulling.RegisterValueChangedCallback(evt =>
{
+ #pragma warning disable CS0618
settings.occlusionCulling = evt.newValue;
+ #pragma warning restore CS0618
m_SceneView.Repaint();
});
@@ -305,7 +315,9 @@ void Bind(SceneView.CameraSettings settings)
ShowExtremeClippingIfNeeded();
// Occlusion culling
+ #pragma warning disable CS0618
m_OcclusionCulling.SetValueWithoutNotify(settings.occlusionCulling);
+ #pragma warning restore CS0618
// Camera Navigation
// Easing
diff --git a/Editor/Mono/AssemblyInfo/AssemblyInfo.cs b/Editor/Mono/AssemblyInfo/AssemblyInfo.cs
index a63fec25a8..cdfb25f874 100644
--- a/Editor/Mono/AssemblyInfo/AssemblyInfo.cs
+++ b/Editor/Mono/AssemblyInfo/AssemblyInfo.cs
@@ -55,6 +55,7 @@
[assembly: InternalsVisibleTo("Unity.IntegrationTests.Profiler")]
[assembly: InternalsVisibleTo("Unity.Modules.ProfilerEditor.MemorySnapshot.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Modules.ProfilerEditor.MemorySnapshot.Tests.Playmode")]
+[assembly: InternalsVisibleTo("Unity.Modules.JobsProfilerEditor.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Modules.UnityConnectEditor.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.IntegrationTests.PS4")]
[assembly: InternalsVisibleTo("Unity.IntegrationTests.PS5")]
@@ -135,6 +136,7 @@
[assembly: InternalsVisibleTo("Unity.Modules.CoreEditor.DragAndDrop.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Modules.CoreEditor.SceneHierarchy.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Modules.CoreEditor.SceneHierarchy.Tests.Common")]
+[assembly: InternalsVisibleTo("Unity.Hierarchy.Editor.Tests")]
[assembly: InternalsVisibleTo("Unity.Modules.Core.NestedPrefabsBackwardsCompatibility.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Modules.CoreEditor.NestedPrefabsFrontEnd.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.PlayMode.Editor.Tests")]
@@ -208,6 +210,8 @@
[assembly: InternalsVisibleTo("SRPSmoke.Editor.Tests")]
[assembly: InternalsVisibleTo("Unity.Testing.HDRP_EditModeTests")]
[assembly: InternalsVisibleTo("Unity.RenderPipelines.Core.Editor.Tests")]
+[assembly: InternalsVisibleTo("Unity.InternalAPIEngineBridge.RenderPipelines.Core.Editor")]
+[assembly: InternalsVisibleTo("Unity.RenderPipelines.Universal.Editor.Tests")]
[assembly: InternalsVisibleTo("Unity.ShaderGraph.Editor")]
[assembly: InternalsVisibleTo("Unity.Testing.SRP.Universal.Foundation.Editor")]
@@ -263,6 +267,7 @@
[assembly: InternalsVisibleTo("Unity.Modules.BuildProfileEditor.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Modules.BuildProfileEditor.BuildPlayerWindow.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Modules.DeviceSimulator.Tests.Editor")]
+[assembly: InternalsVisibleTo("Unity.Modules.Core.PlayerSettings.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Modules.UI.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Modules.Multiplayer.Server.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Modules.Multiplayer.PlayMode.Tests.Editor")]
@@ -273,12 +278,17 @@
[assembly: InternalsVisibleTo("Unity.Modules.Physics.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Modules.GI.EditorBake.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Modules.GI.LightProbes.Tests.Editor")]
+[assembly: InternalsVisibleTo("Unity.Modules.GI.EnvironmentLighting.Tests.Editor")]
+[assembly: InternalsVisibleTo("Unity.Modules.GI.LightingWindow.Tests.Editor")]
+[assembly: InternalsVisibleTo("Unity.Modules.GI.LightingExplorer.Tests.Editor")]
+[assembly: InternalsVisibleTo("Unity.Modules.GI.LightingSettings.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Modules.GI.BakedGI.SubScenes.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Modules.GI.BakedGI.SceneVisibility.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Modules.GI.UVUnwrap.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Modules.GI.BakedGI.ColorSpace.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Modules.GI.LightingInspectorMeshRenderer.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Modules.GI.TextureEncoding.Tests.Editor")]
+[assembly: InternalsVisibleTo("Unity.Modules.GI.LightingDataAsset.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Modules.Physics2D.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Modules.U2D.NineSlice.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Modules.GI.Prefabs.Tests.Editor")]
diff --git a/Editor/Mono/AssemblyReloadEvents.cs b/Editor/Mono/AssemblyReloadEvents.cs
index d27222cc81..15585e2749 100644
--- a/Editor/Mono/AssemblyReloadEvents.cs
+++ b/Editor/Mono/AssemblyReloadEvents.cs
@@ -16,6 +16,9 @@ public static event AssemblyReloadCallback beforeAssemblyReload
remove => m_BeforeAssemblyReloadEvent.Remove(value);
}
[AutoStaticsCleanupOnCodeReload]
+ // Backing list for the beforeAssemblyReload event: subscribers attach through their own lifecycle
+ // and re-subscribe after a code reload, so the cleared list refills itself.
+ [IgnoreForUAL0015("Event backing store whose subscribers re-register through their own lifecycle")]
private static EventWithPerformanceTracker m_BeforeAssemblyReloadEvent =
new EventWithPerformanceTracker($"{nameof(AssemblyReloadEvents)}.{nameof(beforeAssemblyReload)}");
public static event AssemblyReloadCallback afterAssemblyReload
@@ -24,6 +27,9 @@ public static event AssemblyReloadCallback afterAssemblyReload
remove => m_AfterAssemblyReloadEvent.Remove(value);
}
[AutoStaticsCleanupOnCodeReload]
+ // Backing store for the afterAssemblyReload event: subscribers attach through their own lifecycle
+ // and re-subscribe after a code reload, so the cleared store refills itself.
+ [IgnoreForUAL0015("Event backing store whose subscribers re-register through their own lifecycle")]
private static EventWithPerformanceTracker m_AfterAssemblyReloadEvent =
new EventWithPerformanceTracker($"{nameof(AssemblyReloadEvents)}.{nameof(afterAssemblyReload)}");
diff --git a/Editor/Mono/AssetDatabase/AssetDatabase.cs b/Editor/Mono/AssetDatabase/AssetDatabase.cs
index 8c987d1f7b..bd2efbe0a9 100644
--- a/Editor/Mono/AssetDatabase/AssetDatabase.cs
+++ b/Editor/Mono/AssetDatabase/AssetDatabase.cs
@@ -50,7 +50,7 @@ public static event ImportPackageCallback importPackageCancelled
remove => m_importPackageCancelledEvent.Remove(value);
}
[AutoStaticsCleanupOnCodeReload(CleanupStrategy = CleanupStrategy.Clear)]
- private static readonly EventWithPerformanceTracker m_importPackageCancelledEvent = new EventWithPerformanceTracker($"{nameof(AssetDatabase)}.{nameof(importPackageCancelled)}");
+ private static EventWithPerformanceTracker m_importPackageCancelledEvent = new EventWithPerformanceTracker($"{nameof(AssetDatabase)}.{nameof(importPackageCancelled)}");
// Delegate to be called when package import fails
public static event ImportPackageFailedCallback importPackageFailed
diff --git a/Editor/Mono/AssetDatabase/ImporterGlobalSettings.cs b/Editor/Mono/AssetDatabase/ImporterGlobalSettings.cs
new file mode 100644
index 0000000000..a470584a35
--- /dev/null
+++ b/Editor/Mono/AssetDatabase/ImporterGlobalSettings.cs
@@ -0,0 +1,217 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using Unity.Scripting.LifecycleManagement;
+using UnityEditorInternal;
+using UnityEngine;
+using UnityEngine.Scripting;
+
+namespace UnityEditor
+{
+ ///
+ /// A settings singleton that asset importers read, kept in sync with asset import workers.
+ ///
+ ///
+ /// Unity synchronizes the values with asset import workers whenever an AssetDatabase refresh starts
+ /// importing assets, so an importer reads the same values whether it runs in the Editor or on a worker.
+ /// This includes changes that you have not saved to disk.
+ ///
+ /// Changing a value does not synchronize it on its own. Unity picks the change up the next time it starts
+ /// importing assets, so imports that are already running keep the values they started with.
+ ///
+ /// Add the to your class to also persist the values between
+ /// Editor sessions, and call Save to write them to disk. Unity synchronizes settings without this
+ /// attribute in the same way, but only keeps them for the current session.
+ ///
+ public class ImporterGlobalSettings : ScriptableObject where T : ScriptableObject
+ {
+ const string k_OverlayPathPrefix = $"ImporterGlobalSettings/";
+
+ [NoAutoStaticsCleanup] // reconstructed from a backup on reload and re-linked in the ctor; lazily recreated if null
+ static T s_Instance;
+
+ ///
+ /// Gets the instance of the ImporterGlobalSettings Singleton. Unity creates the Singleton instance when this property is accessed for the first time. If you use the FilePathAttribute, then Unity loads the data on the first access as well.
+ ///
+ public static T instance
+ {
+ get
+ {
+ if (s_Instance == null)
+ CreateAndLoad();
+
+ return s_Instance;
+ }
+ }
+
+ // On domain reload ScriptableObject objects gets reconstructed from a backup. We therefore set the s_Instance here
+ protected ImporterGlobalSettings()
+ {
+ if (s_Instance != null)
+ {
+ Debug.LogError("ImporterGlobalSettings already exists. Did you query the settings in a constructor?");
+ return;
+ }
+
+ object casted = this;
+ s_Instance = casted as T;
+ System.Diagnostics.Debug.Assert(s_Instance != null);
+
+ // Registering the singleton for recording in the SourceAssetDB and synchronization on workers.
+ ImporterGlobalSettingsRegistry.Register(typeof(T), GetOverlayPath(), this);
+ }
+
+ private static void CreateAndLoad()
+ {
+ System.Diagnostics.Debug.Assert(s_Instance == null);
+
+ // If worker, load it from SourceAssetDB
+ if (AssetDatabase.IsAssetImportWorkerProcess())
+ {
+ InternalEditorUtility.LoadSerializedFileAndForget("mem://" + GetOverlayPath());
+ }
+
+ // Either no overlay was synced yet, or this is the main editor, which owns the on-disk state.
+ if (s_Instance == null)
+ {
+ // If a file exists then load it and deserialize it.
+ // This creates an instance of T which will set s_Instance in the constructor. Then it will deserialize it and call relevant serialization callbacks.
+ string filePath = GetFilePath();
+ if (!string.IsNullOrEmpty(filePath))
+ InternalEditorUtility.LoadSerializedFileAndForget(filePath);
+ }
+
+ if (s_Instance == null)
+ {
+ T t = CreateInstance();
+ t.hideFlags = HideFlags.HideAndDontSave & ~HideFlags.NotEditable;
+ }
+
+ System.Diagnostics.Debug.Assert(s_Instance != null);
+ }
+
+ private static string GetOverlayPath()
+ {
+ return k_OverlayPathPrefix + typeof(T).FullName;
+ }
+
+ ///
+ /// Saves the current state of the ImporterGlobalSettings.
+ /// Call Save to save the current state of the ImporterGlobalSettings to disk for persistence. If you call this function and your class has no FilePathAttribute, then saving has no effect.
+ /// Note: Don't call this method from ScriptableObject.OnValidate because the singleton can be in the process of reading its data from a file, which causes an error.
+ ///
+ /// If true then the file is saved as text, if false it is saved as binary.
+ protected virtual void Save(bool saveAsText)
+ {
+ if (s_Instance == null)
+ {
+ Debug.LogError("Cannot save ImporterGlobalSettings: no instance!");
+ return;
+ }
+
+ // Import worker are consummers only for these singletons
+ if (AssetDatabase.IsAssetImportWorkerProcess())
+ {
+ Debug.LogWarning($"Saving ImporterGlobalSettings '{GetType()}' has no effect on an asset import worker process.");
+ return;
+ }
+
+ string filePath = GetFilePath();
+ if (!string.IsNullOrEmpty(filePath))
+ {
+ string folderPath = Path.GetDirectoryName(filePath);
+ if (!Directory.Exists(folderPath))
+ Directory.CreateDirectory(folderPath);
+
+ InternalEditorUtility.SaveToSerializedFileAndForget(new[] { s_Instance }, filePath, saveAsText);
+ }
+ else
+ {
+ Debug.LogWarning($"Saving has no effect. Your class '{GetType()}' is missing the FilePathAttribute. Use this attribute to specify where to save your ImporterGlobalSettings.\nOnly call Save() and use this attribute if you want your state to survive between sessions of Unity.");
+ }
+ }
+
+ ///
+ /// Get the file path where this ImporterGlobalSettings is saved to.
+ /// If you call this function and your class has no FilePathAttribute, then an empty string is returned.
+ ///
+ /// The file path where this ImporterGlobalSettings is saved to.
+ protected static string GetFilePath()
+ {
+ var attr = typeof(T).GetCustomAttribute(inherit: true);
+ return attr?.filepath ?? string.Empty;
+ }
+ }
+
+ ///
+ /// The ImporterGlobalSettings instances that exist in this process, registered as they are created.
+ /// On the main editor the registry is what gets captured and overlaid onto the asset import workers; on a
+ /// worker it is used to drop the states once a new overlay has been synced.
+ ///
+ static partial class ImporterGlobalSettingsRegistry
+ {
+ [AutoStaticsCleanupOnCodeReload]
+ // Suppressed at the declaration rather than at the call site so that it also covers the constructor of
+ // every class deriving from ImporterGlobalSettings, which inherits the side effect.
+ [IgnoreForUAL0015("Registrations are cleared when the CodeLoaded scope exits and re-made by the ImporterGlobalSettings constructor when the instances are reconstructed")]
+ static readonly Dictionary s_States =
+ new Dictionary();
+
+ internal static void Register(Type stateType, string overlayPath, UnityEngine.Object instance)
+ {
+ s_States[stateType] = (overlayPath, instance);
+ }
+
+ ///
+ /// Invoked on the main editor (from native AssetDatabase::RecordImporterGlobalSettingsOverlays) to collect the
+ /// live states along with the path each one is overlaid under (matching index in both arrays).
+ ///
+ [RequiredByNativeCode]
+ internal static UnityEngine.Object[] CaptureImporterGlobalSettings(out string[] overlayPaths)
+ {
+ Debug.Assert(!AssetDatabase.IsAssetImportWorkerProcess(), "CaptureImporterGlobalSettings should only be called in the main Editor (not on workers)");
+
+ var paths = new List(s_States.Count);
+ var instances = new List(s_States.Count);
+
+ foreach (var state in s_States.Values)
+ {
+ if (state.instance == null)
+ continue;
+
+ paths.Add(state.overlayPath);
+ instances.Add(state.instance);
+ }
+
+ overlayPaths = paths.ToArray();
+ return instances.ToArray();
+ }
+
+ ///
+ /// Invoked on import workers (via AssetImportWorkerClient) after they are re-prepared following a
+ /// change on the main editor, so that the next access reloads from the refreshed overlay.
+ ///
+ [RequiredByNativeCode]
+ internal static void ReloadImporterGlobalSettings()
+ {
+ Debug.Assert(AssetDatabase.IsAssetImportWorkerProcess(), "ReloadImporterGlobalSettings should only be called on an Import Worker");
+
+ var instances = new List(s_States.Count);
+ foreach (var state in s_States.Values)
+ instances.Add(state.instance);
+
+ s_States.Clear();
+
+ foreach (var instance in instances)
+ {
+ if (instance != null)
+ UnityEngine.Object.DestroyImmediate(instance);
+ }
+ }
+ }
+}
diff --git a/Editor/Mono/AssetModificationProcessor.cs b/Editor/Mono/AssetModificationProcessor.cs
index 949e866083..ca1a24d7c0 100644
--- a/Editor/Mono/AssetModificationProcessor.cs
+++ b/Editor/Mono/AssetModificationProcessor.cs
@@ -82,6 +82,9 @@ static bool CheckArgumentsAndReturnType(object[] args, MethodInfo method, System
#pragma warning disable 0618
[AutoStaticsCleanupOnCodeReload]
+ // The AssetModificationProcessors getter rebuilds this from TypeCache whenever it is null, so the
+ // list cleared on reload is recollected on the next use.
+ [IgnoreForUAL0015("Processor type list rebuilt from TypeCache by the AssetModificationProcessors getter")]
static System.Collections.Generic.IEnumerable assetModificationProcessors = null;
static System.Collections.Generic.IEnumerable AssetModificationProcessors
{
@@ -584,6 +587,9 @@ internal static void OnStatusUpdated()
}
[AutoStaticsCleanupOnCodeReload]
+ // GetMakeEditableMethods re-reflects the MakeEditable methods whenever this is null, so the array
+ // cleared on reload is rebuilt on the next use.
+ [IgnoreForUAL0015("Reflected method array rebuilt by GetMakeEditableMethods when null")]
static MethodInfo[] s_MakeEditableMethods;
static MethodInfo[] GetMakeEditableMethods()
diff --git a/Editor/Mono/AssetPipeline/AssetImportCallbackDispatch.cs b/Editor/Mono/AssetPipeline/AssetImportCallbackDispatch.cs
new file mode 100644
index 0000000000..1a93bb1116
--- /dev/null
+++ b/Editor/Mono/AssetPipeline/AssetImportCallbackDispatch.cs
@@ -0,0 +1,423 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using System;
+using System.Collections.Generic;
+using Unity.Scripting.LifecycleManagement;
+using UnityEditor.AssetImporters;
+using UnityEngine;
+using UnityEngine.Scripting;
+
+namespace UnityEditor
+{
+ // Fans each import callback out to postprocessors and import blocks, and owns the per-import state both need.
+ // Scopes are stacked, so a nested import restores the outer one when it ends.
+ internal static partial class AssetImportCallbackDispatch
+ {
+ // Blocks run between the postprocessors ordered below this and those at or above it. The default
+ // GetPostprocessOrder() is 0, so a postprocessor opts into running after blocks by returning 1 or more.
+ internal const int k_PostprocessOrderThreshold = 1;
+
+ sealed class Scope
+ {
+ public AssetImporter Importer { get; }
+ public AssetImportContext Context { get; }
+
+ public Scope(AssetImporter importer, AssetImportContext context)
+ {
+ Importer = importer;
+ Context = context;
+ }
+ }
+
+ [AutoStaticsCleanupOnCodeReload]
+ static Stack s_ScopeStack = new Stack();
+
+ static Scope currentScope => s_ScopeStack.Count > 0 ? s_ScopeStack.Peek() : null;
+
+ static AssetImporter currentImporter => currentScope?.Importer;
+ static AssetImportContext currentContext => currentScope?.Context;
+
+
+ static void PushScope(Scope scope)
+ {
+ s_ScopeStack.Push(scope);
+ }
+
+ // A code reload resets the stack, so a cleanup can arrive for a scope that was pushed before the reload.
+ static void PopScope()
+ {
+ if (s_ScopeStack.Count == 0)
+ return;
+
+ s_ScopeStack.Pop();
+ }
+
+ [RequiredByNativeCode]
+ static void InitImportCallbacks(AssetImportContext context, string pathName, Type importerType, AssetImporter importer, double importStartTime)
+ {
+ PushScope(new Scope(importer, context));
+ AssetPostprocessingInternal.InitPostprocessors(context, pathName, importerType, importStartTime);
+ }
+
+ // For imports that run postprocessors without an importer instance, such as scene processing.
+ // Blocks hang off an importer's collection, so a scope without one reaches no blocks.
+ [RequiredByNativeCode]
+ static void InitImportCallbacksWithoutImporter(AssetImportContext context, string pathName, Type importerType, double importStartTime)
+ {
+ PushScope(new Scope(null, context));
+ AssetPostprocessingInternal.InitPostprocessors(context, pathName, importerType, importStartTime);
+ }
+
+ [RequiredByNativeCode]
+ static void InitImportCallbacksForTextureGenerator(string pathName)
+ {
+ PushScope(new Scope(null, null));
+ AssetPostprocessingInternal.InitPostprocessorsForTextureGenerator(pathName);
+ }
+
+ [RequiredByNativeCode]
+ static void CleanupImportCallbacks()
+ {
+ try
+ {
+ AssetPostprocessingInternal.CleanupPostprocessors();
+ }
+ finally
+ {
+ PopScope();
+ }
+ }
+
+ static int RunPostprocessorRange(string methodName, object[] args, int minOrder, int maxOrder, bool analyticsEnabled, ref float duration)
+ {
+ if (!analyticsEnabled)
+ {
+ AssetPostprocessingInternal.CallPostProcessMethodsInRange(methodName, args, minOrder, maxOrder);
+ return 0;
+ }
+
+ return AssetPostprocessingInternal.CallPostProcessMethodsInRange(methodName, args, minOrder, maxOrder, ref duration);
+ }
+
+ static int RunEarlyRange(string methodName, object[] args, bool analyticsEnabled, ref float duration)
+ {
+ return RunPostprocessorRange(methodName, args, int.MinValue, k_PostprocessOrderThreshold - 1, analyticsEnabled, ref duration);
+ }
+
+ static int RunLateRange(string methodName, object[] args, bool analyticsEnabled, ref float duration)
+ {
+ return RunPostprocessorRange(methodName, args, k_PostprocessOrderThreshold, int.MaxValue, analyticsEnabled, ref duration);
+ }
+
+ static bool RunEarlyRangeUntilReturnedObjectIsValid(string methodName, object[] args, out T returnedObject, ref float duration) where T : class
+ {
+ return AssetPostprocessingInternal.CallPostProcessMethodsInRangeUntilReturnedObjectIsValid(methodName, args, int.MinValue, k_PostprocessOrderThreshold - 1, out returnedObject, ref duration);
+ }
+
+ static bool RunLateRangeUntilReturnedObjectIsValid(string methodName, object[] args, out T returnedObject, ref float duration) where T : class
+ {
+ return AssetPostprocessingInternal.CallPostProcessMethodsInRangeUntilReturnedObjectIsValid(methodName, args, k_PostprocessOrderThreshold, int.MaxValue, out returnedObject, ref duration);
+ }
+
+ static int RunAllPostprocessors(string methodName, object[] args, bool analyticsEnabled, ref float duration)
+ {
+ return RunPostprocessorRange(methodName, args, int.MinValue, int.MaxValue, analyticsEnabled, ref duration);
+ }
+
+ static bool RunAllPostprocessorsUntilReturnedObjectIsValid(string methodName, object[] args, out T returnedObject, ref float duration) where T : class
+ {
+ return AssetPostprocessingInternal.CallPostProcessMethodsInRangeUntilReturnedObjectIsValid(methodName, args, int.MinValue, int.MaxValue, out returnedObject, ref duration);
+ }
+
+ [RequiredByNativeCode]
+ internal static void DispatchPreprocessModel(string pathName)
+ {
+ object[] args = null;
+ bool analyticsEnabled = AssetPostprocessingInternal.IsAssetPostprocessorAnalyticsEnabled();
+ float duration = 0f;
+
+ int invocationCount = RunAllPostprocessors("OnPreprocessModel", args, analyticsEnabled, ref duration);
+
+ if (analyticsEnabled)
+ AssetPostprocessingInternal.RecordPostProcessMethodCallAnalytics("OnPreprocessModel", invocationCount, duration);
+ }
+
+ [RequiredByNativeCode]
+ internal static void DispatchPreprocessAnimation(string pathName)
+ {
+ object[] args = null;
+ bool analyticsEnabled = AssetPostprocessingInternal.IsAssetPostprocessorAnalyticsEnabled();
+ float duration = 0f;
+
+ int invocationCount = RunAllPostprocessors("OnPreprocessAnimation", args, analyticsEnabled, ref duration);
+
+ if (analyticsEnabled)
+ AssetPostprocessingInternal.RecordPostProcessMethodCallAnalytics("OnPreprocessAnimation", invocationCount, duration);
+ }
+
+ [RequiredByNativeCode]
+ internal static void DispatchPostprocessModel(GameObject gameObject)
+ {
+ object[] args = { gameObject };
+ bool analyticsEnabled = AssetPostprocessingInternal.IsAssetPostprocessorAnalyticsEnabled();
+ float duration = 0f;
+
+ int invocationCount = RunAllPostprocessors("OnPostprocessModel", args, analyticsEnabled, ref duration);
+
+ if (analyticsEnabled)
+ AssetPostprocessingInternal.RecordPostProcessMethodCallAnalytics("OnPostprocessModel", invocationCount, duration);
+ }
+
+ [RequiredByNativeCode]
+ internal static void DispatchPostprocessMeshHierarchy(GameObject root)
+ {
+ object[] args = { root };
+ bool analyticsEnabled = AssetPostprocessingInternal.IsAssetPostprocessorAnalyticsEnabled();
+ float duration = 0f;
+
+ int invocationCount = RunAllPostprocessors("OnPostprocessMeshHierarchy", args, analyticsEnabled, ref duration);
+
+ if (analyticsEnabled)
+ AssetPostprocessingInternal.RecordPostProcessMethodCallAnalytics("OnPostprocessMeshHierarchy", invocationCount, duration);
+ }
+
+ [RequiredByNativeCode]
+ internal static void DispatchPostprocessAnimation(GameObject root, AnimationClip clip)
+ {
+ object[] args = { root, clip };
+ bool analyticsEnabled = AssetPostprocessingInternal.IsAssetPostprocessorAnalyticsEnabled();
+ float duration = 0f;
+
+ int invocationCount = RunAllPostprocessors("OnPostprocessAnimation", args, analyticsEnabled, ref duration);
+
+ if (analyticsEnabled)
+ AssetPostprocessingInternal.RecordPostProcessMethodCallAnalytics("OnPostprocessAnimation", invocationCount, duration);
+ }
+
+ [RequiredByNativeCode]
+ internal static void DispatchPostprocessMaterial(Material material)
+ {
+ object[] args = { material };
+ bool analyticsEnabled = AssetPostprocessingInternal.IsAssetPostprocessorAnalyticsEnabled();
+ float duration = 0f;
+
+ int invocationCount = RunAllPostprocessors("OnPostprocessMaterial", args, analyticsEnabled, ref duration);
+
+ if (analyticsEnabled)
+ AssetPostprocessingInternal.RecordPostProcessMethodCallAnalytics("OnPostprocessMaterial", invocationCount, duration);
+ }
+
+ [RequiredByNativeCode]
+ internal static void DispatchPreprocessMaterialDescription(MaterialDescription description, Material material, AnimationClip[] animations)
+ {
+ object[] args = { description, material, animations };
+ bool analyticsEnabled = AssetPostprocessingInternal.IsAssetPostprocessorAnalyticsEnabled();
+ float duration = 0f;
+
+ int invocationCount = RunAllPostprocessors("OnPreprocessMaterialDescription", args, analyticsEnabled, ref duration);
+
+ if (analyticsEnabled)
+ AssetPostprocessingInternal.RecordPostProcessMethodCallAnalytics("OnPreprocessMaterialDescription", invocationCount, duration);
+ }
+
+ [RequiredByNativeCode]
+ internal static void DispatchPreprocessCameraDescription(AssetImportContext assetImportContext, CameraDescription description, Camera camera, AnimationClip[] animations)
+ {
+ assetImportContext.DependsOnCustomDependency(AssetPostprocessingInternal.kCameraPostprocessorDependencyName);
+
+ object[] args = { description, camera, animations };
+ bool analyticsEnabled = AssetPostprocessingInternal.IsAssetPostprocessorAnalyticsEnabled();
+ float duration = 0f;
+
+ int invocationCount = RunAllPostprocessors("OnPreprocessCameraDescription", args, analyticsEnabled, ref duration);
+
+ if (analyticsEnabled)
+ AssetPostprocessingInternal.RecordPostProcessMethodCallAnalytics("OnPreprocessCameraDescription", invocationCount, duration);
+ }
+
+ [RequiredByNativeCode]
+ internal static void DispatchPreprocessLightDescription(AssetImportContext assetImportContext, LightDescription description, Light light, AnimationClip[] animations)
+ {
+ assetImportContext.DependsOnCustomDependency(AssetPostprocessingInternal.kLightPostprocessorDependencyName);
+
+ object[] args = { description, light, animations };
+ bool analyticsEnabled = AssetPostprocessingInternal.IsAssetPostprocessorAnalyticsEnabled();
+ float duration = 0f;
+
+ int invocationCount = RunAllPostprocessors("OnPreprocessLightDescription", args, analyticsEnabled, ref duration);
+
+ if (analyticsEnabled)
+ AssetPostprocessingInternal.RecordPostProcessMethodCallAnalytics("OnPreprocessLightDescription", invocationCount, duration);
+ }
+
+ [RequiredByNativeCode]
+ internal static bool DispatchHasAssignMaterial()
+ {
+ if (AssetPostprocessingInternal.ProcessMeshHasAssignMaterial())
+ return true;
+
+
+ return false;
+ }
+
+ [RequiredByNativeCode]
+ internal static bool DispatchHasPostprocessGameObjectWithUserProperties()
+ {
+ if (AssetPostprocessingInternal.HasPostprocessGameObjectWithUserProperties())
+ return true;
+
+
+ return false;
+ }
+
+ [RequiredByNativeCode]
+ internal static bool DispatchHasPostprocessGameObjectWithAnimatedUserProperties()
+ {
+ if (AssetPostprocessingInternal.HasPostprocessGameObjectWithAnimatedUserProperties())
+ return true;
+
+
+ return false;
+ }
+
+ // The analytics count is postprocessor invocations only, so a material supplied by a block leaves it at 0.
+ [RequiredByNativeCode]
+ internal static Material DispatchAssignMaterial(Renderer renderer, Material material)
+ {
+ object[] args = { material, renderer }; // material first
+ bool analyticsEnabled = AssetPostprocessingInternal.IsAssetPostprocessorAnalyticsEnabled();
+ float duration = 0f;
+
+ bool foundByPostprocessor = RunAllPostprocessorsUntilReturnedObjectIsValid("OnAssignMaterialModel", args, out Material assignedMaterial, ref duration);
+
+ if (analyticsEnabled)
+ AssetPostprocessingInternal.RecordPostProcessMethodCallAnalytics("OnAssignMaterialModel", foundByPostprocessor ? 1 : 0, duration);
+
+ return assignedMaterial;
+ }
+
+ [RequiredByNativeCode]
+ internal static void DispatchPostprocessGameObjectWithUserProperties(GameObject root, string[] propNames, object[] propValues)
+ {
+ object[] args = { root, propNames, propValues };
+ bool analyticsEnabled = AssetPostprocessingInternal.IsAssetPostprocessorAnalyticsEnabled();
+ float duration = 0f;
+
+ int invocationCount = RunAllPostprocessors("OnPostprocessGameObjectWithUserProperties", args, analyticsEnabled, ref duration);
+
+ if (analyticsEnabled)
+ AssetPostprocessingInternal.RecordPostProcessMethodCallAnalytics("OnPostprocessGameObjectWithUserProperties", invocationCount, duration);
+ }
+
+ // One conversion and one copy-back: the same array threads through both walks and the block step between them.
+ [RequiredByNativeCode]
+ internal static void DispatchPostprocessGameObjectWithAnimatedUserProperties(GameObject root, IntPtr bindingsPtr)
+ {
+ var bindings = AnimationUtility.BindingsArrayPtrToBindingsArray(bindingsPtr);
+ object[] args = { root, bindings };
+ bool analyticsEnabled = AssetPostprocessingInternal.IsAssetPostprocessorAnalyticsEnabled();
+ float duration = 0f;
+
+ int invocationCount = RunAllPostprocessors("OnPostprocessGameObjectWithAnimatedUserProperties", args, analyticsEnabled, ref duration);
+
+ if (analyticsEnabled)
+ AssetPostprocessingInternal.RecordPostProcessMethodCallAnalytics("OnPostprocessGameObjectWithAnimatedUserProperties", invocationCount, duration);
+
+ AnimationUtility.CopyBindingsArrayToBindingsArrayPtr(bindings, bindingsPtr);
+ }
+
+ [RequiredByNativeCode]
+ internal static void DispatchPreprocessTexture(string pathName, AssetImportContext context)
+ {
+ context?.DependsOnCustomDependency(AssetPostprocessingInternal.kTexturePreprocessorDependencyName);
+
+ object[] args = null;
+ bool analyticsEnabled = AssetPostprocessingInternal.IsAssetPostprocessorAnalyticsEnabled();
+ float duration = 0f;
+
+ int invocationCount = RunAllPostprocessors("OnPreprocessTexture", args, analyticsEnabled, ref duration);
+
+ if (analyticsEnabled)
+ AssetPostprocessingInternal.RecordPostProcessMethodCallAnalytics("OnPreprocessTexture", invocationCount, duration);
+ }
+
+ // Note: The postprocess callbacks below are also called from TextureGenerator.GenerateTextureScripting, which pushes a scope with no importer.
+
+ [RequiredByNativeCode]
+ internal static void DispatchPostprocessTexture(Texture2D tex, string pathName, AssetImportContext context)
+ {
+ context?.DependsOnCustomDependency(AssetPostprocessingInternal.kTexture2DPostprocessorDependencyName);
+
+ object[] args = { tex };
+ bool analyticsEnabled = AssetPostprocessingInternal.IsAssetPostprocessorAnalyticsEnabled();
+ float duration = 0f;
+
+ int invocationCount = RunAllPostprocessors("OnPostprocessTexture", args, analyticsEnabled, ref duration);
+
+ if (analyticsEnabled)
+ AssetPostprocessingInternal.RecordPostProcessMethodCallAnalytics("OnPostprocessTexture", invocationCount, duration);
+ }
+
+ [RequiredByNativeCode]
+ internal static void DispatchPostprocessCubemap(Cubemap tex, string pathName, AssetImportContext context)
+ {
+ context?.DependsOnCustomDependency(AssetPostprocessingInternal.kTextureCubePostprocessorDependencyName);
+
+ object[] args = { tex };
+ bool analyticsEnabled = AssetPostprocessingInternal.IsAssetPostprocessorAnalyticsEnabled();
+ float duration = 0f;
+
+ int invocationCount = RunAllPostprocessors("OnPostprocessCubemap", args, analyticsEnabled, ref duration);
+
+ if (analyticsEnabled)
+ AssetPostprocessingInternal.RecordPostProcessMethodCallAnalytics("OnPostprocessCubemap", invocationCount, duration);
+ }
+
+ [RequiredByNativeCode]
+ internal static void DispatchPostprocessTexture3D(Texture3D tex, string pathName, AssetImportContext context)
+ {
+ context?.DependsOnCustomDependency(AssetPostprocessingInternal.kTexture3DPostprocessorDependencyName);
+
+ object[] args = { tex };
+ bool analyticsEnabled = AssetPostprocessingInternal.IsAssetPostprocessorAnalyticsEnabled();
+ float duration = 0f;
+
+ int invocationCount = RunAllPostprocessors("OnPostprocessTexture3D", args, analyticsEnabled, ref duration);
+
+ if (analyticsEnabled)
+ AssetPostprocessingInternal.RecordPostProcessMethodCallAnalytics("OnPostprocessTexture3D", invocationCount, duration);
+ }
+
+ [RequiredByNativeCode]
+ internal static void DispatchPostprocessTexture2DArray(Texture2DArray tex, string pathName, AssetImportContext context)
+ {
+ context?.DependsOnCustomDependency(AssetPostprocessingInternal.kTexture2DArrayPostprocessorDependencyName);
+
+ object[] args = { tex };
+ bool analyticsEnabled = AssetPostprocessingInternal.IsAssetPostprocessorAnalyticsEnabled();
+ float duration = 0f;
+
+ int invocationCount = RunAllPostprocessors("OnPostprocessTexture2DArray", args, analyticsEnabled, ref duration);
+
+ if (analyticsEnabled)
+ AssetPostprocessingInternal.RecordPostProcessMethodCallAnalytics("OnPostprocessTexture2DArray", invocationCount, duration);
+ }
+
+ [RequiredByNativeCode]
+ internal static void DispatchPostprocessSprites(Texture2D tex, string pathName, Sprite[] sprites, AssetImportContext context)
+ {
+ context?.DependsOnCustomDependency(AssetPostprocessingInternal.kTextureSpritePostprocessorDependencyName);
+
+ object[] args = { tex, sprites };
+ bool analyticsEnabled = AssetPostprocessingInternal.IsAssetPostprocessorAnalyticsEnabled();
+ float duration = 0f;
+
+ int invocationCount = RunAllPostprocessors("OnPostprocessSprites", args, analyticsEnabled, ref duration);
+
+ if (analyticsEnabled)
+ AssetPostprocessingInternal.RecordPostProcessMethodCallAnalytics("OnPostprocessSprites", invocationCount, duration);
+ }
+ }
+}
diff --git a/Editor/Mono/AssetPipeline/AssetImportContext.bindings.cs b/Editor/Mono/AssetPipeline/AssetImportContext.bindings.cs
index f289d40860..79eb808e9f 100644
--- a/Editor/Mono/AssetPipeline/AssetImportContext.bindings.cs
+++ b/Editor/Mono/AssetPipeline/AssetImportContext.bindings.cs
@@ -105,7 +105,7 @@ internal void DependsOnImportedAsset(string path)
DependsOnImportedAssetInternal(path);
}
- [NativeName("GetArtifactFilePath")]
+ [NativeName("GetArtifactFilePath_Binding")]
private extern string GetArtifactFilePath_Internal(string path, string fileName);
[Obsolete("GetArtifactFilePath has been deprecated. Use GetArtifactData to get the data instead.")]
@@ -121,6 +121,7 @@ public string GetArtifactFilePath(GUID guid, string fileName)
}
[Obsolete("GetArtifactFilePath has been deprecated. Use GetArtifactData to get the data instead.")]
+ [NativeName("GetArtifactFilePath_Binding")]
public extern string GetArtifactFilePath(ArtifactKey key, string fileName);
[NativeName("GetArtifactData")]
diff --git a/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterEditor.cs b/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterEditor.cs
index 7d91527359..735ea8d38b 100644
--- a/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterEditor.cs
+++ b/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterEditor.cs
@@ -15,10 +15,10 @@ internal class SpeedTree9ImporterEditor : AssetImporterTabbedEditor
{
private static class Styles
{
- public static readonly GUIContent ApplyAndGenerate = EditorGUIUtility.TrTextContent("Apply & Generate Materials", "Apply current importer settings and generate materials with new settings.");
- public static readonly GUIContent Regenerate = EditorGUIUtility.TrTextContent("Regenerate Materials", "Regenerate materials from the current importer settings.");
- public static readonly GUIContent RegenerateRemapped = EditorGUIUtility.TrTextContent("Regenerate Materials", "Regenerate the remapped materials from the current import settings.");
- public static readonly GUIContent ApplyAndGenerateRemapped = EditorGUIUtility.TrTextContent("Apply & Generate Materials", "Apply current importer settings and regenerate the remapped materials with new settings.");
+ public static readonly GUIContent ApplyAndGenerate = L10n.TextContent("Apply & Generate Materials", "Apply current importer settings and generate materials with new settings.", null, null);
+ public static readonly GUIContent Regenerate = L10n.TextContent("Regenerate Materials", "Regenerate materials from the current importer settings.", null, null);
+ public static readonly GUIContent RegenerateRemapped = L10n.TextContent("Regenerate Materials", "Regenerate the remapped materials from the current import settings.", null, null);
+ public static readonly GUIContent ApplyAndGenerateRemapped = L10n.TextContent("Apply & Generate Materials", "Apply current importer settings and regenerate the remapped materials with new settings.", null, null);
public static readonly string ModelTabName = "Model";
public static readonly string MaterialsTabName = "Materials";
diff --git a/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterMaterialEditor.cs b/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterMaterialEditor.cs
index 41046848a0..27526bbb1c 100644
--- a/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterMaterialEditor.cs
+++ b/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterMaterialEditor.cs
@@ -12,18 +12,18 @@ class SpeedTree9ImporterMaterialEditor : BaseSpeedTree9ImporterTabUI
{
private static class Styles
{
- public static readonly GUIContent RemapOptions = EditorGUIUtility.TrTextContent("On Demand Remap");
- public static readonly GUIContent RemapMaterialsInProject = EditorGUIUtility.TrTextContent("Search and Remap...", "Click on this button to search and remap the materials from the project.");
- public static readonly GUIContent ExternalMaterialMappings = EditorGUIUtility.TrTextContent("Remapped Materials", "External materials to use for each embedded material.");
+ public static readonly GUIContent RemapOptions = L10n.TextContent("On Demand Remap", null, null, null);
+ public static readonly GUIContent RemapMaterialsInProject = L10n.TextContent("Search and Remap...", "Click on this button to search and remap the materials from the project.", null, null);
+ public static readonly GUIContent ExternalMaterialMappings = L10n.TextContent("Remapped Materials", "External materials to use for each embedded material.", null, null);
- public static readonly GUIContent Materials = EditorGUIUtility.TrTextContent("Materials");
- public static readonly GUIContent ExtractEmbeddedMaterials = EditorGUIUtility.TrTextContent("Extract Materials...", "Click on this button to extract the embedded materials.");
+ public static readonly GUIContent Materials = L10n.TextContent("Materials", null, null, null);
+ public static readonly GUIContent ExtractEmbeddedMaterials = L10n.TextContent("Extract Materials...", "Click on this button to extract the embedded materials.", null, null);
- public static readonly GUIContent InternalMaterialHelp = EditorGUIUtility.TrTextContent("Materials are embedded inside the imported asset.");
- public static readonly GUIContent MaterialAssignmentsHelp = EditorGUIUtility.TrTextContent("Material assignments can be remapped below.");
+ public static readonly GUIContent InternalMaterialHelp = L10n.TextContent("Materials are embedded inside the imported asset.", null, null, null);
+ public static readonly GUIContent MaterialAssignmentsHelp = L10n.TextContent("Material assignments can be remapped below.", null, null, null);
- public static readonly GUIContent ExternalMaterialSearchHelp = EditorGUIUtility.TrTextContent("Searches the user provided directory and matches the materials that share the same name and LOD with the originally imported material.");
- public static readonly GUIContent SelectMaterialFolder = EditorGUIUtility.TrTextContent("Select Materials Folder");
+ public static readonly GUIContent ExternalMaterialSearchHelp = L10n.TextContent("Searches the user provided directory and matches the materials that share the same name and LOD with the originally imported material.", null, null, null);
+ public static readonly GUIContent SelectMaterialFolder = L10n.TextContent("Select Materials Folder", null, null, null);
}
private SpeedTree9Importer m_STImporter;
diff --git a/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterModelEditor.cs b/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterModelEditor.cs
index ac481ed547..56789497b8 100644
--- a/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterModelEditor.cs
+++ b/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterModelEditor.cs
@@ -641,7 +641,7 @@ private void DrawLODSettingCustomizationGUI(List lods, int lodIn
else
{
// Toggle
- GUIContent customizationLabel = EditorGUIUtility.TrTextContent(String.Format("Customize {0} options", lods[lodIndex].LODName), "To override options for a certain LOD, check this box and select the LOD from the LOD slider above");
+ GUIContent customizationLabel = L10n.TextContent(String.Format("Customize {0} options", lods[lodIndex].LODName), "To override options for a certain LOD, check this box and select the LOD from the LOD slider above", null, null);
EditorGUILayout.PropertyField(lodSettingOverride, customizationLabel);
// Warning
diff --git a/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterWindEditor.cs b/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterWindEditor.cs
index 9ae55d60f0..2316f9b848 100644
--- a/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterWindEditor.cs
+++ b/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterWindEditor.cs
@@ -12,10 +12,10 @@ class SpeedTree9ImporterWindEditor : BaseSpeedTree9ImporterTabUI
{
private class Styles
{
- public static readonly GUIContent EnableWind = EditorGUIUtility.TrTextContent("Enable Wind");
- public static readonly GUIContent StrengthResponse = EditorGUIUtility.TrTextContent("Strength Response", "The strength response of the wind.");
- public static readonly GUIContent DirectionResponse = EditorGUIUtility.TrTextContent("Direction Response", "The direction response of the wind.");
- public static readonly GUIContent WindRandomness = EditorGUIUtility.TrTextContent("Randomness", "Amount of world position based noise applied to each tree.");
+ public static readonly GUIContent EnableWind = L10n.TextContent("Enable Wind", null, null, null);
+ public static readonly GUIContent StrengthResponse = L10n.TextContent("Strength Response", "The strength response of the wind.", null, null);
+ public static readonly GUIContent DirectionResponse = L10n.TextContent("Direction Response", "The direction response of the wind.", null, null);
+ public static readonly GUIContent WindRandomness = L10n.TextContent("Randomness", "Amount of world position based noise applied to each tree.", null, null);
}
private SerializedProperty m_EnableWind;
diff --git a/Editor/Mono/AssetPipeline/SpeedTree/SpeedTreeImporterCommon.cs b/Editor/Mono/AssetPipeline/SpeedTree/SpeedTreeImporterCommon.cs
index b0d1685186..239fbbe648 100644
--- a/Editor/Mono/AssetPipeline/SpeedTree/SpeedTreeImporterCommon.cs
+++ b/Editor/Mono/AssetPipeline/SpeedTree/SpeedTreeImporterCommon.cs
@@ -97,9 +97,9 @@ static class SpeedTreeImporterCommonEditor
internal class Styles
{
// Meshes labels
- public static readonly GUIContent MeshesHeader = EditorGUIUtility.TrTextContent("Meshes");
- public static readonly GUIContent UnitConversion = EditorGUIUtility.TrTextContent("Unit Conversion", "Select the unit conversion to apply to the imported SpeedTree asset.");
- public static readonly GUIContent ScaleFactor = EditorGUIUtility.TrTextContent("Scale Factor", "How much to scale the tree model, interpreting the exported units as meters. Must be positive.");
+ public static readonly GUIContent MeshesHeader = L10n.TextContent("Meshes", null, null, null);
+ public static readonly GUIContent UnitConversion = L10n.TextContent("Unit Conversion", "Select the unit conversion to apply to the imported SpeedTree asset.", null, null);
+ public static readonly GUIContent ScaleFactor = L10n.TextContent("Scale Factor", "How much to scale the tree model, interpreting the exported units as meters. Must be positive.", null, null);
public static readonly GUIContent[] UnitConversionNames =
{
new GUIContent("Leave As Is")
@@ -110,21 +110,21 @@ internal class Styles
};
// Materials labels
- public static readonly GUIContent MaterialHeader = EditorGUIUtility.TrTextContent("Material");
- public static readonly GUIContent MainColor = EditorGUIUtility.TrTextContent("Main Color", "The color modulating the diffuse lighting component.");
- public static readonly GUIContent EnableColorVariation = EditorGUIUtility.TrTextContent("Color Variation", "Color is determined by linearly interpolating between the Main Color & Color Variation values based on the world position X, Y and Z values");
- public static readonly GUIContent EnableBump = EditorGUIUtility.TrTextContent("Normal Map", "Enable normal (Bump) mapping.");
- public static readonly GUIContent EnableSubsurface = EditorGUIUtility.TrTextContent("Subsurface Scattering", "Enable subsurface scattering effects.");
- public static readonly GUIContent HueVariation = EditorGUIUtility.TrTextContent("Variation Color (RGB), Intensity (A)", "Tint the tree with the Variation Color");
- public static readonly GUIContent AlphaTestRef = EditorGUIUtility.TrTextContent("Alpha Cutoff", "The alpha-test reference value.");
- public static readonly GUIContent TransmissionScale = EditorGUIUtility.TrTextContent("Transmission Scale", "The transmission scale value.");
+ public static readonly GUIContent MaterialHeader = L10n.TextContent("Material", null, null, null);
+ public static readonly GUIContent MainColor = L10n.TextContent("Main Color", "The color modulating the diffuse lighting component.", null, null);
+ public static readonly GUIContent EnableColorVariation = L10n.TextContent("Color Variation", "Color is determined by linearly interpolating between the Main Color & Color Variation values based on the world position X, Y and Z values", null, null);
+ public static readonly GUIContent EnableBump = L10n.TextContent("Normal Map", "Enable normal (Bump) mapping.", null, null);
+ public static readonly GUIContent EnableSubsurface = L10n.TextContent("Subsurface Scattering", "Enable subsurface scattering effects.", null, null);
+ public static readonly GUIContent HueVariation = L10n.TextContent("Variation Color (RGB), Intensity (A)", "Tint the tree with the Variation Color", null, null);
+ public static readonly GUIContent AlphaTestRef = L10n.TextContent("Alpha Cutoff", "The alpha-test reference value.", null, null);
+ public static readonly GUIContent TransmissionScale = L10n.TextContent("Transmission Scale", "The transmission scale value.", null, null);
// Lighting labels
- public static readonly GUIContent LightingHeader = EditorGUIUtility.TrTextContent("Lighting");
- public static readonly GUIContent CastShadows = EditorGUIUtility.TrTextContent("Cast Shadows", "The tree casts shadow");
- public static readonly GUIContent ReceiveShadows = EditorGUIUtility.TrTextContent("Receive Shadows", "The tree receives shadow");
- public static readonly GUIContent UseLightProbes = EditorGUIUtility.TrTextContent("Light Probes", "The tree uses light probe for lighting"); // TODO: update help text
- public static readonly GUIContent UseReflectionProbes = EditorGUIUtility.TrTextContent("Reflection Probes", "The tree uses reflection probe for rendering"); // TODO: update help text
+ public static readonly GUIContent LightingHeader = L10n.TextContent("Lighting", null, null, null);
+ public static readonly GUIContent CastShadows = L10n.TextContent("Cast Shadows", "The tree casts shadow", null, null);
+ public static readonly GUIContent ReceiveShadows = L10n.TextContent("Receive Shadows", "The tree receives shadow", null, null);
+ public static readonly GUIContent UseLightProbes = L10n.TextContent("Light Probes", "The tree uses light probe for lighting", null, null); // TODO: update help text
+ public static readonly GUIContent UseReflectionProbes = L10n.TextContent("Reflection Probes", "The tree uses reflection probe for rendering", null, null); // TODO: update help text
public static readonly GUIContent[] ReflectionProbeUsageNames = GetReflectionProbeUsageNames();
public static GUIContent[] GetReflectionProbeUsageNames()
@@ -142,12 +142,12 @@ public static GUIContent[] GetReflectionProbeUsageNames()
}
// Additional Settings labels
- public static readonly GUIContent AdditionalSettingsHeader = EditorGUIUtility.TrTextContent("Additional Settings");
- public static readonly GUIContent MotionVectorMode = EditorGUIUtility.TrTextContent("Motion Vectors", "Motion vector mode to set for the mesh renderer of each LOD object");
+ public static readonly GUIContent AdditionalSettingsHeader = L10n.TextContent("Additional Settings", null, null, null);
+ public static readonly GUIContent MotionVectorMode = L10n.TextContent("Motion Vectors", "Motion vector mode to set for the mesh renderer of each LOD object", null, null);
// Wind labels
- public static readonly GUIContent WindHeader = EditorGUIUtility.TrTextContent("Wind");
- public static readonly GUIContent WindQuality = EditorGUIUtility.TrTextContent("Wind Quality", "Controls the wind effect's quality.");
+ public static readonly GUIContent WindHeader = L10n.TextContent("Wind", null, null, null);
+ public static readonly GUIContent WindQuality = L10n.TextContent("Wind Quality", "Controls the wind effect's quality.", null, null);
public static readonly GUIContent[] MotionVectorModeNames = // Match SharedRendererDataTypes.h / enum MotionVectorGenerationMode
{
new GUIContent("Camera Motion Only") // kMotionVectorCamera = 0, // Use camera motion for motion vectors
@@ -168,18 +168,18 @@ public static GUIContent[] GetWindQualityNames()
}
// LOD labels
- public static readonly GUIContent LODHeader = EditorGUIUtility.TrTextContent("LOD");
- public static readonly GUIContent ResetLOD = EditorGUIUtility.TrTextContent("Reset LOD to...", "Unify the LOD settings for all selected assets");
- public static readonly GUIContent SmoothLOD = EditorGUIUtility.TrTextContent("Smooth Transitions", "Toggles smooth LOD transitions");
- public static readonly GUIContent AnimateCrossFading = EditorGUIUtility.TrTextContent("Animate Cross-fading", "Cross-fading is animated instead of being calculated by distance");
- public static readonly GUIContent CrossFadeWidth = EditorGUIUtility.TrTextContent("Crossfade Width", "Proportion of the last 3D mesh LOD region width which is used for cross-fading to billboard tree");
- public static readonly GUIContent FadeOutWidth = EditorGUIUtility.TrTextContent("Fade Out Width", "The proportion of the billboard LOD region width that is used to fade out the billboard.");
-
- public static readonly GUIContent EnableLodCustomizationsWarn = EditorGUIUtility.TrTextContent("Customizing LOD options may help with tuning the GPU performance but will likely negatively impact the instanced draw batching, i.e. CPU performance.\nPlease use the per-LOD customizations with careful memory and performance profiling for both CPU and GPU and remember that these options are a trade-off rather than a free win.");
- public static readonly GUIContent BillboardSettingsHelp = EditorGUIUtility.TrTextContent("Billboard options are separate from the 3D model options shown above.\nChange the options below for influencing billboard rendering.");
- public static readonly GUIContent MultiSelectionLODNotSupported = EditorGUIUtility.TrTextContent("Multi-selection is not supported for LOD settings.");
-
- public static readonly GUIContent ApplyAndGenerate = EditorGUIUtility.TrTextContent("Apply & Generate Materials", "Apply current importer settings and generate asset materials with the new settings.");
+ public static readonly GUIContent LODHeader = L10n.TextContent("LOD", null, null, null);
+ public static readonly GUIContent ResetLOD = L10n.TextContent("Reset LOD to...", "Unify the LOD settings for all selected assets", null, null);
+ public static readonly GUIContent SmoothLOD = L10n.TextContent("Smooth Transitions", "Toggles smooth LOD transitions", null, null);
+ public static readonly GUIContent AnimateCrossFading = L10n.TextContent("Animate Cross-fading", "Cross-fading is animated instead of being calculated by distance", null, null);
+ public static readonly GUIContent CrossFadeWidth = L10n.TextContent("Crossfade Width", "Proportion of the last 3D mesh LOD region width which is used for cross-fading to billboard tree", null, null);
+ public static readonly GUIContent FadeOutWidth = L10n.TextContent("Fade Out Width", "The proportion of the billboard LOD region width that is used to fade out the billboard.", null, null);
+
+ public static readonly GUIContent EnableLodCustomizationsWarn = L10n.TextContent("Customizing LOD options may help with tuning the GPU performance but will likely negatively impact the instanced draw batching, i.e. CPU performance.\nPlease use the per-LOD customizations with careful memory and performance profiling for both CPU and GPU and remember that these options are a trade-off rather than a free win.", null, null, null);
+ public static readonly GUIContent BillboardSettingsHelp = L10n.TextContent("Billboard options are separate from the 3D model options shown above.\nChange the options below for influencing billboard rendering.", null, null, null);
+ public static readonly GUIContent MultiSelectionLODNotSupported = L10n.TextContent("Multi-selection is not supported for LOD settings.", null, null, null);
+
+ public static readonly GUIContent ApplyAndGenerate = L10n.TextContent("Apply & Generate Materials", "Apply current importer settings and generate asset materials with the new settings.", null, null);
}
static internal void ShowMeshGUI(
diff --git a/Editor/Mono/AssetPipeline/TextureImporter.bindings.cs b/Editor/Mono/AssetPipeline/TextureImporter.bindings.cs
index e44c9e850e..65389b912f 100644
--- a/Editor/Mono/AssetPipeline/TextureImporter.bindings.cs
+++ b/Editor/Mono/AssetPipeline/TextureImporter.bindings.cs
@@ -20,8 +20,10 @@ namespace UnityEditor
[NativeHeader("Editor/Src/AssetPipeline/TextureImporting/TextureImporterPlatformSettingsUtils.h")]
[global::UnityEngine.NativeClass("TextureImporter", PersistentTypeId = 1006)]
[NativeHeader("Editor/Src/EditorUserBuildSettings.h")]
+ [UnityEngine.ExtensionOfNativeClass]
public sealed partial class TextureImporter : AssetImporter
{
+
[FreeFunction]
internal static extern string GetTexturePlatformSerializationName(string platformName);
diff --git a/Editor/Mono/AssetPostprocessor.cs b/Editor/Mono/AssetPostprocessor.cs
index 2b73d3b12f..865e7aed8a 100644
--- a/Editor/Mono/AssetPostprocessor.cs
+++ b/Editor/Mono/AssetPostprocessor.cs
@@ -10,10 +10,12 @@
using System.Linq;
using System.Reflection;
using UnityEditor.AssetImporters;
+using UnityEditor.Build.Content;
using Object = UnityEngine.Object;
using UnityEditor.Profiling;
using UnityEditor.Callbacks;
using Unity.Scripting.LifecycleManagement;
+using UnityEngine.SceneManagement;
namespace UnityEditor
{
@@ -66,6 +68,10 @@ public PostprocessorInfo(Type assetPostprocessorType, string[] implementedMethod
// The context of the import, used to specify dependencies
public AssetImportContext context { get { return m_Context; } internal set { m_Context = value; } }
+ // GetPostprocessOrder() as read once after assetPath is assigned, so an implementation answering per asset
+ // answers for this one, and the answer cannot change during the import.
+ internal int m_PostprocessOrder;
+
// Logs an import warning to the console.
[ExcludeFromDocs]
[Obsolete("Use context.LogImportWarning(string) instead.")]
@@ -176,6 +182,7 @@ public override List GetCallbacks()
internal partial class AssetPostprocessingInternal
{
+
// What is it:
// Static postprocessor methods always called for each importer that are part of importer static dependency.
// No new postprocessors should be added to these lists. Please reach out to #devs-import-workflow to talk about new additions.
@@ -213,6 +220,11 @@ internal partial class AssetPostprocessingInternal
"OnPostprocessTexture2DArray"
};
+ static readonly string[] k_DynamicSinglePassBuildPostprocessors =
+ {
+ "OnProcessScene",
+ };
+
static readonly string[] k_IHVImporterPostprocessors =
{
"OnPostprocessTexture",
@@ -263,6 +275,10 @@ internal partial class AssetPostprocessingInternal
{
"OnPostprocessSprites",
};
+ static readonly string[] k_DynamicSinglePassBuild_Scene_Postprocessors =
+ {
+ "OnProcessScene",
+ };
[NoAutoStaticsCleanup] // Lookup tables keyed/valued by built-in importer types and fixed dependency-name constants; contents are domain-invariant and safe to persist across reloads.
static Dictionary s_PostprocessorMethodsByDependencyKey;
@@ -287,6 +303,7 @@ static AssetPostprocessingInternal()
s_DynamicPostprocessorMethodsByImporterType = new Dictionary();
s_DynamicPostprocessorMethodsByImporterType.Add(typeof(ModelImporter), k_DynamicModelImporterPostprocessors);
s_DynamicPostprocessorMethodsByImporterType.Add(typeof(TextureImporter), k_TextureImporterPostprocessors);
+ s_DynamicPostprocessorMethodsByImporterType.Add(typeof(SinglePassBuildImporter), k_DynamicSinglePassBuildPostprocessors);
s_PostprocessorMethodsByDependencyKey = new Dictionary();
s_PostprocessorMethodsByDependencyKey.Add(kCameraPostprocessorDependencyName, k_CameraPostprocessors);
@@ -297,6 +314,7 @@ static AssetPostprocessingInternal()
s_PostprocessorMethodsByDependencyKey.Add(kTexture3DPostprocessorDependencyName, k_Texture3DPostprocessors);
s_PostprocessorMethodsByDependencyKey.Add(kTextureCubePostprocessorDependencyName, k_TextureCubePostprocessors);
s_PostprocessorMethodsByDependencyKey.Add(kTextureSpritePostprocessorDependencyName, k_SpritePostprocessors);
+ s_PostprocessorMethodsByDependencyKey.Add(kSinglePassBuildSceneProcessorDependencyName, k_DynamicSinglePassBuild_Scene_Postprocessors);
}
[Serializable]
@@ -419,6 +437,7 @@ private static string BuildHashString(SortedList list)
internal const string kTexture2DArrayPostprocessorDependencyName = "postprocessor/texture2DArray";
internal const string kTextureSpritePostprocessorDependencyName = "postprocessor/textureSprite";
internal const string kTexturePreprocessorDependencyName = "postprocessor/texturePreprocessor";
+ internal const string kSinglePassBuildSceneProcessorDependencyName = "postprocessor/build/scene";
[AutoStaticsCleanupOnCodeReload]
static Stack> m_PostprocessStack = null;
@@ -451,6 +470,8 @@ private static string BuildHashString(SortedList list)
static string m_TextureSpriteDependencyName = null;
[AutoStaticsCleanupOnCodeReload]
static string m_TexturePreprocessorDependencyName = null;
+ [AutoStaticsCleanupOnCodeReload]
+ static string m_SinglePassBuildSceneProcessorsHashString = null;
[AutoStaticsCleanupOnCodeReload]
static Dictionary> s_StaticPostprocessorsPerImporterType = new Dictionary>();
@@ -469,8 +490,7 @@ static Type[] GetCachedAssetPostprocessorClasses()
return m_PostprocessorClasses;
}
- [RequiredByNativeCode]
- static void InitPostprocessorsForTextureGenerator(string pathName)
+ internal static void InitPostprocessorsForTextureGenerator(string pathName)
{
var analyticsEvent = new AssetPostProcessorAnalyticsData();
analyticsEvent.importActionId = "None";
@@ -482,6 +502,7 @@ static void InitPostprocessorsForTextureGenerator(string pathName)
var assetPostprocessor = (AssetPostprocessor)Activator.CreateInstance(postprocessorInfo.Type);
assetPostprocessor.assetPath = pathName;
assetPostprocessor.context = null;
+ assetPostprocessor.m_PostprocessOrder = assetPostprocessor.GetPostprocessOrder();
m_ImportProcessors.Add(assetPostprocessor);
}
@@ -490,6 +511,7 @@ static void InitPostprocessorsForTextureGenerator(string pathName)
var assetPostprocessor = (AssetPostprocessor)Activator.CreateInstance(postprocessorInfo.Type);
assetPostprocessor.assetPath = pathName;
assetPostprocessor.context = null;
+ assetPostprocessor.m_PostprocessOrder = assetPostprocessor.GetPostprocessOrder();
m_ImportProcessors.Add(assetPostprocessor);
}
@@ -499,8 +521,7 @@ static void InitPostprocessorsForTextureGenerator(string pathName)
m_PostprocessStack.Push(m_ImportProcessors);
}
- [RequiredByNativeCode]
- static void InitPostprocessors(AssetImportContext context, string pathName, Type importerType, double importStartTime)
+ internal static void InitPostprocessors(AssetImportContext context, string pathName, Type importerType, double importStartTime)
{
var analyticsEvent = new AssetPostProcessorAnalyticsData();
analyticsEvent.importActionId = ((int)Math.Floor(importStartTime * 1000)).ToString();
@@ -512,6 +533,7 @@ static void InitPostprocessors(AssetImportContext context, string pathName, Type
var assetPostprocessor = (AssetPostprocessor)Activator.CreateInstance(postprocessorInfo.Type);
assetPostprocessor.assetPath = pathName;
assetPostprocessor.context = context;
+ assetPostprocessor.m_PostprocessOrder = assetPostprocessor.GetPostprocessOrder();
m_ImportProcessors.Add(assetPostprocessor);
}
@@ -520,6 +542,7 @@ static void InitPostprocessors(AssetImportContext context, string pathName, Type
var assetPostprocessor = (AssetPostprocessor)Activator.CreateInstance(postprocessorInfo.Type);
assetPostprocessor.assetPath = pathName;
assetPostprocessor.context = context;
+ assetPostprocessor.m_PostprocessOrder = assetPostprocessor.GetPostprocessOrder();
m_ImportProcessors.Add(assetPostprocessor);
}
@@ -529,8 +552,7 @@ static void InitPostprocessors(AssetImportContext context, string pathName, Type
m_PostprocessStack.Push(m_ImportProcessors);
}
- [RequiredByNativeCode]
- static void CleanupPostprocessors()
+ internal static void CleanupPostprocessors()
{
if (m_PostprocessStack != null)
{
@@ -683,43 +705,13 @@ static void PreprocessAsset()
}
}
- [RequiredByNativeCode]
- static void PreprocessModel(string pathName)
- {
- CallPostProcessMethods("OnPreprocessModel", null);
- }
-
[RequiredByNativeCode]
static void PreprocessSpeedTree(string pathName)
{
CallPostProcessMethods("OnPreprocessSpeedTree", null);
}
- [RequiredByNativeCode]
- static void PreprocessAnimation(string pathName)
- {
- CallPostProcessMethods("OnPreprocessAnimation", null);
- }
-
- [RequiredByNativeCode]
- static void PostprocessAnimation(GameObject root, AnimationClip clip)
- {
- object[] args = { root, clip };
- CallPostProcessMethods("OnPostprocessAnimation", args);
- }
-
- [RequiredByNativeCode]
- static Material ProcessMeshAssignMaterial(Renderer renderer, Material material)
- {
- object[] args = { material, renderer };
- Material assignedMaterial;
- CallPostProcessMethodsUntilReturnedObjectIsValid("OnAssignMaterialModel", args, out assignedMaterial);
-
- return assignedMaterial;
- }
-
- [RequiredByNativeCode]
- static bool ProcessMeshHasAssignMaterial()
+ internal static bool ProcessMeshHasAssignMaterial()
{
foreach (AssetPostprocessor inst in m_ImportProcessors)
{
@@ -730,20 +722,6 @@ static bool ProcessMeshHasAssignMaterial()
return false;
}
- [RequiredByNativeCode]
- static void PostprocessMeshHierarchy(GameObject root)
- {
- object[] args = { root };
- CallPostProcessMethods("OnPostprocessMeshHierarchy", args);
- }
-
- [RequiredByNativeCode]
- static void PostprocessMesh(GameObject gameObject)
- {
- object[] args = { gameObject };
- CallPostProcessMethods("OnPostprocessModel", args);
- }
-
[RequiredByNativeCode]
static void PostprocessSpeedTree(GameObject gameObject)
{
@@ -751,54 +729,7 @@ static void PostprocessSpeedTree(GameObject gameObject)
CallPostProcessMethods("OnPostprocessSpeedTree", args);
}
- [RequiredByNativeCode]
- static void PostprocessMaterial(Material material)
- {
- object[] args = { material };
- CallPostProcessMethods("OnPostprocessMaterial", args);
- }
-
- [RequiredByNativeCode]
- static void PreprocessCameraDescription(AssetImportContext assetImportContext, CameraDescription description, Camera camera, AnimationClip[] animations)
- {
- assetImportContext.DependsOnCustomDependency(kCameraPostprocessorDependencyName);
- object[] args = { description, camera, animations };
- CallPostProcessMethods("OnPreprocessCameraDescription", args);
- }
-
- [RequiredByNativeCode]
- static void PreprocessLightDescription(AssetImportContext assetImportContext, LightDescription description, Light light, AnimationClip[] animations)
- {
- assetImportContext.DependsOnCustomDependency(kLightPostprocessorDependencyName);
- object[] args = { description, light, animations };
- CallPostProcessMethods("OnPreprocessLightDescription", args);
- }
-
- [RequiredByNativeCode]
- static void PreprocessMaterialDescription(MaterialDescription description, Material material, AnimationClip[] animations)
- {
- object[] args = { description, material, animations };
- CallPostProcessMethods("OnPreprocessMaterialDescription", args);
- }
-
- [RequiredByNativeCode]
- static void PostprocessGameObjectWithUserProperties(GameObject go, string[] prop_names, object[] prop_values)
- {
- object[] args = { go, prop_names, prop_values };
- CallPostProcessMethods("OnPostprocessGameObjectWithUserProperties", args);
- }
-
- [RequiredByNativeCode]
- static void PostprocessGameObjectWithAnimatedUserProperties(GameObject go, IntPtr bindingsPtr)
- {
- var bindings = AnimationUtility.BindingsArrayPtrToBindingsArray(bindingsPtr);
- object[] args = { go, bindings };
- CallPostProcessMethods("OnPostprocessGameObjectWithAnimatedUserProperties", args);
- AnimationUtility.CopyBindingsArrayToBindingsArrayPtr(bindings, bindingsPtr);
- }
-
- [RequiredByNativeCode]
- static bool HasPostprocessGameObjectWithUserProperties()
+ internal static bool HasPostprocessGameObjectWithUserProperties()
{
foreach (AssetPostprocessor inst in m_ImportProcessors)
{
@@ -809,8 +740,7 @@ static bool HasPostprocessGameObjectWithUserProperties()
return false;
}
- [RequiredByNativeCode]
- static bool HasPostprocessGameObjectWithAnimatedUserProperties()
+ internal static bool HasPostprocessGameObjectWithAnimatedUserProperties()
{
foreach (AssetPostprocessor inst in m_ImportProcessors)
{
@@ -822,110 +752,11 @@ static bool HasPostprocessGameObjectWithAnimatedUserProperties()
}
[RequiredByNativeCode]
- static void PreprocessTexture(string pathName, AssetImportContext context)
+ static bool SinglePassBuildProcessScene(AssetImportContext assetImportContext, Scene scene, SceneImportContext sceneContext)
{
- if (context != null)
- {
- context.DependsOnCustomDependency(kTexturePreprocessorDependencyName);
- }
-
- CallPostProcessMethods("OnPreprocessTexture", null);
- }
-
- [RequiredByNativeCode]
- static void PreprocessTextureFromScript(string pathName)
- {
- CallPostProcessMethods("OnPreprocessTexture", null);
- }
-
- [RequiredByNativeCode]
- static void PostprocessTexture(Texture2D tex, string pathName, AssetImportContext context)
- {
- if (context != null)
- {
- context.DependsOnCustomDependency(kTexture2DPostprocessorDependencyName);
- }
- object[] args = { tex };
- CallPostProcessMethods("OnPostprocessTexture", args);
- }
-
- [RequiredByNativeCode]
- static void PostprocessTextureFromScript(Texture2D tex, string pathName)
- {
- object[] args = { tex };
- CallPostProcessMethods("OnPostprocessTexture", args);
- }
-
- [RequiredByNativeCode]
- static void PostprocessCubemap(Cubemap tex, string pathName, AssetImportContext context)
- {
- if (context != null)
- {
- context.DependsOnCustomDependency(kTextureCubePostprocessorDependencyName);
- }
- object[] args = { tex };
- CallPostProcessMethods("OnPostprocessCubemap", args);
- }
-
- [RequiredByNativeCode]
- static void PostprocessCubemapFromScript(Cubemap tex, string pathName)
- {
- object[] args = { tex };
- CallPostProcessMethods("OnPostprocessCubemap", args);
- }
-
- [RequiredByNativeCode]
- static void PostprocessTexture3D(Texture3D tex, string pathName, AssetImportContext context)
- {
- if (context != null)
- {
- context.DependsOnCustomDependency(kTexture3DPostprocessorDependencyName);
- }
- object[] args = { tex };
- CallPostProcessMethods("OnPostprocessTexture3D", args);
- }
-
- [RequiredByNativeCode]
- static void PostprocessTexture3DFromScript(Texture3D tex, string pathName)
- {
- object[] args = { tex };
- CallPostProcessMethods("OnPostprocessTexture3D", args);
- }
-
- [RequiredByNativeCode]
- static void PostprocessTexture2DArray(Texture2DArray tex, string pathName, AssetImportContext context)
- {
- if (context != null)
- {
- context.DependsOnCustomDependency(kTexture2DArrayPostprocessorDependencyName);
- }
- object[] args = { tex };
- CallPostProcessMethods("OnPostprocessTexture2DArray", args);
- }
-
- [RequiredByNativeCode]
- static void PostprocessTexture2DArrayFromScript(Texture2DArray tex, string pathName)
- {
- object[] args = { tex };
- CallPostProcessMethods("OnPostprocessTexture2DArray", args);
- }
-
- [RequiredByNativeCode]
- static void PostprocessSprites(Texture2D tex, string pathName, Sprite[] sprites, AssetImportContext context)
- {
- if (context != null)
- {
- context.DependsOnCustomDependency(kTextureSpritePostprocessorDependencyName);
- }
- object[] args = { tex, sprites };
- CallPostProcessMethods("OnPostprocessSprites", args);
- }
-
- [RequiredByNativeCode]
- static void PostprocessSpritesFromScript(Texture2D tex, string pathName, Sprite[] sprites)
- {
- object[] args = { tex, sprites };
- CallPostProcessMethods("OnPostprocessSprites", args);
+ assetImportContext.DependsOnCustomDependency(kSinglePassBuildSceneProcessorDependencyName);
+ object[] args = { scene, sceneContext };
+ return CallPostProcessMethods_HandlingExceptions("OnProcessScene", args);
}
[RequiredByNativeCode]
@@ -999,6 +830,7 @@ static void RefreshCustomDependencies()
AssetDatabase.RegisterCustomDependency(kTexture2DArrayPostprocessorDependencyName, Hash128.Compute(GetTexture2DArrayProcessorsHashString()));
AssetDatabase.RegisterCustomDependency(kTextureSpritePostprocessorDependencyName, Hash128.Compute(GetTextureSpriteProcessorsHashString()));
AssetDatabase.RegisterCustomDependency(kTexturePreprocessorDependencyName, Hash128.Compute(GetTexturePreProcessorsHashString()));
+ AssetDatabase.RegisterCustomDependency(kSinglePassBuildSceneProcessorDependencyName, Hash128.Compute(GetSinglePassBuildSceneProcessorsHashString()));
}
static void GetProcessorHashString(string methodName, ref string hashString)
@@ -1088,34 +920,85 @@ static string GetTextureSpriteProcessorsHashString()
return m_TextureSpriteDependencyName;
}
- static bool IsAssetPostprocessorAnalyticsEnabled()
+ [RequiredByNativeCode]
+ internal static string GetSinglePassBuildSceneProcessorsHashString()
+ {
+ GetProcessorHashString("OnProcessScene", ref m_SinglePassBuildSceneProcessorsHashString);
+ return m_SinglePassBuildSceneProcessorsHashString;
+ }
+
+ internal static bool IsAssetPostprocessorAnalyticsEnabled()
{
return EditorAnalytics.enabled;
}
- static void CallPostProcessMethodsUntilReturnedObjectIsValid(string methodName, object[] args, out T returnedObject) where T : class
+ ///
+ /// Call each in current list.
+ ///
+ ///
+ /// This method does not stop if any of the throws.
+ /// All available methods will always be called.
+ ///
+ /// The name of the method to call.
+ /// List of arguments requires.
+ /// True if nothing have thrown, false otherwise.
+ static bool CallPostProcessMethods_HandlingExceptions(string methodName, object[] args)
{
- returnedObject = default(T);
- int invocationCount = 0;
- float startTime = Time.realtimeSinceStartup;
+ if (m_ImportProcessors == null)
+ {
+ Debug.LogException(new Exception("m_ImportProcessors is null, InitPostProcessors should be called before any of the post process methods are called."));
+ return false;
+ }
- foreach (AssetPostprocessor inst in m_ImportProcessors)
+ bool noError = true;
+ if (IsAssetPostprocessorAnalyticsEnabled())
{
- if (InvokeMethodIfAvailable(inst, methodName, args, ref returnedObject))
+ int invocationCount = 0;
+ float startTime = Time.realtimeSinceStartup;
+ foreach (AssetPostprocessor inst in m_ImportProcessors)
{
- invocationCount++;
- break;
+ try
+ {
+ if (InvokeMethodIfAvailable(inst, methodName, args))
+ invocationCount++;
+ }
+ catch (Exception e)
+ {
+ if(e is TargetInvocationException te && te.InnerException != null)
+ e = te.InnerException;
+ Debug.LogException(e);
+ noError = false;
+ }
}
- }
- if (IsAssetPostprocessorAnalyticsEnabled() && invocationCount > 0)
+ if (invocationCount > 0)
+ {
+ var methodCallAnalytics = new AssetPostProcessorMethodCallAnalyticsData();
+ methodCallAnalytics.invocationCount = invocationCount;
+ methodCallAnalytics.methodName = methodName;
+ methodCallAnalytics.duration_sec = Time.realtimeSinceStartup - startTime;
+ s_AnalyticsEventsStack.Peek().postProcessorCalls.Add(methodCallAnalytics);
+ }
+ }
+ else
{
- var methodCallAnalytics = new AssetPostProcessorMethodCallAnalyticsData();
- methodCallAnalytics.invocationCount = invocationCount;
- methodCallAnalytics.methodName = methodName;
- methodCallAnalytics.duration_sec = Time.realtimeSinceStartup - startTime;
- s_AnalyticsEventsStack.Peek().postProcessorCalls.Add(methodCallAnalytics);
+ foreach (AssetPostprocessor inst in m_ImportProcessors)
+ {
+ try
+ {
+ InvokeMethodIfAvailable(inst, methodName, args);
+ }
+ catch (Exception e)
+ {
+ if(e is TargetInvocationException te && te.InnerException != null)
+ e = te.InnerException;
+ Debug.LogException(e);
+ noError = false;
+ }
+ }
}
+
+ return noError;
}
static void CallPostProcessMethods(string methodName, object[] args)
@@ -1164,6 +1047,89 @@ static object InvokeMethod(MethodInfo method, object[] args)
return res;
}
+ // Runs the postprocessors whose order falls between minOrder and maxOrder, both ends included.
+ // Don't stop at the first one out of range: m_ImportProcessors is sorted using its own call
+ // to GetPostprocessOrder(), which need not match the stored order, so a postprocessor in
+ // range can still turn up later.
+ internal static void CallPostProcessMethodsInRange(string methodName, object[] args, int minOrder, int maxOrder)
+ {
+ if (m_ImportProcessors == null)
+ {
+ throw new Exception("m_ImportProcessors is null, InitPostProcessors should be called before any of the post process methods are called.");
+ }
+
+ foreach (AssetPostprocessor inst in m_ImportProcessors)
+ {
+ if (inst.m_PostprocessOrder < minOrder || inst.m_PostprocessOrder > maxOrder)
+ continue;
+
+ InvokeMethodIfAvailable(inst, methodName, args);
+ }
+ }
+
+ internal static int CallPostProcessMethodsInRange(string methodName, object[] args, int minOrder, int maxOrder, ref float duration)
+ {
+ if (m_ImportProcessors == null)
+ {
+ throw new Exception("m_ImportProcessors is null, InitPostProcessors should be called before any of the post process methods are called.");
+ }
+
+ int invocationCount = 0;
+ float startTime = Time.realtimeSinceStartup;
+
+ foreach (AssetPostprocessor inst in m_ImportProcessors)
+ {
+ if (inst.m_PostprocessOrder < minOrder || inst.m_PostprocessOrder > maxOrder)
+ continue;
+
+ if (InvokeMethodIfAvailable(inst, methodName, args))
+ invocationCount++;
+ }
+
+ duration += Time.realtimeSinceStartup - startTime;
+ return invocationCount;
+ }
+
+ internal static bool CallPostProcessMethodsInRangeUntilReturnedObjectIsValid(string methodName, object[] args, int minOrder, int maxOrder, out T returnedObject, ref float duration) where T : class
+ {
+ if (m_ImportProcessors == null)
+ {
+ throw new Exception("m_ImportProcessors is null, InitPostProcessors should be called before any of the post process methods are called.");
+ }
+
+ returnedObject = default(T);
+ bool found = false;
+ float startTime = Time.realtimeSinceStartup;
+
+ foreach (AssetPostprocessor inst in m_ImportProcessors)
+ {
+ if (inst.m_PostprocessOrder < minOrder || inst.m_PostprocessOrder > maxOrder)
+ continue;
+
+ if (InvokeMethodIfAvailable(inst, methodName, args, ref returnedObject))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ duration += Time.realtimeSinceStartup - startTime;
+ return found;
+ }
+
+ // Records one entry; invocationCount and duration are the totals for the callback.
+ internal static void RecordPostProcessMethodCallAnalytics(string methodName, int invocationCount, float duration)
+ {
+ if (invocationCount > 0)
+ {
+ var methodCallAnalytics = new AssetPostProcessorMethodCallAnalyticsData();
+ methodCallAnalytics.invocationCount = invocationCount;
+ methodCallAnalytics.methodName = methodName;
+ methodCallAnalytics.duration_sec = duration;
+ s_AnalyticsEventsStack.Peek().postProcessorCalls.Add(methodCallAnalytics);
+ }
+ }
+
static bool InvokeMethodIfAvailable(object target, string methodName, object[] args)
{
var type = target.GetType();
diff --git a/Editor/Mono/AssetStore/AssetStoreWindow.cs b/Editor/Mono/AssetStore/AssetStoreWindow.cs
index 57270ab3e9..06ac0f1a7c 100644
--- a/Editor/Mono/AssetStore/AssetStoreWindow.cs
+++ b/Editor/Mono/AssetStore/AssetStoreWindow.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: Packman not yet converted
using UnityEngine;
using UnityEngine.UIElements;
using UnityEditor.Connect;
@@ -14,10 +13,6 @@ namespace UnityEditor
[EditorWindowTitle(title = "Asset Store", icon = "Asset Store")]
internal class AssetStoreWindow : EditorWindow
{
- #pragma warning disable UAL0015 // this side effect does not outlive the current call (global trigger / lazily-loaded asset re-fetched on next access); a stale reference is harmlessly replaced
- internal AssetStoreWindow() {}
- #pragma warning restore UAL0015
-
public static AssetStoreWindow Init()
{
if (EditorPrefs.GetBool("AlwaysOpenAssetStoreInBrowser", false))
@@ -62,7 +57,7 @@ public void OnEnable()
var lightStyleSheet = EditorGUIUtility.Load(UIElementsEditorUtility.s_DefaultCommonLightStyleSheetPath) as StyleSheet;
var assetStoreStyleSheet = EditorGUIUtility.Load("StyleSheets/AssetStore/AssetStoreWindow.uss") as StyleSheet;
var styleSheet = CreateInstance();
- styleSheet.isDefaultStyleSheet = true;
+ styleSheet.priority = UnityEngine.UIElements.StyleSheetPriority.Builtin;
var resolver = new StyleSheets.StyleSheetResolver();
resolver.AddStyleSheets(lightStyleSheet, assetStoreStyleSheet);
@@ -119,4 +114,3 @@ private void SetMinMaxSizes()
private Toggle alwaysOpenInBrowserToggle { get { return rootVisualElement.Q("alwaysOpenInBrowserToggle"); } }
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/AttributeHelper.cs b/Editor/Mono/AttributeHelper.cs
index 2caeadf252..ed64bc2055 100644
--- a/Editor/Mono/AttributeHelper.cs
+++ b/Editor/Mono/AttributeHelper.cs
@@ -312,6 +312,9 @@ public IEnumerable FilterAndSortOnAttribute(Func filter,
}
[AutoStaticsCleanupOnCodeReload]
+ // Reflection memo: GetMethodsWithAttribute re-scans and re-adds an entry on every miss, so entries
+ // dropped on reload are recomputed from the currently loaded assemblies.
+ [IgnoreForUAL0015("Reflection memo, entries recomputed on demand by GetMethodsWithAttribute")]
static Dictionary s_DecoratedMethodsByAttrTypeCache = new Dictionary();
private const BindingFlags kAllStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
diff --git a/Editor/Mono/Audio/Analytics/AudioRandomContainerQuitAnalyticsEvent.cs b/Editor/Mono/Audio/Analytics/AudioRandomContainerQuitAnalyticsEvent.cs
index cbefb12928..27a8a63948 100644
--- a/Editor/Mono/Audio/Analytics/AudioRandomContainerQuitAnalyticsEvent.cs
+++ b/Editor/Mono/Audio/Analytics/AudioRandomContainerQuitAnalyticsEvent.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: AudioAuthoring not yet converted
using System;
using UnityEngine;
using UnityEngine.Analytics;
@@ -63,4 +62,3 @@ static void SendEvent()
EditorAnalytics.SendAnalytic(new AudioRandomAnalytic(assetPaths.Length));
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/Audio/AudioContainerWindow.cs b/Editor/Mono/Audio/AudioContainerWindow.cs
index 74c997b220..e44e74d255 100644
--- a/Editor/Mono/Audio/AudioContainerWindow.cs
+++ b/Editor/Mono/Audio/AudioContainerWindow.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: AudioAuthoring not yet converted
using System;
using System.Collections.Generic;
using System.Linq;
@@ -22,10 +21,6 @@ namespace UnityEditor;
sealed partial class AudioContainerWindow : EditorWindow
{
- #pragma warning disable UAL0015 // this side effect does not outlive the current call (global trigger / lazily-loaded asset re-fetched on next access); a stale reference is harmlessly replaced
- public AudioContainerWindow() { }
- #pragma warning restore UAL0015
-
enum Icons
{
Play = 0,
@@ -1578,4 +1573,3 @@ static void Preview(ShortcutArguments args)
#endregion
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/Audio/AudioContainerWindowState.cs b/Editor/Mono/Audio/AudioContainerWindowState.cs
index ac344de75e..c900df7467 100644
--- a/Editor/Mono/Audio/AudioContainerWindowState.cs
+++ b/Editor/Mono/Audio/AudioContainerWindowState.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: AudioAuthoring not yet converted
using System;
using UnityEditor.UIElements;
using UnityEngine;
@@ -368,4 +367,3 @@ void OnSelectionChanged()
UpdateTarget();
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/Audio/Mixer/AudioMixerDescription.cs b/Editor/Mono/Audio/Mixer/AudioMixerDescription.cs
index 09834b0393..bc84785756 100644
--- a/Editor/Mono/Audio/Mixer/AudioMixerDescription.cs
+++ b/Editor/Mono/Audio/Mixer/AudioMixerDescription.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: AudioAuthoring not yet converted
using UnityEngine;
using UnityEditor;
using System;
@@ -159,4 +158,3 @@ public static bool RegisterAudioMixerEffect(string name, MixerParameterDefinitio
private static readonly List s_MixerEffectDefinitions = new List();
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/Audio/Mixer/GUI/AudioMixerChannelStripView.cs b/Editor/Mono/Audio/Mixer/GUI/AudioMixerChannelStripView.cs
index 666b71d258..abbc5d09fe 100644
--- a/Editor/Mono/Audio/Mixer/GUI/AudioMixerChannelStripView.cs
+++ b/Editor/Mono/Audio/Mixer/GUI/AudioMixerChannelStripView.cs
@@ -335,7 +335,7 @@ class PatchSlot
public float x, y;
}
- GUIContent bypassButtonContent = EditorGUIUtility.TrTextContent("", "Toggle bypass on this effect");
+ GUIContent bypassButtonContent = L10n.TextContent("", "Toggle bypass on this effect", null, null);
void EffectSlot(Rect effectRect, AudioMixerSnapshotController snapshot, AudioMixerEffectController effect, int effectIndex, ref int highlightEffectIndex, ChannelStripParams p, ref Dictionary patchslots)
{
if (effect == null)
@@ -975,8 +975,8 @@ private void DoEffectSlotInsertEffectPopup(Rect buttonRect, AudioMixerGroupContr
var effect = group.effects[effectSlotIndex];
if (!effect.IsAttenuation() && !effect.IsSend() && !effect.IsReceive() && !effect.IsDuckVolume())
{
- pm.AddItem(EditorGUIUtility.TrTextContent("Allow Wet Mixing (causes higher memory usage)"), effect.enableWetMix, delegate { AudioMixerUtility.ToggleEffectWetMix(effect); });
- pm.AddItem(EditorGUIUtility.TrTextContent("Bypass"), effect.bypass, delegate { effect.bypass = !effect.bypass; m_Controller.UpdateBypass(); InspectorWindow.RepaintAllInspectors(); });
+ pm.AddItem(L10n.TextContent("Allow Wet Mixing (causes higher memory usage)", null, null, null), effect.enableWetMix, delegate { AudioMixerUtility.ToggleEffectWetMix(effect); });
+ pm.AddItem(L10n.TextContent("Bypass", null, null, null), effect.bypass, delegate { effect.bypass = !effect.bypass; m_Controller.UpdateBypass(); InspectorWindow.RepaintAllInspectors(); });
pm.AddSeparator("");
}
@@ -995,7 +995,7 @@ private void DoEffectSlotInsertEffectPopup(Rect buttonRect, AudioMixerGroupContr
if (!effect.IsAttenuation())
{
pm.AddSeparator("");
- pm.AddItem(EditorGUIUtility.TrTextContent("Remove"), false, RemoveEffectPopupCallback, new EffectContext(m_Controller, groups, effectSlotIndex, ""));
+ pm.AddItem(L10n.TextContent("Remove", null, null, null), false, RemoveEffectPopupCallback, new EffectContext(m_Controller, groups, effectSlotIndex, ""));
bool insertedSeparator = false;
if (effect.IsSend())
{
@@ -1008,7 +1008,7 @@ private void DoEffectSlotInsertEffectPopup(Rect buttonRect, AudioMixerGroupContr
}
var sendContext = new ConnectSendContext(group.controller, effect, null);
- pm.AddItem(EditorGUIUtility.TrTextContent("Disconnect from '" + effect.GetSendTargetDisplayString(effectMap) + "'") , false, ConnectSendPopupCallback, sendContext);
+ pm.AddItem(L10n.TextContent("Disconnect from '" + effect.GetSendTargetDisplayString(effectMap) + "'", null, null, null) , false, ConnectSendPopupCallback, sendContext);
}
if (!insertedSeparator)
@@ -1278,7 +1278,7 @@ void DoTotaldB(ChannelStripParams p)
GUI.Label(rect, string.Format(CultureInfo.InvariantCulture.NumberFormat, "{0:F1} dB", vu_level), styles.totalVULevel);
}
- GUIContent addText = EditorGUIUtility.TrTextContent("Add...");
+ GUIContent addText = L10n.TextContent("Add...", null, null, null);
void DoEffectList(ChannelStripParams p, bool selected, ref int highlightEffectIndex, ref Dictionary patchslots, bool showBusConnectionsOfSelection)
{
Event evt = Event.current;
diff --git a/Editor/Mono/Audio/Mixer/GUI/AudioMixerDrawUtils.cs b/Editor/Mono/Audio/Mixer/GUI/AudioMixerDrawUtils.cs
index b8af67f480..374fc5e63c 100644
--- a/Editor/Mono/Audio/Mixer/GUI/AudioMixerDrawUtils.cs
+++ b/Editor/Mono/Audio/Mixer/GUI/AudioMixerDrawUtils.cs
@@ -72,18 +72,18 @@ public class Styles
public GUIStyle warningOverlay = GetStyle("WarningOverlay");
public Texture2D scrollShadowTexture = EditorGUIUtility.FindTexture("ScrollShadow");
public Texture2D leftToRightShadowTexture = EditorGUIUtility.FindTexture("LeftToRightShadow");
- public GUIContent soloGUIContent = EditorGUIUtility.TrTextContent("", "Adds this group to set of soloed groups");
- public GUIContent muteGUIContent = EditorGUIUtility.TrTextContent("", "Mutes this group");
- public GUIContent bypassGUIContent = EditorGUIUtility.TrTextContent("", "Bypasses the effects on this group");
- public GUIContent effectSlotGUIContent = EditorGUIUtility.TrTextContent("", "Drag horizontally to change wet mix levels or vertically to change order of effects. Note: Enable wet mixing in the context menu.");
- public GUIContent attenuationSlotGUIContent = EditorGUIUtility.TrTextContent("", "Place the attenuation slot in the effect stack where attenuation should take effect");
- public GUIContent emptySendSlotGUIContent = EditorGUIUtility.TrTextContent("", "Connect to a Receive in the context menu or in the inspector");
- public GUIContent returnSlotGUIContent = EditorGUIUtility.TrTextContent("", "Connect a Send to this Receive");
- public GUIContent duckVolumeSlotGUIContent = EditorGUIUtility.TrTextContent("", "Connect a Send to this Duck Volume");
- public GUIContent duckingFaderGUIContent = EditorGUIUtility.TrTextContent("", "Ducking Fader");
- public GUIContent attenuationFader = EditorGUIUtility.TrTextContent("", "Attenuation fader");
- public GUIContent vuMeterGUIContent = EditorGUIUtility.TrTextContent("", "The VU meter shows the current level of the mix of all sounds and subgroups.");
- public GUIContent referencedGroups = EditorGUIUtility.TrTextContent("Referenced groups", "Mixer groups that are hidden but are referenced by the visible mixer groups are shown here for convenience");
+ public GUIContent soloGUIContent = L10n.TextContent("", "Adds this group to set of soloed groups", null, null);
+ public GUIContent muteGUIContent = L10n.TextContent("", "Mutes this group", null, null);
+ public GUIContent bypassGUIContent = L10n.TextContent("", "Bypasses the effects on this group", null, null);
+ public GUIContent effectSlotGUIContent = L10n.TextContent("", "Drag horizontally to change wet mix levels or vertically to change order of effects. Note: Enable wet mixing in the context menu.", null, null);
+ public GUIContent attenuationSlotGUIContent = L10n.TextContent("", "Place the attenuation slot in the effect stack where attenuation should take effect", null, null);
+ public GUIContent emptySendSlotGUIContent = L10n.TextContent("", "Connect to a Receive in the context menu or in the inspector", null, null);
+ public GUIContent returnSlotGUIContent = L10n.TextContent("", "Connect a Send to this Receive", null, null);
+ public GUIContent duckVolumeSlotGUIContent = L10n.TextContent("", "Connect a Send to this Duck Volume", null, null);
+ public GUIContent duckingFaderGUIContent = L10n.TextContent("", "Ducking Fader", null, null);
+ public GUIContent attenuationFader = L10n.TextContent("", "Attenuation fader", null, null);
+ public GUIContent vuMeterGUIContent = L10n.TextContent("", "The VU meter shows the current level of the mix of all sounds and subgroups.", null, null);
+ public GUIContent referencedGroups = L10n.TextContent("Referenced groups", "Mixer groups that are hidden but are referenced by the visible mixer groups are shown here for convenience", null, null);
public GUIContent sendString = new GUIContent("s");
diff --git a/Editor/Mono/Audio/Mixer/GUI/AudioMixerEffectGUI.cs b/Editor/Mono/Audio/Mixer/GUI/AudioMixerEffectGUI.cs
index 1e2c213e7b..b4c4dcdecc 100644
--- a/Editor/Mono/Audio/Mixer/GUI/AudioMixerEffectGUI.cs
+++ b/Editor/Mono/Audio/Mixer/GUI/AudioMixerEffectGUI.cs
@@ -55,22 +55,22 @@ public static bool Slider(GUIContent label, ref float value, float displayScale,
GenericMenu pm = new GenericMenu();
if (!exposed)
- pm.AddItem(EditorGUIUtility.TrTextContent("Expose '" + path.ResolveStringPath(false) + "' to script"), false, ExposePopupCallback, new ExposedParamContext(controller, path));
+ pm.AddItem(L10n.TextContent("Expose '" + path.ResolveStringPath(false) + "' to script", null, null, null), false, ExposePopupCallback, new ExposedParamContext(controller, path));
else
- pm.AddItem(EditorGUIUtility.TrTextContent("Unexpose"), false, UnexposePopupCallback, new ExposedParamContext(controller, path));
+ pm.AddItem(L10n.TextContent("Unexpose", null, null, null), false, UnexposePopupCallback, new ExposedParamContext(controller, path));
ParameterTransitionType existingType;
bool overrideExists = controller.TargetSnapshot.GetTransitionTypeOverride(path.parameter, out existingType);
System.Diagnostics.Debug.Assert(!overrideExists || existingType == ParameterTransitionType.Lerp);
pm.AddSeparator(string.Empty);
- pm.AddItem(EditorGUIUtility.TrTextContent("Linear Snapshot Transition"), existingType == ParameterTransitionType.Lerp, ParameterTransitionOverrideCallback, new ParameterTransitionOverrideContext(controller, path.parameter, ParameterTransitionType.Lerp));
- pm.AddItem(EditorGUIUtility.TrTextContent("Smoothstep Snapshot Transition"), existingType == ParameterTransitionType.Smoothstep, ParameterTransitionOverrideCallback, new ParameterTransitionOverrideContext(controller, path.parameter, ParameterTransitionType.Smoothstep));
- pm.AddItem(EditorGUIUtility.TrTextContent("Squared Snapshot Transition"), existingType == ParameterTransitionType.Squared, ParameterTransitionOverrideCallback, new ParameterTransitionOverrideContext(controller, path.parameter, ParameterTransitionType.Squared));
- pm.AddItem(EditorGUIUtility.TrTextContent("SquareRoot Snapshot Transition"), existingType == ParameterTransitionType.SquareRoot, ParameterTransitionOverrideCallback, new ParameterTransitionOverrideContext(controller, path.parameter, ParameterTransitionType.SquareRoot));
- pm.AddItem(EditorGUIUtility.TrTextContent("BrickwallStart Snapshot Transition"), existingType == ParameterTransitionType.BrickwallStart, ParameterTransitionOverrideCallback, new ParameterTransitionOverrideContext(controller, path.parameter, ParameterTransitionType.BrickwallStart));
- pm.AddItem(EditorGUIUtility.TrTextContent("BrickwallEnd Snapshot Transition"), existingType == ParameterTransitionType.BrickwallEnd, ParameterTransitionOverrideCallback, new ParameterTransitionOverrideContext(controller, path.parameter, ParameterTransitionType.BrickwallEnd));
- pm.AddItem(EditorGUIUtility.TrTextContent("Attenuation Snapshot Transition"), existingType == ParameterTransitionType.Attenuation, ParameterTransitionOverrideCallback, new ParameterTransitionOverrideContext(controller, path.parameter, ParameterTransitionType.Attenuation));
+ pm.AddItem(L10n.TextContent("Linear Snapshot Transition", null, null, null), existingType == ParameterTransitionType.Lerp, ParameterTransitionOverrideCallback, new ParameterTransitionOverrideContext(controller, path.parameter, ParameterTransitionType.Lerp));
+ pm.AddItem(L10n.TextContent("Smoothstep Snapshot Transition", null, null, null), existingType == ParameterTransitionType.Smoothstep, ParameterTransitionOverrideCallback, new ParameterTransitionOverrideContext(controller, path.parameter, ParameterTransitionType.Smoothstep));
+ pm.AddItem(L10n.TextContent("Squared Snapshot Transition", null, null, null), existingType == ParameterTransitionType.Squared, ParameterTransitionOverrideCallback, new ParameterTransitionOverrideContext(controller, path.parameter, ParameterTransitionType.Squared));
+ pm.AddItem(L10n.TextContent("SquareRoot Snapshot Transition", null, null, null), existingType == ParameterTransitionType.SquareRoot, ParameterTransitionOverrideCallback, new ParameterTransitionOverrideContext(controller, path.parameter, ParameterTransitionType.SquareRoot));
+ pm.AddItem(L10n.TextContent("BrickwallStart Snapshot Transition", null, null, null), existingType == ParameterTransitionType.BrickwallStart, ParameterTransitionOverrideCallback, new ParameterTransitionOverrideContext(controller, path.parameter, ParameterTransitionType.BrickwallStart));
+ pm.AddItem(L10n.TextContent("BrickwallEnd Snapshot Transition", null, null, null), existingType == ParameterTransitionType.BrickwallEnd, ParameterTransitionOverrideCallback, new ParameterTransitionOverrideContext(controller, path.parameter, ParameterTransitionType.BrickwallEnd));
+ pm.AddItem(L10n.TextContent("Attenuation Snapshot Transition", null, null, null), existingType == ParameterTransitionType.Attenuation, ParameterTransitionOverrideCallback, new ParameterTransitionOverrideContext(controller, path.parameter, ParameterTransitionType.Attenuation));
pm.AddSeparator(string.Empty);
pm.ShowAsContext();
diff --git a/Editor/Mono/Audio/Mixer/GUI/AudioMixerEffectView.cs b/Editor/Mono/Audio/Mixer/GUI/AudioMixerEffectView.cs
index 1f8aea4c2c..7be32f3b76 100644
--- a/Editor/Mono/Audio/Mixer/GUI/AudioMixerEffectView.cs
+++ b/Editor/Mono/Audio/Mixer/GUI/AudioMixerEffectView.cs
@@ -28,14 +28,14 @@ class AudioMixerEffectView
static class Texts
{
- public static readonly GUIContent editInPlaymode = EditorGUIUtility.TrTextContent("Edit in Playmode");
- public static readonly GUIContent pitch = EditorGUIUtility.TrTextContent("Pitch");
- public static readonly GUIContent addEffect = EditorGUIUtility.TrTextContent("Add Effect");
- public static readonly GUIContent volume = EditorGUIUtility.TrTextContent("Volume");
- public static readonly GUIContent sendLevel = EditorGUIUtility.TrTextContent("Send level");
- public static readonly GUIContent bus = EditorGUIUtility.TrTextContent("Receive");
- public static readonly GUIContent none = EditorGUIUtility.TrTextContent("None");
- public static readonly GUIContent wet = EditorGUIUtility.TrTextContent("Wet", "Enables/disables wet/dry ratio on this effect. Note that this makes the DSP graph more complex and requires additional CPU and memory, so use it only when necessary.");
+ public static readonly GUIContent editInPlaymode = L10n.TextContent("Edit in Playmode", null, null, null);
+ public static readonly GUIContent pitch = L10n.TextContent("Pitch", null, null, null);
+ public static readonly GUIContent addEffect = L10n.TextContent("Add Effect", null, null, null);
+ public static readonly GUIContent volume = L10n.TextContent("Volume", null, null, null);
+ public static readonly GUIContent sendLevel = L10n.TextContent("Send level", null, null, null);
+ public static readonly GUIContent bus = L10n.TextContent("Receive", null, null, null);
+ public static readonly GUIContent none = L10n.TextContent("None", null, null, null);
+ public static readonly GUIContent wet = L10n.TextContent("Wet", "Enables/disables wet/dry ratio on this effect. Note that this makes the DSP graph more complex and requires additional CPU and memory, so use it only when necessary.", null, null);
public const string dB = "dB";
public const string percentage = "%";
public const string cpuFormatString = " - CPU: {0:#0.00}%";
@@ -372,8 +372,8 @@ private static void ShowEffectContextMenu(AudioMixerGroupController group, Audio
{
if (!effect.IsAttenuation() && !effect.IsSend() && !effect.IsDuckVolume())
{
- menu.AddItem(EditorGUIUtility.TrTextContent("Allow Wet Mixing (causes higher memory usage)"), effect.enableWetMix, delegate { AudioMixerUtility.ToggleEffectWetMix(effect); });
- menu.AddItem(EditorGUIUtility.TrTextContent("Bypass"), effect.bypass, delegate()
+ menu.AddItem(L10n.TextContent("Allow Wet Mixing (causes higher memory usage)", null, null, null), effect.enableWetMix, delegate { AudioMixerUtility.ToggleEffectWetMix(effect); });
+ menu.AddItem(L10n.TextContent("Bypass", null, null, null), effect.bypass, delegate()
{
Undo.RecordObject(effect, "Bypass Effect");
effect.bypass = !effect.bypass;
@@ -384,7 +384,7 @@ private static void ShowEffectContextMenu(AudioMixerGroupController group, Audio
menu.AddSeparator("");
}
- menu.AddItem(EditorGUIUtility.TrTextContent("Copy effect settings to all snapshots"), false, delegate()
+ menu.AddItem(L10n.TextContent("Copy effect settings to all snapshots", null, null, null), false, delegate()
{
Undo.RecordObject(controller, "Copy effect settings to all snapshots");
if (effect.IsAttenuation())
@@ -396,7 +396,7 @@ private static void ShowEffectContextMenu(AudioMixerGroupController group, Audio
if (!effect.IsAttenuation() && !effect.IsSend() && !effect.IsDuckVolume() && effect.enableWetMix)
{
- menu.AddItem(EditorGUIUtility.TrTextContent("Copy effect settings to all snapshots, including wet level"), false, delegate()
+ menu.AddItem(L10n.TextContent("Copy effect settings to all snapshots, including wet level", null, null, null), false, delegate()
{
Undo.RecordObject(controller, "Copy effect settings to all snapshots, including wet level");
controller.CopyEffectSettingsToAllSnapshots(group, effectIndex, controller.TargetSnapshot, true);
@@ -414,7 +414,7 @@ private static void ShowEffectContextMenu(AudioMixerGroupController group, Audio
if (!effect.IsAttenuation())
{
menu.AddSeparator("");
- menu.AddItem(EditorGUIUtility.TrTextContent("Remove this effect"), false, delegate()
+ menu.AddItem(L10n.TextContent("Remove this effect", null, null, null), false, delegate()
{
controller.RemoveEffect(effect, group);
AudioMixerUtility.RepaintAudioMixerAndInspectors();
@@ -428,13 +428,13 @@ private static void ShowBusPopupMenu(int effectIndex, AudioMixerGroupController
{
GenericMenu pm = new GenericMenu();
var sendContext = new AudioMixerChannelStripView.ConnectSendContext(group.controller, effect, null);
- pm.AddItem(EditorGUIUtility.TrTextContent("None"), false, AudioMixerChannelStripView.ConnectSendPopupCallback, sendContext);
+ pm.AddItem(L10n.TextContent("None", null, null, null), false, AudioMixerChannelStripView.ConnectSendPopupCallback, sendContext);
pm.AddSeparator("");
AudioMixerChannelStripView.AddMenuItemsForReturns(pm, string.Empty, effectIndex, group, allGroups, effectMap, effect, true);
if (pm.GetItemCount() == 2)
{
- pm.AddDisabledItem(EditorGUIUtility.TrTextContent("No valid Receive targets found"));
+ pm.AddDisabledItem(L10n.TextContent("No valid Receive targets found", null, null, null));
}
pm.DropDown(buttonRect);
diff --git a/Editor/Mono/Audio/Mixer/GUI/AudioMixerExposedParameterView.cs b/Editor/Mono/Audio/Mixer/GUI/AudioMixerExposedParameterView.cs
index 8c86a45d7a..f92e323aa0 100644
--- a/Editor/Mono/Audio/Mixer/GUI/AudioMixerExposedParameterView.cs
+++ b/Editor/Mono/Audio/Mixer/GUI/AudioMixerExposedParameterView.cs
@@ -71,13 +71,13 @@ public void OnContextClick(int itemIndex)
{
GenericMenu pm = new GenericMenu();
pm.AddItem(
- EditorGUIUtility.TrTextContent("Unexpose"),
+ L10n.TextContent("Unexpose", null, null, null),
false,
delegate(object data) { Delete((int)data); },
itemIndex);
pm.AddItem(
- EditorGUIUtility.TrTextContent("Rename"),
+ L10n.TextContent("Rename", null, null, null),
false,
delegate(object data) { m_ReorderableListWithRenameAndScrollView.BeginRename((int)data, 0f); },
itemIndex);
diff --git a/Editor/Mono/Audio/Mixer/GUI/AudioMixerExposedParametersPopup.cs b/Editor/Mono/Audio/Mixer/GUI/AudioMixerExposedParametersPopup.cs
index 2177876d08..2ca624a8d5 100644
--- a/Editor/Mono/Audio/Mixer/GUI/AudioMixerExposedParametersPopup.cs
+++ b/Editor/Mono/Audio/Mixer/GUI/AudioMixerExposedParametersPopup.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: AudioAuthoring not yet converted
using System;
using Unity.Scripting.LifecycleManagement;
using UnityEngine;
@@ -25,7 +24,7 @@ internal static void Popup(AudioMixerController controller, GUIStyle style, para
}
// Cache to prevent constructing string on every event
- static readonly GUIContent m_ButtonContent = EditorGUIUtility.TrTextContent("", "Audio Mixer parameters can be exposed to scripting. Select an Audio Mixer Group, right click one of its properties in the Inspector and select 'Expose'.");
+ static readonly GUIContent m_ButtonContent = L10n.TextContent("", "Audio Mixer parameters can be exposed to scripting. Select an Audio Mixer Group, right click one of its properties in the Inspector and select 'Expose'.", null, null);
[NoAutoStaticsCleanup] // Display cache invalidation counter compared against controller state; -1 default re-syncs on next access, safe to persist across reload.
static int m_LastNumExposedParams = -1;
static GUIContent GetButtonContent(AudioMixerController controller)
@@ -64,4 +63,3 @@ public override Vector2 GetWindowSize()
}
}
} // namespace
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/Audio/Mixer/GUI/AudioMixerGroupTreeView.cs b/Editor/Mono/Audio/Mixer/GUI/AudioMixerGroupTreeView.cs
index 0f2f2b618a..210f4c222c 100644
--- a/Editor/Mono/Audio/Mixer/GUI/AudioMixerGroupTreeView.cs
+++ b/Editor/Mono/Audio/Mixer/GUI/AudioMixerGroupTreeView.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: AudioAuthoring not yet converted
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
@@ -163,8 +162,8 @@ public override bool IsRenamingItemAllowed(TreeViewItem node)
internal class AudioGroupTreeViewGUI : TreeViewGUI
{
readonly float column1Width = 20f;
- readonly GUIContent k_VisibleON = EditorGUIUtility.TrIconContent("animationvisibilitytoggleon");
- readonly GUIContent k_VisibleOFF = EditorGUIUtility.TrIconContent("animationvisibilitytoggleoff");
+ readonly GUIContent k_VisibleON = L10n.IconContent("animationvisibilitytoggleon", null, null);
+ readonly GUIContent k_VisibleOFF = L10n.IconContent("animationvisibilitytoggleoff", null, null);
public Action NodeWasToggled;
public AudioMixerController m_Controller = null;
@@ -300,8 +299,8 @@ internal class AudioMixerGroupTreeView
class Styles
{
- public GUIContent header = EditorGUIUtility.TrTextContent("Groups", "An Audio Mixer Group is used by e.g Audio Sources to modify the audio output before it reaches the Audio Listener. An Audio Mixer Group will route its output to another Audio Mixer Group if it is made a child of that group. The Master Group will route its output to the Audio Listener if it doesn't route its output into another Mixer.");
- public GUIContent addButton = EditorGUIUtility.TrIconContent("CreateAddNew", "Add child group");
+ public GUIContent header = L10n.TextContent("Groups", "An Audio Mixer Group is used by e.g Audio Sources to modify the audio output before it reaches the Audio Listener. An Audio Mixer Group will route its output to another Audio Mixer Group if it is made a child of that group. The Master Group will route its output to the Audio Listener if it doesn't route its output into another Mixer.", null, null);
+ public GUIContent addButton = L10n.IconContent("CreateAddNew", "Add child group", null);
public Texture2D audioMixerGroupIcon = EditorGUIUtility.FindTexture(typeof(UnityEngine.Audio.AudioMixerGroup));
}
@@ -476,12 +475,12 @@ public void OnTreeViewContextClick(EntityId index)
if (!EditorApplication.isPlaying)
{
- pm.AddItem(EditorGUIUtility.TrTextContent("Add child group"), false, AddChildGroupPopupCallback, new AudioMixerGroupPopupContext(m_Controller, mixerNode.group));
+ pm.AddItem(L10n.TextContent("Add child group", null, null, null), false, AddChildGroupPopupCallback, new AudioMixerGroupPopupContext(m_Controller, mixerNode.group));
if (mixerNode.group != m_Controller.masterGroup)
{
- pm.AddItem(EditorGUIUtility.TrTextContent("Add sibling group"), false, AddSiblingGroupPopupCallback, new AudioMixerGroupPopupContext(m_Controller, mixerNode.group));
+ pm.AddItem(L10n.TextContent("Add sibling group", null, null, null), false, AddSiblingGroupPopupCallback, new AudioMixerGroupPopupContext(m_Controller, mixerNode.group));
pm.AddSeparator("");
- pm.AddItem(EditorGUIUtility.TrTextContent("Rename"), false, RenameGroupCallback, node);
+ pm.AddItem(L10n.TextContent("Rename", null, null, null), false, RenameGroupCallback, node);
// Mastergroup cannot be deleted nor duplicated
var selection = GetGroupSelectionWithoutMasterGroup().ToArray();
@@ -491,7 +490,7 @@ public void OnTreeViewContextClick(EntityId index)
}
else
{
- pm.AddDisabledItem(EditorGUIUtility.TrTextContent("Modifying group topology in play mode is not allowed"));
+ pm.AddDisabledItem(L10n.TextContent("Modifying group topology in play mode is not allowed", null, null, null));
}
pm.ShowAsContext();
@@ -705,4 +704,3 @@ public void OnUndoRedoPerformed(in UndoRedoInfo info)
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/Audio/Mixer/GUI/AudioMixerGroupViewList.cs b/Editor/Mono/Audio/Mixer/GUI/AudioMixerGroupViewList.cs
index 1f8d11b2b6..6abb9e0642 100644
--- a/Editor/Mono/Audio/Mixer/GUI/AudioMixerGroupViewList.cs
+++ b/Editor/Mono/Audio/Mixer/GUI/AudioMixerGroupViewList.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: AudioAuthoring not yet converted
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
@@ -23,8 +22,8 @@ internal class AudioMixerGroupViewList
class Styles
{
- public GUIContent header = EditorGUIUtility.TrTextContent("Views", "A view is the saved visibility state of the current Mixer Groups. Use views to setup often used combinations of Mixer Groups.");
- public GUIContent addButton = EditorGUIUtility.TrIconContent("CreateAddNew");
+ public GUIContent header = L10n.TextContent("Views", "A view is the saved visibility state of the current Mixer Groups. Use views to setup often used combinations of Mixer Groups.", null, null);
+ public GUIContent addButton = L10n.IconContent("CreateAddNew", null, null);
public Texture2D viewsIcon = EditorGUIUtility.FindTexture("AudioMixerView Icon");
}
[NoAutoStaticsCleanup] // Lazy GUIContent/Texture2D style cache rebuilt on demand via null check; assets survive reload, safe to persist.
@@ -223,9 +222,9 @@ static public void Show(Rect buttonRect, int viewIndex, AudioMixerGroupViewList
{
var menu = new GenericMenu();
data input = new data() { viewIndex = viewIndex, list = list };
- menu.AddItem(EditorGUIUtility.TrTextContent("Rename"), false, Rename, input);
- menu.AddItem(EditorGUIUtility.TrTextContent("Duplicate"), false, Duplicate, input);
- menu.AddItem(EditorGUIUtility.TrTextContent("Delete"), false, Delete, input);
+ menu.AddItem(L10n.TextContent("Rename", null, null, null), false, Rename, input);
+ menu.AddItem(L10n.TextContent("Duplicate", null, null, null), false, Duplicate, input);
+ menu.AddItem(L10n.TextContent("Delete", null, null, null), false, Delete, input);
menu.DropDown(buttonRect);
}
@@ -251,4 +250,3 @@ static void Delete(object userData)
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/Audio/Mixer/GUI/AudioMixerSnapshotView.cs b/Editor/Mono/Audio/Mixer/GUI/AudioMixerSnapshotView.cs
index 1a4d3aaf03..a278dfa3fc 100644
--- a/Editor/Mono/Audio/Mixer/GUI/AudioMixerSnapshotView.cs
+++ b/Editor/Mono/Audio/Mixer/GUI/AudioMixerSnapshotView.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: AudioAuthoring not yet converted
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
@@ -23,9 +22,9 @@ internal class AudioMixerSnapshotListView
class Styles
{
- public GUIContent starIcon = EditorGUIUtility.TrIconContent("Favorite", "Start snapshot");
- public GUIContent header = EditorGUIUtility.TrTextContent("Snapshots", "A snapshot is a set of values for all parameters in the mixer. When using the mixer, you modify parameters in the selected snapshot. Blend between multiple snapshots at runtime.");
- public GUIContent addButton = EditorGUIUtility.TrIconContent("CreateAddNew");
+ public GUIContent starIcon = L10n.IconContent("Favorite", "Start snapshot", null);
+ public GUIContent header = L10n.TextContent("Snapshots", "A snapshot is a set of values for all parameters in the mixer. When using the mixer, you modify parameters in the selected snapshot. Blend between multiple snapshots at runtime.", null, null);
+ public GUIContent addButton = L10n.IconContent("CreateAddNew", null, null);
public Texture2D snapshotsIcon = EditorGUIUtility.FindTexture(typeof(UnityEngine.Audio.AudioMixerSnapshot));
}
[NoAutoStaticsCleanup] // Lazy GUIContent/Texture2D style cache rebuilt on demand via null check; assets survive reload, safe to persist.
@@ -291,11 +290,11 @@ static public void Show(Rect buttonRect, AudioMixerSnapshotController snapshot,
{
var menu = new GenericMenu();
data input = new data() { snapshot = snapshot, list = list };
- menu.AddItem(EditorGUIUtility.TrTextContent("Set as start Snapshot"), false, SetAsStartupSnapshot, input);
+ menu.AddItem(L10n.TextContent("Set as start Snapshot", null, null, null), false, SetAsStartupSnapshot, input);
menu.AddSeparator("");
- menu.AddItem(EditorGUIUtility.TrTextContent("Rename"), false, Rename, input);
- menu.AddItem(EditorGUIUtility.TrTextContent("Duplicate"), false, Duplicate, input);
- menu.AddItem(EditorGUIUtility.TrTextContent("Delete"), false, Delete, input);
+ menu.AddItem(L10n.TextContent("Rename", null, null, null), false, Rename, input);
+ menu.AddItem(L10n.TextContent("Duplicate", null, null, null), false, Duplicate, input);
+ menu.AddItem(L10n.TextContent("Delete", null, null, null), false, Delete, input);
menu.DropDown(buttonRect);
}
@@ -331,4 +330,3 @@ public void OnUndoRedoPerformed(in UndoRedoInfo info)
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/Audio/Mixer/GUI/AudioMixerWindow.cs b/Editor/Mono/Audio/Mixer/GUI/AudioMixerWindow.cs
index 9972c87994..8228b71fba 100644
--- a/Editor/Mono/Audio/Mixer/GUI/AudioMixerWindow.cs
+++ b/Editor/Mono/Audio/Mixer/GUI/AudioMixerWindow.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: AudioAuthoring not yet converted
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
@@ -18,10 +17,6 @@ namespace UnityEditor
[EditorWindowTitle(title = "Audio Mixer", icon = "Audio Mixer")]
internal partial class AudioMixerWindow : EditorWindow, IHasCustomMenu
{
- #pragma warning disable UAL0015 // this side effect does not outlive the current call (global trigger / lazily-loaded asset re-fetched on next access); a stale reference is harmlessly replaced
- public AudioMixerWindow() { }
- #pragma warning restore UAL0015
-
[AutoStaticsCleanupOnCodeReload]
static AudioMixerWindow s_Instance;
@@ -120,7 +115,7 @@ class GUIContents
public GUIContents()
{
#pragma warning disable UAL0015 // lazily-loaded icon re-fetched on next access; a stale reference is harmlessly replaced
- editSnapShots = EditorGUIUtility.TrTextContent("Edit in Play Mode", "Edit in playmode and your changes are automatically saved. Note when editing is disabled, live values are shown.", EditorGUIUtility.TrIconContent("Animation.Record", "Are scene and inspector changes recorded into the animation curves?").image);
+ editSnapShots = EditorGUIUtility.TrTextContent("Edit in Play Mode", "Edit in playmode and your changes are automatically saved. Note when editing is disabled, live values are shown.", L10n.IconContent("Animation.Record", "Are scene and inspector changes recorded into the animation curves?", null).image);
#pragma warning restore UAL0015
}
}
@@ -729,31 +724,31 @@ public MixerParameterDefinition ParamDef(string name, string desc, string units,
// Add items to the context menu for the AudioMixerWindow (the tree horizontal lines, upper right corner)
public virtual void AddItemsToMenu(GenericMenu menu)
{
- menu.AddItem(EditorGUIUtility.TrTextContent("Sort groups alphabetically"), m_SortGroupsAlphabetically, delegate { m_SortGroupsAlphabetically = !m_SortGroupsAlphabetically; });
- menu.AddItem(EditorGUIUtility.TrTextContent("Show referenced groups"), m_ShowReferencedBuses, delegate { m_ShowReferencedBuses = !m_ShowReferencedBuses; });
- menu.AddItem(EditorGUIUtility.TrTextContent("Show group connections"), m_ShowBusConnections, delegate { m_ShowBusConnections = !m_ShowBusConnections; });
+ menu.AddItem(L10n.TextContent("Sort groups alphabetically", null, null, null), m_SortGroupsAlphabetically, delegate { m_SortGroupsAlphabetically = !m_SortGroupsAlphabetically; });
+ menu.AddItem(L10n.TextContent("Show referenced groups", null, null, null), m_ShowReferencedBuses, delegate { m_ShowReferencedBuses = !m_ShowReferencedBuses; });
+ menu.AddItem(L10n.TextContent("Show group connections", null, null, null), m_ShowBusConnections, delegate { m_ShowBusConnections = !m_ShowBusConnections; });
if (m_ShowBusConnections)
- menu.AddItem(EditorGUIUtility.TrTextContent("Only highlight selected group connections"), m_ShowBusConnectionsOfSelection, delegate { m_ShowBusConnectionsOfSelection = !m_ShowBusConnectionsOfSelection; });
+ menu.AddItem(L10n.TextContent("Only highlight selected group connections", null, null, null), m_ShowBusConnectionsOfSelection, delegate { m_ShowBusConnectionsOfSelection = !m_ShowBusConnectionsOfSelection; });
menu.AddSeparator("");
- menu.AddItem(EditorGUIUtility.TrTextContent("Vertical layout"), layoutMode == LayoutMode.Vertical, delegate { layoutMode = LayoutMode.Vertical; });
- menu.AddItem(EditorGUIUtility.TrTextContent("Horizontal layout"), layoutMode == LayoutMode.Horizontal, delegate { layoutMode = LayoutMode.Horizontal; });
+ menu.AddItem(L10n.TextContent("Vertical layout", null, null, null), layoutMode == LayoutMode.Vertical, delegate { layoutMode = LayoutMode.Vertical; });
+ menu.AddItem(L10n.TextContent("Horizontal layout", null, null, null), layoutMode == LayoutMode.Horizontal, delegate { layoutMode = LayoutMode.Horizontal; });
menu.AddSeparator("");
- menu.AddItem(EditorGUIUtility.TrTextContent("Use RMS metering for display"), EditorPrefs.GetBool(kAudioMixerUseRMSMetering, true), delegate { EditorPrefs.SetBool(kAudioMixerUseRMSMetering, true); });
- menu.AddItem(EditorGUIUtility.TrTextContent("Use peak metering for display"), !EditorPrefs.GetBool(kAudioMixerUseRMSMetering, true), delegate { EditorPrefs.SetBool(kAudioMixerUseRMSMetering, false); });
+ menu.AddItem(L10n.TextContent("Use RMS metering for display", null, null, null), EditorPrefs.GetBool(kAudioMixerUseRMSMetering, true), delegate { EditorPrefs.SetBool(kAudioMixerUseRMSMetering, true); });
+ menu.AddItem(L10n.TextContent("Use peak metering for display", null, null, null), !EditorPrefs.GetBool(kAudioMixerUseRMSMetering, true), delegate { EditorPrefs.SetBool(kAudioMixerUseRMSMetering, false); });
if (Unsupported.IsDeveloperMode())
{
menu.AddSeparator("");
- menu.AddItem(EditorGUIUtility.TrTextContent("DEVELOPER/Groups Rendered Above"), m_GroupsRenderedAboveSections, delegate { m_GroupsRenderedAboveSections = !m_GroupsRenderedAboveSections; });
- menu.AddItem(EditorGUIUtility.TrTextContent("DEVELOPER/Build 10 groups"), false, delegate { m_Controller.BuildTestSetup(0, 7, 10); });
- menu.AddItem(EditorGUIUtility.TrTextContent("DEVELOPER/Build 20 groups"), false, delegate { m_Controller.BuildTestSetup(0, 7, 20); });
- menu.AddItem(EditorGUIUtility.TrTextContent("DEVELOPER/Build 40 groups"), false, delegate { m_Controller.BuildTestSetup(0, 7, 40); });
- menu.AddItem(EditorGUIUtility.TrTextContent("DEVELOPER/Build 80 groups"), false, delegate { m_Controller.BuildTestSetup(0, 7, 80); });
- menu.AddItem(EditorGUIUtility.TrTextContent("DEVELOPER/Build 160 groups"), false, delegate { m_Controller.BuildTestSetup(0, 7, 160); });
- menu.AddItem(EditorGUIUtility.TrTextContent("DEVELOPER/Build chain of 10 groups"), false, delegate { m_Controller.BuildTestSetup(1, 1, 10); });
- menu.AddItem(EditorGUIUtility.TrTextContent("DEVELOPER/Build chain of 20 groups "), false, delegate { m_Controller.BuildTestSetup(1, 1, 20); });
- menu.AddItem(EditorGUIUtility.TrTextContent("DEVELOPER/Build chain of 40 groups"), false, delegate { m_Controller.BuildTestSetup(1, 1, 40); });
- menu.AddItem(EditorGUIUtility.TrTextContent("DEVELOPER/Build chain of 80 groups"), false, delegate { m_Controller.BuildTestSetup(1, 1, 80); });
- menu.AddItem(EditorGUIUtility.TrTextContent("DEVELOPER/Show overlays"), m_ShowDeveloperOverlays, delegate { m_ShowDeveloperOverlays = !m_ShowDeveloperOverlays; });
+ menu.AddItem(L10n.TextContent("DEVELOPER/Groups Rendered Above", null, null, null), m_GroupsRenderedAboveSections, delegate { m_GroupsRenderedAboveSections = !m_GroupsRenderedAboveSections; });
+ menu.AddItem(L10n.TextContent("DEVELOPER/Build 10 groups", null, null, null), false, delegate { m_Controller.BuildTestSetup(0, 7, 10); });
+ menu.AddItem(L10n.TextContent("DEVELOPER/Build 20 groups", null, null, null), false, delegate { m_Controller.BuildTestSetup(0, 7, 20); });
+ menu.AddItem(L10n.TextContent("DEVELOPER/Build 40 groups", null, null, null), false, delegate { m_Controller.BuildTestSetup(0, 7, 40); });
+ menu.AddItem(L10n.TextContent("DEVELOPER/Build 80 groups", null, null, null), false, delegate { m_Controller.BuildTestSetup(0, 7, 80); });
+ menu.AddItem(L10n.TextContent("DEVELOPER/Build 160 groups", null, null, null), false, delegate { m_Controller.BuildTestSetup(0, 7, 160); });
+ menu.AddItem(L10n.TextContent("DEVELOPER/Build chain of 10 groups", null, null, null), false, delegate { m_Controller.BuildTestSetup(1, 1, 10); });
+ menu.AddItem(L10n.TextContent("DEVELOPER/Build chain of 20 groups ", null, null, null), false, delegate { m_Controller.BuildTestSetup(1, 1, 20); });
+ menu.AddItem(L10n.TextContent("DEVELOPER/Build chain of 40 groups", null, null, null), false, delegate { m_Controller.BuildTestSetup(1, 1, 40); });
+ menu.AddItem(L10n.TextContent("DEVELOPER/Build chain of 80 groups", null, null, null), false, delegate { m_Controller.BuildTestSetup(1, 1, 80); });
+ menu.AddItem(L10n.TextContent("DEVELOPER/Show overlays", null, null, null), m_ShowDeveloperOverlays, delegate { m_ShowDeveloperOverlays = !m_ShowDeveloperOverlays; });
}
}
}
@@ -779,7 +774,7 @@ static public void Show(Rect buttonRect, string[] classNames, EntityId initialSe
}
else
{
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("No Audio Mixers found in this project"));
+ menu.AddDisabledItem(L10n.TextContent("No Audio Mixers found in this project", null, null, null));
}
menu.DropDown(buttonRect);
}
@@ -792,4 +787,3 @@ static void SelectCallback(object userData)
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/Audio/Mixer/GUI/AudioMixersTreeView.cs b/Editor/Mono/Audio/Mixer/GUI/AudioMixersTreeView.cs
index 4c5c54e68f..22c8411ff5 100644
--- a/Editor/Mono/Audio/Mixer/GUI/AudioMixersTreeView.cs
+++ b/Editor/Mono/Audio/Mixer/GUI/AudioMixersTreeView.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: AudioAuthoring not yet converted
using System.IO;
using UnityEditor.ProjectWindowCallback;
using UnityEngine;
@@ -536,8 +535,8 @@ internal class AudioMixersTreeView
class Styles
{
- public GUIContent header = EditorGUIUtility.TrTextContent("Mixers", "All mixers in the project are shown here. By default, a mixer outputs to the AudioListener but mixers can also route their output to other mixers. Each mixer shows where it outputs (in parenthesis). To reroute a mixer simply drag the mixer upon another mixer and select a group from the popup.");
- public GUIContent addButton = EditorGUIUtility.TrIconContent("CreateAddNew", "Add mixer asset. The asset will be saved in the same folder as the current selected mixer or, if none is selected, saved in the Assets folder.");
+ public GUIContent header = L10n.TextContent("Mixers", "All mixers in the project are shown here. By default, a mixer outputs to the AudioListener but mixers can also route their output to other mixers. Each mixer shows where it outputs (in parenthesis). To reroute a mixer simply drag the mixer upon another mixer and select a group from the popup.", null, null);
+ public GUIContent addButton = L10n.IconContent("CreateAddNew", "Add mixer asset. The asset will be saved in the same folder as the current selected mixer or, if none is selected, saved in the Assets folder.", null);
public Texture2D audioMixerIcon = EditorGUIUtility.FindTexture(typeof(AudioMixerController));
}
[NoAutoStaticsCleanup] // Lazy GUIContent/Texture2D holder loaded by fixed name/type, re-initialized on first access via null-check; safe to persist across reload.
@@ -584,9 +583,9 @@ public void OnTreeViewContextClick(EntityId index)
if (node != null)
{
GenericMenu pm = new GenericMenu();
- pm.AddItem(EditorGUIUtility.TrTextContent("Rename"), false, RenameAudioMixerCallback, node);
+ pm.AddItem(L10n.TextContent("Rename", null, null, null), false, RenameAudioMixerCallback, node);
pm.AddSeparator("");
- pm.AddItem(EditorGUIUtility.TrTextContent("Delete AudioMixer"), false, DeleteAudioMixerCallback, node.mixer);
+ pm.AddItem(L10n.TextContent("Delete AudioMixer", null, null, null), false, DeleteAudioMixerCallback, node.mixer);
pm.ShowAsContext();
}
}
@@ -703,7 +702,7 @@ void OnMixersDroppedOnMixerCallback(AudioMixerController[] draggedMixers, AudioM
Object startSelection = draggedMixers.Length == 1 ? draggedMixers[0].outputAudioMixerGroup : null;
ObjectSelector.get.Show(startSelection, typeof(AudioMixerGroup), null, false, new List() { droppedUponMixer.GetEntityId() });
ObjectSelector.get.objectSelectorID = kObjectSelectorID;
- ObjectSelector.get.titleContent = EditorGUIUtility.TrTextContent("Select Output Audio Mixer Group");
+ ObjectSelector.get.titleContent = L10n.TextContent("Select Output Audio Mixer Group", null, null, null);
GUIUtility.ExitGUI();
}
}
@@ -748,4 +747,3 @@ void HandleObjectSelectorResult()
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/Audio/StreamedAudioClipPreview.cs b/Editor/Mono/Audio/StreamedAudioClipPreview.cs
index d7f6e8dedd..d776864ca2 100644
--- a/Editor/Mono/Audio/StreamedAudioClipPreview.cs
+++ b/Editor/Mono/Audio/StreamedAudioClipPreview.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: AudioAuthoring not yet converted
using System;
using System.Collections.Generic;
using System.Reflection;
@@ -66,9 +65,9 @@ public ClipPreviewDetails(AudioClip clip, bool isLooping, int size, double local
this.clip = clip;
- #pragma warning disable UAL0018 // rebuilt/resubscribed wholesale on the next reload via this object's own lifecycle; a stale value in the interim is never observed
+#pragma warning disable UAL0018 // ClipPreviewDetails is a per-call descriptor built and consumed inside a single draw; the overview array does not outlive it, and the next draw re-fetches (recomputing it if the cache was dropped)
preview = AudioClipMinMaxOverview.GetOverviewFor(clip);
- #pragma warning restore UAL0018
+#pragma warning restore UAL0018
if (preview == null)
throw new ArgumentException("Clip " + clip + "'s overview preview is null");
@@ -470,4 +469,3 @@ bool OnNewWaveformData(WaveformStreamer streamer, float[] data, int remaining)
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/Audio/UIElements/AudioContainerElementClipField.cs b/Editor/Mono/Audio/UIElements/AudioContainerElementClipField.cs
index e085cdd8a5..30b15608c0 100644
--- a/Editor/Mono/Audio/UIElements/AudioContainerElementClipField.cs
+++ b/Editor/Mono/Audio/UIElements/AudioContainerElementClipField.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: AudioAuthoring not yet converted
using System;
using Unity.Scripting.LifecycleManagement;
using UnityEditor.UIElements;
@@ -115,4 +114,3 @@ static void GenerateVisualContent(MeshGenerationContext context)
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/Audio/UIElements/OnAudioFilterReadLevelMeter.cs b/Editor/Mono/Audio/UIElements/OnAudioFilterReadLevelMeter.cs
index 8184a9fda6..121fe6ec90 100644
--- a/Editor/Mono/Audio/UIElements/OnAudioFilterReadLevelMeter.cs
+++ b/Editor/Mono/Audio/UIElements/OnAudioFilterReadLevelMeter.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: AudioAuthoring not yet converted
using UnityEngine;
using UnityEngine.UIElements;
@@ -24,4 +23,3 @@ public OnAudioFilterReadLevelMeter(MonoBehaviour behaviour)
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/BuildPipeline/Android/IAndroidGradleDependencyProvider.cs b/Editor/Mono/BuildPipeline/Android/IAndroidGradleDependencyProvider.cs
new file mode 100644
index 0000000000..b178f21540
--- /dev/null
+++ b/Editor/Mono/BuildPipeline/Android/IAndroidGradleDependencyProvider.cs
@@ -0,0 +1,31 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using UnityEditor.Build;
+using UnityEngine.Bindings;
+
+namespace UnityEditor.Android
+{
+ // Both are called on the main thread before the Gradle project is generated.
+ [VisibleToOtherModules]
+ internal interface IAndroidGradleDependencyProvider : IOrderedCallback
+ {
+ // group:artifact:version
+ string[] OnCollectGradleDependencies(AndroidGradleDependencyProviderContext context);
+
+ // Maven repository URLs the dependencies resolve from.
+ string[] OnCollectMavenRepositories(AndroidGradleDependencyProviderContext context);
+ }
+
+ [VisibleToOtherModules]
+ internal struct AndroidGradleDependencyProviderContext
+ {
+ public BuildTarget platform { get; }
+
+ public AndroidGradleDependencyProviderContext(BuildTarget platform)
+ {
+ this.platform = platform;
+ }
+ }
+}
diff --git a/Editor/Mono/BuildPipeline/AssemblyStripper.cs b/Editor/Mono/BuildPipeline/AssemblyStripper.cs
index 47a9342b25..89026171c0 100644
--- a/Editor/Mono/BuildPipeline/AssemblyStripper.cs
+++ b/Editor/Mono/BuildPipeline/AssemblyStripper.cs
@@ -279,7 +279,7 @@ public static string WriteEditorData(BuildPostProcessArgs args, NPath linkerInpu
typesInScenes = typesInScenes,
allNativeTypes = CollectNativeTypeData().ToArray(),
forceIncludeModules = forceIncludeModules.ToArray(),
- forceExcludeModules = forceExcludeModules.ToArray()
+ forceExcludeModules = forceExcludeModules.ToArray(),
};
var path = linkerInputDirectory.Combine("EditorToUnityLinkerData.json");
@@ -287,6 +287,26 @@ public static string WriteEditorData(BuildPostProcessArgs args, NPath linkerInpu
return path.MakeAbsolute().ToString();
}
+ ///
+ /// Writes the ManagedCapture config (from ) to
+ /// its own file and returns the absolute path, or null when no module supplies a config.
+ /// Passing the config in a dedicated file (via LinkRequest.ManagedCaptureConfigFile) rather
+ /// than embedding it in the editor-to-linker data isolates it: a bad or oversized capture
+ /// config can't corrupt the shared editor data the rest of engine stripping depends on.
+ /// The payload is base64 so the intermediate artifact is not casually readable or editable;
+ /// the linker accepts either form.
+ ///
+ public static string WriteManagedCaptureConfig(NPath linkerInputDirectory)
+ {
+ var config = onProvideManagedCaptureConfig?.Invoke();
+ if (string.IsNullOrEmpty(config))
+ return null;
+
+ var path = linkerInputDirectory.Combine("ManagedCapture.dat");
+ File.WriteAllText(path.ToString(), Convert.ToBase64String(Encoding.UTF8.GetBytes(config)));
+ return path.MakeAbsolute().ToString();
+ }
+
static List GetTypesInScenesInformation(BuildReport report, RuntimeClassRegistry rcr)
{
var items = new List();
@@ -373,6 +393,14 @@ public bool IsModuleIncluded(string moduleName)
[AutoStaticsCleanupOnCodeReload]
public static event Action onCollectIncludedModules;
+ ///
+ /// Event for modules to provide ManagedCapture configuration JSON.
+ /// Invoked during WriteManagedCaptureConfig to write the config to its own linker input
+ /// file, passed to the linker via LinkRequest.ManagedCaptureConfigFile.
+ ///
+ [AutoStaticsCleanupOnCodeReload]
+ public static event Func onProvideManagedCaptureConfig;
+
static void CollectIncludedAndExcludedModules(out List forceInclude, out List forceExclude)
{
forceInclude = new List();
diff --git a/Editor/Mono/BuildPipeline/BuildCallbackContext.bindings.cs b/Editor/Mono/BuildPipeline/BuildCallbackContext.bindings.cs
index c7f3b25fe2..15553fd161 100644
--- a/Editor/Mono/BuildPipeline/BuildCallbackContext.bindings.cs
+++ b/Editor/Mono/BuildPipeline/BuildCallbackContext.bindings.cs
@@ -69,7 +69,7 @@ public bool IsPlayerBuild
[FreeFunction("BuildCallbackContextBindings::IsContentOnlyBuild")]
private static extern bool IsContentOnlyBuildInternal(IntPtr self);
- ///Returns true if the build is a content only build type like an AssetBundle build.
+ ///Returns true if the build is a content directory or AssetBundle build.
public bool IsContentOnlyBuild
{
get
diff --git a/Editor/Mono/BuildPipeline/BuildPipeline.bindings.cs b/Editor/Mono/BuildPipeline/BuildPipeline.bindings.cs
index 429dce798c..2d7a6826c2 100644
--- a/Editor/Mono/BuildPipeline/BuildPipeline.bindings.cs
+++ b/Editor/Mono/BuildPipeline/BuildPipeline.bindings.cs
@@ -46,8 +46,8 @@ public enum PlayerConnectionInitiateMode
}
// Lets you programmatically build players or AssetBundles which can be loaded from the web.
- ///API for building players or AssetBundles.
- ///The BuildPipeline class in the Unity Editor namespace provides essential tools to programmatically Build Players and Build AssetBundles.
+ ///API for building Players or content builds.
+ ///The BuildPipeline class in the Unity Editor namespace provides tools to build Players and create content directory or AssetBundle builds.
///AssetBundles can be loaded from external sources such as the web, enhancing the flexibility and scalability of content delivery in Unity applications.
///The class contains several static properties and methods to facilitate building workflows.
///AssetBundles
@@ -183,7 +183,7 @@ public static BuildReport BuildPlayer(string[] levels, string locationPathName,
///
///Calling this method will invalidate any variables in the editor script that reference GameObjects, so they will need to be reacquired after the call.
///
- ///Scripts can run at strategic points during the build by implementing one of the supported callback interfaces, for example , , and .
+ ///Scripts can run at strategic points during the build by implementing one of the supported callback interfaces, for example , and , or by overriding .
///
///Note: Be aware that changes to [scripting symbols](xref:um-platform-dependent-compilation) only take effect at the next domain reload, when scripts are recompiled.
///
@@ -372,9 +372,9 @@ internal static bool ValidateScenePaths(string[] scenes, out string errorMessage
internal static extern bool IsFeatureSupported(string define, BuildTarget platform);
// Is a player currently building?
- ///Returns true when Unity is actively building a Player or AssetBundles
- ///This returns true during Player builds () and AssetBundle builds ().
- ///It can be used to check the context inside script code that could be triggered during a build, for example when is being used on a .
+ ///Checks whether Unity is actively building a Player or a content build.
+ ///This returns true during Player builds (), content directory builds (), and AssetBundle builds ().
+ ///Use it to check the context inside script code that might be triggered during a build, for example when is used on a .
public static extern bool isBuildingPlayer { [FreeFunction("IsBuildingPlayer")] get; }
@@ -494,11 +494,17 @@ public static BuildReport BuildContentDirectory(BuildContentDirectoryParameters
buildParameters.metadataPath = BuildHistory.ReserveBuildReportDirectory(
buildSessionGuid.ToString(), buildParameters.buildStartTimeTicks);
- var report = BuildContentDirectoryInternal(buildParameters, buildSessionGuid);
-
- BuildHistory.FinalizeBuild(report);
-
- return report;
+ try
+ {
+ var report = BuildContentDirectoryInternal(buildParameters, buildSessionGuid);
+ BuildHistory.FinalizeBuild(report);
+ return report;
+ }
+ finally
+ {
+ // BuildHistory.ReserveBuildReportDirectory registered this as the running build.
+ BuildHistory.EndActiveBuild(buildSessionGuid);
+ }
}
[NativeMethod(ThrowsException = true)]
@@ -585,7 +591,7 @@ public static AssetBundleManifest BuildAssetBundles(string outputPath, AssetBund
/// This signature of BuildAssetBundles is recommended and exposes the most functionality. The other signatures, documented below,
///are retained for backward compatibility and convenience.
///
- ///During the build process, classes that implement are called as scenes are processed. Similarly the way shaders are built can be influenced by implementing or .
+ ///During the build process, classes derived from that define are called as scenes are processed. Similarly the way shaders are built can be influenced by implementing or .
///
///This method returns an instance that describes the AssetBundles produced.
///
diff --git a/Editor/Mono/BuildPipeline/BuildPipelineInterfaces.cs b/Editor/Mono/BuildPipeline/BuildPipelineInterfaces.cs
index 65178a3298..4d9cf47d5f 100644
--- a/Editor/Mono/BuildPipeline/BuildPipelineInterfaces.cs
+++ b/Editor/Mono/BuildPipeline/BuildPipelineInterfaces.cs
@@ -543,6 +543,8 @@ public interface IPreprocessShaders : IOrderedCallback
///
///Unity invokes the `OnProcessShader` callback in Player, AssetBundle, and content directory builds. If there are any shaders already in the cache, then this method isn't invoked for those shaders. To ensure the callback runs for all shaders, perform a [clean build](xref:um-build-clean-build). To run it for a specific shader, modify that shader or one of its dependent assets.
///
+ ///In content directory builds, Unity also invokes the callback again for cached shaders when the version of its changes. Increment the version whenever you change the callback's code.
+ ///
///To help you identify keywords and variants to strip, you can [check what shader variants you have in your project](xref:um-shader-how-many-variants). For example if you [declare a keyword](xref:um-sl-multiple-program-variants) called `DEBUG` in your shader code using `#pragma multi_compile _ DEBUG`, the following [Editor script](xref:um-special-folders) finds and strips shader variants that use the keyword.
///
///The script does the following when you build your application:
@@ -624,6 +626,8 @@ public interface IPreprocessComputeShaders : IOrderedCallback
///
///This callback is invoked for Player, AssetBundle, and content directory builds.
///
+ ///In content directory builds, Unity invokes the callback again for cached compute shaders when the version of its changes. Increment the version whenever you change the callback's code.
+ ///
///For more information about build callbacks, refer to [Use build callbacks](xref:um-build-callbacks)
///The compute shader that Unity is about to compile.
///The name of the kernel that Unity is about to compile.
@@ -1213,6 +1217,17 @@ internal static void OnSceneProcess_HashVersion(out Hash128 hashVersion)
{
foreach (IProcessSceneWithReport processor in processors.sceneProcessorsWithReport)
{
+ if (processor is AttributeCallbackWrapper wrapper)
+ {
+ MethodInfo method = wrapper.m_method;
+ hashVersion.Append(method.DeclaringType.AssemblyQualifiedName);
+ hashVersion.Append(method.Name);
+
+ var postProcessSceneAttribute = (Callbacks.PostProcessSceneAttribute)Attribute.GetCustomAttribute(method, typeof(Callbacks.PostProcessSceneAttribute));
+ hashVersion.Append(postProcessSceneAttribute.GetVersion());
+ continue;
+ }
+
Type processorType = processor.GetType();
hashVersion.Append(processorType.AssemblyQualifiedName);
@@ -1222,6 +1237,49 @@ internal static void OnSceneProcess_HashVersion(out Hash128 hashVersion)
}
}
+ [RequiredByNativeCode]
+ internal static void OnShaderProcess_HashVersion(out Hash128 hashVersion)
+ {
+ hashVersion = ComputeCallbackVersionHash(GetConcreteCallbackTypes(TypeCache.GetTypesDerivedFrom()));
+ }
+
+ [RequiredByNativeCode]
+ internal static void OnComputeShaderProcess_HashVersion(out Hash128 hashVersion)
+ {
+ hashVersion = ComputeCallbackVersionHash(GetConcreteCallbackTypes(TypeCache.GetTypesDerivedFrom()));
+ }
+
+ static List GetConcreteCallbackTypes(TypeCache.TypeCollection types)
+ {
+ var callbackTypes = new List();
+ foreach (var type in types)
+ {
+ if (!type.IsAbstract && !type.IsInterface)
+ callbackTypes.Add(type);
+ }
+ return callbackTypes;
+ }
+
+ internal static Hash128 ComputeCallbackVersionHash(List callbackTypes)
+ {
+ var sortedTypes = new List(callbackTypes);
+ sortedTypes.Sort((a, b) => string.CompareOrdinal(a.AssemblyQualifiedName, b.AssemblyQualifiedName));
+
+ var hashVersion = new Hash128();
+ foreach (var type in sortedTypes)
+ {
+ hashVersion.Append(type.AssemblyQualifiedName);
+ hashVersion.Append(GetCallbackVersion(type));
+ }
+ return hashVersion;
+ }
+
+ internal static uint GetCallbackVersion(Type callbackType)
+ {
+ var attribute = Attribute.GetCustomAttribute(callbackType, typeof(BuildCallbackVersionAttribute), false) as BuildCallbackVersionAttribute;
+ return attribute != null ? attribute.Version : 1;
+ }
+
[RequiredByNativeCode]
internal static void OnBuildPostProcess(BuildReport report)
{
diff --git a/Editor/Mono/BuildPipeline/BuildPlatform.cs b/Editor/Mono/BuildPipeline/BuildPlatform.cs
index 89e69747a3..6a247b234c 100644
--- a/Editor/Mono/BuildPipeline/BuildPlatform.cs
+++ b/Editor/Mono/BuildPipeline/BuildPlatform.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: ContentBuild not yet converted
using UnityEngine;
using System;
using System.Collections.Generic;
@@ -227,6 +226,8 @@ internal partial class BuildPlatforms
// re-created on first access rather than eagerly, so it never runs before native
// BuildTargetDiscovery is ready during a build.
[AutoStaticsCleanupOnCodeReload]
+ // Lazy singleton created by the instance property on the next access after cleanup nulls it.
+ [IgnoreForUAL0015("Lazy singleton recreated by the instance property after cleanup nulls it")]
static BuildPlatforms s_Instance;
public static BuildPlatforms instance => s_Instance ??= new BuildPlatforms();
@@ -358,4 +359,3 @@ public List GetValidPlatforms()
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/BuildPipeline/BuildTarget.cs b/Editor/Mono/BuildPipeline/BuildTarget.cs
index 6a2f163a1c..b9381b63f7 100644
--- a/Editor/Mono/BuildPipeline/BuildTarget.cs
+++ b/Editor/Mono/BuildPipeline/BuildTarget.cs
@@ -10,7 +10,7 @@ namespace UnityEditor
// Target build platform.
// When adding new platform, read this first - https://confluence.hq.unity3d.com/display/DEV/Adding+new+platform
// When removing platform, read this first - https://confluence.hq.unity3d.com/display/DEV/Removing+platform
- ///Specifies the target platform for a Player or AssetBundle build.
+ ///Specifies the target platform for a Player or content build.
///Pass a platform property to BuildTarget to specify the target platform to build a Player for. For example, use to target the Android platform. At runtime, use BuildTarget to identify the currently selected build target. Only actively supported platforms are documented in this list.
///
///**Important**: When targeting Windows, it's recommended to use the StandaloneWindow64 target unless you specifically need to target devices that use a 32-bit CPU.
@@ -203,6 +203,7 @@ public enum BuildTarget
///
Switch2 = 48,
+ [System.Obsolete("Kepler has been removed in 6000.7")]
[ExcludeFromDocs]
Kepler = 49,
diff --git a/Editor/Mono/BuildPipeline/BuildTargetDiscovery.bindings.cs b/Editor/Mono/BuildPipeline/BuildTargetDiscovery.bindings.cs
index 1050e57b66..ec3c68b18a 100644
--- a/Editor/Mono/BuildPipeline/BuildTargetDiscovery.bindings.cs
+++ b/Editor/Mono/BuildPipeline/BuildTargetDiscovery.bindings.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: ContentBuild not yet converted
using System;
using System.IO;
using System.Collections.Generic;
@@ -96,6 +95,7 @@ public struct DiscoveredTargetInfo
static readonly string k_CreateIPlatformProviderFailedError = L10n.Tr("Failed to create IPlatformProvider instance for type '{0}'.", null);
static readonly string k_PlatformDeprecatedDefaultWithDisplayName = L10n.Tr("The {0} platform is deprecated.", null);
static readonly string k_DerivedPlatformUsesDeprecatedBase = L10n.Tr("This platform is based on {0}, which is deprecated.", null);
+ static readonly string k_PinnedPackageVersionNotInstalledWarning = L10n.Tr("Package '{0}' is installed at version {1}, but the platform requires version {2}. The installed version was kept.", null);
public static extern bool PlatformHasFlag(BuildTarget platform, TargetAttributes flag);
@@ -142,6 +142,11 @@ public static bool BuildTargetSupportsRenderer(BuildPlatform platform, GraphicsD
if (platform.namedBuildTarget == NamedBuildTarget.Standalone)
buildTarget = DesktopStandaloneBuildWindowExtension.GetBestStandaloneTarget(buildTarget);
+ return BuildTargetSupportsRenderer(buildTarget, type);
+ }
+
+ public static bool BuildTargetSupportsRenderer(BuildTarget buildTarget, GraphicsDeviceType type)
+ {
foreach (int var in GetRenderList(buildTarget))
{
if ((GraphicsDeviceType)var == type)
@@ -349,6 +354,35 @@ internal class PlatformPackageList
public PlatformPackageInfo[] recommendedPackages = Array.Empty();
}
+ ///
+ /// Identifies a package to install: a bare package name plus an optional pinned version.
+ /// The two are kept apart so every lookup and comparison can use
+ /// directly, and only combines them.
+ ///
+ [Serializable]
+ [VisibleToOtherModules("UnityEditor.BuildProfileModule")]
+ internal struct PlatformPackageIdentifier
+ {
+ public string name;
+
+ ///
+ /// Pinned package version, or empty when the registry should resolve the latest version.
+ ///
+ public string version;
+
+ public PlatformPackageIdentifier(string name, string version = "")
+ {
+ this.name = name;
+ this.version = version;
+ }
+
+ ///
+ /// Identifier for a Package Manager installation request.
+ ///
+ public string GetInstallIdentifier() =>
+ string.IsNullOrEmpty(version) ? name : $"{name}@{version}";
+ }
+
[Serializable]
[VisibleToOtherModules("UnityEditor.BuildProfileModule")]
internal class PlatformPackageInfo
@@ -371,16 +405,26 @@ internal class PlatformPackageInfo
///
public string deprecationMessage;
+ ///
+ /// Optional pinned package version. When empty the registry resolves the latest version,
+ /// which is the behaviour for every package that does not specify one.
+ ///
+ public string version;
+
public PlatformPackageInfo() {}
- public PlatformPackageInfo(string displayName, string qualifiedName, string description, string publisher = "", bool hasThumbnail = false)
+ public PlatformPackageInfo(string displayName, string qualifiedName, string description, string publisher = "", bool hasThumbnail = false, string version = "")
{
this.displayName = displayName;
this.qualifiedName = qualifiedName;
this.description = description;
this.publisher = publisher;
this.hasThumbnail = hasThumbnail;
+ this.version = version;
}
+
+ public PlatformPackageIdentifier GetPackageIdentifier() =>
+ new PlatformPackageIdentifier(qualifiedName, version);
}
[VisibleToOtherModules("UnityEditor.BuildProfileModule")]
@@ -764,6 +808,14 @@ public PlatformGroup() { }
new PlatformInfo
{
displayName = "Unity Render Service for Android™",
+ downloadLinkName = "Android",
+ description = L10n.Tr(
+ "Unity Render Service for Android™ lets you integrate Unity as a service in Android applications developed with Android Studio, " +
+ "so that Java and Kotlin applications can display Unity's 3D graphics within their own user interface. " +
+ "A single render service can drive multiple screens, or multiple windows per screen, which helps optimize CPU and memory " +
+ "consumption for use cases such as automotive dashboards. " +
+ "This platform offers pre-configured settings for building Unity as a render service."
+ , null),
buildTarget = BuildTarget.Android,
iconName = "BuildSettings.Android",
internalPackages = new PlatformPackageList
@@ -773,7 +825,7 @@ public PlatformGroup() { }
new PlatformPackageInfo(L10n.Tr("Unity Render Service for Android™ Support", null), "com.unity.android.render-service", L10n.Tr("Enables building the Unity Render Service for Android™. Contact your sales representative for access.", null))
}
},
- flags = PlatformAttributes.IsWindowsBuildTarget | PlatformAttributes.IsWindowsArm64BuildTarget | PlatformAttributes.IsLinuxBuildTarget | PlatformAttributes.IsMacBuildTarget | PlatformAttributes.IsDerivedBuildTarget | PlatformAttributes.IsHidden | PlatformAttributes.IsVisibleInPlatformBrowserOnly
+ flags = PlatformAttributes.IsWindowsBuildTarget | PlatformAttributes.IsWindowsArm64BuildTarget | PlatformAttributes.IsLinuxBuildTarget | PlatformAttributes.IsMacBuildTarget | PlatformAttributes.IsDerivedBuildTarget
}
},
{
@@ -848,22 +900,15 @@ public PlatformGroup() { }
flags = PlatformAttributes.IsWindowsBuildTarget | PlatformAttributes.IsWindowsArm64BuildTarget | PlatformAttributes.IsLinuxBuildTarget | PlatformAttributes.IsMacBuildTarget,
buildProfilePlatformBannerBgColorHex = "#FF443A"
}
- },
- {
- new("f8c7649c24f344129a97cf9854e2d582"),
- new PlatformInfo
- {
- displayName = "Kepler",
- buildTarget = BuildTarget.Kepler,
- iconName = "BuildSettings.EmbeddedLinux",
- flags = PlatformAttributes.IsMacBuildTarget | PlatformAttributes.IsNDAPlatform | PlatformAttributes.IsHidden
- }
}
};
[NoAutoStaticsCleanup] // GUID->bool install map, value types only, repopulated by Initialize; no user-code refs
static readonly Dictionary k_PlatformInstalledData = new();
- [AutoStaticsCleanupOnCodeReload]
+ // Filled only from the static constructor below, which runs once per domain and not again on a code
+ // reload, so emptying this map would make every reader behave as if no SDK platform is installed.
+ // Same static-constructor path as k_PlatformInstalledData above, so the two are kept consistent.
+ [NoAutoStaticsCleanup]
static readonly Dictionary k_SDKPlatformExtensions = new();
[NoAutoStaticsCleanup] // immutable static platform-group config (strings + GUID arrays), no user-code refs
@@ -929,8 +974,7 @@ public PlatformGroup() { }
new("f1d7bec2fd7f42f481c66ef512f47845"), // EmbeddedLinux
new("f188349a68c441ec9e3eb4c6f59abd41"), // LinuxHeadlessSimulation
new("99ef95e1e9b048fa9628d7eed27a8646"), // QNX
- // new("1e09bd9b55c8d45e9a11b4727bf18e88"), // Android Render Service will come here, but is hidden until entitlements are available.
- new("f8c7649c24f344129a97cf9854e2d582"), // Kepler
+ new("1e09bd9b55c8d45e9a11b4727bf18e88"), // Unity Render Service for Android™
}
},
new PlatformGroup
@@ -1650,32 +1694,71 @@ public static string[] GetAllPlatformPackageNames()
}
///
- /// Returns all required package names for a given platform. A required package
- /// is any package (internal or partner) that must be installed for a build to succeed.
+ /// Warns when a package pins a version but a different one is already installed. Installing
+ /// the pin would override a version the user may have chosen deliberately, so the installed
+ /// version is kept and the mismatch is only reported.
+ ///
+ /// Only plain version pins are comparable. The version slot may also carry a source such as
+ /// a "file:" path or a git URL, which has no version to compare against.
+ ///
+ [VisibleToOtherModules("UnityEditor.BuildProfileModule")]
+ internal static void WarnIfPinnedVersionNotInstalled(PlatformPackageIdentifier identifier)
+ {
+ var installedVersion = PackageManager.PackageInfo.FindForPackageName(identifier.name)?.version;
+ if (!ShouldWarnAboutPinnedVersion(identifier.version, installedVersion))
+ return;
+
+ Debug.LogWarning(string.Format(
+ k_PinnedPackageVersionNotInstalledWarning, identifier.name, installedVersion, identifier.version));
+ }
+
+ ///
+ /// True when a pinned version is comparable to what is installed and the two differ. Only a
+ /// plain version pin is comparable: the version slot may also carry a source such as a
+ /// "file:" path or a git URL, which has no version to compare against.
+ ///
+ [VisibleToOtherModules("UnityEditor.BuildProfileModule")]
+ internal static bool ShouldWarnAboutPinnedVersion(string pinnedVersion, string installedVersion)
+ {
+ if (string.IsNullOrEmpty(pinnedVersion) || !char.IsDigit(pinnedVersion[0]))
+ return false;
+
+ return !string.IsNullOrEmpty(installedVersion) && installedVersion != pinnedVersion;
+ }
+
+ ///
+ /// Returns identifiers for all required packages a given platform is still missing. A
+ /// required package is any package (internal or partner) that must be installed for a
+ /// build to succeed. The result feeds a Package Manager installation request, so each
+ /// identifier carries the package's pinned version when the catalog specifies one.
///
/// Platform identifier.
- /// Set of required package names.
- public static string[] GetAllMissingRequiredPlatformPackageNames(GUID platformId)
+ /// Set of required package identifiers.
+ public static PlatformPackageIdentifier[] GetAllMissingRequiredPlatformPackageIdentifiers(GUID platformId)
{
- var allPackageNames = new HashSet();
+ var seenPackageNames = new HashSet();
+ var result = new List();
var internalPackages = BuildTargetDiscovery.BuildPlatformInternalPackages(platformId);
var partnerPackages = BuildTargetDiscovery.BuildPlatformPartnerPackages(platformId);
- foreach (var package in internalPackages.requiredPackages)
- {
- if (!PackageManager.PackageInfo.IsPackageRegistered(package.qualifiedName))
- allPackageNames.Add(package.qualifiedName);
- }
+ AddMissingRequiredPackages(internalPackages);
+ AddMissingRequiredPackages(partnerPackages);
- foreach (var package in partnerPackages.requiredPackages)
+ return result.ToArray();
+
+ void AddMissingRequiredPackages(PlatformPackageList packageList)
{
- if (!PackageManager.PackageInfo.IsPackageRegistered(package.qualifiedName))
- allPackageNames.Add(package.qualifiedName);
- }
+ foreach (var package in packageList.requiredPackages)
+ {
+ if (!seenPackageNames.Add(package.qualifiedName))
+ continue;
- var result = new string[allPackageNames.Count];
- allPackageNames.CopyTo(result);
- return result;
+ if (PackageManager.PackageInfo.IsPackageRegistered(package.qualifiedName))
+ WarnIfPinnedVersionNotInstalled(package.GetPackageIdentifier());
+ else
+ result.Add(package.GetPackageIdentifier());
+ }
+ }
}
[System.Obsolete("BuildPlatformDescription(BuildTarget) is obsolete. Use BuildPlatformDescription(IBuildTarget) instead.", false)]
@@ -1891,4 +1974,3 @@ public static PlatformGroup[] GetPlatformGroups()
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/BuildPipeline/BuildTargetGroup.cs b/Editor/Mono/BuildPipeline/BuildTargetGroup.cs
index 3d1b2e067a..03c3b2444b 100644
--- a/Editor/Mono/BuildPipeline/BuildTargetGroup.cs
+++ b/Editor/Mono/BuildPipeline/BuildTargetGroup.cs
@@ -156,6 +156,7 @@ public enum BuildTargetGroup
///Nintendo Switch™ 2 target.
Switch2 = 37,
+ [Obsolete("Kepler has been removed in 6000.7")]
[ExcludeFromDocs]
Kepler = 38,
}
diff --git a/Editor/Mono/BuildPipeline/DataBuildDirtyTracker.cs b/Editor/Mono/BuildPipeline/DataBuildDirtyTracker.cs
index 9519e5cdd2..3c5c511a02 100644
--- a/Editor/Mono/BuildPipeline/DataBuildDirtyTracker.cs
+++ b/Editor/Mono/BuildPipeline/DataBuildDirtyTracker.cs
@@ -7,6 +7,7 @@
using System.Linq;
using NiceIO;
using Unity.Collections;
+using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEngine;
using UnityEngine.Scripting;
@@ -46,6 +47,7 @@ class BuildData
public string[] resourcePaths;
public BuildOptions buildOptions;
public string unityVersion;
+ public string sceneProcessorsHash;
// These options could impact the cache data files.
[NoAutoStaticsCleanup] // constant bitmask of BuildOptions enum flags (value type), never mutated; safe to persist
@@ -167,10 +169,25 @@ bool DoCheckDirty()
return true;
}
+ if (ComputeSceneProcessorsHash() != buildData.sceneProcessorsHash)
+ {
+ Console.WriteLine("Rebuilding Data files because scene processing callbacks or their versions have changed");
+ return true;
+ }
+
Console.WriteLine("Not rebuilding Data files -- no changes");
return false;
}
+ static string ComputeSceneProcessorsHash()
+ {
+#pragma warning disable CS0618 // Type or member is obsolete
+ BuildPipelineInterfaces.OnSceneProcess_HashVersion(out var hash);
+#pragma warning restore CS0618
+ hash.Append(AssetPostprocessingInternal.GetSinglePassBuildSceneProcessorsHashString());
+ return hash.ToString();
+ }
+
[RequiredByNativeCode]
static public void WriteBuildData(string buildDataPath, BuildReport report, string[] scenes, string[] prefabs, string activeBuildProfilePath, string[] assemblyNames)
{
@@ -207,6 +224,7 @@ static public void WriteBuildData(string buildDataPath, BuildReport report, stri
activeBuildProfile = new BuildDataInputFile(activeBuildProfilePath, developmentBuild),
buildOptions = report.summary.options & BuildData.BuildOptionsMask,
unityVersion = Application.unityVersion,
+ sceneProcessorsHash = ComputeSceneProcessorsHash(),
resourcePaths = resourcePaths,
#pragma warning disable UAC2001 // Avoid Linq
enabledModules = ModuleMetadata.GetModuleNames()
diff --git a/Editor/Mono/BuildPipeline/DesktopStandaloneBuildWindowExtension.cs b/Editor/Mono/BuildPipeline/DesktopStandaloneBuildWindowExtension.cs
index ba70cc4b8a..dd25fc7da7 100644
--- a/Editor/Mono/BuildPipeline/DesktopStandaloneBuildWindowExtension.cs
+++ b/Editor/Mono/BuildPipeline/DesktopStandaloneBuildWindowExtension.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: ContentBuild not yet converted
using System;
using System.Collections.Generic;
using UnityEditor;
@@ -12,8 +11,8 @@
internal abstract class DesktopStandaloneBuildWindowExtension : DefaultBuildWindowExtension
{
- private GUIContent m_StandaloneTarget = EditorGUIUtility.TrTextContent("Target Platform", "Destination platform for standalone build");
- private GUIContent m_Architecture = EditorGUIUtility.TrTextContent("Architecture", "Build m_Architecture for standalone");
+ private GUIContent m_StandaloneTarget = L10n.TextContent("Target Platform", "Destination platform for standalone build", null, null);
+ private GUIContent m_Architecture = L10n.TextContent("Architecture", "Build m_Architecture for standalone", null, null);
private BuildTarget[] m_StandaloneSubtargets;
private GUIContent[] m_StandaloneSubtargetStrings;
@@ -68,17 +67,17 @@ private void SetupStandaloneSubtargets()
if (ModuleManager.IsPlatformSupportLoadedByBuildTarget(BuildTarget.StandaloneWindows))
{
standaloneSubtargetsList.Add(BuildTarget.StandaloneWindows);
- standaloneSubtargetStringsList.Add(EditorGUIUtility.TrTextContent("Windows"));
+ standaloneSubtargetStringsList.Add(L10n.TextContent("Windows", null, null, null));
}
if (ModuleManager.IsPlatformSupportLoadedByBuildTarget(BuildTarget.StandaloneOSX))
{
standaloneSubtargetsList.Add(BuildTarget.StandaloneOSX);
- standaloneSubtargetStringsList.Add(EditorGUIUtility.TrTextContent("macOS"));
+ standaloneSubtargetStringsList.Add(L10n.TextContent("macOS", null, null, null));
}
if (ModuleManager.IsPlatformSupportLoadedByBuildTarget(BuildTarget.StandaloneLinux64))
{
standaloneSubtargetsList.Add(BuildTarget.StandaloneLinux64);
- standaloneSubtargetStringsList.Add(EditorGUIUtility.TrTextContent("Linux"));
+ standaloneSubtargetStringsList.Add(L10n.TextContent("Linux", null, null, null));
}
m_StandaloneSubtargets = standaloneSubtargetsList.ToArray();
@@ -276,4 +275,3 @@ public override bool ShouldDrawWaitForManagedDebugger()
return true;
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/BuildPipeline/DesktopStandalonePostProcessor.cs b/Editor/Mono/BuildPipeline/DesktopStandalonePostProcessor.cs
index 6b36e64b80..a6d735c98b 100644
--- a/Editor/Mono/BuildPipeline/DesktopStandalonePostProcessor.cs
+++ b/Editor/Mono/BuildPipeline/DesktopStandalonePostProcessor.cs
@@ -54,8 +54,9 @@ public override void UpdateBootConfig(BuildTarget target, BootConfigData config,
config.AddKey("nolog");
if (PlayerSettings.GetCaptureStartupLogs(NamedBuildTarget.FromActiveSettings(target)))
config.Set("capture-startup-logs", "1");
- if (PlayerSettings.enableDirectStorage)
- config.Set("enable-directstorage", PlayerSettings.enableDirectStorage.ToString()); // Shared naming with other DirectStorage supported platforms
+
+ config.Set("enable-directstorage", PlayerSettings.enableDirectStorage.ToString()); // Shared naming with other DirectStorage supported platforms
+
}
public override ILaunchReport LaunchPlayer(BuildLaunchPlayerArgs args)
diff --git a/Editor/Mono/BuildPipeline/NamedBuildTarget.cs b/Editor/Mono/BuildPipeline/NamedBuildTarget.cs
index 8edd7f926b..a38c9e9949 100644
--- a/Editor/Mono/BuildPipeline/NamedBuildTarget.cs
+++ b/Editor/Mono/BuildPipeline/NamedBuildTarget.cs
@@ -153,8 +153,6 @@ public static NamedBuildTarget FromBuildTargetGroup(BuildTargetGroup buildTarget
return new NamedBuildTarget("GameCoreXboxOne");
case BuildTargetGroup.PS5:
return new NamedBuildTarget("PS5");
- case BuildTargetGroup.Kepler:
- return new NamedBuildTarget("Kepler");
}
throw new ArgumentException($"There is no a valid NamedBuildTarget for BuildTargetGroup '{buildTargetGroup}'");
diff --git a/Editor/Mono/BuildPipeline/Settings/BuildAssetBundleOptions.bindings.cs b/Editor/Mono/BuildPipeline/Settings/BuildAssetBundleOptions.bindings.cs
index 335a7c11f8..472d16dd1f 100644
--- a/Editor/Mono/BuildPipeline/Settings/BuildAssetBundleOptions.bindings.cs
+++ b/Editor/Mono/BuildPipeline/Settings/BuildAssetBundleOptions.bindings.cs
@@ -33,7 +33,7 @@ public enum BuildAssetBundleOptions
// Don't compress the data when creating the asset bundle.
///Don't compress the data when creating the AssetBundle.
- ///Builds the AssetBundle without any compression, resulting in larger file sizes but faster build and load times. Uncompressed AssetBundles are 16-byte aligned.
+ ///Builds the AssetBundle without any compression, resulting in larger file sizes but faster build and load times.
///
/// See [AssetBundles compression wiki](xref:um-asset-bundles-cache), , , and .
UncompressedAssetBundle = 1, // 1 << 0
@@ -131,6 +131,7 @@ public enum BuildAssetBundleOptions
/// When this flag is set, errors logged from these build callbacks also fail the build:
/// ,
/// ,
+ /// ,
/// ,
/// , and
/// .
diff --git a/Editor/Mono/BuildPipeline/Settings/BuildAssetBundlesParameters.bindings.cs b/Editor/Mono/BuildPipeline/Settings/BuildAssetBundlesParameters.bindings.cs
index 987e46e690..1aa3ae2466 100644
--- a/Editor/Mono/BuildPipeline/Settings/BuildAssetBundlesParameters.bindings.cs
+++ b/Editor/Mono/BuildPipeline/Settings/BuildAssetBundlesParameters.bindings.cs
@@ -50,7 +50,6 @@ public struct BuildAssetBundlesParameters
///
///
///
- ///
public int subtarget { get; set; }
///User-specified preprocessor defines used while compiling assemblies during the AssetBundle build. (optional)
///Preprocessor defines may be used to exclude serialized fields from class definitions, so this can be have an influence on how objects are serialized during the build process.
diff --git a/Editor/Mono/BuildPipeline/Settings/BuildContentOptions.bindings.cs b/Editor/Mono/BuildPipeline/Settings/BuildContentOptions.bindings.cs
index e185bfbad9..9b18f388eb 100644
--- a/Editor/Mono/BuildPipeline/Settings/BuildContentOptions.bindings.cs
+++ b/Editor/Mono/BuildPipeline/Settings/BuildContentOptions.bindings.cs
@@ -63,6 +63,7 @@ public enum BuildContentOptions
/// When this flag is set, errors logged from these build callbacks also fail the build:
/// ,
/// ,
+ /// ,
/// ,
/// , and
/// .
diff --git a/Editor/Mono/BuildPipeline/Settings/BuildOptions.bindings.cs b/Editor/Mono/BuildPipeline/Settings/BuildOptions.bindings.cs
index dc5abcfa8a..b2a1866f59 100644
--- a/Editor/Mono/BuildPipeline/Settings/BuildOptions.bindings.cs
+++ b/Editor/Mono/BuildPipeline/Settings/BuildOptions.bindings.cs
@@ -229,6 +229,7 @@ public enum BuildOptions
/// When this flag is set, errors logged from these build callbacks also fail the build:
/// ,
/// ,
+ /// ,
/// ,
/// , and
/// .
diff --git a/Editor/Mono/BuildPipeline/Settings/BuildPlayerOptions.bindings.cs b/Editor/Mono/BuildPipeline/Settings/BuildPlayerOptions.bindings.cs
index 513bae79b0..2401ff46cd 100644
--- a/Editor/Mono/BuildPipeline/Settings/BuildPlayerOptions.bindings.cs
+++ b/Editor/Mono/BuildPipeline/Settings/BuildPlayerOptions.bindings.cs
@@ -89,7 +89,6 @@ public struct BuildPlayerOptions
///
///
///
- ///
public int subtarget { get; set; }
///The flags to apply when building the Player.
///Set this property to a bitwise combination of values before you pass to . For example, set options = BuildOptions.Development | BuildOptions.AutoRunPlayer to create a development build and run it automatically after the build completes.
diff --git a/Editor/Mono/BuildPipeline/iOS/ISwiftPackageProvider.cs b/Editor/Mono/BuildPipeline/iOS/ISwiftPackageProvider.cs
new file mode 100644
index 0000000000..2565f0f03a
--- /dev/null
+++ b/Editor/Mono/BuildPipeline/iOS/ISwiftPackageProvider.cs
@@ -0,0 +1,66 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using UnityEditor.Build;
+using UnityEngine.Bindings;
+
+namespace UnityEditor.iOS
+{
+ [VisibleToOtherModules]
+ internal interface ISwiftPackageProvider : IOrderedCallback
+ {
+ // Called on the main thread before the build program generates the Xcode project.
+ XcodeSwiftPackage[] OnCollectSwiftPackages(SwiftPackageProviderContext context);
+ }
+
+ [VisibleToOtherModules]
+ internal struct SwiftPackageProviderContext
+ {
+ public BuildTarget platform { get; }
+
+ public SwiftPackageProviderContext(BuildTarget platform)
+ {
+ this.platform = platform;
+ }
+ }
+
+ [VisibleToOtherModules]
+ internal enum XcodeSwiftPackageRequirementKind
+ {
+ ExactVersion,
+ UpToNextMajorVersion,
+ UpToNextMinorVersion,
+ VersionRange,
+ Branch,
+ Revision,
+ }
+
+ // The fields Xcode stores for each kind: version for ExactVersion, minimumVersion for the UpToNext kinds,
+ // minimumVersion and maximumVersion for VersionRange, and branch or revision for the others.
+ [VisibleToOtherModules]
+ internal struct XcodeSwiftPackageRequirement
+ {
+ public XcodeSwiftPackageRequirementKind kind { get; set; }
+ public string version { get; set; }
+ public string minimumVersion { get; set; }
+ public string maximumVersion { get; set; }
+ public string branch { get; set; }
+ public string revision { get; set; }
+ }
+
+ [VisibleToOtherModules]
+ internal struct XcodeSwiftPackageProduct
+ {
+ public string name { get; set; }
+ public bool weakLink { get; set; }
+ }
+
+ [VisibleToOtherModules]
+ internal struct XcodeSwiftPackage
+ {
+ public string url { get; set; }
+ public XcodeSwiftPackageRequirement requirement { get; set; }
+ public XcodeSwiftPackageProduct[] products { get; set; }
+ }
+}
diff --git a/Editor/Mono/BuildPlayerSceneTreeView.cs b/Editor/Mono/BuildPlayerSceneTreeView.cs
index 15331c143d..3bd08c62eb 100644
--- a/Editor/Mono/BuildPlayerSceneTreeView.cs
+++ b/Editor/Mono/BuildPlayerSceneTreeView.cs
@@ -306,7 +306,7 @@ protected override void ContextClicked()
if (GetSelection().Count > 0)
{
GenericMenu menu = new GenericMenu();
- menu.AddItem(EditorGUIUtility.TrTextContent("Remove Selection"), false, RemoveSelection);
+ menu.AddItem(L10n.TextContent("Remove Selection", null, null, null), false, RemoveSelection);
menu.ShowAsContext();
}
}
diff --git a/Editor/Mono/BuildPlayerWindow.cs b/Editor/Mono/BuildPlayerWindow.cs
index 059d3d9908..7586a69eb2 100644
--- a/Editor/Mono/BuildPlayerWindow.cs
+++ b/Editor/Mono/BuildPlayerWindow.cs
@@ -42,21 +42,21 @@ class Styles
public GUIStyle oddRow = "CN EntryBackOdd";
public GUIStyle platformSelector = "PlayerSettingsPlatform";
- public GUIContent platformTitle = EditorGUIUtility.TrTextContent("Platform", "Which platform to build for");
- public GUIContent switchPlatform = EditorGUIUtility.TrTextContent("Switch Platform");
- public GUIContent build = EditorGUIUtility.TrTextContent("Build");
- public GUIContent buildAndRun = EditorGUIUtility.TrTextContent("Build And Run");
- public GUIContent scenesInBuild = EditorGUIUtility.TrTextContent("Scenes In Build", "Which scenes to include in the build");
- public GUIContent checkOut = EditorGUIUtility.TrTextContent("Check out");
- public GUIContent addOpenSource = EditorGUIUtility.TrTextContent("Add Open Scenes");
+ public GUIContent platformTitle = L10n.TextContent("Platform", "Which platform to build for", null, null);
+ public GUIContent switchPlatform = L10n.TextContent("Switch Platform", null, null, null);
+ public GUIContent build = L10n.TextContent("Build", null, null, null);
+ public GUIContent buildAndRun = L10n.TextContent("Build And Run", null, null, null);
+ public GUIContent scenesInBuild = L10n.TextContent("Scenes In Build", "Which scenes to include in the build", null, null);
+ public GUIContent checkOut = L10n.TextContent("Check out", null, null, null);
+ public GUIContent addOpenSource = L10n.TextContent("Add Open Scenes", null, null, null);
public string noModuleLoaded = L10n.Tr("No {0} module loaded.", null);
- public GUIContent openDownloadPage = EditorGUIUtility.TrTextContent("Open Download Page");
- public GUIContent installModuleWithHub = EditorGUIUtility.TrTextContent("Install with Unity Hub");
+ public GUIContent openDownloadPage = L10n.TextContent("Open Download Page", null, null, null);
+ public GUIContent installModuleWithHub = L10n.TextContent("Install with Unity Hub", null, null, null);
public string EditorWillNeedToBeReloaded = L10n.Tr("Note: Editor will need to be restarted to load any newly installed modules", null);
public string infoText = L10n.Tr("{0} is not included in your Unity Pro license. Your {0} build will include a Unity Personal Edition splash screen.\n\nYou must be eligible to use Unity Personal Edition to use this build option. Please refer to our EULA for further information.", null);
- public GUIContent eula = EditorGUIUtility.TrTextContent("Eula");
+ public GUIContent eula = L10n.TextContent("Eula", null, null, null);
public string addToYourPro = L10n.Tr("Add {0} to your Unity Pro license", null);
- public GUIContent installInBuildFolder = EditorGUIUtility.TrTextContent("Install into source code 'build' folder", "Install into source checkout 'build' folder, for debugging with source code");
+ public GUIContent installInBuildFolder = L10n.TextContent("Install into source code 'build' folder", "Install into source checkout 'build' folder, for debugging with source code", null, null);
public Texture2D activePlatformIcon = EditorGUIUtility.IconContent("BuildSettings.SelectedIcon").image as Texture2D;
@@ -70,31 +70,31 @@ public GUIContent GetDownloadErrorForTarget(BuildTarget target)
}
// string and matching enum values for standalone subtarget dropdowm
- public GUIContent debugBuild = EditorGUIUtility.TrTextContent("Development Build");
- public GUIContent autoconnectProfiler = EditorGUIUtility.TrTextContent("Autoconnect Profiler", "When the build is started, an open Profiler Window will automatically connect to the Player and start profiling. The \"Build And Run\" option will also automatically open the Profiler Window.");
- public GUIContent autoconnectProfilerDisabled = EditorGUIUtility.TrTextContent("Autoconnect Profiler", "Profiling is only enabled in a Development Player.");
- public GUIContent buildWithDeepProfiler = EditorGUIUtility.TrTextContent("Deep Profiling Support", "Build Player with Deep Profiling Support. This might affect Player performance.");
- public GUIContent buildWithDeepProfilerDisabled = EditorGUIUtility.TrTextContent("Deep Profiling Support", "Profiling is only enabled in a Development Player.");
- public GUIContent allowDebugging = EditorGUIUtility.TrTextContent("Script Debugging", "Enable this setting to allow your script code to be debugged.");
- public GUIContent waitForManagedDebugger = EditorGUIUtility.TrTextContent("Wait For Managed Debugger", "Show a dialog where you can attach a managed debugger before any script execution. Can also use volume Up or Down button to confirm on Android.");
- public GUIContent managedDebuggerFixedPort = EditorGUIUtility.TrTextContent("Managed Debugger Fixed Port", "Use the specified port to attach to the managed debugger. If 0, the port will be automatically selected.");
- public GUIContent explicitNullChecks = EditorGUIUtility.TrTextContent("Explicit Null Checks");
- public GUIContent explicitDivideByZeroChecks = EditorGUIUtility.TrTextContent("Divide By Zero Checks");
- public GUIContent explicitArrayBoundsChecks = EditorGUIUtility.TrTextContent("Array Bounds Checks");
- public GUIContent learnAboutUnityCloudBuild = EditorGUIUtility.TrTextContent("Learn about Unity Build Automation");
- public GUIContent compressionMethod = EditorGUIUtility.TrTextContent("Compression Method", "Compression applied to Player data (scenes and resources).\nDefault - none or default platform compression.\nLZ4 - fast compression suitable for Development Builds.\nLZ4HC - higher compression rate variance of LZ4, causes longer build times. Works best for Release Builds.");
-
- public readonly GUIContent assetImportOverrides = EditorGUIUtility.TrTextContent("Asset Import Overrides", "Asset import overrides for local development. Reducing maximum texture size or compression settings can speed up asset imports and platform switches.");
- public readonly GUIContent maxTextureSize = EditorGUIUtility.TrTextContent("Max Texture Size", "Maximum texture import size for local development. Reducing maximum texture size can speed up asset imports and platform switches.");
+ public GUIContent debugBuild = L10n.TextContent("Development Build", null, null, null);
+ public GUIContent autoconnectProfiler = L10n.TextContent("Autoconnect Profiler", "When the build is started, an open Profiler Window will automatically connect to the Player and start profiling. The \"Build And Run\" option will also automatically open the Profiler Window.", null, null);
+ public GUIContent autoconnectProfilerDisabled = L10n.TextContent("Autoconnect Profiler", "Profiling is only enabled in a Development Player.", null, null);
+ public GUIContent buildWithDeepProfiler = L10n.TextContent("Deep Profiling Support", "Build Player with Deep Profiling Support. This might affect Player performance.", null, null);
+ public GUIContent buildWithDeepProfilerDisabled = L10n.TextContent("Deep Profiling Support", "Profiling is only enabled in a Development Player.", null, null);
+ public GUIContent allowDebugging = L10n.TextContent("Script Debugging", "Enable this setting to allow your script code to be debugged.", null, null);
+ public GUIContent waitForManagedDebugger = L10n.TextContent("Wait For Managed Debugger", "Show a dialog where you can attach a managed debugger before any script execution. Can also use volume Up or Down button to confirm on Android.", null, null);
+ public GUIContent managedDebuggerFixedPort = L10n.TextContent("Managed Debugger Fixed Port", "Use the specified port to attach to the managed debugger. If 0, the port will be automatically selected.", null, null);
+ public GUIContent explicitNullChecks = L10n.TextContent("Explicit Null Checks", null, null, null);
+ public GUIContent explicitDivideByZeroChecks = L10n.TextContent("Divide By Zero Checks", null, null, null);
+ public GUIContent explicitArrayBoundsChecks = L10n.TextContent("Array Bounds Checks", null, null, null);
+ public GUIContent learnAboutUnityCloudBuild = L10n.TextContent("Learn about Unity Build Automation", null, null, null);
+ public GUIContent compressionMethod = L10n.TextContent("Compression Method", "Compression applied to Player data (scenes and resources).\nDefault - none or default platform compression.\nLZ4 - fast compression suitable for Development Builds.\nLZ4HC - higher compression rate variance of LZ4, causes longer build times. Works best for Release Builds.", null, null);
+
+ public readonly GUIContent assetImportOverrides = L10n.TextContent("Asset Import Overrides", "Asset import overrides for local development. Reducing maximum texture size or compression settings can speed up asset imports and platform switches.", null, null);
+ public readonly GUIContent maxTextureSize = L10n.TextContent("Max Texture Size", "Maximum texture import size for local development. Reducing maximum texture size can speed up asset imports and platform switches.", null, null);
public readonly GUIContent[] maxTextureSizeLabels =
{
- EditorGUIUtility.TrTextContent("No Override", "Use maximum texture size as specified in per-texture import settings."),
- EditorGUIUtility.TrTextContent("Max 2048", "Make imported textures never exceed 2048 pixels in width or height."),
- EditorGUIUtility.TrTextContent("Max 1024", "Make imported textures never exceed 1024 pixels in width or height."),
- EditorGUIUtility.TrTextContent("Max 512", "Make imported textures never exceed 512 pixels in width or height."),
- EditorGUIUtility.TrTextContent("Max 256", "Make imported textures never exceed 256 pixels in width or height."),
- EditorGUIUtility.TrTextContent("Max 128", "Make imported textures never exceed 128 pixels in width or height."),
- EditorGUIUtility.TrTextContent("Max 64", "Make imported textures never exceed 64 pixels in width or height."),
+ L10n.TextContent("No Override", "Use maximum texture size as specified in per-texture import settings.", null, null),
+ L10n.TextContent("Max 2048", "Make imported textures never exceed 2048 pixels in width or height.", null, null),
+ L10n.TextContent("Max 1024", "Make imported textures never exceed 1024 pixels in width or height.", null, null),
+ L10n.TextContent("Max 512", "Make imported textures never exceed 512 pixels in width or height.", null, null),
+ L10n.TextContent("Max 256", "Make imported textures never exceed 256 pixels in width or height.", null, null),
+ L10n.TextContent("Max 128", "Make imported textures never exceed 128 pixels in width or height.", null, null),
+ L10n.TextContent("Max 64", "Make imported textures never exceed 64 pixels in width or height.", null, null),
};
public readonly int[] maxTextureSizeValues =
{
@@ -108,10 +108,10 @@ public GUIContent GetDownloadErrorForTarget(BuildTarget target)
};
public readonly GUIContent[] textureCompressionLabels =
{
- EditorGUIUtility.TrTextContent("No Override", "Do not modify texture import compression settings."),
- EditorGUIUtility.TrTextContent("Force Fast Compressor", "Use a faster but lower quality texture compression mode for all compressed textures. Turn off Crunch compression."),
- EditorGUIUtility.TrTextContent("Force Uncompressed", "Do not compress textures."),
- EditorGUIUtility.TrTextContent("Force No Crunch", "Disable crunch compression on textures.")
+ L10n.TextContent("No Override", "Do not modify texture import compression settings.", null, null),
+ L10n.TextContent("Force Fast Compressor", "Use a faster but lower quality texture compression mode for all compressed textures. Turn off Crunch compression.", null, null),
+ L10n.TextContent("Force Uncompressed", "Do not compress textures.", null, null),
+ L10n.TextContent("Force No Crunch", "Disable crunch compression on textures.", null, null)
};
public readonly int[] textureCompressionValues =
{
@@ -121,8 +121,8 @@ public GUIContent GetDownloadErrorForTarget(BuildTarget target)
(int)OverrideTextureCompression.ForceNoCrunchCompression
};
- public readonly GUIContent textureCompression = EditorGUIUtility.TrTextContent("Texture Compression", "Texture compression override for local development. Fast or Uncompressed can speed up asset imports and platform switches.");
- public readonly GUIContent applyOverrides = EditorGUIUtility.TrTextContent("Apply Overrides", "Apply asset import override settings");
+ public readonly GUIContent textureCompression = L10n.TextContent("Texture Compression", "Texture compression override for local development. Fast or Uncompressed can speed up asset imports and platform switches.", null, null);
+ public readonly GUIContent applyOverrides = L10n.TextContent("Apply Overrides", "Apply asset import override settings", null, null);
public Compression[] compressionTypes =
{
@@ -133,9 +133,9 @@ public GUIContent GetDownloadErrorForTarget(BuildTarget target)
public GUIContent[] compressionStrings =
{
- EditorGUIUtility.TrTextContent("Default"),
- EditorGUIUtility.TrTextContent("LZ4"),
- EditorGUIUtility.TrTextContent("LZ4HC"),
+ L10n.TextContent("Default", null, null, null),
+ L10n.TextContent("LZ4", null, null, null),
+ L10n.TextContent("LZ4HC", null, null, null),
};
[NoAutoStaticsCleanup] // GUIStyle wrapping EditorStyles.foldout; survives code reload
@@ -240,7 +240,7 @@ public BuildPlayerWindow()
s_CurrOverrideMaxTextureSize = -1;
minSize = new Vector2(640, 580);
position = new Rect(50, 50, minSize.x, minSize.y);
- titleContent = EditorGUIUtility.TrTextContent("Build Settings");
+ titleContent = L10n.TextContent("Build Settings", null, null, null);
}
BuildPlayerSceneTreeView m_TreeView;
@@ -420,7 +420,7 @@ void ActiveBuildTargetsGUI()
// Switch build target
BuildTarget selectedTarget = EditorUserBuildSettingsUtils.CalculateSelectedBuildTarget();
GUI.enabled = BuildPipeline.IsBuildPlatformSupported(selectedTarget);
- if (GUILayout.Button(EditorGUIUtility.TrTextContent("Player Settings..."), GUILayout.Width(Styles.kButtonWidth)))
+ if (GUILayout.Button(L10n.TextContent("Player Settings...", null, null, null), GUILayout.Width(Styles.kButtonWidth)))
{
SettingsService.OpenProjectSettings("Project/Player");
GUIUtility.ExitGUI();
@@ -447,7 +447,7 @@ void ShowAlert()
GUILayout.BeginHorizontal();
GUILayout.Space(10);
GUILayout.BeginVertical();
- EditorGUILayout.HelpBox(EditorGUIUtility.TrTextContent("Unable to access Unity services. Please log in, or request membership to this project to use these services.").text, MessageType.Warning);
+ EditorGUILayout.HelpBox(L10n.TextContent("Unable to access Unity services. Please log in, or request membership to this project to use these services.", null, null, null).text, MessageType.Warning);
GUILayout.EndVertical();
GUILayout.Space(5);
GUILayout.EndHorizontal();
diff --git a/Editor/Mono/BuildProfile/BuildProfile.API.cs b/Editor/Mono/BuildProfile/BuildProfile.API.cs
index edb0763f4b..ba5463e25a 100644
--- a/Editor/Mono/BuildProfile/BuildProfile.API.cs
+++ b/Editor/Mono/BuildProfile/BuildProfile.API.cs
@@ -41,20 +41,43 @@ public static void SetActiveBuildProfile(BuildProfile buildProfile)
///
/// Instantiates a new component of type T and adds it as a sub-asset to the build profile.
///
- /// Returns the newly created ScriptableObect.
+ ///
+ /// When T is , the new component starts as a copy of the global player
+ /// settings.
+ ///
+ /// Returns the newly created component.
///
- /// Thrown when a sub asset of the same type already exists in the build profile.
+ /// Thrown when a component of the same type already exists in the build profile, or when T is
+ /// . To give a build profile its own player settings, use
+ /// as T.
+ ///
+ ///
+ /// Thrown when T is and the build profile is a classic platform profile.
///
[VisibleToOtherModules]
public T CreateComponent() where T : UnityEngine.Object
{
- var found = GetComponent();
- if (found != null)
+ if (typeof(T) == typeof(PlayerSettings))
+ throw new ArgumentException($"{nameof(PlayerSettings)} can't be created on a build profile. Use {nameof(CreateComponent)}<{nameof(BuildProfilePlayerSettings)}>() instead.");
+
+ ThrowIfComponentExists(typeof(T));
+
+ if (typeof(T) == typeof(BuildProfilePlayerSettings))
+ return CreatePlayerSettingsComponent() as T;
+
+ var type = typeof(T);
+ if (type == typeof(BuildProfileGraphicsSettings))
{
- throw new ArgumentException($"The component {typeof(T).Name} already exists in the build profile {name}.");
+ BuildProfileModuleUtil.CreateGraphicsSettings(this);
+ return graphicsSettings as T;
+ }
+
+ if (type == typeof(BuildProfileQualitySettings))
+ {
+ BuildProfileModuleUtil.CreateQualitySettings(this);
+ return qualitySettings as T;
}
- var type = typeof(T);
if (type.IsSubclassOf(typeof(ScriptableObject)))
{
var objectToAdd = ScriptableObject.CreateInstance(type);
@@ -71,6 +94,10 @@ public T CreateComponent() where T : UnityEngine.Object
/// Gets a component of type T associated with the build profile, its global fallback,
/// or null if the component is not available.
///
+ ///
+ /// has no global fallback. A null result means that the build profile
+ /// uses the global player settings, which this method returns when T is .
+ ///
public T GetComponent() where T : UnityEngine.Object
{
if (typeof(T) == typeof(PlayerSettings))
@@ -80,27 +107,37 @@ public T GetComponent() where T : UnityEngine.Object
return s_GlobalPlayerSettings as T;
}
- var path = AssetDatabase.GetAssetPath(this);
- var objectsFound = AssetDatabase.LoadAllAssetsAtPath(path);
- foreach (var obj in objectsFound)
- {
- if (obj is T component)
- {
- return component;
- }
- }
+ if (typeof(T) == typeof(BuildProfilePlayerSettings))
+ return buildProfilePlayerSettings as T;
- return null;
+ var found = FindComponentObject(typeof(T));
+ if (found is null)
+ return null;
+
+ // A reference component resolves to its referenced asset; an unassigned/orphaned proxy
+ // resolves to null and is treated as if the component was not added.
+ return found is ReferencedComponent proxy ? proxy.reference as T : found as T;
}
///
- /// Adds a given component to the build profile. If the component is not an asset in
- /// the project, it will be added as a sub-asset to the build profile.
+ /// Adds a component to the build profile, either as a subasset or as a reference to an existing project asset.
///
- /// Component type
- ///
- /// Thrown when objectToAdd is null.
- /// Thrown if adding a sub-asset of an existing component type.
+ ///
+ /// How the component is stored depends on whether already exists as a
+ /// project asset. An object that isn't a project asset becomes a hidden subasset of the build profile,
+ /// and the profile owns it. An object that's already a project asset stays where it is, and the profile
+ /// stores a reference to it instead.
+ ///
+ /// A build profile holds at most one component of each type.
+ ///
+ /// The type of the component to add.
+ /// The object to add to the build profile.
+ /// Thrown when is null.
+ /// Thrown when the build profile already contains a component of
+ /// type , or when is a
+ /// built-in override that only can add.
+ /// Thrown when is a
+ /// . Use instead.
public void AddComponent(T objectToAdd) where T : UnityEngine.Object
{
if (objectToAdd == null)
@@ -108,46 +145,62 @@ public void AddComponent(T objectToAdd) where T : UnityEngine.Object
throw new ArgumentNullException(nameof(objectToAdd), "The object to add cannot be null.");
}
- // Don't allow duplicates
- var found = GetComponent();
- if (found != null)
- {
- throw new ArgumentException($"The component {typeof(T).Name} already exists in the build profile {name}.");
- }
+ if (objectToAdd is BuildProfilePlayerSettings)
+ throw new NotSupportedException($"{nameof(BuildProfilePlayerSettings)} can't be added to a build profile. Use {nameof(CreateComponent)}<{nameof(BuildProfilePlayerSettings)}>() instead.");
+ if (IsSettingsOverrideType(objectToAdd.GetType()))
+ throw new ArgumentException($"{objectToAdd.GetType().Name} can't be added to a build profile as an existing object. Use {nameof(CreateComponent)}<{objectToAdd.GetType().Name}>() instead.", nameof(objectToAdd));
+
+ ThrowIfComponentExists(typeof(T));
- var assetPath = AssetDatabase.GetAssetPath(objectToAdd);
- if (string.IsNullOrEmpty(assetPath))
+ if (!AssetDatabase.Contains(objectToAdd))
{
objectToAdd.hideFlags |= HideFlags.HideInHierarchy;
AssetDatabase.AddObjectToAsset(objectToAdd, this);
return;
}
- // TODO: Future API will support build profile components as reference.
- throw new InvalidOperationException($"Asset {objectToAdd} has a non-null path. Build profile components should be sub-assets.");
+ if (objectToAdd is not ScriptableObject referenceAsset)
+ throw new InvalidOperationException($"Referenced build profile components must be ScriptableObjects; {objectToAdd} is not.");
+
+ CreateReferenceProxy(typeof(T), referenceAsset);
}
///
/// Removes the first occurence of a component of type T from the build profile.
///
+ ///
+ /// When T is , the build profile falls back to the global player
+ /// settings. If the build profile is active, Unity recompiles scripts when the scripting defines change, and
+ /// logs a warning for settings that only take effect after an editor restart.
+ ///
public void RemoveComponent() where T : UnityEngine.Object
{
- var found = GetComponent();
- if (found == null)
+ if (typeof(T) == typeof(BuildProfilePlayerSettings))
+ {
+ RemovePlayerSettingsComponent();
+ return;
+ }
+
+ var toRemove = FindComponentObject(typeof(T));
+ if (toRemove == null)
{
Debug.LogWarning($"The component {typeof(T).Name} does not exist in the build profile {name}. ");
return;
}
- if (Array.IndexOf(this.requiredComponents, found) > -1)
+ if (Array.IndexOf(requiredComponents, toRemove) > -1)
throw new ArgumentException($"The component {typeof(T).Name} is required and cannot be removed from the build profile {name}.");
- AssetDatabase.RemoveObjectFromAsset(found);
+ RemoveComponentObject(toRemove);
}
///
/// Removes a specific component from the build profile.
///
+ ///
+ /// Passing an object that the profile holds by reference removes the proxy sub-asset linking to
+ /// it; the referenced object itself stays on disk.
+ ///
/// Object to remove.
/// objectToRemove is null.
/// objectToRemove is not part of the build profile.
@@ -158,41 +211,109 @@ public void RemoveComponent(T objectToRemove) where T : UnityEngine.Object
throw new ArgumentNullException(nameof(objectToRemove), "The object to remove cannot be null.");
}
- var path = AssetDatabase.GetAssetPath(this);
- var objectToRemovePath = AssetDatabase.GetAssetPath(objectToRemove);
+ if (objectToRemove is BuildProfilePlayerSettings)
+ {
+ if (objectToRemove != buildProfilePlayerSettings)
+ throw new ArgumentException($"The object {objectToRemove} is not part of the build profile {name}.");
+
+ RemovePlayerSettingsComponent();
+ return;
+ }
- if (objectToRemovePath != path)
+ // A profile holds at most one component per type, so a reference component of type T is the
+ // proxy pointing at objectToRemove; the proxy is the sub-asset that belongs to the profile.
+ var toRemove = FindComponentObject(typeof(T));
+ if (toRemove is ReferencedComponent proxy)
+ {
+ if (proxy.reference != objectToRemove)
+ {
+ throw new ArgumentException($"The object {objectToRemove} is not the reference for the build profile component of type {typeof(T).Name}.");
+ }
+ }
+ else if (toRemove != objectToRemove)
{
+ var objectToRemovePath = AssetDatabase.GetAssetPath(objectToRemove);
+ var path = AssetDatabase.GetAssetPath(this);
throw new ArgumentException($"The object {objectToRemove} (path: {objectToRemovePath}) is not part of the build profile {path}.");
}
- if (Array.IndexOf(this.requiredComponents, objectToRemove) > -1)
+ if (Array.IndexOf(requiredComponents, objectToRemove) > -1)
throw new ArgumentException($"The component {typeof(T).Name} is required and cannot be removed from the build profile {name}.");
- AssetDatabase.RemoveObjectFromAsset(objectToRemove);
+ RemoveComponentObject(toRemove);
+ }
+
+ ///
+ /// Adds a reference to a standalone on-disk settings asset, or an unassigned reference when
+ /// is null.
+ ///
+ /// Thrown when a component of the same type already exists, or when
+ /// is a built-in override that only can add.
+ /// Thrown when a non-null reference is not an on-disk asset.
+ internal void AddReferenceComponent(Type componentType, ScriptableObject reference)
+ {
+ if (IsSettingsOverrideType(componentType))
+ throw new ArgumentException($"{componentType.Name} can't be added to a build profile as a reference. Use {nameof(CreateComponent)}<{componentType.Name}>() instead.", nameof(componentType));
+
+ ThrowIfComponentExists(componentType);
+ ThrowIfNotOnDisk(reference);
+
+ CreateReferenceProxy(componentType, reference);
+ }
+
+ ///
+ /// Reassigns an existing reference component's source in place, preserving the proxy sub-asset.
+ /// A null reference leaves the component present but unassigned. The caller is responsible for
+ /// ensuring the reference component exists; use to add one.
+ ///
+ ///
+ /// Thrown when a non-null reference is not an on-disk asset, or when no reference component of
+ /// exists.
+ ///
+ internal void SetReferenceComponent(Type componentType, ScriptableObject reference)
+ {
+ ThrowIfNotOnDisk(reference);
+
+ var proxy = FindReferenceProxy(componentType);
+ if (proxy == null)
+ throw new InvalidOperationException($"No reference component of type {componentType.Name} exists in the build profile {name}. Add it before setting its reference.");
+
+ Undo.RecordObject(proxy, "Set Settings Source");
+ proxy.reference = reference;
+ EditorUtility.SetDirty(proxy);
}
///
/// Forcibly removes the first occurrence of a component of type T from the build profile.
- /// For internal use only to accommodate required components.
///
[VisibleToOtherModules]
internal void ForceRemoveComponent() where T : UnityEngine.Object
{
- var found = GetComponent();
- if (found == null)
+ if (typeof(T) == typeof(BuildProfilePlayerSettings))
+ {
+ RemovePlayerSettingsComponent();
+ return;
+ }
+
+ var toRemove = FindComponentObject(typeof(T));
+ if (toRemove == null)
{
Debug.LogWarning($"The component {typeof(T).Name} does not exist in the build profile {name}. ");
return;
}
- AssetDatabase.RemoveObjectFromAsset(found);
+ RemoveComponentObject(toRemove);
}
///
/// Gets a component of type T associated with the currently active build profile,
/// its global fallback, or null if the component is not available.
///
+ ///
+ /// When T is , a null result means that the global player settings apply,
+ /// either because no build profile is active or because the active build profile has no player settings
+ /// component. To read the player settings that apply, use as T.
+ ///
public static T GetActiveComponent() where T : UnityEngine.Object
{
var buildProfile = GetActiveBuildProfile();
@@ -206,5 +327,91 @@ public static T GetActiveComponent() where T : UnityEngine.Object
return buildProfile.GetComponent();
}
+
+ UnityEngine.Object[] LoadAllComponentObjects()
+ {
+ var path = AssetDatabase.GetAssetPath(this);
+ return AssetDatabase.LoadAllAssetsAtPath(path);
+ }
+
+ // The engine reads these overrides only through the cached fields, which BuildProfileModuleUtil keeps in sync.
+ static bool IsSettingsOverrideType(Type componentType) =>
+ componentType == typeof(BuildProfileGraphicsSettings) || componentType == typeof(BuildProfileQualitySettings);
+
+ void RemoveComponentObject(UnityEngine.Object toRemove)
+ {
+ if (toRemove is BuildProfileGraphicsSettings)
+ BuildProfileModuleUtil.RemoveGraphicsSettings(this);
+ else if (toRemove is BuildProfileQualitySettings)
+ BuildProfileModuleUtil.RemoveQualitySettings(this);
+ else
+ AssetDatabase.RemoveObjectFromAsset(toRemove);
+ }
+
+ UnityEngine.Object FindComponentObject(Type componentType)
+ {
+ foreach (var obj in LoadAllComponentObjects())
+ {
+ if (IsComponentOfType(obj, componentType))
+ return obj;
+ }
+
+ return null;
+ }
+
+ void ThrowIfComponentExists(Type componentType)
+ {
+ if (HasComponentOfType(componentType))
+ throw new ArgumentException($"The component {componentType.Name} already exists in the build profile {name}.");
+ }
+
+ static void ThrowIfNotOnDisk(ScriptableObject reference)
+ {
+ if (reference != null && !AssetDatabase.Contains(reference))
+ throw new InvalidOperationException($"Expected on-disk asset for {reference}.");
+ }
+
+ ReferencedComponent CreateReferenceProxy(Type componentType, ScriptableObject reference)
+ {
+ var proxy = ScriptableObject.CreateInstance();
+ proxy.hideFlags = HideFlags.HideInHierarchy;
+ proxy.name = componentType.Name;
+ proxy.reference = reference;
+ proxy.referenceType = componentType;
+ AssetDatabase.AddObjectToAsset(proxy, this);
+ return proxy;
+ }
+
+ ReferencedComponent FindReferenceProxy(Type componentType)
+ {
+ foreach (var obj in LoadAllComponentObjects())
+ {
+ if (obj is ReferencedComponent proxy && proxy.Matches(componentType))
+ return proxy;
+ }
+
+ return null;
+ }
+
+ bool HasComponentOfType(Type componentType)
+ {
+ if (componentType == typeof(PlayerSettings))
+ return m_PlayerSettings != null || s_GlobalPlayerSettings != null;
+
+ // The field is set before the sub-asset is attached, e.g. while the profile isn't saved yet.
+ if (componentType == typeof(BuildProfilePlayerSettings))
+ return buildProfilePlayerSettings != null;
+
+ return FindComponentObject(componentType) is not null;
+ }
+
+ ///
+ /// Returns true when a component of type T is present, including an unassigned reference
+ /// whose source has not been set.
+ ///
+ internal bool HasComponent() where T : UnityEngine.Object => HasComponentOfType(typeof(T));
+
+ static bool IsComponentOfType(UnityEngine.Object obj, Type componentType) =>
+ componentType.IsInstanceOfType(obj) || (obj is ReferencedComponent proxy && proxy.Matches(componentType));
}
}
diff --git a/Editor/Mono/BuildProfile/BuildProfile.Create.cs b/Editor/Mono/BuildProfile/BuildProfile.Create.cs
index e933bac814..d2322150b7 100644
--- a/Editor/Mono/BuildProfile/BuildProfile.Create.cs
+++ b/Editor/Mono/BuildProfile/BuildProfile.Create.cs
@@ -134,7 +134,7 @@ public static BuildProfile CreateBuildProfile(
BuildProfileModuleUtil.EnsureCustomBuildProfileFolderExists();
string assetPath = BuildProfileModuleUtil.GetProfilePathWithProvidedName(platformId, profileName);
- var packagesToInstall = BuildTargetDiscovery.GetAllMissingRequiredPlatformPackageNames(platformId);
+ var packagesToInstall = BuildTargetDiscovery.GetAllMissingRequiredPlatformPackageIdentifiers(platformId);
return CreateInstance(platformId, assetPath, -1, packagesToInstall, onProfileReady);
}
@@ -146,7 +146,7 @@ public static BuildProfile CreateBuildProfile(
[VisibleToOtherModules("UnityEditor.BuildProfileModule")]
internal static BuildProfile CreateInstance(GUID platformId, string assetPath)
{
- return CreateInstance(platformId, assetPath, -1, Array.Empty());
+ return CreateInstance(platformId, assetPath, -1, Array.Empty());
}
[VisibleToOtherModules("UnityEditor.BuildProfileModule")]
@@ -154,7 +154,7 @@ internal static BuildProfile CreateInstance(
GUID platformId,
string assetPath,
int preconfiguredSettingsVariant,
- string[] packagesToAdd,
+ BuildTargetDiscovery.PlatformPackageIdentifier[] packagesToAdd,
UnityAction onProfileReady = null,
GUID selectedPlatformGuid = default)
{
diff --git a/Editor/Mono/BuildProfile/BuildProfile.cs b/Editor/Mono/BuildProfile/BuildProfile.cs
index 6f4cf0caad..82b43bd2cb 100644
--- a/Editor/Mono/BuildProfile/BuildProfile.cs
+++ b/Editor/Mono/BuildProfile/BuildProfile.cs
@@ -341,31 +341,6 @@ internal string GetLastRunnableBuildPathKey()
return BuildProfileModuleUtil.GetLastRunnableBuildKeyFromAssetPath(assetPath, key);
}
- [VisibleToOtherModules]
- internal void ResetToGlobalQualitySettingsValues()
- {
- var namedBuildTargetName = NamedBuildTarget.FromTargetAndSubtarget(buildTarget, (int)subtarget).TargetName;
- var globalQualityLevels = QualitySettings.GetActiveQualityLevelsForPlatform(namedBuildTargetName);
-
- var newBuildProfileQualityLevels = new string[globalQualityLevels.Length];
-
- // populates newBuildProfileQualityLevels array with global quality levels in their existing order
- for (int i = 0; i < globalQualityLevels.Length; i++)
- {
- newBuildProfileQualityLevels[i] = QualitySettings.names[globalQualityLevels[i]];
- }
-
- var globalDefaultQualityLevelIndex = QualitySettings.GetDefaultQualityForPlatform(namedBuildTargetName);
- if (globalDefaultQualityLevelIndex != -1)
- qualitySettings.defaultQualityLevel = QualitySettings.names[globalDefaultQualityLevelIndex];
- else
- qualitySettings.defaultQualityLevel = newBuildProfileQualityLevels.Length > 0 ? QualitySettings.names[globalQualityLevels[0]] : string.Empty;
-
- qualitySettings.qualityLevels = newBuildProfileQualityLevels;
-
- EditorUtility.SetDirty(qualitySettings);
- }
-
void OnEnable()
{
isMultiTarget = BuildTargetDiscovery.BuildPlatformIsMultiTargetPlatform(platformGuid);
@@ -391,12 +366,13 @@ void OnEnable()
// On disk changes invoke OnEnable,
// Check against the last observed editor defines.
string[] lastCompiledDefines = BuildProfileContext.instance.cachedEditorScriptingDefines;
+ string[] lastCompiledSDKPlatformDefines = BuildProfileContext.instance.cachedSDKPlatformDefines.defines;
m_ScriptingDefines = BuildProfileModuleUtil.RemoveInvalidScriptingDefines(m_ScriptingDefines);
- if (ArrayUtility.ArrayEquals(m_ScriptingDefines, lastCompiledDefines))
+ if (ArrayUtility.ArrayEquals(m_ScriptingDefines, lastCompiledDefines) &&
+ ArrayUtility.ArrayEquals(SDKPlatformCompilationUtil.GetActivePlatformEditorDefines(), lastCompiledSDKPlatformDefines))
{
return;
}
- BuildProfileContext.instance.cachedEditorScriptingDefines = m_ScriptingDefines;
BuildProfileModuleUtil.RequestScriptCompilation(this);
}
diff --git a/Editor/Mono/BuildProfile/BuildProfileContext.cs b/Editor/Mono/BuildProfile/BuildProfileContext.cs
index 1ef316fcdb..d3ea98ffd8 100644
--- a/Editor/Mono/BuildProfile/BuildProfileContext.cs
+++ b/Editor/Mono/BuildProfile/BuildProfileContext.cs
@@ -34,6 +34,9 @@ internal sealed partial class BuildProfileContext : ScriptableObject
const string k_BuildProfilePath = "Library/BuildProfiles";
const string k_SharedProfilePath = $"{k_BuildProfilePath}/SharedProfile.asset";
[AutoStaticsCleanupOnCodeReload]
+ // Singleton cache: the instance getter loads or creates the context when this is null, and the
+ // constructor assigns it back when the ScriptableObject is reconstructed.
+ [IgnoreForUAL0015("Singleton cache reloaded or recreated on demand by the instance getter")]
static BuildProfileContext s_Instance;
bool m_Initializing;
@@ -49,6 +52,9 @@ internal sealed partial class BuildProfileContext : ScriptableObject
[SerializeField]
string[] m_CachedEditorScriptingDefines = Array.Empty();
+ [SerializeField]
+ CachedSDKPlatformDefines m_CachedSDKPlatformDefines = new() { platformGuid = string.Empty, defines = Array.Empty() };
+
[SerializeField]
List m_LastRunnableBuildPathKeys = new();
internal List LastRunnableBuildPathKeys => m_LastRunnableBuildPathKeys;
@@ -70,6 +76,19 @@ internal string[] cachedEditorScriptingDefines
set => m_CachedEditorScriptingDefines = value;
}
+ ///
+ /// Cached SDK platform defines for the active profile.
+ /// On editor startup, scripts compile before SDK platforms are registered
+ /// from their packages, so the cached value supplies their defines.
+ /// On disk changes to build profile asset, cached value is referenced
+ /// when determining if a recompilation is required.
+ ///
+ internal CachedSDKPlatformDefines cachedSDKPlatformDefines
+ {
+ get => m_CachedSDKPlatformDefines;
+ set => m_CachedSDKPlatformDefines = value;
+ }
+
///
/// Specifies the custom build profile used by the build pipeline and editor APIs when getting build settings.
///
@@ -97,6 +116,7 @@ internal static BuildProfile activeProfile
{
var prev = EditorUserBuildSettings.activeBuildProfile;
var prevDefines = BuildDefines.GetBuildProfileScriptDefines();
+ var prevSDKPlatformDefines = SDKPlatformCompilationUtil.GetActivePlatformEditorDefines();
if (value == null || value.platformBuildProfile == null)
{
@@ -108,7 +128,9 @@ internal static BuildProfile activeProfile
EditorGraphicsSettings.activeProfileHasGraphicsSettings = false;
var defines = BuildDefines.GetBuildProfileScriptDefines();
- if (!ArrayUtility.ArrayEquals(prevDefines, defines))
+ var sdkPlatformDefines = SDKPlatformCompilationUtil.GetActivePlatformEditorDefines();
+ if (!ArrayUtility.ArrayEquals(prevDefines, defines) ||
+ !ArrayUtility.ArrayEquals(prevSDKPlatformDefines, sdkPlatformDefines))
BuildProfileModuleUtil.RequestScriptCompilation(null);
return;
@@ -141,7 +163,8 @@ internal static BuildProfile activeProfile
EditorGraphicsSettings.activeProfileHasGraphicsSettings = ActiveProfileHasGraphicsSettings();
value.scriptingDefines = BuildProfileModuleUtil.RemoveInvalidScriptingDefines(value.scriptingDefines);
- if (!ArrayUtility.ArrayEquals(prevDefines, value.scriptingDefines))
+ if (!ArrayUtility.ArrayEquals(prevDefines, value.scriptingDefines) ||
+ !ArrayUtility.ArrayEquals(prevSDKPlatformDefines, SDKPlatformCompilationUtil.GetActivePlatformEditorDefines()))
BuildProfileModuleUtil.RequestScriptCompilation(value);
}
}
@@ -326,7 +349,7 @@ internal BuildProfile GetForClassicPlatform(GUID platformGuid)
internal void RegisterProfileAwaitingInitialization(
BuildProfile profile,
- string[] packagesToAdd,
+ BuildTargetDiscovery.PlatformPackageIdentifier[] packagesToAdd,
int preconfiguredSettingsVariant,
UnityAction onProfileCreated)
{
@@ -558,6 +581,7 @@ void InitializeInstance()
EditorGraphicsSettings.activeProfileHasGraphicsSettings = ActiveProfileHasGraphicsSettings();
SyncClassicPlatformToEditorUserBuildSettings();
+ EngineDiagnosticsSettings.NotifyActiveBuildProfileChanged();
}
finally
{
@@ -745,7 +769,7 @@ BuildProfile GetOrCreateSharedBuildProfile()
return buildProfile;
}
- static void SaveBuildProfileInProject(BuildProfile profile)
+ internal static void SaveBuildProfileInProject(BuildProfile profile)
{
if (!Directory.Exists(k_BuildProfilePath))
{
@@ -845,6 +869,7 @@ static bool SetActiveCommonGraphicsSetting(string settingName, int value)
return false;
activeProfile.graphicsSettings.SetGraphicsSetting(settingName, value);
+ EditorUtility.SetDirty(activeProfile.graphicsSettings);
return true;
}
@@ -864,6 +889,7 @@ static bool SetActiveAlwaysIncludedShaders(Shader[] shaders)
return false;
activeProfile.graphicsSettings.alwaysIncludedShaders = shaders;
+ EditorUtility.SetDirty(activeProfile.graphicsSettings);
return true;
}
@@ -928,6 +954,7 @@ static bool SetActiveShaderVariantCollections(ShaderVariantCollection[] collecti
return false;
activeProfile.graphicsSettings.preloadedShaders = collection;
+ EditorUtility.SetDirty(activeProfile.graphicsSettings);
return true;
}
@@ -949,36 +976,46 @@ static string GetActiveProfileDefaultQualityLevel()
return activeProfile.qualitySettings.defaultQualityLevel;
}
+ // Every field of the struct has to travel through here, or a native write drops the ones left
+ // out: the settings are rebuilt from these arguments, not merged into the existing ones.
+ // Counts and enums cross as int, which is what the proxy generator supports for by-ref values.
[RequiredByNativeCode, UsedImplicitly]
- static bool SetActiveShaderBuildSettings(ShaderBuildSettings.KeywordDeclarationOverride[] keywordDeclarationOverrides, string[] defines, ShaderBuildSettings.ShaderCompilerSettings[] compilerSettings)
+ static bool SetActiveShaderBuildSettings(ShaderBuildSettings.KeywordDeclarationOverride[] keywordDeclarationOverrides, string[] defines, int numInternalDefines, ShaderBuildSettings.ShaderCompilerSettings[] compilerSettings, int fastBuildMode)
{
if (!ActiveProfileHasGraphicsSettings())
return false;
- activeProfile.graphicsSettings.shaderBuildSettings = new ShaderBuildSettings
+ activeProfile.graphicsSettings.SetShaderBuildSettings_Internal(new ShaderBuildSettings
{
keywordDeclarationOverrides = keywordDeclarationOverrides,
defines = defines,
- compilerSettings = compilerSettings
- };
+ numInternalDefines = (uint)numInternalDefines,
+ compilerSettings = compilerSettings,
+ fastBuildMode = (ShaderBuildSettings.FastBuildMode)fastBuildMode
+ });
+ EditorUtility.SetDirty(activeProfile.graphicsSettings);
return true;
}
[RequiredByNativeCode, UsedImplicitly]
- static void GetActiveShaderBuildSettings(out ShaderBuildSettings.KeywordDeclarationOverride[] keywordDeclarationOverrides, out string[] defines, out ShaderBuildSettings.ShaderCompilerSettings[] compilerSettings)
+ static void GetActiveShaderBuildSettings(out ShaderBuildSettings.KeywordDeclarationOverride[] keywordDeclarationOverrides, out string[] defines, out int numInternalDefines, out ShaderBuildSettings.ShaderCompilerSettings[] compilerSettings, out int fastBuildMode)
{
if (!ActiveProfileHasGraphicsSettings())
{
keywordDeclarationOverrides = null;
defines = null;
+ numInternalDefines = 0;
compilerSettings = null;
+ fastBuildMode = (int)ShaderBuildSettings.FastBuildMode.Off;
return;
}
var shaderBuildSettings = activeProfile.graphicsSettings.shaderBuildSettings;
keywordDeclarationOverrides = shaderBuildSettings.keywordDeclarationOverrides;
defines = shaderBuildSettings.defines;
+ numInternalDefines = (int)shaderBuildSettings.numInternalDefines;
compilerSettings = shaderBuildSettings.compilerSettings;
+ fastBuildMode = (int)shaderBuildSettings.fastBuildMode;
}
// Lets native code check whether its shader build settings snapshot is stale without marshalling the data.
@@ -1032,13 +1069,6 @@ static bool HasActiveProfileWithPlayerSettings(out EntityId entityId)
}
- [RequiredByNativeCode]
- [VisibleToOtherModules]
- internal static BuildProfileEngineDiagnosticsState GetActiveBuildProfileEngineDiagnosticsState()
- {
- var profile = activeProfile ?? instance.GetForClassicPlatform(EditorUserBuildSettings.activePlatformGuid);
- return profile?.platformBuildProfile?.insightsSettingsContainer.buildProfileEngineDiagnosticsState ?? BuildProfileEngineDiagnosticsState.ProjectSettings;
- }
static bool ShouldReturnActiveProfile(GUID platformGuid, string sharedSetting = null)
{
diff --git a/Editor/Mono/BuildProfile/BuildProfileEntitlementClaimInfo.cs b/Editor/Mono/BuildProfile/BuildProfileEntitlementClaimInfo.cs
new file mode 100644
index 0000000000..6eca386805
--- /dev/null
+++ b/Editor/Mono/BuildProfile/BuildProfileEntitlementClaimInfo.cs
@@ -0,0 +1,402 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using UnityEditor.Connect;
+using UnityEditor.Marketplace;
+using UnityEngine;
+using UnityEngine.Bindings;
+
+namespace UnityEditor.Build.Profile
+{
+ ///
+ /// Asset Store entitlement claim tracker for build profile initialization. Claims run before the
+ /// package install request, so a package gated behind an entitlement the user does not hold can
+ /// still be installed.
+ ///
+ [Serializable]
+ [VisibleToOtherModules("UnityEditor.BuildProfileModule")]
+ class BuildProfileEntitlementClaimInfo
+ {
+ public Action OnClaimRequestComplete;
+ public enum Status
+ {
+ Pending,
+ Processing,
+ Granted,
+ Failed,
+ }
+
+ public enum Failure
+ {
+ None,
+
+ ///
+ /// The product endpoint has no product for the package. Expected for packages
+ /// that are not Asset Store products or is missing from the endpoint's whitelist.
+ ///
+ NoProduct,
+
+ Rejected,
+ ServiceError,
+ Transport,
+ Timeout,
+ NoOrganization,
+ }
+
+ [Serializable]
+ public class Claim
+ {
+ public string packageName;
+
+ ///
+ /// Marketplace product ID to claim against. Empty until the entry is resolved.
+ ///
+ public string productId;
+
+ ///
+ /// Whether the package is classified (e.g., Asset Store vs. registry package).
+ ///
+ public bool isClassified;
+
+ public Status status;
+ public Failure failure;
+ public string detail;
+ }
+
+ const int k_SessionTimeoutSeconds = 90;
+ const int k_RetryDelaySeconds = 1;
+ const int k_AttemptTimeoutSeconds = 30;
+
+ ///
+ /// Claims to make, one per package.
+ ///
+ [SerializeField]
+ public Claim[] claims = Array.Empty();
+
+ ///
+ /// Wall clock tick the claim session expires at.
+ ///
+ [SerializeField]
+ internal long m_SessionTicks;
+
+ internal long m_NextAttemptTicks;
+
+ Task m_InFlightClaim;
+ internal Task inFlightClaim => m_InFlightClaim;
+ CancellationTokenSource m_Cancellation;
+
+ bool m_CallbacksSubscribed;
+
+ IPackageServiceInfoProvider m_Provider;
+
+ ///
+ /// Only a test-injected provider is stored. The shared one is read through on every access,
+ /// because UAL0018 forbids capturing it into a field.
+ ///
+ internal IPackageServiceInfoProvider provider
+ {
+ get => m_Provider ?? BuildProfileContext.packageServiceInfoProvider;
+ set => m_Provider = value;
+ }
+
+ internal static BuildProfileEntitlementClaimInfo Create(
+ BuildTargetDiscovery.PlatformPackageIdentifier[] packagesToAdd,
+ IPackageServiceInfoProvider providerOverride = null)
+ {
+ var provider = providerOverride ?? BuildProfileContext.packageServiceInfoProvider;
+ var claims = new List();
+
+ foreach (var package in packagesToAdd)
+ {
+ var packageName = package.name;
+
+ if (!provider.HasPackageInfo(packageName))
+ {
+ claims.Add(new Claim { packageName = packageName });
+ continue;
+ }
+
+ if (!provider.IsAssetStorePackage(packageName))
+ continue;
+
+ if (provider.HasMarketplaceEntitlement(packageName) == true)
+ continue;
+
+ claims.Add(new Claim
+ {
+ packageName = packageName,
+ productId = provider.GetMarketplaceProductId(packageName),
+ isClassified = true,
+ });
+ }
+
+ return claims.Count == 0
+ ? null
+ : new BuildProfileEntitlementClaimInfo { claims = claims.ToArray(), provider = providerOverride };
+ }
+
+ ///
+ /// Begins or resumes claiming entitlements.
+ ///
+ public void RequestEntitlementClaims()
+ {
+ if (m_CallbacksSubscribed || IsClaimRequestDone())
+ return;
+
+ if (m_SessionTicks == 0)
+ m_SessionTicks = DateTime.UtcNow.Ticks + TimeSpan.FromSeconds(k_SessionTimeoutSeconds).Ticks;
+
+ EditorApplication.update += ProcessClaims;
+ AssemblyReloadEvents.beforeAssemblyReload += UnsubscribeRequestCallbacks;
+ m_CallbacksSubscribed = true;
+ }
+
+ ///
+ /// Check if all claim requests are completed.
+ ///
+ public bool IsClaimRequestDone() => NextOutstandingClaim() == null;
+
+ ///
+ /// Whether the package is known to be installable: the entitlement is held,
+ /// the package is not an Asset Store product and so needs none, or it was never
+ /// classified because the lookup that would have classified it failed.
+ ///
+ public bool IsPackageInstallable(string packageName)
+ {
+ foreach (var claim in claims)
+ {
+ if (claim.packageName != packageName)
+ continue;
+
+ if (claim.status == Status.Granted)
+ return true;
+
+ if (claim.status != Status.Failed)
+ return false;
+
+ return claim.failure == Failure.NoProduct || !claim.isClassified;
+ }
+
+ return true;
+ }
+
+ public void Cleanup()
+ {
+ UnsubscribeRequestCallbacks();
+ OnClaimRequestComplete = null;
+ }
+
+ ///
+ /// Detaches the editor update loop and the domain-reload hook, and abandons any request in
+ /// flight.
+ ///
+ void UnsubscribeRequestCallbacks()
+ {
+ if (!m_CallbacksSubscribed)
+ return;
+
+ m_CallbacksSubscribed = false;
+ AssemblyReloadEvents.beforeAssemblyReload -= UnsubscribeRequestCallbacks;
+ EditorApplication.update -= ProcessClaims;
+ m_Cancellation?.Cancel();
+ }
+
+ internal void ProcessClaims()
+ {
+ var claim = NextOutstandingClaim();
+ if (claim == null)
+ {
+ FinishClaimRequest();
+ return;
+ }
+
+ // Checked before the deadline, so it cannot fail an entry a running request is about to
+ // resolve.
+ if (m_InFlightClaim != null)
+ return;
+
+ var now = DateTime.UtcNow.Ticks;
+ if (now >= m_SessionTicks)
+ {
+ FailOutstandingClaims(Failure.Timeout);
+ FinishClaimRequest();
+ return;
+ }
+
+ if (now < m_NextAttemptTicks)
+ return;
+
+ var attempt = AttemptClaimAsync(claim);
+ if (!attempt.IsCompleted)
+ m_InFlightClaim = attempt;
+ }
+
+ void FinishClaimRequest()
+ {
+ UnsubscribeRequestCallbacks();
+ OnClaimRequestComplete?.Invoke();
+ }
+
+ async Task AttemptClaimAsync(Claim claim)
+ {
+ m_Cancellation = new CancellationTokenSource();
+ m_Cancellation.CancelAfter(TimeSpan.FromSeconds(k_AttemptTimeoutSeconds));
+
+ var isClaimAttempt = !string.IsNullOrEmpty(claim.productId);
+
+ try
+ {
+ if (isClaimAttempt)
+ await PostClaimAsync(claim, m_Cancellation.Token);
+ else
+ await ResolveProductIdAsync(claim, m_Cancellation.Token);
+ }
+ catch (OperationCanceledException)
+ {
+ // Timed out, or aborted by a domain reload. Either way the entry is untouched, so the
+ // next tick retries it until the deadline.
+ }
+ catch (NoOrganizationIdException exception)
+ {
+ FailOutstandingClaims(Failure.NoOrganization);
+ Debug.LogError("Could not claim the Asset Store entitlements: " + exception.Message);
+ FinishClaimRequest();
+ }
+ catch (MarketplaceApiException exception)
+ {
+ // Only a claim the service answered can be a refusal. A failed product lookup is the
+ // metadata call and says nothing about entitlement.
+ SetClaimFailed(claim, isClaimAttempt && exception.error.status < 500 ? Failure.Rejected : Failure.ServiceError);
+ claim.detail = exception.Message;
+ LogClaimFailure(claim);
+ }
+ catch (UnityConnectWebRequestException exception)
+ {
+ SetClaimFailed(claim, Failure.Transport);
+ claim.detail = exception.Message;
+ LogClaimFailure(claim);
+ }
+ catch (Exception exception)
+ {
+ SetClaimFailed(claim, Failure.ServiceError);
+ claim.detail = exception.Message;
+ LogClaimFailure(claim);
+ }
+ finally
+ {
+ m_Cancellation?.Dispose();
+ m_Cancellation = null;
+ m_InFlightClaim = null;
+ }
+ }
+
+ async Task ResolveProductIdAsync(Claim claim, CancellationToken cancellationToken)
+ {
+ var productInfo = await AwaitOrAbandon(
+ provider.GetOrFetchProductInfoAsync(claim.packageName), cancellationToken);
+
+ claim.isClassified = true;
+
+ if (productInfo == null || string.IsNullOrEmpty(productInfo.productId))
+ {
+ SetClaimFailed(claim, Failure.NoProduct);
+ return;
+ }
+
+ if (productInfo.entitlement?.hasEntitlement == true)
+ {
+ claim.status = Status.Granted;
+ return;
+ }
+
+ claim.productId = productInfo.productId;
+ }
+
+ async Task PostClaimAsync(Claim claim, CancellationToken cancellationToken)
+ {
+ var result = await AwaitOrAbandon(
+ provider.ClaimEntitlementAsync(claim.productId), cancellationToken);
+
+ switch (result.status)
+ {
+ case ClaimStatus.Granted:
+ claim.status = Status.Granted;
+ break;
+
+ case ClaimStatus.Processing:
+ claim.status = Status.Processing;
+ m_NextAttemptTicks = DateTime.UtcNow.Ticks + TimeSpan.FromSeconds(k_RetryDelaySeconds).Ticks;
+ break;
+
+ default:
+ SetClaimFailed(claim, Failure.ServiceError);
+ claim.detail = "The claim service answered with an unrecognized status.";
+ LogClaimFailure(claim);
+ break;
+ }
+ }
+
+ ///
+ /// Awaits a gateway request, or abandons it when cancelled. Those requests carry no cancellation
+ /// token by design, because one may be shared with another caller. Hence, cancel here means
+ /// leaving the request to finish for whoever else wants it.
+ ///
+ static async Task AwaitOrAbandon(Task request, CancellationToken cancellationToken)
+ {
+ var cancelled = Task.Delay(Timeout.Infinite, cancellationToken);
+ if (await Task.WhenAny(request, cancelled) != request)
+ {
+ // Once abandoned, nobody may be left to observe a failure on it.
+ _ = request.ContinueWith(t => { _ = t.Exception; }, TaskContinuationOptions.OnlyOnFaulted);
+ throw new OperationCanceledException(cancellationToken);
+ }
+
+ return await request;
+ }
+
+ static bool IsOutstanding(Claim claim) => claim.status == Status.Pending || claim.status == Status.Processing;
+
+ Claim NextOutstandingClaim()
+ {
+ foreach (var claim in claims)
+ {
+ if (IsOutstanding(claim))
+ return claim;
+ }
+
+ return null;
+ }
+
+ void FailOutstandingClaims(Failure failure)
+ {
+ foreach (var claim in claims)
+ {
+ if (!IsOutstanding(claim))
+ continue;
+
+ SetClaimFailed(claim, failure);
+ LogClaimFailure(claim);
+ }
+ }
+
+ static void SetClaimFailed(Claim claim, Failure failure)
+ {
+ claim.status = Status.Failed;
+ claim.failure = failure;
+ }
+
+ static void LogClaimFailure(Claim claim)
+ {
+ if (claim.failure == Failure.NoProduct || claim.failure == Failure.NoOrganization || !claim.isClassified)
+ return;
+
+ var reason = string.IsNullOrEmpty(claim.detail) ? claim.failure.ToString() : claim.detail;
+ Debug.LogError($"Could not claim the Asset Store entitlement for '{claim.packageName}': {reason}");
+ }
+ }
+}
diff --git a/Editor/Mono/BuildProfile/BuildProfileGraphicsSettings.cs b/Editor/Mono/BuildProfile/BuildProfileGraphicsSettings.cs
index e925b47139..b73d047dbe 100644
--- a/Editor/Mono/BuildProfile/BuildProfileGraphicsSettings.cs
+++ b/Editor/Mono/BuildProfile/BuildProfileGraphicsSettings.cs
@@ -13,10 +13,17 @@
[assembly: InternalsVisibleTo("UnityEditor.Rendering.ShaderBuildSettings.Tests")]
namespace UnityEditor.Build.Profile
{
+ ///
+ /// A build profile component that overrides the global graphics settings.
+ ///
+ [ExcludeFromObjectFactory]
+ [ExcludeFromPreset]
[VisibleToOtherModules("UnityEditor.BuildProfileModule")]
- sealed class BuildProfileGraphicsSettings : ScriptableObject
+ public sealed class BuildProfileGraphicsSettings : ScriptableObject
{
internal const int k_InvalidGraphicsSetting = -2;
+ const string k_LastDefaultPropertyPath = "m_EditorClassIdentifier";
+ const string k_ShaderBuildSettingsTypeName = "ShaderBuildSettings";
[SerializeField] StrippingModes m_LightmapStripping = StrippingModes.Automatic;
[SerializeField] bool m_LightmapKeepPlain = true;
@@ -50,6 +57,10 @@ sealed class BuildProfileGraphicsSettings : ScriptableObject
[SerializeField] int m_PreloadShadersBatchTimeLimit = -1;
[SerializeField] ShaderBuildSettings m_ShaderBuildSettings;
+ internal BuildProfileGraphicsSettings()
+ {
+ }
+
internal StrippingModes lightmapStripping
{
get => m_LightmapStripping;
@@ -128,28 +139,56 @@ internal BatchRendererGroupStrippingMode brgStripping
set => m_BrgStripping = value;
}
- internal bool logWhenShaderIsCompiled
+ ///
+ /// Whether Unity adds a log entry each time it compiles a shader at runtime.
+ ///
+ public bool logWhenShaderIsCompiled
{
get => m_LogWhenShaderIsCompiled;
- set => m_LogWhenShaderIsCompiled = value;
+ set
+ {
+ m_LogWhenShaderIsCompiled = value;
+ EditorUtility.SetDirty(this);
+ }
}
- internal bool cameraRelativeLightCulling
+ ///
+ /// Whether Unity uses the camera position instead of the world space origin as the reference point for culling lights.
+ ///
+ public bool cameraRelativeLightCulling
{
get => m_CameraRelativeLightCulling;
- set => m_CameraRelativeLightCulling = value;
+ set
+ {
+ m_CameraRelativeLightCulling = value;
+ EditorUtility.SetDirty(this);
+ }
}
- internal bool cameraRelativeShadowCulling
+ ///
+ /// Whether Unity uses the camera position instead of the world space origin as the reference point for culling shadows.
+ ///
+ public bool cameraRelativeShadowCulling
{
get => m_CameraRelativeShadowCulling;
- set => m_CameraRelativeShadowCulling = value;
+ set
+ {
+ m_CameraRelativeShadowCulling = value;
+ EditorUtility.SetDirty(this);
+ }
}
- internal VideoShadersIncludeMode videoShadersIncludeMode
+ ///
+ /// Whether and when Unity includes video shaders in the build.
+ ///
+ public VideoShadersIncludeMode videoShadersIncludeMode
{
get => m_VideoShadersIncludeMode;
- set => m_VideoShadersIncludeMode = value;
+ set
+ {
+ m_VideoShadersIncludeMode = value;
+ EditorUtility.SetDirty(this);
+ }
}
internal Shader[] alwaysIncludedShaders
@@ -158,10 +197,17 @@ internal Shader[] alwaysIncludedShaders
set => m_AlwaysIncludedShaders = value;
}
- internal LightProbeOutsideHullStrategy lightProbeOutsideHullStrategy
+ ///
+ /// How Unity lights a Renderer that uses Light Probes but is outside the Light Probe tetrahedral hull.
+ ///
+ public LightProbeOutsideHullStrategy lightProbeOutsideHullStrategy
{
get => m_LightProbeOutsideHullStrategy;
- set => m_LightProbeOutsideHullStrategy = value;
+ set
+ {
+ m_LightProbeOutsideHullStrategy = value;
+ EditorUtility.SetDirty(this);
+ }
}
internal GraphicsStateCollection graphicsStateCollection
@@ -229,19 +275,40 @@ internal int preloadShadersBatchTimeLimit
set => m_PreloadShadersBatchTimeLimit = value;
}
- internal ShaderBuildSettings shaderBuildSettings
+ ///
+ /// The shader build settings, such as shader defines and keyword declaration overrides.
+ ///
+ public ShaderBuildSettings shaderBuildSettings
{
- get => m_ShaderBuildSettings;
- set => m_ShaderBuildSettings = value;
+ get => m_ShaderBuildSettings.DeepCopy();
+ set
+ {
+ var settings = EditorGraphicsSettings.SanitizeCompilerSettings(value.DeepCopy(), nameof(shaderBuildSettings));
+ var changed = !m_ShaderBuildSettings.ValueEquals(settings);
+ m_ShaderBuildSettings = settings;
+ EditorUtility.SetDirty(this);
+
+ var activeProfile = BuildProfileContext.activeProfile;
+ if (activeProfile == null || activeProfile.graphicsSettings != this)
+ return;
+
+ ShaderBuildSettings.CheckKeywordDeclarationOverridesHaveMatches(settings.keywordDeclarationOverrides);
+
+ if (changed)
+ AssetDatabase.Refresh();
+ }
}
- public void Instantiate()
+ // For values that EditorGraphicsSettings.SetShaderBuildSettings already sanitized.
+ internal void SetShaderBuildSettings_Internal(ShaderBuildSettings settings) => m_ShaderBuildSettings = settings;
+
+ internal void Instantiate()
{
name = "Graphics Settings";
hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector;
}
- public void SetGraphicsSetting(string settingName, int value)
+ internal void SetGraphicsSetting(string settingName, int value)
{
switch (settingName)
{
@@ -325,7 +392,7 @@ public void SetGraphicsSetting(string settingName, int value)
static bool IntegerToBoolean(int value) => value != 0;
}
- public int GetGraphicsSetting(string settingName)
+ internal int GetGraphicsSetting(string settingName)
{
return settingName switch
{
@@ -359,5 +426,38 @@ public int GetGraphicsSetting(string settingName)
static int BooleanToInteger(bool value) => value ? 1 : 0;
}
+
+ [VisibleToOtherModules("UnityEditor.BuildProfileModule")]
+ internal void ResetToGlobalValues()
+ {
+ using var serializedObject = new SerializedObject(this);
+ var globalGraphicsSettings = GraphicsSettings.GetGraphicsSettings();
+ using var globalGraphicsSettingsSO = new SerializedObject(globalGraphicsSettings);
+ var profileSerializedProperty = serializedObject.FindProperty(k_LastDefaultPropertyPath);
+
+ while (profileSerializedProperty.Next(false))
+ {
+ var globalSerializedProperty = globalGraphicsSettingsSO.FindProperty(profileSerializedProperty.name);
+ if (profileSerializedProperty.isArray)
+ {
+ profileSerializedProperty.arraySize = globalSerializedProperty.arraySize;
+ for (int i = 0; i < profileSerializedProperty.arraySize; i++)
+ {
+ var profileArrayElement = profileSerializedProperty.GetArrayElementAtIndex(i);
+ var globalArrayElement = globalSerializedProperty.GetArrayElementAtIndex(i);
+ profileArrayElement.boxedValue = globalArrayElement.boxedValue;
+ }
+ }
+ else
+ {
+ profileSerializedProperty.boxedValue = profileSerializedProperty.type == k_ShaderBuildSettingsTypeName
+ ? EditorGraphicsSettings.GetShaderBuildSettings()
+ : globalSerializedProperty.boxedValue;
+ }
+ }
+
+ serializedObject.ApplyModifiedProperties();
+ EditorUtility.SetDirty(this);
+ }
}
}
diff --git a/Editor/Mono/BuildProfile/BuildProfileGraphicsSettingsEditor.cs b/Editor/Mono/BuildProfile/BuildProfileGraphicsSettingsEditor.cs
index 1509da72de..f55c09b4b1 100644
--- a/Editor/Mono/BuildProfile/BuildProfileGraphicsSettingsEditor.cs
+++ b/Editor/Mono/BuildProfile/BuildProfileGraphicsSettingsEditor.cs
@@ -19,8 +19,6 @@ class BuildProfileGraphicsSettingsEditor : Editor
{
const string k_Uxml = "BuildProfile/UXML/BuildProfileCommonGraphicsSettings.uxml";
const string k_StyleSheet = "BuildProfile/StyleSheets/BuildProfile.uss";
- const string k_LastDefaultPropertyPath = "m_EditorClassIdentifier";
- const string k_ShaderBuildSettingsTypeName = "ShaderBuildSettings";
ShaderBuildSettingsUI m_ShaderBuildSettingsUI = new();
EnumField m_DefaultMeshBufferTargetField;
@@ -210,35 +208,5 @@ static bool IsSubclassOfGeneric(Type genericType, Type typeToCheck) {
}
return false;
}
-
- public void ResetToGlobalGraphicsSettingsValues()
- {
- var globalGraphicsSettings = GraphicsSettings.GetGraphicsSettings();
- var globalGraphicsSettingsSO = new SerializedObject(globalGraphicsSettings);
- var profileSerializedProperty = serializedObject.FindProperty(k_LastDefaultPropertyPath);
-
- while (profileSerializedProperty.Next(false))
- {
- var globalSerializedProperty = globalGraphicsSettingsSO.FindProperty(profileSerializedProperty.name);
- if (profileSerializedProperty.isArray)
- {
- profileSerializedProperty.arraySize = globalSerializedProperty.arraySize;
- for (int i = 0; i < profileSerializedProperty.arraySize; i++)
- {
- var profileArrayElement = profileSerializedProperty.GetArrayElementAtIndex(i);
- var globalArrayElement = globalSerializedProperty.GetArrayElementAtIndex(i);
- profileArrayElement.boxedValue = globalArrayElement.boxedValue;
- }
- }
- else
- {
- profileSerializedProperty.boxedValue = profileSerializedProperty.type == k_ShaderBuildSettingsTypeName
- ? EditorGraphicsSettings.GetShaderBuildSettings()
- : globalSerializedProperty.boxedValue;
- }
- }
-
- serializedObject.ApplyModifiedProperties();
- }
}
}
diff --git a/Editor/Mono/BuildProfile/BuildProfileInitialization.cs b/Editor/Mono/BuildProfile/BuildProfileInitialization.cs
index c36b8a5dd4..da4600a2b8 100644
--- a/Editor/Mono/BuildProfile/BuildProfileInitialization.cs
+++ b/Editor/Mono/BuildProfile/BuildProfileInitialization.cs
@@ -3,6 +3,8 @@
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using System;
+using System.Collections.Generic;
+using UnityEditorInternal;
using UnityEngine;
using UnityEngine.Bindings;
using UnityEngine.Events;
@@ -56,6 +58,20 @@ public enum State
/// SDK platform extension) is pending.
///
AwaitingExtension,
+
+ ///
+ /// Asset Store entitlements are being claimed for packages that need one before they can
+ /// install. Entered ahead of , and skipped entirely when
+ /// there is nothing to claim.
+ ///
+ ClaimingEntitlements,
+
+ ///
+ /// The install request is done, and the editor is still registering, importing and
+ /// compiling the packages. Extensions may read package assets on creation, so this waits
+ /// until the packages are registered and the editor is idle again.
+ ///
+ AwaitingPackageRegistration,
}
///
@@ -76,6 +92,12 @@ public enum State
[SerializeField]
public BuildProfilePackageAddInfo packageAddInfo;
+ ///
+ /// Asset Store entitlement claim tracker. Null if nothing needs claiming.
+ ///
+ [SerializeField]
+ public BuildProfileEntitlementClaimInfo entitlementClaimInfo;
+
///
/// Optional callback invoked when the build profile is .
///
@@ -104,13 +126,16 @@ public enum State
[NonSerialized]
bool m_EnteredAwaitingExtensionThisSession;
+ [NonSerialized]
+ EditorApplication.CallbackFunction m_OnEditorUpdate;
+
///
/// Factory method to create a instance.
///
public static BuildProfileInitialization Create(
BuildProfile profile,
string profileGUID,
- string[] packagesToAdd,
+ BuildTargetDiscovery.PlatformPackageIdentifier[] packagesToAdd,
int preconfiguredSettingsVariant,
UnityAction onProfileReady)
{
@@ -126,6 +151,7 @@ public static BuildProfileInitialization Create(
{
packagesToAdd = packagesToAdd,
};
+ init.entitlementClaimInfo = BuildProfileEntitlementClaimInfo.Create(packagesToAdd);
}
if (onProfileReady != null)
@@ -194,6 +220,8 @@ public State OnState(BuildProfile profile)
{
if (RequiresDerivedPlatformActivation(profile))
NextState(State.AwaitingDomainReload, profile);
+ else if (entitlementClaimInfo != null)
+ NextState(State.ClaimingEntitlements, profile);
else if (packageAddInfo != null)
NextState(State.InstallingPackages, profile);
else if (!AreRequiredExtensionsAvailable(profile))
@@ -205,6 +233,25 @@ public State OnState(BuildProfile profile)
case State.AwaitingDomainReload:
if (!m_ReloadPending)
{
+ if (entitlementClaimInfo != null)
+ NextState(State.ClaimingEntitlements, profile);
+ else if (packageAddInfo != null)
+ NextState(State.InstallingPackages, profile);
+ else if (!AreRequiredExtensionsAvailable(profile))
+ NextState(State.AwaitingExtension, profile);
+ else
+ NextState(State.Ready, profile);
+ }
+ break;
+ case State.ClaimingEntitlements:
+ if (entitlementClaimInfo != null && !entitlementClaimInfo.IsClaimRequestDone())
+ {
+ AddEntitlementClaimCallbacks(profile);
+ entitlementClaimInfo.RequestEntitlementClaims();
+ }
+ else
+ {
+ DropPackagesWithoutEntitlements();
if (packageAddInfo != null)
NextState(State.InstallingPackages, profile);
else if (!AreRequiredExtensionsAvailable(profile))
@@ -217,7 +264,9 @@ public State OnState(BuildProfile profile)
if (packageAddInfo != null)
{
AddPackageInstallationCallbacks(profile);
- if (packageAddInfo.IsPackageRequestDone())
+ if (packageAddInfo.isRegistrationPending)
+ NextState(State.AwaitingPackageRegistration, profile);
+ else if (packageAddInfo.IsPackageRequestDone())
{
// Expect no packages left to install on profile enable.
// This ensures that any package compilation has finished.
@@ -232,6 +281,16 @@ public State OnState(BuildProfile profile)
NextState(State.InstallingError, profile);
}
break;
+ case State.AwaitingPackageRegistration:
+ if (IsPackageRegistrationDone(IsEditorBusy()))
+ {
+ RemovePackageRegistrationCallbacks();
+ profile.OnPackageAddComplete?.Invoke();
+ NextState(!AreRequiredExtensionsAvailable(profile) ? State.AwaitingExtension : State.Ready, profile);
+ }
+ else
+ AddPackageRegistrationCallbacks(profile);
+ break;
case State.InstallingError:
Debug.LogError(JsonUtility.ToJson(this.packageAddInfo?.GetPackageAddProgressInfo()));
if (!AreRequiredExtensionsAvailable(profile))
@@ -282,6 +341,27 @@ static bool RequiresDerivedPlatformActivation(BuildProfile profile)
return EditorPrefs.GetInt(platformId.ToString(), 0) == 0;
}
+ ///
+ /// Removes packages from the install request unless their entitlement is confirmed, or they
+ /// need none at all.
+ ///
+ void DropPackagesWithoutEntitlements()
+ {
+ if (packageAddInfo == null || entitlementClaimInfo == null)
+ return;
+
+ var packages = packageAddInfo.packagesToAdd;
+ var remaining = new List(packages.Length);
+ foreach (var package in packages)
+ {
+ if (entitlementClaimInfo.IsPackageInstallable(package.name))
+ remaining.Add(package);
+ }
+
+ if (remaining.Count != packages.Length)
+ packageAddInfo.packagesToAdd = remaining.ToArray();
+ }
+
///
/// Perform one-time actions upon entering a new state.
///
@@ -336,6 +416,51 @@ void AddPackageInstallationCallbacks(BuildProfile profile)
};
}
+ ///
+ /// Re-checks the package registration on editor updates, which resume once the editor has finished
+ /// the refresh that registers, imports and compiles the packages, also after an editor restart.
+ /// On domain reload these callbacks should be re-attached to ensure proper notification.
+ ///
+ void AddPackageRegistrationCallbacks(BuildProfile profile)
+ {
+ if (m_OnEditorUpdate != null)
+ return;
+
+ m_OnEditorUpdate = () =>
+ {
+ // The profile asset was deleted while waiting.
+ if (profile == null)
+ RemovePackageRegistrationCallbacks();
+ else if (IsPackageRegistrationDone(IsEditorBusy()))
+ BuildProfileContext.instance.UpdateBuildProfileInitialization(profile);
+ };
+ EditorApplication.update += m_OnEditorUpdate;
+ }
+
+ ///
+ /// Packages count as registered as soon as the editor resolves them, before it imports and
+ /// compiles them, so the registration is only done once the editor is idle again.
+ ///
+ public bool IsPackageRegistrationDone(bool isEditorBusy) =>
+ !isEditorBusy && (packageAddInfo == null || packageAddInfo.IsPackageRequestDone());
+
+ static bool IsEditorBusy() =>
+ EditorApplication.isCompiling || EditorApplication.isUpdating || InternalEditorUtility.IsScriptReloadRequested();
+
+ void RemovePackageRegistrationCallbacks()
+ {
+ EditorApplication.update -= m_OnEditorUpdate;
+ m_OnEditorUpdate = null;
+ }
+
+ ///
+ /// Sets up the entitlement claim completion callback. Re-attached on domain reload.
+ ///
+ void AddEntitlementClaimCallbacks(BuildProfile profile)
+ {
+ entitlementClaimInfo.OnClaimRequestComplete = () => BuildProfileContext.instance.UpdateBuildProfileInitialization(profile);
+ }
+
///
/// Cleanup internal event handlers and serialized references.
///
@@ -346,6 +471,11 @@ void Cleanup()
packageAddInfo?.Cleanup();
packageAddInfo = null;
+
+ entitlementClaimInfo?.Cleanup();
+ entitlementClaimInfo = null;
+
+ RemovePackageRegistrationCallbacks();
}
bool IsCallbackPersistable(UnityAction callback) => callback.Method.IsStatic || callback.Target is UnityEngine.Object;
diff --git a/Editor/Mono/BuildProfile/BuildProfileModuleUtil.cs b/Editor/Mono/BuildProfile/BuildProfileModuleUtil.cs
index 5effe850fa..0b632777c3 100644
--- a/Editor/Mono/BuildProfile/BuildProfileModuleUtil.cs
+++ b/Editor/Mono/BuildProfile/BuildProfileModuleUtil.cs
@@ -49,12 +49,12 @@ internal partial class BuildProfileModuleUtil
static readonly string k_DerivedPlatformDisabled = L10n.Tr("{0} was disabled via command-line arguments.", null);
static readonly string k_EditorWillNeedToBeReloaded = L10n.Tr("Note: Editor will need to be restarted to load any newly installed modules", null);
static readonly string k_BuildProfileRecompileReason = L10n.Tr("Active build profile scripting defines changes.", null);
- static readonly GUIContent k_OpenDownloadPage = EditorGUIUtility.TrTextContent("Open Download Page");
- static readonly GUIContent k_InstallModuleWithHub = EditorGUIUtility.TrTextContent("Install with Unity Hub");
+ static readonly GUIContent k_OpenDownloadPage = L10n.TextContent("Open Download Page", null, null, null);
+ static readonly GUIContent k_InstallModuleWithHub = L10n.TextContent("Install with Unity Hub", null, null, null);
static readonly string k_ModuleInstalled = L10n.Tr("{0} module has been installed.", null);
static readonly string k_RestartNeeded = L10n.Tr("Please restart the Unity Editor to load the module.", null);
static readonly string k_RestartEditor = L10n.Tr("Restart Unity Editor", null);
- static readonly GUIContent k_ActivateDerivedPlatform = EditorGUIUtility.TrTextContent("Enable Platform");
+ static readonly GUIContent k_ActivateDerivedPlatform = L10n.TextContent("Enable Platform", null, null, null);
[NoAutoStaticsCleanup] // static config set, allocated once, contains only string literals
static readonly HashSet s_BuildProfileIconModules = new()
{
@@ -405,10 +405,7 @@ public static bool IsStandalonePlatform(BuildTarget buildTarget) =>
///
public static bool IsBuildTargetSupportedByCoverage(BuildTarget buildTarget)
{
- if (!IsStandalonePlatform(buildTarget))
- return false;
-
- if (buildTarget == BuildTarget.StandaloneWindows)
+ if (!IsPlatformSupportedByCoverage(buildTarget))
return false;
var namedBuildTarget = NamedBuildTarget.FromActiveSettings(buildTarget);
@@ -417,6 +414,18 @@ public static bool IsBuildTargetSupportedByCoverage(BuildTarget buildTarget)
return scriptingBackend == ScriptingImplementation.Mono2x || scriptingBackend == ScriptingImplementation.IL2CPP;
}
+ ///
+ /// Returns true if the platform can produce coverage instrumented players,
+ /// regardless of the currently selected scripting backend.
+ ///
+ internal static bool IsPlatformSupportedByCoverage(BuildTarget buildTarget) => buildTarget switch
+ {
+ // 32-bit Windows standalone is not supported.
+ BuildTarget.StandaloneWindows => false,
+ BuildTarget.Android => true,
+ _ => IsStandalonePlatform(buildTarget)
+ };
+
public static bool IsBuildAutomationSupported(GUID platformGuid)
{
if (!BuildTargetDiscovery.TryGetBuildTarget(platformGuid, out var iBuildTarget))
@@ -702,18 +711,20 @@ public static void RemovePlayerSettings(BuildProfile buildProfile)
}
///
- /// Create graphics settings override for build profile
+ /// Create graphics settings override for build profile, initialized to the project's graphics settings.
///
public static void CreateGraphicsSettings(BuildProfile buildProfile)
{
if (buildProfile.graphicsSettings != null)
return;
- buildProfile.graphicsSettings = ScriptableObject.CreateInstance();
- buildProfile.graphicsSettings.Instantiate();
- AssetDatabase.AddObjectToAsset(buildProfile.graphicsSettings, buildProfile);
+ buildProfile.graphicsSettings = AddSettingsSubAsset(buildProfile, s => s.Instantiate());
+ buildProfile.graphicsSettings.ResetToGlobalValues();
EditorUtility.SetDirty(buildProfile);
UpdateActiveEditors(buildProfile);
+
+ if (buildProfile == BuildProfileContext.activeProfile)
+ OnActiveProfileGraphicsSettingsChanged(true);
}
///
@@ -724,23 +735,29 @@ public static void RemoveGraphicsSettings(BuildProfile buildProfile)
if (buildProfile.graphicsSettings == null)
return;
- AssetDatabase.RemoveObjectFromAsset(buildProfile.graphicsSettings);
+ var removed = buildProfile.graphicsSettings;
+ AssetDatabase.RemoveObjectFromAsset(removed);
buildProfile.graphicsSettings = null;
EditorUtility.SetDirty(buildProfile);
UpdateActiveEditors(buildProfile);
+
+ if (buildProfile == BuildProfileContext.activeProfile)
+ OnActiveProfileGraphicsSettingsChanged(false);
+
+ UnityEngine.Object.DestroyImmediate(removed, true);
}
///
- /// Create graphics settings quality for build profile
+ /// Create quality settings override for build profile, initialized to the project's quality levels
+ /// for the profile's platform.
///
public static void CreateQualitySettings(BuildProfile buildProfile)
{
if (buildProfile.qualitySettings != null)
return;
- buildProfile.qualitySettings = ScriptableObject.CreateInstance();
- buildProfile.qualitySettings.Instantiate();
- AssetDatabase.AddObjectToAsset(buildProfile.qualitySettings, buildProfile);
+ buildProfile.qualitySettings = AddSettingsSubAsset(buildProfile, s => s.Instantiate());
+ buildProfile.qualitySettings.ResetToGlobalValues(buildProfile);
EditorUtility.SetDirty(buildProfile);
UpdateActiveEditors(buildProfile);
@@ -755,12 +772,32 @@ public static void RemoveQualitySettings(BuildProfile buildProfile)
if (buildProfile.qualitySettings == null)
return;
- AssetDatabase.RemoveObjectFromAsset(buildProfile.qualitySettings);
+ var removed = buildProfile.qualitySettings;
+ AssetDatabase.RemoveObjectFromAsset(removed);
buildProfile.qualitySettings = null;
EditorUtility.SetDirty(buildProfile);
UpdateActiveEditors(buildProfile);
BuildProfileQualitySettingsEditor.RefreshCachedQualitySettingEntities();
+
+ UnityEngine.Object.DestroyImmediate(removed, true);
+ }
+
+ static T AddSettingsSubAsset(BuildProfile buildProfile, Action instantiate) where T : ScriptableObject
+ {
+ var settings = ScriptableObject.CreateInstance();
+ instantiate(settings);
+ try
+ {
+ AssetDatabase.AddObjectToAsset(settings, buildProfile);
+ }
+ catch
+ {
+ UnityEngine.Object.DestroyImmediate(settings);
+ throw;
+ }
+
+ return settings;
}
public static void NotifyBuildProfileExtensionOfCreation(BuildProfile buildProfile, int preconfiguredSettingsVariant)
@@ -833,6 +870,8 @@ public static void RequestScriptCompilation(BuildProfile profile)
else
BuildProfileContext.instance.cachedEditorScriptingDefines = Array.Empty();
+ BuildProfileContext.instance.cachedSDKPlatformDefines = SDKPlatformCompilationUtil.GetActivePlatformDefinesToCache();
+
EditorApplication.delayCall += TryRecompileScripts;
}
@@ -842,7 +881,7 @@ public static void RequestScriptCompilation(BuildProfile profile)
///
public static void CreateNewAssetWithName(
GUID platformId, string customProfileName, string preconfiguredSettingsVariantName,
- int preconfiguredSettingsVariant, string[] packagesToAdd, UnityAction onCreate,
+ int preconfiguredSettingsVariant, BuildTargetDiscovery.PlatformPackageIdentifier[] packagesToAdd, UnityAction onCreate,
GUID selectedPlatformGuid = default)
{
BuildProfileModuleUtil.EnsureCustomBuildProfileFolderExists();
@@ -937,6 +976,28 @@ internal static void SuppressMissingTypeWarning()
SerializationUtility.SuppressMissingTypeWarning(nameof(BuildProfile));
}
+ [VisibleToOtherModules("UnityEditor.BuildProfileModule")]
+ internal static void AddReferenceComponent(BuildProfile profile, Type componentType, ScriptableObject reference)
+ {
+ profile.AddReferenceComponent(componentType, reference);
+ }
+
+ [VisibleToOtherModules("UnityEditor.BuildProfileModule")]
+ internal static bool HasComponent(BuildProfile profile) where T : UnityEngine.Object
+ {
+ return profile.HasComponent();
+ }
+
+ ///
+ /// Reassigns an existing reference component's source in place. Throws when the component does
+ /// not exist; see .
+ ///
+ [VisibleToOtherModules("UnityEditor.BuildProfileModule")]
+ internal static void SetReferenceComponent(BuildProfile profile, Type componentType, ScriptableObject reference)
+ {
+ profile.SetReferenceComponent(componentType, reference);
+ }
+
public static string GetDefaultNewProfilePath(GUID platformGuid)
{
var platformDisplayName = BuildProfileModuleUtil.GetClassicPlatformDisplayName(platformGuid);
@@ -1199,34 +1260,9 @@ public static PlayerSettings GetGlobalPlayerSettings()
public static bool HandlePlayerSettingsChanged(
BuildProfile currentBuildProfile, BuildProfile nextBuildProfile)
{
- PlayerSettings projectSettingsPlayerSettings = GetGlobalPlayerSettings();
- PlayerSettings currentPlayerSettings = projectSettingsPlayerSettings;
- PlayerSettings nextPlayerSettings = projectSettingsPlayerSettings;
-
- var currentBuildTarget = EditorUserBuildSettings.activeBuildTarget;
- var nextBuildTarget = EditorUserBuildSettings.activeBuildTarget;
-
- if (currentBuildProfile != null)
- {
- // For build platform switch on the active multi-target profile, buildTarget
- // is already mutated, so falling back to the pre-switch active buildTarget.
- if (currentBuildProfile != nextBuildProfile)
- currentBuildTarget = currentBuildProfile.buildTarget;
-
- if (currentBuildProfile.playerSettings != null)
- {
- currentPlayerSettings = currentBuildProfile.playerSettings;
- }
- }
-
- if (nextBuildProfile != null)
- {
- nextBuildTarget = nextBuildProfile.buildTarget;
- if (nextBuildProfile.playerSettings != null)
- {
- nextPlayerSettings = nextBuildProfile.playerSettings;
- }
- }
+ GetPlayerSettingsChange(currentBuildProfile, nextBuildProfile,
+ out var currentPlayerSettings, out var nextPlayerSettings,
+ out var currentBuildTarget, out var nextBuildTarget);
var settingsRequiringRestart = PlayerSettings.GetSettingsRequiringRestart(currentPlayerSettings,
nextPlayerSettings, currentBuildTarget, nextBuildTarget);
@@ -1261,6 +1297,65 @@ public static bool HandlePlayerSettingsChanged(
return true;
}
+ ///
+ /// Same as , but logs a warning for settings that require an
+ /// editor restart instead of prompting the user.
+ ///
+ public static void HandlePlayerSettingsChangedWithoutPrompt(
+ BuildProfile currentBuildProfile, BuildProfile nextBuildProfile)
+ {
+ GetPlayerSettingsChange(currentBuildProfile, nextBuildProfile,
+ out var currentPlayerSettings, out var nextPlayerSettings,
+ out var currentBuildTarget, out var nextBuildTarget);
+
+ var settingsRequiringRestart = PlayerSettings.GetSettingsRequiringRestart(currentPlayerSettings,
+ nextPlayerSettings, currentBuildTarget, nextBuildTarget);
+ if (settingsRequiringRestart.Length > 0)
+ {
+ var playerSettingNames = GetPlayerSettingNamesToEditorRestartPromptText(settingsRequiringRestart);
+ Debug.LogWarning("[BuildProfile] The Unity editor must be restarted for the following settings to take effect:\n" + playerSettingNames);
+ }
+
+ PlayerSettingsEditor.HandlePlayerSettingsChanged(
+ currentPlayerSettings, nextPlayerSettings,
+ currentBuildTarget, nextBuildTarget);
+ }
+
+ static void GetPlayerSettingsChange(
+ BuildProfile currentBuildProfile, BuildProfile nextBuildProfile,
+ out PlayerSettings currentPlayerSettings, out PlayerSettings nextPlayerSettings,
+ out BuildTarget currentBuildTarget, out BuildTarget nextBuildTarget)
+ {
+ PlayerSettings projectSettingsPlayerSettings = GetGlobalPlayerSettings();
+ currentPlayerSettings = projectSettingsPlayerSettings;
+ nextPlayerSettings = projectSettingsPlayerSettings;
+
+ currentBuildTarget = EditorUserBuildSettings.activeBuildTarget;
+ nextBuildTarget = EditorUserBuildSettings.activeBuildTarget;
+
+ if (currentBuildProfile != null)
+ {
+ // For build platform switch on the active multi-target profile, buildTarget
+ // is already mutated, so falling back to the pre-switch active buildTarget.
+ if (currentBuildProfile != nextBuildProfile)
+ currentBuildTarget = currentBuildProfile.buildTarget;
+
+ if (currentBuildProfile.playerSettings != null)
+ {
+ currentPlayerSettings = currentBuildProfile.playerSettings;
+ }
+ }
+
+ if (nextBuildProfile != null)
+ {
+ nextBuildTarget = nextBuildProfile.buildTarget;
+ if (nextBuildProfile.playerSettings != null)
+ {
+ nextPlayerSettings = nextBuildProfile.playerSettings;
+ }
+ }
+ }
+
///
/// Show the restart editor dialog with the names of the settings that required the restart to take effect.
@@ -1474,8 +1569,13 @@ public static void InstallRequiredPackagesForClassicProfileIfRequired(BuildProfi
var packageName = package.qualifiedName;
if (!PackageManager.PackageInfo.IsPackageRegistered(packageName))
{
+ // The prompt lists bare names; only the install request carries the pinned version.
packageStringBuilder.AppendLine().Append(packageName);
- neededPackages.Add(packageName);
+ neededPackages.Add(package.GetPackageIdentifier().GetInstallIdentifier());
+ }
+ else
+ {
+ BuildTargetDiscovery.WarnIfPinnedVersionNotInstalled(package.GetPackageIdentifier());
}
}
diff --git a/Editor/Mono/BuildProfile/BuildProfilePackageAddInfo.cs b/Editor/Mono/BuildProfile/BuildProfilePackageAddInfo.cs
index 0040341271..0d668c31be 100644
--- a/Editor/Mono/BuildProfile/BuildProfilePackageAddInfo.cs
+++ b/Editor/Mono/BuildProfile/BuildProfilePackageAddInfo.cs
@@ -30,40 +30,38 @@ public enum ProgressState
public record struct ProgressEntry(ProgressState state, string name, int packageCount);
///
- /// Packages pending installation.
+ /// Packages pending installation. Each entry keeps its name and optional pinned version
+ /// apart, so lookups can match on the name while the install request uses both.
///
[field: SerializeField]
- public string[] packagesToAdd { get; set; } = Array.Empty();
+ public BuildTargetDiscovery.PlatformPackageIdentifier[] packagesToAdd { get; set; }
+ = Array.Empty();
+
+ ///
+ /// True when the installed packages were not registered yet as the request completed.
+ /// is then kept to tell when they are.
+ ///
+ [SerializeField]
+ public bool isRegistrationPending;
public Action OnPackageAddProgress;
public Action OnPackageAddComplete;
- [SerializeField]
- bool isPackageAddRequest = false;
+ [NonSerialized]
+ string m_PackageRequestKey;
- PackageManager.Requests.AddAndRemoveRequest m_PackageAddRequest = null;
ProgressEntry m_PackageAddProgressInfo = new();
- bool m_CallbacksSubscribed = false;
-
///
/// Begins package installation if not already started.
///
public void RequestPackageInstallation()
{
- if (m_PackageAddRequest != null && isPackageAddRequest)
+ if (m_PackageRequestKey != null || packagesToAdd.Length <= 0)
return;
- if (packagesToAdd.Length > 0)
- {
- m_PackageAddRequest = PackageManager.Client.AddAndRemove(packagesToAdd);
- m_PackageAddRequest.progressUpdated += HandlePackageAddProgress;
-
- EditorApplication.update += CheckCompletion;
- AssemblyReloadEvents.beforeAssemblyReload += UnsubscribeRequestCallbacks;
- m_CallbacksSubscribed = true;
- isPackageAddRequest = true;
- }
+ m_PackageRequestKey = BuildProfilePackageInstaller.RequestPackageInstallation(
+ packagesToAdd, HandlePackageAddProgress, HandlePackageAddComplete);
}
///
@@ -74,7 +72,7 @@ public bool IsPackageRequestDone()
{
foreach (var package in packagesToAdd)
{
- if (!PackageManager.PackageInfo.IsPackageRegistered(package))
+ if (!PackageManager.PackageInfo.IsPackageRegistered(package.name))
return false;
}
@@ -88,120 +86,26 @@ public bool IsPackageRequestDone()
///
public void Cleanup()
{
- UnsubscribeRequestCallbacks();
- isPackageAddRequest = false;
- m_PackageAddRequest = null;
+ BuildProfilePackageInstaller.RemovePackageAddCallbacks(
+ m_PackageRequestKey, HandlePackageAddProgress, HandlePackageAddComplete);
+ m_PackageRequestKey = null;
OnPackageAddComplete = null;
OnPackageAddProgress = null;
}
- ///
- /// Detaches the native-backed callbacks (the editor update loop, the request's
- /// progressUpdated event, and the domain-reload hook). These must be removed
- /// before any domain reload — otherwise the native delegate lists keep GC handles to
- /// this instance from the previous domain, producing "invalid GC handle ... from a
- /// previous domain" warnings when they are later resolved or released.
- ///
- void UnsubscribeRequestCallbacks()
- {
- // Skip when the callbacks weren't subscribed in this domain. After a domain reload
- // the request object may survive (serialized) but its progress tracker does not, so
- // unsubscribing progressUpdated would dereference a null tracker and throw.
- if (!m_CallbacksSubscribed)
- return;
-
- m_CallbacksSubscribed = false;
- AssemblyReloadEvents.beforeAssemblyReload -= UnsubscribeRequestCallbacks;
- EditorApplication.update -= CheckCompletion;
- if (m_PackageAddRequest != null)
- m_PackageAddRequest.progressUpdated -= HandlePackageAddProgress;
- }
-
- bool ContainsPackage(string name)
+ void HandlePackageAddProgress(ProgressEntry info)
{
- foreach (var package in packagesToAdd)
- {
- if (package == name)
- return true;
- }
- return false;
+ m_PackageAddProgressInfo = info;
+ OnPackageAddProgress?.Invoke();
}
- void CheckCompletion()
+ void HandlePackageAddComplete(bool succeeded)
{
- if (m_PackageAddRequest == null || !m_PackageAddRequest.IsCompleted)
- return;
-
- UnsubscribeRequestCallbacks();
-
- if (m_PackageAddRequest.Status >= PackageManager.StatusCode.Failure)
- Debug.LogError(m_PackageAddRequest.Error.message);
-
- packagesToAdd = Array.Empty();
+ // The request reports done before the editor resolves and registers the packages.
+ isRegistrationPending = succeeded && !IsPackageRequestDone();
+ if (!isRegistrationPending)
+ packagesToAdd = Array.Empty();
OnPackageAddComplete?.Invoke();
}
-
- void HandlePackageAddProgress(PackageManager.ProgressUpdateEventArgs progress)
- {
- int readyPackageNum = 0;
- bool packageInstalling = false;
- bool packageDownloading = false;
- bool packageErr = false;
- string packageNames = "";
- int packagesDownloadingCnt = 0;
- int packagesInstallingCnt = 0;
- int packagesErrCnt = 0;
-
- ProgressEntry info;
- foreach (var entry in progress.entries)
- {
- if (ContainsPackage(entry.name))
- {
- if (packageNames.Length > 0)
- packageNames += " ";
- switch (entry.state)
- {
- case PackageManager.ProgressState.Ready:
- readyPackageNum += 1;
- break;
- case PackageManager.ProgressState.Error:
- packageErr = true;
- packagesErrCnt++;
- break;
- case PackageManager.ProgressState.Downloading:
- packageDownloading = true;
- packagesDownloadingCnt++;
- break;
- case PackageManager.ProgressState.Installing:
- packageInstalling = true;
- packagesInstallingCnt++;
- break;
- }
- packageNames += entry.name;
- }
- }
-
- bool done = (readyPackageNum == packagesToAdd.Length);
-
- if (packageErr)
- {
- info = new ProgressEntry(ProgressState.PackageError, packageNames, packagesErrCnt);
- }
- else if (packageInstalling)
- {
- info = new ProgressEntry(ProgressState.PackageInstalling, packageNames, packagesInstallingCnt);
- }
- else if (packageDownloading)
- {
- info = new ProgressEntry(ProgressState.PackageDownloading, packageNames, packagesDownloadingCnt);
- }
- else
- {
- info = new ProgressEntry(done ? ProgressState.ConfigurationRunning : ProgressState.ConfigurationPending, packageNames, 0);
- }
-
- m_PackageAddProgressInfo = info;
- OnPackageAddProgress?.Invoke();
- }
}
}
diff --git a/Editor/Mono/BuildProfile/BuildProfilePackageInstaller.cs b/Editor/Mono/BuildProfile/BuildProfilePackageInstaller.cs
new file mode 100644
index 0000000000..8c554f4063
--- /dev/null
+++ b/Editor/Mono/BuildProfile/BuildProfilePackageInstaller.cs
@@ -0,0 +1,210 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using System;
+using System.Collections.Generic;
+using Unity.Scripting.LifecycleManagement;
+using UnityEngine;
+using ProgressEntry = UnityEditor.Build.Profile.BuildProfilePackageAddInfo.ProgressEntry;
+using ProgressState = UnityEditor.Build.Profile.BuildProfilePackageAddInfo.ProgressState;
+
+namespace UnityEditor.Build.Profile
+{
+ ///
+ /// Runs the package installations requested by build profile creation. Profiles created in one
+ /// batch ask for the same packages, so they join a single request instead of issuing one each,
+ /// and every joiner is told about its progress and completion.
+ ///
+ static partial class BuildProfilePackageInstaller
+ {
+ internal class PackageRequest
+ {
+ public PackageManager.Requests.AddAndRemoveRequest packageAddRequest;
+ public string[] packageQualifiedNames;
+ public Action onPackageAddProgress;
+ public Action onPackageAddComplete;
+
+ public void HandlePackageAddProgress(PackageManager.ProgressUpdateEventArgs progress)
+ {
+ int readyPackageNum = 0;
+ bool packageInstalling = false;
+ bool packageDownloading = false;
+ bool packageErr = false;
+ string packageNames = "";
+ int packagesDownloadingCnt = 0;
+ int packagesInstallingCnt = 0;
+ int packagesErrCnt = 0;
+
+ ProgressEntry info;
+ foreach (var entry in progress.entries)
+ {
+ if (Array.IndexOf(packageQualifiedNames, entry.name) >= 0)
+ {
+ if (packageNames.Length > 0)
+ packageNames += " ";
+ switch (entry.state)
+ {
+ case PackageManager.ProgressState.Ready:
+ readyPackageNum += 1;
+ break;
+ case PackageManager.ProgressState.Error:
+ packageErr = true;
+ packagesErrCnt++;
+ break;
+ case PackageManager.ProgressState.Downloading:
+ packageDownloading = true;
+ packagesDownloadingCnt++;
+ break;
+ case PackageManager.ProgressState.Installing:
+ packageInstalling = true;
+ packagesInstallingCnt++;
+ break;
+ }
+ packageNames += entry.name;
+ }
+ }
+
+ bool done = (readyPackageNum == packageQualifiedNames.Length);
+
+ if (packageErr)
+ {
+ info = new ProgressEntry(ProgressState.PackageError, packageNames, packagesErrCnt);
+ }
+ else if (packageInstalling)
+ {
+ info = new ProgressEntry(ProgressState.PackageInstalling, packageNames, packagesInstallingCnt);
+ }
+ else if (packageDownloading)
+ {
+ info = new ProgressEntry(ProgressState.PackageDownloading, packageNames, packagesDownloadingCnt);
+ }
+ else
+ {
+ info = new ProgressEntry(done ? ProgressState.ConfigurationRunning : ProgressState.ConfigurationPending, packageNames, 0);
+ }
+
+ onPackageAddProgress?.Invoke(info);
+ }
+ }
+
+ [AutoStaticsCleanupOnCodeReload]
+ internal static readonly Dictionary s_InFlightPackageRequests = new();
+
+ ///
+ /// Begins installing , or joins the request already
+ /// installing them. Both callbacks are invoked for every joiner, and the completion
+ /// callback is told whether the request succeeded.
+ ///
+ ///
+ /// The key identifying the request.
+ ///
+ public static string RequestPackageInstallation(
+ BuildTargetDiscovery.PlatformPackageIdentifier[] packagesToAdd,
+ Action onPackageAddProgress,
+ Action onPackageAddComplete)
+ {
+ var key = GetPackageRequestKey(packagesToAdd);
+ if (s_InFlightPackageRequests.TryGetValue(key, out var inFlight))
+ {
+ inFlight.onPackageAddProgress += onPackageAddProgress;
+ inFlight.onPackageAddComplete += onPackageAddComplete;
+ return key;
+ }
+
+ var installIdentifiers = new string[packagesToAdd.Length];
+ var packageNames = new string[packagesToAdd.Length];
+ for (int i = 0; i < packagesToAdd.Length; i++)
+ {
+ installIdentifiers[i] = packagesToAdd[i].GetInstallIdentifier();
+ packageNames[i] = packagesToAdd[i].name;
+ }
+
+ var request = new PackageRequest
+ {
+ packageAddRequest = PackageManager.Client.AddAndRemove(installIdentifiers),
+ packageQualifiedNames = packageNames,
+ onPackageAddProgress = onPackageAddProgress,
+ onPackageAddComplete = onPackageAddComplete,
+ };
+
+ SubscribeRequestCallbacks();
+ s_InFlightPackageRequests[key] = request;
+ request.packageAddRequest.progressUpdated += request.HandlePackageAddProgress;
+ return key;
+ }
+
+ public static void RemovePackageAddCallbacks(
+ string key,
+ Action onPackageAddProgress,
+ Action onPackageAddComplete)
+ {
+ if (key == null || !s_InFlightPackageRequests.TryGetValue(key, out var inFlight))
+ return;
+
+ inFlight.onPackageAddProgress -= onPackageAddProgress;
+ inFlight.onPackageAddComplete -= onPackageAddComplete;
+ }
+
+ static string GetPackageRequestKey(BuildTargetDiscovery.PlatformPackageIdentifier[] packagesToAdd)
+ {
+ var installIdentifiers = new string[packagesToAdd.Length];
+ for (int i = 0; i < packagesToAdd.Length; i++)
+ installIdentifiers[i] = packagesToAdd[i].GetInstallIdentifier();
+
+ Array.Sort(installIdentifiers, StringComparer.Ordinal);
+ return string.Join("\n", installIdentifiers);
+ }
+
+ static void CheckCompletion()
+ {
+ List completed = null;
+ foreach (var entry in s_InFlightPackageRequests)
+ {
+ if (entry.Value.packageAddRequest.IsCompleted)
+ (completed ??= new List()).Add(entry.Key);
+ }
+
+ if (completed == null)
+ return;
+
+ foreach (var key in completed)
+ {
+ var packageAdd = s_InFlightPackageRequests[key];
+ s_InFlightPackageRequests.Remove(key);
+ packageAdd.packageAddRequest.progressUpdated -= packageAdd.HandlePackageAddProgress;
+
+ var succeeded = packageAdd.packageAddRequest.Status < PackageManager.StatusCode.Failure;
+ if (!succeeded)
+ Debug.LogError(packageAdd.packageAddRequest.Error.message);
+
+ packageAdd.onPackageAddComplete?.Invoke(succeeded);
+ }
+
+ if (s_InFlightPackageRequests.Count == 0)
+ UnsubscribeRequestCallbacks();
+ }
+
+ static void SubscribeRequestCallbacks()
+ {
+ EditorApplication.update -= CheckCompletion;
+ EditorApplication.update += CheckCompletion;
+ AssemblyReloadEvents.beforeAssemblyReload -= UnsubscribeRequestCallbacks;
+ AssemblyReloadEvents.beforeAssemblyReload += UnsubscribeRequestCallbacks;
+ }
+
+ ///
+ /// Detaches the native-backed callbacks before a domain reload — otherwise the native
+ /// delegate lists keep GC handles to objects from the previous domain, producing
+ /// "invalid GC handle ... from a previous domain" warnings when they are later released.
+ ///
+ static void UnsubscribeRequestCallbacks()
+ {
+ AssemblyReloadEvents.beforeAssemblyReload -= UnsubscribeRequestCallbacks;
+ EditorApplication.update -= CheckCompletion;
+
+ foreach (var entry in s_InFlightPackageRequests)
+ entry.Value.packageAddRequest.progressUpdated -= entry.Value.HandlePackageAddProgress;
+ }
+ }
+}
diff --git a/Editor/Mono/BuildProfile/BuildProfilePlatformSettingsBase.cs b/Editor/Mono/BuildProfile/BuildProfilePlatformSettingsBase.cs
index f97d56e9ee..65622df36c 100644
--- a/Editor/Mono/BuildProfile/BuildProfilePlatformSettingsBase.cs
+++ b/Editor/Mono/BuildProfile/BuildProfilePlatformSettingsBase.cs
@@ -47,7 +47,7 @@ internal abstract class BuildProfilePlatformSettingsBase
[SerializeField] Compression m_CompressionType = (Compression)k_InvalidCompressionIdx;
[SerializeField] bool m_InstallInBuildFolder = false;
[SerializeField] BuildProfileInsightsSettingsContainer m_InsightsSettingsContainer = new()
- { buildProfileEngineDiagnosticsState = BuildProfileEngineDiagnosticsState.ProjectSettings };
+ { buildProfileEngineDiagnosticsState = BuildProfileEngineDiagnosticsState.Default };
[SerializeField] bool m_AdaptivePerformanceEnabled = false;
internal protected virtual bool development
diff --git a/Editor/Mono/BuildProfile/BuildProfilePlayerSettings.bindings.cs b/Editor/Mono/BuildProfile/BuildProfilePlayerSettings.bindings.cs
index 3fa125937d..d39052be69 100644
--- a/Editor/Mono/BuildProfile/BuildProfilePlayerSettings.bindings.cs
+++ b/Editor/Mono/BuildProfile/BuildProfilePlayerSettings.bindings.cs
@@ -9,36 +9,64 @@
namespace UnityEditor.Build.Profile;
+///
+/// The player settings that a build profile stores in place of the global player settings.
+///
+///
+/// A build profile has at most one component. Use
+/// to add one that starts as a copy of the global player settings,
+/// to check whether a profile has one, and
+/// to remove it. A build profile without this component uses the
+/// global player settings, which returns when you request
+/// .
+///
+/// To change the player settings of the active build profile, use the API. To change the
+/// player settings of an inactive build profile, edit this component through a . The
+/// serialized property paths of this component aren't part of the public API and might change in future versions.
+///
[NativeHeader("Runtime/Misc/PlayerSettings.h")]
[NativeClass("BuildProfilePlayerSettings", PersistentTypeId = 0x43E334C1)]
[UsedByNativeCode]
[VisibleToOtherModules]
-internal class BuildProfilePlayerSettings : UnityEngine.Object
+[ExcludeFromObjectFactory]
+[ExcludeFromPreset]
+public sealed class BuildProfilePlayerSettings : UnityEngine.Object
{
private extern static void Internal_Create([Writable] BuildProfilePlayerSettings self);
internal SerializedObject GetSerializedObject() => new SerializedObject(this);
[RequiredByNativeCode]
- public BuildProfilePlayerSettings()
+ internal BuildProfilePlayerSettings()
{
Internal_Create(this);
}
- [NativeMethod("GetPlatformGraphicsAPIs")]
- internal extern GraphicsDeviceType[] GetGraphicsAPIs(BuildTarget platform);
+ [NativeMethod("GetPlatformGraphicsAPIsWithUGKVariants")]
+ internal extern GraphicsDeviceType[] GetGraphicsAPIsWithUGKVariants(BuildTarget platform);
+
+ [NativeMethod("GetPlatformGraphicsAPIUGKFlags")]
+ internal extern int[] GetGraphicsAPIUGKFlags(BuildTarget platform);
[NativeMethod("SetPlatformGraphicsAPIs")]
- private extern void SetGraphicsAPIsImpl(BuildTarget platform, GraphicsDeviceType[] apis, bool skipValidation);
+ private extern void SetGraphicsAPIsImpl(BuildTarget platform, GraphicsDeviceType[] apis, bool skipValidation, int[] ugkFlags);
- internal void SetGraphicsAPIs(BuildTarget platform, GraphicsDeviceType[] apis, bool shouldSync)
+ internal void SetGraphicsAPIs(BuildTarget platform, GraphicsDeviceType[] apis, int[] ugkFlags, bool shouldSync)
{
- SetGraphicsAPIsImpl(platform, apis, false);
+ SetGraphicsAPIsImpl(platform, apis, false, ugkFlags);
// we do cache api list in player settings editor, so if we update from script we should forcibly update cache
if (shouldSync)
PlayerSettingsEditor.SyncEditors(platform);
}
+ [NativeMethod("GetPlatformGraphicsAPIs")]
+ internal extern GraphicsDeviceType[] GetGraphicsAPIs(BuildTarget platform);
+
+ internal void SetGraphicsAPIs(BuildTarget platform, GraphicsDeviceType[] apis, bool shouldSync)
+ {
+ SetGraphicsAPIs(platform, apis, null, shouldSync);
+ }
+
[NativeMethod("GetPlatformAutomaticGraphicsAPIs")]
internal extern bool GetUseDefaultGraphicsAPIs(BuildTarget platform);
diff --git a/Editor/Mono/BuildProfile/BuildProfilePlayerSettings.cs b/Editor/Mono/BuildProfile/BuildProfilePlayerSettings.cs
index f4931d1d33..295b595c37 100644
--- a/Editor/Mono/BuildProfile/BuildProfilePlayerSettings.cs
+++ b/Editor/Mono/BuildProfile/BuildProfilePlayerSettings.cs
@@ -99,6 +99,9 @@ internal void Clear()
const string k_ProjectSettingsAssetPath = "ProjectSettings/ProjectSettings.asset";
[AutoStaticsCleanupOnCodeReload]
+ // Handle to the global player settings object; TryLoadProjectSettingsAssetPlayerSettings fetches
+ // it again on the next access while it is null.
+ [IgnoreForUAL0015("Lazily re-fetched by TryLoadProjectSettingsAssetPlayerSettings when null")]
static PlayerSettings s_GlobalPlayerSettings;
[AutoStaticsCleanupOnCodeReload]
@@ -173,6 +176,47 @@ internal void RemoveBuildProfilePlayerSettings()
}
}
+ BuildProfilePlayerSettings CreatePlayerSettingsComponent()
+ {
+ if (BuildProfileContext.IsClassicPlatformProfile(this))
+ throw new InvalidOperationException($"The classic platform profile {name} can't have a {nameof(BuildProfilePlayerSettings)} component.");
+
+ CreatePlayerSettingsFromGlobal();
+ return buildProfilePlayerSettings;
+ }
+
+ void RemovePlayerSettingsComponent()
+ {
+ if (buildProfilePlayerSettings == null)
+ {
+ Debug.LogWarning($"The component {nameof(BuildProfilePlayerSettings)} does not exist in the build profile {name}. ");
+ return;
+ }
+
+ if (BuildProfileContext.activeProfile != this)
+ {
+ RemoveBuildProfilePlayerSettings();
+ EditorUtility.SetDirty(this);
+ return;
+ }
+
+ // Compare against the profile's values before the removal destroys them.
+ var namedBuildTarget = NamedBuildTarget.FromTargetAndSubtarget(buildTarget, (int)subtarget);
+ var profileDefineSymbols = PlayerSettings.GetScriptingDefineSymbols(namedBuildTarget);
+ var profileCompilerArguments = PlayerSettings.GetAdditionalCompilerArguments(namedBuildTarget);
+ BuildProfileModuleUtil.HandlePlayerSettingsChangedWithoutPrompt(this, null);
+
+ RemoveBuildProfilePlayerSettings();
+ EditorUtility.SetDirty(this);
+
+ if (profileDefineSymbols != PlayerSettings.GetScriptingDefineSymbols(namedBuildTarget) ||
+ !ArrayUtility.ArrayEquals(profileCompilerArguments, PlayerSettings.GetAdditionalCompilerArguments(namedBuildTarget)))
+ {
+ scriptingDefines = BuildProfileModuleUtil.RemoveInvalidScriptingDefines(scriptingDefines);
+ BuildProfileModuleUtil.RequestScriptCompilation(this);
+ }
+ }
+
internal static void CleanUpPlayerSettingsForDeletedBuildProfiles(IList currentBuildProfiles)
{
TrySetProjectSettingsAssetAsGlobalManagerPlayerSettings();
diff --git a/Editor/Mono/BuildProfile/BuildProfilePlayerSettingsEditor.cs b/Editor/Mono/BuildProfile/BuildProfilePlayerSettingsEditor.cs
index 6c19f7500e..566e14c100 100644
--- a/Editor/Mono/BuildProfile/BuildProfilePlayerSettingsEditor.cs
+++ b/Editor/Mono/BuildProfile/BuildProfilePlayerSettingsEditor.cs
@@ -21,6 +21,19 @@ public BuildProfilePlayerSettingsAccessor(BuildProfilePlayerSettings playerSetti
m_PlayerSettings = playerSettings;
}
+ public GraphicsDeviceType[] GetGraphicsAPIsWithUGKVariants_Internal(BuildTarget platform)
+ {
+ return m_PlayerSettings.GetGraphicsAPIsWithUGKVariants(platform);
+ }
+ public int[] GetGraphicsAPIUGKFlags_Internal(BuildTarget platform)
+ {
+ return m_PlayerSettings.GetGraphicsAPIUGKFlags(platform);
+ }
+ public void SetGraphicsAPIs_Internal(BuildTarget platform, GraphicsDeviceType[] apis, int[] ugkFlags, bool shouldSync)
+ {
+ m_PlayerSettings.SetGraphicsAPIs(platform, apis, ugkFlags, shouldSync);
+ }
+
public GraphicsDeviceType[] GetGraphicsAPIs_Internal(BuildTarget platform)
{
return m_PlayerSettings.GetGraphicsAPIs(platform);
diff --git a/Editor/Mono/BuildProfile/BuildProfileQualitySettings.cs b/Editor/Mono/BuildProfile/BuildProfileQualitySettings.cs
index 7855a8d3a0..6f3df0443b 100644
--- a/Editor/Mono/BuildProfile/BuildProfileQualitySettings.cs
+++ b/Editor/Mono/BuildProfile/BuildProfileQualitySettings.cs
@@ -8,67 +8,147 @@
namespace UnityEditor.Build.Profile
{
+ ///
+ /// A build profile component that overrides the global quality levels for the build profile's platform.
+ ///
+ [ExcludeFromObjectFactory]
+ [ExcludeFromPreset]
[VisibleToOtherModules("UnityEditor.BuildProfileModule")]
- sealed class BuildProfileQualitySettings : ScriptableObject
+ public sealed class BuildProfileQualitySettings : ScriptableObject
{
[SerializeField] string m_DefaultQualityLevel = string.Empty;
[SerializeField] string[] m_QualityLevels = Array.Empty();
+ internal BuildProfileQualitySettings()
+ {
+ }
+
+ ///
+ /// The name of the quality level that the build uses at startup.
+ ///
public string defaultQualityLevel
{
get => m_DefaultQualityLevel;
- set => m_DefaultQualityLevel = value;
+ set
+ {
+ if (value == null)
+ throw new ArgumentNullException(nameof(value));
+
+ if (value == m_DefaultQualityLevel)
+ return;
+
+ if (Array.IndexOf(m_QualityLevels, value) == -1)
+ throw new ArgumentException($"'{value}' isn't one of this build profile's quality levels.", nameof(value));
+
+ m_DefaultQualityLevel = value;
+ EditorUtility.SetDirty(this);
+ }
}
+ ///
+ /// The names of the quality levels to include in the build.
+ ///
public string[] qualityLevels
{
- get => m_QualityLevels;
- set => m_QualityLevels = value;
+ get => (string[])m_QualityLevels.Clone();
+ set
+ {
+ ThrowIfInvalidQualityLevels(value);
+
+ m_QualityLevels = (string[])value.Clone();
+ if (Array.IndexOf(m_QualityLevels, m_DefaultQualityLevel) == -1)
+ m_DefaultQualityLevel = m_QualityLevels.Length > 0 ? m_QualityLevels[0] : string.Empty;
+
+ EditorUtility.SetDirty(this);
+ }
+ }
+
+ static void ThrowIfInvalidQualityLevels(string[] qualityLevels)
+ {
+ if (qualityLevels == null)
+ throw new ArgumentNullException(nameof(qualityLevels));
+
+ var projectQualityLevels = QualitySettings.names;
+ for (int i = 0; i < qualityLevels.Length; i++)
+ {
+ var qualityLevel = qualityLevels[i];
+ if (string.IsNullOrEmpty(qualityLevel))
+ throw new ArgumentException($"The quality level at index {i} is null or empty.", nameof(qualityLevels));
+ if (Array.IndexOf(projectQualityLevels, qualityLevel) == -1)
+ throw new ArgumentException($"'{qualityLevel}' isn't a quality level in the project.", nameof(qualityLevels));
+ if (Array.IndexOf(qualityLevels, qualityLevel, 0, i) != -1)
+ throw new ArgumentException($"'{qualityLevel}' appears more than once.", nameof(qualityLevels));
+ }
+ }
+
+ [VisibleToOtherModules("UnityEditor.BuildProfileModule")]
+ internal void ResetToGlobalValues(BuildProfile profile)
+ {
+ var namedBuildTargetName = NamedBuildTarget.FromTargetAndSubtarget(profile.buildTarget, (int)profile.subtarget).TargetName;
+ var globalQualityLevels = QualitySettings.GetActiveQualityLevelsForPlatform(namedBuildTargetName);
+
+ var newQualityLevels = new string[globalQualityLevels.Length];
+
+ // populates newQualityLevels array with global quality levels in their existing order
+ for (int i = 0; i < globalQualityLevels.Length; i++)
+ {
+ newQualityLevels[i] = QualitySettings.names[globalQualityLevels[i]];
+ }
+
+ var globalDefaultQualityLevelIndex = QualitySettings.GetDefaultQualityForPlatform(namedBuildTargetName);
+ if (globalDefaultQualityLevelIndex != -1)
+ m_DefaultQualityLevel = QualitySettings.names[globalDefaultQualityLevelIndex];
+ else
+ m_DefaultQualityLevel = newQualityLevels.Length > 0 ? QualitySettings.names[globalQualityLevels[0]] : string.Empty;
+
+ m_QualityLevels = newQualityLevels;
+
+ EditorUtility.SetDirty(this);
}
- public void Instantiate()
+ internal void Instantiate()
{
name = "Quality Settings";
hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector;
}
- public void RemoveQualityLevel(string qualityLevel)
+ internal void RemoveQualityLevel(string qualityLevel)
{
- var index = Array.IndexOf(qualityLevels, qualityLevel);
+ var index = Array.IndexOf(m_QualityLevels, qualityLevel);
if (index == -1)
return;
- var newQualityLevels = new string[qualityLevels.Length - 1];
- Array.Copy(qualityLevels, 0, newQualityLevels, 0, index);
- Array.Copy(qualityLevels, index + 1, newQualityLevels, index, qualityLevels.Length - index - 1);
- qualityLevels = newQualityLevels;
+ var newQualityLevels = new string[m_QualityLevels.Length - 1];
+ Array.Copy(m_QualityLevels, 0, newQualityLevels, 0, index);
+ Array.Copy(m_QualityLevels, index + 1, newQualityLevels, index, m_QualityLevels.Length - index - 1);
+ m_QualityLevels = newQualityLevels;
- if (defaultQualityLevel == qualityLevel)
- defaultQualityLevel = qualityLevels.Length > 0 ? qualityLevels[0] : string.Empty;
+ if (m_DefaultQualityLevel == qualityLevel)
+ m_DefaultQualityLevel = m_QualityLevels.Length > 0 ? m_QualityLevels[0] : string.Empty;
EditorUtility.SetDirty(this);
}
- public void RenameQualityLevel(string oldName, string newName)
+ internal void RenameQualityLevel(string oldName, string newName)
{
- var index = Array.IndexOf(qualityLevels, oldName);
+ var index = Array.IndexOf(m_QualityLevels, oldName);
if (index == -1)
return;
- qualityLevels[index] = newName;
+ m_QualityLevels[index] = newName;
- if (defaultQualityLevel == oldName)
- defaultQualityLevel = newName;
+ if (m_DefaultQualityLevel == oldName)
+ m_DefaultQualityLevel = newName;
EditorUtility.SetDirty(this);
}
- public void AddQualityLevel(string qualityLevel)
+ internal void AddQualityLevel(string qualityLevel)
{
- var newQualityLevels = new string[qualityLevels.Length + 1];
- Array.Copy(qualityLevels, 0, newQualityLevels, 0, qualityLevels.Length);
- newQualityLevels[qualityLevels.Length] = qualityLevel;
- qualityLevels = newQualityLevels;
+ var newQualityLevels = new string[m_QualityLevels.Length + 1];
+ Array.Copy(m_QualityLevels, 0, newQualityLevels, 0, m_QualityLevels.Length);
+ newQualityLevels[m_QualityLevels.Length] = qualityLevel;
+ m_QualityLevels = newQualityLevels;
EditorUtility.SetDirty(this);
}
diff --git a/Editor/Mono/BuildProfile/BuildProfileQualitySettingsEditor.cs b/Editor/Mono/BuildProfile/BuildProfileQualitySettingsEditor.cs
index 1ce8720f5d..403ead9b24 100644
--- a/Editor/Mono/BuildProfile/BuildProfileQualitySettingsEditor.cs
+++ b/Editor/Mono/BuildProfile/BuildProfileQualitySettingsEditor.cs
@@ -19,7 +19,7 @@ sealed partial class BuildProfileQualitySettingsEditor : Editor
const string k_Uxml = "BuildProfile/UXML/BuildProfileQualitySettings.uxml";
const string k_StyleSheet = "BuildProfile/StyleSheets/BuildProfile.uss";
const string k_QualitySettingsWindow = "Project/Quality";
- static readonly GUIContent k_qualitySettingsWindow = EditorGUIUtility.TrTextContent("Quality...");
+ static readonly GUIContent k_qualitySettingsWindow = L10n.TextContent("Quality...", null, null, null);
static readonly string k_InvalidQualityLevelWarning =
L10n.Tr("The Quality levels in this profile do not match those that exist in the project. This may result in unexpected results on build.", null);
static readonly string k_EmptyQualitySettingsWarning =
diff --git a/Editor/Mono/BuildProfile/IPackageServiceInfoProvider.cs b/Editor/Mono/BuildProfile/IPackageServiceInfoProvider.cs
new file mode 100644
index 0000000000..49dcb46a74
--- /dev/null
+++ b/Editor/Mono/BuildProfile/IPackageServiceInfoProvider.cs
@@ -0,0 +1,32 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using System;
+using System.Threading.Tasks;
+using UnityEditor.Marketplace;
+
+namespace UnityEditor.Build.Profile
+{
+ interface IPackageServiceInfoProvider
+ {
+ bool HasPackageInfo(string packageName);
+ bool IsAssetStorePackage(string packageName);
+ bool? HasMarketplaceEntitlement(string packageName);
+ string GetMarketplaceProductId(string packageName);
+
+ Task GetOrFetchProductInfoAsync(string packageName);
+
+ /// No organization ID to claim against.
+ Task ClaimEntitlementAsync(string productId);
+ }
+
+ ///
+ /// Thrown when a claim cannot be made because the project has no organization to claim against.
+ /// Raised only after waiting: an organization that has not arrived yet is not an absent one.
+ ///
+ class NoOrganizationIdException : Exception
+ {
+ public NoOrganizationIdException(string message) : base(message) { }
+ }
+}
diff --git a/Editor/Mono/BuildProfile/MarketplaceEditorApi/IMarketplaceWebRequestSender.cs b/Editor/Mono/BuildProfile/MarketplaceEditorApi/IMarketplaceWebRequestSender.cs
new file mode 100644
index 0000000000..09930f53bf
--- /dev/null
+++ b/Editor/Mono/BuildProfile/MarketplaceEditorApi/IMarketplaceWebRequestSender.cs
@@ -0,0 +1,71 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using UnityEditor.Connect;
+using UnityEngine.Networking;
+
+namespace UnityEditor.Marketplace;
+
+readonly struct MarketplaceWebResponse
+{
+ public long responseCode { get; }
+ public string body { get; }
+
+ public MarketplaceWebResponse(long responseCode, string body)
+ {
+ this.responseCode = responseCode;
+ this.body = body;
+ }
+}
+
+interface IMarketplaceWebRequestSender
+{
+ Task GetAsync(string url, string bearerToken, CancellationToken cancellationToken);
+ Task PostJsonAsync(string url, string jsonBody, string bearerToken, CancellationToken cancellationToken);
+}
+
+class MarketplaceWebRequestSender : IMarketplaceWebRequestSender
+{
+ const string k_JsonContentType = "application/json";
+ const string k_TransportFailureMessage = "Could not reach the marketplace service: {0}";
+
+ public async Task GetAsync(string url, string bearerToken, CancellationToken cancellationToken)
+ {
+ using (var request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbGET))
+ return await SendAsync(request, bearerToken, cancellationToken);
+ }
+
+ public async Task PostJsonAsync(string url, string jsonBody, string bearerToken, CancellationToken cancellationToken)
+ {
+ using (var request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST))
+ {
+ request.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(jsonBody));
+ request.SetRequestHeader("Content-Type", k_JsonContentType);
+ return await SendAsync(request, bearerToken, cancellationToken);
+ }
+ }
+
+ static async Task SendAsync(UnityWebRequest request, string bearerToken, CancellationToken cancellationToken)
+ {
+ request.downloadHandler = new DownloadHandlerBuffer();
+ request.suppressErrorsToConsole = true;
+
+ // No token when signed out; the product endpoint answers unauthenticated calls.
+ if (!string.IsNullOrEmpty(bearerToken))
+ request.SetRequestHeader("AUTHORIZATION", "Bearer " + bearerToken);
+
+ await UnityConnectWebRequestUtils.SendWebRequestAsync(request, cancellationToken);
+
+ if (request.result == UnityWebRequest.Result.ConnectionError)
+ {
+ throw UnityConnectWebRequestUtils.CreateUnityWebRequestException(
+ request, string.Format(k_TransportFailureMessage, request.error));
+ }
+
+ return new MarketplaceWebResponse(request.responseCode, request.downloadHandler.text);
+ }
+}
diff --git a/Editor/Mono/BuildProfile/MarketplaceEditorApi/IServiceTokenProvider.cs b/Editor/Mono/BuildProfile/MarketplaceEditorApi/IServiceTokenProvider.cs
new file mode 100644
index 0000000000..14a6c64574
--- /dev/null
+++ b/Editor/Mono/BuildProfile/MarketplaceEditorApi/IServiceTokenProvider.cs
@@ -0,0 +1,22 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace UnityEditor.Marketplace;
+
+interface IServiceTokenProvider
+{
+ Task GetServiceTokenAsync(CancellationToken cancellationToken);
+}
+
+class ServiceTokenProvider : IServiceTokenProvider
+{
+ // Null when there is no Genesis token to exchange, e.g. when signed out.
+ public Task GetServiceTokenAsync(CancellationToken cancellationToken)
+ {
+ return CloudProjectSettings.GetServiceTokenAsync(cancellationToken);
+ }
+}
diff --git a/Editor/Mono/BuildProfile/MarketplaceEditorApi/MarketplaceApiError.cs b/Editor/Mono/BuildProfile/MarketplaceEditorApi/MarketplaceApiError.cs
new file mode 100644
index 0000000000..67f49d383f
--- /dev/null
+++ b/Editor/Mono/BuildProfile/MarketplaceEditorApi/MarketplaceApiError.cs
@@ -0,0 +1,98 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using System;
+
+namespace UnityEditor.Marketplace;
+
+///
+/// Problem-details envelope returned by the marketplace editor endpoints. Only
+/// and are guaranteed by the schema; the rest may be absent.
+///
+readonly struct MarketplaceApiError
+{
+ ///
+ /// HTTP status text.
+ ///
+ public string title { get; }
+
+ ///
+ /// HTTP status code.
+ ///
+ public int status { get; }
+
+ ///
+ /// Application-specific error code. Optional.
+ ///
+ public int code { get; }
+
+ ///
+ /// Error type. Optional.
+ ///
+ public string type { get; }
+
+ ///
+ /// Human-readable error message. Optional.
+ ///
+ public string detail { get; }
+
+ ///
+ /// Request ID for tracing. Optional.
+ ///
+ public string requestId { get; }
+
+ public MarketplaceApiError(string title, int status, int code, string type, string detail, string requestId)
+ {
+ this.title = title;
+ this.status = status;
+ this.code = code;
+ this.type = type;
+ this.detail = detail;
+ this.requestId = requestId;
+ }
+
+ public static MarketplaceApiError FromResponse(MarketplaceWebResponse response)
+ {
+ var fields = MarketplaceJson.Parse(response.body);
+ if (fields == null)
+ return new MarketplaceApiError(null, (int)response.responseCode, 0, null, null, null);
+
+ return new MarketplaceApiError(
+ MarketplaceJson.ReadString(fields, "title"),
+ MarketplaceJson.ReadInt(fields, "status", (int)response.responseCode),
+ MarketplaceJson.ReadInt(fields, "code", 0),
+ MarketplaceJson.ReadString(fields, "type"),
+ MarketplaceJson.ReadString(fields, "detail"),
+ MarketplaceJson.ReadString(fields, "requestId"));
+ }
+
+ public static MarketplaceApiError Unparsable(MarketplaceWebResponse response, string url)
+ {
+ return new MarketplaceApiError(
+ null,
+ (int)response.responseCode,
+ 0,
+ null,
+ "Could not parse the response from " + url,
+ null);
+ }
+
+ public override string ToString()
+ {
+ var message = !string.IsNullOrEmpty(detail) ? detail : title;
+ var identifier = !string.IsNullOrEmpty(requestId) ? " (requestId: " + requestId + ")" : string.Empty;
+ return "[" + status + "] " + message + identifier;
+ }
+}
+
+class MarketplaceApiException : Exception
+{
+ public MarketplaceApiError error { get; }
+
+ public MarketplaceApiException(MarketplaceApiError error)
+ : base(error.ToString())
+ {
+ this.error = error;
+ }
+}
diff --git a/Editor/Mono/BuildProfile/MarketplaceEditorApi/MarketplaceEditorApiClient.cs b/Editor/Mono/BuildProfile/MarketplaceEditorApi/MarketplaceEditorApiClient.cs
new file mode 100644
index 0000000000..5472b8ef5c
--- /dev/null
+++ b/Editor/Mono/BuildProfile/MarketplaceEditorApi/MarketplaceEditorApiClient.cs
@@ -0,0 +1,100 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using UnityEditor.Connect;
+
+namespace UnityEditor.Marketplace;
+
+interface IMarketplaceEditorApiClient
+{
+ ///
+ /// Resolves a UPM technical name to product info. Returns null when the service has no product
+ /// for the name, which covers both a package outside the partner whitelist and one with no
+ /// published version.
+ ///
+ Task GetPackageInfoAsync(string technicalName, CancellationToken cancellationToken = default);
+
+ ///
+ /// Claims an entitlement, and doubles as the poll for one still processing: the endpoint is
+ /// idempotent, so calling again with the same arguments returns the current grant status.
+ ///
+ Task ClaimEntitlementAsync(EntitlementClaimArgs args, CancellationToken cancellationToken = default);
+}
+
+class MarketplaceEditorApiClient : IMarketplaceEditorApiClient
+{
+ const string k_ProductsPath = "/api/marketplace/products/editor/v1";
+ const string k_EntitlementsPath = "/api/marketplace/entitlements/editor/v1";
+ const int k_NotFoundResponseCode = 404;
+
+ readonly string m_BaseUrl;
+ readonly IMarketplaceWebRequestSender m_RequestSender;
+ readonly IServiceTokenProvider m_TokenProvider;
+
+ public MarketplaceEditorApiClient()
+ : this(
+ UnityConnect.instance.GetConfigurationURL(CloudConfigUrl.ServicesGateway),
+ new MarketplaceWebRequestSender(),
+ new ServiceTokenProvider())
+ {
+ }
+
+ public MarketplaceEditorApiClient(string baseUrl, IMarketplaceWebRequestSender requestSender, IServiceTokenProvider tokenProvider)
+ {
+ m_BaseUrl = baseUrl;
+ m_RequestSender = requestSender;
+ m_TokenProvider = tokenProvider;
+ }
+
+ public async Task GetPackageInfoAsync(string technicalName, CancellationToken cancellationToken = default)
+ {
+ if (string.IsNullOrEmpty(technicalName))
+ throw new ArgumentException("A technical name is required.", nameof(technicalName));
+
+ var url = m_BaseUrl + k_ProductsPath + "/packages/" + Uri.EscapeDataString(technicalName);
+ var token = await m_TokenProvider.GetServiceTokenAsync(cancellationToken);
+ var response = await m_RequestSender.GetAsync(url, token, cancellationToken);
+
+ if (response.responseCode == k_NotFoundResponseCode)
+ return null;
+
+ return MarketplacePackageInfo.FromFields(ReadSuccessBody(response, url));
+ }
+
+ public async Task ClaimEntitlementAsync(EntitlementClaimArgs args, CancellationToken cancellationToken = default)
+ {
+ if (string.IsNullOrEmpty(args.organizationId))
+ throw new ArgumentException("An organization id is required to claim an entitlement.", nameof(args));
+ if (string.IsNullOrEmpty(args.productId))
+ throw new ArgumentException("A product id is required to claim an entitlement.", nameof(args));
+
+ var url = m_BaseUrl + k_EntitlementsPath + "/entitlements/claim/product";
+ var body = Json.Serialize(new Dictionary
+ {
+ { "organizationId", args.organizationId },
+ { "productId", args.productId },
+ });
+
+ var token = await m_TokenProvider.GetServiceTokenAsync(cancellationToken);
+ var response = await m_RequestSender.PostJsonAsync(url, body, token, cancellationToken);
+
+ return EntitlementClaimResult.FromFields(ReadSuccessBody(response, url));
+ }
+
+ static Dictionary ReadSuccessBody(MarketplaceWebResponse response, string url)
+ {
+ if (response.responseCode < 200 || response.responseCode >= 300)
+ throw new MarketplaceApiException(MarketplaceApiError.FromResponse(response));
+
+ var fields = MarketplaceJson.Parse(response.body);
+ if (fields == null)
+ throw new MarketplaceApiException(MarketplaceApiError.Unparsable(response, url));
+
+ return fields;
+ }
+}
diff --git a/Editor/Mono/BuildProfile/MarketplaceEditorApi/MarketplaceEditorApiModels.cs b/Editor/Mono/BuildProfile/MarketplaceEditorApi/MarketplaceEditorApiModels.cs
new file mode 100644
index 0000000000..0135992503
--- /dev/null
+++ b/Editor/Mono/BuildProfile/MarketplaceEditorApi/MarketplaceEditorApiModels.cs
@@ -0,0 +1,246 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using System.Collections.Generic;
+
+namespace UnityEditor.Marketplace;
+
+enum ClaimStatus
+{
+ Unknown,
+ Granted,
+ Processing,
+}
+
+readonly struct EntitlementClaimArgs
+{
+ ///
+ /// The organization ID claiming the entitlement.
+ ///
+ public string organizationId { get; }
+
+ ///
+ /// The product ID being claimed.
+ ///
+ public string productId { get; }
+
+ public EntitlementClaimArgs(string organizationId, string productId)
+ {
+ this.organizationId = organizationId;
+ this.productId = productId;
+ }
+}
+
+class EntitlementClaimResult
+{
+ const string k_GrantedStatus = "granted";
+ const string k_ProcessingStatus = "processing";
+
+ ///
+ /// Opaque stable identifier for this claim (base64url of organizationId:productId).
+ ///
+ public string id { get; }
+ public ClaimStatus status { get; }
+
+ EntitlementClaimResult(string id, ClaimStatus status)
+ {
+ this.id = id;
+ this.status = status;
+ }
+
+ public static EntitlementClaimResult FromFields(Dictionary fields)
+ {
+ return new EntitlementClaimResult(
+ MarketplaceJson.ReadString(fields, "id"),
+ ParseStatus(MarketplaceJson.ReadString(fields, "status")));
+ }
+
+ static ClaimStatus ParseStatus(string status)
+ {
+ switch (status)
+ {
+ case k_GrantedStatus:
+ return ClaimStatus.Granted;
+
+ case k_ProcessingStatus:
+ return ClaimStatus.Processing;
+
+ default:
+ return ClaimStatus.Unknown;
+ }
+ }
+}
+
+class MarketplacePublisherInfo
+{
+ ///
+ /// Publisher ID.
+ ///
+ public string id { get; }
+
+ ///
+ /// Publisher display name.
+ ///
+ public string name { get; }
+
+ MarketplacePublisherInfo(string id, string name)
+ {
+ this.id = id;
+ this.name = name;
+ }
+
+ public static MarketplacePublisherInfo FromFields(Dictionary fields)
+ {
+ if (fields == null)
+ return null;
+
+ return new MarketplacePublisherInfo(
+ MarketplaceJson.ReadString(fields, "id"),
+ MarketplaceJson.ReadString(fields, "name"));
+ }
+}
+
+class MarketplaceLicenseInfo
+{
+ ///
+ /// License ID.
+ ///
+ public string id { get; }
+
+ ///
+ /// License display name.
+ ///
+ public string name { get; }
+
+ ///
+ /// URL to the full license text. Optional.
+ ///
+ public string url { get; }
+
+ ///
+ /// Inline license terms. Optional.
+ ///
+ public string terms { get; }
+
+ MarketplaceLicenseInfo(string id, string name, string url, string terms)
+ {
+ this.id = id;
+ this.name = name;
+ this.url = url;
+ this.terms = terms;
+ }
+
+ public static MarketplaceLicenseInfo FromFields(Dictionary fields)
+ {
+ if (fields == null)
+ return null;
+
+ return new MarketplaceLicenseInfo(
+ MarketplaceJson.ReadString(fields, "id"),
+ MarketplaceJson.ReadString(fields, "name"),
+ MarketplaceJson.ReadString(fields, "url"),
+ MarketplaceJson.ReadString(fields, "terms"));
+ }
+}
+
+class MarketplaceEntitlementInfo
+{
+ ///
+ /// Whether the authenticated user is entitled to the product. Optional. Null when the
+ /// check could not be performed, which is not the same as not being entitled. Callers
+ /// must treat this as a tri-state and consult .
+ ///
+ public bool? hasEntitlement { get; }
+
+ ///
+ /// Error message when entitlement check could not be performed (e.g. not authenticated).
+ /// Optional.
+ ///
+ public string error { get; }
+
+ MarketplaceEntitlementInfo(bool? hasEntitlement, string error)
+ {
+ this.hasEntitlement = hasEntitlement;
+ this.error = error;
+ }
+
+ public static MarketplaceEntitlementInfo FromFields(Dictionary fields)
+ {
+ if (fields == null)
+ return null;
+
+ return new MarketplaceEntitlementInfo(
+ MarketplaceJson.ReadNullableBool(fields, "hasEntitlement"),
+ MarketplaceJson.ReadString(fields, "error"));
+ }
+}
+
+class MarketplacePackageInfo
+{
+ ///
+ /// Marketplace product ID. Where the package is listed under a product, this is that parent
+ /// product's ID rather than the one the registry reports for the package itself.
+ ///
+ public string productId { get; }
+
+ ///
+ /// Product display name.
+ ///
+ public string name { get; }
+
+ ///
+ /// Short product summary.
+ ///
+ public string summary { get; }
+
+ public string publisherId { get; }
+ public string licenseId { get; }
+
+ ///
+ /// Card thumbnail URL.
+ ///
+ public string thumbnail { get; }
+
+ public MarketplacePublisherInfo publisher { get; }
+ public MarketplaceLicenseInfo license { get; }
+ public MarketplaceEntitlementInfo entitlement { get; }
+
+ MarketplacePackageInfo(
+ string productId,
+ string name,
+ string summary,
+ string publisherId,
+ string licenseId,
+ string thumbnail,
+ MarketplacePublisherInfo publisher,
+ MarketplaceLicenseInfo license,
+ MarketplaceEntitlementInfo entitlement)
+ {
+ this.productId = productId;
+ this.name = name;
+ this.summary = summary;
+ this.publisherId = publisherId;
+ this.licenseId = licenseId;
+ this.thumbnail = thumbnail;
+ this.publisher = publisher;
+ this.license = license;
+ this.entitlement = entitlement;
+ }
+
+ public static MarketplacePackageInfo FromFields(Dictionary fields)
+ {
+ var expanded = MarketplaceJson.ReadObject(fields, "expanded");
+
+ return new MarketplacePackageInfo(
+ MarketplaceJson.ReadString(fields, "productId"),
+ MarketplaceJson.ReadString(fields, "name"),
+ MarketplaceJson.ReadString(fields, "summary"),
+ MarketplaceJson.ReadString(fields, "publisherId"),
+ MarketplaceJson.ReadString(fields, "licenseId"),
+ MarketplaceJson.ReadString(fields, "thumbnail"),
+ MarketplacePublisherInfo.FromFields(MarketplaceJson.ReadObject(expanded, "publisher")),
+ MarketplaceLicenseInfo.FromFields(MarketplaceJson.ReadObject(expanded, "license")),
+ MarketplaceEntitlementInfo.FromFields(MarketplaceJson.ReadObject(expanded, "entitlement")));
+ }
+}
diff --git a/Editor/Mono/BuildProfile/MarketplaceEditorApi/MarketplaceJson.cs b/Editor/Mono/BuildProfile/MarketplaceEditorApi/MarketplaceJson.cs
new file mode 100644
index 0000000000..cfeb8dc70d
--- /dev/null
+++ b/Editor/Mono/BuildProfile/MarketplaceEditorApi/MarketplaceJson.cs
@@ -0,0 +1,66 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using System;
+using System.Collections.Generic;
+
+namespace UnityEditor.Marketplace;
+
+static class MarketplaceJson
+{
+ public static Dictionary Parse(string json)
+ {
+ if (string.IsNullOrEmpty(json))
+ return null;
+
+ try
+ {
+ return Json.Deserialize(json) as Dictionary;
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+ }
+
+ public static Dictionary ReadObject(Dictionary fields, string key)
+ {
+ if (fields == null)
+ return null;
+
+ return fields.TryGetValue(key, out var value) ? value as Dictionary : null;
+ }
+
+ public static string ReadString(Dictionary fields, string key)
+ {
+ if (fields == null)
+ return null;
+
+ return fields.TryGetValue(key, out var value) ? value as string : null;
+ }
+
+ public static int ReadInt(Dictionary fields, string key, int fallback)
+ {
+ if (fields == null || !fields.TryGetValue(key, out var value) || value == null)
+ return fallback;
+
+ // MiniJson yields long for integral values and double otherwise.
+ try
+ {
+ return Convert.ToInt32(value);
+ }
+ catch (Exception)
+ {
+ return fallback;
+ }
+ }
+
+ public static bool? ReadNullableBool(Dictionary fields, string key)
+ {
+ if (fields == null || !fields.TryGetValue(key, out var value))
+ return null;
+
+ return value as bool?;
+ }
+}
diff --git a/Editor/Mono/BuildProfile/PlatformPackageServiceInfoProvider.cs b/Editor/Mono/BuildProfile/PlatformPackageServiceInfoProvider.cs
index 12972b27bd..e5f033d88f 100644
--- a/Editor/Mono/BuildProfile/PlatformPackageServiceInfoProvider.cs
+++ b/Editor/Mono/BuildProfile/PlatformPackageServiceInfoProvider.cs
@@ -6,15 +6,15 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEditor.Connect;
+using UnityEditor.Marketplace;
using UnityEditor.PackageManager;
-using UnityEditor.PackageManager.UI.Internal;
using UnityEngine;
using UnityEngine.Bindings;
namespace UnityEditor.Build.Profile;
[VisibleToOtherModules]
-internal class PlatformPackageServiceInfoProvider
+internal class PlatformPackageServiceInfoProvider : IPackageServiceInfoProvider
{
public event Action OnPackageInfoUpdated;
public event Action OnUserLoginStateChanged;
@@ -27,29 +27,35 @@ public enum RequestState
Timeout
}
- List m_PackageServiceInfoList = new List();
- readonly HttpClientFactory m_HttpClientFactory = new HttpClientFactory();
- readonly DateTimeProxy m_DateTimeProxy = new DateTimeProxy();
- readonly UnityConnectProxy m_UnityConnectProxy = new UnityConnectProxy();
- readonly UnityOAuthProxy m_UnityOAuthProxy = new UnityOAuthProxy();
- readonly AssetStoreOAuth m_AssetStoreOAuth;
+ readonly Dictionary m_PackageServiceInfos = new Dictionary();
+
+ ///
+ /// Product info requests in flight keyed by package name.
+ ///
+ readonly Dictionary> m_InFlightProductInfo = new Dictionary>();
+
+ ///
+ /// Claims in flight, keyed by organization and product.
+ ///
+ readonly Dictionary> m_InFlightClaims = new Dictionary>();
+
+ Task m_InFlightOrganizationIdWait;
+
+ IMarketplaceEditorApiClient m_ApiClient;
+ IMarketplaceEditorApiClient apiClient => m_ApiClient ??= new MarketplaceEditorApiClient();
public bool isUserLoggedIn => UnityConnect.instance.loggedIn;
public void ShowLogin() => UnityConnect.instance.ShowLogin();
- string m_Host = string.Empty;
- string host
- {
- get
- {
- if (string.IsNullOrEmpty(m_Host))
- m_Host = m_UnityConnectProxy.GetConfigurationURL(Connect.CloudConfigUrl.CloudPackagesApi);
- return m_Host;
- }
- }
-
const int k_TimeoutSeconds = 30;
- const string k_ProductInfoUri = "/-/api/product";
+
+ ///
+ /// Must stay below BuildProfileEntitlementClaimInfo.k_AttemptTimeoutSeconds. The wait runs inside a
+ /// claim attempt, so if the attempt times out first the caller sees a cancellation instead of
+ /// NoOrganizationIdException, and the stage reports a timeout rather than the missing organization.
+ ///
+ const int k_OrganizationIdTimeoutSeconds = 20;
+ const int k_OrganizationIdPollMilliseconds = 250;
class PlatformPackageServiceInfo
{
@@ -57,6 +63,7 @@ class PlatformPackageServiceInfo
public long productId { get; }
public PackageManager.PackageInfo packageInfo { get; }
public Texture thumbnail;
+ public MarketplacePackageInfo marketplaceInfo;
public PlatformPackageServiceInfo(PackageManager.PackageInfo packageInfo)
{
@@ -78,14 +85,13 @@ public void Dispose()
internal PlatformPackageServiceInfoProvider()
{
- m_AssetStoreOAuth = new AssetStoreOAuth(m_DateTimeProxy, m_UnityConnectProxy, m_UnityOAuthProxy, m_HttpClientFactory);
UnityConnect.instance.StateChanged += OnStateChanged;
}
public void Dispose()
{
UnityConnect.instance.StateChanged -= OnStateChanged;
- foreach (var packageInfo in m_PackageServiceInfoList)
+ foreach (var packageInfo in m_PackageServiceInfos.Values)
packageInfo.Dispose();
}
@@ -108,18 +114,36 @@ public bool FetchInfo()
return true;
}
+ public bool HasPackageInfo(string packageName) => GetPackageInfo(packageName) != null;
+
public PackageManager.PackageInfo GetPackageInfo(string packageName)
{
- var packageServiceInfo = m_PackageServiceInfoList.Find(p => p.name == packageName);
- return packageServiceInfo?.packageInfo;
+ return m_PackageServiceInfos.TryGetValue(packageName, out var packageServiceInfo) ? packageServiceInfo.packageInfo : null;
}
public Texture GetThumbnail(string packageName)
{
- var packageServiceInfo = m_PackageServiceInfoList.Find(p => p.name == packageName);
- return packageServiceInfo?.thumbnail;
+ return m_PackageServiceInfos.TryGetValue(packageName, out var packageServiceInfo) ? packageServiceInfo.thumbnail : null;
}
+ public string GetMarketplaceProductId(string packageName) => GetMarketplaceInfo(packageName)?.productId;
+
+ ///
+ /// Whether the user already holds the entitlement for the package. Null means unknown, which is not the same
+ /// as not entitled and must not be collapsed into it.
+ ///
+ public bool? HasMarketplaceEntitlement(string packageName) => GetMarketplaceInfo(packageName)?.entitlement?.hasEntitlement;
+
+ ///
+ /// URL of the license the package is distributed under. Null when the product names no license.
+ ///
+ public string GetLicenseUrl(string packageName) => GetMarketplaceInfo(packageName)?.license?.url;
+
+ ///
+ /// Short name to label with.
+ ///
+ public string GetLicenseName(string packageName) => GetMarketplaceInfo(packageName)?.license?.name;
+
///
/// True when the package uses the Enterprise licensing model, meaning access is granted by an entitlement.
///
@@ -128,6 +152,128 @@ public bool IsEnterprisePackage(string packageName)
return GetPackageInfo(packageName)?.entitlements?.licensingModel == EntitlementLicensingModel.Enterprise;
}
+ ///
+ /// True when the package uses the Asset Store licensing model, meaning access depends on an
+ /// entitlement the user can claim.
+ ///
+ public bool IsAssetStorePackage(string packageName)
+ {
+ return GetPackageInfo(packageName)?.entitlements?.licensingModel == EntitlementLicensingModel.AssetStore;
+ }
+
+ public Task GetOrFetchProductInfoAsync(string packageName)
+ {
+ var resolved = GetMarketplaceInfo(packageName);
+ if (resolved != null)
+ return Task.FromResult(resolved);
+
+ if (m_InFlightProductInfo.TryGetValue(packageName, out var inFlight))
+ return inFlight;
+
+ var request = FetchProductInfoCoreAsync(packageName);
+ if (!request.IsCompleted)
+ m_InFlightProductInfo[packageName] = request;
+
+ return request;
+ }
+
+ public async Task ClaimEntitlementAsync(string productId)
+ {
+ var claimOrganizationId = await GetOrganizationIdAsync();
+ var key = claimOrganizationId + ":" + productId;
+
+ if (m_InFlightClaims.TryGetValue(key, out var inFlight))
+ return await inFlight;
+
+ var request = ClaimEntitlementCoreAsync(key, claimOrganizationId, productId);
+ if (!request.IsCompleted)
+ m_InFlightClaims[key] = request;
+
+ return await request;
+ }
+
+ MarketplacePackageInfo GetMarketplaceInfo(string packageName)
+ {
+ return m_PackageServiceInfos.TryGetValue(packageName, out var packageServiceInfo) ? packageServiceInfo.marketplaceInfo : null;
+ }
+
+ async Task FetchProductInfoCoreAsync(string packageName)
+ {
+ try
+ {
+ var productInfo = await apiClient.GetPackageInfoAsync(packageName);
+ if (productInfo != null && m_PackageServiceInfos.TryGetValue(packageName, out var packageServiceInfo))
+ packageServiceInfo.marketplaceInfo = productInfo;
+
+ return productInfo;
+ }
+ finally
+ {
+ m_InFlightProductInfo.Remove(packageName);
+ }
+ }
+
+ async Task ClaimEntitlementCoreAsync(string key, string claimOrganizationId, string productId)
+ {
+ try
+ {
+ return await apiClient.ClaimEntitlementAsync(new EntitlementClaimArgs(claimOrganizationId, productId));
+ }
+ finally
+ {
+ m_InFlightClaims.Remove(key);
+ }
+ }
+
+ ///
+ /// The project-linked organization's Genesis ID, which is what the services gateway expects an
+ /// entitlement to be claimed against. Empty until the project state machine has resolved it.
+ ///
+ string organizationId => CloudProjectSettings.organizationKey;
+
+ Task GetOrganizationIdAsync()
+ {
+ if (m_InFlightOrganizationIdWait != null)
+ return m_InFlightOrganizationIdWait;
+
+ var request = WaitForOrganizationIdAsync();
+ if (!request.IsCompleted)
+ m_InFlightOrganizationIdWait = request;
+
+ return request;
+ }
+
+ ///
+ /// Polls until the organization ID resolves or it is clear that none is coming.
+ /// Required for claiming entitlements for
+ /// at project start up and batch mode.
+ ///
+ async Task WaitForOrganizationIdAsync()
+ {
+ try
+ {
+ var deadline = DateTime.UtcNow + TimeSpan.FromSeconds(k_OrganizationIdTimeoutSeconds);
+ while (true)
+ {
+ var resolved = organizationId;
+ if (!string.IsNullOrEmpty(resolved))
+ return resolved;
+
+ if (UnityConnect.instance.isUserInfoReady && !UnityConnect.instance.loggedIn)
+ throw new NoOrganizationIdException("No user is signed in.");
+
+ if (UnityConnect.instance.projectInfo.valid || DateTime.UtcNow >= deadline)
+ throw new NoOrganizationIdException("The project's organization could not be determined.");
+
+ await Task.Delay(k_OrganizationIdPollMilliseconds);
+ }
+ }
+ finally
+ {
+ m_InFlightOrganizationIdWait = null;
+ }
+ }
+
async void FetchPackageServiceInfo()
{
var packageInfoSearchTasks = new List>();
@@ -138,11 +284,10 @@ async void FetchPackageServiceInfo()
var packageInfo = PackageManager.PackageInfo.FindForPackageName(name);
if (packageInfo != null)
{
- var packageServiceInfo = new PlatformPackageServiceInfo(packageInfo);
- m_PackageServiceInfoList.Add(packageServiceInfo);
+ var packageServiceInfo = Store(packageInfo);
if (packageServiceInfo.productId != -1)
- packageInfoSearchTasks.Add(FetchThumbnailFromAssetStoreAsync(packageServiceInfo.productId));
+ packageInfoSearchTasks.Add(FetchProductInfoAsync(packageServiceInfo.name));
}
else
packageInfoSearchTasks.Add(FetchPackageServiceInfoFromServerAsync(name));
@@ -157,7 +302,7 @@ async void FetchPackageServiceInfo()
var searchTask = Task.WhenAll(packageInfoSearchTasks);
var timeoutTask = Task.Delay(TimeSpan.FromSeconds(k_TimeoutSeconds));
-
+
var completedTask = await Task.WhenAny(searchTask, timeoutTask);
if (completedTask == timeoutTask)
@@ -205,13 +350,11 @@ async void OnUpdate()
try
{
- var packageInformation = new PlatformPackageServiceInfo(packageInfo);
- m_PackageServiceInfoList.Add(packageInformation);
-
+ var packageInformation = Store(packageInfo);
if (packageInformation.productId != -1)
{
- var thumbnailResult = await FetchThumbnailFromAssetStoreAsync(packageInformation.productId);
- if (!thumbnailResult)
+ var productInfoResult = await FetchProductInfoAsync(packageInformation.name);
+ if (!productInfoResult)
{
tcs.SetResult(false);
return;
@@ -234,64 +377,39 @@ async void OnUpdate()
return tcs.Task;
}
- Task FetchThumbnailFromAssetStoreAsync(long productId)
+ async Task FetchProductInfoAsync(string packageName)
{
- var tcs = new TaskCompletionSource();
- FetchProductInfoFromAssetStore(productId, async result =>
+ try
{
- try
- {
- if (result == null || result.ContainsKey("errorMessage"))
- {
- tcs.SetResult(false);
- return;
- }
-
- var imageUrl = GetThumbnailUrlFromProductDetails(result);
- if (string.IsNullOrEmpty(imageUrl))
- {
- tcs.SetResult(false);
- return;
- }
+ var productInfo = await GetOrFetchProductInfoAsync(packageName);
+ if (productInfo == null || !m_PackageServiceInfos.TryGetValue(packageName, out var packageServiceInfo))
+ return false;
- var image = await DownloadImageByUrlAsync(imageUrl);
- if (image == null)
- {
- tcs.SetResult(false);
- return;
- }
+ if (string.IsNullOrEmpty(productInfo.thumbnail))
+ return false;
- var packageServiceInfo = m_PackageServiceInfoList.Find(p => p.productId == productId);
- if (packageServiceInfo == null)
- {
- tcs.SetResult(false);
- return;
- }
+ var image = await DownloadImageByUrlAsync(productInfo.thumbnail);
+ if (image == null)
+ return false;
- image.hideFlags = HideFlags.HideAndDontSave;
- packageServiceInfo.thumbnail = image;
- tcs.SetResult(true);
- }
- catch
- {
- tcs.SetResult(false);
- }
- });
- return tcs.Task;
+ image.hideFlags = HideFlags.HideAndDontSave;
+ packageServiceInfo.thumbnail = image;
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
}
- string GetThumbnailUrlFromProductDetails(IDictionary productDetail)
+ PlatformPackageServiceInfo Store(PackageManager.PackageInfo packageInfo)
{
- if (productDetail == null)
- return string.Empty;
-
- var mainImageDictionary = productDetail.GetDictionary("mainImage");
- var thumbnailUrl = mainImageDictionary?.GetString("small_v2");
- if (string.IsNullOrEmpty(thumbnailUrl))
- return string.Empty;
+ var packageServiceInfo = new PlatformPackageServiceInfo(packageInfo);
+ if (m_PackageServiceInfos.TryGetValue(packageServiceInfo.name, out var existing))
+ existing.Dispose();
- thumbnailUrl = thumbnailUrl.Replace("//d2ujflorbtfzji.cloudfront.net/", "//assetstorev1-prd-cdn.unity3d.com/");
- return "http:" + thumbnailUrl;
+ m_PackageServiceInfos[packageServiceInfo.name] = packageServiceInfo;
+ return packageServiceInfo;
}
Task DownloadImageByUrlAsync(string url)
@@ -304,7 +422,7 @@ Task DownloadImageByUrlAsync(string url)
return tcs.Task;
}
- var httpRequest = m_HttpClientFactory.GetASyncHTTPClient(url);
+ var httpRequest = new AsyncHTTPClient(url);
httpRequest.doneCallback = httpClient =>
{
if (httpClient.IsSuccess() && httpClient.texture != null)
@@ -316,33 +434,4 @@ Task DownloadImageByUrlAsync(string url)
return tcs.Task;
}
-
- void FetchProductInfoFromAssetStore(long productId, Action> value)
- {
- var url = $"{host}{k_ProductInfoUri}/{productId}";
- var httpRequest = m_HttpClientFactory.GetASyncHTTPClient(url);
- m_AssetStoreOAuth.FetchAccessToken(
- token =>
- {
- httpRequest.tag = $"GetPlatformPackageProductDetail{productId}";
- httpRequest.header["Content-Type"] = "application/json";
- httpRequest.header["Authorization"] = "Bearer " + token.accessToken;
- httpRequest.doneCallback = request =>
- {
- if (!request.IsSuccess())
- {
- value?.Invoke(null);
- return;
- }
-
- var parsedResult = m_HttpClientFactory.ParseResponseAsDictionary(request);
- value?.Invoke(parsedResult);
- };
- httpRequest.Begin();
- },
- error =>
- {
- value?.Invoke(null);
- });
- }
}
diff --git a/Editor/Mono/BuildProfile/ReferencedComponent.cs b/Editor/Mono/BuildProfile/ReferencedComponent.cs
new file mode 100644
index 0000000000..d203b2ad5e
--- /dev/null
+++ b/Editor/Mono/BuildProfile/ReferencedComponent.cs
@@ -0,0 +1,36 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using System;
+using UnityEngine;
+using UnityEngine.Bindings;
+
+namespace UnityEditor.Build.Profile
+{
+ ///
+ /// Proxy sub-asset linking a build profile to a standalone on-disk settings asset instead of an
+ /// embedded settings object. Resolved by .
+ ///
+ [VisibleToOtherModules("UnityEditor.BuildProfileModule")]
+ sealed class ReferencedComponent : ScriptableObject
+ {
+ [SerializeField] ScriptableObject m_Reference;
+ [SerializeField] MonoScript m_ReferenceType;
+
+ public ScriptableObject reference
+ {
+ get => m_Reference;
+ set => m_Reference = value;
+ }
+
+ public Type referenceType
+ {
+ get => m_ReferenceType != null ? m_ReferenceType.GetClass() : null;
+ set => m_ReferenceType = value != null ? MonoScript.FromType(value) : null;
+ }
+
+ public bool Matches(Type componentType) =>
+ componentType.IsInstanceOfType(reference) || componentType.IsAssignableFrom(referenceType);
+ }
+}
diff --git a/Editor/Mono/BuildProfile/SharedPlatformSettings.cs b/Editor/Mono/BuildProfile/SharedPlatformSettings.cs
index 7b0cb4d176..8a17bf7b87 100644
--- a/Editor/Mono/BuildProfile/SharedPlatformSettings.cs
+++ b/Editor/Mono/BuildProfile/SharedPlatformSettings.cs
@@ -491,7 +491,7 @@ public void CopySharedSettingsToBuildProfile(BuildProfile profile)
platformSettings.development = development;
platformSettings.connectProfiler = connectProfiler;
platformSettings.buildWithDeepProfilingSupport = buildWithDeepProfilingSupport;
- if (!BuildProfileModuleUtil.IsStandalonePlatform(buildTarget))
+ if (!BuildProfileModuleUtil.IsPlatformSupportedByCoverage(buildTarget))
platformSettings.buildWithCodeCoverage = false;
else
platformSettings.buildWithCodeCoverage = buildWithCodeCoverage;
diff --git a/Editor/Mono/Clipboard/Clipboard.cs b/Editor/Mono/Clipboard/Clipboard.cs
index bc9be4774e..54321cd4cd 100644
--- a/Editor/Mono/Clipboard/Clipboard.cs
+++ b/Editor/Mono/Clipboard/Clipboard.cs
@@ -19,6 +19,10 @@ namespace UnityEditor
internal static partial class Clipboard
{
[AutoStaticsCleanupOnCodeReload]
+ // Parse cache of the system clipboard string: FetchState compares it against
+ // EditorGUIUtility.systemCopyBuffer and rebuilds the state whenever they differ, so the clipboard
+ // contents themselves are never held here.
+ [IgnoreForUAL0015("Parse cache of the system clipboard, rebuilt by FetchState on the next access")]
static ClipboardState m_State = new ClipboardState();
public static bool hasLong
diff --git a/Editor/Mono/Clipboard/ClipboardContextMenu.cs b/Editor/Mono/Clipboard/ClipboardContextMenu.cs
index f2ac0e2e1e..74a447dfa7 100644
--- a/Editor/Mono/Clipboard/ClipboardContextMenu.cs
+++ b/Editor/Mono/Clipboard/ClipboardContextMenu.cs
@@ -316,20 +316,20 @@ internal static void SetupPropertyCopyPaste(SerializedProperty property, Generic
}
}
- static readonly GUIContent kCopyContent = EditorGUIUtility.TrTextContent("Copy");
- static readonly GUIContent kPasteContent = EditorGUIUtility.TrTextContent("Paste");
- static readonly GUIContent kCopyEulerContent = EditorGUIUtility.TrTextContent("Copy Euler Angles");
- static readonly GUIContent kCopyQuatContent = EditorGUIUtility.TrTextContent("Copy Quaternion");
- static readonly GUIContent kCopyPathContent = EditorGUIUtility.TrTextContent("Copy Path");
- static readonly GUIContent kCopyGuidContent = EditorGUIUtility.TrTextContent("Copy GUID");
- static readonly GUIContent kPasteMinColorContent = EditorGUIUtility.TrTextContent("Paste Min Color");
- static readonly GUIContent kPasteMaxColorContent = EditorGUIUtility.TrTextContent("Paste Max Color");
- static readonly GUIContent kPasteMinGradientContent = EditorGUIUtility.TrTextContent("Paste Min Gradient");
- static readonly GUIContent kPasteMaxGradientContent = EditorGUIUtility.TrTextContent("Paste Max Gradient");
- static readonly GUIContent kPasteMinScalarContent = EditorGUIUtility.TrTextContent("Paste Min Scalar");
- static readonly GUIContent kPasteMaxScalarContent = EditorGUIUtility.TrTextContent("Paste Max Scalar");
- static readonly GUIContent kPasteMinCurveContent = EditorGUIUtility.TrTextContent("Paste Min Curve");
- static readonly GUIContent kPasteMaxCurveContent = EditorGUIUtility.TrTextContent("Paste Max Curve");
+ static readonly GUIContent kCopyContent = L10n.TextContent("Copy", null, null, null);
+ static readonly GUIContent kPasteContent = L10n.TextContent("Paste", null, null, null);
+ static readonly GUIContent kCopyEulerContent = L10n.TextContent("Copy Euler Angles", null, null, null);
+ static readonly GUIContent kCopyQuatContent = L10n.TextContent("Copy Quaternion", null, null, null);
+ static readonly GUIContent kCopyPathContent = L10n.TextContent("Copy Path", null, null, null);
+ static readonly GUIContent kCopyGuidContent = L10n.TextContent("Copy GUID", null, null, null);
+ static readonly GUIContent kPasteMinColorContent = L10n.TextContent("Paste Min Color", null, null, null);
+ static readonly GUIContent kPasteMaxColorContent = L10n.TextContent("Paste Max Color", null, null, null);
+ static readonly GUIContent kPasteMinGradientContent = L10n.TextContent("Paste Min Gradient", null, null, null);
+ static readonly GUIContent kPasteMaxGradientContent = L10n.TextContent("Paste Max Gradient", null, null, null);
+ static readonly GUIContent kPasteMinScalarContent = L10n.TextContent("Paste Min Scalar", null, null, null);
+ static readonly GUIContent kPasteMaxScalarContent = L10n.TextContent("Paste Max Scalar", null, null, null);
+ static readonly GUIContent kPasteMinCurveContent = L10n.TextContent("Paste Min Curve", null, null, null);
+ static readonly GUIContent kPasteMaxCurveContent = L10n.TextContent("Paste Max Curve", null, null, null);
static void AddSeparator(GenericMenu menu)
{
diff --git a/Editor/Mono/CodeEditor/DefaultExternalCodeEditor.cs b/Editor/Mono/CodeEditor/DefaultExternalCodeEditor.cs
index 6cbc36ba63..b3d1ea2b07 100644
--- a/Editor/Mono/CodeEditor/DefaultExternalCodeEditor.cs
+++ b/Editor/Mono/CodeEditor/DefaultExternalCodeEditor.cs
@@ -15,7 +15,7 @@ namespace UnityEditor
{
internal class DefaultExternalCodeEditor : IExternalCodeEditor
{
- static readonly GUIContent k_ResetArguments = EditorGUIUtility.TrTextContent("Reset argument");
+ static readonly GUIContent k_ResetArguments = L10n.TextContent("Reset argument", null, null, null);
static readonly string[] supportedExtensions = { "json", "asmdef", "log", "cs", "uxml", "uss", "shader", "compute", "cginc", "hlsl", "glslinc", "template", "raytrace" };
static bool IsOSX => Application.platform == RuntimePlatform.OSXEditor;
static bool IsWindows => Application.platform == RuntimePlatform.WindowsEditor;
diff --git a/Editor/Mono/CompilationPipeline.bindings.cs b/Editor/Mono/CompilationPipeline.bindings.cs
index fd6c8b2922..710bbf215b 100644
--- a/Editor/Mono/CompilationPipeline.bindings.cs
+++ b/Editor/Mono/CompilationPipeline.bindings.cs
@@ -28,6 +28,7 @@ public static partial class CompilationPipeline
[FreeFunction]
internal static extern bool IsScriptDebugInfoEnabled();
+ [VisibleToOtherModules("UnityEditor.BurstModule")]
[FreeFunction]
internal static extern bool IsUsingMSBuild();
}
diff --git a/Editor/Mono/ConsoleWindow.cs b/Editor/Mono/ConsoleWindow.cs
index 1a2ead37d2..711e011c94 100644
--- a/Editor/Mono/ConsoleWindow.cs
+++ b/Editor/Mono/ConsoleWindow.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: Kernel not yet converted
using System;
using System.Globalization;
using System.Collections.Generic;
@@ -99,16 +98,16 @@ internal class Constants
[NoAutoStaticsCleanup] // lazy shared GUIStyle, safe to persist
public static GUIStyle ConsoleSearchNoResult;
- public static readonly GUIContent Clear = EditorGUIUtility.TrTextContent("Clear", "Clear console entries");
- public static readonly GUIContent ClearOnPlay = EditorGUIUtility.TrTextContent("Clear on Play");
- public static readonly GUIContent ClearOnBuild = EditorGUIUtility.TrTextContent("Clear on Build");
- public static readonly GUIContent ClearOnRecompile = EditorGUIUtility.TrTextContent("Clear on Recompile");
- public static readonly GUIContent Collapse = EditorGUIUtility.TrTextContent("Collapse", "Collapse identical entries");
- public static readonly GUIContent ErrorPause = EditorGUIUtility.TrTextContent("Error Pause", "Pause Play Mode on error");
- public static readonly GUIContent StopForAssert = EditorGUIUtility.TrTextContent("Stop for Assert");
- public static readonly GUIContent StopForError = EditorGUIUtility.TrTextContent("Stop for Error");
- public static readonly GUIContent UseMonospaceFont = EditorGUIUtility.TrTextContent("Use Monospace font");
- public static readonly GUIContent StripLoggingCallstack = EditorGUIUtility.TrTextContent("Strip logging callstack");
+ public static readonly GUIContent Clear = L10n.TextContent("Clear", "Clear console entries", null, null);
+ public static readonly GUIContent ClearOnPlay = L10n.TextContent("Clear on Play", null, null, null);
+ public static readonly GUIContent ClearOnBuild = L10n.TextContent("Clear on Build", null, null, null);
+ public static readonly GUIContent ClearOnRecompile = L10n.TextContent("Clear on Recompile", null, null, null);
+ public static readonly GUIContent Collapse = L10n.TextContent("Collapse", "Collapse identical entries", null, null);
+ public static readonly GUIContent ErrorPause = L10n.TextContent("Error Pause", "Pause Play Mode on error", null, null);
+ public static readonly GUIContent StopForAssert = L10n.TextContent("Stop for Assert", null, null, null);
+ public static readonly GUIContent StopForError = L10n.TextContent("Stop for Error", null, null, null);
+ public static readonly GUIContent UseMonospaceFont = L10n.TextContent("Use Monospace font", null, null, null);
+ public static readonly GUIContent StripLoggingCallstack = L10n.TextContent("Strip logging callstack", null, null, null);
public static int LogStyleLineCount
{
@@ -1252,10 +1251,10 @@ public void ToggleLogStackTracesForAll(object userData)
public void AddItemsToMenu(GenericMenu menu)
{
- menu.AddItem(EditorGUIUtility.TrTextContent("Open Player Log"), false, UnityEditorInternal.InternalEditorUtility.OpenPlayerConsole);
- menu.AddItem(EditorGUIUtility.TrTextContent("Open Editor Log"), false, UnityEditorInternal.InternalEditorUtility.OpenEditorConsole);
+ menu.AddItem(L10n.TextContent("Open Player Log", null, null, null), false, UnityEditorInternal.InternalEditorUtility.OpenPlayerConsole);
+ menu.AddItem(L10n.TextContent("Open Editor Log", null, null, null), false, UnityEditorInternal.InternalEditorUtility.OpenEditorConsole);
- menu.AddItem(EditorGUIUtility.TrTextContent("Show Timestamp"), HasFlag(ConsoleFlags.ShowTimestamp), SetTimestamp);
+ menu.AddItem(L10n.TextContent("Show Timestamp", null, null, null), HasFlag(ConsoleFlags.ShowTimestamp), SetTimestamp);
if (m_DevBuild)
{
@@ -1381,7 +1380,7 @@ private void AddStackTraceLoggingMenu(GenericMenu menu)
data.logType = logType;
data.stackTraceLogType = stackTraceLogType;
- menu.AddItem(EditorGUIUtility.TrTextContent("Stack Trace Logging/" + logType + "/" + stackTraceLogType), PlayerSettings.GetStackTraceLogType(logType) == stackTraceLogType,
+ menu.AddItem(L10n.TextContent("Stack Trace Logging/" + logType + "/" + stackTraceLogType, null, null, null), PlayerSettings.GetStackTraceLogType(logType) == stackTraceLogType,
ToggleLogStackTraces, data);
}
}
@@ -1398,7 +1397,7 @@ private void AddStackTraceLoggingMenu(GenericMenu menu)
foreach (StackTraceLogType stackTraceLogType in Enum.GetValues(typeof(StackTraceLogType)))
{
- menu.AddItem(EditorGUIUtility.TrTextContent("Stack Trace Logging/All/" + stackTraceLogType), (StackTraceLogType)stackTraceLogTypeForAll == stackTraceLogType,
+ menu.AddItem(L10n.TextContent("Stack Trace Logging/All/" + stackTraceLogType, null, null, null), (StackTraceLogType)stackTraceLogTypeForAll == stackTraceLogType,
ToggleLogStackTracesForAll, stackTraceLogType);
}
}
@@ -1571,4 +1570,3 @@ public void Dispose()
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/ConsoleWindowUtility.cs b/Editor/Mono/ConsoleWindowUtility.cs
index e0b17caf4f..e1126477df 100644
--- a/Editor/Mono/ConsoleWindowUtility.cs
+++ b/Editor/Mono/ConsoleWindowUtility.cs
@@ -10,6 +10,9 @@ namespace UnityEditor
public static partial class ConsoleWindowUtility
{
[AutoStaticsCleanupOnCodeReload]
+ // Subscribers attach through their own lifecycle and re-subscribe after a code reload, so the
+ // cleared invocation list refills itself.
+ [IgnoreForUAL0015("Event whose subscribers re-register through their own lifecycle after a code reload")]
public static event Action consoleLogsChanged;
public static void GetConsoleLogCounts(out int error, out int warn, out int log)
diff --git a/Editor/Mono/ContainerWindow.cs b/Editor/Mono/ContainerWindow.cs
index 5b1b8fa026..3895cd45a8 100644
--- a/Editor/Mono/ContainerWindow.cs
+++ b/Editor/Mono/ContainerWindow.cs
@@ -50,11 +50,27 @@ internal static class NativeHandleMarshaller
static internal bool macEditor => Application.platform == RuntimePlatform.OSXEditor;
[AutoStaticsCleanupOnCodeReload]
+ // Flag that is only true inside a modal window loop and set back to false when it ends, so false
+ // is the correct resting value; the next modal window sets it again.
+ [IgnoreForUAL0015("Transient modal-loop flag, set again by the next modal window")]
static internal bool s_Modal = false;
[AutoStaticsCleanupOnCodeReload]
+ // Cache of the window that Show(ShowMode.MainWindow) registered. Cleared on code reload, but the
+ // native window outlives the scope, so the getter below rediscovers it on the next access.
+ [IgnoreForUAL0015("Main-window cache rediscovered on demand by the mainWindow getter after cleanup")]
private static ContainerWindow s_MainWindow;
- static internal ContainerWindow mainWindow { get => s_MainWindow; }
+ static internal ContainerWindow mainWindow
+ {
+ get
+ {
+ // A code reload clears the cache without re-running Show(), so recover the still-live
+ // main window the way WindowLayout does rather than reporting that there is none.
+ if (!s_MainWindow)
+ s_MainWindow = WindowLayout.FindMainWindow();
+ return s_MainWindow;
+ }
+ }
static readonly string s_ContextMenuID = "UnityEditor.UIElements.EditorMenuExtensions+ContextMenu";
@@ -81,6 +97,9 @@ public ContainerWindow()
}
[AutoStaticsCleanupOnCodeReload]
+ // Callback installed by SetMppmCanCloseCallback from the multiplayer clone systems, which are
+ // recreated on load and install it again; a null slot means the default "can close" answer.
+ [IgnoreForUAL0015("Callback reinstalled by SetMppmCanCloseCallback when the clone systems are recreated")]
private static Func MppmCloseCallback;
internal void __internalAwake()
@@ -182,7 +201,9 @@ public void Show(ShowMode showMode, bool loadPosition, bool displayImmediately,
{
try
{
- if (showMode == ShowMode.MainWindow && s_MainWindow && s_MainWindow != this)
+ // Read through the mainWindow getter so the guard still sees a live main window after a
+ // code reload has cleared the s_MainWindow cache.
+ if (showMode == ShowMode.MainWindow && mainWindow && mainWindow != this)
throw new InvalidOperationException("Trying to create a second main window from layout when one already exists.");
bool useMousePos = showMode == ShowMode.AuxWindow;
@@ -360,7 +381,9 @@ internal static void SetMppmCanCloseCallback(Func mppmCanCloseCallback )
private static bool AskToClose(List allUnsaved)
{
- Debug.Assert(Application.isHumanControllingUs && allUnsaved.Count > 0);
+ // An installed interaction context answers dialogs programmatically, so it stands in for a human here.
+ Debug.Assert((Application.isHumanControllingUs || GlobalInteractionContext.TestAccess.isInstalled)
+ && allUnsaved.Count > 0);
DialogResult option = DialogResult.Cancel;
if (allUnsaved.Count == 1)
diff --git a/Editor/Mono/CutBoard.cs b/Editor/Mono/CutBoard.cs
index dfbd484a2e..76173044c8 100644
--- a/Editor/Mono/CutBoard.cs
+++ b/Editor/Mono/CutBoard.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: SceneManagement not yet converted
using System;
using Unity.Scripting.LifecycleManagement;
using UnityEngine;
@@ -109,7 +108,9 @@ internal static void PasteAsChildren(Transform parent, bool worldPositionStays)
}
}
+#pragma warning disable UAL0018 // hands the cut selection back to the user's selection, which owns objects that exist independently of the cut board; the cut board's own copy is dropped by Reset() on the next line
Selection.objects = m_SelectedObjects;
+#pragma warning restore UAL0018
// Reset cutBoard and greyed out gameObject list after paste
Reset();
}
@@ -127,7 +128,9 @@ private static void PasteAsSiblings(Transform target, bool worldPositionStays)
}
}
+#pragma warning disable UAL0018 // hands the cut selection back to the user's selection, which owns objects that exist independently of the cut board; the cut board's own copy is dropped by Reset() on the next line
Selection.objects = m_SelectedObjects;
+#pragma warning restore UAL0018
// Reset cutBoard and greyed out gameObject list after paste
Reset();
}
@@ -142,7 +145,9 @@ internal static void PasteToScene(Scene targetScene, Transform targetGO)
}
}
+#pragma warning disable UAL0018 // hands the cut selection back to the user's selection, which owns objects that exist independently of the cut board; the cut board's own copy is dropped by Reset() on the next line
Selection.objects = m_SelectedObjects;
+#pragma warning restore UAL0018
// Reset cutBoard and greyed out gameObject list after paste
Reset();
}
@@ -223,4 +228,3 @@ private static bool CanSetParent(Transform transform, Transform target)
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/DeploymentTargets/IDeploymentTargetsExtension.cs b/Editor/Mono/DeploymentTargets/IDeploymentTargetsExtension.cs
index 889cacaf57..883c384f63 100644
--- a/Editor/Mono/DeploymentTargets/IDeploymentTargetsExtension.cs
+++ b/Editor/Mono/DeploymentTargets/IDeploymentTargetsExtension.cs
@@ -62,10 +62,15 @@ internal enum DeploymentTargetStatus
Unknown,
}
- internal struct DeploymentTargetIdAndStatus
+ internal struct DeploymentTargetIdAndStatus : IEquatable
{
public DeploymentTargetId id;
public DeploymentTargetStatus status;
+
+ public bool Equals(DeploymentTargetIdAndStatus other)
+ {
+ return id == other.id && status == other.status;
+ }
}
internal interface IDeploymentLaunchResult
diff --git a/Editor/Mono/DragAndDrop.bindings.cs b/Editor/Mono/DragAndDrop.bindings.cs
index 36e92e0796..3702a50aa3 100644
--- a/Editor/Mono/DragAndDrop.bindings.cs
+++ b/Editor/Mono/DragAndDrop.bindings.cs
@@ -5,7 +5,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
-using System.Linq;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Scripting.LifecycleManagement;
using UnityEditorInternal;
@@ -53,6 +52,9 @@ public struct DragAndDropWindowTarget
public partial class DragAndDrop
{
[AutoStaticsCleanupOnCodeReload]
+ // Drag payload for the gesture in progress: PrepareStartDrag clears it and SetGenericData refills it
+ // at the start of every drag, and a code reload ends any drag, so nothing is left to lose.
+ [IgnoreForUAL0015("Per-gesture drag payload, cleared and refilled at the start of every drag")]
private static Hashtable s_GenericData;
[AutoStaticsCleanupOnCodeReload]
private static Dictionary> m_DropHandlers;
@@ -312,6 +314,45 @@ public static Object[] objectReferences
public static extern DragAndDropVisualMode visualMode { get; set; }
public static extern void AcceptDrag();
+
+ internal struct PreviewData
+ {
+ public EntityId ownerId { get; set; }
+ public IReadOnlyDictionary itemCenters { get; set; }
+ public float itemSize { get; set; }
+ public bool listMode { get; set; }
+
+ // Scrolled-to area, in the same space as itemCenters.
+ public Rect visibleRect { get; set; }
+
+ // Null when the caller has none; SetDragPreviewLabelStyle then falls back to the editor's label style.
+ public GUIStyle labelStyle { get; set; }
+ }
+
+ [NativeMethod("SetDragPreviewOwnerId")]
+ internal static extern void SetDragPreviewOwnerId(EntityId ownerId);
+
+ [NativeMethod("SetDragPreviewLayout")]
+ internal static extern void SetDragPreviewLayout(float[] itemOffsets, float itemSize, bool listMode, Rect visibleRect);
+
+ [NativeMethod("SetDragPreviewLabelStyle")]
+ private static extern void SetDragPreviewLabelStyle_Internal(Color color, string fontName, float fontSize);
+
+ internal static void SetDragPreviewLabelStyle(GUIStyle style)
+ {
+ var resolved = style ?? EditorStyles.label;
+ // Not GUI.skin?.font: ?. skips Unity's null check, so a destroyed skin would throw here
+ // rather than fall through.
+ var skin = GUI.skin;
+ var font = resolved.font ?? (skin != null ? skin.font : null) ?? EditorStyles.label.font;
+ var fontSize = 0f;
+
+ if (style != null)
+ fontSize = style.fontSize > 0 ? style.fontSize : (font ? font.fontSize : 0f);
+
+ SetDragPreviewLabelStyle_Internal(resolved.normal.textColor, font ? font.name : "", fontSize);
+ }
+
[NativeMethod("PrepareStartDrag")]
private static extern void PrepareStartDrag_Internal();
[NativeMethod("Cleanup")]
diff --git a/Editor/Mono/DrivenRectTransformUndo.cs b/Editor/Mono/DrivenRectTransformUndo.cs
index 43baf0f3c2..114ddedf50 100644
--- a/Editor/Mono/DrivenRectTransformUndo.cs
+++ b/Editor/Mono/DrivenRectTransformUndo.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: Kernel not yet converted
using UnityEngine;
using Unity.Scripting.LifecycleManagement;
@@ -10,6 +9,9 @@ namespace UnityEditor
{
internal partial class DrivenRectTransformUndo
{
+ // Both undo events are cleared on code reload, so these subscriptions have to be re-established on
+ // every load. A static constructor would only run once per domain, and driven RectTransform values
+ // would stop being refreshed around undo/redo after the first reload.
[OnCodeLoaded]
static void Initialize()
{
@@ -36,4 +38,3 @@ static void OnUndoRedoPerformed(in UndoRedoInfo info)
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/EditorApplication.cs b/Editor/Mono/EditorApplication.cs
index d622a3822d..5912d3d19f 100644
--- a/Editor/Mono/EditorApplication.cs
+++ b/Editor/Mono/EditorApplication.cs
@@ -205,6 +205,9 @@ public static void DirtyHierarchyWindowSorting()
// Delegate for generic updates.
[AutoStaticsCleanupOnCodeReload]
+ // Subscribers attach through their own lifecycle and re-subscribe after a code reload, so the
+ // cleared invocation list refills itself.
+ [IgnoreForUAL0015("Event whose subscribers re-register through their own lifecycle after a code reload")]
public static CallbackFunction update;
[AutoStaticsCleanupOnCodeReload]
@@ -212,6 +215,9 @@ public static void DirtyHierarchyWindowSorting()
[VisibleToOtherModules("UnityEditor.UIToolkitAuthoringModule")]
[AutoStaticsCleanupOnCodeReload]
+ // Subscribers attach through their own lifecycle and re-subscribe after a code reload, so the
+ // cleared invocation list refills itself.
+ [IgnoreForUAL0015("Event whose subscribers re-register through their own lifecycle after a code reload")]
internal static event CallbackFunction tick;
public static event Func wantsToQuit
@@ -220,6 +226,9 @@ public static event Func wantsToQuit
remove => m_WantsToQuitEvent.Remove(value);
}
[AutoStaticsCleanupOnCodeReload]
+ // Backing store for the wantsToQuit event: subscribers attach through their own lifecycle - main view
+ // and window OnEnable, search and scenario initialization - so the cleared store refills itself.
+ [IgnoreForUAL0015("Event backing store whose subscribers re-register through their own lifecycle")]
private static EventWithPerformanceTracker> m_WantsToQuitEvent = new EventWithPerformanceTracker>($"{nameof(EditorApplication)}.{nameof(wantsToQuit)}");
public static event Action quitting
@@ -228,9 +237,15 @@ public static event Action quitting
remove => m_QuittingEvent.Remove(value);
}
[AutoStaticsCleanupOnCodeReload]
+ // Backing store for the quitting event: subscribers attach through their own lifecycle and
+ // re-subscribe after a code reload, so the cleared store refills itself.
+ [IgnoreForUAL0015("Event backing store whose subscribers re-register through their own lifecycle")]
private static EventWithPerformanceTracker m_QuittingEvent = new EventWithPerformanceTracker($"{nameof(EditorApplication)}.{nameof(quitting)}");
[AutoStaticsCleanupOnCodeReload]
+ // One-shot delegate slot: it is read and set back to null on every tick, and callers re-add
+ // themselves whenever they need another delayed call, so nothing persists here by design.
+ [IgnoreForUAL0015("One-shot delayed-call slot, nulled on every tick and re-added by callers")]
public static CallbackFunction delayCall;
[AutoStaticsCleanupOnCodeReload]
private static DelegateWithPerformanceTracker m_DelayCallEvent = new DelegateWithPerformanceTracker($"{nameof(EditorApplication)}.{nameof(delayCall)}");
@@ -261,6 +276,9 @@ public static event Action hierarchyChanged
remove => m_HierarchyChangedEvent.Remove(value);
}
[AutoStaticsCleanupOnCodeReload]
+ // Backing list for the hierarchyChanged event: subscribers attach through their own lifecycle and
+ // re-subscribe after a code reload, so the cleared list refills itself.
+ [IgnoreForUAL0015("Event backing store whose subscribers re-register through their own lifecycle")]
private static EventWithPerformanceTracker m_HierarchyChangedEvent = new EventWithPerformanceTracker($"{nameof(EditorApplication)}.{nameof(hierarchyChanged)}");
[Obsolete("Use EditorApplication.hierarchyChanged")]
@@ -273,6 +291,9 @@ public static event Action projectChanged
remove => m_ProjectChangedEvent.Remove(value);
}
[AutoStaticsCleanupOnCodeReload]
+ // Backing list for the projectChanged event: subscribers attach through their own lifecycle and
+ // re-subscribe after a code reload, so the cleared list refills itself.
+ [IgnoreForUAL0015("Event backing store whose subscribers re-register through their own lifecycle")]
private static EventWithPerformanceTracker m_ProjectChangedEvent = new EventWithPerformanceTracker($"{nameof(EditorApplication)}.{nameof(projectChanged)}");
[Obsolete("Use EditorApplication.projectChanged")]
@@ -290,6 +311,10 @@ public static event Action projectChanged
[VisibleToOtherModules("UnityEditor.UIBuilderModule", "UnityEditor.GraphToolkitModule", "UnityEditor.UIToolkitAuthoringModule")]
[AutoStaticsCleanupOnCodeReload]
+ // Subscribers attach through their own lifecycle - an [OnCodeLoaded] initializer in the UI asset
+ // registry, the UI Builder scoped class constructor and clients calling RegisterFileSavedCallback -
+ // so the cleared invocation list refills itself.
+ [IgnoreForUAL0015("Event whose subscribers re-register through their own lifecycle after a code reload")]
internal static CallbackFunction fileMenuSaved;
[AutoStaticsCleanupOnCodeReload]
@@ -303,6 +328,9 @@ internal static void Internal_FrameAndRenameNewGameObject()
// Delegate for changed keyboard modifier keys.
[AutoStaticsCleanupOnCodeReload]
+ // Subscribers attach through their own lifecycle and re-subscribe after a code reload, so the
+ // cleared invocation list refills itself.
+ [IgnoreForUAL0015("Event whose subscribers re-register through their own lifecycle after a code reload")]
public static CallbackFunction modifierKeysChanged;
public static event Action pauseStateChanged
@@ -311,6 +339,9 @@ public static event Action pauseStateChanged
remove => m_PauseStateChangedEvent.Remove(value);
}
[AutoStaticsCleanupOnCodeReload]
+ // Backing store for the pauseStateChanged event: subscribers attach through their own lifecycle and
+ // re-subscribe after a code reload, so the cleared store refills itself.
+ [IgnoreForUAL0015("Event backing store whose subscribers re-register through their own lifecycle")]
private static EventWithPerformanceTracker> m_PauseStateChangedEvent = new EventWithPerformanceTracker>($"{nameof(EditorApplication)}.{nameof(pauseStateChanged)}");
public static event Action playModeStateChanged
@@ -319,6 +350,9 @@ public static event Action playModeStateChanged
remove => m_PlayModeStateChangedEvent.Remove(value);
}
[AutoStaticsCleanupOnCodeReload]
+ // Backing list for the playModeStateChanged event: subscribers attach through their own lifecycle
+ // and re-subscribe after a code reload, so the cleared list refills itself.
+ [IgnoreForUAL0015("Event backing store whose subscribers re-register through their own lifecycle")]
private static EventWithPerformanceTracker> m_PlayModeStateChangedEvent = new EventWithPerformanceTracker>($"{nameof(EditorApplication)}.{nameof(playModeStateChanged)}");
[VisibleToOtherModules]
@@ -337,15 +371,24 @@ internal static event Action enterPlayModePreStart
// Global key up/down or mouse up/down/drag events that were not handled by anyone
[VisibleToOtherModules("UnityEditor.UIToolkitAuthoringModule")]
[AutoStaticsCleanupOnCodeReload]
+ // Subscribers attach through their own lifecycle: ShortcutIntegration re-adds its handler when the
+ // enabled flag is set again while lazily recreating its controller.
+ [IgnoreForUAL0015("Event whose subscribers re-register through their own lifecycle after a code reload")]
internal static CallbackFunction globalEventHandler;
[AutoStaticsCleanupOnCodeReload]
internal static CallbackFunction shortcutHelperBarEventHandler;
// Returns true when the pressed keys are defined in the Trigger
[AutoStaticsCleanupOnCodeReload]
+ // ShortcutIntegration re-adds its handler from InitializeController, which runs again the first
+ // time the shortcut controller is accessed after a code reload.
+ [IgnoreForUAL0015("Handler re-added by ShortcutIntegration when its controller is lazily recreated")]
internal static Func doPressedKeysTriggerAnyShortcut;
[AutoStaticsCleanupOnCodeReload]
+ // Subscribers attach through their own lifecycle - ShortcutIntegration re-subscribes when its
+ // controller is lazily recreated - so the cleared invocation list refills itself.
+ [IgnoreForUAL0015("Event whose subscribers re-register through their own lifecycle after a code reload")]
public static event Action focusChanged;
// Windows were reordered
@@ -354,6 +397,10 @@ internal static event Action enterPlayModePreStart
// Global contextual menus for inspector values
[AutoStaticsCleanupOnCodeReload]
+ // Subscribers re-register through their own lifecycle after a code load: the animation menu goes
+ // through AnimationPropertyContextualMenu.Instance, which recreates itself on next use, and the
+ // inspector-side subscribers re-attach from OnEnable.
+ [IgnoreForUAL0015("Event whose subscribers re-register through their own lifecycle after a code load")]
public static SerializedPropertyCallbackFunction contextualPropertyMenu;
[AutoStaticsCleanupOnCodeReload]
diff --git a/Editor/Mono/EditorGUI.cs b/Editor/Mono/EditorGUI.cs
index a8df067918..d82ec96303 100644
--- a/Editor/Mono/EditorGUI.cs
+++ b/Editor/Mono/EditorGUI.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: IMGUIControls not yet converted
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@@ -40,9 +39,15 @@ namespace UnityEditor
public sealed partial class EditorGUI
{
[AutoStaticsCleanupOnCodeReload]
+ // Points at the text editor currently being edited: BeginEditing assigns it when a field takes
+ // focus and EndEditing clears it, so the next edited field repopulates it.
+ [IgnoreForUAL0015("Active text editor slot, reassigned by BeginEditing on the next edited field")]
private static RecycledTextEditor activeEditor;
[AutoStaticsCleanupOnCodeReload]
+ // Shared delayed text editor, re-created by the s_DelayedTextEditor accessor on the next access once
+ // the text system is ready.
+ [IgnoreForUAL0015("Shared delayed text editor re-created on the next access when null")]
internal static DelayedTextEditor s_DelayedTextEditorInternal;
internal static DelayedTextEditor s_DelayedTextEditor
{
@@ -55,6 +60,9 @@ internal static DelayedTextEditor s_DelayedTextEditor
}
[AutoStaticsCleanupOnCodeReload]
+ // Shared scratch text editor, re-created by the s_RecycledEditor accessor on the next access once the
+ // text system is ready.
+ [IgnoreForUAL0015("Shared scratch text editor re-created on the next access when null")]
internal static RecycledTextEditor s_RecycledEditorInternal;
internal static RecycledTextEditor s_RecycledEditor
{
@@ -207,14 +215,14 @@ private enum DragCandidateState
private static readonly GUIContent[] s_WHLabels = {EditorGUIUtility.TextContent("W"), EditorGUIUtility.TextContent("H")};
- private static readonly GUIContent s_CenterLabel = EditorGUIUtility.TrTextContent("Center");
- private static readonly GUIContent s_ExtentLabel = EditorGUIUtility.TrTextContent("Extent");
- private static readonly GUIContent s_PositionLabel = EditorGUIUtility.TrTextContent("Position");
- private static readonly GUIContent s_SizeLabel = EditorGUIUtility.TrTextContent("Size");
- internal static readonly GUIContent s_PleasePressAKey = EditorGUIUtility.TrTextContent("[Please press a key]");
+ private static readonly GUIContent s_CenterLabel = L10n.TextContent("Center", null, null, null);
+ private static readonly GUIContent s_ExtentLabel = L10n.TextContent("Extent", null, null, null);
+ private static readonly GUIContent s_PositionLabel = L10n.TextContent("Position", null, null, null);
+ private static readonly GUIContent s_SizeLabel = L10n.TextContent("Size", null, null, null);
+ internal static readonly GUIContent s_PleasePressAKey = L10n.TextContent("[Please press a key]", null, null, null);
- internal static readonly GUIContent s_ClipingPlanesLabel = EditorGUIUtility.TrTextContent("Clipping Planes", "The distances from the Camera where rendering starts and stops.");
- internal static readonly GUIContent[] s_NearAndFarLabels = { EditorGUIUtility.TrTextContent("Near", "The closest point to the Camera where drawing occurs."), EditorGUIUtility.TrTextContent("Far", "The furthest point from the Camera that drawing occurs.") };
+ internal static readonly GUIContent s_ClipingPlanesLabel = L10n.TextContent("Clipping Planes", "The distances from the Camera where rendering starts and stops.", null, null);
+ internal static readonly GUIContent[] s_NearAndFarLabels = { L10n.TextContent("Near", "The closest point to the Camera where drawing occurs.", null, null), L10n.TextContent("Far", "The furthest point from the Camera that drawing occurs.", null, null) };
internal const float kNearFarLabelsWidth = 35f;
[NoAutoStaticsCleanup] // transient control id; value type
@@ -273,7 +281,7 @@ private enum DragCandidateState
[NoAutoStaticsCleanup] // auto-prop; transient bool state
public static bool showMixedValue { get; set; }
- private static readonly GUIContent s_MixedValueContent = EditorGUIUtility.TrTextContent("\u2014", "Mixed Values");
+ private static readonly GUIContent s_MixedValueContent = L10n.TextContent("\u2014", "Mixed Values", null, null);
internal static GUIContent mixedValueContent => s_MixedValueContent;
@@ -686,6 +694,9 @@ public virtual void BeginEditing(int id, string newText, Rect position, GUIStyle
// The text setter skips the refresh when the text is unchanged, but the cached
// handle's native generation may have been evicted while unfocused (UUM-149393).
UpdateTextHandle();
+
+ // Presetting m_HasFocus skips OnFocus, so reveal the cursor ourselves (UUM-149213)
+ showCursor = true;
}
public virtual void EndEditing()
@@ -697,6 +708,7 @@ public virtual void EndEditing()
}
controlID = 0;
+ SelectNone();
s_ActuallyEditing = false;
s_EditingWasCompleted = false;
s_AllowContextCutOrPaste = true;
@@ -882,21 +894,21 @@ static void ShowTextEditorPopupMenu()
if (RecycledTextEditor.s_AllowContextCutOrPaste)
{
if ((s_RecycledEditor.hasSelection || s_DelayedTextEditor.hasSelection) && !s_RecycledEditor.isPasswordField && enabled && !EditorGUI.showMixedValue)
- pm.AddItem(EditorGUIUtility.TrTextContent("Cut"), false, new PopupMenuEvent(EventCommandNames.Cut, GUIView.current).SendEvent);
+ pm.AddItem(L10n.TextContent("Cut", null, null, null), false, new PopupMenuEvent(EventCommandNames.Cut, GUIView.current).SendEvent);
else
- pm.AddDisabledItem(EditorGUIUtility.TrTextContent("Cut"));
+ pm.AddDisabledItem(L10n.TextContent("Cut", null, null, null));
}
// Copy -- when GUI is disabled, allow Copy even with no selection (will copy everything)
if (((s_RecycledEditor.hasSelection || s_DelayedTextEditor.hasSelection) || !enabled) && !s_RecycledEditor.isPasswordField && !EditorGUI.showMixedValue)
- pm.AddItem(EditorGUIUtility.TrTextContent("Copy"), false, new PopupMenuEvent(EventCommandNames.Copy, GUIView.current).SendEvent);
+ pm.AddItem(L10n.TextContent("Copy", null, null, null), false, new PopupMenuEvent(EventCommandNames.Copy, GUIView.current).SendEvent);
else
- pm.AddDisabledItem(EditorGUIUtility.TrTextContent("Copy"));
+ pm.AddDisabledItem(L10n.TextContent("Copy", null, null, null));
// Paste
if (s_RecycledEditor.CanPaste() && RecycledTextEditor.s_AllowContextCutOrPaste && enabled)
{
- pm.AddItem(EditorGUIUtility.TrTextContent("Paste"), false, new PopupMenuEvent(EventCommandNames.Paste, GUIView.current).SendEvent);
+ pm.AddItem(L10n.TextContent("Paste", null, null, null), false, new PopupMenuEvent(EventCommandNames.Paste, GUIView.current).SendEvent);
}
else
{
@@ -3513,7 +3525,7 @@ internal static GenericMenu FillPropertyContextMenu(SerializedProperty property,
pm.AddSeparator("");
}
- pm.AddItem(EditorGUIUtility.TrTextContent("Duplicate Array Element"), false, (a) =>
+ pm.AddItem(L10n.TextContent("Duplicate Array Element", null, null, null), false, (a) =>
{
var list = ReorderableList.GetReorderableListFromSerializedProperty(parentArrayProperty);
@@ -3572,7 +3584,7 @@ internal static GenericMenu FillPropertyContextMenu(SerializedProperty property,
}
EditorGUIUtility.editingTextField = false;
}, propertyWithPath);
- pm.AddItem(EditorGUIUtility.TrTextContent("Delete Array Element"), false, (a) =>
+ pm.AddItem(L10n.TextContent("Delete Array Element", null, null, null), false, (a) =>
{
var list = ReorderableList.GetReorderableListFromSerializedProperty(parentArrayProperty);
@@ -3646,7 +3658,7 @@ internal static GenericMenu FillPropertyContextMenu(SerializedProperty property,
{
if (pm.GetItemCount() > 0)
pm.AddSeparator("");
- pm.AddItem(EditorGUIUtility.TrTextContent("Print Property Path"), false, e => Debug.Log(((SerializedProperty)e).propertyPath), propertyWithPath);
+ pm.AddItem(L10n.TextContent("Print Property Path", null, null, null), false, e => Debug.Log(((SerializedProperty)e).propertyPath), propertyWithPath);
}
// If property is a reference and we're using VCS, add item to check it out
@@ -4184,8 +4196,14 @@ internal static void Popup(Rect position, SerializedProperty property, GUIConten
}
[AutoStaticsCleanupOnCodeReload]
+ // Scratch slot for one enum popup: assigned right before PopupInternal and cleared right after, so it
+ // is repopulated by the next enum popup.
+ [IgnoreForUAL0015("Per-call scratch slot, assigned and cleared around each enum popup")]
private static Func s_CurrentCheckEnumEnabled;
[AutoStaticsCleanupOnCodeReload]
+ // Enum data for the popup currently being shown; reassigned from the cached enum data on every enum
+ // popup call.
+ [IgnoreForUAL0015("Per-call enum data, reassigned on every enum popup call")]
private static EnumData s_CurrentEnumData;
private static bool CheckCurrentEnumTypeEnabled(int value)
@@ -4340,6 +4358,9 @@ internal sealed partial class PopupCallbackInfo
{
// The global shared popup state
[AutoStaticsCleanupOnCodeReload]
+ // In-flight popup state: a fresh instance is created every time a popup menu is displayed, and it is
+ // nulled once the selection has been handed back to OnGUI.
+ [IgnoreForUAL0015("Transient popup state recreated each time a popup menu is displayed")]
public static PopupCallbackInfo instance = null;
// Name of the command event sent from the popup menu to OnGUI when user has changed selection
@@ -7513,8 +7534,14 @@ internal static void DrawLivePropertyBackground(Rect position, bool fixupRectFor
}
[AutoStaticsCleanupOnCodeReload]
+ // Deferred keyboard command for the property being drawn: set by BeginProperty and consumed and
+ // nulled again by EndProperty in the same GUI pass, so nothing needs to survive a reload.
+ [IgnoreForUAL0015("Per-GUI-pass slot, set on BeginProperty and consumed by EndProperty")]
private static SerializedProperty s_PendingPropertyKeyboardHandling = null;
[AutoStaticsCleanupOnCodeReload]
+ // Deferred delete command for the property being drawn: set while handling the event and executed and
+ // nulled once the property stack unwinds in the same GUI pass.
+ [IgnoreForUAL0015("Per-GUI-pass slot, set while handling the event and executed as the property stack unwinds")]
private static SerializedProperty s_PendingPropertyDelete = null;
private static void DoPropertyFieldKeyboardHandling(SerializedProperty property)
@@ -7965,7 +7992,7 @@ internal static bool PropertyFieldInternal(Rect position, SerializedProperty pro
return ScriptAttributeUtility.GetHandler(property).OnGUI(position, property, label, includeChildren);
}
- static readonly string s_ArrayMultiInfoFormatString = EditorGUIUtility.TrTextContent("This field cannot display arrays with more than {0} elements when multiple objects are selected.").text;
+ static readonly string s_ArrayMultiInfoFormatString = L10n.TextContent("This field cannot display arrays with more than {0} elements when multiple objects are selected.", null, null, null).text;
static readonly GUIContent s_ArrayMultiInfoContent = new GUIContent();
static readonly ProfilerMarker s_EvalExpressionMarker = new ProfilerMarker("Inspector.EvaluateMultiExpression");
@@ -9497,15 +9524,17 @@ public static bool PropertyField(Rect position, SerializedProperty property, GUI
// Cleanup is registered manually below instead of via [AutoStaticsCleanupOnCodeReload].
internal static class EnumNamesCache
{
- [NoAutoStaticsCleanup] // cleared manually by __AutoStaticsCleanup via the __autoCleanup registration below
+ // Each cache below is reset by the hand-written __AutoStaticsCleanup() further down rather
+ // than by generated cleanup, so the analyzer must not manage them.
+ [NoAutoStaticsCleanup]
static Dictionary s_EnumTypeLocalizedGUIContents = new();
- [NoAutoStaticsCleanup] // cleared manually by __AutoStaticsCleanup via the __autoCleanup registration below
+ [NoAutoStaticsCleanup]
static Dictionary s_SerializedPropertyEnumLocalizedGUIContents = new();
- [NoAutoStaticsCleanup] // cleared manually by __AutoStaticsCleanup via the __autoCleanup registration below
+ [NoAutoStaticsCleanup]
static Dictionary s_IsEnumTypeUsingFlagsAttribute = new();
- [NoAutoStaticsCleanup] // cleared manually by __AutoStaticsCleanup via the __autoCleanup registration below
+ [NoAutoStaticsCleanup]
static Dictionary s_SerializedPropertyEnumDisplayNames = new();
- [NoAutoStaticsCleanup] // cleared manually by __AutoStaticsCleanup via the __autoCleanup registration below
+ [NoAutoStaticsCleanup]
static Dictionary s_SerializedPropertyEnumNames = new();
[System.Runtime.CompilerServices.CompilerGenerated]
@@ -9612,9 +9641,10 @@ internal static bool IsEnumTypeUsingFlagsAttribute(Type type)
// Not partial: same compiler PDB bug as EnumNamesCache above.
static class HelpButtonCache
{
- [NoAutoStaticsCleanup] // cleared manually by __AutoStaticsCleanup via the __autoCleanup registration below
+ // Reset by the hand-written __AutoStaticsCleanup() below rather than generated cleanup.
+ [NoAutoStaticsCleanup]
static Dictionary s_TypeIsPartOfTargetAssembliesMap = new();
- [NoAutoStaticsCleanup] // cleared manually by __AutoStaticsCleanup via the __autoCleanup registration below
+ [NoAutoStaticsCleanup]
static Dictionary s_ObjectHasHelp = new();
[System.Runtime.CompilerServices.CompilerGenerated]
@@ -9681,4 +9711,3 @@ internal static bool IsObjectPartOfTargetAssemblies(Object obj)
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/EditorGUILayout.cs b/Editor/Mono/EditorGUILayout.cs
index 29d5bb6a16..d7aa34fa60 100644
--- a/Editor/Mono/EditorGUILayout.cs
+++ b/Editor/Mono/EditorGUILayout.cs
@@ -29,7 +29,7 @@ sealed partial class EditorGUILayout
internal static readonly SavedBool s_SelectedDefault = new SavedBool("Platform.ShownDefaultTab", true);
- // Keep in sync with Tests/EditModeAndPlayModeTests/PlayerSettings/Assets/Editor/PlayerSettingsApplicationIdentifierTests.cs.
+ // Keep in sync with Modules/Core/Tests/UTFTests/PlayerSettings/Editor/PlayerSettingsApplicationIdentifierTests.cs.
static readonly GUIStyle s_TabOnlyOne = "Tab onlyOne";
static readonly GUIStyle s_TabFirst = "Tab first";
static readonly GUIStyle s_TabMiddle = "Tab middle";
diff --git a/Editor/Mono/EditorGUIUtility.bindings.cs b/Editor/Mono/EditorGUIUtility.bindings.cs
index c8795fc960..d2ec7882f5 100644
--- a/Editor/Mono/EditorGUIUtility.bindings.cs
+++ b/Editor/Mono/EditorGUIUtility.bindings.cs
@@ -119,7 +119,7 @@ internal static extern int skinIndex
public static extern bool IsDisplayReferencedByCameras(int displayIndex);
// Send an input event into the game.
- public static extern void QueueGameViewInputEvent(Event evt);
+ public static extern void QueueGameViewInputEvent([NotNull] Event evt);
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
[Obsolete("RenderGameViewCameras is no longer supported.Consider rendering cameras manually.", true)]
diff --git a/Editor/Mono/EditorGUIUtility.cs b/Editor/Mono/EditorGUIUtility.cs
index 5d14ac497d..3f37f986b4 100644
--- a/Editor/Mono/EditorGUIUtility.cs
+++ b/Editor/Mono/EditorGUIUtility.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: IMGUIControls not yet converted
using System;
using System.Collections;
using System.Collections.Generic;
@@ -30,12 +29,24 @@ namespace UnityEditor
{
public sealed partial class EditorGUIUtility : GUIUtility
{
+ [NoAutoStaticsCleanup] // drained by DestroyResourcesRegisteredForCleanup on code unload
+ static readonly List s_ResourcesRegisteredForCleanup = new List();
+
[VisibleToOtherModules("UnityEditor.VectorGraphicsModule")]
internal static void RegisterResourceForCleanupOnDomainReload(UnityObject obj)
{
-#pragma warning disable UAC0006 // CORECLR_FIXME: CoreCLR would handle this using OnCodeUnloading/OnCodeLoaded
- AppDomain.CurrentDomain.DomainUnload += (object sender, EventArgs e) => { UnityObject.DestroyImmediate(obj); };
-#pragma warning restore UAC0006
+ s_ResourcesRegisteredForCleanup.Add(obj);
+ }
+
+ [OnCodeUnloading]
+ static void DestroyResourcesRegisteredForCleanup()
+ {
+ foreach (var obj in s_ResourcesRegisteredForCleanup)
+ {
+ if (obj)
+ UnityObject.DestroyImmediate(obj);
+ }
+ s_ResourcesRegisteredForCleanup.Clear();
}
public class PropertyCallbackScope : IDisposable
@@ -75,25 +86,37 @@ protected override void CloseScope()
}
}
- [NoAutoStaticsCleanup] // registered with RegisterResourceForCleanupOnDomainReload — cleaned up by engine on domain reload, not UAL
+ [NoAutoStaticsCleanup] // destroyed on code unload by RegisterResourceForCleanupOnDomainReload
internal static Material s_GUITextureBlit2SRGBMaterial;
+ [NoAutoStaticsCleanup] // color space last applied to s_GUITextureBlit2SRGBMaterial; reset whenever the material is recreated
+ static ColorSpace s_GUITextureBlit2SRGBColorSpace;
internal static Material GUITextureBlit2SRGBMaterial
{
get
{
+ bool forceUpdate = false;
if (!s_GUITextureBlit2SRGBMaterial)
{
Shader shader = LoadRequired("SceneView/GUITextureBlit2SRGB.shader") as Shader;
s_GUITextureBlit2SRGBMaterial = new Material(shader);
s_GUITextureBlit2SRGBMaterial.hideFlags |= HideFlags.DontSaveInEditor;
RegisterResourceForCleanupOnDomainReload(s_GUITextureBlit2SRGBMaterial);
+ forceUpdate = true;
+ }
+
+ // _ManualTex2SRGB isn't declared in the shader properties, so SetFloat always dirties the material.
+ // A dirty material triggers an edit-mode player loop update, so only set it when it changes.
+ ColorSpace colorSpace = QualitySettings.activeColorSpace;
+ if (forceUpdate || colorSpace != s_GUITextureBlit2SRGBColorSpace)
+ {
+ s_GUITextureBlit2SRGBMaterial.SetFloat("_ManualTex2SRGB", colorSpace == ColorSpace.Linear ? 1.0f : 0.0f);
+ s_GUITextureBlit2SRGBColorSpace = colorSpace;
}
- s_GUITextureBlit2SRGBMaterial.SetFloat("_ManualTex2SRGB", QualitySettings.activeColorSpace == ColorSpace.Linear ? 1.0f : 0.0f);
return s_GUITextureBlit2SRGBMaterial;
}
}
- [NoAutoStaticsCleanup] // registered with RegisterResourceForCleanupOnDomainReload — cleaned up by engine on domain reload, not UAL
+ [NoAutoStaticsCleanup] // destroyed on code unload by RegisterResourceForCleanupOnDomainReload
internal static Material s_GUITextureBlitSceneGUI;
internal static Material GUITextureBlitSceneGUIMaterial
{
@@ -138,29 +161,6 @@ internal static Material GUITextureBlitSceneGUIMaterial
[NoAutoStaticsCleanup] // GUIContent cache; re-populated on demand after reload
static Hashtable s_TextGUIContents = new Hashtable();
- [NoAutoStaticsCleanup] // GUIContent cache; re-populated on demand after reload
- static Hashtable s_GUIContents = new Hashtable();
- // Named groups get their own tables. The ungrouped ones are keyed on caller-supplied strings,
- // including raw icon names and tooltips, so no prefix scheme can be proven not to collide.
- [NoAutoStaticsCleanup] // GUIContent cache; re-populated on demand after reload
- static Dictionary<(string group, string key), GUIContent> s_GroupGUIContents = new Dictionary<(string, string), GUIContent>();
- [NoAutoStaticsCleanup] // GUIContent cache; re-populated on demand after reload
- static Dictionary<(string group, string key), GUIContent> s_GroupIconGUIContents = new Dictionary<(string, string), GUIContent>();
-
- static GUIContent GetCachedContent(Hashtable ungrouped, Dictionary<(string, string), GUIContent> grouped, string groupName, string key)
- {
- if (groupName == null)
- return key == null ? null : (GUIContent)ungrouped[key];
- return grouped.TryGetValue((groupName, key), out var gc) ? gc : null;
- }
-
- static void SetCachedContent(Hashtable ungrouped, Dictionary<(string, string), GUIContent> grouped, string groupName, string key, GUIContent gc)
- {
- if (groupName == null)
- ungrouped[key] = gc;
- else
- grouped[(groupName, key)] = gc;
- }
[NoAutoStaticsCleanup] // GUIContent icon cache; re-populated on demand after reload
static Hashtable s_IconGUIContents = new Hashtable();
[NoAutoStaticsCleanup] // skinned icon cache; re-populated on demand after reload
@@ -192,6 +192,11 @@ static void Initialize()
[AutoStaticsCleanupOnCodeReload]
internal static event Action contextualPropertyMenu;
[AutoStaticsCleanupOnCodeReload]
+ // Subscribers re-register on their own: PropertyCallbackScope subscribes in its constructor and
+ // unsubscribes on dispose, the trail renderer and preset editors subscribe per enable/preview,
+ // and the localization package subscribes from an [InitializeOnLoad] cctor that re-runs when its
+ // assembly reloads, so the cleared invocation list refills itself.
+ [IgnoreForUAL0015("Event whose subscribers re-register from short-lived scopes or [InitializeOnLoad]")]
internal static event Action beginProperty;
internal static void BeginPropertyCallback(Rect totalRect, SerializedProperty property)
@@ -589,58 +594,31 @@ public static GUIContent TrTextContent(string key, string text, string tooltip,
return TrTextContent(key, text, tooltip, icon, null);
}
+ // L10n owns the content cache, so everything here hands over to it. The key taking overload is
+ // the exception: it is public, and its caller chose the key, so that key has to be honoured.
public static GUIContent TrTextContent(string key, string text, string tooltip, Texture icon, string groupName)
{
- GUIContent gc = GetCachedContent(s_GUIContents, s_GroupGUIContents, groupName, key);
- if (gc == null)
- {
- gc = new GUIContent(L10n.Tr(text, groupName));
- if (tooltip != null)
- {
- gc.tooltip = L10n.Tr(tooltip, groupName);
- }
- if (icon != null)
- {
- gc.image = icon;
- }
- SetCachedContent(s_GUIContents, s_GroupGUIContents, groupName, key, gc);
- }
- return gc;
+ return L10n.CachedTextContent(groupName, key, text, tooltip, icon);
}
public static GUIContent TrTextContent(string text, string tooltip = null, Texture icon = null)
{
- string key = string.Format("{0}|{1}", text ?? "", tooltip ?? "");
- return TrTextContent(key, text, tooltip, icon, null);
- }
-
- // The ungrouped keys have never included the texture, so two icons sharing a tooltip return
- // each other's content. That is long standing, and changing it would move behaviour callers
- // may rely on, so it stays. The grouped tables are new and do not inherit it.
- static string WithIconIdentity(string groupName, string key, Texture icon)
- {
- if (groupName == null || icon == null)
- return key;
- return string.Format("{0}|{1}", key, icon.GetEntityId());
+ return L10n.TextContentCore(null, text, tooltip, icon);
}
public static GUIContent TrTextContent(string text, string tooltip, string iconName)
{
- string key = iconName == null ? string.Format("{0}|{1}", text ?? "", tooltip ?? "") :
- string.Format("{0}|{1}|{2}|{3}", text ?? "", tooltip ?? "", iconName, pixelsPerPoint);
- return TrTextContent(key, text, tooltip, LoadIconRequired(iconName), null);
+ return L10n.TextContent(text, tooltip, iconName, null);
}
public static GUIContent TrTextContent(string text, Texture icon)
{
- return TrTextContent(text, null, icon);
+ return L10n.TextContent(text, icon, null);
}
public static GUIContent TrTextContent(string text, Texture icon, string groupName)
{
- string tooltip = null;
- string key = string.Format("{0}|{1}", text ?? "", tooltip ?? "");
- return TrTextContent(WithIconIdentity(groupName, key, icon), text, tooltip, icon, groupName);
+ return L10n.TextContent(text, icon, groupName);
}
public static GUIContent TrTextContentWithIcon(string text, Texture icon)
@@ -665,9 +643,7 @@ public static GUIContent TrTextContentWithIcon(string text, string tooltip, stri
public static GUIContent TrTextContentWithIcon(string text, string tooltip, string iconName, string groupName)
{
- string key = iconName == null ? string.Format("{0}|{1}", text ?? "", tooltip ?? "") :
- string.Format("{0}|{1}|{2}|{3}", text ?? "", tooltip ?? "", iconName, pixelsPerPoint);
- return TrTextContent(key, text, tooltip, LoadIconRequired(iconName), groupName);
+ return L10n.TextContentWithIcon(text, tooltip, iconName, groupName);
}
public static GUIContent TrTextContentWithIcon(string text, string tooltip, Texture icon)
@@ -677,8 +653,7 @@ public static GUIContent TrTextContentWithIcon(string text, string tooltip, Text
public static GUIContent TrTextContentWithIcon(string text, string tooltip, Texture icon, string groupName)
{
- string key = string.Format("{0}|{1}", text ?? "", tooltip ?? "");
- return TrTextContent(WithIconIdentity(groupName, key, icon), text, tooltip, icon, groupName);
+ return L10n.TextContentWithIcon(text, tooltip, icon, groupName);
}
public static GUIContent TrTextContentWithIcon(string text, string tooltip, MessageType messageType)
@@ -744,24 +719,7 @@ internal static GUIContent TrIconContent(string iconName, string tooltip, bool l
internal static GUIContent TrIconContent(string iconName, string tooltip, bool lightenTexture, string groupName)
{
- string key = tooltip == null ? string.Format("{0}|{1}", iconName, pixelsPerPoint) :
- string.Format("{0}|{1}|{2}", iconName, tooltip, pixelsPerPoint);
- GUIContent gc = GetCachedContent(s_IconGUIContents, s_GroupIconGUIContents, groupName, key);
- if (gc != null)
- {
- return gc;
- }
- gc = new GUIContent();
-
- if (tooltip != null)
- {
- gc.tooltip = L10n.Tr(tooltip, groupName);
- }
- gc.image = LoadIconRequired(iconName);
- if (lightenTexture && gc.image is Texture2D tex2D)
- gc.image = LightenTexture(tex2D);
- SetCachedContent(s_IconGUIContents, s_GroupIconGUIContents, groupName, key, gc);
- return gc;
+ return L10n.IconContentNamed(groupName, iconName, tooltip, lightenTexture);
}
public static GUIContent TrIconContent(Texture icon, string tooltip = null)
@@ -771,20 +729,7 @@ public static GUIContent TrIconContent(Texture icon, string tooltip = null)
public static GUIContent TrIconContent(Texture icon, string tooltip, string groupName)
{
- string key = tooltip == null ? null : WithIconIdentity(groupName, tooltip, icon);
- GUIContent gc = GetCachedContent(s_IconGUIContents, s_GroupIconGUIContents, groupName, key);
- if (gc != null)
- {
- return gc;
- }
- gc = new GUIContent { image = icon };
- if (tooltip != null)
- {
- gc.tooltip = L10n.Tr(tooltip, groupName);
- SetCachedContent(s_IconGUIContents, s_GroupIconGUIContents, groupName, key, gc);
- }
-
- return gc;
+ return L10n.IconContent(icon, tooltip, groupName);
}
[ExcludeFromDocs]
@@ -1005,7 +950,10 @@ static Texture2D InnerLoadGeneratedIconOrNormalIcon(string name)
return tex;
}
- [NoAutoStaticsCleanup] // pure deterministic name+skinIndex -> prefixed-name string transform; no user-code or ALC references
+ [Unity.Scripting.LifecycleManagement.AutoStaticsCleanupOnCodeReload]
+ // Memo of derived icon names: GetIconNameForSkin re-derives and re-adds any missing entry on the
+ // next lookup, so the cache emptied on reload refills on demand.
+ [IgnoreForUAL0015("Derived icon-name memo, re-derived on the next miss by GetIconNameForSkin")]
private static readonly Dictionary<(string, int), string> s_IconNamePerSkinCache = new(32);
internal static string GetIconNameForSkin(string name, int in_SkinIndex)
@@ -1042,6 +990,9 @@ internal static Texture2D LoadIconForSkin(string name, int in_SkinIndex)
// Cache for icon paths from IconAttribute - avoids repeated reflection
// Key is (Type, inherit) to handle both inherit=true and inherit=false cases correctly
[Unity.Scripting.LifecycleManagement.AutoStaticsCleanupOnCodeReload]
+ // Reflection memo for IconAttribute paths: GetIconPathFromAttribute re-reads the attribute and
+ // re-adds a missing entry, so the cache emptied on reload refills on demand.
+ [IgnoreForUAL0015("IconAttribute reflection memo, re-read on the next miss")]
private static Dictionary<(Type, bool), string> s_IconPathCache = new Dictionary<(Type, bool), string>();
[UsedByNativeCode]
@@ -1427,10 +1378,7 @@ internal static GUIStyle GetBasicTextureStyle(Texture2D tex)
internal static void NotifyLanguageChanged(SystemLanguage newLanguage)
{
s_TextGUIContents = new Hashtable();
- s_GUIContents = new Hashtable();
s_IconGUIContents = new Hashtable();
- s_GroupGUIContents.Clear();
- s_GroupIconGUIContents.Clear();
L10n.ClearCache();
EditorUtility.Internal_UpdateMenuTitleForLanguage(newLanguage);
LocalizationDatabase.currentEditorLanguage = newLanguage;
@@ -1551,6 +1499,12 @@ internal static void ResetGUIState()
GUI.color = EditorUtility.activePlayModeTint;
GUI.enabled = true;
GUI.changed = false;
+ ResetPerPassState();
+ }
+
+ // The per-pass state not covered by the native GUIState; SavedEditorGUIState snapshots exactly this set.
+ internal static void ResetPerPassState()
+ {
EditorGUI.indentLevel = 0;
EditorGUI.ClearStacks();
fieldWidth = 0;
diff --git a/Editor/Mono/EditorGraphicsSettings.bindings.cs b/Editor/Mono/EditorGraphicsSettings.bindings.cs
index 3adec15fd0..d0ec958ccc 100644
--- a/Editor/Mono/EditorGraphicsSettings.bindings.cs
+++ b/Editor/Mono/EditorGraphicsSettings.bindings.cs
@@ -235,6 +235,15 @@ internal static bool IsGlobalSettingsContaining(UnityEngine.Object renderPipelin
[NativeName("SetShaderBuildSettings")] extern internal static void SetShaderBuildSettingsImpl(ShaderBuildSettings settings);
public static void SetShaderBuildSettings(ShaderBuildSettings settings)
+ {
+ settings = SanitizeCompilerSettings(settings, nameof(SetShaderBuildSettings));
+
+ ShaderBuildSettings.CheckKeywordDeclarationOverridesHaveMatches(settings.keywordDeclarationOverrides);
+
+ SetShaderBuildSettingsImpl(settings);
+ }
+
+ internal static ShaderBuildSettings SanitizeCompilerSettings(ShaderBuildSettings settings, string caller)
{
// Sanitize instead of throwing: compilerSettings is internal, so a caller editing
// something else (e.g. Defines) can't repair bad rows read from disk (e.g. a project
@@ -242,10 +251,9 @@ public static void SetShaderBuildSettings(ShaderBuildSettings settings)
var original = settings.compilerSettings ?? Array.Empty();
var sanitized = ShaderBuildSettings.SanitizeShaderCompilerSettings(original);
if (!ReferenceEquals(sanitized, original))
- Debug.LogWarning("SetShaderBuildSettings: dropped invalid or duplicate shader compiler settings rows.");
+ Debug.LogWarning($"{caller}: dropped invalid or duplicate shader compiler settings rows.");
settings.compilerSettings = sanitized;
-
- SetShaderBuildSettingsImpl(settings);
+ return settings;
}
extern public static bool ShouldValidateGraphicsForActiveBuildTarget();
diff --git a/Editor/Mono/EditorMode/ModeService.cs b/Editor/Mono/EditorMode/ModeService.cs
index d0cc46c7f6..fe93a96c0f 100644
--- a/Editor/Mono/EditorMode/ModeService.cs
+++ b/Editor/Mono/EditorMode/ModeService.cs
@@ -79,6 +79,9 @@ public override void OnImportAsset(AssetImportContext ctx)
ctx.AddObjectToAsset("mode", modeDescriptor);
ctx.SetMainObject(modeDescriptor);
+ // The scan that filled the path cache may have run before this file was imported.
+ SessionState.EraseString(ModeService.k_ModePathsCache);
+
if (!allowExplicitModeRefresh)
return;
@@ -110,6 +113,7 @@ public struct ModeChangedArgs
}
internal const string k_ModePathsCache = "mode-paths-cache";
+ const string k_ModePathsNotScanned = "";
internal const string k_DefaultModeId = "default";
internal const string k_ModeCurrentIdKeyName = "mode-current-id";
internal const string k_MenuKeyChecked = "checked";
@@ -133,6 +137,9 @@ public struct ModeChangedArgs
public static string currentId => currentIndex == -1 || modes.Length == 0 ? k_DefaultModeId : modes[currentIndex].id;
[AutoStaticsCleanupOnCodeReload]
+ // Re-derived on code load: Initialize calls LoadModes, which resolves the mode from session and
+ // project prefs and calls SetModeIndex again.
+ [IgnoreForUAL0015("Current mode index re-derived by LoadModes on every code load")]
public static int currentIndex { get; private set; }
[AutoStaticsCleanupOnCodeReload]
private static ModeEntry[] modes { get; set; } = Array.Empty();
@@ -145,11 +152,18 @@ private static bool initialModeChanged
}
[AutoStaticsCleanupOnCodeReload]
+ // Initialize re-adds the mode service handlers on every code load and other subscribers attach
+ // through their own lifecycle, so the cleared invocation list refills itself.
+ [IgnoreForUAL0015("Event re-subscribed by Initialize on code load and by subscribers own lifecycle")]
public static event Action modeChanged;
[OnCodeLoaded]
static void Initialize()
{
+ // Import workers never show modes, menus or layouts.
+ if (AssetDatabase.IsAssetImportWorkerProcess())
+ return;
+
Log("Initialize");
LoadModes();
@@ -472,10 +486,11 @@ internal static void ScanModes()
var builtinModeFile = Path.Combine(EditorApplication.applicationContentsPath, "Resources/default.mode");
FillModeData(builtinModeFile, modesData);
- var modeFilePathCache = SessionState.GetString(k_ModePathsCache, "");
-
- if (modeFilePathCache == "")
+ // An empty cache is valid; most projects have no mode files.
+ var modeFilePathCache = SessionState.GetString(k_ModePathsCache, k_ModePathsNotScanned);
+ if (modeFilePathCache == k_ModePathsNotScanned)
{
+ modeFilePathCache = "";
var modeDescriptors = AssetDatabase.EnumerateAllAssets(new SearchFilter
{
searchArea = SearchFilter.SearchArea.InPackagesOnly,
diff --git a/Editor/Mono/EditorPrefs.bindings.cs b/Editor/Mono/EditorPrefs.bindings.cs
index eb1a6bce82..1f95ea094a 100644
--- a/Editor/Mono/EditorPrefs.bindings.cs
+++ b/Editor/Mono/EditorPrefs.bindings.cs
@@ -17,6 +17,9 @@ public sealed partial class EditorPrefs
// long term solution is to make editor prefs read/writes threadsafe
internal delegate void ValueWasUpdated(string key);
[AutoStaticsCleanupOnCodeReload]
+ // PlayModeTintResolver re-subscribes from its [OnCodeLoaded] Initialize() on every code load, so
+ // the invocation list cleared here is repopulated before any pref can change.
+ [IgnoreForUAL0015("Event re-subscribed on every code load by PlayModeTintResolver.Initialize()")]
internal static event ValueWasUpdated onValueWasUpdated;
// Sets the value of the preference identified by /key/.
diff --git a/Editor/Mono/EditorSceneManager.cs b/Editor/Mono/EditorSceneManager.cs
index 4baa315e33..e73c030de6 100644
--- a/Editor/Mono/EditorSceneManager.cs
+++ b/Editor/Mono/EditorSceneManager.cs
@@ -31,6 +31,9 @@ public static event UnityAction activeSceneChangedInEditMode
}
[AutoStaticsCleanupOnCodeReload(CleanupStrategy = CleanupStrategy.Clear)]
+ // Backing store for the activeSceneChangedInEditMode event: subscribers attach through their own
+ // lifecycle and re-subscribe after a code reload, so the cleared store refills itself.
+ [IgnoreForUAL0015("Event backing store whose subscribers re-register through their own lifecycle")]
private static EventWithPerformanceTracker> m_ActiveSceneChangedInEditModeEvent = new EventWithPerformanceTracker>($"{nameof(EditorSceneManager)}.{nameof(activeSceneChangedInEditMode)}");
private static void PlayModeStateChangedCallback(PlayModeStateChange state)
diff --git a/Editor/Mono/EditorSettings.bindings.cs b/Editor/Mono/EditorSettings.bindings.cs
index 603c410867..c5809c2b21 100644
--- a/Editor/Mono/EditorSettings.bindings.cs
+++ b/Editor/Mono/EditorSettings.bindings.cs
@@ -366,6 +366,10 @@ internal static bool useLegacyHierarchy
[VisibleToOtherModules]
[AutoStaticsCleanupOnCodeReload]
+ // The hierarchy windows subscribe in OnEnable and unsubscribe in OnDisable, and the
+ // HierarchyPreferences relay re-subscribes from its [OnCodeLoaded] Initialize() on every code
+ // load, so all handlers are restored after a reload.
+ [IgnoreForUAL0015("Subscribers re-register via OnEnable or [OnCodeLoaded] after a code reload")]
internal static Action useLegacyHierarchyChanged;
[StaticAccessor("GetEditorSettings()", StaticAccessorType.Dot)]
@@ -376,5 +380,8 @@ internal static bool useLegacyHierarchy
[StaticAccessor("GetEditorSettings()", StaticAccessorType.Dot)]
public static extern bool hideBuildProfileClassicPlatforms { get; set; }
+
+ [StaticAccessor("GetEditorSettings()", StaticAccessorType.Dot)]
+ internal static extern bool enableLegacyUmbraCulling { get; }
}
}
diff --git a/Editor/Mono/EditorUserBuildSettingsQNX.bindings.cs b/Editor/Mono/EditorUserBuildSettingsQNX.bindings.cs
index 005c73de10..fd7eca9290 100644
--- a/Editor/Mono/EditorUserBuildSettingsQNX.bindings.cs
+++ b/Editor/Mono/EditorUserBuildSettingsQNX.bindings.cs
@@ -14,4 +14,29 @@ public enum QNXOsVersion
[UnityEngine.InspectorName("Neutrino RTOS 8.0")]
Neutrino80 = 2,
}
+
+ ///
+ /// The network stack to build a QNX 7.1 player against.
+ ///
+ ///
+ /// QNX 7.1 provides two network stacks, and Unity builds a separate player variation for each one.
+ /// QNX 8.0 provides only io-sock, so it ignores this setting.
+ ///
+ /// requires a QNX 7.1 SDK that provides the io-sock headers.
+ /// A build against an SDK without them fails and names the SDK path in the error.
+ ///
+ public enum QNX71NetworkStack
+ {
+ ///
+ /// Uses the io-pkt network stack, which is the default on QNX 7.1.
+ ///
+ [UnityEngine.InspectorName("io-pkt (QNX 7.1 default)")]
+ IoPkt = 0,
+
+ ///
+ /// Uses the io-sock network stack, which requires an io-sock enabled QNX 7.1 SDK.
+ ///
+ [UnityEngine.InspectorName("io-sock")]
+ IoSock = 1,
+ }
}
diff --git a/Editor/Mono/EditorUtility.cs b/Editor/Mono/EditorUtility.cs
index cc4937480a..b0e983084b 100644
--- a/Editor/Mono/EditorUtility.cs
+++ b/Editor/Mono/EditorUtility.cs
@@ -74,6 +74,9 @@ public static string GetDialogOptOutMessage(DialogOptOutDecisionType dialogOptOu
public delegate void SelectMenuItemFunction(object userData, string[] options, int selected);
[AutoStaticsCleanupOnCodeReload]
+ // EditorContextualMenuManager re-registers its pointer-reset handler on every code load from its
+ // static constructor.
+ [IgnoreForUAL0015("Event re-subscribed on every code load by EditorContextualMenuManager's static constructor")]
internal static event Action onResetMouseDown;
public static bool LoadWindowLayout(string path)
diff --git a/Editor/Mono/EditorWindow.cs b/Editor/Mono/EditorWindow.cs
index b55e6b2adb..c2cd48012e 100644
--- a/Editor/Mono/EditorWindow.cs
+++ b/Editor/Mono/EditorWindow.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: EditorWindowManagement not yet converted
using UnityEngine;
using Unity.Scripting.LifecycleManagement;
using System.Linq;
@@ -120,7 +119,9 @@ internal SerializableJsonDictionary viewDataDictionary
if (m_EnableViewDataPersistence && m_ViewDataDictionary == null)
{
string editorPrefFileName = this.GetType().ToString();
+#pragma warning disable UAL0018 // m_ViewDataDictionary lives on the EditorWindow instance, which a code reload recreates along with this cache; the fresh window re-resolves its entry from the new view-data singleton on first access
m_ViewDataDictionary = UIElements.EditorWindowViewData.instance[editorPrefFileName];
+#pragma warning restore UAL0018
}
return m_ViewDataDictionary;
}
@@ -307,7 +308,7 @@ internal static GUIContent GetLocalizedTitleContentFromType(Type t)
return EditorGUIUtility.TrTextContentWithIcon(attr.title, iconName);
}
- return EditorGUIUtility.TrTextContent(attr.title);
+ return L10n.TextContent(attr.title, null, null, null);
}
// Fallback to type name (Do not localize type name)
@@ -466,6 +467,9 @@ static public EditorWindow focusedWindow
static public Action focusedWindowChanged;
[AutoStaticsCleanupOnCodeReload]
+ // Subscribers attach through their own lifecycle and re-subscribe after a code reload, so the
+ // cleared invocation list refills itself.
+ [IgnoreForUAL0015("Event whose subscribers re-register through their own lifecycle after a code reload")]
static public event Action windowFocusChanged;
static internal void OnWindowFocusChanged()
@@ -1333,9 +1337,7 @@ public EditorWindow()
titleContent.text = GetType().ToString();
saveChangesMessage = $"{GetType()} has unsaved changes.";
- #pragma warning disable UAL0015 // this side effect does not outlive the current call (global trigger / lazily-loaded asset re-fetched on next access); a stale reference is harmlessly replaced
UpdateWindowMenuListing();
- #pragma warning restore UAL0015
}
void InitializeOverlayCanvas()
@@ -1535,10 +1537,7 @@ private VisualElement CreateRoot()
viewDataKey = rootName,
renderHints = RenderHints.ClipWithScissors
};
- // The theme stylesheet is also applied at the panel level (see DefaultEditorWindowBackend),
- // but it is kept here on the window content root as well: existing packages rely on
- // rootVisualElement always owning the editor theme stylesheet (e.g. reading styleSheets[0]).
- UIElementsEditorUtility.AddDefaultEditorStyleSheets(root);
+ root.styleSheets.Add(UIElementsEditorUtility.GetEditorWindowRootPlaceholderStyleSheet());
root.style.overflow = Overflow.Hidden;
return root;
}
@@ -1716,4 +1715,3 @@ public static int GetAntiAliasing(this EditorWindow window)
}
}
} //namespace
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/ExternalPlayModeView/ExternalPlayModeView.cs b/Editor/Mono/ExternalPlayModeView/ExternalPlayModeView.cs
index 0f4cdc781c..ad65a18eeb 100644
--- a/Editor/Mono/ExternalPlayModeView/ExternalPlayModeView.cs
+++ b/Editor/Mono/ExternalPlayModeView/ExternalPlayModeView.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: PlayModeFramework not yet converted
using System;
using UnityEngine;
using UnityEngine.Internal;
@@ -170,4 +169,3 @@ public bool vSyncEnabled
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/FileUtil.cs b/Editor/Mono/FileUtil.cs
index 1ce8c52c96..ca4a503ffd 100644
--- a/Editor/Mono/FileUtil.cs
+++ b/Editor/Mono/FileUtil.cs
@@ -6,6 +6,7 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
+using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.Bindings;
@@ -301,6 +302,82 @@ internal static string NiceWinPath(string unityPath)
return Application.platform == RuntimePlatform.WindowsEditor ? unityPath.Replace("/", @"\") : unityPath;
}
+ const int k_WindowsMaxPath = 260;
+ const string k_WindowsExtendedPathPrefix = @"\\?\";
+ const string k_WindowsExtendedUncPathPrefix = @"\\?\UNC\";
+
+ ///
+ /// Returns in a form the platform file APIs accept regardless of its
+ /// length, by applying the Windows extended-length prefix to paths that would otherwise exceed
+ /// MAX_PATH. A no-op off Windows, and for paths that already fit.
+ ///
+ ///
+ /// Managed System.IO calls are not long-path aware, unlike the native file system layer,
+ /// so a deeply nested asset otherwise fails to open with .
+ ///
+ internal static string ToExtendedLengthPath(string path)
+ {
+ if (string.IsNullOrEmpty(path) || path.Length < k_WindowsMaxPath)
+ return path;
+
+ if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ return path;
+
+ ReadOnlySpan span = path.AsSpan();
+
+ // The prefix suppresses normalisation, so it only works on a fully qualified, backslash-separated path.
+ if (span.Length >= 4 && IsSlash(span[0]) && IsSlash(span[1]) && span[2] == '?' && IsSlash(span[3]))
+ {
+ if (span.IndexOf('/') < 0)
+ return path;
+
+ return string.Create(path.Length, path, static (dest, src) =>
+ {
+ CopyReplacingSlashes(src.AsSpan(), dest);
+ });
+ }
+
+ if (span.Length >= 2 && IsSlash(span[0]) && IsSlash(span[1]))
+ {
+ return string.Create(k_WindowsExtendedUncPathPrefix.Length + path.Length - 2, path, static (dest, src) =>
+ {
+ k_WindowsExtendedUncPathPrefix.AsSpan().CopyTo(dest);
+ CopyReplacingSlashes(src.AsSpan(2), dest.Slice(k_WindowsExtendedUncPathPrefix.Length));
+ });
+ }
+
+ if (span.Length >= 3 && char.IsLetter(span[0]) && span[1] == ':' && IsSlash(span[2]))
+ {
+ return string.Create(k_WindowsExtendedPathPrefix.Length + path.Length, path, static (dest, src) =>
+ {
+ k_WindowsExtendedPathPrefix.AsSpan().CopyTo(dest);
+ CopyReplacingSlashes(src.AsSpan(), dest.Slice(k_WindowsExtendedPathPrefix.Length));
+ });
+ }
+
+ return path;
+
+ static bool IsSlash(char c) => c == '\\' || c == '/';
+
+ static void CopyReplacingSlashes(ReadOnlySpan source, Span destination)
+ {
+ for (int i = 0; i < source.Length; i++)
+ destination[i] = source[i] == '/' ? '\\' : source[i];
+ }
+ }
+
+ ///
+ /// Resolves as does, but returns a
+ /// result that stays usable with managed System.IO calls when it exceeds MAX_PATH.
+ /// Note that such a result uses Windows path separators rather than Unity ones.
+ ///
+ /// The file or directory for which to obtain absolute path information.
+ [VisibleToOtherModules("UnityEditor.UIElementsModule")]
+ internal static string PathToAbsolutePathForFileIO(string path)
+ {
+ return ToExtendedLengthPath(PathToAbsolutePath(path));
+ }
+
internal static string UnityGetFileNameWithoutExtension(string path)
{
// this is because on Windows \\ means network path, in unity all \ are converted to /
diff --git a/Editor/Mono/GI/Lightmapping.bindings.cs b/Editor/Mono/GI/Lightmapping.bindings.cs
index 71537febad..b855e00102 100644
--- a/Editor/Mono/GI/Lightmapping.bindings.cs
+++ b/Editor/Mono/GI/Lightmapping.bindings.cs
@@ -518,6 +518,37 @@ public static void Tetrahedralize(Vector3[] positions, out int[] outIndices, out
[StaticAccessor("GetLightmapSettings()")]
public static extern LightingDataAsset lightingDataAsset { get; set; }
+ const string k_ChangeLightingDataAssetUndoName = "Change Lighting Data Asset";
+
+ internal static void SetLightingDataAssetWithUndo(LightingDataAsset asset)
+ {
+ Undo.RecordObject(LightmapEditorSettings.GetLightmapSettings(), k_ChangeLightingDataAssetUndoName);
+ lightingDataAsset = asset;
+ }
+
+ [FreeFunction]
+ [NativeHeader("Editor/Src/GI/LightingDataAssetManager.h")]
+ static extern void ReloadBakedLightingData();
+
+ [OnCodeLoaded]
+ static void RegisterLightingDataAssetUndoHandler()
+ {
+ Undo.undoRedoEvent += OnLightingDataAssetUndoRedo;
+ }
+
+ [OnCodeUnloading]
+ static void UnregisterLightingDataAssetUndoHandler()
+ {
+ Undo.undoRedoEvent -= OnLightingDataAssetUndoRedo;
+ }
+
+ static void OnLightingDataAssetUndoRedo(in UndoRedoInfo info)
+ {
+ // Undo restores only the reference, not the baked data it loaded
+ if (info.undoName == k_ChangeLightingDataAssetUndoName)
+ ReloadBakedLightingData();
+ }
+
[StaticAccessor("GetLightmapSettings().GetReadOnlySharedData()")]
public static extern bool HasDynamicGILightmapTextures();
diff --git a/Editor/Mono/GUI/AboutWindow.cs b/Editor/Mono/GUI/AboutWindow.cs
index a24c226444..97d59511c0 100644
--- a/Editor/Mono/GUI/AboutWindow.cs
+++ b/Editor/Mono/GUI/AboutWindow.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: EditorWindowManagement not yet converted
using System;
using UnityEngine;
using UnityEngine.Scripting;
@@ -15,10 +14,6 @@ namespace UnityEditor
{
internal partial class AboutWindow : EditorWindow
{
- #pragma warning disable UAL0015 // this side effect does not outlive the current call (global trigger / lazily-loaded asset re-fetched on next access); a stale reference is harmlessly replaced
- public AboutWindow() { }
- #pragma warning restore UAL0015
-
// s_Instance is nulled when the window is closed (OnDestroy)
[AutoStaticsCleanupOnCodeReload]
static AboutWindow s_Instance;
@@ -286,4 +281,3 @@ private string FormatExtensionVersionString()
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/GUI/AppStatusBar.cs b/Editor/Mono/GUI/AppStatusBar.cs
index 50b128bc2e..1546554d7e 100644
--- a/Editor/Mono/GUI/AppStatusBar.cs
+++ b/Editor/Mono/GUI/AppStatusBar.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: ProgressWindow not yet converted
using System;
using System.Globalization;
using System.Linq;
@@ -39,8 +38,8 @@ internal static class Styles
public static readonly GUIContent[] statusWheel;
public static readonly GUIContent assemblyLock = EditorGUIUtility.IconContent("AssemblyLock", "|Assemblies are currently locked. Compilation will resume once they are unlocked");
- public static readonly GUIContent progressIcon = EditorGUIUtility.TrIconContent("Progress", "Show progress details");
- public static readonly GUIContent progressHideIcon = EditorGUIUtility.TrIconContent("Progress", "Hide progress details");
+ public static readonly GUIContent progressIcon = L10n.IconContent("Progress", "Show progress details", null);
+ public static readonly GUIContent progressHideIcon = L10n.IconContent("Progress", "Hide progress details", null);
static Styles()
{
@@ -492,4 +491,3 @@ internal static void StatusChangedImmediate()
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/GUI/AssetPopupBackend.cs b/Editor/Mono/GUI/AssetPopupBackend.cs
index 2e80cec370..51e1adfa50 100644
--- a/Editor/Mono/GUI/AssetPopupBackend.cs
+++ b/Editor/Mono/GUI/AssetPopupBackend.cs
@@ -130,7 +130,7 @@ public override void Cancelled(EntityId entityId, string pathName, string resour
{
// Create item
gm.AddSeparator("");
- gm.AddItem(EditorGUIUtility.TrTextContent("Create New..."), false, delegate
+ gm.AddItem(L10n.TextContent("Create New...", null, null, null), false, delegate
{
var newAsset = Activator.CreateInstance();
var doCreate = ScriptableObject.CreateInstance();
diff --git a/Editor/Mono/GUI/AssetSaveDialog.cs b/Editor/Mono/GUI/AssetSaveDialog.cs
index 95dd88994c..6d28974f83 100644
--- a/Editor/Mono/GUI/AssetSaveDialog.cs
+++ b/Editor/Mono/GUI/AssetSaveDialog.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: IMGUIControls not yet converted
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
@@ -21,10 +20,10 @@ class Styles
public GUIStyle box = "OL Box";
public GUIStyle button = "LargeButton";
public GUIStyle labelHeight = "Label";
- public GUIContent saveSelected = EditorGUIUtility.TrTextContent("Save Selected");
- public GUIContent saveAll = EditorGUIUtility.TrTextContent("Save All");
- public GUIContent dontSave = EditorGUIUtility.TrTextContent("Don't Save");
- public GUIContent close = EditorGUIUtility.TrTextContent("Close");
+ public GUIContent saveSelected = L10n.TextContent("Save Selected", null, null, null);
+ public GUIContent saveAll = L10n.TextContent("Save All", null, null, null);
+ public GUIContent dontSave = L10n.TextContent("Don't Save", null, null, null);
+ public GUIContent close = L10n.TextContent("Close", null, null, null);
public float buttonWidth;
public Styles()
{
@@ -81,7 +80,7 @@ public static void ShowWindow(string[] inAssets, out string[] assetsThatShouldBe
}
AssetSaveDialog win = EditorWindow.GetWindowDontShow();
- win.titleContent = EditorGUIUtility.TrTextContent("Save Assets");
+ win.titleContent = L10n.TextContent("Save Assets", null, null, null);
win.SetAssets(assets);
win.minSize = new Vector2(400, 100);
@@ -248,4 +247,3 @@ void RebuildLists(bool selected)
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/GUI/CacheServerToggle.cs b/Editor/Mono/GUI/CacheServerToggle.cs
index 0340edc8f3..63ec769325 100644
--- a/Editor/Mono/GUI/CacheServerToggle.cs
+++ b/Editor/Mono/GUI/CacheServerToggle.cs
@@ -2,29 +2,33 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: IMGUIControls not yet converted
using UnityEngine;
using UnityEditor.Experimental;
+using Unity.Scripting.LifecycleManagement;
namespace UnityEditor
{
- internal class CacheServerToggle
+ internal partial class CacheServerToggle
{
private readonly GUIContent m_CacheServerNotEnabledContent;
private readonly GUIContent m_CacheServerDisconnectedContent;
private readonly GUIContent m_CacheServerConnectedContent;
private readonly PopupLocation[] m_PopupLocation;
- static CacheServerToggle()
+ // cacheServerConnectionChanged is cleared on code reload, so this subscription has to be
+ // re-established on every load. A static constructor would only run once per domain, and the status
+ // bar would stop repainting on cache server connection changes after the first reload.
+ [OnCodeLoaded]
+ static void Initialize()
{
AssetDatabase.cacheServerConnectionChanged += OnCacherServerConnectionChanged;
}
public CacheServerToggle()
{
- m_CacheServerNotEnabledContent = EditorGUIUtility.TrIconContent("CacheServerDisabled", "Cache Server disabled");
- m_CacheServerDisconnectedContent = EditorGUIUtility.TrIconContent("CacheServerDisconnected", "Cache Server disconnected");
- m_CacheServerConnectedContent = EditorGUIUtility.TrIconContent("CacheServerConnected", "Cache Server connected");
+ m_CacheServerNotEnabledContent = L10n.IconContent("CacheServerDisabled", "Cache Server disabled", null);
+ m_CacheServerDisconnectedContent = L10n.IconContent("CacheServerDisconnected", "Cache Server disconnected", null);
+ m_CacheServerConnectedContent = L10n.IconContent("CacheServerConnected", "Cache Server connected", null);
m_PopupLocation = new[] { PopupLocation.AboveAlignRight };
}
@@ -61,4 +65,3 @@ private static void OnCacherServerConnectionChanged(CacheServerConnectionChanged
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/GUI/CacheServerWindow.cs b/Editor/Mono/GUI/CacheServerWindow.cs
index 97667e0a30..25aa20d858 100644
--- a/Editor/Mono/GUI/CacheServerWindow.cs
+++ b/Editor/Mono/GUI/CacheServerWindow.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: IMGUIControls not yet converted
using UnityEngine;
using UnityEditor.Experimental;
using System;
@@ -30,14 +29,14 @@ internal class CacheServerWindow : PopupWindowContent
public CacheServerWindow()
{
- m_StatusMessageDisabled = EditorGUIUtility.TrTextContent("No cache server connected");
- m_StatusMessageConnected = EditorGUIUtility.TrTextContent("Connected");
- m_StatusMessageError = EditorGUIUtility.TrTextContent("Attempting to reconnect");
- m_OpenProjectSettings = EditorGUIUtility.TrTextContent("Open Project Settings...");
+ m_StatusMessageDisabled = L10n.TextContent("No cache server connected", null, null, null);
+ m_StatusMessageConnected = L10n.TextContent("Connected", null, null, null);
+ m_StatusMessageError = L10n.TextContent("Attempting to reconnect", null, null, null);
+ m_OpenProjectSettings = L10n.TextContent("Open Project Settings...", null, null, null);
m_UploadArtifacts = EditorGUIUtility.TrTextContent("Upload Artifacts", m_UploadArtifactsDefaultToolip);
m_UploadShaderCache = EditorGUIUtility.TrTextContent("Upload Shader Cache", m_UploadShaderCacheDefaultToolip);
m_UploadAllRevisions = EditorGUIUtility.TrTextContent("Upload All Revisions", m_UploadAllRevisionsDefaultToolip);
- m_RefreshIcon = EditorGUIUtility.TrIconContent("Refresh", "Refresh connection");
+ m_RefreshIcon = L10n.IconContent("Refresh", "Refresh connection", null);
m_WindowStyle = new GUIStyle { padding = new RectOffset(6, 6, 6, 6) };
}
@@ -204,4 +203,3 @@ public override Vector2 GetWindowSize()
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/GUI/ColorPicker.cs b/Editor/Mono/GUI/ColorPicker.cs
index 653cc5ca17..4866b1eec7 100644
--- a/Editor/Mono/GUI/ColorPicker.cs
+++ b/Editor/Mono/GUI/ColorPicker.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: IMGUIControls not yet converted
using System;
using Unity.Scripting.LifecycleManagement;
using UnityEditorInternal;
@@ -15,10 +14,6 @@ namespace UnityEditor
[VisibleToOtherModules("UnityEditor.GraphToolkitModule")]
internal class ColorPicker : EditorWindow
{
- #pragma warning disable UAL0015 // this side effect does not outlive the current call (global trigger / lazily-loaded asset re-fetched on next access); a stale reference is harmlessly replaced
- internal ColorPicker() {}
- #pragma warning restore UAL0015
-
private const string k_HeightPrefKey = "CPickerHeight";
private const string k_ShowDefaultsPrefKey = "CPDefaultsShow";
private const string k_ShowPresetsPrefKey = "CPPresetsShow";
@@ -500,12 +495,12 @@ static class Styles
public static readonly GUIStyle selectedExposureSwatchStroke = "ColorPickerCurrentExposureSwatchBorder";
#pragma warning disable UAL0015 // only populates EditorGUIUtility's icon-name-per-skin cache, which repopulates on demand from a fixed icon name
- public static readonly GUIContent eyeDropper = EditorGUIUtility.TrIconContent("EyeDropper.Large", "Pick a color from the screen.");
+ public static readonly GUIContent eyeDropper = L10n.IconContent("EyeDropper.Large", "Pick a color from the screen.", null);
#pragma warning restore UAL0015
- public static readonly GUIContent exposureValue = EditorGUIUtility.TrTextContent("Intensity", "Number of stops to over- or under-expose the color. The intensity calculates each time based on the predefined max color component of 191 (0.749) when Color Picker opens.");
- public static readonly GUIContent hexLabel = EditorGUIUtility.TrTextContent("Hexadecimal");
- public static readonly GUIContent presetsToggle = EditorGUIUtility.TrTextContent("Swatches");
- public static readonly GUIContent defaultsToggle = EditorGUIUtility.TrTextContent("Defaults");
+ public static readonly GUIContent exposureValue = L10n.TextContent("Intensity", "Number of stops to over- or under-expose the color. The intensity calculates each time based on the predefined max color component of 191 (0.749) when Color Picker opens.", null, null);
+ public static readonly GUIContent hexLabel = L10n.TextContent("Hexadecimal", null, null, null);
+ public static readonly GUIContent presetsToggle = L10n.TextContent("Swatches", null, null, null);
+ public static readonly GUIContent defaultsToggle = L10n.TextContent("Defaults", null, null, null);
public static readonly ScalableGUIContent originalColorSwatchFill =
new ScalableGUIContent(string.Empty, "The original color. Click this swatch to reset the color picker to this value.", "ColorPicker-OriginalColor");
@@ -517,10 +512,10 @@ static class Styles
public static readonly Texture2D alphaSliderCheckerBackground =
EditorGUIUtility.LoadRequired("Previews/Textures/textureChecker.png") as Texture2D;
- public static readonly GUIContent RGB0_255Mode = EditorGUIUtility.TrTextContent("RGB 0-255");
- public static readonly GUIContent RGB0_1Mode = EditorGUIUtility.TrTextContent("RGB 0-1.0");
- public static readonly GUIContent RGB0_InfMode = EditorGUIUtility.TrTextContent("RGB 0-Inf");
- public static readonly GUIContent RGBHSVMode = EditorGUIUtility.TrTextContent("HSV");
+ public static readonly GUIContent RGB0_255Mode = L10n.TextContent("RGB 0-255", null, null, null);
+ public static readonly GUIContent RGB0_1Mode = L10n.TextContent("RGB 0-1.0", null, null, null);
+ public static readonly GUIContent RGB0_InfMode = L10n.TextContent("RGB 0-Inf", null, null, null);
+ public static readonly GUIContent RGBHSVMode = L10n.TextContent("HSV", null, null, null);
public static readonly GUIContent[] sliderLDRModeLabels = new[]
{
@@ -1299,7 +1294,7 @@ static void Show(GUIView viewToUpdate, Action colorChangedCallback, Color
cp.m_Color.exposureValue = 0;
}
- cp.titleContent = hdr ? EditorGUIUtility.TrTextContent("HDR Color") : EditorGUIUtility.TrTextContent("Color");
+ cp.titleContent = hdr ? L10n.TextContent("HDR Color", null, null, null) : L10n.TextContent("Color", null, null, null);
float height = EditorPrefs.GetInt(k_HeightPrefKey, (int)cp.position.height);
cp.minSize = new Vector2(Styles.fixedWindowWidth, height);
cp.maxSize = new Vector2(Styles.fixedWindowWidth, height);
@@ -1376,6 +1371,9 @@ internal partial class EyeDropper : GUIView
GUIView m_DelegateView;
Texture2D m_Preview;
[AutoStaticsCleanupOnCodeReload]
+ // Lazy singleton: the instance getter creates an EyeDropper when this is null and the constructor
+ // assigns it back, so the next eye-dropper session recreates it.
+ [IgnoreForUAL0015("Lazy singleton recreated by the instance getter after cleanup nulls it")]
static EyeDropper s_Instance;
[NoAutoStaticsCleanup] // value type, safe to persist; overwritten at start of each EyeDropper session
private static Vector2 s_PickCoordinates = Vector2.zero;
@@ -1635,4 +1633,3 @@ protected override bool OnFocus()
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/GUI/DockArea.cs b/Editor/Mono/GUI/DockArea.cs
index 64e5162438..8c3ddb4677 100644
--- a/Editor/Mono/GUI/DockArea.cs
+++ b/Editor/Mono/GUI/DockArea.cs
@@ -10,8 +10,6 @@
using UnityEditor.Experimental;
using UnityEditorInternal;
using UnityEngine.XR;
-using System.Threading;
-using System.Threading.Tasks;
using Unity.Scripting.LifecycleManagement;
namespace UnityEditor
@@ -64,6 +62,9 @@ private static class Styles
// Which pane window would we drop the currently dragged pane over
[NoAutoStaticsCleanup] // transient drag-state index; value type, safe to persist across code reload
static int s_PlaceholderPos;
+ // Which dock area would we drop the currently dragged pane over
+ [AutoStaticsCleanupOnCodeReload]
+ static DockArea s_PlaceholderDockArea;
// Which pane is currently being dragged around
[AutoStaticsCleanupOnCodeReload]
static EditorWindow s_DragPane;
@@ -116,8 +117,9 @@ private static bool s_HasStaticTabsCapability
private Rect m_TabAreaRect = Rect.zero;
private int? newlyAddedTabIndex = null;
- internal int pendingSelect = -1;
- internal int pendingSelectVersion = 0;
+ private int m_PendingSelect = -1;
+ private double m_PendingSelectTimestamp;
+
public int selected
{
get { return m_Selected; }
@@ -330,7 +332,15 @@ public DropInfo DragOver(EditorWindow window, Vector2 mouseScreenPosition)
var tabWidth = GetTabWidth(tabStyle, window);
int mPos = GetTabAtMousePos(tabStyle, pos);
- if (s_PlaceholderPos != mPos)
+ if (!ReferenceEquals(this, s_PlaceholderDockArea))
+ {
+ Repaint();
+ if (s_PlaceholderDockArea)
+ s_PlaceholderDockArea.Repaint();
+ s_PlaceholderPos = mPos;
+ s_PlaceholderDockArea = this;
+ }
+ else if (s_PlaceholderPos != mPos)
{
Repaint();
s_PlaceholderPos = mPos;
@@ -440,6 +450,10 @@ private void DrawTabs(Rect tabAreaRect)
if (m_ScrollOffset > 0f)
clipRect.xMin += 3;
+ // Need to clear current "pending tab" if mouse moves outside of Tab's area during DragDrop
+ if (Event.current.type == EventType.DragUpdated && !clipRect.Contains(Event.current.mousePosition))
+ m_PendingSelect = -1;
+
using (new GUI.ClipScope(clipRect, new Vector2(-m_ScrollOffset - 1f, 0)))
{
if (tabStyle == null)
@@ -702,20 +716,20 @@ protected override void AddDefaultItemsToMenu(GenericMenu menu, EditorWindow vie
if (view)
{
if (parent.window.showMode == ShowMode.MainWindow && view.CanMaximize())
- menu.AddItem(EditorGUIUtility.TrTextContent("Maximize"), !(parent is SplitView), Maximize, view);
+ menu.AddItem(L10n.TextContent("Maximize", null, null, null), !(parent is SplitView), Maximize, view);
else
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Maximize"));
+ menu.AddDisabledItem(L10n.TextContent("Maximize", null, null, null));
}
bool closeAllowed = (window.showMode != ShowMode.MainWindow || AllowTabAction());
if (closeAllowed)
- menu.AddItem(EditorGUIUtility.TrTextContent("Close Tab"), false, Close, view);
+ menu.AddItem(L10n.TextContent("Close Tab", null, null, null), false, Close, view);
else
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Close Tab"));
+ menu.AddDisabledItem(L10n.TextContent("Close Tab", null, null, null));
menu.AddSeparator("");
IEnumerable types = GetPaneTypes();
- GUIContent baseContent = EditorGUIUtility.TrTextContent("Add Tab");
+ GUIContent baseContent = L10n.TextContent("Add Tab", null, null, null);
foreach (Type t in types)
{
if (t == null)
@@ -869,45 +883,25 @@ private static void CheckDragWindowExists()
private void OpenTabDelayed(int tabToOpen)
{
- if (tabToOpen == selected)
- {
- return;
- }
-
- if (tabToOpen == -1)
+ if (tabToOpen == -1 || tabToOpen == selected)
{
+ m_PendingSelect = -1;
return;
}
- if (Interlocked.Exchange(ref pendingSelect, tabToOpen) == tabToOpen)
+ // Delay a short time before selecting the specified Tab
+ double now = EditorApplication.timeSinceStartup;
+ if (tabToOpen != m_PendingSelect)
{
+ m_PendingSelect = tabToOpen;
+ m_PendingSelectTimestamp = now;
return;
}
- int pendingVersion = Interlocked.Increment(ref pendingSelectVersion);
-
- var t = Task.Run(async delegate
- {
- await Task.Delay(250);
- return 0;
- });
-
- t.Wait();
-
- Vector2 mousePos = GUIUtility.ScreenToGUIPoint(Editor.GetCurrentMousePosition());
- int tabUnderMouse = GetTabAtMousePos(tabStyle, mousePos);
-
- if (tabUnderMouse != tabToOpen)
- {
- Interlocked.CompareExchange(ref pendingSelect, -1, tabToOpen);
+ if (now - m_PendingSelectTimestamp < 0.25)
return;
- }
-
- if (pendingVersion != pendingSelectVersion)
- {
- return;
- }
+ m_PendingSelect = -1;
selected = tabToOpen;
}
@@ -1147,7 +1141,7 @@ private float DragTab(Rect tabAreaRect, float scrollOffset, GUIStyle tabStyle, G
case EventType.DragExited:
{
- Interlocked.Exchange(ref pendingSelect, -1);
+ m_PendingSelect = -1;
break;
}
@@ -1396,12 +1390,12 @@ protected override void AddDefaultItemsToMenu(GenericMenu menu, EditorWindow win
{
base.AddDefaultItemsToMenu(menu, window);
- menu.AddItem(EditorGUIUtility.TrTextContent("Maximize"), !(parent is SplitView), Unmaximize, window);
- menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Close Tab"));
+ menu.AddItem(L10n.TextContent("Maximize", null, null, null), !(parent is SplitView), Unmaximize, window);
+ menu.AddDisabledItem(L10n.TextContent("Close Tab", null, null, null));
menu.AddSeparator("");
IEnumerable types = GetPaneTypes();
- GUIContent baseContent = EditorGUIUtility.TrTextContent("Add Tab");
+ GUIContent baseContent = L10n.TextContent("Add Tab", null, null, null);
foreach (Type t in types)
{
if (t == null)
diff --git a/Editor/Mono/GUI/EditorGUIInternal.cs b/Editor/Mono/GUI/EditorGUIInternal.cs
index 88848f4295..bd568bbe2d 100644
--- a/Editor/Mono/GUI/EditorGUIInternal.cs
+++ b/Editor/Mono/GUI/EditorGUIInternal.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: IMGUIControls not yet converted
using Unity.Scripting.LifecycleManagement;
using UnityEngine;
using Object = UnityEngine.Object;
@@ -23,7 +22,7 @@ static internal GUIStyle mixedToggleStyle
private static GUIStyle s_MixedToggleStyle = EditorStyles.toggleMixed;
const float kExposureSliderAbsoluteMax = 23.0f;
- static readonly GUIContent s_ExposureIcon = EditorGUIUtility.TrIconContent("Exposure", "Controls the number of stops to over or under expose the texture.");
+ static readonly GUIContent s_ExposureIcon = L10n.IconContent("Exposure", "Controls the number of stops to over or under expose the texture.", null);
static internal Rect GetTooltipRect() { return tooltipRect; }
static internal string GetMouseTooltip() { return mouseTooltip; }
@@ -110,4 +109,3 @@ internal sealed class EditorGUILayoutUtilityInternal : GUILayoutUtility
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/GUI/EditorStyles.cs b/Editor/Mono/GUI/EditorStyles.cs
index f8a2415f88..63a10ea836 100644
--- a/Editor/Mono/GUI/EditorStyles.cs
+++ b/Editor/Mono/GUI/EditorStyles.cs
@@ -401,6 +401,30 @@ internal static GUIStyle searchFieldCancelButton
internal static GUIStyle dropDownList { get { return s_Current.m_DropDownList; } }
private GUIStyle m_DropDownList;
+ internal static GUIStyle advancedDropdownItem => s_Current.m_AdvancedDropdownItem;
+ private GUIStyle m_AdvancedDropdownItem;
+
+ internal static GUIStyle advancedDropdownItemFaint => s_Current.m_AdvancedDropdownItemFaint;
+ private GUIStyle m_AdvancedDropdownItemFaint;
+
+ internal static GUIStyle advancedDropdownBackground => s_Current.m_AdvancedDropdownBackground;
+ private GUIStyle m_AdvancedDropdownBackground;
+
+ internal static GUIStyle advancedDropdownHeader => s_Current.m_AdvancedDropdownHeader;
+ private GUIStyle m_AdvancedDropdownHeader;
+
+ internal static GUIStyle advancedDropdownCheckmark => s_Current.m_AdvancedDropdownCheckmark;
+ private GUIStyle m_AdvancedDropdownCheckmark;
+
+ internal static GUIStyle defaultLineSeparator => s_Current.m_DefaultLineSeparator;
+ private GUIStyle m_DefaultLineSeparator;
+
+ internal static GUIStyle arrowNavigationRight => s_Current.m_ArrowNavigationRight;
+ private GUIStyle m_ArrowNavigationRight;
+
+ internal static GUIStyle arrowNavigationLeft => s_Current.m_ArrowNavigationLeft;
+ private GUIStyle m_ArrowNavigationLeft;
+
internal static GUIStyle dropDownToggleButton { get { return s_Current.m_DropDownToggleButton; } }
private GUIStyle m_DropDownToggleButton;
internal static GUIStyle minMaxStateDropdown { get { return s_Current.m_MinMaxStateDropdown; } }
@@ -532,6 +556,21 @@ private void InitSharedStyles()
m_ToolbarSlider = GetStyle("ToolbarSlider");
m_MinMaxHorizontalSliderThumb = GetStyle("MinMaxHorizontalSliderThumb");
m_DropDownList = GetStyle("DropDownButton");
+ m_AdvancedDropdownItem = GetStyle("DD ItemStyle");
+ var faintColor = EditorGUIUtility.isProSkin ? new Color(0.5f, 0.5f, 0.5f, 1f) : new Color(0.25f, 0.25f, 0.25f, 1f);
+ m_AdvancedDropdownItemFaint = new GUIStyle(m_AdvancedDropdownItem);
+ m_AdvancedDropdownItemFaint.normal.textColor = faintColor;
+ m_AdvancedDropdownItemFaint.hover.textColor = faintColor;
+ m_AdvancedDropdownItemFaint.active.textColor = faintColor;
+ m_AdvancedDropdownItemFaint.focused.textColor = faintColor;
+ m_AdvancedDropdownBackground = GetStyle("DD Background");
+ // A copy: the ellipsis and padding tweaks must not mutate the skin's own style instance.
+ m_AdvancedDropdownHeader = new GUIStyle(GetStyle("DD HeaderStyle")) { clipping = TextClipping.Ellipsis };
+ m_AdvancedDropdownHeader.padding.left = 20;
+ m_AdvancedDropdownCheckmark = GetStyle("DD ItemCheckmark");
+ m_DefaultLineSeparator = GetStyle("DefaultLineSeparator");
+ m_ArrowNavigationRight = GetStyle("ArrowNavigationRight");
+ m_ArrowNavigationLeft = GetStyle("ArrowNavigationLeft");
m_DropDownToggleButton = GetStyle("DropDownToggleButton");
m_MinMaxStateDropdown = GetStyle("IN MinMaxStateDropdown");
m_ProgressBarBack = GetStyle("ProgressBarBack");
diff --git a/Editor/Mono/GUI/FallbackEditorWindow.cs b/Editor/Mono/GUI/FallbackEditorWindow.cs
index 3d00404864..5773ac9365 100644
--- a/Editor/Mono/GUI/FallbackEditorWindow.cs
+++ b/Editor/Mono/GUI/FallbackEditorWindow.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: IMGUIControls not yet converted
using UnityEngine;
using UnityEditor;
using System.Collections;
@@ -18,7 +17,7 @@ internal class FallbackEditorWindow : EditorWindow
void OnEnable()
{
- titleContent = EditorGUIUtility.TrTextContent("Failed to load");
+ titleContent = L10n.TextContent("Failed to load", null, null, null);
}
void OnGUI()
@@ -37,4 +36,3 @@ void OnGUI()
}
}
} // namespace
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/GUI/FlexibleMenu/FlexibleMenu.cs b/Editor/Mono/GUI/FlexibleMenu/FlexibleMenu.cs
index 3e76a3d2b8..9d9e0911f4 100644
--- a/Editor/Mono/GUI/FlexibleMenu/FlexibleMenu.cs
+++ b/Editor/Mono/GUI/FlexibleMenu/FlexibleMenu.cs
@@ -13,7 +13,7 @@ internal partial class FlexibleMenu : PopupWindowContent
class Styles
{
public GUIStyle menuItem = "MenuItem";
- public GUIContent plusButtonText = EditorGUIUtility.TrTextContent("", "Add New Item");
+ public GUIContent plusButtonText = L10n.TextContent("", "Add New Item", null, null);
}
[NoAutoStaticsCleanup] // lazy GUIStyle/GUIContent cache; re-created on first access, safe to persist
static Styles s_Styles;
@@ -307,8 +307,8 @@ static public void Show(int itemIndex, FlexibleMenu caller)
{
s_Caller = caller;
GenericMenu menu = new GenericMenu();
- menu.AddItem(EditorGUIUtility.TrTextContent("Edit..."), false, Edit, itemIndex);
- menu.AddItem(EditorGUIUtility.TrTextContent("Delete"), false, Delete, itemIndex);
+ menu.AddItem(L10n.TextContent("Edit...", null, null, null), false, Edit, itemIndex);
+ menu.AddItem(L10n.TextContent("Delete", null, null, null), false, Delete, itemIndex);
menu.ShowAsContext();
EditorGUIUtility.ExitGUI();
}
diff --git a/Editor/Mono/GUI/GradientEditor.cs b/Editor/Mono/GUI/GradientEditor.cs
index 684a814a91..b0127955dd 100644
--- a/Editor/Mono/GUI/GradientEditor.cs
+++ b/Editor/Mono/GUI/GradientEditor.cs
@@ -18,10 +18,10 @@ class Styles
public GUIStyle downSwatch = "Grad Down Swatch";
public GUIStyle downSwatchOverlay = "Grad Down Swatch Overlay";
- public GUIContent modeText = EditorGUIUtility.TrTextContent("Mode");
- public GUIContent alphaText = EditorGUIUtility.TrTextContent("Alpha");
- public GUIContent colorText = EditorGUIUtility.TrTextContent("Color");
- public GUIContent locationText = EditorGUIUtility.TrTextContent("Location");
+ public GUIContent modeText = L10n.TextContent("Mode", null, null, null);
+ public GUIContent alphaText = L10n.TextContent("Alpha", null, null, null);
+ public GUIContent colorText = L10n.TextContent("Color", null, null, null);
+ public GUIContent locationText = L10n.TextContent("Location", null, null, null);
public GUIContent percentText = new GUIContent("%");
static GUIStyle GetStyle(string name)
@@ -32,9 +32,9 @@ static GUIStyle GetStyle(string name)
public readonly GUIContent[] modeTexts =
{
- EditorGUIUtility.TrTextContent("Blend (Classic)"),
- EditorGUIUtility.TrTextContent("Blend (Perceptual)"),
- EditorGUIUtility.TrTextContent("Fixed")
+ L10n.TextContent("Blend (Classic)", null, null, null),
+ L10n.TextContent("Blend (Perceptual)", null, null, null),
+ L10n.TextContent("Fixed", null, null, null)
};
public readonly int[] modeValues =
{
@@ -543,9 +543,12 @@ void AssignBack()
// The binding's FieldValueChange collapses every gradient commit into the entry undo group;
// without an increment between gestures the entry never advances and they merge.
- static void EndGradientGesture()
+ internal static void EndGradientGesture()
{
- Undo.SetCurrentGroupName("Modify Gradient");
+ // Flush first so pending changes count; naming registers an undo record, which clears redo. (UUM-153510)
+ Undo.FlushTrackedObjects();
+ if (!Undo.HasRedo())
+ Undo.SetCurrentGroupName("Modify Gradient");
Undo.IncrementCurrentGroup();
}
diff --git a/Editor/Mono/GUI/GradientPicker.cs b/Editor/Mono/GUI/GradientPicker.cs
index 78ae1a2721..21a8d2443d 100644
--- a/Editor/Mono/GUI/GradientPicker.cs
+++ b/Editor/Mono/GUI/GradientPicker.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: IMGUIControls not yet converted
using UnityEditorInternal;
using UnityEngine;
using Unity.Scripting.LifecycleManagement;
@@ -27,6 +26,9 @@ protected override void DrawPreset(PresetLibrary lib, Rect rect, object presetOb
internal partial class GradientPicker : EditorWindow
{
[AutoStaticsCleanupOnCodeReload]
+ // Shared window cache: PrepareShow re-acquires the gradient editor window through GetWindow whenever
+ // this is null, so the next Show call restores it.
+ [IgnoreForUAL0015("Shared window cache re-acquired by PrepareShow through GetWindow")]
private static GradientPicker s_GradientPicker;
public static string presetsEditorPrefID { get { return "Gradient"; } }
@@ -320,4 +322,3 @@ private void OnUndoPerformed(in UndoRedoInfo info)
}
}
} // namespace
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/GUI/ManagedDebuggerToggle.cs b/Editor/Mono/GUI/ManagedDebuggerToggle.cs
index 54dc30a566..1f9766bccc 100644
--- a/Editor/Mono/GUI/ManagedDebuggerToggle.cs
+++ b/Editor/Mono/GUI/ManagedDebuggerToggle.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: IMGUIControls not yet converted
using UnityEngine;
using UnityEditor.Compilation;
using UnityEditor.Scripting;
@@ -18,9 +17,9 @@ internal class ManagedDebuggerToggle
public ManagedDebuggerToggle()
{
- m_DebuggerAttachedContent = EditorGUIUtility.TrIconContent("DebuggerAttached", "Debugger Attached");
- m_DebuggerDisabledContent = EditorGUIUtility.TrIconContent("DebuggerDisabled", "Debugger Disabled");
- m_DebuggerEnabledContent = EditorGUIUtility.TrIconContent("DebuggerEnabled", "Debugger Enabled");
+ m_DebuggerAttachedContent = L10n.IconContent("DebuggerAttached", "Debugger Attached", null);
+ m_DebuggerDisabledContent = L10n.IconContent("DebuggerDisabled", "Debugger Disabled", null);
+ m_DebuggerEnabledContent = L10n.IconContent("DebuggerEnabled", "Debugger Enabled", null);
m_PopupLocation = new[] { PopupLocation.AboveAlignRight };
}
@@ -55,4 +54,3 @@ private GUIContent GetDebuggerContent(bool debuggerAttached, CodeOptimization op
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/GUI/ManagedDebuggerWindow.cs b/Editor/Mono/GUI/ManagedDebuggerWindow.cs
index 4603508424..62264cefdb 100644
--- a/Editor/Mono/GUI/ManagedDebuggerWindow.cs
+++ b/Editor/Mono/GUI/ManagedDebuggerWindow.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: IMGUIControls not yet converted
using UnityEngine;
using UnityEditor.Compilation;
using UnityEditor.Scripting;
@@ -29,7 +28,9 @@ internal class ManagedDebuggerWindow : PopupWindowContent
static ManagedDebuggerWindow()
{
+#pragma warning disable UAL0015 // Mono-only subscription (this whole block is compiled out under CoreCLR): a Mono domain reload re-runs this static constructor, so the cleared invocation list refills; the CoreCLR code-reload cleanup never sees this subscriber
SubscribeToDebuggerAttached();
+#pragma warning restore UAL0015
}
public ManagedDebuggerWindow(CodeOptimization codeOptimization)
@@ -38,16 +39,16 @@ public ManagedDebuggerWindow(CodeOptimization codeOptimization)
if (CodeOptimization.Debug == m_CodeOptimization)
{
- m_CodeOptimizationTitleContent = EditorGUIUtility.TrTextContent("Mode: Debug");
- m_CodeOptimizationButtonContent = EditorGUIUtility.TrTextContent("Switch to release mode");
+ m_CodeOptimizationTitleContent = L10n.TextContent("Mode: Debug", null, null, null);
+ m_CodeOptimizationButtonContent = L10n.TextContent("Switch to release mode", null, null, null);
m_CodeOptimizationTextContent = (!EditorUtility.scriptCompilationFailed) ?
EditorGUIUtility.TrTextContentWithIcon("Release mode disables C# debugging but improves C# performance.\nSwitching to release mode will recompile and reload all scripts.", EditorGUIUtility.GetHelpIcon(MessageType.Info)) :
EditorGUIUtility.TrTextContentWithIcon("All compiler errors must be fixed before switching to release mode.", EditorGUIUtility.GetHelpIcon(MessageType.Error));
}
else
{
- m_CodeOptimizationTitleContent = EditorGUIUtility.TrTextContent("Mode: Release");
- m_CodeOptimizationButtonContent = EditorGUIUtility.TrTextContent("Switch to debug mode");
+ m_CodeOptimizationTitleContent = L10n.TextContent("Mode: Release", null, null, null);
+ m_CodeOptimizationButtonContent = L10n.TextContent("Switch to debug mode", null, null, null);
m_CodeOptimizationTextContent = (!EditorUtility.scriptCompilationFailed) ?
EditorGUIUtility.TrTextContentWithIcon("Debug mode enables C# debugging but reduces C# performance.\nSwitching to debug mode will recompile and reload all scripts.", EditorGUIUtility.GetHelpIcon(MessageType.Info)) :
EditorGUIUtility.TrTextContentWithIcon("All compiler errors must be fixed before switching to debug mode.", EditorGUIUtility.GetHelpIcon(MessageType.Error));
@@ -165,4 +166,3 @@ private static void ToggleDebugState(CodeOptimization codeOptimization)
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/GUI/MaskFieldGUI.cs b/Editor/Mono/GUI/MaskFieldGUI.cs
index b6eab44518..b740a02d4c 100644
--- a/Editor/Mono/GUI/MaskFieldGUI.cs
+++ b/Editor/Mono/GUI/MaskFieldGUI.cs
@@ -20,6 +20,9 @@ private partial class MaskCallbackInfo
{
// The global shared popup state
[AutoStaticsCleanupOnCodeReload]
+ // In-flight mask dropdown state: a fresh instance is created every time the mask menu is displayed and
+ // is only read while that menu hands its selection back to OnGUI.
+ [IgnoreForUAL0015("Transient mask-dropdown state recreated each time the menu is displayed")]
public static MaskCallbackInfo m_Instance;
// Name of the command event sent from the popup menu to OnGUI when user has changed selection
diff --git a/Editor/Mono/GUI/ObjectField.cs b/Editor/Mono/GUI/ObjectField.cs
index 80b16cb7ad..01b8b2521d 100644
--- a/Editor/Mono/GUI/ObjectField.cs
+++ b/Editor/Mono/GUI/ObjectField.cs
@@ -13,9 +13,9 @@ namespace UnityEditor
{
public sealed partial class EditorGUI
{
- static private readonly GUIContent s_SceneMismatch = EditorGUIUtility.TrTextContent("Scene mismatch (cross scene references not supported)");
- static private readonly GUIContent s_TypeMismatch = EditorGUIUtility.TrTextContent("Type mismatch");
- static private readonly GUIContent s_Select = EditorGUIUtility.TrTextContent("Select");
+ static private readonly GUIContent s_SceneMismatch = L10n.TextContent("Scene mismatch (cross scene references not supported)", null, null, null);
+ static private readonly GUIContent s_TypeMismatch = L10n.TextContent("Type mismatch", null, null, null);
+ static private readonly GUIContent s_Select = L10n.TextContent("Select", null, null, null);
const float k_NullObjectReferenceOpacity = 0.7f;
diff --git a/Editor/Mono/GUI/PopupWindow.cs b/Editor/Mono/GUI/PopupWindow.cs
index cc01f6c41e..3859438251 100644
--- a/Editor/Mono/GUI/PopupWindow.cs
+++ b/Editor/Mono/GUI/PopupWindow.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: IMGUIControls not yet converted
using UnityEngine;
using UnityEngine.UIElements;
using Unity.Scripting.LifecycleManagement;
@@ -246,4 +245,3 @@ static PopupWindow FindExistingPopupWindow(PopupWindowContent windowContent)
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/GUI/PopupWindowWithoutFocus.cs b/Editor/Mono/GUI/PopupWindowWithoutFocus.cs
index 6406e0fc5a..299ffedec6 100644
--- a/Editor/Mono/GUI/PopupWindowWithoutFocus.cs
+++ b/Editor/Mono/GUI/PopupWindowWithoutFocus.cs
@@ -2,7 +2,6 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: IMGUIControls not yet converted
using UnityEngine;
using UnityEngine.Scripting;
using UnityEditor;
@@ -16,6 +15,9 @@ namespace UnityEditor
partial class PopupWindowWithoutFocus : PopupWindow
{
[AutoStaticsCleanupOnCodeReload]
+ // Points at the popup window that is currently open; OnEnable assigns it and OnDisable clears it, and
+ // Show creates a new popup when it is null.
+ [IgnoreForUAL0015("Open-popup reference reassigned by OnEnable and recreated by Show when null")]
static PopupWindowWithoutFocus s_PopupWindowWithoutFocus;
bool hasBeenFocused = false;
@@ -108,4 +110,3 @@ static bool OnGlobalMouseOrKeyEvent(EventType type, KeyCode keyCode, float mouse
}
}
}
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/GUI/PropertyDrawers/Dictionary/DictionaryDrawerIMGUI.cs b/Editor/Mono/GUI/PropertyDrawers/Dictionary/DictionaryDrawerIMGUI.cs
index f77ac7614c..8b6d60357f 100644
--- a/Editor/Mono/GUI/PropertyDrawers/Dictionary/DictionaryDrawerIMGUI.cs
+++ b/Editor/Mono/GUI/PropertyDrawers/Dictionary/DictionaryDrawerIMGUI.cs
@@ -2,12 +2,11 @@
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
-#pragma warning disable UAL0015,UAL0018,UAL0019,UAL0020,UAL0021 // AutoStaticsCleanup usage analysis: IMGUIControls not yet converted
-#pragma warning disable UAL0010,UAL0011,UAL0012,UAL0013,UAL0014 // AutoStaticsCleanup: IMGUIControls not yet converted
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
+using UnityEngine.Pool;
using UnityEngine.UIElements;
using UnityEditor.IMGUI.Controls;
using UnityEditor.UIElements;
@@ -91,6 +90,7 @@ static class Styles
public const float k_EmptyLabelIndent = 18f;
public const float k_IgnoredHelpBoxTopMargin = 4f;
public const float k_IgnoredHelpBoxBottomMargin = 4f;
+ public const float k_FoldoutInfoRightInset = 4f;
public static readonly GUIStyle headerBackground = "RL Header";
public static readonly GUIStyle boxBackground = "RL Background";
@@ -99,8 +99,8 @@ static class Styles
public static readonly GUIStyle columnLabel = "MultiColumnHeader";
public static readonly GUIStyle columnLabelClipped = new GUIStyle(columnLabel) { clipping = TextClipping.Ellipsis };
- public static readonly GUIContent iconPlus = EditorGUIUtility.TrIconContent("Toolbar Plus");
- public static readonly GUIContent iconMinus = EditorGUIUtility.TrIconContent("Toolbar Minus");
+ public static readonly GUIContent iconPlus = L10n.IconContent("Toolbar Plus", null, null);
+ public static readonly GUIContent iconMinus = L10n.IconContent("Toolbar Minus", null, null);
public static readonly Texture2D sortAscIcon = EditorGUIUtility.LoadIconRequired("UIPackageResources/Images/scrollup_uielements.png");
public static readonly Texture2D sortDescIcon = EditorGUIUtility.LoadIconRequired("UIPackageResources/Images/scrolldown_uielements.png");
@@ -1151,8 +1151,19 @@ void DrawIgnoredHelpBox(Rect position, float startY, float helpBoxHeight, string
float helpBoxY = startY + Styles.k_IgnoredHelpBoxTopMargin;
var helpBoxRect = new Rect(position.x, helpBoxY, position.width, helpBoxHeight);
- if (DrawerEditorGUI.HelpBoxWithButton(helpBoxRect, MessageType.Warning, helpBoxText, Texts.SelectFirstIgnoredButtonLabel))
+ if (duplicateEntryIndices.Count + nullKeyEntryIndices.Count == 1)
+ {
+ if (DrawerEditorGUI.HelpBoxWithButton(helpBoxRect, MessageType.Warning, helpBoxText, Texts.SelectIgnoredButtonLabel))
+ SelectFirstIgnored();
+ return;
+ }
+
+ int pressedIndex = DrawerEditorGUI.HelpBoxWithButtons(helpBoxRect, MessageType.Warning, helpBoxText,
+ Texts.SelectFirstIgnoredButtonLabel, Texts.SelectAllIgnoredButtonLabel);
+ if (pressedIndex == 0)
SelectFirstIgnored();
+ else if (pressedIndex == 1)
+ SelectAllIgnored();
}
void SelectFirstIgnored()
@@ -1168,6 +1179,22 @@ void SelectFirstIgnored()
treeView.SetFocus();
}
+ void SelectAllIgnored()
+ {
+ if (treeView == null)
+ return;
+
+ using (ListPool.Get(out var displayIndices))
+ {
+ CollectIgnoredDisplayIndices(duplicateEntryIndices, nullKeyEntryIndices, sortedIndices, displayIndices);
+ if (displayIndices.Count == 0)
+ return;
+
+ treeView.SetSelection(displayIndices, TreeViewSelectionOptions.RevealAndFrame);
+ treeView.SetFocus();
+ }
+ }
+
void DrawFoldoutHeader(Rect rect, SerializedProperty property, GUIContent label)
{
int duplicateCount = duplicateEntryIndices?.Count ?? 0;
@@ -1188,9 +1215,15 @@ void DrawFoldoutHeader(Rect rect, SerializedProperty property, GUIContent label)
}
var infoSize = EditorStyles.miniLabel.CalcSize(new GUIContent(infoText));
- var infoRect = new Rect(rect.xMax - infoSize.x - 4f, rect.y, infoSize.x, rect.height);
+ float infoRight = rect.xMax - Styles.k_FoldoutInfoRightInset;
+ float minInfoX = rect.x + EditorStyles.foldout.CalcSize(label).x;
+ float infoX = Mathf.Max(minInfoX, infoRight - infoSize.x);
+ var infoRect = new Rect(infoX, rect.y, infoRight - infoX, rect.height);
property.isExpanded = EditorGUI.Foldout(rect, property.isExpanded, label, true);
+ if (infoRect.width <= 0f)
+ return;
+
using (new EditorGUI.DisabledScope(true))
EditorGUI.LabelField(infoRect, infoText, EditorStyles.miniLabel);
}
@@ -1655,7 +1688,7 @@ static void HandleContextMenu(Rect headerRect, DrawerInstanceIMGUI instance)
var menu = new GenericMenu();
// The three layouts form a radio group (the active one is checked),
- // followed by a separator and the "Reset to Defaults" action.
+ // followed by a separator and the "Reset Layout" action.
AddLayoutItem(menu, Texts.TwoColumnsLayoutLabel, DictionaryLayout.TwoColumns, instance);
AddLayoutItem(menu, Texts.OneColumnWithValueFoldoutLayoutLabel, DictionaryLayout.OneColumnWithValueFoldout, instance);
AddLayoutItem(menu, Texts.OneColumnWithValueVisibleLayoutLabel, DictionaryLayout.OneColumnWithValueVisible, instance);
@@ -1667,14 +1700,14 @@ static void HandleContextMenu(Rect headerRect, DrawerInstanceIMGUI instance)
if (instance.header.HasCachedState)
{
- menu.AddItem(new GUIContent(Texts.ResetToDefaultsLabel), false, () =>
+ menu.AddItem(new GUIContent(Texts.ResetLayoutLabel), false, () =>
{
instance.ResetToDefaults();
});
}
else
{
- menu.AddDisabledItem(new GUIContent(Texts.ResetToDefaultsLabel));
+ menu.AddDisabledItem(new GUIContent(Texts.ResetLayoutLabel));
}
menu.ShowAsContext();
@@ -2325,6 +2358,10 @@ internal static class DrawerEditorGUI
const float k_HelpBoxButtonHeight = 20f;
const float k_HelpBoxButtonMinWidth = 60f;
const float k_HelpBoxButtonInset = 4f;
+ const float k_HelpBoxButtonSpacing = 2f;
+ // EditorStyles.helpBox scales its icon down by whatever room is left after the text, so the
+ // icon would otherwise change size with the message and feed back into the text height.
+ static readonly Vector2 k_HelpBoxIconSize = new Vector2(16f, 16f);
// EditorStyles.helpBox uses MiddleLeft vertical alignment, which would vertically
// center the icon+text inside the (intentionally taller-than-text) HelpBoxWithButton
@@ -2350,8 +2387,11 @@ static GUIStyle helpBoxUpperLeft
public static float GetHelpBoxWithButtonHeight(MessageType messageType, string message, float width)
{
var content = EditorGUIUtility.TempContent(message, EditorGUIUtility.GetHelpIcon(messageType));
- float textHeight = helpBoxUpperLeft.CalcHeight(content, width);
- return textHeight + k_HelpBoxButtonGap + k_HelpBoxButtonHeight;
+ using (new EditorGUIUtility.IconSizeScope(k_HelpBoxIconSize))
+ {
+ float textHeight = helpBoxUpperLeft.CalcHeight(content, width);
+ return textHeight + k_HelpBoxButtonGap + k_HelpBoxButtonHeight;
+ }
}
// Draws an EditorGUI.HelpBox-styled box (with the icon for the given MessageType)
@@ -2359,17 +2399,39 @@ public static float GetHelpBoxWithButtonHeight(MessageType messageType, string m
// button is pressed. Button width auto-fits the button text (clamped to k_HelpBoxButtonMinWidth)
// using the same style we draw it with, so longer labels stay readable without truncation.
public static bool HelpBoxWithButton(Rect position, MessageType messageType, string message, string buttonText)
+ {
+ return HelpBoxWithButtons(position, messageType, message, buttonText, null) == 0;
+ }
+
+ public static int HelpBoxWithButtons(
+ Rect position, MessageType messageType, string message, string firstButtonText, string secondButtonText)
{
var content = EditorGUIUtility.TempContent(message, EditorGUIUtility.GetHelpIcon(messageType));
- GUI.Label(position, content, helpBoxUpperLeft);
+ using (new EditorGUIUtility.IconSizeScope(k_HelpBoxIconSize))
+ GUI.Label(position, content, helpBoxUpperLeft);
+
+ float rightEdge = position.xMax - k_HelpBoxButtonInset;
+ float minX = position.x + helpBoxUpperLeft.padding.left;
+ float buttonY = position.yMax - k_HelpBoxButtonHeight - k_HelpBoxButtonInset;
+
+ int pressedIndex = -1;
+ if (DrawHelpBoxButton(secondButtonText, buttonY, minX, ref rightEdge))
+ pressedIndex = 1;
+ if (DrawHelpBoxButton(firstButtonText, buttonY, minX, ref rightEdge))
+ pressedIndex = 0;
+ return pressedIndex;
+ }
+
+ static bool DrawHelpBoxButton(string buttonText, float buttonY, float minX, ref float rightEdge)
+ {
+ if (string.IsNullOrEmpty(buttonText) || rightEdge <= minX)
+ return false;
var buttonContent = EditorGUIUtility.TempContent(buttonText);
float buttonWidth = Mathf.Max(k_HelpBoxButtonMinWidth, GUI.skin.button.CalcSize(buttonContent).x);
- var buttonRect = new Rect(
- position.xMax - buttonWidth - k_HelpBoxButtonInset,
- position.yMax - k_HelpBoxButtonHeight - k_HelpBoxButtonInset,
- buttonWidth,
- k_HelpBoxButtonHeight);
+ float buttonX = Mathf.Max(minX, rightEdge - buttonWidth);
+ var buttonRect = new Rect(buttonX, buttonY, rightEdge - buttonX, k_HelpBoxButtonHeight);
+ rightEdge = buttonX - k_HelpBoxButtonSpacing;
return GUI.Button(buttonRect, buttonContent);
}
@@ -2393,5 +2455,3 @@ public void Dispose()
}
} // end of namespace
-#pragma warning restore UAL0010,UAL0011,UAL0012,UAL0013,UAL0014
-#pragma warning restore UAL0015,UAL0018,UAL0019,UAL0020,UAL0021
diff --git a/Editor/Mono/GUI/PropertyDrawers/Dictionary/DictionaryDrawerShared.cs b/Editor/Mono/GUI/PropertyDrawers/Dictionary/DictionaryDrawerShared.cs
index 808e211b40..832bcf11c0 100644
--- a/Editor/Mono/GUI/PropertyDrawers/Dictionary/DictionaryDrawerShared.cs
+++ b/Editor/Mono/GUI/PropertyDrawers/Dictionary/DictionaryDrawerShared.cs
@@ -102,7 +102,7 @@ internal static class Texts
// Foldout titles for a dictionary value that is itself a collection, replacing the "Value" displayName.
internal static readonly string NestedArrayLabel = L10n.Tr("Array", null);
internal static readonly string NestedListLabel = L10n.Tr("List", null);
- internal static readonly string ResetToDefaultsLabel = L10n.Tr("Reset to Defaults", null);
+ internal static readonly string ResetLayoutLabel = L10n.Tr("Reset Layout", null);
internal static readonly string MultiEditUnsupportedMessage = L10n.Tr("Dictionary: Multi-object editing is not supported.", null); // Entries are sorted by key, so a given row may correspond to different entries across targets, so edits could affect unrelated entries
internal static readonly string DuplicateMarkerTooltip = L10n.Tr("An element with the same key already exists, so this element is excluded from the runtime dictionary.", null);
internal static readonly string NullKeyMarkerTooltip = L10n.Tr("The key is null. A dictionary only stores entries with a valid (non-null) key, so this element is excluded from the runtime dictionary.", null);
@@ -114,7 +114,9 @@ internal static class Texts
internal static readonly string NullKeysHelpBoxSingle = L10n.Tr("1 null key ignored. A dictionary key can't be null.", null);
internal static readonly string NullKeysHelpBoxFormat = L10n.Tr("{0} null keys ignored. A dictionary key can't be null.", null);
internal static readonly string MixedIgnoredHelpBoxFormat = L10n.Tr("{0} entries ignored. Ensure all keys are unique and non-null.", null);
- internal static readonly string SelectFirstIgnoredButtonLabel = L10n.Tr("Select", null);
+ internal static readonly string SelectIgnoredButtonLabel = L10n.Tr("Select", null);
+ internal static readonly string SelectAllIgnoredButtonLabel = L10n.Tr("Select All", null);
+ internal static readonly string SelectFirstIgnoredButtonLabel = L10n.Tr("Select First", null);
// Header context-menu labels for the three DictionaryLayout values, shown as a
// radio group (the active layout is checked).
internal static readonly string TwoColumnsLayoutLabel = L10n.Tr("Two Columns", null);
@@ -187,7 +189,7 @@ internal class DictionaryState
// Disk-backed, persistent across editor sessions. Key: Hash128 of normalized path
// ([\d+] → []) so container siblings share state (linked views). Eviction: only via
- // explicit RemoveState from the "Reset to Defaults" context menu.
+ // explicit RemoveState from the "Reset Layout" context menu.
[NoAutoStaticsCleanup] // disk-backed state cache, intentionally persistent across sessions; safe to persist
static readonly StateCache s_StateCache = new StateCache("Library/StateCache/DictionaryDrawer/");
@@ -311,6 +313,9 @@ internal static void ClearCachedState(Hash128 stateCacheKey)
internal static bool ShowSerializedOrder => SessionState.GetBool(k_SerializedOrderSessionKey, false);
[AutoStaticsCleanupOnCodeReload] // stale view handlers after reload would pin the old ALC; views resubscribe on attach
+ // Subscribed when a DictionaryView attaches to a panel and unsubscribed when it detaches, so an
+ // empty handler list is correct while no view is attached; reattaching subscribes again.
+ [IgnoreForUAL0015("Subscribed and unsubscribed as DictionaryViews attach to and detach from a panel")]
internal static event Action SerializedOrderChanged;
internal static void SetShowSerializedOrder(bool value)
@@ -601,6 +606,9 @@ public AssemblyLayoutEntry(DictionaryDisplayAttribute attribute, string declarin
// a project. The key is the closed Dictionary targetType.
// Statics reset on domain reload, so the cache rebuilds automatically when assemblies change.
[AutoStaticsCleanupOnCodeReload]
+ // Lazy registry: GetAssemblyLayoutRegistry rescans the loaded assemblies whenever this is null, so
+ // the mapping cleared on reload is rebuilt from the assemblies that are actually loaded now.
+ [IgnoreForUAL0015("Lazy assembly-layout registry rebuilt by GetAssemblyLayoutRegistry on the next access")]
static Dictionary s_AssemblyLayoutRegistry;
static Dictionary GetAssemblyLayoutRegistry()
@@ -840,6 +848,31 @@ internal static int FindFirstIgnoredDisplayIndex(
return firstDisplayIndex == int.MaxValue ? -1 : firstDisplayIndex;
}
+ internal static void CollectIgnoredDisplayIndices(
+ IEnumerable duplicateArrayIndices,
+ IEnumerable nullKeyArrayIndices,
+ SortedIndexMap sortedIndices,
+ List displayIndices)
+ {
+ displayIndices.Clear();
+ AddDisplayIndices(duplicateArrayIndices, sortedIndices, displayIndices);
+ AddDisplayIndices(nullKeyArrayIndices, sortedIndices, displayIndices);
+ displayIndices.Sort();
+ }
+
+ static void AddDisplayIndices(IEnumerable arrayIndices, SortedIndexMap sortedIndices, List displayIndices)
+ {
+ if (arrayIndices == null)
+ return;
+
+ foreach (var arrayIndex in arrayIndices)
+ {
+ if (!sortedIndices.ContainsArrayIndex(arrayIndex))
+ continue;
+ displayIndices.Add(sortedIndices.ToDisplayIndex(arrayIndex));
+ }
+ }
+
static int MinDisplayIndex(IEnumerable