diff --git a/Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/Data.cs b/Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/Data.cs index e496ccbdd9..fc674d689c 100644 --- a/Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/Data.cs +++ b/Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/Data.cs @@ -56,6 +56,7 @@ public class AssemblyData public class ScriptCompilationData_Out { public AssemblyData_Out[] Assemblies; + public bool LocalizeCompilerMessages; } public class AssemblyData_Out diff --git a/Editor/Mono/2D/SpriteAtlas/EditorSpriteAtlas.bindings.cs b/Editor/Mono/2D/SpriteAtlas/EditorSpriteAtlas.bindings.cs index fb99a0ffde..bf615783af 100644 --- a/Editor/Mono/2D/SpriteAtlas/EditorSpriteAtlas.bindings.cs +++ b/Editor/Mono/2D/SpriteAtlas/EditorSpriteAtlas.bindings.cs @@ -111,8 +111,23 @@ internal static void RegisterAndPackAtlas(this SpriteAtlas spriteAtlas, AssetImp extern public static void SetTextureSettings([NotNull] this SpriteAtlas spriteAtlas, SpriteAtlasTextureSettings src); extern public static SpriteAtlasPackingSettings GetPackingSettings([NotNull] this SpriteAtlas spriteAtlas); extern public static void SetPackingSettings([NotNull] this SpriteAtlas spriteAtlas, SpriteAtlasPackingSettings src); - extern public static TextureImporterPlatformSettings GetPlatformSettings([NotNull] this SpriteAtlas spriteAtlas, string buildTarget); - extern public static void SetPlatformSettings([NotNull] this SpriteAtlas spriteAtlas, TextureImporterPlatformSettings src); + + [NativeName("GetPlatformSettings")] + extern private static TextureImporterPlatformSettings GetPlatformSettings_Internal([NotNull] this SpriteAtlas spriteAtlas, string buildTarget); + public static TextureImporterPlatformSettings GetPlatformSettings(this SpriteAtlas spriteAtlas, string buildTarget) + { + buildTarget = TextureImporter.GetTexturePlatformSerializationName(buildTarget); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". + return GetPlatformSettings_Internal(spriteAtlas, buildTarget); + } + + [NativeName("SetPlatformSettings")] + extern private static void SetPlatformSettings_Internal([NotNull] this SpriteAtlas spriteAtlas, TextureImporterPlatformSettings src); + public static void SetPlatformSettings(this SpriteAtlas spriteAtlas, TextureImporterPlatformSettings src) + { + src.name = TextureImporter.GetTexturePlatformSerializationName(src.name); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". + SetPlatformSettings_Internal(spriteAtlas, src); + } + extern public static void SetIncludeInBuild([NotNull] this SpriteAtlas spriteAtlas, bool value); extern public static void SetIsVariant([NotNull] this SpriteAtlas spriteAtlas, bool value); extern public static void SetMasterAtlas([NotNull] this SpriteAtlas spriteAtlas, SpriteAtlas value); @@ -126,8 +141,23 @@ internal static void RegisterAndPackAtlas(this SpriteAtlas spriteAtlas, AssetImp extern internal static TextureFormat GetTextureFormat([NotNull] this SpriteAtlas spriteAtlas, BuildTarget target); extern internal static Sprite[] GetPackedSprites([NotNull] this SpriteAtlas spriteAtlas); extern internal static Hash128 GetStoredHash([NotNull] this SpriteAtlas spriteAtlas); - extern internal static TextureImporterPlatformSettings GetSecondaryPlatformSettings([NotNull] this SpriteAtlas spriteAtlas, string buildTarget, string secondaryTextureName); - extern internal static void SetSecondaryPlatformSettings([NotNull] this SpriteAtlas spriteAtlas, TextureImporterPlatformSettings src, string secondaryTextureName); + + [NativeName("GetSecondaryPlatformSettings")] + extern private static TextureImporterPlatformSettings GetSecondaryPlatformSettings_Internal([NotNull] this SpriteAtlas spriteAtlas, string buildTarget, string secondaryTextureName); + internal static TextureImporterPlatformSettings GetSecondaryPlatformSettings(this SpriteAtlas spriteAtlas, string buildTarget, string secondaryTextureName) + { + buildTarget = TextureImporter.GetTexturePlatformSerializationName(buildTarget); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". + return GetSecondaryPlatformSettings_Internal(spriteAtlas, buildTarget, secondaryTextureName); + } + + [NativeName("SetSecondaryPlatformSettings")] + extern private static void SetSecondaryPlatformSettings_Internal([NotNull] this SpriteAtlas spriteAtlas, TextureImporterPlatformSettings src, string secondaryTextureName); + internal static void SetSecondaryPlatformSettings(this SpriteAtlas spriteAtlas, TextureImporterPlatformSettings src, string secondaryTextureName) + { + src.name = TextureImporter.GetTexturePlatformSerializationName(src.name); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". + SetSecondaryPlatformSettings_Internal(spriteAtlas, src, secondaryTextureName); + } + extern internal static bool GetSecondaryColorSpace([NotNull] this SpriteAtlas spriteAtlas, string secondaryTextureName); extern internal static void SetSecondaryColorSpace([NotNull] this SpriteAtlas spriteAtlas, string secondaryTextureName, bool srGB); extern internal static void DeleteSecondaryPlatformSettings([NotNull] this SpriteAtlas spriteAtlas, string secondaryTextureName); diff --git a/Editor/Mono/2D/SpriteAtlas/SpriteAtlasImporter.bindings.cs b/Editor/Mono/2D/SpriteAtlas/SpriteAtlasImporter.bindings.cs index 71a6d39d3a..3d1695cba4 100644 --- a/Editor/Mono/2D/SpriteAtlas/SpriteAtlasImporter.bindings.cs +++ b/Editor/Mono/2D/SpriteAtlas/SpriteAtlasImporter.bindings.cs @@ -15,7 +15,7 @@ namespace UnityEditor.U2D { // SpriteAtlas Importer lets you modify [[SpriteAtlas]] - [HelpURL("https://docs.unity3d.com/2023.2/Documentation/Manual/SpriteAtlasV2.html")] + [HelpURL("https://docs.unity3d.com/6000.1/Documentation/Manual/sprite/atlas/v2/sprite-atlas-v2.html")] [NativeHeader("Editor/Src/2D/SpriteAtlas/SpriteAtlasImporter.h")] public sealed partial class SpriteAtlasImporter : AssetImporter { @@ -24,11 +24,41 @@ public sealed partial class SpriteAtlasImporter : AssetImporter extern public bool includeInBuild { get; set; } extern public SpriteAtlasPackingSettings packingSettings { get; set; } extern public SpriteAtlasTextureSettings textureSettings { get; set; } - extern public void SetPlatformSettings(TextureImporterPlatformSettings src); - extern public TextureImporterPlatformSettings GetPlatformSettings(string buildTarget); + + [NativeName("SetPlatformSettings")] + extern private void SetPlatformSettings_Internal(TextureImporterPlatformSettings src); + public void SetPlatformSettings(TextureImporterPlatformSettings src) + { + src.name = TextureImporter.GetTexturePlatformSerializationName(src.name); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". + SetPlatformSettings_Internal(src); + } + + [NativeName("GetPlatformSettings")] + extern private TextureImporterPlatformSettings GetPlatformSettings_Internal(string buildTarget); + public TextureImporterPlatformSettings GetPlatformSettings(string buildTarget) + { + buildTarget = TextureImporter.GetTexturePlatformSerializationName(buildTarget); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". + return GetPlatformSettings_Internal(buildTarget); + } + extern internal TextureFormat GetTextureFormat(BuildTarget target); - extern internal TextureImporterPlatformSettings GetSecondaryPlatformSettings(string buildTarget, string secondaryTextureName); - extern internal void SetSecondaryPlatformSettings(TextureImporterPlatformSettings src, string secondaryTextureName); + + [NativeName("GetSecondaryPlatformSettings")] + extern private TextureImporterPlatformSettings GetSecondaryPlatformSettings_Internal(string buildTarget, string secondaryTextureName); + internal TextureImporterPlatformSettings GetSecondaryPlatformSettings(string buildTarget, string secondaryTextureName) + { + buildTarget = TextureImporter.GetTexturePlatformSerializationName(buildTarget); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". + return GetSecondaryPlatformSettings_Internal(buildTarget, secondaryTextureName); + } + + [NativeName("SetSecondaryPlatformSettings")] + extern private void SetSecondaryPlatformSettings_Internal(TextureImporterPlatformSettings src, string secondaryTextureName); + internal void SetSecondaryPlatformSettings(TextureImporterPlatformSettings src, string secondaryTextureName) + { + src.name = TextureImporter.GetTexturePlatformSerializationName(src.name); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". + SetSecondaryPlatformSettings_Internal(src, secondaryTextureName); + } + extern internal bool GetSecondaryColorSpace(string secondaryTextureName); extern internal void SetSecondaryColorSpace(string secondaryTextureName, bool srGB); extern internal void DeleteSecondaryPlatformSettings(string secondaryTextureName); diff --git a/Editor/Mono/Animation/AnimationUtility.bindings.cs b/Editor/Mono/Animation/AnimationUtility.bindings.cs index 2300fb495c..28e6a2a34b 100644 --- a/Editor/Mono/Animation/AnimationUtility.bindings.cs +++ b/Editor/Mono/Animation/AnimationUtility.bindings.cs @@ -130,7 +130,12 @@ public static AnimationClip[] GetAnimationClips(GameObject gameObject) var extraClips = new List(); clipSources[i].GetAnimationClips(extraClips); - allClips.AddRange(extraClips); + allClips.Capacity = allClips.Count + extraClips.Count; + foreach (var clip in extraClips) + { + if (clip != null) + allClips.Add(clip); + } } return allClips.ToArray(); diff --git a/Editor/Mono/Animation/AnimationWindow/AnimationWindowHierarchy.cs b/Editor/Mono/Animation/AnimationWindow/AnimationWindowHierarchy.cs index 2147351f6a..634f44d4e2 100644 --- a/Editor/Mono/Animation/AnimationWindow/AnimationWindowHierarchy.cs +++ b/Editor/Mono/Animation/AnimationWindow/AnimationWindowHierarchy.cs @@ -44,6 +44,7 @@ internal class AnimationWindowHierarchy // Animation window shared state private AnimationWindowState m_State; private TreeViewController m_TreeView; + private AnimationWindowHierarchyGUI m_HierarchyGUI; public Vector2 GetContentSize() { @@ -65,6 +66,7 @@ public void OnGUI(Rect position) { m_TreeView.OnEvent(); m_TreeView.OnGUI(position, GUIUtility.GetControlID(FocusType.Keyboard)); + m_HierarchyGUI.ReclaimPendingFieldFocus(); } public void Init(EditorWindow owner, Rect rect) @@ -74,9 +76,10 @@ public void Init(EditorWindow owner, Rect rect) m_TreeView = new TreeViewController(owner, m_State.hierarchyState); m_State.hierarchyData = new AnimationWindowHierarchyDataSource(m_TreeView, m_State); + m_HierarchyGUI = new AnimationWindowHierarchyGUI(m_TreeView, m_State); m_TreeView.Init(rect, m_State.hierarchyData, - new AnimationWindowHierarchyGUI(m_TreeView, m_State), + m_HierarchyGUI, null ); diff --git a/Editor/Mono/Animation/AnimationWindow/AnimationWindowHierarchyGUI.cs b/Editor/Mono/Animation/AnimationWindow/AnimationWindowHierarchyGUI.cs index 1154cf213e..c525aec8b5 100644 --- a/Editor/Mono/Animation/AnimationWindow/AnimationWindowHierarchyGUI.cs +++ b/Editor/Mono/Animation/AnimationWindow/AnimationWindowHierarchyGUI.cs @@ -30,6 +30,9 @@ internal class AnimationWindowHierarchyGUI : TreeViewGUI private int[] m_HierarchyItemValueControlIDs; private int[] m_HierarchyItemButtonControlIDs; + private bool m_NeedsToReclaimFieldFocus; + private int m_FieldToReclaimFocus; + private const float k_RowRightOffset = 10; private const float k_ValueFieldDragWidth = 15; private const float k_ValueFieldWidth = 80; @@ -352,11 +355,12 @@ private void DoValueField(Rect rect, AnimationWindowHierarchyNode node, int row) && Event.current.type == EventType.KeyDown && (Event.current.character == '\n' || (int)Event.current.character == 3)); - // Force back keyboard focus to float field editor when editing it. - // TreeView forces keyboard focus on itself at mouse down and we lose focus here. + // Force back keyboard focus to float field editor when editing it since the TreeView forces keyboard focus on itself at mouse down. + // The focus will be reclaimed after the TreeViewController.OnGUI call. if (EditorGUI.s_RecycledEditor.controlID == id && Event.current.type == EventType.MouseDown && valueFieldRect.Contains(Event.current.mousePosition)) { - GUIUtility.keyboardControl = id; + m_NeedsToReclaimFieldFocus = true; + m_FieldToReclaimFocus = id; } if (curve.isDiscreteCurve) @@ -421,6 +425,15 @@ private void DoValueField(Rect rect, AnimationWindowHierarchyNode node, int row) } } + internal void ReclaimPendingFieldFocus() + { + if (m_NeedsToReclaimFieldFocus) + { + GUIUtility.keyboardControl = m_FieldToReclaimFocus; + m_NeedsToReclaimFieldFocus = false; + } + } + private bool DoTreeViewButton(int id, Rect position, GUIContent content, GUIStyle style) { Event evt = Event.current; diff --git a/Editor/Mono/Animation/AnimationWindow/CurveEditorWindow.cs b/Editor/Mono/Animation/AnimationWindow/CurveEditorWindow.cs index 86e23e41ff..afc8c86d47 100644 --- a/Editor/Mono/Animation/AnimationWindow/CurveEditorWindow.cs +++ b/Editor/Mono/Animation/AnimationWindow/CurveEditorWindow.cs @@ -30,12 +30,13 @@ public enum NormalizationMode } //const int kToolbarHeight = 17; - const int kPresetsHeight = 46; + const int kPresetsHeight = 50; static CurveEditorWindow s_SharedCurveEditor; internal CurveEditor m_CurveEditor; + Vector2 m_PresetScrollPosition; AnimationCurve m_Curve; Color m_Color; @@ -469,20 +470,38 @@ void OnGUI() m_CurveEditor.OnGUI(); // Preset swatch area - GUI.Box(new Rect(0, position.height - kPresetsHeight, position.width, kPresetsHeight), "", ms_Styles.curveSwatchArea); + var presetRect = new Rect(0, position.height - kPresetsHeight, position.width, kPresetsHeight); + GUI.Box(presetRect, "", ms_Styles.curveSwatchArea); + Color curveColor = m_Color; curveColor.a *= 0.6f; - const float margin = 45f; const float width = 40f; const float height = 25f; - float yPos = position.height - kPresetsHeight + (kPresetsHeight - height) * 0.5f; + const float spaceBetweenSwatches = 5f; + const float presetDropdownSize = 16f; + const float horizontalScrollbarHeight = 15f; + const float presetDropdownCenteringOffset = 2f; + float yPos = (kPresetsHeight - height) * 0.5f; InitCurvePresets(); CurvePresetLibrary curveLibrary = m_CurvePresets.GetPresetLibraryEditor().GetCurrentLib(); if (curveLibrary != null) { - for (int i = 0; i < curveLibrary.Count(); i++) + var numPresets = curveLibrary.Count(); + var presetDropDownRect = new Rect(spaceBetweenSwatches, yPos + presetDropdownCenteringOffset, presetDropdownSize, presetDropdownSize); + Rect contentRect = new Rect(0, 0, numPresets * (width + spaceBetweenSwatches) + presetDropDownRect.xMax, presetRect.height - horizontalScrollbarHeight); + m_PresetScrollPosition = GUI.BeginScrollView( + presetRect, // Rectangle of the visible area + m_PresetScrollPosition, // Current scroll position + contentRect, // Rectangle containing all content + false, // Always show horizontal scrollbar + false // Always show vertical scrollbar + ); + + PresetDropDown(presetDropDownRect); + + Rect swatchRect = new Rect(presetDropDownRect.xMax + spaceBetweenSwatches, yPos, width, height); + for (int i = 0; i < numPresets; i++) { - Rect swatchRect = new Rect(margin + (width + 5f) * i, yPos, width, height); m_GUIContent.tooltip = curveLibrary.GetName(i); if (GUI.Button(swatchRect, m_GUIContent, ms_Styles.curveSwatch)) { @@ -496,14 +515,11 @@ void OnGUI() if (Event.current.type == EventType.Repaint) curveLibrary.Draw(swatchRect, i); - if (swatchRect.xMax > position.width - 2 * margin) - break; + swatchRect.x += width + spaceBetweenSwatches; } + GUI.EndScrollView(); } - Rect presetDropDownButtonRect = new Rect(margin - 20f, yPos + 5f, 20, 20); - PresetDropDown(presetDropDownButtonRect); - // For adding default preset curves //if (EditorGUI.DropdownButton(new Rect (position.width -26, yPos, 20, 20), GUIContent.none, FocusType.Passive, "OL Plus")) // AddDefaultPresetsToCurrentLib (); diff --git a/Editor/Mono/Animation/StateMachine.cs b/Editor/Mono/Animation/StateMachine.cs index f438f78a37..7a0a5c021d 100644 --- a/Editor/Mono/Animation/StateMachine.cs +++ b/Editor/Mono/Animation/StateMachine.cs @@ -100,11 +100,12 @@ public void RemoveCondition(AnimatorCondition condition) } } - + [HelpURL("StateMachineTransitions")] internal class AnimatorDefaultTransition : ScriptableObject { } + [HelpURL("class-State")] public partial class AnimatorState : Object { private PushUndoIfNeeded undoHandler = new PushUndoIfNeeded(true); @@ -242,6 +243,7 @@ public int uniqueNameHash } } + [HelpURL("NestedStateMachines")] public partial class AnimatorStateMachine : Object { private PushUndoIfNeeded undoHandler = new PushUndoIfNeeded(true); diff --git a/Editor/Mono/Annotation/AnnotationWindow.cs b/Editor/Mono/Annotation/AnnotationWindow.cs index 7e544715ec..8442759474 100644 --- a/Editor/Mono/Annotation/AnnotationWindow.cs +++ b/Editor/Mono/Annotation/AnnotationWindow.cs @@ -56,7 +56,6 @@ private enum EnabledState const float k_WindowWidth = 270; const float k_ScrollBarWidth = 14; const float k_ListElementHeight = 18; - const float k_JustClosedPeriod = 200; const float k_FrameWidth = 1f; float iconSize = 16; float gizmoRightAlign; @@ -66,6 +65,8 @@ private enum EnabledState static AnnotationWindow s_AnnotationWindow = null; static long s_LastClosedTime; + const long k_JustClosedPeriod = 400; + static Styles m_Styles; List m_RecentAnnotations; List m_BuiltinAnnotations; @@ -229,6 +230,7 @@ private void IconHasChanged() } } } + SyncToState(); Repaint(); } diff --git a/Editor/Mono/Annotation/LayerVisibilityWindow.cs b/Editor/Mono/Annotation/LayerVisibilityWindow.cs index 0fe1e241aa..177ad6e7cd 100644 --- a/Editor/Mono/Annotation/LayerVisibilityWindow.cs +++ b/Editor/Mono/Annotation/LayerVisibilityWindow.cs @@ -39,14 +39,14 @@ public Styles() const float k_FrameWidth = 1f; const float k_ToggleSize = 17; const float k_SeparatorHeight = 6; - const float k_JustClosedPeriod = 200; const string k_LayerVisible = "Show/Hide Layer"; const string k_LayerPickable = "Toggle Pickable status this Layer. Non-Pickable items cannot be selected in the Scene View."; private static LayerVisibilityWindow s_LayerVisibilityWindow; private static long s_LastClosedTime; - private static Styles s_Styles; + const long k_JustClosedPeriod = 400; + private static Styles s_Styles; private List s_LayerNames; private List s_LayerMasks; private int m_AllLayersMask; diff --git a/Editor/Mono/Annotation/SceneRenderModeWindow.cs b/Editor/Mono/Annotation/SceneRenderModeWindow.cs index 38cb99b119..58674ab1c4 100644 --- a/Editor/Mono/Annotation/SceneRenderModeWindow.cs +++ b/Editor/Mono/Annotation/SceneRenderModeWindow.cs @@ -146,6 +146,18 @@ static class Styles } + // UUM-96180: Default CameraMode should be DrawCameraMode.GIContributorsReceivers + internal static SceneView.CameraMode defaultCameraMode + { + get + { + var mode = Styles.sBuiltinCameraModes [3]; + if(mode.drawMode != DrawCameraMode.GIContributorsReceivers) + Debug.LogError("Default Draw Camera mode should be set to DrawCameraMode.GIContributorsReceivers."); + return mode; + } + } + private Dictionary foldoutStates = new Dictionary(); private float windowHeight diff --git a/Editor/Mono/AssemblyInfo/AssemblyInfo.cs b/Editor/Mono/AssemblyInfo/AssemblyInfo.cs index 5bfbee96d4..01d5dc58fc 100644 --- a/Editor/Mono/AssemblyInfo/AssemblyInfo.cs +++ b/Editor/Mono/AssemblyInfo/AssemblyInfo.cs @@ -144,7 +144,7 @@ [assembly: InternalsVisibleTo("Unity.XR.Remoting.Editor")] [assembly: InternalsVisibleTo("UnityEngine.Common")] [assembly: InternalsVisibleTo("Unity.UI.Builder.Editor")] -[assembly: InternalsVisibleTo("UnityEditor.UIElements.Tests.Tests")] // for UI Test Framework +[assembly: InternalsVisibleTo("Unity.UI.TestFramework.Editor.Tests")] // for UI Test Framework [assembly: InternalsVisibleTo("UnityEditor.UIBuilderModule")] [assembly: InternalsVisibleTo("Unity.UI.Builder.EditorTests")] [assembly: InternalsVisibleTo("Unity.GraphViewTestUtilities.Editor")] diff --git a/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9Importer.cs b/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9Importer.cs index a38271eb6b..eec1335d57 100644 --- a/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9Importer.cs +++ b/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9Importer.cs @@ -317,8 +317,8 @@ static ulong ComputeDefaultShaderHash() static void DirtyCustomDependencies() { - DateTime now = DateTime.Now; - if (Application.isPlaying || now - s_LastCheck < k_CheckDependencyFrequency) + DateTime now = DateTime.UtcNow; + if (Application.isPlaying || ((now - s_LastCheck) < k_CheckDependencyFrequency)) { return; } diff --git a/Editor/Mono/AssetPipeline/TextureImporter.bindings.cs b/Editor/Mono/AssetPipeline/TextureImporter.bindings.cs index 0a377b32a6..e304c3437b 100644 --- a/Editor/Mono/AssetPipeline/TextureImporter.bindings.cs +++ b/Editor/Mono/AssetPipeline/TextureImporter.bindings.cs @@ -17,11 +17,12 @@ namespace UnityEditor [NativeHeader("Editor/Src/AssetPipeline/TextureImporting/TextureImporter.h")] [NativeHeader("Editor/Src/AssetPipeline/TextureImporting/TextureImporter.deprecated.h")] [NativeHeader("Editor/Src/AssetPipeline/TextureImporting/TextureImporterUtils.h")] + [NativeHeader("Editor/Src/AssetPipeline/TextureImporting/TextureImporterPlatformSettingsUtils.h")] [NativeHeader("Editor/Src/EditorUserBuildSettings.h")] public sealed partial class TextureImporter : AssetImporter { [FreeFunction] - internal static extern string GetFixedPlatformName(string platform); + internal static extern string GetTexturePlatformSerializationName(string platformName); [Obsolete("textureFormat is no longer accessible at the TextureImporter level. For old 'simple' formats use the textureCompression property for the equivalent automatic choice (Uncompressed for TrueColor, Compressed and HQCommpressed for 16 bits). For platform specific formats use the [[PlatformTextureSettings]] API. Using this setter will setup various parameters to match the new automatic system as well as possible. Getter will return the last value set.")] public extern TextureImporterFormat textureFormat @@ -103,8 +104,7 @@ public bool GetPlatformTextureSettings(string platform, out int maxTextureSize, // public API will always return a valid TextureImporterPlatformSettings, creating it based on the default one if it did not exist. public TextureImporterPlatformSettings GetPlatformTextureSettings(string platform) { - // make sure we are converting the settings to use the proper BuildTarget name to get them (the way it works on other importers) - platform = GetFixedPlatformName(platform); + platform = GetTexturePlatformSerializationName(platform); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". TextureImporterPlatformSettings dest = GetPlatformTextureSetting_Internal(platform); if (platform != dest.name) @@ -122,27 +122,24 @@ public TextureImporterPlatformSettings GetDefaultPlatformTextureSettings() public TextureImporterFormat GetAutomaticFormat(string platform) { - platform = GetFixedPlatformName(platform); + platform = GetTexturePlatformSerializationName(platform); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". TextureImporterSettings settings = new TextureImporterSettings(); ReadTextureSettings(settings); TextureImporterPlatformSettings platformSettings = GetPlatformTextureSettings(platform); - List validPlatforms = BuildPlatforms.instance.GetValidPlatforms(); - foreach (BuildPlatform bp in validPlatforms) + BuildTarget buildTarget = BuildPipeline.GetBuildTargetByName(platform); + if (buildTarget != BuildTarget.NoTarget) { - if (bp.name == platform) - { - return DefaultFormatFromTextureParameters(settings, - !platformSettings.overridden ? GetDefaultPlatformTextureSettings() : platformSettings, - DoesSourceTextureHaveAlpha(), - IsSourceTextureHDR(), - bp.defaultTarget); - - // Regarding the "GetDefaultPlatformTextureSettings" call: in case 1281084, we made it so that platform settings stop automatically - // resetting to the default platform's settings when the platform override is disabled. This introduced a regression where - // "GetAutomaticFormat" would not return the actual format used by platforms with a disabled override, (as in, the one indicated in - // the default platform's settings) which is why we pass in the default platform's settings instead. - } + return DefaultFormatFromTextureParameters(settings, + !platformSettings.overridden ? GetDefaultPlatformTextureSettings() : platformSettings, + DoesSourceTextureHaveAlpha(), + IsSourceTextureHDR(), + buildTarget); + + // Regarding the "GetDefaultPlatformTextureSettings" call: in case 1281084, we made it so that platform settings stop automatically + // resetting to the default platform's settings when the platform override is disabled. This introduced a regression where + // "GetAutomaticFormat" would not return the actual format used by platforms with a disabled override, (as in, the one indicated in + // the default platform's settings) which is why we pass in the default platform's settings instead. } return TextureImporterFormat.Automatic; @@ -183,8 +180,7 @@ public void SetPlatformTextureSettings(string platform, int maxTextureSize, Text // Set specific target platform settings public void SetPlatformTextureSettings(TextureImporterPlatformSettings platformSettings) { - // we need to fix the name in case the user changed it to some mismatching value - platformSettings.name = GetFixedPlatformName(platformSettings.name); + platformSettings.name = GetTexturePlatformSerializationName(platformSettings.name); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". SetPlatformTextureSettings_Internal(platformSettings); } @@ -194,7 +190,8 @@ public void SetPlatformTextureSettings(TextureImporterPlatformSettings platformS public void ClearPlatformTextureSettings(string platform) { - ClearPlatformTextureSettings_Internal(GetFixedPlatformName(platform)); + platform = GetTexturePlatformSerializationName(platform); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". + ClearPlatformTextureSettings_Internal(platform); } [FreeFunction] diff --git a/Editor/Mono/Audio/AudioContainerWindow.cs b/Editor/Mono/Audio/AudioContainerWindow.cs index 55e043b889..03355b2a14 100644 --- a/Editor/Mono/Audio/AudioContainerWindow.cs +++ b/Editor/Mono/Audio/AudioContainerWindow.cs @@ -119,6 +119,7 @@ internal static void CreateAudioRandomContainerWindow() window.Show(); } + // Used by tests. internal bool IsInitializedForTargetDisplay() { return m_ContainerElementsInitialized && m_IsSubscribedToGUICallbacksAndEvents; @@ -168,12 +169,12 @@ void Update() if (!m_IsVisible) return; - if (State.IsPlayingOrPaused()) { UpdateClipFieldProgressBars(); } + if (State.IsPreviewPlayingOrPaused()) { UpdateClipFieldProgressBars(); } else if (!m_ClipFieldProgressBarsAreCleared) { ClearClipFieldProgressBars(); } if (m_Meter != null) { - if (State.IsPlayingOrPaused()) + if (State.IsPreviewPlayingOrPaused()) { if (State != null) { m_Meter.Value = State.GetMeterValue(); } else { m_Meter.Value = -80.0f; } @@ -410,20 +411,20 @@ void BindAndTrackPreviewProperties() void OnPlayStopButtonClicked() { - if (State.IsPlayingOrPaused()) + if (State.IsPreviewPlayingOrPaused()) { - State.Stop(); + State.StopPreview(); ClearClipFieldProgressBars(); } else - State.Play(); + State.PlayPreview(); UpdateTransportButtonStates(); } void OnSkipButtonClicked() { - if (State.IsPlayingOrPaused()) + if (State.IsPreviewPlayingOrPaused()) State.Skip(); } @@ -431,9 +432,9 @@ void UpdateTransportButtonStates() { var editorIsPaused = EditorApplication.isPaused; - m_PlayStopButton?.SetEnabled(State.IsReadyToPlay() && !editorIsPaused && !EditorUtility.audioMasterMute); - m_SkipButton?.SetEnabled(State.IsPlayingOrPaused() && State.AudioContainer.triggerMode == AudioRandomContainerTriggerMode.Automatic && !editorIsPaused && !EditorUtility.audioMasterMute); - m_PlayStopButtonImage.style.backgroundImage = State.IsPlayingOrPaused() ? GetIconTexture(Icons.Stop) : GetIconTexture(Icons.Play); + m_PlayStopButton?.SetEnabled(State.IsReadyToPlayPreview() && !editorIsPaused && !EditorUtility.audioMasterMute); + m_SkipButton?.SetEnabled(State.IsPreviewPlayingOrPaused() && State.AudioContainer.triggerMode == AudioRandomContainerTriggerMode.Automatic && !editorIsPaused && !EditorUtility.audioMasterMute); + m_PlayStopButtonImage.style.backgroundImage = State.IsPreviewPlayingOrPaused() ? GetIconTexture(Icons.Stop) : GetIconTexture(Icons.Play); } Texture2D GetIconTexture(Icons icon) @@ -700,10 +701,7 @@ void SubscribeToClipListCallbacksAndEvents() m_ClipsListView.itemIndexChanged += OnItemListIndexChanged; m_ClipsListView.makeItem = OnMakeListItem; m_ClipsListView.bindItem = OnBindListItem; - - // We need a no-op unbind callback to prevent the default implementation from being run. - // See the comments in UUM-46918. - m_ClipsListView.unbindItem = (elm, idx) => { }; + m_ClipsListView.unbindItem = OnUnbindListItem; m_DragManipulator.addAudioClipsDelegate += OnAudioClipDrag; } @@ -792,6 +790,24 @@ void OnBindListItem(VisualElement element, int index) enabledToggle.TrackPropertyValue(enabledProperty, OnElementEnabledToggleChanged); audioClipField.TrackPropertyValue(audioClipProperty, OnElementAudioClipChanged); volumeField.TrackPropertyValue(volumeProperty, OnElementPropertyChanged); + + enabledToggle.RegisterValueChangedCallback(OnElementEnabledToggleChanged); + audioClipField.RegisterValueChangedCallback(OnElementAudioClipChanged); + } + + void OnUnbindListItem(VisualElement element, int index) + { + var enabledToggle = UIToolkitUtilities.GetChildByName(element, "enabled-toggle"); + var audioClipField = UIToolkitUtilities.GetChildByName(element, "audio-clip-field"); + var volumeField = UIToolkitUtilities.GetChildByName(element, "volume-field"); + + enabledToggle.UnregisterValueChangedCallback(OnElementEnabledToggleChanged); + audioClipField.UnregisterValueChangedCallback(OnElementAudioClipChanged); + audioClipField.UnregisterCallback(OnListDragPerform); + + enabledToggle.Unbind(); + audioClipField.Unbind(); + volumeField.Unbind(); } static void OnListDragPerform(DragPerformEvent evt) @@ -799,19 +815,15 @@ static void OnListDragPerform(DragPerformEvent evt) evt.StopPropagation(); } - void OnElementAudioClipChanged(SerializedProperty property) + void OnElementEnabledToggleChanged(ChangeEvent evt) { - var element = property.serializedObject.targetObject as AudioContainerElement; - Assert.IsNotNull(element); - var clip = property.objectReferenceValue as AudioClip; - UpdateListElementName(element, clip); - OnElementPropertyChanged(property); - UpdateTransportButtonStates(); - State.AudioContainer.NotifyObservers(AudioRandomContainer.ChangeEventType.List); + State.OnAudioClipListChanged(); } void OnElementEnabledToggleChanged(SerializedProperty property) { + State.OnAudioClipListChanged(); + OnElementPropertyChanged(property); UpdateTransportButtonStates(); @@ -820,9 +832,23 @@ void OnElementEnabledToggleChanged(SerializedProperty property) var last = State.AudioContainer.avoidRepeatingLast; State.AudioContainer.avoidRepeatingLast = -1; State.AudioContainer.avoidRepeatingLast = last; + } - if (State.IsPlayingOrPaused()) - State.AudioContainer.NotifyObservers(AudioRandomContainer.ChangeEventType.List); + void OnElementAudioClipChanged(ChangeEvent evt) + { + State.OnAudioClipListChanged(); + } + + void OnElementAudioClipChanged(SerializedProperty property) + { + State.OnAudioClipListChanged(); + + var element = property.serializedObject.targetObject as AudioContainerElement; + Assert.IsNotNull(element); + var clip = property.objectReferenceValue as AudioClip; + UpdateListElementName(element, clip); + OnElementPropertyChanged(property); + UpdateTransportButtonStates(); } void OnElementPropertyChanged(SerializedProperty property) @@ -833,6 +859,8 @@ void OnElementPropertyChanged(SerializedProperty property) void OnListItemsAdded(IEnumerable indices) { + State.OnAudioClipListChanged(); + var indicesArray = indices as int[] ?? indices.ToArray(); const string undoName = $"Add {nameof(AudioRandomContainer)} element"; var groupUndoName = undoName; @@ -868,6 +896,8 @@ void OnListItemsAdded(IEnumerable indices) void OnListItemsRemoved(IEnumerable indices) { + State.OnAudioClipListChanged(); + var indicesArray = indices as int[] ?? indices.ToArray(); // Confusingly, this callback is sometimes invoked post-delete and sometimes pre-delete, @@ -889,17 +919,18 @@ void OnListItemsRemoved(IEnumerable indices) } Undo.SetCurrentGroupName(undoName); - State.AudioContainer.NotifyObservers(AudioRandomContainer.ChangeEventType.List); } void OnItemListIndexChanged(int oldIndex, int newIndex) { + State.OnAudioClipListChanged(); Undo.SetCurrentGroupName($"Reorder {nameof(AudioRandomContainer)} list"); - State.AudioContainer.NotifyObservers(AudioRandomContainer.ChangeEventType.List); } void OnAudioClipDrag(List audioClips) { + State.OnAudioClipListChanged(); + const string undoName = $"Add {nameof(AudioRandomContainer)} element"; var groupUndoName = undoName; @@ -934,8 +965,11 @@ void OnAudioClipDrag(List audioClips) } } + // NOTE: this function is also the entry point for handling undo-redo of clip list changes. void OnAudioClipListChanged(SerializedProperty property) { + State.OnAudioClipListChanged(); + // Do manual fixup of orphaned subassets after a possible undo of item removal // because the undo system does not play nice with RegisterCreatedObjectUndo. if (m_CachedElements.Count < State.AudioContainer.elements.Length) @@ -965,11 +999,7 @@ void OnAudioClipListChanged(SerializedProperty property) // Force a list rebuild when the list has changed or it will not always render correctly m_ClipsListView.Rebuild(); - // This function is the first entry-point in `AudioContainerWindow` after an undo-event that alters the - // audio clip list has been triggered. And, whenever the list is altered, we need to make sure the state is stopped. - State.Stop(); ClearClipFieldProgressBars(); - UpdateTransportButtonStates(); SetTitle(); } @@ -1182,7 +1212,7 @@ void ShowTooltip(TooltipEvent evt) } else { - var mode = State.IsPlayingOrPaused() ? "Stop" : "Play"; + var mode = State.IsPreviewPlayingOrPaused() ? "Stop" : "Play"; var shortcut = ShortcutManager.instance.GetShortcutBinding("Audio/Play-stop Audio Random Container"); if (shortcut.Equals(ShortcutBinding.empty)) @@ -1315,9 +1345,9 @@ void OnCountRandomizationButtonClicked() void OnAudioMasterMuteChanged(bool isMuted) { - if (isMuted && State.IsPlayingOrPaused()) + if (isMuted && State.IsPreviewPlayingOrPaused()) { - State.Stop(); + State.StopPreview(); ClearClipFieldProgressBars(); } diff --git a/Editor/Mono/Audio/AudioContainerWindowState.cs b/Editor/Mono/Audio/AudioContainerWindowState.cs index df5c740271..aa9d29c7b9 100644 --- a/Editor/Mono/Audio/AudioContainerWindowState.cs +++ b/Editor/Mono/Audio/AudioContainerWindowState.cs @@ -13,6 +13,9 @@ namespace UnityEditor; sealed class AudioContainerWindowState { + // Used by tests. + internal const string previewAudioSourceName = "PreviewAudioSource595651"; + AudioRandomContainer m_AudioContainer; AudioSource m_PreviewAudioSource; SerializedObject m_SerializedObject; @@ -21,7 +24,7 @@ sealed class AudioContainerWindowState // Need this flag to track transport state changes immediately, as there could be a // one-frame delay to get the correct value from AudioSource.isContainerPlaying. - bool m_IsPlayingOrPausedLocalFlag; + bool m_IsPreviewPlayingOrPausedLocalFlag; bool m_IsSuspended; internal event EventHandler TargetChanged; @@ -73,10 +76,10 @@ internal void UpdateTargetPath() internal void Reset() { - Stop(); + StopPreview(); m_AudioContainer = null; m_SerializedObject = null; - m_IsPlayingOrPausedLocalFlag = false; + m_IsPreviewPlayingOrPausedLocalFlag = false; UpdateTargetPath(); } @@ -88,20 +91,20 @@ internal VisualElement GetResourceTrackerElement() internal void OnDestroy() { - Stop(); + StopPreview(); if (m_PreviewAudioSource != null) Object.DestroyImmediate(m_PreviewAudioSource.gameObject); EditorApplication.playModeStateChanged -= OnEditorPlayModeStateChanged; - Selection.selectionChanged -= OnSelectionChanged; EditorApplication.pauseStateChanged -= OnEditorPauseStateChanged; + Selection.selectionChanged -= OnSelectionChanged; } internal void Suspend() { m_IsSuspended = true; - Stop(); + StopPreview(); if (m_PreviewAudioSource != null) Object.DestroyImmediate(m_PreviewAudioSource.gameObject); @@ -225,9 +228,9 @@ void UpdateResourceTrackerElement() } } - internal void Play() + internal void PlayPreview() { - var canNotPlay = m_IsSuspended || IsPlayingOrPaused() || !IsReadyToPlay(); + var canNotPlay = m_IsSuspended || IsPreviewPlayingOrPaused() || !IsReadyToPlayPreview(); if (canNotPlay) return; @@ -239,7 +242,7 @@ internal void Play() // This means that this object is a hidden part of the user's scene during play/pause. var gameObject = new GameObject { - name = "PreviewAudioSource595651", + name = previewAudioSourceName, hideFlags = HideFlags.HideInHierarchy | HideFlags.DontSaveInEditor | HideFlags.DontSaveInBuild }; @@ -249,28 +252,36 @@ internal void Play() m_PreviewAudioSource.resource = m_AudioContainer; m_PreviewAudioSource.Play(); - m_IsPlayingOrPausedLocalFlag = true; + m_IsPreviewPlayingOrPausedLocalFlag = true; TransportStateChanged?.Invoke(this, EventArgs.Empty); EditorApplication.update += OnEditorApplicationUpdate; } - internal void Stop() + internal void StopPreview() { - var canNotStop = m_IsSuspended || !IsPlayingOrPaused(); + var canNotStop = m_IsSuspended || !IsPreviewPlayingOrPaused(); if (canNotStop) return; m_PreviewAudioSource.Stop(); m_PreviewAudioSource.resource = null; - m_IsPlayingOrPausedLocalFlag = false; + m_IsPreviewPlayingOrPausedLocalFlag = false; TransportStateChanged?.Invoke(this, EventArgs.Empty); EditorApplication.update -= OnEditorApplicationUpdate; } + internal void OnAudioClipListChanged() + { + // We don't support live updates that affect the clip scheduling, + // so stop the preview and any other audio sources that reference this asset. + AudioContainer.NotifyObservers(AudioRandomContainer.ChangeEventType.List); + StopPreview(); + } + internal void Skip() { - var canNotSkip = m_IsSuspended || !IsPlayingOrPaused(); + var canNotSkip = m_IsSuspended || !IsPreviewPlayingOrPaused(); if (canNotSkip) return; @@ -278,16 +289,16 @@ internal void Skip() m_PreviewAudioSource.SkipToNextElementIfHasContainer(); } - internal bool IsPlayingOrPaused() + internal bool IsPreviewPlayingOrPaused() { - return m_IsPlayingOrPausedLocalFlag || (m_PreviewAudioSource != null && m_PreviewAudioSource.isContainerPlaying); + return m_IsPreviewPlayingOrPausedLocalFlag || (m_PreviewAudioSource != null && m_PreviewAudioSource.isContainerPlaying); } /// /// Checks if the window has a current target with at least one enabled audio clip assigned. /// /// Whether or not there are valid audio clips to play - internal bool IsReadyToPlay() + internal bool IsReadyToPlayPreview() { if (m_AudioContainer == null) return false; @@ -303,7 +314,7 @@ internal bool IsReadyToPlay() internal ActivePlayable[] GetActivePlayables() { - return IsPlayingOrPaused() ? m_PreviewAudioSource.containerActivePlayables : null; + return IsPreviewPlayingOrPaused() ? m_PreviewAudioSource.containerActivePlayables : null; } internal float GetMeterValue() @@ -321,7 +332,7 @@ void OnEditorApplicationUpdate() if (m_PreviewAudioSource != null && m_PreviewAudioSource.isContainerPlaying) return; - m_IsPlayingOrPausedLocalFlag = false; + m_IsPreviewPlayingOrPausedLocalFlag = false; TransportStateChanged?.Invoke(this, EventArgs.Empty); EditorApplication.update -= OnEditorApplicationUpdate; } @@ -330,7 +341,7 @@ void OnEditorPlayModeStateChanged(PlayModeStateChange state) { if (state is PlayModeStateChange.ExitingEditMode or PlayModeStateChange.ExitingPlayMode) { - Stop(); + StopPreview(); if (m_PreviewAudioSource != null) { diff --git a/Editor/Mono/BaseBuildTarget.cs b/Editor/Mono/BaseBuildTarget.cs index 98fc1b3fb0..60372109e2 100644 --- a/Editor/Mono/BaseBuildTarget.cs +++ b/Editor/Mono/BaseBuildTarget.cs @@ -29,6 +29,7 @@ internal abstract class BaseBuildTarget : IBuildTarget public virtual IUIPlatformProperties UIPlatformProperties => Properties as IUIPlatformProperties; public virtual IAudioPlatformProperties AudioPlatformProperties => Properties as IAudioPlatformProperties; public virtual IVRPlatformProperties VRPlatformProperties => Properties as IVRPlatformProperties; + public virtual ISubtargetPlatformProperties TextureSubtargetPlatformProperties => Properties as ISubtargetPlatformProperties; protected virtual IPlatformProperties Properties => null; diff --git a/Editor/Mono/BuildPipeline.bindings.cs b/Editor/Mono/BuildPipeline.bindings.cs index a114434beb..c0dbf43f76 100644 --- a/Editor/Mono/BuildPipeline.bindings.cs +++ b/Editor/Mono/BuildPipeline.bindings.cs @@ -190,7 +190,10 @@ public enum BuildAssetBundleOptions UseContentHash = 65536, // 1 << 16 // Use when AssetBundle dependencies need to be calculated recursively, such as when you have a dependency chain of matching typed Scriptable Objects - RecurseDependencies = 131072 // 1 << 17 + RecurseDependencies = 131072, // 1 << 17 + + // Sprites are normally copied to all bundles that reference them. This flag prevents that behavior if the sprite is not in an atlas. + StripUnatlasedSpriteCopies = 262144 // 1 << 18 } // Keep in sync with CanAppendBuild in EditorUtility.h @@ -700,6 +703,9 @@ public static string GetPlaybackEngineDirectory(BuildTargetGroup buildTargetGrou [FreeFunction(IsThreadSafe = true)] public static extern string GetPlaybackEngineDirectory(BuildTargetGroup buildTargetGroup, BuildTarget target, BuildOptions options, bool assertUnsupportedPlatforms); + [FreeFunction] + internal static extern bool IsServerBuildPlatformSupported(BuildTarget target); + internal static string GetBuildToolsDirectory(BuildTarget target) { return Path.Combine(GetPlaybackEngineDirectory(target, BuildOptions.None, false), "Tools"); diff --git a/Editor/Mono/BuildPlayerWindow.cs b/Editor/Mono/BuildPlayerWindow.cs index 8fa6f04d80..96e7e90327 100644 --- a/Editor/Mono/BuildPlayerWindow.cs +++ b/Editor/Mono/BuildPlayerWindow.cs @@ -491,52 +491,9 @@ void OnGUI() { styles = new Styles(); } - - if (!UnityConnect.instance.canBuildWithUPID) - { - ShowAlert(); - } - GUILayout.Space(5); - GUILayout.BeginHorizontal(); - GUILayout.Space(10); - GUILayout.BeginVertical(); - - string message = ""; - var buildSettingsLocked = !AssetDatabase.IsOpenForEdit(kEditorBuildSettingsPath, out message, StatusQueryOptions.UseCachedIfPossible); - - using (new EditorGUI.DisabledScope(buildSettingsLocked)) - { - ActiveScenesGUI(); - // Clear all and Add Current Scene - GUILayout.BeginHorizontal(); - if (buildSettingsLocked) - { - GUI.enabled = true; - if (GUILayout.Button(styles.checkOut)) - AssetDatabase.MakeEditable(kEditorBuildSettingsPath); - GUILayout.Label(message); - GUI.enabled = false; - } - GUILayout.FlexibleSpace(); - if (GUILayout.Button(styles.addOpenSource)) - AddOpenScenes(); - GUILayout.EndHorizontal(); - } - - GUILayout.Space(10); - - GUILayout.BeginHorizontal(GUILayout.Height(400)); - ActiveBuildTargetsGUI(); - GUILayout.Space(10); - GUILayout.BeginVertical(); - ShowBuildTargetSettings(); - GUILayout.EndVertical(); - GUILayout.EndHorizontal(); - - GUILayout.Space(10); - GUILayout.EndVertical(); - GUILayout.Space(10); - GUILayout.EndHorizontal(); + // Override the 'old' build settings window in favor of the new build profile window. + BuildPipeline.ShowBuildProfileWindow(); + Close(); } static bool IsAnyStandaloneModuleLoaded() diff --git a/Editor/Mono/BuildProfile/BuildProfile.cs b/Editor/Mono/BuildProfile/BuildProfile.cs index 78c49d6f6e..591568d85c 100644 --- a/Editor/Mono/BuildProfile/BuildProfile.cs +++ b/Editor/Mono/BuildProfile/BuildProfile.cs @@ -403,6 +403,13 @@ void ValidateDataConsistency() } CheckSceneListConsistency(); + + // On disk changes to active profile may change platform guid. + // Specifically copying the entire YAML of a valid build profile. + if (this == BuildProfileContext.activeProfile && platformGuid != EditorUserBuildSettings.activePlatformGuid) + { + EditorUserBuildSettings.SwitchActiveBuildTargetGuid(this); + } } /// diff --git a/Editor/Mono/BuildProfile/BuildProfileAPI.cs b/Editor/Mono/BuildProfile/BuildProfileAPI.cs index 92f21c04d1..4146238d8f 100644 --- a/Editor/Mono/BuildProfile/BuildProfileAPI.cs +++ b/Editor/Mono/BuildProfile/BuildProfileAPI.cs @@ -33,5 +33,41 @@ public static void SetActiveBuildProfile(BuildProfile buildProfile) BuildProfileModuleUtil.SwitchLegacyActiveFromBuildProfile(buildProfile); } + + /// + /// Gets a component of type T associated with the build profile, its global fallback, + /// or null if the component is not available. + /// + public T GetComponent() where T : class + { + if (typeof(T) == typeof(PlayerSettings)) + { + if (m_PlayerSettings != null) + return m_PlayerSettings as T; + return s_GlobalPlayerSettings as T; + } + + return null; + } + + /// + /// Gets a component of type T associated with the currently active build profile, + /// its global fallback, or null if the component is not available. + /// + public static T GetActiveComponent() where T : class + { + var buildProfile = GetActiveBuildProfile(); + if (buildProfile == null) + { + if (typeof(T) == typeof(PlayerSettings)) + return s_GlobalPlayerSettings as T; + } + else + { + return buildProfile.GetComponent(); + } + + return null; + } } } diff --git a/Editor/Mono/BuildProfile/BuildProfileContext.cs b/Editor/Mono/BuildProfile/BuildProfileContext.cs index 62fa3fcafd..e329b0d0de 100644 --- a/Editor/Mono/BuildProfile/BuildProfileContext.cs +++ b/Editor/Mono/BuildProfile/BuildProfileContext.cs @@ -66,12 +66,7 @@ internal static BuildProfile activeProfile { get { - // Active Build profile may be deleted from the project. - var activeProfile = EditorUserBuildSettings.activeBuildProfile; - if (activeProfile != null && activeProfile.CanBuildLocally()) - return activeProfile; - - return null; + return EditorUserBuildSettings.activeBuildProfile; } set @@ -275,6 +270,27 @@ internal static BuildProfile GetActiveOrClassicBuildProfile( return IsSharedProfile(platformGuid) ? instance.sharedProfile : instance.GetForClassicPlatform(platformGuid); } + /// + /// This method allows the native code to fetch the development setting from + /// build profiles without allocations. + /// + /// + /// This is a workaround for certain allocation-sensitive graphics tests to pass. + /// We use return by ref to avoid the allocation that occurs when the return value + /// is boxed into a ScriptingObjectPtr. + /// + [RequiredByNativeCode] + internal static void GetActiveOrClassicBuildProfileDevelopmentSetting(ref bool value) + { + var profile = IsSharedSettingEnabledInActiveProfile("development") ? + activeProfile : instance.sharedProfile; + + if (profile == null || profile.platformBuildProfile == null) + value = false; + else + value = profile.platformBuildProfile.development; + } + internal static bool TryGetActiveOrClassicPlatformSettingsBase( BuildTarget target, StandaloneBuildSubtarget subTarget, out T result) where T : BuildProfilePlatformSettingsBase { @@ -808,6 +824,19 @@ static void UpdateActiveProfilePlayerSettingsObjectFromYAML() activeProfile?.UpdatePlayerSettingsObjectFromYAML(); } + [RequiredByNativeCode] + static void SerializeActiveProfilePlayerSettings() + { + var profile = EditorUserBuildSettings.activeBuildProfile; + if (profile == null) + return; + + if (!EditorUtility.IsDirty(profile.playerSettings)) + return; + + profile.SerializePlayerSettings(); + } + static bool ShouldReturnActiveProfile(GUID platformGuid, string sharedSetting = null) { if (!string.IsNullOrEmpty(sharedSetting)) @@ -842,5 +871,21 @@ static void Save() => InternalEditorUtility.SaveToSerializedFileAndForget(new[] [VisibleToOtherModules] internal static bool IsSharedProfile(GUID platformGuid) => platformGuid.Empty(); + + internal static void UpdateScriptingDefineSymbolsInActivePlayerSettingsOverride(NamedBuildTarget buildTarget, string defines) + { + var profile = activeProfile; + + if (profile == null) + return; + + if (profile.playerSettings == null) + return; + + PlayerSettings.SetScriptingDefineSymbols_Internal(profile.playerSettings, buildTarget.TargetName, defines); + profile.SerializePlayerSettings(); + EditorUtility.SetDirty(profile); + AssetDatabase.SaveAssetIfDirty(profile); + } } } diff --git a/Editor/Mono/BuildProfile/BuildProfileCreate.cs b/Editor/Mono/BuildProfile/BuildProfileCreate.cs index 7935e50689..5f7a57a357 100644 --- a/Editor/Mono/BuildProfile/BuildProfileCreate.cs +++ b/Editor/Mono/BuildProfile/BuildProfileCreate.cs @@ -3,6 +3,7 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; +using System.IO; using JetBrains.Annotations; using UnityEditor.Modules; using UnityEngine; @@ -67,6 +68,7 @@ internal static void CreateInstance(GUID platformId, string assetPath) [VisibleToOtherModules("UnityEditor.BuildProfileModule")] internal static void CreateInstance(GUID platformId, string assetPath, int preconfiguredSettingsVariant, string[] packagesToAdd) { + ValidateFileNameLength(assetPath); ValidatePlatform(platformId); var (buildTarget, subtarget) = BuildProfileModuleUtil.GetBuildTargetAndSubtarget(platformId); @@ -99,6 +101,18 @@ static void ValidatePlatform(GUID platformGuid) throw new ArgumentException("A build profile must be created for a valid platform."); } + /// + /// Validates if the provided path name length is supported by the Asset database. + /// Throws an ArgumentException if the platform is not valid. + /// + /// The path to the build profile to be created. + static void ValidateFileNameLength(string assetPath) + { + // File name length is limited by the asset database + if (Path.GetFileName(assetPath).Length > BuildProfileModuleUtil.k_MaxAssetFileNameLength) + throw new ArgumentException($"Build profile name is too long ({Path.GetFileName(assetPath).Length}) - max supported is {BuildProfileModuleUtil.k_MaxAssetFileNameLength}"); + } + internal void NotifyBuildProfileExtensionOfCreation(int preconfiguredSettingsVariant) { var buildProfileExtension = BuildProfileModuleUtil.GetBuildProfileExtension(platformGuid); diff --git a/Editor/Mono/BuildProfile/BuildProfileModuleUtil.cs b/Editor/Mono/BuildProfile/BuildProfileModuleUtil.cs index 893d7244f5..99115d49a6 100644 --- a/Editor/Mono/BuildProfile/BuildProfileModuleUtil.cs +++ b/Editor/Mono/BuildProfile/BuildProfileModuleUtil.cs @@ -13,6 +13,7 @@ using UnityEngine.UIElements; using TargetAttributes = UnityEditor.BuildTargetDiscovery.TargetAttributes; using UnityEditor.Profiling; +using InternalEditorUtility = UnityEditorInternal.InternalEditorUtility; namespace UnityEditor.Build.Profile { @@ -25,8 +26,15 @@ internal class BuildProfileModuleUtil const string k_BuyProUrl = "https://store.unity.com/products/unity-pro"; const string k_ConsoleModuleUrl = "https://unity3d.com/platform-installation"; const string k_LastRunnableBuildPathSeparator = "_"; + // The asset database supports file name length to max. 250 symbols + // Leave 3 symbols for the GenerateUniqueAssetPath() that adds " 1"(2,3...) in case + // an asset with such name already exists. + public const int k_MaxAssetFileNameLength = 247; + // For UI cases where the extension `.asset` is not taken into consideration + public const int k_MaxAssetFileNameLengthWithoutExtension = k_MaxAssetFileNameLength - 6; static readonly string k_NoModuleLoaded = L10n.Tr("No {0} module loaded."); static readonly string k_DerivedPlatformInactive = L10n.Tr("{0} is currently disabled."); + static readonly string k_DerivedPlatformDisabled = L10n.Tr("{0} was disabled via command-line arguments."); static readonly string k_EditorWillNeedToBeReloaded = L10n.Tr("Note: Editor will need to be restarted to load any newly installed modules"); static readonly string k_BuildProfileRecompileReason = L10n.Tr("Active build profile scripting defines changes."); static readonly GUIContent k_OpenDownloadPage = EditorGUIUtility.TrTextContent("Open Download Page"); @@ -167,11 +175,48 @@ public static VisualElement CreateModuleNotInstalledElement(GUID platformId) }); } + // TODO: this is a workaround for onboarding instructions to fix EmbeddedLinux and QNX + // needs to be removed when https://jira.unity3d.com/browse/PLAT-7721 is implemented + if (BuildTargetDiscovery.BuildPlatformTryGetCustomInstallLinkAndText(platformId, out var url, out var text)) + { + return new IMGUIContainer(() => + { + GUILayout.Label(EditorGUIUtility.TextContent(text), EditorStyles.wordWrappedLabel); + + EditorGUILayout.BeginHorizontal(); + // Add the space to align the button with text + GUILayout.Space(6); + if (GUILayout.Button("Contact Sales", EditorStyles.miniButton, GUILayout.ExpandWidth(false))) + { + Help.BrowseURL(url); + } + EditorGUILayout.EndHorizontal(); + }); + } + + if (IsBuildProfileDisabledViaArguments(platformId)) + return new IMGUIContainer(() => + { + GUILayout.Label(EditorGUIUtility.TextContent(string.Format(k_DerivedPlatformDisabled, BuildTargetDiscovery.BuildPlatformDisplayName(platformId)))); + }); + return new IMGUIContainer( () => BuildPlayerWindow.ShowNoModuleLabel(platformId, k_NoModuleLoaded, k_OpenDownloadPage, k_InstallModuleWithHub, k_EditorWillNeedToBeReloaded)); } + static bool IsBuildProfileDisabledViaArguments(GUID platformId) + { + var (buildTarget, _) = BuildTargetDiscovery.GetBuildTargetAndSubtargetFromGUID(platformId); + var buildTargetInfoList = BuildTargetDiscovery.GetBuildTargetInfoList(); + + foreach (var info in buildTargetInfoList) + if (info.buildTargetPlatformVal.Equals(buildTarget)) + return InternalEditorUtility.IsPlaybackEngineDisabled(info.dirName); + + return false; + } + /// /// Exported from , UI code specifically for when current license does not cover /// BuildTarget. @@ -227,7 +272,7 @@ public static string GetModuleName(GUID platformId) /// public static void SwitchLegacyActiveFromBuildProfile(BuildProfile profile) { - EditorUserBuildSettings.SwitchActiveBuildTargetGuid(profile.platformGuid); + EditorUserBuildSettings.SwitchActiveBuildTargetGuid(profile); } public static void SwitchLegacySelectedBuildTargets(BuildProfile profile) diff --git a/Editor/Mono/BuildProfile/BuildProfilePlayerSettings.cs b/Editor/Mono/BuildProfile/BuildProfilePlayerSettings.cs index 025ff00c3c..c285c6bccf 100644 --- a/Editor/Mono/BuildProfile/BuildProfilePlayerSettings.cs +++ b/Editor/Mono/BuildProfile/BuildProfilePlayerSettings.cs @@ -198,12 +198,16 @@ internal void DeserializePlayerSettings() if (!HasSerializedPlayerSettings()) return; + var isDirtyBeforeDeserialize = EditorUtility.IsDirty(m_PlayerSettings); if (m_PlayerSettings == null) m_PlayerSettings = PlayerSettings.DeserializeFromYAMLString(m_PlayerSettingsYaml.GetYamlString()); else UpdatePlayerSettingsObjectFromYAML(); s_LoadedPlayerSettings.Add(m_PlayerSettings); + if (!isDirtyBeforeDeserialize) + EditorUtility.ClearDirty(m_PlayerSettings); + PlayerSettings.EnsureUnityConnectSettingsEqual(m_PlayerSettings, s_GlobalPlayerSettings); UpdateGlobalManagerPlayerSettings(); } diff --git a/Editor/Mono/BuildProfile/BuildProfileRenameOverlay.cs b/Editor/Mono/BuildProfile/BuildProfileRenameOverlay.cs index a39406c187..ae408f18fa 100644 --- a/Editor/Mono/BuildProfile/BuildProfileRenameOverlay.cs +++ b/Editor/Mono/BuildProfile/BuildProfileRenameOverlay.cs @@ -18,6 +18,7 @@ internal class BuildProfileRenameOverlay { static readonly string k_InvalidChars = BuildProfileModuleUtil.GetFilenameInvalidCharactersStr(); static readonly string k_ErrorMessage = string.Format(L10n.Tr("A file name can't contain any of the following characters:\t{0}"), k_InvalidChars); + static readonly string k_ErrorMessageLength = string.Format(L10n.Tr("Build profile name can't be longer than {0} symbols"), BuildProfileModuleUtil.k_MaxAssetFileNameLengthWithoutExtension); TextField m_TextField; Rect? m_ErrorRect = null; @@ -56,6 +57,16 @@ public void OnNameChanged(string previousValue, string newValue) m_TextField.cursorIndex = targetIndex; m_TextField.selectIndex = targetIndex; } + else if (newValue.Length > BuildProfileModuleUtil.k_MaxAssetFileNameLengthWithoutExtension) + { + TooltipView.Show(k_ErrorMessageLength, errorRect); + m_TextField.SetValueWithoutNotify(previousValue); + + // The cursor should be kept in place when adding too much + var targetIndex = Mathf.Max(m_TextField.cursorIndex - 1, 0); + m_TextField.cursorIndex = targetIndex; + m_TextField.selectIndex = targetIndex; + } else { TooltipView.ForceClose(); diff --git a/Editor/Mono/BuildTargetDiscovery.bindings.cs b/Editor/Mono/BuildTargetDiscovery.bindings.cs index 2f81a00d0a..42523a4f47 100644 --- a/Editor/Mono/BuildTargetDiscovery.bindings.cs +++ b/Editor/Mono/BuildTargetDiscovery.bindings.cs @@ -243,6 +243,11 @@ struct PlatformInfo public string iconName = "BuildSettings.Editor"; public string subtitle = ""; public List nameAndLinkToShowUnderTitle = null; + + // TODO: this is a workaround for onboarding instructions to fix EmbeddedLinux and QNX + // needs to be removed when https://jira.unity3d.com/browse/PLAT-7721 is implemented + public NameAndLink? temporaryLabelAndLinkForIndustrialOnboarding = null; + public PlatformInfo() {} public bool HasFlag(PlatformAttributes flag) { return (flags & flag) == flag; } @@ -251,7 +256,7 @@ public PlatformInfo() {} static GUID EmptyGuid = new GUID(""); - static Dictionary s_PlatformGUIDDataQuickLookup = new Dictionary(); + static Dictionary s_BuildTargetToPlatformGUID = new Dictionary(); // This list should not be exposed ouside of BuildTargetDiscovery to avoid NDA spillage, provide a access function for data here instead. // Changes here should be synced with the usage of @@ -355,7 +360,7 @@ public PlatformInfo() {} buildTarget = BuildTarget.Android, nameAndLinkToShowUnderTitle = new List { - new NameAndLink{name = L10n.Tr("Unity Android Manual"), linkUrl = "https://docs.unity3d.com/Manual/android.html"}, + new NameAndLink{name = L10n.Tr("Unity Android Manual"), linkUrl = $"https://docs.unity3d.com/{Help.GetShortReleaseVersion()}/Documentation/Manual/android.html"}, }, iconName = "BuildSettings.Android", flags = PlatformAttributes.IsWindowsBuildTarget | PlatformAttributes.IsWindowsArm64BuildTarget | PlatformAttributes.IsLinuxBuildTarget | PlatformAttributes.IsMacBuildTarget @@ -507,9 +512,13 @@ public PlatformInfo() {} { displayName = "Meta Quest", downloadLinkName = "Android", + description = L10n.Tr( + "Take advantage of Unity's support for Meta Quest platforms and leverage Unity's extensive XR tools and frameworks. " + + "This platform offers default project settings and configurations for a streamlined publishing process." + ), buildTarget = BuildTarget.Android, iconName = "BuildSettings.Meta", - requiredPackage = new string[]{L10n.Tr("com.unity.xr.meta-openxr") }, + requiredPackage = new string[]{L10n.Tr("com.unity.xr.openxr") }, flags = PlatformAttributes.IsWindowsBuildTarget | PlatformAttributes.IsWindowsArm64BuildTarget | PlatformAttributes.IsLinuxBuildTarget | PlatformAttributes.IsMacBuildTarget | PlatformAttributes.IsDerivedBuildTarget } }, @@ -582,6 +591,11 @@ public PlatformInfo() {} description = L10n.Tr("Choose Embedded Linux, a compact version of Linux, if you are planning to build applications for embedded devices and appliances."), buildTarget = BuildTarget.EmbeddedLinux, iconName = "BuildSettings.EmbeddedLinux", + + // TODO: this is a workaround for onboarding instructions to fix EmbeddedLinux and QNX + // needs to be removed when https://jira.unity3d.com/browse/PLAT-7721 is implemented + temporaryLabelAndLinkForIndustrialOnboarding = new NameAndLink{name = L10n.Tr("No Embedded Linux module loaded. If you are a current Embedded Platforms customer, contact your Account Manager for download instructions.\nFor information on access and licensing, contact the Unity Sales team."), linkUrl = "https://create.unity.com/unity-for-industries?sfcid=7015G000000KFqdQAG&sflsa=2023-04-na-dg-hmi-solutions-contact-us"}, + flags = PlatformAttributes.IsWindowsBuildTarget | PlatformAttributes.IsLinuxBuildTarget | PlatformAttributes.IsMacBuildTarget } }, @@ -595,6 +609,11 @@ public PlatformInfo() {} description = L10n.Tr("Deploy the Unity runtime to automotive and other embedded systems utilizing the Blackberry® QNX® real-time operating system."), buildTarget = BuildTarget.QNX, iconName = "BuildSettings.QNX", + + // TODO: this is a workaround for onboarding instructions to fix EmbeddedLinux and QNX + // needs to be removed when https://jira.unity3d.com/browse/PLAT-7721 is implemented + temporaryLabelAndLinkForIndustrialOnboarding = new NameAndLink{name = L10n.Tr("No QNX® module loaded. If you are a current Embedded Platforms customer, contact your Account Manager for download instructions.\nFor information on access and licensing, contact the Unity Sales team."), linkUrl = "https://create.unity.com/unity-for-industries?sfcid=7015G000000KFqdQAG&sflsa=2023-04-na-dg-hmi-solutions-contact-us"}, + flags = PlatformAttributes.IsWindowsBuildTarget | PlatformAttributes.IsWindowsArm64BuildTarget | PlatformAttributes.IsLinuxBuildTarget | PlatformAttributes.IsMacBuildTarget } }, @@ -641,10 +660,7 @@ public static GUID GetGUIDFromBuildTarget(NamedBuildTarget namedBuildTarget, Bui /// The platform GUID. Derived platform GUID when the active platform is a derived platform. Base platform GUID otherwise. public static GUID GetGUIDFromBuildTarget(BuildTarget buildTarget) { - if (buildTarget == BuildTarget.StandaloneWindows) //workaround for win64 and win having the same guid in new Build Target system - buildTarget = BuildTarget.StandaloneWindows64; - - if (s_PlatformGUIDDataQuickLookup.TryGetValue(buildTarget, out GUID value)) + if (s_BuildTargetToPlatformGUID.TryGetValue(buildTarget, out GUID value)) { var module = ModuleManager.FindPlatformSupportModule(value); if (module != null && module is IDerivedBuildTargetProvider) @@ -683,7 +699,7 @@ internal static GUID GetBasePlatformGUIDFromBuildTarget(NamedBuildTarget namedBu if (TryGetServerGUIDFromBuildTarget(namedBuildTarget, buildTarget, out var value)) return value; - if (s_PlatformGUIDDataQuickLookup.TryGetValue(buildTarget, out GUID guid)) + if (s_BuildTargetToPlatformGUID.TryGetValue(buildTarget, out GUID guid)) return guid; return EmptyGuid; @@ -702,7 +718,7 @@ internal static GUID GetBasePlatformGUID(GUID platformGuid) if (!platformInfo.HasFlag(PlatformAttributes.IsDerivedBuildTarget)) return platformGuid; - if (s_PlatformGUIDDataQuickLookup.TryGetValue(platformInfo.buildTarget, out GUID basePlatformGuid)) + if (s_BuildTargetToPlatformGUID.TryGetValue(platformInfo.buildTarget, out GUID basePlatformGuid)) return basePlatformGuid; return EmptyGuid; @@ -720,12 +736,15 @@ static void PreloadBuildPlatformInstalledData() { foreach (var platform in allPlatforms) { + // Capture BuildTarget to GUID mapping for all platforms. + // Considers that StandaloneWindows and StandaloneWindows64 are the same platform. if (platform.Value.buildTarget != BuildTarget.StandaloneWindows && platform.Value.subtarget != StandaloneBuildSubtarget.Server - && !platform.Value.HasFlag(PlatformAttributes.IsDerivedBuildTarget) - ) //workaround for win64 and win having the same guid in new Build Target system and for derived build targets + && !platform.Value.HasFlag(PlatformAttributes.IsDerivedBuildTarget)) { - s_PlatformGUIDDataQuickLookup.Add(platform.Value.buildTarget, platform.Key); + s_BuildTargetToPlatformGUID.Add(platform.Value.buildTarget, platform.Key); + if (platform.Value.buildTarget == BuildTarget.StandaloneWindows64) + s_BuildTargetToPlatformGUID.Add(BuildTarget.StandaloneWindows, platform.Key); } var playbackEngineDirectory = BuildPipeline.GetPlaybackEngineDirectory(platform.Value.buildTarget, BuildOptions.None, false); @@ -743,41 +762,10 @@ static void PreloadBuildPlatformInstalledData() } bool isInstalled = false; - if (platform.Value.HasFlag(PlatformAttributes.IsWindowsServerBuildTarget)) - { - var serverVariations = new[] - { - "win32_server_development", - "win32_server_nondevelopment", - "win64_server_development", - "win64_server_nondevelopment", - "win_arm64_server_development", - "win_arm64_server_nondevelopment", - }; - isInstalled = VariationPresent(serverVariations, playbackEngineDirectory); - } - else if (platform.Value.HasFlag(PlatformAttributes.IsLinuxServerBuildTarget)) - { - var serverVariations = new[] - { - "linux64_server_development", - "linux64_server_nondevelopment", - }; - isInstalled = VariationPresent(serverVariations, playbackEngineDirectory); - } - else if (platform.Value.HasFlag(PlatformAttributes.IsMacServerBuildTarget)) - { - var serverVariations = new[] - { - "macos_x64_server_development", - "macos_x64_server_nondevelopment", - "macos_arm64_server_development", - "macos_arm64_server_nondevelopment", - "macos_x64arm64_server_development", - "macos_x64arm64_server_nondevelopment", - }; - isInstalled = VariationPresent(serverVariations, playbackEngineDirectory); - } + if (platform.Value.HasFlag(PlatformAttributes.IsWindowsServerBuildTarget) || + platform.Value.HasFlag(PlatformAttributes.IsLinuxServerBuildTarget) || + platform.Value.HasFlag(PlatformAttributes.IsMacServerBuildTarget)) + isInstalled = BuildPipeline.IsServerBuildPlatformSupported(platform.Value.buildTarget); else isInstalled = true; @@ -785,24 +773,6 @@ static void PreloadBuildPlatformInstalledData() } } - static bool VariationPresent(string[] variations, string playbackEngineDirectory) - { - if (string.IsNullOrEmpty(playbackEngineDirectory)) - return false; - - var scriptingBackends = new[] { "mono", "il2cpp", "coreclr" }; - foreach (var variation in variations) - { - foreach (var backend in scriptingBackends) - { - if (Directory.Exists(Paths.Combine(playbackEngineDirectory, "Variations", variation + "_" + backend))) - return true; - } - } - - return false; - } - [System.Obsolete("BuildPlatformIsInstalled(BuildTarget) is obsolete. Use BuildPlatformIsInstalled(IBuildTarget) instead.", false)] public static bool BuildPlatformIsInstalled(BuildTarget platform) => BuildPlatformIsInstalled(GetGUIDFromBuildTarget(platform)); @@ -942,6 +912,25 @@ public static List BuildPlatformNameLinkList(GUID guid) return null; } + // TODO: this is a workaround for onboarding instructions to fix EmbeddedLinux and QNX + // needs to be removed when https://jira.unity3d.com/browse/PLAT-7721 is implemented + public static bool BuildPlatformTryGetCustomInstallLinkAndText(GUID guid, out string url, out string text) + { + if (allPlatforms.TryGetValue(guid, out PlatformInfo platformInfo)) + { + var nameAndLink = platformInfo.temporaryLabelAndLinkForIndustrialOnboarding; + if (nameAndLink != null) + { + url = nameAndLink.Value.linkUrl; + text = nameAndLink.Value.name; + return true; + } + } + url = string.Empty; + text = string.Empty; + return false; + } + [System.Obsolete("BuildPlatformOnboardingInstructions(BuildTarget) is obsolete. Use BuildPlatformOnboardingInstructions(IBuildTarget) instead.", false)] public static string BuildPlatformOnboardingInstructions(BuildTarget platform) => BuildPlatformOnboardingInstructions(GetGUIDFromBuildTarget(platform)); diff --git a/Editor/Mono/CodeEditor/DefaultExternalCodeEditor.cs b/Editor/Mono/CodeEditor/DefaultExternalCodeEditor.cs index 9dcb83225e..56dd6830d4 100644 --- a/Editor/Mono/CodeEditor/DefaultExternalCodeEditor.cs +++ b/Editor/Mono/CodeEditor/DefaultExternalCodeEditor.cs @@ -8,13 +8,15 @@ using Unity.CodeEditor; using UnityEditorInternal; using UnityEngine; +using UnityEngine.Assertions; +using NiceIO; namespace UnityEditor { internal class DefaultExternalCodeEditor : IExternalCodeEditor { static readonly GUIContent k_ResetArguments = EditorGUIUtility.TrTextContent("Reset argument"); - static readonly string[] supportedExtensions = {"json", "asmdef", "log", "cs", "uxml", "uss", "shader", "compute", "cginc", "hlsl", "glslinc", "template", "raytrace" }; + 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; static bool IsLinux => Application.platform == RuntimePlatform.LinuxEditor; @@ -37,7 +39,7 @@ string Arguments // So on OSX we change the key for per application for script editor args, // to avoid reading the one from previous versions. // The year 2021: Delete mac hack. - if (Application.platform == RuntimePlatform.OSXEditor) + if (IsOSX) { var oldMac = EditorPrefs.GetString("kScriptEditorArgs_" + Installation); if (!string.IsNullOrEmpty(oldMac)) @@ -50,7 +52,7 @@ string Arguments } set { - if (Application.platform == RuntimePlatform.OSXEditor) + if (IsOSX) { EditorPrefs.SetString("kScriptEditorArgs_" + Installation, value); } @@ -134,11 +136,15 @@ public bool OpenProject(string path, int line, int column) return false; } - string applicationPath = CodeEditor.CurrentEditorPath.Trim(); + var applicationPath = CodeEditor.CurrentEditorPath.Trim(); + var doesNotExistWarning = + $"External Code Editor application path does not exist ({applicationPath})! Please select a different application."; - if (!string.IsNullOrEmpty(applicationPath) && !File.Exists(applicationPath)) + var npath = new NPath(applicationPath); + if (applicationPath == null || !npath.Exists()) { - UnityEngine.Debug.LogWarning($"External Code Editor application path does not exist ({applicationPath})! Please select a different application"); + UnityEngine.Debug.LogWarning(doesNotExistWarning); + return false; } if (applicationPath == CodeEditor.SystemDefaultPath) @@ -146,25 +152,21 @@ public bool OpenProject(string path, int line, int column) return InternalEditorUtility.OpenFileAtLineExternal(path, -1, -1); } - if (IsOSX) - { - return CodeEditor.OSOpenFile(applicationPath, CodeEditor.ParseArgument(Arguments, path, line, column)); - } - string fileName = ""; string arguments = ""; - - if (IsLinux) - { - fileName = applicationPath; - arguments = CodeEditor.ParseArgument(Arguments, path, line, column); - } - - if (IsWindows) + switch (Application.platform) { - fileName = "cmd.exe"; - arguments = "/C \"" + CodeEditor.QuoteForProcessStart(applicationPath) + - " " + CodeEditor.ParseArgument(Arguments, path, line, column) + "\""; + case RuntimePlatform.OSXEditor: + return CodeEditor.OSOpenFile(applicationPath, CodeEditor.ParseArgument(Arguments, path, line, column)); + case RuntimePlatform.LinuxEditor: + fileName = applicationPath; + arguments = CodeEditor.ParseArgument(Arguments, path, line, column); + break; + case RuntimePlatform.WindowsEditor: + fileName = "cmd.exe"; + arguments = "/C \"" + CodeEditor.QuoteForProcessStart(applicationPath) + + " " + CodeEditor.ParseArgument(Arguments, path, line, column) + "\""; + break; } var process = new Process diff --git a/Editor/Mono/Commands/GOCreationCommands.cs b/Editor/Mono/Commands/GOCreationCommands.cs index 0d41b387a1..7a1c5cc9c5 100644 --- a/Editor/Mono/Commands/GOCreationCommands.cs +++ b/Editor/Mono/Commands/GOCreationCommands.cs @@ -13,7 +13,18 @@ namespace UnityEditor { static class GOCreationCommands { - internal static SavedBool s_PlaceObjectsAtWorldOrigin = new SavedBool("Create3DObject.PlaceAtWorldOrigin", false); + internal enum PlacementMode + { + SceneIntersection, + WorldOrigin, + ScenePivot + } + static SavedInt s_PlacementModePref = new SavedInt("Create3DObject.PlacementMode", 0); + internal static PlacementMode s_PlacementMode + { + get => (PlacementMode)s_PlacementModePref.value; + set => s_PlacementModePref.value = (int)value; + } // This is here because we can't pass Scenes around with the MenuCommand context. SceneHierarchy toggles this // flag when add object context menu items are invoked from a context click on Scene headers. If you make use of @@ -22,7 +33,7 @@ static class GOCreationCommands static bool placeObjectsAtWorldOrigin { - get { return s_PlaceObjectsAtWorldOrigin.value || forcePlaceObjectsAtWorldOrigin; } + get { return s_PlacementMode == PlacementMode.WorldOrigin || forcePlaceObjectsAtWorldOrigin; } } private static void SetGameObjectParent(GameObject go, Transform parentTransform) @@ -38,6 +49,32 @@ private static void SetGameObjectParent(GameObject go, Transform parentTransform ObjectFactory.AddComponent(go); } + internal static Vector3 GetNewObjectPosition() + { + var sceneView = SceneView.lastActiveSceneView; + if (placeObjectsAtWorldOrigin || sceneView == null) return Vector3.zero; + + if (sceneView.in2DMode) + return new Vector3((float)Math.Round(sceneView.pivot.x, 5), (float)Math.Round(sceneView.pivot.y, 5), 0f); + + if (s_PlacementMode == PlacementMode.SceneIntersection) + { + var prevCamera = Camera.current; + + Handles.Internal_SetCurrentCamera(sceneView.camera); + var guiPoint = HandleUtility.WorldToGUIPoint(sceneView.pivot); + var didPlace = HandleUtility.PlaceObject(guiPoint, out var position, out _); + Handles.Internal_SetCurrentCamera(prevCamera); + + if (didPlace) + return new Vector3((float)Math.Round(position.x, 5), (float)Math.Round(position.y, 5), (float)Math.Round(position.z, 5)); + } + + // if s_PlacementMode == PlacementMode.ScenePivot + var pivot = sceneView.pivot; + return new Vector3((float)Math.Round(pivot.x, 5), (float)Math.Round(pivot.y, 5), (float)Math.Round(pivot.z, 5)); + } + internal static void Place(GameObject go, GameObject parent, bool ignoreSceneViewPosition = true, bool alignWithSceneCamera = false) { Transform defaultObjectTransform = SceneView.GetDefaultParentObjectIfSet(); @@ -77,7 +114,7 @@ internal static void Place(GameObject go, GameObject parent, bool ignoreSceneVie SceneView.AlignCameraWithView(cam); } else - SceneView.PlaceGameObjectInFrontOfSceneView(go); + go.transform.position = GetNewObjectPosition(); } StageUtility.PlaceGameObjectInCurrentStage(go); // may change parent diff --git a/Editor/Mono/ConsoleWindow.cs b/Editor/Mono/ConsoleWindow.cs index 926d15ca8a..d8920dda87 100644 --- a/Editor/Mono/ConsoleWindow.cs +++ b/Editor/Mono/ConsoleWindow.cs @@ -739,6 +739,7 @@ const bool { SetActiveEntry(entry); m_LastActiveEntryIndex = entry.globalLineIndex; + m_TextScroll = Vector2.zero; activeEntryChanged?.Invoke(); } @@ -782,16 +783,13 @@ const bool if (rowDoubleClicked != -1) LogEntries.RowGotDoubleClicked(rowDoubleClicked); - // Display active text (We want word wrapped text with a vertical scrollbar) - m_TextScroll = GUILayout.BeginScrollView(m_TextScroll, Constants.Box); - string stackWithHyperlinks = StacktraceWithHyperlinks(m_ActiveText, m_CallstackTextStart, s_StripLoggingCallstack, m_ActiveMode); float height = Constants.MessageStyle.CalcHeight(GUIContent.Temp(stackWithHyperlinks), position.width); - EditorGUILayout.SelectableLabel(stackWithHyperlinks, Constants.MessageStyle, - GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true), GUILayout.MinHeight(height + 10)); - GUILayout.EndScrollView(); + var rect = EditorGUILayout.BeginHorizontal(GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true), GUILayout.MinHeight(height + 10)); + EditorGUI.ScrollableLabelAreaInternal(rect, stackWithHyperlinks, ref m_TextScroll, Constants.MessageStyle); + EditorGUILayout.EndHorizontal(); SplitterGUILayout.EndVerticalSplit(); } diff --git a/Editor/Mono/ContainerWindow.bindings.cs b/Editor/Mono/ContainerWindow.bindings.cs index 688b693e07..19f7812017 100644 --- a/Editor/Mono/ContainerWindow.bindings.cs +++ b/Editor/Mono/ContainerWindow.bindings.cs @@ -70,10 +70,6 @@ extern Rect Internal_Position [FreeFunction(k_ScriptingPrefix + "MoveBehindOf", HasExplicitThis = true)] public extern void MoveBehindOf(ContainerWindow other); - // Fit a container window to the screen. - [FreeFunction(k_ScriptingPrefix + "FitWindowRectToScreen", HasExplicitThis = true)] - public extern Rect FitWindowRectToScreen(Rect r, bool forceCompletelyVisible, bool useMouseScreen); - [FreeFunction(k_ScriptingPrefix + "SendCaptionEvent", HasExplicitThis = true)] public extern void SendCaptionEvent(bool mouseDown); @@ -108,7 +104,11 @@ extern Rect Internal_Position [FreeFunction(k_ScriptingPrefix + "GetOrderedWindowList")] internal static extern void GetOrderedWindowList(); + [FreeFunction(k_ScriptingPrefix + "FitRectToMouseScreen")] + internal static extern Rect FitRectToMouseScreen(Rect rect, bool forceCompletelyVisible, ContainerWindow windowForBorderCalculation); + [FreeFunction(k_ScriptingPrefix + "FitRectToScreen")] - internal static extern Rect FitRectToScreen(Rect defaultRect, bool forceCompletelyVisible, bool useMouseScreen); + internal static extern Rect FitRectToScreen(Rect rect, Vector2 uiPositionToFindScreen, bool forceCompletelyVisible, ContainerWindow windowForBorderCalculation); + } } diff --git a/Editor/Mono/ContainerWindow.cs b/Editor/Mono/ContainerWindow.cs index 2b30bbef67..5703264ccf 100644 --- a/Editor/Mono/ContainerWindow.cs +++ b/Editor/Mono/ContainerWindow.cs @@ -149,7 +149,7 @@ internal void ShowPopupWithMode(ShowMode mode, bool giveFocus) Internal_BringLiveAfterCreation(true, giveFocus, false); // Fit window to screen - needs to be done after bringing the window live - position = FitWindowRectToScreen(m_PixelRect, true, false); + position = FitRectToScreen(m_PixelRect, m_PixelRect.center, true, this); rootView.position = new Rect(0, 0, GUIUtility.RoundToPixelGrid(m_PixelRect.width), GUIUtility.RoundToPixelGrid(m_PixelRect.height)); rootView.Reflow(); } @@ -231,7 +231,11 @@ public void Show(ShowMode showMode, bool loadPosition, bool displayImmediately, internal void FitWindowToScreen(bool useMousePos) { - position = FitWindowRectToScreen(m_PixelRect, true, useMousePos); + if (useMousePos) + position = FitRectToMouseScreen(m_PixelRect, true, this); + else + position = FitRectToScreen(m_PixelRect, m_PixelRect.center, true, this); + if (rootView) rootView.position = new Rect(0, 0, GUIUtility.RoundToPixelGrid(m_PixelRect.width), GUIUtility.RoundToPixelGrid(m_PixelRect.height)); } diff --git a/Editor/Mono/ContextMenuUtility.cs b/Editor/Mono/ContextMenuUtility.cs index ff7d7aad52..70dad80a2d 100644 --- a/Editor/Mono/ContextMenuUtility.cs +++ b/Editor/Mono/ContextMenuUtility.cs @@ -186,7 +186,9 @@ internal static void ShowActionMenu() if (dropdownMenu.MenuItems().Count == 0) AddAction(dropdownMenu, "No Actions for this Context", null, false); - dropdownMenu.DoDisplayEditorMenu(new Rect(Event.current.mousePosition, Vector2.zero)); +#pragma warning disable CS0618 // Type or member is obsolete + EditorMenuExtensions.DoDisplayEditorMenuFromImGUI(dropdownMenu, new Rect(Event.current.mousePosition, Vector2.zero)); +#pragma warning restore CS0618 // Type or member is obsolete } static T[] ToArray(IEnumerable enumerable) where T : Object diff --git a/Editor/Mono/EditorGUI.cs b/Editor/Mono/EditorGUI.cs index dae7dabbe4..aca9600529 100644 --- a/Editor/Mono/EditorGUI.cs +++ b/Editor/Mono/EditorGUI.cs @@ -581,6 +581,7 @@ public virtual void EndEditing() { if (activeEditor == this) { + activeEditor.m_HasFocus = false; activeEditor = null; } @@ -886,7 +887,9 @@ internal static bool MightBePrintableKey(Event evt) return false; if (evt.keyCode >= KeyCode.JoystickButton0 && evt.keyCode <= KeyCode.Joystick8Button19) return false; - if (evt.keyCode >= KeyCode.F1 && evt.keyCode <= KeyCode.F15) + if (evt.keyCode >= KeyCode.F1 && evt.keyCode <= KeyCode.F15 || + // KeyCode.F15 (296) and KeyCode.F16 (670) are not contiguous + evt.keyCode >= KeyCode.F16 && evt.keyCode <= KeyCode.F24) return false; switch (evt.keyCode) { @@ -1998,71 +2001,131 @@ internal static string SearchField(Rect position, string text) return text; } - internal static string ScrollableTextAreaInternal(Rect position, string text, ref Vector2 scrollPosition, GUIStyle style) + struct ScrollableAreaScope : IDisposable { - if (Event.current.type == EventType.Layout) - return text; + GUIStyle style; + Vector2 scrollPosition; + Vector2 oldScrollValue; - int id = GUIUtility.GetControlID(s_TextAreaHash, FocusType.Keyboard, position); + public ScrollableAreaScope(int id, ref Rect position, string text, ref Vector2 scrollPosition, ref GUIStyle style) + { + this.style = style; + this.scrollPosition = scrollPosition; - position = IndentedRect(position); - float fullTextHeight = style.CalcHeight(GUIContent.Temp(text), position.width); - Rect viewRect = new Rect(0, 0, position.width, fullTextHeight); + position = IndentedRect(position); + float fullTextHeight = style.CalcHeight(GUIContent.Temp(text), position.width); + Rect viewRect = new Rect(0, 0, position.width, fullTextHeight); - Vector2 oldStyleScrollValue = style.contentOffset; + oldScrollValue = style.contentOffset; + if (position.height < viewRect.height) + { + //Scroll bar position + Rect scrollbarPosition = position; + scrollbarPosition.width = GUI.skin.verticalScrollbar.fixedWidth; + scrollbarPosition.height -= 2; + scrollbarPosition.y += 1; + scrollbarPosition.x = position.x + position.width - scrollbarPosition.width; + position.width -= scrollbarPosition.width; - if (position.height < viewRect.height) - { - //Scroll bar position - Rect scrollbarPosition = position; - scrollbarPosition.width = GUI.skin.verticalScrollbar.fixedWidth; - scrollbarPosition.height -= 2; - scrollbarPosition.y += 1; - scrollbarPosition.x = position.x + position.width - scrollbarPosition.width; + //textEditor width changed, recalculate Text and viewRect areas. + fullTextHeight = style.CalcHeight(GUIContent.Temp(text), position.width); + viewRect = new Rect(0, 0, position.width, fullTextHeight); - position.width -= scrollbarPosition.width; + if (position.Contains(Event.current.mousePosition) && Event.current.type == EventType.ScrollWheel) + { + const float mouseWheelMultiplier = 10f; + float desiredY = scrollPosition.y + Event.current.delta.y * mouseWheelMultiplier; + scrollPosition.y = Mathf.Clamp(desiredY, 0f, viewRect.height); + Event.current.Use(); + } - //textEditor width changed, recalculate Text and viewRect areas. - fullTextHeight = style.CalcHeight(GUIContent.Temp(text), position.width); - viewRect = new Rect(0, 0, position.width, fullTextHeight); + scrollPosition.y = GUI.VerticalScrollbar(scrollbarPosition, scrollPosition.y, position.height, 0, viewRect.height); - if (position.Contains(Event.current.mousePosition) && Event.current.type == EventType.ScrollWheel) - { - const float mouseWheelMultiplier = 10f; - float desiredY = scrollPosition.y + Event.current.delta.y * mouseWheelMultiplier; - scrollPosition.y = Mathf.Clamp(desiredY, 0f, viewRect.height); - Event.current.Use(); + if (!s_RecycledEditor.IsEditingControl(id)) + { + //When not editing we use the style.draw, so we need to change the offset on the style instead of the RecycledEditor. + style.contentOffset -= scrollPosition; + style.Internal_clipOffset = scrollPosition; + } + else + { + //Move the Editor offset to match our scrollbar + s_RecycledEditor.scrollOffset = scrollPosition; + } } + } + public void Dispose() + { + style.contentOffset = oldScrollValue; + style.Internal_clipOffset = Vector2.zero; + } + } - scrollPosition.y = GUI.VerticalScrollbar(scrollbarPosition, scrollPosition.y, position.height, 0, viewRect.height); + internal static void ScrollableLabelAreaInternal(Rect position, string text, ref Vector2 scrollPosition, GUIStyle style) + { + if (Event.current.type == EventType.Layout) + return; + int id = GUIUtility.GetControlID(s_SelectableLabelHash, FocusType.Keyboard, position); + var sendEventToTextEditor = SendEventToTextEditor(id, Event.current); - if (!s_RecycledEditor.IsEditingControl(id)) - { - //When not editing we use the style.draw, so we need to change the offset on the style instead of the RecycledEditor. - style.contentOffset -= scrollPosition; - style.Internal_clipOffset = scrollPosition; - } - else + using (new ScrollableAreaScope(id, ref position, text, ref scrollPosition, ref style)) + { + //Events cannot be handled at the scope level + EventType beforeTextFieldEventType = Event.current.type; + // It's possible that DoTextField will throw so we use a scoped cursor that we can safely reset the color. + using (new CursorColorScope(Color.clear)) { - //Move the Editor offset to match our scrollbar - s_RecycledEditor.scrollOffset = scrollPosition; + + RecycledTextEditor.s_AllowContextCutOrPaste = false; + if (sendEventToTextEditor) + { + bool isMouseDown = Event.current.rawType == EventType.MouseDown && Event.current.button == 0; + bool isEditingControl = s_RecycledEditor.IsEditingControl(id); + + DoTextField(s_RecycledEditor, id, position, text, style, string.Empty, out _, false, true, false); + + if (isMouseDown && Event.current.type == EventType.Used) + { + // We just took control over the Scrollable label + if (!isEditingControl && s_RecycledEditor.IsEditingControl(id)) + { + // Properly set the scroll offset as it was set to 0 in the editor.BeginEditing + // Move the recycled Editor offset to match our scrollbar + s_RecycledEditor.scrollOffset = scrollPosition; + } + } + } } + + //Only update the out scrollPosition if the user has interacted with the TextArea (the current event was used) + if (beforeTextFieldEventType != Event.current.type) + scrollPosition = s_RecycledEditor.scrollOffset; } - bool dummy; - EventType beforeTextFieldEventType = Event.current.type; - string newValue = DoTextField(s_RecycledEditor, id, position, text, style, null, out dummy, false, true, false); + } + + internal static string ScrollableTextAreaInternal(Rect position, string text, ref Vector2 scrollPosition, GUIStyle style) + { + if (Event.current.type == EventType.Layout) + return text; + + int id = GUIUtility.GetControlID(s_TextAreaHash, FocusType.Keyboard, position); - //Only update the our scrollPosition if the user has interacted with the TextArea (the current event was used) - if (beforeTextFieldEventType != Event.current.type) + string newValue = string.Empty; + using (new ScrollableAreaScope(id, ref position, text, ref scrollPosition, ref style)) { - scrollPosition = s_RecycledEditor.scrollOffset; + //Events cannot be handled at the scope level + EventType beforeTextFieldEventType = Event.current.type; + newValue = DoTextField(s_RecycledEditor, id, position, text, style, null, out _, false, true, + false); + + //Only update the out scrollPosition if the user has interacted with the TextArea (the current event was used) + if (beforeTextFieldEventType != Event.current.type) + scrollPosition = s_RecycledEditor.scrollOffset; } - style.contentOffset = oldStyleScrollValue; - style.Internal_clipOffset = Vector2.zero; return newValue; } @@ -2075,12 +2138,8 @@ internal static string TextAreaInternal(Rect position, string text, GUIStyle sty return text; } - // Make a selectable label field. (Useful for showing read-only info that can be copy-pasted.) - internal static void SelectableLabelInternal(Rect position, string text, GUIStyle style) + static bool SendEventToTextEditor(int id, Event e) { - int id = GUIUtility.GetControlID(s_SelectableLabelHash, FocusType.Keyboard, position); - Event e = Event.current; - var sendEventToTextEditor = true; if (GUIUtility.keyboardControl == id && e.GetTypeForControl(id) == EventType.KeyDown) { @@ -2110,15 +2169,24 @@ internal static void SelectableLabelInternal(Rect position, string text, GUIStyl sendEventToTextEditor = false; } - // Its possible that DoTextField will throw so we use a scoped cursor that we can safely reset the color. + return sendEventToTextEditor; + } + + // Make a selectable label field. (Useful for showing read-only info that can be copy-pasted.) + internal static void SelectableLabelInternal(Rect position, string text, GUIStyle style) + { + int id = GUIUtility.GetControlID(s_SelectableLabelHash, FocusType.Keyboard, position); + + var sendEventToTextEditor = SendEventToTextEditor(id, Event.current); + + // It's possible that DoTextField will throw so we use a scoped cursor that we can safely reset the color. using (new CursorColorScope(Color.clear)) { RecycledTextEditor.s_AllowContextCutOrPaste = false; if (sendEventToTextEditor) { - bool dummy; - DoTextField(s_RecycledEditor, id, IndentedRect(position), text, style, string.Empty, out dummy, false, true, false); + DoTextField(s_RecycledEditor, id, IndentedRect(position), text, style, string.Empty, out _, false, true, false); } } } @@ -7681,7 +7749,7 @@ internal static bool DefaultPropertyField(Rect position, SerializedProperty prop bool toggled = DropdownButton(position, toggleLabelContent, FocusType.Keyboard, EditorStyles.layerMaskField); if (toggled) { - PopupWindowWithoutFocus.Show(position, new MaskFieldDropDown(property)); + PopupWindow.Show(position, new MaskFieldDropDown(property)); GUIUtility.ExitGUI(); } break; diff --git a/Editor/Mono/EditorSettings.bindings.cs b/Editor/Mono/EditorSettings.bindings.cs index 26f1d7bf77..98c914a017 100644 --- a/Editor/Mono/EditorSettings.bindings.cs +++ b/Editor/Mono/EditorSettings.bindings.cs @@ -242,6 +242,9 @@ internal static extern string Internal_ProjectGenerationUserExtensions [StaticAccessor("GetEditorSettings()", StaticAccessorType.Dot)] public static extern bool useLegacyProbeSampleCount { get; set; } + [StaticAccessor("GetEditorSettings()", StaticAccessorType.Dot)] + public static extern bool shadowmaskStitching { get; set; } + [Obsolete("The disableCookiesInLightmapper setting is no longer supported. Cookies are always enabled in the Progressive Lightmapper.", true)] public static bool disableCookiesInLightmapper { diff --git a/Editor/Mono/EditorUserBuildSettings.bindings.cs b/Editor/Mono/EditorUserBuildSettings.bindings.cs index b4fa497d6d..c60c3b0977 100644 --- a/Editor/Mono/EditorUserBuildSettings.bindings.cs +++ b/Editor/Mono/EditorUserBuildSettings.bindings.cs @@ -12,6 +12,7 @@ using UnityEditor.Build.Profile; using UnityEngine; using UnityEditor.Modules; +using UnityEngine.Assertions; namespace UnityEditor { @@ -134,6 +135,7 @@ public enum AndroidBuildSubtarget ASTC = -1, } + [Obsolete("UnityEditor.AndroidCreateSymbols has been deprecated. Use UnityEditor.Android.DebugSymbols.level instead")] public enum AndroidCreateSymbols { Disabled, @@ -820,8 +822,9 @@ internal static GUID activePlatformGuid [NativeMethod("SwitchActiveBuildTargetGuid")] private static extern bool SwitchActiveBuildTargetAndSubTargetGuid(GUID platformGuid, BuildTarget target, int subtarget); - internal static bool SwitchActiveBuildTargetGuid(GUID platformGuid) + internal static bool SwitchActiveBuildTargetGuid(BuildProfile profile) { + var platformGuid = profile.platformGuid; // Account for derived platforms. // Jira https://jira.unity3d.com/browse/PLAT-9234 // The editor triggers recompilation on a build target or subtarget change already. Both of these values @@ -834,10 +837,17 @@ internal static bool SwitchActiveBuildTargetGuid(GUID platformGuid) } var (buildTargetFromGuid, subTargetFromGuid) = BuildTargetDiscovery.GetBuildTargetAndSubtargetFromGUID(platformGuid); - int activeSubtarget = (int)subTargetFromGuid; - if(subTargetFromGuid == StandaloneBuildSubtarget.Default) - activeSubtarget = EditorUserBuildSettings.GetActiveSubtargetFor(buildTargetFromGuid); + + //This will either get the base or derived platform to cast to correct baseplatformsetting type. + bool result = BuildTargetDiscovery.TryGetBuildTarget(profile.buildTarget, out var buildTarget); + if (result && buildTarget != null) + { + var subtarget = (buildTarget.TextureSubtargetPlatformProperties == null) ? -1 : + buildTarget.TextureSubtargetPlatformProperties.GetSubtargetFromPlatformSettings(profile.platformBuildProfile); + if (subtarget != -1) activeSubtarget = subtarget; + } + return SwitchActiveBuildTargetAndSubTargetGuid(platformGuid, buildTargetFromGuid, activeSubtarget); } diff --git a/Editor/Mono/EditorUtility.cs b/Editor/Mono/EditorUtility.cs index 6b26a19e4a..12fc408434 100644 --- a/Editor/Mono/EditorUtility.cs +++ b/Editor/Mono/EditorUtility.cs @@ -79,6 +79,8 @@ public static string GetDialogOptOutMessage(DialogOptOutDecisionType dialogOptOu public delegate void SelectMenuItemFunction(object userData, string[] options, int selected); + internal static event Action onResetMouseDown; + public static bool LoadWindowLayout(string path) { return WindowLayout.TryLoadWindowLayout(path, false); @@ -412,6 +414,7 @@ internal static void ResetMouseDown() GUIUtility.hotControl = 0; //Delay call because the freezing of the editor is affecting the active clutch shortcuts resetting properly EditorApplication.delayCall += () => ShortcutIntegration.instance.trigger.ResetActiveClutches(); + onResetMouseDown?.Invoke(); } internal static void DisplayCustomMenu(Rect position, string[] options, int[] selected, SelectMenuItemFunction callback, object userData) @@ -462,12 +465,19 @@ internal static void DisplayCustomMenuWithSeparators(Rect position, string[] opt DisplayCustomMenuWithSeparators(position, options, enabled, separator, selected, callback, userData, showHotkey, false); } + + //This method is only valid during onGUI callbacks, prefer DisplayCustomMenuWithSeparatorsWithScreenSpacePosition that work al the time. internal static void DisplayCustomMenuWithSeparators(Rect position, string[] options, bool[] enabled, bool[] separator, int[] selected, SelectMenuItemFunction callback, object userData, bool showHotkey, bool allowDisplayNames, bool shouldDiscardMenuOnSecondClick = false) { Vector2 temp = GUIUtility.GUIToScreenPoint(new Vector2(position.x, position.y)); position.x = temp.x; position.y = temp.y; + DisplayCustomMenuWithSeparatorsWithScreenSpacePosition(position, options, enabled, separator, selected, callback, userData, showHotkey, allowDisplayNames, shouldDiscardMenuOnSecondClick); + } + + internal static void DisplayCustomMenuWithSeparatorsWithScreenSpacePosition(Rect position, string[] options, bool[] enabled, bool[] separator, int[] selected, SelectMenuItemFunction callback, object userData, bool showHotkey, bool allowDisplayNames, bool shouldDiscardMenuOnSecondClick) + { Internal_DisplayCustomMenu(position, options, enabled, separator, selected, callback, userData, showHotkey, allowDisplayNames, shouldDiscardMenuOnSecondClick); ResetMouseDown(); } diff --git a/Editor/Mono/EditorWindow.cs b/Editor/Mono/EditorWindow.cs index e91fcb9c87..ea5453184b 100644 --- a/Editor/Mono/EditorWindow.cs +++ b/Editor/Mono/EditorWindow.cs @@ -120,6 +120,9 @@ internal SerializableJsonDictionary viewDataDictionary } } + // This list contains all the editor windows that are already enabled/active/alive. + // It is important to check if your window is contained in this list before doing anything, + // such like UI build/rebuild. internal static List activeEditorWindows { get; } = new List(); internal void SaveViewData() @@ -1258,7 +1261,7 @@ public Rect position if (!da) { if(m_Parent.window.showMode == ShowMode.Tooltip || m_Parent.window.showMode == ShowMode.PopupMenu) - m_Parent.window.position = m_Parent.window.FitWindowRectToScreen(value, true, false); + m_Parent.window.position = ContainerWindow.FitRectToScreen(value, value.center, true, m_Parent.window); else m_Parent.window.position = value; m_Parent.window.OnResize(); diff --git a/Editor/Mono/GI/Lightmapping.bindings.cs b/Editor/Mono/GI/Lightmapping.bindings.cs index 112fbb67cc..9fc30918f9 100644 --- a/Editor/Mono/GI/Lightmapping.bindings.cs +++ b/Editor/Mono/GI/Lightmapping.bindings.cs @@ -241,12 +241,24 @@ internal static FilterMode filterMode // Starts an asynchronous bake job. [FreeFunction] - public static extern bool BakeAsync(); + internal static extern bool BakeAsyncImpl(); - // Stars a synchronous bake job. + // Starts a synchronous bake job. [FreeFunction] - public static extern bool Bake(); + internal static extern bool BakeImpl(); + + public static bool BakeAsync() + { + RenderPipelineManager.TryPrepareRenderPipeline(GraphicsSettings.currentRenderPipeline); + return BakeAsyncImpl(); + } + public static bool Bake() + { + RenderPipelineManager.TryPrepareRenderPipeline(GraphicsSettings.currentRenderPipeline); + return BakeImpl(); + } + // Cancels the currently running asynchronous bake job. [FreeFunction("CancelLightmapping")] public static extern void Cancel(); @@ -716,6 +728,7 @@ public static bool extractAmbientOcclusion public static bool BakeAsync(Scene targetScene) { + RenderPipelineManager.TryPrepareRenderPipeline(GraphicsSettings.currentRenderPipeline); return BakeSceneAsync(targetScene); } @@ -726,6 +739,7 @@ public static bool BakeAsync(Scene targetScene) public static bool Bake(Scene targetScene) { + RenderPipelineManager.TryPrepareRenderPipeline(GraphicsSettings.currentRenderPipeline); return BakeScene(targetScene); } diff --git a/Editor/Mono/GUI/ColorMutator.cs b/Editor/Mono/GUI/ColorMutator.cs index c335e38cd4..ee764725e0 100644 --- a/Editor/Mono/GUI/ColorMutator.cs +++ b/Editor/Mono/GUI/ColorMutator.cs @@ -47,7 +47,7 @@ internal static void DecomposeHdrColor(Color linearColorHdr, out Color32 baseLin } [SerializeField] private Color m_OriginalColor; - [SerializeField] private Color m_HDRBaseColor; + [SerializeField] private Color m_HDRBaseColor; // This field is needed to compute the correct exposure value. Without it, the exposure would have rounding errors. [SerializeField] private byte[] m_Color = new byte[4]; [SerializeField] private float[] m_ColorHdr = new float[4]; [SerializeField] private float[] m_Hsv = new float[3]; @@ -68,12 +68,21 @@ public float exposureValue return; m_ExposureValue = value; var newRgbFloat = m_HDRBaseColor * Mathf.Pow(2f, m_ExposureValue - m_BaseExposureValue); - m_ColorHdr[(int)RgbaChannel.R] = newRgbFloat.r; - m_ColorHdr[(int)RgbaChannel.G] = newRgbFloat.g; - m_ColorHdr[(int)RgbaChannel.B] = newRgbFloat.b; + m_ColorHdr[(int)RgbaChannel.R] = FloatClampSafe(newRgbFloat.r); + m_ColorHdr[(int)RgbaChannel.G] = FloatClampSafe(newRgbFloat.g); + m_ColorHdr[(int)RgbaChannel.B] = FloatClampSafe(newRgbFloat.b); } } + static float FloatClampSafe(float value) + { + if (float.IsPositiveInfinity(value) || float.IsNaN(value)) + return float.MaxValue; + if (float.IsNegativeInfinity(value)) + return float.MinValue; + return value; + } + public byte GetColorChannel(RgbaChannel channel) { return m_Color[(int)channel]; @@ -93,6 +102,7 @@ public void SetColorChannel(RgbaChannel channel, byte value) m_ColorHdr[channelIndex] = value / 255f; if (channel != RgbaChannel.A) m_ColorHdr[channelIndex] *= Mathf.Pow(2f, m_ExposureValue); + m_HDRBaseColor = new Color(m_ColorHdr[0], m_ColorHdr[1], m_ColorHdr[2], m_ColorHdr[3]); Color.RGBToHSV(color, out m_Hsv[(int)HsvChannel.H], out m_Hsv[(int)HsvChannel.S], out m_Hsv[(int)HsvChannel.V]); } diff --git a/Editor/Mono/GUI/ColorPicker.cs b/Editor/Mono/GUI/ColorPicker.cs index ff63053f6c..24664b9e8c 100644 --- a/Editor/Mono/GUI/ColorPicker.cs +++ b/Editor/Mono/GUI/ColorPicker.cs @@ -12,6 +12,7 @@ namespace UnityEditor internal class ColorPicker : EditorWindow { private const string k_HeightPrefKey = "CPickerHeight"; + private const string k_ShowDefaultsPrefKey = "CPDefaultsShow"; private const string k_ShowPresetsPrefKey = "CPPresetsShow"; // HDR and LDR have different slider mode pref keys because they have different defaults for the sake of discoverability private const string k_SliderModePrefKey = "CPSliderMode"; @@ -40,6 +41,8 @@ internal class ColorPicker : EditorWindow Texture2D m_ColorBox; static int s_Slider2Dhash = "Slider2D".GetHashCode(); [SerializeField] + bool m_ShowDefaults = true; + [SerializeField] bool m_ShowPresets = true; [SerializeField] @@ -482,6 +485,7 @@ static class Styles 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 ScalableGUIContent originalColorSwatchFill = new ScalableGUIContent(string.Empty, "The original color. Click this swatch to reset the color picker to this value.", "ColorPicker-OriginalColor"); @@ -877,6 +881,218 @@ void DoExposureSwatches() GUI.contentColor = contentColor; } + // used in TestColorPicker.OpeningColorPicker_InitialisesCorrectDefaultColors + internal static (Color, string)[] TESTdefaultColors => instance?.m_DefaultColors; + readonly (Color, string)[] m_DefaultColors = new (Color, string)[] + { + (Color.red, nameof(Color.red)), + (Color.green, nameof(Color.green)), + (Color.blue, nameof(Color.blue)), + (Color.yellow, nameof(Color.yellow)), + (Color.cyan, nameof(Color.cyan)), + (Color.magenta, nameof(Color.magenta)), + (Color.gray1, nameof(Color.gray1)), + (Color.gray2, nameof(Color.gray2)), + (Color.gray3, nameof(Color.gray3)), + (Color.gray4, nameof(Color.gray4)), + (Color.gray5, nameof(Color.gray5)), + (Color.gray6, nameof(Color.gray6)), + (Color.gray7, nameof(Color.gray7)), + (Color.gray8, nameof(Color.gray8)), + (Color.gray9, nameof(Color.gray9)), + (Color.white, nameof(Color.white)), + (Color.whiteSmoke, nameof(Color.whiteSmoke)), + (Color.gainsboro, nameof(Color.gainsboro)), + (Color.lightGray, nameof(Color.lightGray)), + (Color.silver, nameof(Color.silver)), + (Color.darkGray, nameof(Color.darkGray)), + (Color.dimGray, nameof(Color.dimGray)), + (Color.black, nameof(Color.black)), + (Color.darkRed, nameof(Color.darkRed)), + (Color.brown, nameof(Color.brown)), + (Color.firebrick, nameof(Color.firebrick)), + (Color.crimson, nameof(Color.crimson)), + (Color.softRed, nameof(Color.softRed)), + (Color.indianRed, nameof(Color.indianRed)), + (Color.violetRed, nameof(Color.violetRed)), + (Color.mediumVioletRed, nameof(Color.mediumVioletRed)), + (Color.deepPink, nameof(Color.deepPink)), + (Color.hotPink, nameof(Color.hotPink)), + (Color.lightPink, nameof(Color.lightPink)), + (Color.pink, nameof(Color.pink)), + (Color.paleVioletRed, nameof(Color.paleVioletRed)), + (Color.maroon, nameof(Color.maroon)), + (Color.rosyBrown, nameof(Color.rosyBrown)), + (Color.lightCoral, nameof(Color.lightCoral)), + (Color.salmon, nameof(Color.salmon)), + (Color.tomato, nameof(Color.tomato)), + (Color.darkSalmon, nameof(Color.darkSalmon)), + (Color.coral, nameof(Color.coral)), + (Color.orangeRed, nameof(Color.orangeRed)), + (Color.lightSalmon, nameof(Color.lightSalmon)), + (Color.sienna, nameof(Color.sienna)), + (Color.saddleBrown, nameof(Color.saddleBrown)), + (Color.chocolate, nameof(Color.chocolate)), + (Color.sandyBrown, nameof(Color.sandyBrown)), + (Color.peru, nameof(Color.peru)), + (Color.darkOrange, nameof(Color.darkOrange)), + (Color.burlywood, nameof(Color.burlywood)), + (Color.tan, nameof(Color.tan)), + (Color.moccasin, nameof(Color.moccasin)), + (Color.peachPuff, nameof(Color.peachPuff)), + (Color.bisque, nameof(Color.bisque)), + (Color.navajoWhite, nameof(Color.navajoWhite)), + (Color.wheat, nameof(Color.wheat)), + (Color.orange, nameof(Color.orange)), + (Color.darkGoldenRod, nameof(Color.darkGoldenRod)), + (Color.goldenRod, nameof(Color.goldenRod)), + (Color.lightGoldenRod, nameof(Color.lightGoldenRod)), + (Color.gold, nameof(Color.gold)), + (Color.softYellow, nameof(Color.softYellow)), + (Color.lightGoldenRodYellow, nameof(Color.lightGoldenRodYellow)), + (Color.beige, nameof(Color.beige)), + (Color.lemonChiffon, nameof(Color.lemonChiffon)), + (Color.lightYellow, nameof(Color.lightYellow)), + (Color.yellowNice, nameof(Color.yellowNice)), + (Color.khaki, nameof(Color.khaki)), + (Color.paleGoldenRod, nameof(Color.paleGoldenRod)), + (Color.darkKhaki, nameof(Color.darkKhaki)), + (Color.olive, nameof(Color.olive)), + (Color.oliveDrab, nameof(Color.oliveDrab)), + (Color.yellowGreen, nameof(Color.yellowGreen)), + (Color.darkOliveGreen, nameof(Color.darkOliveGreen)), + (Color.softGreen, nameof(Color.softGreen)), + (Color.greenYellow, nameof(Color.greenYellow)), + (Color.chartreuse, nameof(Color.chartreuse)), + (Color.lawnGreen, nameof(Color.lawnGreen)), + (Color.darkGreen, nameof(Color.darkGreen)), + (Color.forestGreen, nameof(Color.forestGreen)), + (Color.limeGreen, nameof(Color.limeGreen)), + (Color.darkSeaGreen, nameof(Color.darkSeaGreen)), + (Color.lightGreen, nameof(Color.lightGreen)), + (Color.paleGreen, nameof(Color.paleGreen)), + (Color.seaGreen, nameof(Color.seaGreen)), + (Color.mediumSeaGreen, nameof(Color.mediumSeaGreen)), + (Color.springGreen, nameof(Color.springGreen)), + (Color.mediumSpringGreen, nameof(Color.mediumSpringGreen)), + (Color.aquamarine, nameof(Color.aquamarine)), + (Color.mediumAquamarine, nameof(Color.mediumAquamarine)), + (Color.turquoise, nameof(Color.turquoise)), + (Color.mediumTurquoise, nameof(Color.mediumTurquoise)), + (Color.lightSeaGreen, nameof(Color.lightSeaGreen)), + (Color.lightSlateGray, nameof(Color.lightSlateGray)), + (Color.slateGray, nameof(Color.slateGray)), + (Color.darkSlateGray, nameof(Color.darkSlateGray)), + (Color.teal, nameof(Color.teal)), + (Color.darkCyan, nameof(Color.darkCyan)), + (Color.lightCyan, nameof(Color.lightCyan)), + (Color.mintCream, nameof(Color.mintCream)), + (Color.honeydew, nameof(Color.honeydew)), + (Color.azure, nameof(Color.azure)), + (Color.paleTurquoise, nameof(Color.paleTurquoise)), + (Color.darkTurquoise, nameof(Color.darkTurquoise)), + (Color.cadetBlue, nameof(Color.cadetBlue)), + (Color.powderBlue, nameof(Color.powderBlue)), + (Color.softBlue, nameof(Color.softBlue)), + (Color.lightBlue, nameof(Color.lightBlue)), + (Color.deepSkyBlue, nameof(Color.deepSkyBlue)), + (Color.skyBlue, nameof(Color.skyBlue)), + (Color.lightSkyBlue, nameof(Color.lightSkyBlue)), + (Color.steelBlue, nameof(Color.steelBlue)), + (Color.dodgerBlue, nameof(Color.dodgerBlue)), + (Color.lightSteelBlue, nameof(Color.lightSteelBlue)), + (Color.ghostWhite, nameof(Color.ghostWhite)), + (Color.aliceBlue, nameof(Color.aliceBlue)), + (Color.lavender, nameof(Color.lavender)), + (Color.cornflowerBlue, nameof(Color.cornflowerBlue)), + (Color.royalBlue, nameof(Color.royalBlue)), + (Color.navyBlue, nameof(Color.navyBlue)), + (Color.midnightBlue, nameof(Color.midnightBlue)), + (Color.darkBlue, nameof(Color.darkBlue)), + (Color.mediumBlue, nameof(Color.mediumBlue)), + (Color.slateBlue, nameof(Color.slateBlue)), + (Color.lightSlateBlue, nameof(Color.lightSlateBlue)), + (Color.mediumSlateBlue, nameof(Color.mediumSlateBlue)), + (Color.darkSlateBlue, nameof(Color.darkSlateBlue)), + (Color.mediumPurple, nameof(Color.mediumPurple)), + (Color.rebeccaPurple, nameof(Color.rebeccaPurple)), + (Color.blueViolet, nameof(Color.blueViolet)), + (Color.indigo, nameof(Color.indigo)), + (Color.purple, nameof(Color.purple)), + (Color.darkOrchid, nameof(Color.darkOrchid)), + (Color.darkViolet, nameof(Color.darkViolet)), + (Color.mediumOrchid, nameof(Color.mediumOrchid)), + (Color.darkMagenta, nameof(Color.darkMagenta)), + (Color.violet, nameof(Color.violet)), + (Color.plum, nameof(Color.plum)), + (Color.thistle, nameof(Color.thistle)), + (Color.orchid, nameof(Color.orchid)), + (Color.lavenderBlush, nameof(Color.lavenderBlush)), + (Color.seashell, nameof(Color.seashell)), + (Color.blanchedAlmond, nameof(Color.blanchedAlmond)), + (Color.papayaWhip, nameof(Color.papayaWhip)), + (Color.cornsilk, nameof(Color.cornsilk)), + (Color.ivory, nameof(Color.ivory)), + (Color.linen, nameof(Color.linen)), + (Color.floralWhite, nameof(Color.floralWhite)), + (Color.antiqueWhite, nameof(Color.antiqueWhite)), + (Color.oldLace, nameof(Color.oldLace)), + (Color.mistyRose, nameof(Color.mistyRose)), + (Color.snow, nameof(Color.snow)), + }; + + Texture2D m_SwatchTex; + void DoDefaultsGUI() + { + if (m_SwatchTex == null) m_SwatchTex = ColorPresetLibrary.CreateColorSwatchWithBorder(14, 14, false); + + m_ShowDefaults = EditorGUILayout.Foldout(m_ShowDefaults, Styles.defaultsToggle, true); + + if (m_ShowDefaults) + { + using (new EditorGUILayout.VerticalScope()) + { + EditorGUILayout.BeginHorizontal(); + for (int i = 0; i < m_DefaultColors.Length; ++i) + { + var (color, name) = m_DefaultColors[i]; + if (i != 0 && i % 15 == 0) + { + GUILayout.FlexibleSpace(); + EditorGUILayout.EndHorizontal(); + EditorGUILayout.BeginHorizontal(); + } + + using (new GUI.ColorScope(color)) + { + string tooltip; + if (m_SliderMode == SliderMode.HSV) + { + Color.RGBToHSV(color, out var h, out var s, out var v); + tooltip = $"{name} HSVA({Mathf.RoundToInt(h * 360)}, {Mathf.RoundToInt(s * 100)}, {Mathf.RoundToInt(v * 100)}, {Mathf.RoundToInt(color.a * 100)})"; + } + else if (m_SliderMode == SliderMode.RGB) + { + tooltip = $"{name} {(Color32)color}";; + } + else + tooltip = $"{name} {color}"; + + var clicked = GUILayout.Button( + new GUIContent(m_SwatchTex, tooltip), + GUIStyle.none, + new GUILayoutOption(GUILayoutOption.Type.fixedWidth, 14f)); + + if (clicked) + SetColor(color); + } + } + GUILayout.FlexibleSpace(); + EditorGUILayout.EndHorizontal(); + } + } + } + void DoPresetsGUI() { var foldoutRect = GUILayoutUtility.GetRect(Styles.presetsToggle, EditorStyles.foldout); @@ -953,6 +1169,8 @@ void OnGUI() GUILayout.Space(Styles.extraVerticalSpacing); } + DoDefaultsGUI(); + DoPresetsGUI(); // Call last to ensure we only use the copy paste events if no @@ -1177,11 +1395,13 @@ void OnEnable() m_SliderMode = (SliderMode)EditorPrefs.GetInt(k_SliderModePrefKey, (int)SliderMode.RGB); m_ShowPresets = EditorPrefs.GetInt(k_ShowPresetsPrefKey, 1) != 0; + m_ShowDefaults = EditorPrefs.GetInt(k_ShowDefaultsPrefKey, 1) != 0; } void OnDisable() { EditorPrefs.SetInt(m_HDR ? k_SliderModeHDRPrefKey : k_SliderModePrefKey, (int)m_SliderMode); + EditorPrefs.SetInt(k_ShowDefaultsPrefKey, m_ShowDefaults ? 1 : 0); EditorPrefs.SetInt(k_ShowPresetsPrefKey, m_ShowPresets ? 1 : 0); EditorPrefs.SetInt(k_HeightPrefKey, (int)position.height); } @@ -1208,6 +1428,8 @@ public void OnDestroy() DestroyImmediate(m_ValTexture); if (m_AlphaTexture) DestroyImmediate(m_AlphaTexture); + if (m_SwatchTex) + DestroyImmediate(m_SwatchTex); s_Instance = null; if (m_ColorLibraryEditorState != null) diff --git a/Editor/Mono/GUI/DockArea.cs b/Editor/Mono/GUI/DockArea.cs index fe953b3807..b228143302 100644 --- a/Editor/Mono/GUI/DockArea.cs +++ b/Editor/Mono/GUI/DockArea.cs @@ -1052,7 +1052,7 @@ private float DragTab(Rect tabAreaRect, float scrollOffset, GUIStyle tabStyle, G // Don't call OnFocus on the tab when it is moved to the new window EditorWindow.CreateNewWindowForEditorWindow(w, loadPosition: false, showImmediately: false, setFocus: false); - w.position = w.m_Parent.window.FitWindowRectToScreen(wPos, true, true); + w.position = ContainerWindow.FitRectToMouseScreen(wPos, true, w.m_Parent.window); Invoke("OnTabNewWindow", w); GUIUtility.hotControl = 0; diff --git a/Editor/Mono/GUI/GenericMenu.cs b/Editor/Mono/GUI/GenericMenu.cs index 9197de3ceb..4c3dabcc2a 100644 --- a/Editor/Mono/GUI/GenericMenu.cs +++ b/Editor/Mono/GUI/GenericMenu.cs @@ -90,7 +90,9 @@ public override string ToString() } } - // Show the menu under the mouse + /// + /// Show the menu under the mouse when used in an OnGUI callback. + /// public void ShowAsContext() { if (Event.current == null) @@ -98,13 +100,32 @@ public void ShowAsContext() DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); } + /// + /// Show the menu at the given rect relative to the current window in an OnGUI callback. + /// + /// public void DropDown(Rect position) { DropDown(position, false); } - // Show the menu at the given screen rect + /// + /// Show the menu at the given rect relative to the current window in an OnGUI callback. + /// + /// internal void DropDown(Rect position, bool shouldDiscardMenuOnSecondClick) + { + Vector2 temp = GUIUtility.GUIToScreenPoint(new Vector2(position.x, position.y)); + position.x = temp.x; + position.y = temp.y; + DropDownScreenSpace(position, shouldDiscardMenuOnSecondClick); + } + + /// + /// Show the menu at the given screen rect. + /// + /// + internal void DropDownScreenSpace(Rect position, bool shouldDiscardMenuOnSecondClick) { string[] titles = new string[m_MenuItems.Count]; bool[] enabled = new bool[m_MenuItems.Count]; @@ -120,8 +141,8 @@ internal void DropDown(Rect position, bool shouldDiscardMenuOnSecondClick) if (item.on) selected.Add(idx); } - - EditorUtility.DisplayCustomMenuWithSeparators(position, titles, enabled, separator, (int[])selected.ToArray(typeof(int)), CatchMenu, null, true, allowDuplicateNames, shouldDiscardMenuOnSecondClick); + + EditorUtility.DisplayCustomMenuWithSeparatorsWithScreenSpacePosition(position, titles, enabled, separator, (int[])selected.ToArray(typeof(int)), CatchMenu, null, true, allowDuplicateNames, shouldDiscardMenuOnSecondClick); } // Display as a popup with /selectedIndex/. How this behaves depends on the platform (on Mac, it'll try to scroll the menu to the right place) diff --git a/Editor/Mono/GUI/GradientEditor.cs b/Editor/Mono/GUI/GradientEditor.cs index 981dc0f236..790ea73264 100644 --- a/Editor/Mono/GUI/GradientEditor.cs +++ b/Editor/Mono/GUI/GradientEditor.cs @@ -87,6 +87,31 @@ public void Init(Gradient gradient, int numSteps, bool hdr, ColorSpace colorSpac m_SelectedSwatch = m_RGBSwatches[0]; } + /// + /// Called when the Gradient has changed and we need to update the swatches. + /// + public void RefreshGradientData() + { + // If we are already editing a Gradient we need to preserve the selected swatch or dragging may be corrupted. + int selectedSwatch = 0; + bool selectedIsAlpha = false; + if (m_SelectedSwatch != null) + { + selectedIsAlpha = m_SelectedSwatch.m_IsAlpha; + selectedSwatch = selectedIsAlpha ? m_AlphaSwatches.IndexOf(m_SelectedSwatch) : m_RGBSwatches.IndexOf(m_SelectedSwatch); + } + + BuildArrays(); + + var swatches = selectedIsAlpha ? m_AlphaSwatches : m_RGBSwatches; + if (swatches.Count > 0) + { + if (selectedSwatch > swatches.Count - 1 || selectedSwatch == -1) + selectedSwatch = 0; + m_SelectedSwatch = swatches[selectedSwatch]; + } + } + public Gradient target { get { return m_Gradient; } diff --git a/Editor/Mono/GUI/GradientPicker.cs b/Editor/Mono/GUI/GradientPicker.cs index 8cd744e784..3a064d470a 100644 --- a/Editor/Mono/GUI/GradientPicker.cs +++ b/Editor/Mono/GUI/GradientPicker.cs @@ -2,9 +2,8 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License -using UnityEngine; -using UnityEditor; using UnityEditorInternal; +using UnityEngine; namespace UnityEditor { @@ -267,14 +266,22 @@ public static void SetCurrentGradient(Gradient gradient) GUI.changed = true; } - public static void CloseWindow() + public static void RefreshGradientData() + { + s_GradientPicker?.m_GradientEditor?.RefreshGradientData(); + RepaintWindow(); + } + + public static void CloseWindow(bool throwExitGUI = true) { if (s_GradientPicker == null) return; s_GradientPicker.UnregisterEvents(); s_GradientPicker.Close(); - GUIUtility.ExitGUI(); + + if (throwExitGUI) + GUIUtility.ExitGUI(); } public static void RepaintWindow() diff --git a/Editor/Mono/GUI/InternalEditorGUI.cs b/Editor/Mono/GUI/InternalEditorGUI.cs index ffebb000a5..e0a3f62917 100644 --- a/Editor/Mono/GUI/InternalEditorGUI.cs +++ b/Editor/Mono/GUI/InternalEditorGUI.cs @@ -305,7 +305,7 @@ internal static void GameViewSizePopup(Rect buttonRect, GameViewSizeGroupType gr if (selectedIndex >= 0 && selectedIndex < group.GetTotalCount()) text = group.GetGameViewSize(selectedIndex).displayText; - if (EditorGUI.DropdownButton(buttonRect, GUIContent.Temp(text), FocusType.Passive, guiStyle)) + if (DropdownButton(buttonRect, GUIContent.Temp(text, GameView.Styles.k_AspectRatioTooltip), FocusType.Passive, guiStyle)) { var menuData = new GameViewSizesMenuItemProvider(groupType); var flexibleMenu = new GameViewSizeMenu(menuData, selectedIndex, new GameViewSizesMenuModifyItemUI(), gameView); diff --git a/Editor/Mono/GUI/MaskFieldGUI.cs b/Editor/Mono/GUI/MaskFieldGUI.cs index e107eeb810..83292e485b 100644 --- a/Editor/Mono/GUI/MaskFieldGUI.cs +++ b/Editor/Mono/GUI/MaskFieldGUI.cs @@ -162,7 +162,7 @@ internal static int DoMaskField(Rect position, int controlID, int mask, string[] { MaskCallbackInfo.m_Instance = new MaskCallbackInfo(controlID); MaskCallbackInfo.m_Instance.m_DropDown = new MaskFieldDropDown(optionNames, flagValues, optionMaskValues, mask, MaskCallbackInfo.m_Instance.SetMaskValueDelegate, autoSelectEverything); - PopupWindowWithoutFocus.Show(position, MaskCallbackInfo.m_Instance.m_DropDown); + PopupWindow.Show(position, MaskCallbackInfo.m_Instance.m_DropDown); } return mask; diff --git a/Editor/Mono/GUI/PopupLocationHelper.cs b/Editor/Mono/GUI/PopupLocationHelper.cs index 287f99c426..886d66d416 100644 --- a/Editor/Mono/GUI/PopupLocationHelper.cs +++ b/Editor/Mono/GUI/PopupLocationHelper.cs @@ -74,12 +74,9 @@ public static Rect GetDropDownRect(Rect buttonRect, Vector2 minSize, Vector2 max return GetLargestRect(croppedRects); } - private static Rect FitRect(Rect rect, ContainerWindow popupContainerWindow) + private static Rect FitRect(Rect rect, Vector2 uiPositionToSelectScreen, ContainerWindow popupContainerWindow) { - if (popupContainerWindow) - return popupContainerWindow.FitWindowRectToScreen(rect, true, true); - else - return ContainerWindow.FitRectToScreen(rect, true, true); + return ContainerWindow.FitRectToScreen(rect, uiPositionToSelectScreen, true, popupContainerWindow); } private static bool PopupRight(Rect buttonRect, Vector2 minSize, Vector2 maxSize, ContainerWindow popupContainerWindow, out Rect resultRect) @@ -90,12 +87,12 @@ private static bool PopupRight(Rect buttonRect, Vector2 minSize, Vector2 maxSize dropDownRectRight.xMax += spaceFromRight; dropDownRectRight.height += k_SpaceFromBottom; - dropDownRectRight = FitRect(dropDownRectRight, popupContainerWindow); + dropDownRectRight = FitRect(dropDownRectRight, buttonRect.center, popupContainerWindow); float availableWidthRight = Mathf.Max(dropDownRectRight.xMax - buttonRect.xMax - spaceFromRight, 0); float windowWidth = Mathf.Min(availableWidthRight, maxSize.x); resultRect = new Rect(dropDownRectRight.x, dropDownRectRight.y, windowWidth, dropDownRectRight.height - k_SpaceFromBottom); - if (availableWidthRight >= minSize.x) + if (Mathf.Ceil(availableWidthRight) >= minSize.x) return true; return false; } @@ -109,12 +106,12 @@ private static bool PopupLeft(Rect buttonRect, Vector2 minSize, Vector2 maxSize, dropDownRectLeft.xMin -= spaceFromLeft; dropDownRectLeft.height += k_SpaceFromBottom; - dropDownRectLeft = FitRect(dropDownRectLeft, popupContainerWindow); + dropDownRectLeft = FitRect(dropDownRectLeft, buttonRect.center, popupContainerWindow); float availableWidthLeft = Mathf.Max(buttonRect.x - dropDownRectLeft.x - spaceFromLeft, 0); float windowWidth = Mathf.Min(availableWidthLeft, maxSize.x); resultRect = new Rect(dropDownRectLeft.x, dropDownRectLeft.y, windowWidth, dropDownRectLeft.height - k_SpaceFromBottom); - if (availableWidthLeft >= minSize.x) + if (Mathf.Ceil(availableWidthLeft) >= minSize.x) return true; return false; } @@ -141,12 +138,12 @@ private static bool PopupAbove(Rect dropDownRectAbove, Rect buttonRect, Vector2 // Expand dropdown height to include empty space above dropDownRectAbove.yMin -= spaceFromTop; // Fit rect to screen - dropDownRectAbove = FitRect(dropDownRectAbove, popupContainerWindow); + dropDownRectAbove = FitRect(dropDownRectAbove, buttonRect.center, popupContainerWindow); // Calculate how much space is available for the window above the button float availableHeightAbove = Mathf.Max(buttonRect.y - dropDownRectAbove.y - spaceFromTop, 0); // If there's room for the window at its minimum size above the button, then place it there - if (availableHeightAbove >= minSize.y) + if (Mathf.Ceil(availableHeightAbove) >= minSize.y) { float windowHeight = Mathf.Min(availableHeightAbove, maxSize.y); { @@ -178,12 +175,12 @@ private static bool PopupBelow(Rect dropDownRectBelow, Rect buttonRect, Vector2 // Expand dropdown height to include empty space below dropDownRectBelow.height += k_SpaceFromBottom; // Fit rect to screen - dropDownRectBelow = FitRect(dropDownRectBelow, popupContainerWindow); + dropDownRectBelow = FitRect(dropDownRectBelow, buttonRect.center, popupContainerWindow); // Calculate how much space is available for the window below the button float availableHeightBelow = Mathf.Max(dropDownRectBelow.yMax - buttonRect.yMax - k_SpaceFromBottom, 0); // If there's room for the window at its minimum size below the button, then place it there - if (availableHeightBelow >= minSize.y) + if (Mathf.Ceil(availableHeightBelow) >= minSize.y) { float windowHeight = Mathf.Min(availableHeightBelow, maxSize.y); resultRect = new Rect(dropDownRectBelow.x, buttonRect.yMax, dropDownRectBelow.width, windowHeight); @@ -203,7 +200,7 @@ private static bool PopupOverlay(Rect buttonRect, Vector2 minSize, Vector2 maxSi { //Stretch the popup over the button Rect if it doesn't fit Rect dropDownRectBelow = new Rect(buttonRect.x, buttonRect.yMax, maxSize.x, maxSize.y); - resultRect = FitRect(dropDownRectBelow, popupContainerWindow); + resultRect = FitRect(dropDownRectBelow, buttonRect.center, popupContainerWindow); return true; } diff --git a/Editor/Mono/GUI/PopupWindow.cs b/Editor/Mono/GUI/PopupWindow.cs index 920167eeef..abad91694e 100644 --- a/Editor/Mono/GUI/PopupWindow.cs +++ b/Editor/Mono/GUI/PopupWindow.cs @@ -155,8 +155,8 @@ void SetWindowSize(Vector2 size) { m_LastWantedSize = size; Rect screenRect = m_Parent.window.GetDropDownRect(m_ActivatorRect, size, size, m_LocationPriorityOrder); - minSize = maxSize = new Vector2(screenRect.width, screenRect.height); position = screenRect; + minSize = maxSize = new Vector2(screenRect.width, screenRect.height); } } diff --git a/Editor/Mono/GUI/PopupWindowWithoutFocus.cs b/Editor/Mono/GUI/PopupWindowWithoutFocus.cs index 91bd085f06..31160dbaa2 100644 --- a/Editor/Mono/GUI/PopupWindowWithoutFocus.cs +++ b/Editor/Mono/GUI/PopupWindowWithoutFocus.cs @@ -28,7 +28,8 @@ class PopupWindowWithoutFocus : PopupWindow if (s_PopupWindowWithoutFocus != null) { - s_PopupWindowWithoutFocus.CloseContent(); + s_PopupWindowWithoutFocus.Close(); + s_PopupWindowWithoutFocus = null; } if (ShouldShowWindow(activatorRect)) diff --git a/Editor/Mono/GUI/SplitView.cs b/Editor/Mono/GUI/SplitView.cs index 74c2c2c93d..50f76f89b4 100644 --- a/Editor/Mono/GUI/SplitView.cs +++ b/Editor/Mono/GUI/SplitView.cs @@ -563,70 +563,69 @@ void CalcRoomForRect(Rect[] sources, Rect r) } } - /// clean up this view & propagate down + /// Clean up this view & propagate up. public void Cleanup() { - // if I'm a one-view splitview, I can propagate my child up and kill myself - SplitView sp = parent as SplitView; + var parentSplitView = parent as SplitView; - if (children.Length == 1 && sp != null) + // My parent is a split view, I am a split view with only one child. + // We move my child up to my parent and then destroy myself. + if (parentSplitView != null && children.Length == 1) { - View c = children[0]; - c.position = position; - if (parent != null) - { - parent.AddChild(c, parent.IndexOfChild(this)); - parent.RemoveChild(this); - if (sp) - sp.Cleanup(); - if (!Unsupported.IsDestroyScriptableObject(this)) - DestroyImmediate(this); - return; - } - else if (c is SplitView) - { - RemoveChild(c); - window.rootView = c; - c.position = new Rect(0, 0, c.window.position.width, window.position.height); - c.Reflow(); - if (!Unsupported.IsDestroyScriptableObject(this)) - DestroyImmediate(this); - return; - } + var child = children[0]; + parentSplitView.AddChild(child, parentSplitView.IndexOfChild(this)); + parentSplitView.RemoveChild(this); + child.position = position; + + if (!Unsupported.IsDestroyScriptableObject(this)) + DestroyImmediate(this); + parentSplitView.Cleanup(); // Propagate the clean up. + + return; } - if (sp != null) + if (parentSplitView != null) { - sp.Cleanup(); - // the parent might have moved US up and gotten rid of itself - sp = parent as SplitView; - - if (sp) + parentSplitView.Cleanup(); + // The parent might have moved US up and gotten rid of itself. Because we are modifying the + // parentSplitView recursively, it might points to a split view that's already been destroyed. + parentSplitView = parent as SplitView; + + // If both my parent and I are split views with same orientation, we can move our views up and + // destroy ourselves. In the example as shown below, we want to move scene and game to the parent + // and then destroy ourselves, to avoid unnecessary nesting. + // + // SplitView (horizontal) -- my parent + // |_____SplitView (horizontal) -- myself + // |______Scene -- my child + // |______Game -- my child + if (parentSplitView != null && parentSplitView.vertical == vertical) { - // If the parent has the same orientation as us, we can move our views up and kill ourselves - if (sp.vertical == vertical) + var myChildren = new List(children); + var idx = parent.IndexOfChild(this); + + foreach (var child in myChildren) { - int idx = new List(parent.children).IndexOf(this); - foreach (View child in children) - { - sp.AddChild(child, idx++); - child.position = new Rect(position.x + child.position.x, position.y + child.position.y, child.position.width, child.position.height); - } + RemoveChild(child); + parentSplitView.AddChild(child, idx++); + child.position = new Rect(position.x + child.position.x, position.y + child.position.y, + child.position.width, child.position.height); + } - // don't let this fall through to the `children == 0` case because we don't want to be removed - // "nicely." our children have already been merged to the parent with correct positions, so - // there is no need to recalculate sibling dimensions (and may incorrectly resize views that - // have been recursively cleaned up). - sp.RemoveChild(this); - if (!Unsupported.IsDestroyScriptableObject(this)) - DestroyImmediate(this, true); - sp.Cleanup(); + // Don't let this fall through to the `children == 0` case because we don't want to be removed + // "nicely." our children have already been merged to the parent with correct positions, so + // there is no need to recalculate sibling dimensions (and may incorrectly resize views that + // have been recursively cleaned up). + parentSplitView.RemoveChild(this); + if (!Unsupported.IsDestroyScriptableObject(this)) + DestroyImmediate(this, true); + parentSplitView.Cleanup(); - return; - } + return; } } + // I am a split view with no children, I need to destroy myself, :( if (children.Length == 0) { if (parent == null && window != null) @@ -636,10 +635,10 @@ public void Cleanup() } else { - ICleanuppable ic = parent as ICleanuppable; - if (parent is SplitView) + var ic = parent as ICleanuppable; + if (parent is SplitView parentSplitVIew) { - ((SplitView)parent).RemoveChildNice(this); + parentSplitVIew.RemoveChildNice(this); if (!Unsupported.IsDestroyScriptableObject(this)) DestroyImmediate(this, true); } diff --git a/Editor/Mono/GUI/Toolbars/EditorToolbarUtility.cs b/Editor/Mono/GUI/Toolbars/EditorToolbarUtility.cs index 1295421de2..c016a53685 100644 --- a/Editor/Mono/GUI/Toolbars/EditorToolbarUtility.cs +++ b/Editor/Mono/GUI/Toolbars/EditorToolbarUtility.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using System.Linq; +using UnityEditor.Overlays; +using UnityEngine; using UnityEngine.UIElements; namespace UnityEditor.Toolbars @@ -73,5 +75,67 @@ internal static VisualTreeAsset LoadUxml(string name) { return EditorGUIUtility.Load($"{k_UxmlPath}{name}.uxml") as VisualTreeAsset; } + + internal static void UpdateIconContent( + string text, + string textIcon, + Texture2D icon, + TextElement textElement, + TextElement textIconElement, + Image iconElement) + { + if (text == string.Empty) + { + textElement.style.display = DisplayStyle.None; + textIconElement.style.display = DisplayStyle.None; + } + else + textElement.style.display = StyleKeyword.Null; + + // First priority: image icon, if available + if (icon != null) + { + if (iconElement != null) + { + iconElement.style.display = DisplayStyle.Flex; + iconElement.image = icon; + } + if (textIconElement != null) + textIconElement.style.display = DisplayStyle.None; + } + else if (iconElement != null && iconElement.resolvedStyle.backgroundImage != null) + { + if (textIconElement != null) + textIconElement.style.display = DisplayStyle.None; + iconElement.style.display = DisplayStyle.Flex; + } + // Second priority: text icon, if available + else if (!string.IsNullOrEmpty(textIcon)) + { + if (textIconElement != null) + { + textIconElement.style.display = DisplayStyle.Flex; + textIconElement.text = OverlayUtilities.GetSignificantLettersForIcon(textIcon); + } + if (iconElement != null) + iconElement.style.display = DisplayStyle.None; + } + // Fall back: abbreviation of text. + else if (!string.IsNullOrEmpty(text)) + { + if (textIconElement != null) + { + textIconElement.style.display = StyleKeyword.Null; + textIconElement.text = OverlayUtilities.GetSignificantLettersForIcon(text); + } + if (iconElement != null) + iconElement.style.display = DisplayStyle.None; + } + else + { + if (iconElement != null) + iconElement.style.display = DisplayStyle.Flex; + } + } } } diff --git a/Editor/Mono/GUI/TreeView/TreeViewGUI.cs b/Editor/Mono/GUI/TreeView/TreeViewGUI.cs index a784cfdbae..007ef5531c 100644 --- a/Editor/Mono/GUI/TreeView/TreeViewGUI.cs +++ b/Editor/Mono/GUI/TreeView/TreeViewGUI.cs @@ -623,10 +623,7 @@ virtual public void DoRenameOverlay() { if (GetRenameOverlay().IsRenaming()) if (!GetRenameOverlay().OnGUI(renameStyle)) - { EndRename(); - GUIUtility.ExitGUI(); - } } virtual protected void SyncFakeItem() {} diff --git a/Editor/Mono/GUI/WindowLayout.cs b/Editor/Mono/GUI/WindowLayout.cs index 432ec6723f..3aeaed6d14 100644 --- a/Editor/Mono/GUI/WindowLayout.cs +++ b/Editor/Mono/GUI/WindowLayout.cs @@ -13,6 +13,7 @@ using UnityEditor.VersionControl; using UnityEditorInternal; using UnityEngine; +using UnityEngine.Pool; using UnityEngine.Scripting; using Directory = System.IO.Directory; using UnityObject = UnityEngine.Object; @@ -444,22 +445,22 @@ private static void LoadLastUsedLayoutForCurrentMode(bool keepMainWindow) { // steps 1-4 foreach (var layout in GetLastLayout()) - if (LoadWindowLayout(layout, layout != ProjectLayoutPath, false, keepMainWindow, false)) + if (LoadWindowLayout_Internal(layout, layout != ProjectLayoutPath, false, keepMainWindow, false)) return; // step 5 foreach (var layout in GetCurrentModeLayouts()) - if (LoadWindowLayout(layout, layout != ProjectLayoutPath, false, keepMainWindow, false)) + if (LoadWindowLayout_Internal(layout, layout != ProjectLayoutPath, false, keepMainWindow, false)) return; // It is not mandatory that modes define a layout. In that case, skip right to the default layout. if (!string.IsNullOrEmpty(ModeService.GetDefaultModeLayout()) - && LoadWindowLayout(ModeService.GetDefaultModeLayout(), true, false, keepMainWindow, false)) + && LoadWindowLayout_Internal(ModeService.GetDefaultModeLayout(), true, false, keepMainWindow, false)) return; // If all else fails, load the default layout that ships with the editor. If that fails, prompt the user to // restore the default layouts. - if (!LoadWindowLayout(GetDefaultLayoutPath(), true, false, keepMainWindow, false)) + if (!LoadWindowLayout_Internal(GetDefaultLayoutPath(), true, false, keepMainWindow, false)) { int option = 0; @@ -640,7 +641,7 @@ internal static void ReloadWindowLayoutMenu() foreach (var layoutPath in layoutPaths) { var name = Path.GetFileNameWithoutExtension(layoutPath); - Menu.AddMenuItem("Window/Layouts/" + name, "", false, layoutMenuItemPriority++, () => LoadWindowLayout(layoutPath, false, true, true, true), null); + Menu.AddMenuItem("Window/Layouts/" + name, "", false, layoutMenuItemPriority++, () => TryLoadWindowLayout(layoutPath, false, true, true, true), null); } layoutMenuItemPriority += 500; @@ -681,14 +682,18 @@ private static void AddLegacyLayoutMenuItems(ref int layoutMenuItemPriority) const string legacyRootMenu = "Window/Layouts/Other Versions"; const string legacyCurrentLayoutPath = "Library/CurrentLayout-default.dwlt"; if (File.Exists(legacyCurrentLayoutPath)) - Menu.AddMenuItem($"{legacyRootMenu}/Default (2020)", "", false, layoutMenuItemPriority++, () => LoadWindowLayout(legacyCurrentLayoutPath, false, true, false, true), null); + Menu.AddMenuItem($"{legacyRootMenu}/Default (2020)", "", false, layoutMenuItemPriority++, () => TryLoadWindowLayout(legacyCurrentLayoutPath, false, true, false, true), null); if (!Directory.Exists(layoutsProjectPath)) return; + var currentMaximizeLayoutPath = FileUtil.CombinePaths(layoutsProjectPath, kMaximizeRestoreFile); + using var pooled = HashSetPool.Get(out var forbiddenPaths); + forbiddenPaths.Add(GetCurrentLayoutPath()); + forbiddenPaths.Add(currentMaximizeLayoutPath); foreach (var layoutPath in Directory.GetFiles(layoutsProjectPath, "*.dwlt")) { - if (layoutPath == GetCurrentLayoutPath()) + if (forbiddenPaths.Contains(layoutPath)) continue; var name = Path.GetFileNameWithoutExtension(layoutPath); var names = Path.GetFileName(name).Split('-'); @@ -698,7 +703,7 @@ private static void AddLegacyLayoutMenuItems(ref int layoutMenuItemPriority) name = ObjectNames.NicifyVariableName(names[0]); menuName = $"{legacyRootMenu}/{name} ({names[1]})"; } - Menu.AddMenuItem(menuName, "", false, layoutMenuItemPriority++, () => LoadWindowLayout(layoutPath, false, true, false, true), null); + Menu.AddMenuItem(menuName, "", false, layoutMenuItemPriority++, () => TryLoadWindowLayout(layoutPath, false, true, false, true), null); } } @@ -1230,7 +1235,21 @@ static void DeleteWindowLayoutImpl(string name, string path) // Attempts to load a layout. If unsuccessful, restores the previous layout. public static bool TryLoadWindowLayout(string path, bool newProjectLayoutWasCreated) { - if (LoadWindowLayout(path, newProjectLayoutWasCreated, true, false, true)) + var flags = GetLoadWindowLayoutFlags(newProjectLayoutWasCreated, true, false, true); + return TryLoadWindowLayout(path, flags); + } + + // Attempts to load a layout. If unsuccessful, restores the previous layout. + public static bool TryLoadWindowLayout(string path, bool newProjectLayoutWasCreated, bool setLastLoadedLayoutName, bool keepMainWindow, bool logErrorsToConsole) + { + var flags = GetLoadWindowLayoutFlags(newProjectLayoutWasCreated, setLastLoadedLayoutName, keepMainWindow, logErrorsToConsole); + return TryLoadWindowLayout(path, flags); + } + + // Attempts to load a layout. If unsuccessful, restores the previous layout. + public static bool TryLoadWindowLayout(string path, LoadWindowLayoutFlags flags) + { + if (LoadWindowLayout_Internal(path, flags)) return true; LoadCurrentModeLayout(FindMainWindow()); return false; @@ -1248,7 +1267,31 @@ public enum LoadWindowLayoutFlags SaveLayoutPreferences = 1 << 5 } + // This method is public only because some packages have internal access and use it. + [Obsolete("Do not use this method. Use TryLoadWindowLayout instead.")] public static bool LoadWindowLayout(string path, bool newProjectLayoutWasCreated, bool setLastLoadedLayoutName, bool keepMainWindow, bool logErrorsToConsole) + { + return TryLoadWindowLayout(path, newProjectLayoutWasCreated, setLastLoadedLayoutName, keepMainWindow, logErrorsToConsole); + } + + // This method is public only because some packages have internal access and use it. + [Obsolete("Do not use this method. Use TryLoadWindowLayout instead.")] + public static bool LoadWindowLayout(string path, LoadWindowLayoutFlags flags) + { + return TryLoadWindowLayout(path, flags); + } + + // IMPORTANT: Do not expose this method outside WindowLayout. Do not use this method as the entry point to load a layout. + // Use TryLoadWindowLayout instead. It can put us in a state that is unrecoverable where all windows are gone but Unity + // still lives as a background process. Only use this method from another method that handles the recovery process in case + // of failure. + static bool LoadWindowLayout_Internal(string path, bool newProjectLayoutWasCreated, bool setLastLoadedLayoutName, bool keepMainWindow, bool logErrorsToConsole) + { + var flags = GetLoadWindowLayoutFlags(newProjectLayoutWasCreated, setLastLoadedLayoutName, keepMainWindow, logErrorsToConsole); + return LoadWindowLayout_Internal(path, flags); + } + + static LoadWindowLayoutFlags GetLoadWindowLayoutFlags(bool newProjectLayoutWasCreated, bool setLastLoadedLayoutName, bool keepMainWindow, bool logErrorsToConsole) { var flags = LoadWindowLayoutFlags.SaveLayoutPreferences; if (newProjectLayoutWasCreated) @@ -1259,11 +1302,14 @@ public static bool LoadWindowLayout(string path, bool newProjectLayoutWasCreated flags |= LoadWindowLayoutFlags.KeepMainWindow; if (logErrorsToConsole) flags |= LoadWindowLayoutFlags.LogsErrorToConsole; - - return LoadWindowLayout(path, flags); + return flags; } - public static bool LoadWindowLayout(string path, LoadWindowLayoutFlags flags) + // IMPORTANT: Do not expose this method outside WindowLayout. Do not use this method as the entry point to load a layout. + // Use TryLoadWindowLayout instead. It can put us in a state that is unrecoverable where all windows are gone but Unity + // still lives as a background process. Only use this method from another method that handles the recovery process in case + // of failure. + static bool LoadWindowLayout_Internal(string path, LoadWindowLayoutFlags flags) { Console.WriteLine($"[LAYOUT] About to load {path}, keepMainWindow={flags.HasFlag(LoadWindowLayoutFlags.KeepMainWindow)}"); @@ -1393,6 +1439,15 @@ public static bool LoadWindowLayout(string path, LoadWindowLayoutFlags flags) // get their parent/owner. if (mainWindow) { + // If at this point the mainWindow still doesn't have a rootView, it probably means we loaded a layout without a main window. + // Delete the current mainWindow so that we can load back the previous layout otherwise every attemp at loading a layout will fail. + if (mainWindow.rootView == null || !mainWindow.rootView) + { + mainWindow.Close(); + UnityObject.DestroyImmediate(mainWindow, true); + throw new LayoutException("Error while reading window layout: no root view on main window."); + } + // Don't adjust height to prevent main window shrink during layout on Linux. mainWindow.SetFreeze(true); mainWindow.Show(mainWindow.showMode, loadPosition: true, displayImmediately: true, setFocus: true); @@ -1479,7 +1534,7 @@ internal static void LoadDefaultLayout() } Debug.Assert(File.Exists(ProjectLayoutPath)); - LoadWindowLayout(ProjectLayoutPath, true, true, false, false); + LoadWindowLayout_Internal(ProjectLayoutPath, true, true, false, false); } public static void CloseWindows() diff --git a/Editor/Mono/GameView/GameView.cs b/Editor/Mono/GameView/GameView.cs index f2048b951e..30643f56fc 100644 --- a/Editor/Mono/GameView/GameView.cs +++ b/Editor/Mono/GameView/GameView.cs @@ -100,9 +100,8 @@ float maxScale internal static class Styles { - public static GUIContent gizmosContent = EditorGUIUtility.TrTextContent("Gizmos"); + public static GUIContent gizmosContent = EditorGUIUtility.TrTextContent("Gizmos", "Activate / Deactivate gizmos in the GameView"); public static GUIContent zoomSliderContent = EditorGUIUtility.TrTextContent("Scale", "Size of the game view on the screen."); - public static GUIContent vsyncContent = EditorGUIUtility.TrTextContent("VSync"); public static GUIContent muteOffContent = EditorGUIUtility.TrIconContent("GameViewAudio On", "Mute Audio"); public static GUIContent muteOnContent = EditorGUIUtility.TrIconContent("GameViewAudio", "Mute Audio"); public static GUIContent shortcutsOnContent = EditorGUIUtility.TrIconContent("Keyboard", "Unity Shortcuts"); @@ -117,6 +116,7 @@ internal static class Styles public const string k_StatsShortcutID = "Game View/Toggle Stats"; public const string k_StatsTooltip = "View general rendering information"; + public const string k_AspectRatioTooltip = "Change aspect ratios, emulate low pixel density, allow VSync"; public static GUIContent renderdocContent; public static GUIStyle gameViewBackgroundStyle; diff --git a/Editor/Mono/Handles/Handles.cs b/Editor/Mono/Handles/Handles.cs index 7c59797439..0cd53ea041 100644 --- a/Editor/Mono/Handles/Handles.cs +++ b/Editor/Mono/Handles/Handles.cs @@ -242,6 +242,12 @@ static Vector3 GetAxisVector(int axis) internal static bool IsHovering(int controlID, Event evt) { + // If the mouse position is over an overlay, return false since we don't want to indicate interactivity with any handles. + if (SceneView.lastActiveSceneView == null || SceneView.lastActiveSceneView.sceneViewMotion == null) + return false; + if (!SceneView.lastActiveSceneView.sceneViewMotion.viewportsUnderMouse) + return false; + return controlID == HandleUtility.nearestControl && GUIUtility.hotControl == 0 && !Tools.viewToolActive; } diff --git a/Editor/Mono/Help.cs b/Editor/Mono/Help.cs index ff333db143..100c7e6dc8 100644 --- a/Editor/Mono/Help.cs +++ b/Editor/Mono/Help.cs @@ -336,6 +336,8 @@ internal static string HelpFileNameForObject(Object obj) { Terrain => "script-Terrain", AudioMixerController or AudioMixerGroupController => "class-AudioMixer", + AudioImporter => "class-AudioClip", // UUM-96832: We don't have an entry in the manual for the audio importer. + VideoClipImporter => "class-VideoClip", // We don't have an entry in the manual for the video clip importer. EditorSettings => "class-EditorManager", SceneAsset => "CreatingScenes", LightingDataAsset => "LightmapSnapshot", @@ -385,6 +387,12 @@ internal static string GetReleaseNotesUrl(string digitsOnlyVersion, string displ return url; } + internal static string GetShortReleaseVersion() + { + var version = InternalEditorUtility.GetUnityVersion(); + return $"{version.Major}.{version.Minor}"; + } + private static void InitDocumentation() { ClearCache(); diff --git a/Editor/Mono/IBuildTarget.cs b/Editor/Mono/IBuildTarget.cs index 09183a6ad4..3d96c4f757 100644 --- a/Editor/Mono/IBuildTarget.cs +++ b/Editor/Mono/IBuildTarget.cs @@ -47,6 +47,10 @@ internal interface IBuildTarget // This function allows to retrieve AudioPlatformProperties derived from IPlatformProperties as quick access IVRPlatformProperties VRPlatformProperties { get; } + // This function retrieves the texture subtarget derived from IPlatformProperties. It should be the same as + // the native subtarget used for each build target with a subtarget related to texture. Note this is different + // from a StandaloneSubtarget. + ISubtargetPlatformProperties TextureSubtargetPlatformProperties { get; } // This function allows to retrieve properties of a give type, derived from IPlatformProperties // if they are available. diff --git a/Editor/Mono/ISubtargetPlatformProperties.cs b/Editor/Mono/ISubtargetPlatformProperties.cs new file mode 100644 index 0000000000..ab7099fbcd --- /dev/null +++ b/Editor/Mono/ISubtargetPlatformProperties.cs @@ -0,0 +1,12 @@ +// 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.Profile; + +namespace UnityEditor; + +internal interface ISubtargetPlatformProperties : IPlatformProperties +{ + int GetSubtargetFromPlatformSettings(BuildProfilePlatformSettingsBase platformSettings = null) => -1; +} diff --git a/Editor/Mono/Inspector/BlendTreeInspector.cs b/Editor/Mono/Inspector/BlendTreeInspector.cs index 35e92099ae..093c9cde8e 100644 --- a/Editor/Mono/Inspector/BlendTreeInspector.cs +++ b/Editor/Mono/Inspector/BlendTreeInspector.cs @@ -399,6 +399,25 @@ private void SetMinMaxThresholds() m_MaxThreshold.floatValue = m_Childs.arraySize > 0 ? max : 1; } + private void BoundBlendParameter(string paramName) + { + // Ensures that the blend parameter is bounded between the root blend tree min and max + if (parentBlendTree != null) + { + for (int i = 0; i < parentBlendTree.recursiveBlendParameterCount; ++i) + { + if (parentBlendTree.GetRecursiveBlendParameter(i) != paramName) + continue; + + float value = GetParameterValue(currentAnimator, m_BlendTree, paramName); + float min = parentBlendTree.GetRecursiveBlendParameterMin(i); + float max = parentBlendTree.GetRecursiveBlendParameterMax(i); + value = Mathf.Clamp(value, min, max); + SetParameterValue(currentAnimator, m_BlendTree, parentBlendTree, paramName, value); + } + } + } + private void ThresholdValues() { Rect r = EditorGUILayout.GetControlRect(); @@ -1318,9 +1337,6 @@ public void RemoveButton(UnityEditorInternal.ReorderableList list) SetMinMaxThresholds(); serializedObject.ApplyModifiedProperties(); - - // Layout has changed, bail out now. - EditorGUIUtility.ExitGUI(); } } @@ -1409,6 +1425,7 @@ public void DrawChild(Rect r, int index, bool isActive, bool isFocused) serializedObject.ApplyModifiedProperties(); m_BlendTree.SortChildren(); SetMinMaxThresholds(); + BoundBlendParameter(m_BlendTree.blendParameter); GUI.changed = true; } } @@ -1445,6 +1462,7 @@ public void DrawChild(Rect r, int index, bool isActive, bool isFocused) pos[i] = Mathf.Clamp(coord, -10000, 10000); position.vector2Value = pos; serializedObject.ApplyModifiedProperties(); + BoundBlendParameter(i == 0 ? m_BlendTree.blendParameter : m_BlendTree.blendParameterY); GUI.changed = true; } } diff --git a/Editor/Mono/Inspector/CameraEditorUtils.cs b/Editor/Mono/Inspector/CameraEditorUtils.cs index f0bb68f270..8ea04089a6 100644 --- a/Editor/Mono/Inspector/CameraEditorUtils.cs +++ b/Editor/Mono/Inspector/CameraEditorUtils.cs @@ -388,10 +388,12 @@ public void Dispose() static RenderTexture GetPreviewTexture(int width, int height, bool hdr) { if (s_PreviewTexture != null - && (s_PreviewTexture.width != width || s_PreviewTexture.height != height - || (GraphicsSettings.currentRenderPipeline == null && s_PreviewTexture.antiAliasing != QualitySettings.antiAliasing))) + && (s_PreviewTexture.width != width || s_PreviewTexture.height != height)) { s_PreviewTexture.Release(); + + s_PreviewTexture.width = width; + s_PreviewTexture.height = height; } if (s_PreviewTexture == null) @@ -402,7 +404,7 @@ static RenderTexture GetPreviewTexture(int width, int height, bool hdr) if (GraphicsSettings.currentRenderPipeline == null) { - // Built-in Render Pipeline, insure that antiAliasing is set to 1 or more + // Built-in Render Pipeline, ensure that antiAliasing is set to 1 or more s_PreviewTexture.antiAliasing = Math.Max(1, QualitySettings.antiAliasing); } else @@ -496,6 +498,10 @@ static RenderTexture RenderInternal(Camera cameraToRender, PreviewSettings setti RenderTexture.active = oldRt; } + // Honor async shader compilation editor settings for this preview + bool oldShaderAsyncState = ShaderUtil.allowAsyncCompilation; + ShaderUtil.allowAsyncCompilation = EditorSettings.asyncShaderCompilation; + using (new SavedStateForCameraPreview(cameraToRender)) { // make sure the preview camera is rendering the same stage as the SceneView is @@ -520,6 +526,8 @@ static RenderTexture RenderInternal(Camera cameraToRender, PreviewSettings setti previewCamera.Render(); } } + + ShaderUtil.allowAsyncCompilation = oldShaderAsyncState; return rt; } } diff --git a/Editor/Mono/Inspector/CameraOverlay.cs b/Editor/Mono/Inspector/CameraOverlay.cs index 61397c9c1b..44c0ce148d 100644 --- a/Editor/Mono/Inspector/CameraOverlay.cs +++ b/Editor/Mono/Inspector/CameraOverlay.cs @@ -13,7 +13,7 @@ namespace UnityEditor { // marked obsolete @thomastu 2023/04/05 [Obsolete("This Overlay is obsolete. Use the global Cameras Overlay instead.", false)] - [Overlay(id = k_OverlayID, displayName = k_DisplayName, defaultDisplay = true)] + [Overlay(id = k_OverlayID, displayName = k_DisplayName, defaultDisplay = false)] [Icon("Icons/Overlays/CameraPreview.png")] class SceneViewCameraOverlay : IMGUIOverlay { diff --git a/Editor/Mono/Inspector/ClothInspector.cs b/Editor/Mono/Inspector/ClothInspector.cs index 39e3571781..f20baf9075 100644 --- a/Editor/Mono/Inspector/ClothInspector.cs +++ b/Editor/Mono/Inspector/ClothInspector.cs @@ -1944,7 +1944,7 @@ void SelfAndInterCollisionEditing() GUILayout.EndVertical(); } - [Overlay(typeof(SceneView), "Scene View/Cloth Constraints", "Cloth Constraints", "unity-sceneview-clothconstraints", priority = (int)OverlayPriority.ClothCollisions)] + [Overlay(typeof(SceneView), "Scene View/Cloth Constraints", "Cloth Constraints", "unity-sceneview-clothconstraints", priority = (int)OverlayPriority.ClothCollisions, defaultDisplay = false, defaultDockIndex = 0)] [Icon("Icons/editconstraints_16.png")] class SceneViewClothConstraintsOverlay : TransientSceneViewOverlay { @@ -1960,7 +1960,7 @@ public override void OnGUI() } } - [Overlay(typeof(SceneView), "Scene View/Cloth Collisions", "Cloth Self-Collision and Inter-Collision", "unity-sceneview-clothcollision", priority = (int)OverlayPriority.ClothCollisions)] + [Overlay(typeof(SceneView), "Scene View/Cloth Collisions", "Cloth Self-Collision and Inter-Collision", "unity-sceneview-clothcollision", priority = (int)OverlayPriority.ClothCollisions, defaultDisplay = false, defaultDockIndex = 0)] [Icon("Icons/editCollision_16.png")] class SceneViewClothCollisionsOverlay : TransientSceneViewOverlay { diff --git a/Editor/Mono/Inspector/Core/AddComponent/AddComponentGUI.cs b/Editor/Mono/Inspector/Core/AddComponent/AddComponentGUI.cs index b143411312..b82002d7c6 100644 --- a/Editor/Mono/Inspector/Core/AddComponent/AddComponentGUI.cs +++ b/Editor/Mono/Inspector/Core/AddComponent/AddComponentGUI.cs @@ -13,17 +13,11 @@ internal class AddComponentGUI : AdvancedDropdownGUI private static class Styles { public static GUIStyle itemStyle = "DD LargeItemStyle"; - internal static GUIStyle lineStyleFaint = new GUIStyle("DD LargeItemStyle"); - static Styles() - { - float val = EditorGUIUtility.isProSkin ? 0.5f : 0.25f; - var color = new Color(val, val, val, 1f); - lineStyleFaint.active.textColor = color; - lineStyleFaint.focused.textColor = color; - lineStyleFaint.hover.textColor = color; - lineStyleFaint.normal.textColor = color; - } + const string k_includeNamespaceProSkin = "{0} ({1})"; + const string k_includeNamespace = "{0} ({1})"; + + public static string includeNamespaceString => EditorGUIUtility.isProSkin ? k_includeNamespaceProSkin : k_includeNamespace; } private Vector2 m_IconSize = new Vector2(16, 16); @@ -39,22 +33,17 @@ public AddComponentGUI(AdvancedDropdownDataSource dataSource, Action GetInspectors() [UsedByNativeCode] internal static void RedrawFromNative() { - var propertyEditors = Resources.FindObjectsOfTypeAll(); - foreach (var propertyEditor in propertyEditors) - propertyEditor.RebuildContentsContainers(); + // This method could be called before `OnEnable` is being called on an editor window. + // Therefore it is important to make sure your editor window is enabled/active/alive first, + // which means it have to be contained in `activeEditorWindows` list. + foreach (var editorWindow in activeEditorWindows) + { + if (editorWindow is PropertyEditor propertyEditor) + propertyEditor.RebuildContentsContainers(); + } } internal static InspectorWindow[] GetAllInspectorWindows() @@ -330,6 +340,9 @@ protected override bool BeginDrawPreviewAndLabels() return m_PreviewWindow == null; } + /// + /// Finalize IMGUI based inspectors. + /// protected override void EndDrawPreviewAndLabels(Event evt, Rect rect, Rect dragRect) { if (m_HasPreview || m_PreviewWindow != null) @@ -345,13 +358,11 @@ protected override void EndDrawPreviewAndLabels(Event evt, Rect rect, Rect dragR { if (m_PreviewWindow == null) { - hasFloatingPreviewWindow = true; - DetachPreview(false); + PopupPreviewWindow(exitGUI: false); } else { - m_PreviewWindow.Close(); - hasFloatingPreviewWindow = false; + DockPreviewWindow(); } }); menu.AddItem( @@ -370,9 +381,12 @@ protected override void EndDrawPreviewAndLabels(Event evt, Rect rect, Rect dragR // Detach preview on right click in preview title bar if (evt.type == EventType.MouseUp && evt.button == 1 && rect.Contains(evt.mousePosition) && m_PreviewWindow == null) - DetachPreview(); + PopupPreviewWindow(exitGUI: true); } + /// + /// Creates the ellipsis menu for UITK based inspectors. + /// protected override void CreatePreviewEllipsisMenu() { if (previewWindow == null) @@ -395,14 +409,13 @@ protected override void CreatePreviewEllipsisMenu() } else { - hasFloatingPreviewWindow = false; - m_PreviewWindow.Close(); + DockPreviewWindow(); previewContainer.style.display = DisplayStyle.Flex; draglineAnchor.style.display = DisplayStyle.Flex; } }, - a => hasFloatingPreviewWindow ? DropdownMenuAction.Status.Checked : DropdownMenuAction.Status.Normal); + a => DropdownMenuAction.Status.Normal); previewWindow.AppendActionToEllipsisMenu( "Minimize in Inspector", @@ -424,13 +437,17 @@ void OnDraglineChange(PointerUpEvent evt) PopupPreviewWindow(); } - void PopupPreviewWindow() + void PopupPreviewWindow(bool exitGUI = false) { - DetachPreview(false); - hasFloatingPreviewWindow = true; + DetachPreview(exitGUI); previewWindow.parent?.Remove(previewWindow); } + void DockPreviewWindow() + { + m_PreviewWindow?.Close(); + } + private void DetachPreview(bool exitGUI = true) { if (Event.current != null) diff --git a/Editor/Mono/Inspector/Core/PreviewWindow.cs b/Editor/Mono/Inspector/Core/PreviewWindow.cs index 3b707fcb7d..48bab6326c 100644 --- a/Editor/Mono/Inspector/Core/PreviewWindow.cs +++ b/Editor/Mono/Inspector/Core/PreviewWindow.cs @@ -57,7 +57,6 @@ protected override void OnDisable() base.OnDisable(); if (m_ParentInspectorWindow != null && GetInspectors().Contains(m_ParentInspectorWindow)) { - m_ParentInspectorWindow.hasFloatingPreviewWindow = false; m_ParentInspectorWindow.RebuildContentsContainers(); } } diff --git a/Editor/Mono/Inspector/Core/PropertyEditor.cs b/Editor/Mono/Inspector/Core/PropertyEditor.cs index cf10b6cf46..c452910f0b 100644 --- a/Editor/Mono/Inspector/Core/PropertyEditor.cs +++ b/Editor/Mono/Inspector/Core/PropertyEditor.cs @@ -135,7 +135,6 @@ class PropertyEditor : EditorWindow, IPropertyView, IHasCustomMenu protected bool m_PreviousPreviewExpandedState; protected bool m_HasPreview; protected HashSet m_DrawnSelection = new HashSet(); - internal bool hasFloatingPreviewWindow { get; set; } readonly List m_EditorTargetTypes = new List(); List m_SupportedDataModes = new(4); @@ -150,8 +149,17 @@ class PropertyEditor : EditorWindow, IPropertyView, IHasCustomMenu EditorElementUpdater m_EditorElementUpdater; IPreviewable m_cachedPreviewEditor; + + /// + /// Delayer used to periodically check if the return value of has changed. + /// Delayer m_HasPreviewPeriodicCheckDelayer; + /// + /// Stores the last return value of in . + /// + bool m_LastHasPreviewPeriodicCheck; + public InspectorMode inspectorMode { get { return m_InspectorMode; } @@ -313,7 +321,6 @@ internal PropertyEditor() editorDragging = new EditorDragging(this); minSize = new Vector2(k_MinimumWindowWidth, minSize.y); m_EditorElementUpdater = new EditorElementUpdater(this); - hasFloatingPreviewWindow = false; } [UsedImplicitly] @@ -361,33 +368,42 @@ protected virtual void OnEnable() if (shouldUpdateSupportedDataModes) EditorApplication.CallDelayed(UpdateSupportedDataModesList); - if (!m_AllPropertyEditors.Contains(this)) m_AllPropertyEditors.Add(this); + if (!m_AllPropertyEditors.Contains(this)) + m_AllPropertyEditors.Add(this); - m_HasPreviewPeriodicCheckDelayer?.Dispose(); + // Setup a periodic check to determine if we need to rebuild the contents containers m_HasPreviewPeriodicCheckDelayer = Delayer.Throttle(HasPreviewPeriodicCheck, TimeSpan.FromMilliseconds(200)); - EditorApplication.update += OnEditorUpdate; + + // Restrict the minimum height of the content area so it can't be collapsed to nothing + m_ScrollView.style.minHeight = 150; } + /// + /// Periodic check if the preview editor has changed its HasPreviewGUI return value. + /// This is not executed on every frame because getting the editor that controls the preview is expensive. + /// void HasPreviewPeriodicCheck(object _) { var previewEditor = GetEditorThatControlsPreview(tracker.activeEditors); - // Do we have a preview? + // Check if the return value of HasPreviewGUI has changed, and rebuild the contents containers if it did var hasPreview = previewEditor != null && previewEditor.HasPreviewGUI(); - if (hasPreview != m_HasPreview) + if (hasPreview != m_LastHasPreviewPeriodicCheck) { - m_HasPreview = hasPreview; RebuildContentsContainers(); + m_LastHasPreviewPeriodicCheck = hasPreview; } } - void OnEditorUpdate() => m_HasPreviewPeriodicCheckDelayer?.Execute(null); - [UsedImplicitly] protected virtual void OnDisable() { - m_HasPreviewPeriodicCheckDelayer?.Dispose(); - hasFloatingPreviewWindow = false; + if (m_HasPreviewPeriodicCheckDelayer != null) + { + m_HasPreviewPeriodicCheckDelayer.Dispose(); + m_HasPreviewPeriodicCheckDelayer = null; + } + ClearPreviewables(); // save vertical scroll position @@ -410,7 +426,6 @@ protected virtual void OnDisable() EditorApplication.playModeStateChanged -= OnPlayModeStateChanged; m_AllPropertyEditors.Remove(this); - EditorApplication.update -= OnEditorUpdate; } private void OnMouseEnter(MouseEnterEvent e) => HoveredPropertyEditor = this; @@ -463,6 +478,8 @@ protected virtual void OnGUI() { if (m_FirstInitialize) RebuildContentsContainers(); + else + m_HasPreviewPeriodicCheckDelayer?.Execute(); } static Editor[] s_Editors = new Editor[10]; @@ -1145,14 +1162,19 @@ internal virtual void RebuildContentsContainers() })); } - if(m_SplitView == null) + if (m_SplitView == null) + { m_SplitView = rootVisualElement.Q(); + // Hide the dragline by default until we determine what kind of preview (UITK or IMGUI) we have + m_SplitView.Q(s_draglineAnchor).style.display = DisplayStyle.None; + } + ClearPreview(); if (m_PreviewResizer != null && editors.Any()) { - if (previewAndLabelElement != null && !hasFloatingPreviewWindow) + if (previewAndLabelElement != null) { VisualElement previewItem = null; CreatePreviewables(); diff --git a/Editor/Mono/Inspector/Core/ScriptAttributeGUI/Implementations/ExposedReferenceDrawer.cs b/Editor/Mono/Inspector/Core/ScriptAttributeGUI/Implementations/ExposedReferenceDrawer.cs index 4aaafc633b..e1e4ac1afa 100644 --- a/Editor/Mono/Inspector/Core/ScriptAttributeGUI/Implementations/ExposedReferenceDrawer.cs +++ b/Editor/Mono/Inspector/Core/ScriptAttributeGUI/Implementations/ExposedReferenceDrawer.cs @@ -4,6 +4,7 @@ using System; using UnityEditor; +using UnityEditor.UIElements; using UnityEngine; using UnityEngine.UIElements; using ObjectField = UnityEditor.UIElements.ObjectField; @@ -156,16 +157,23 @@ public override VisualElement CreatePropertyGUI(SerializedProperty prop) obj.RegisterValueChangedCallback(SetReference); obj.AddManipulator(new ContextualMenuManipulator(BuildContextualMenu)); + obj.AddToClassList(ObjectField.alignedFieldUssClassName); + // Track for Undo/Redo changes which can come from exposedPropertyTable Undo.UndoRedoCallback undoRedoCallback = () => { m_Item.UpdateValue(); obj.SetValueWithoutNotify(m_Item.currentReferenceValue); }; + // Track the property for external changed including Undo/Redo + obj.TrackPropertyValue(prop, _ => undoRedoCallback()); obj.RegisterCallback(evt => Undo.undoRedoPerformed += undoRedoCallback); obj.RegisterCallback(evt => Undo.undoRedoPerformed -= undoRedoCallback); + // Set the serialized property so we can support drag and drop + obj.SetProperty(ObjectField.serializedPropertyKey, m_Item.exposedPropertyDefault); + return obj; } diff --git a/Editor/Mono/Inspector/Core/ScriptAttributeGUI/PropertyHandler.cs b/Editor/Mono/Inspector/Core/ScriptAttributeGUI/PropertyHandler.cs index b4d045eeae..f5ab2f6bb7 100644 --- a/Editor/Mono/Inspector/Core/ScriptAttributeGUI/PropertyHandler.cs +++ b/Editor/Mono/Inspector/Core/ScriptAttributeGUI/PropertyHandler.cs @@ -96,13 +96,26 @@ public void HandleAttribute(SerializedProperty property, PropertyAttribute attri return; } - // When `attribute.applyToCollection` is set to true, we need to early return for any non-collection fields within a collection. - // Collections and fields that are not part of a collection should comply with the attribute. - if (attribute.applyToCollection && !propertyType.IsArrayOrList() && property.propertyPath.Contains("[")) - return; - - // Look for its drawer type of this attribute - HandleDrawnType(property, attribute.GetType(), propertyType, field, attribute); + // Case 1: If property is a collection, applyToCollection == false, early return to avoid custom drawer; + // Case 2: If property is not a collection but within a collection, applyToCollection == true, early return + // to avoid custom drawer; + // Case 3: If property is not a collection nor within a collection, applyToCollection value should + // NOT have any effects on it. Custom drawer should be used. + // Case 4: Rest of the cases, custom drawer should be used. + var isArrayOrList = propertyType != null && propertyType.IsArrayOrList(); + switch (attribute.applyToCollection) + { + // Case 1. + case false when isArrayOrList: + // Case 2. + case true when !isArrayOrList && property.propertyPath.Contains("["): + return; + // Case 3 & 4. + default: + // Look for its drawer type of this attribute + HandleDrawnType(property, attribute.GetType(), propertyType, field, attribute); + break; + } } public void HandleDrawnType(SerializedProperty property, Type drawnType, Type propertyType, FieldInfo field, PropertyAttribute attribute) diff --git a/Editor/Mono/Inspector/GameObjectInspector.cs b/Editor/Mono/Inspector/GameObjectInspector.cs index 686eae006d..9f145d12e1 100644 --- a/Editor/Mono/Inspector/GameObjectInspector.cs +++ b/Editor/Mono/Inspector/GameObjectInspector.cs @@ -49,7 +49,7 @@ static class Styles public static GUIContent layerContent = EditorGUIUtility.TrTextContent("Layer", "The layer that this GameObject is in.\n\nChoose Add Layer... to edit the list of available layers."); public static GUIContent tagContent = EditorGUIUtility.TrTextContent("Tag", "The tag that this GameObject has.\n\nChoose Untagged to remove the current tag.\n\nChoose Add Tag... to edit the list of available tags."); public static GUIContent staticPreviewContent = EditorGUIUtility.TrTextContent("Static Preview", "This asset is greater than 8MB so, by default, the Asset Preview displays a static preview.\nTo view the asset interactively, click the Asset Preview."); - + public static float tagFieldWidth = EditorGUI.CalcPrefixLabelWidth(Styles.tagContent, EditorStyles.boldLabel); public static float layerFieldWidth = EditorGUI.CalcPrefixLabelWidth(Styles.layerContent, EditorStyles.boldLabel); @@ -1370,7 +1370,14 @@ internal void OnSceneDragInternal(SceneView sceneView, int index, EventType type m_DragObject.transform.position = Matrix4x4.identity.MultiplyPoint(point + (normal * offset)); } else - m_DragObject.transform.position = HandleUtility.GUIPointToWorldRay(mousePosition).GetPoint(10); + { + // UUM-95510 scale the distance based on FOV to avoid very close placement at low FOVs + var clampedFov = Mathf.Clamp(sceneView.camera.fieldOfView, 4f, 60f); + var fovFactor = Mathf.InverseLerp(60f, 4f, clampedFov); + var distanceFactor = Mathf.Pow(fovFactor, 6); + var distance = (distanceFactor * 110f) + 10f; + m_DragObject.transform.position = HandleUtility.GUIPointToWorldRay(mousePosition).GetPoint(distance); + } if (alt) { diff --git a/Editor/Mono/Inspector/LODGroupEditor.cs b/Editor/Mono/Inspector/LODGroupEditor.cs index 09d0339c97..75fbdc8eb2 100644 --- a/Editor/Mono/Inspector/LODGroupEditor.cs +++ b/Editor/Mono/Inspector/LODGroupEditor.cs @@ -1624,23 +1624,33 @@ void InitPreview() protected void DoRenderPreview() { + bool LODSelected = activeLOD >= 0; if (m_PreviewUtility.renderTexture.width <= 0 || m_PreviewUtility.renderTexture.height <= 0 - || m_NumberOfLODs <= 0 - || activeLOD < 0) + || m_NumberOfLODs <= 0) return; var bounds = new Bounds(Vector3.zero, Vector3.zero); bool boundsSet = false; + Renderer[] renderers = null; + if (target is LODGroup targetLODGroup) + { + LOD[] lodArray = targetLODGroup.GetLODs(); + int lodIndex = LODSelected ? activeLOD : 0; // display LOD0 if no LOD is selected + if (lodIndex >= 0 && lodIndex < lodArray.Length) + { + renderers = lodArray[lodIndex].renderers; + } + } + if (renderers == null) + return; + var meshsToRender = new List(); var billboards = new List(); - var renderers = serializedObject.FindProperty(string.Format(kRenderRootPath, activeLOD)); - for (int i = 0; i < renderers.arraySize; i++) + for (int i = 0; i < renderers.Length; i++) { - var lodRenderRef = renderers.GetArrayElementAtIndex(i).FindPropertyRelative("renderer"); - var renderer = lodRenderRef.objectReferenceValue as Renderer; - + var renderer = renderers[i]; if (renderer == null) continue; diff --git a/Editor/Mono/Inspector/MaterialEditor.cs b/Editor/Mono/Inspector/MaterialEditor.cs index f638d3acd2..f42785e4c3 100644 --- a/Editor/Mono/Inspector/MaterialEditor.cs +++ b/Editor/Mono/Inspector/MaterialEditor.cs @@ -24,7 +24,6 @@ public partial class MaterialEditor : Editor { private static class Styles { - public static readonly GUIStyle inspectorBigInner = "IN BigTitle inner"; public static readonly GUIContent reflectionProbePickerIcon = EditorGUIUtility.TrIconContent("ReflectionProbeSelector"); public static readonly GUIContent lightmapEmissiveLabelRealtimeGISupport = EditorGUIUtility.TrTextContent("Global Illumination", "Controls if the emission is Baked or Realtime.\n\nBaked only has effect in scenes where Baked Global Illumination is enabled.\n\nRealtime uses Realtime Global Illumination if enabled in the scene. Otherwise the emission won't light up other objects."); public static readonly GUIContent lightmapEmissiveLabel = EditorGUIUtility.TrTextContent("Global Illumination", "Controls if the emission is Baked or Realtime.\n\nBaked only has effect in scenes where Baked Global Illumination is enabled.\n\nRealtime won't light up other objects since Realtime Global Illumination is not supported."); @@ -3092,7 +3091,7 @@ internal override bool HasLargeHeader() internal override void OnHeaderIconGUI(Rect iconRect) { - OnPreviewGUI(iconRect, Styles.inspectorBigInner); + OnPreviewGUI(iconRect, "IN BigTitle inner"); } [MenuItem("CONTEXT/Material/Flatten Material Variant", true)] diff --git a/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs b/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs index af5d799ce9..e6f8a2c6e9 100644 --- a/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs +++ b/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs @@ -77,9 +77,7 @@ class SettingsContent public static readonly GUIContent renderingTitle = EditorGUIUtility.TrTextContent("Rendering"); public static readonly GUIContent vulkanSettingsTitle = EditorGUIUtility.TrTextContent("Vulkan Settings"); public static readonly GUIContent identificationTitle = EditorGUIUtility.TrTextContent("Identification"); - public static readonly GUIContent macAppStoreTitle = EditorGUIUtility.TrTextContent("Mac App Store Options"); public static readonly GUIContent configurationTitle = EditorGUIUtility.TrTextContent("Configuration"); - public static readonly GUIContent macConfigurationTitle = EditorGUIUtility.TrTextContent("Mac Configuration"); public static readonly GUIContent optimizationTitle = EditorGUIUtility.TrTextContent("Optimization"); public static readonly GUIContent loggingTitle = EditorGUIUtility.TrTextContent("Stack Trace*"); public static readonly GUIContent legacyTitle = EditorGUIUtility.TrTextContent("Legacy"); @@ -89,8 +87,6 @@ class SettingsContent public static readonly GUIContent usePlayerLog = EditorGUIUtility.TrTextContent("Use Player Log"); public static readonly GUIContent resizableWindow = EditorGUIUtility.TrTextContent("Resizable Window"); public static readonly GUIContent forceSingleInstance = EditorGUIUtility.TrTextContent("Force Single Instance"); - public static readonly GUIContent overrideDefaultApplicationIdentifier = EditorGUIUtility.TrTextContent("Override Default Bundle Identifier"); - public static readonly GUIContent bundleIdentifier = EditorGUIUtility.TrTextContent("Bundle Identifier"); public static readonly GUIContent shaderSectionTitle = EditorGUIUtility.TrTextContent("Shader Settings"); public static readonly GUIContent shaderVariantLoadingTitle = EditorGUIUtility.TrTextContent("Shader Variant Loading Settings"); @@ -129,9 +125,6 @@ class SettingsContent public static readonly GUIContent UIRequiresFullScreen = EditorGUIUtility.TrTextContent("Requires Fullscreen"); public static readonly GUIContent UIStatusBarHidden = EditorGUIUtility.TrTextContent("Status Bar Hidden"); public static readonly GUIContent UIStatusBarStyle = EditorGUIUtility.TrTextContent("Status Bar Style"); - public static readonly GUIContent useMacAppStoreValidation = EditorGUIUtility.TrTextContent("Mac App Store Validation"); - public static readonly GUIContent macAppStoreCategory = EditorGUIUtility.TrTextContent("Category", "'LSApplicationCategoryType'"); - public static readonly GUIContent macOSURLSchemes = EditorGUIUtility.TrTextContent("Supported URL schemes*"); public static readonly GUIContent fullscreenMode = EditorGUIUtility.TrTextContent("Fullscreen Mode ", " Not all platforms support all modes"); public static readonly GUIContent exclusiveFullscreen = EditorGUIUtility.TrTextContent("Exclusive Fullscreen"); public static readonly GUIContent fullscreenWindow = EditorGUIUtility.TrTextContent("Fullscreen Window"); @@ -176,12 +169,11 @@ class SettingsContent public static readonly GUIContent packageNameError = EditorGUIUtility.TrTextContent("The Package Name must follow the convention 'com.YourCompanyName.YourProductName' and must contain only alphanumeric and underscore characters. Each segment must start with an alphabetical character."); public static readonly GUIContent applicationBuildNumber = EditorGUIUtility.TrTextContent("Build"); public static readonly GUIContent appleDeveloperTeamID = EditorGUIUtility.TrTextContent("iOS Developer Team ID", "Developers can retrieve their Team ID by visiting the Apple Developer site under Account > Membership."); - public static readonly GUIContent gcIncremental = EditorGUIUtility.TrTextContent("Use incremental GC", "With incremental Garbage Collection, the Garbage Collector will try to time-slice the collection task into multiple steps, to avoid long GC times preventing content from running smoothly."); + public static readonly GUIContent gcIncremental = EditorGUIUtility.TrTextContent("Use incremental GC*", "With incremental Garbage Collection, the Garbage Collector will try to time-slice the collection task into multiple steps, to avoid long GC times preventing content from running smoothly."); public static readonly GUIContent accelerometerFrequency = EditorGUIUtility.TrTextContent("Accelerometer Frequency*"); public static readonly GUIContent cameraUsageDescription = EditorGUIUtility.TrTextContent("Camera Usage Description*", "String shown to the user when requesting permission to use the device camera. Written to the NSCameraUsageDescription field in Xcode project's info.plist file"); public static readonly GUIContent locationUsageDescription = EditorGUIUtility.TrTextContent("Location Usage Description*", "String shown to the user when requesting permission to access the device location. Written to the NSLocationWhenInUseUsageDescription field in Xcode project's info.plist file."); public static readonly GUIContent microphoneUsageDescription = EditorGUIUtility.TrTextContent("Microphone Usage Description*", "String shown to the user when requesting to use the device microphone. Written to the NSMicrophoneUsageDescription field in Xcode project's info.plist file"); - public static readonly GUIContent bluetoothUsageDescription = EditorGUIUtility.TrTextContent("Bluetooth Usage Description*", "String shown to the user when requesting to use the device bluetooth. Written to the NSBluetoothAlwaysUsageDescription field in Xcode project's info.plist file"); public static readonly GUIContent muteOtherAudioSources = EditorGUIUtility.TrTextContent("Mute Other Audio Sources*"); public static readonly GUIContent prepareIOSForRecording = EditorGUIUtility.TrTextContent("Prepare iOS for Recording"); public static readonly GUIContent forceIOSSpeakersWhenRecording = EditorGUIUtility.TrTextContent("Force iOS Speakers when Recording"); @@ -201,7 +193,7 @@ class SettingsContent public static readonly GUIContent autoGraphicsAPIForLinux = EditorGUIUtility.TrTextContent("Auto Graphics API for Linux"); public static readonly GUIContent iOSURLSchemes = EditorGUIUtility.TrTextContent("Supported URL schemes*"); - public static readonly GUIContent iOSExternalAudioInputNotSupported = EditorGUIUtility.TrTextContent("Audio input from Bluetooth microphones is not supported when Mute Other Audio Sources and Prepare iOS for Recording are both off."); + public static readonly GUIContent iOSExternalAudioInputNotSupported = EditorGUIUtility.TrTextContent("Audio input from Bluetooth microphones is not supported when Mute Other Audio Sources is off."); public static readonly GUIContent require31 = EditorGUIUtility.TrTextContent("Require ES3.1"); public static readonly GUIContent requireAEP = EditorGUIUtility.TrTextContent("Require ES3.1+AEP"); public static readonly GUIContent require32 = EditorGUIUtility.TrTextContent("Require ES3.2"); @@ -342,9 +334,6 @@ internal static void SyncEditors(BuildTarget target) // macOS SerializedProperty m_ApplicationBundleVersion; - SerializedProperty m_UseMacAppStoreValidation; - SerializedProperty m_MacAppStoreCategory; - SerializedProperty m_MacURLSchemes; // vulkan SerializedProperty m_VulkanNumSwapchainBuffers; @@ -359,7 +348,6 @@ internal static void SyncEditors(BuildTarget target) SerializedProperty m_CameraUsageDescription; SerializedProperty m_LocationUsageDescription; SerializedProperty m_MicrophoneUsageDescription; - SerializedProperty m_BluetoothUsageDescription; SerializedProperty m_IPhoneScriptCallOptimization; @@ -401,7 +389,6 @@ internal static void SyncEditors(BuildTarget target) SerializedProperty m_OverrideDefaultApplicationIdentifier; SerializedProperty m_ApplicationIdentifier; - SerializedProperty m_BuildNumber; // General SerializedProperty m_CompanyName; @@ -621,7 +608,6 @@ void OnEnable() m_OverrideDefaultApplicationIdentifier = FindPropertyAssert("overrideDefaultApplicationIdentifier"); m_ApplicationIdentifier = FindPropertyAssert("applicationIdentifier"); - m_BuildNumber = FindPropertyAssert("buildNumber"); m_ApplicationBundleVersion = serializedObject.FindProperty("bundleVersion"); if (m_ApplicationBundleVersion == null) @@ -641,7 +627,6 @@ void OnEnable() m_CameraUsageDescription = FindPropertyAssert("cameraUsageDescription"); m_LocationUsageDescription = FindPropertyAssert("locationUsageDescription"); m_MicrophoneUsageDescription = FindPropertyAssert("microphoneUsageDescription"); - m_BluetoothUsageDescription = FindPropertyAssert("bluetoothUsageDescription"); m_EnableInternalProfiler = FindPropertyAssert("enableInternalProfiler"); m_ActionOnDotNetUnhandledException = FindPropertyAssert("actionOnDotNetUnhandledException"); @@ -689,9 +674,6 @@ void OnEnable() m_BakeCollisionMeshes = FindPropertyAssert("bakeCollisionMeshes"); m_DedicatedServerOptimizations = FindPropertyAssert("dedicatedServerOptimizations"); m_ResizableWindow = FindPropertyAssert("resizableWindow"); - m_UseMacAppStoreValidation = FindPropertyAssert("useMacAppStoreValidation"); - m_MacAppStoreCategory = FindPropertyAssert("macAppStoreCategory"); - m_MacURLSchemes = FindPropertyAssert("macOSURLSchemes"); m_VulkanNumSwapchainBuffers = FindPropertyAssert("vulkanNumSwapchainBuffers"); m_VulkanEnableLateAcquireNextImage = FindPropertyAssert("vulkanEnableLateAcquireNextImage"); m_VulkanEnableCommandBufferRecycling = FindPropertyAssert("vulkanEnableCommandBufferRecycling"); @@ -732,9 +714,15 @@ void OnEnable() var validPlatformsLength = validPlatforms.Length; m_SettingsExtensions = new ISettingEditorExtension[validPlatformsLength]; var currentPlatform = 0; + var isStandaloneGroup = EditorUserBuildSettings.activeBuildTargetGroup == BuildTargetGroup.Standalone; for (int i = 0; i < validPlatformsLength; i++) { - string module = ModuleManager.GetTargetStringFromBuildTargetGroup(validPlatforms[i].namedBuildTarget.ToBuildTargetGroup()); + // Show the settings of the active standalone platform for the standalone tab in global player settings + var buildTargetGroup = validPlatforms[i].namedBuildTarget.ToBuildTargetGroup(); + var module = (isStandaloneGroup && buildTargetGroup == BuildTargetGroup.Standalone) ? + ModuleManager.GetTargetStringFromBuildTarget(EditorUserBuildSettings.activeBuildTarget) : + ModuleManager.GetTargetStringFromBuildTargetGroup(buildTargetGroup); + m_SettingsExtensions[i] = ModuleManager.GetEditorSettingsExtension(module); if (m_SettingsExtensions[i] != null) m_SettingsExtensions[i].OnEnable(this); @@ -802,20 +790,27 @@ internal void ConfigurePlayerSettingsForBuildProfile( // We don't want to show other platform tabs that it's not the build profile one var gotValidPlatform = false; var buildProfileBasePlatformGuid = BuildTargetDiscovery.GetBasePlatformGUID(buildProfilePlatformGuid); + var buildProfileSubtarget = BuildTargetDiscovery.GetBuildTargetAndSubtargetFromGUID(buildProfileBasePlatformGuid).Item2; + var isBuildProfilePlatformStandalone = buildProfileSubtarget == StandaloneBuildSubtarget.Player; + var isBuildProfilePlatformServer = buildProfileSubtarget == StandaloneBuildSubtarget.Server; for (int i = 0; i < validPlatforms.Length; i++) { var buildTarget = validPlatforms[i].defaultTarget; var namedBuildTarget = validPlatforms[i].namedBuildTarget; var basePlatformGuid = BuildTargetDiscovery.GetBasePlatformGUIDFromBuildTarget(namedBuildTarget, buildTarget); - if (basePlatformGuid == buildProfileBasePlatformGuid) - { - var copy = (BuildPlatform)validPlatforms[i].Clone(); - copy.tooltip = string.Empty; - validPlatforms[0] = copy; - gotValidPlatform = true; - break; - } + // Player settings tabs are shown by BuildPlatform/NamedBuildTarget, so we need to compare the + // NamedBuildTarget in addition to the base platform guid for standalone and server platforms + var isStandalone = namedBuildTarget == NamedBuildTarget.Standalone && isBuildProfilePlatformStandalone; + var isServer = namedBuildTarget == NamedBuildTarget.Server && isBuildProfilePlatformServer; + if (basePlatformGuid != buildProfileBasePlatformGuid && !(isStandalone || isServer)) + continue; + + var copy = (BuildPlatform)validPlatforms[i].Clone(); + copy.tooltip = string.Empty; + validPlatforms[0] = copy; + gotValidPlatform = true; + break; } if (!gotValidPlatform) @@ -2407,7 +2402,8 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, ISettingEditorExte } bool graphicsJobsOptionEnabled = true; - bool graphicsJobs = PlayerSettings.GetGraphicsJobsForPlatform_Internal(m_CurrentTarget, platform.defaultTarget); + bool graphicsJobs = PlayerSettings.GetGraphicsJobsForPlatform_Internal(m_CurrentTarget, + platform.namedBuildTarget.ToBuildTargetGroup() == BuildTargetGroup.Standalone ? EditorUserBuildSettings.selectedStandaloneTarget : platform.defaultTarget); bool newGraphicsJobs = graphicsJobs; if (platform.namedBuildTarget == NamedBuildTarget.XboxOne) @@ -2470,7 +2466,9 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, ISettingEditorExte if (EditorGUI.EndChangeCheck() && (newGraphicsJobs != graphicsJobs)) { Undo.RecordObject(target, SettingsContent.undoChangedGraphicsJobsString); - PlayerSettings.SetGraphicsJobsForPlatform_Internal(m_CurrentTarget, platform.defaultTarget, newGraphicsJobs); + PlayerSettings.SetGraphicsJobsForPlatform_Internal(m_CurrentTarget, + platform.namedBuildTarget.ToBuildTargetGroup() == BuildTargetGroup.Standalone ? EditorUserBuildSettings.selectedStandaloneTarget : platform.defaultTarget, newGraphicsJobs); + OnTargetObjectChangedDirectly(); if (IsActivePlayerSettingsEditor() && CheckApplyGraphicsJobsModeChange(platform.defaultTarget)) @@ -2848,37 +2846,11 @@ private bool CheckIfWebGPUInGfxAPIList() private void OtherSectionIdentificationGUI(BuildPlatform platform, ISettingEditorExtension settingsExtension) { // Identification - if (settingsExtension != null && settingsExtension.HasIdentificationGUI()) { GUILayout.Label(SettingsContent.identificationTitle, EditorStyles.boldLabel); settingsExtension.IdentificationSectionGUI(); - EditorGUILayout.Space(); - } - else if (platform.namedBuildTarget.ToBuildTargetGroup() == BuildTargetGroup.Standalone && CanShowPlatformSettingsForHostPlatform(BuildTarget.StandaloneOSX, platform)) - { - // TODO this should be move to an extension if we have one for MacOS or Standalone target at some point. - GUILayout.Label(SettingsContent.macAppStoreTitle, EditorStyles.boldLabel); - - EditorGUILayout.PropertyField(m_OverrideDefaultApplicationIdentifier, SettingsContent.overrideDefaultApplicationIdentifier); - - using (var horizontal = new EditorGUILayout.HorizontalScope()) - { - using (new EditorGUI.PropertyScope(horizontal.rect, GUIContent.none, m_OverrideDefaultApplicationIdentifier)) - { - using (new EditorGUI.IndentLevelScope()) - { - ShowApplicationIdentifierUI(BuildTargetGroup.Standalone, SettingsContent.bundleIdentifier.text, "'CFBundleIdentifier'"); - } - } - } - - PlayerSettingsEditor.ShowBuildNumberUI(m_BuildNumber, NamedBuildTarget.Standalone, "Build", "'CFBundleVersion'"); - - EditorGUILayout.PropertyField(m_MacAppStoreCategory, SettingsContent.macAppStoreCategory); - EditorGUILayout.PropertyField(m_UseMacAppStoreValidation, SettingsContent.useMacAppStoreValidation); - EditorGUILayout.Space(); } } @@ -3322,16 +3294,19 @@ private void OtherSectionConfigurationGUI(BuildPlatform platform, ISettingEditor EditorGUILayout.PropertyField(m_AccelerometerFrequency, SettingsContent.accelerometerFrequency); if (platform.namedBuildTarget == NamedBuildTarget.iOS || platform.namedBuildTarget == NamedBuildTarget.tvOS || platform.namedBuildTarget == NamedBuildTarget.Android) + { EditorGUILayout.PropertyField(m_MuteOtherAudioSources, SettingsContent.muteOtherAudioSources); + if (m_MuteOtherAudioSources.boolValue == false && platform.namedBuildTarget == NamedBuildTarget.iOS) + EditorGUILayout.HelpBox(SettingsContent.iOSExternalAudioInputNotSupported.text, MessageType.Warning); + } + // TVOS TODO: check what should stay or go if (platform.namedBuildTarget == NamedBuildTarget.iOS || platform.namedBuildTarget == NamedBuildTarget.tvOS) { if (platform.namedBuildTarget == NamedBuildTarget.iOS) { EditorGUILayout.PropertyField(m_PrepareIOSForRecording, SettingsContent.prepareIOSForRecording); - if (m_MuteOtherAudioSources.boolValue == false && m_PrepareIOSForRecording.boolValue == false) - EditorGUILayout.HelpBox(SettingsContent.iOSExternalAudioInputNotSupported.text, MessageType.Warning); EditorGUILayout.PropertyField(m_ForceIOSSpeakersWhenRecording, SettingsContent.forceIOSSpeakersWhenRecording); } @@ -3383,20 +3358,8 @@ private void OtherSectionConfigurationGUI(BuildPlatform platform, ISettingEditor if (settingsExtension != null) settingsExtension.ConfigurationSectionGUI(); - - EditorGUILayout.Space(); - - if (platform.namedBuildTarget == NamedBuildTarget.Standalone && CanShowPlatformSettingsForHostPlatform(BuildTarget.StandaloneOSX, platform)) - { - GUILayout.Label(SettingsContent.macConfigurationTitle, EditorStyles.boldLabel); - EditorGUILayout.PropertyField(m_CameraUsageDescription, SettingsContent.cameraUsageDescription); - EditorGUILayout.PropertyField(m_MicrophoneUsageDescription, SettingsContent.microphoneUsageDescription); - EditorGUILayout.PropertyField(m_BluetoothUsageDescription, SettingsContent.bluetoothUsageDescription); - EditorGUILayout.PropertyField(m_MacURLSchemes, SettingsContent.macOSURLSchemes, true); - - EditorGUILayout.Space(); - } + EditorGUILayout.Space(); } private string GetScriptingDefineSymbolsForGroup(NamedBuildTarget buildTarget) @@ -4220,14 +4183,6 @@ private void FindPlayerSettingsAttributeSections() m_boxes.Sort((a, b) => a.order.CompareTo(b.order)); } - bool CanShowPlatformSettingsForHostPlatform(BuildTarget settingsBuildTarget, BuildPlatform currentPlatform) - { - if (IsBuildProfileEditor() && currentPlatform.defaultTarget != settingsBuildTarget) - return false; - - return BuildTargetDiscovery.BuildPlatformIsAvailableOnHostPlatform(settingsBuildTarget, SystemInfo.operatingSystemFamily); - } - void SyncColorGamuts() { if (m_ColorGamutList == null) diff --git a/Editor/Mono/Inspector/PlayerSettingsEditor/WebTemplateManagerBase.cs b/Editor/Mono/Inspector/PlayerSettingsEditor/WebTemplateManagerBase.cs index a5c608a5d8..70ec5cc995 100644 --- a/Editor/Mono/Inspector/PlayerSettingsEditor/WebTemplateManagerBase.cs +++ b/Editor/Mono/Inspector/PlayerSettingsEditor/WebTemplateManagerBase.cs @@ -79,7 +79,7 @@ public int GetTemplateIndex(string path) return i; } } - return 0; + return -1; } public void ClearTemplates() @@ -172,12 +172,16 @@ public void SelectionUI(SerializedProperty templateProp) if (s_Styles == null) s_Styles = new Styles(); - bool wasChanged = GUI.changed; - + bool templateChanged = false; using (var vertical = new EditorGUILayout.VerticalScope()) { using (new EditorGUI.PropertyScope(vertical.rect, GUIContent.none, templateProp)) { + + var selectedTemplateIndex = GetTemplateIndex(templateProp.stringValue); + if (selectedTemplateIndex < 0) + EditorGUILayout.HelpBox("No valid template is selected. Choose a template to proceed.", MessageType.Error); + using (var horizontal = new EditorGUILayout.HorizontalScope()) { if (TemplateGUIThumbnails.Length < 1) @@ -189,26 +193,36 @@ public void SelectionUI(SerializedProperty templateProp) int numCols = Mathf.Min((int)Mathf.Max((Screen.width - kWebTemplateGridPadding * 2.0f) / kThumbnailSize, 1.0f), TemplateGUIThumbnails.Length); int numRows = Mathf.Max((int)Mathf.Ceil((float)TemplateGUIThumbnails.Length / (float)numCols), 1); - templateProp.stringValue = Templates[ - ThumbnailList( - GUILayoutUtility.GetRect(numCols * kThumbnailSize, numRows * (kThumbnailSize + kThumbnailLabelHeight), GUILayout.ExpandWidth(false)), - GetTemplateIndex(templateProp.stringValue), - TemplateGUIThumbnails, - numCols - )].ToString(); + var updatedSelectedTemplateIndex = ThumbnailList( + GUILayoutUtility.GetRect(numCols * kThumbnailSize, numRows * (kThumbnailSize + kThumbnailLabelHeight), GUILayout.ExpandWidth(false)), + selectedTemplateIndex, + TemplateGUIThumbnails, + numCols + ); + templateChanged = selectedTemplateIndex != updatedSelectedTemplateIndex; + + // Only set/update templateProp and selectedTemplateIndex if there is a valid template selection. + if (updatedSelectedTemplateIndex > -1) + { + templateProp.stringValue = Templates[updatedSelectedTemplateIndex].ToString(); + selectedTemplateIndex = updatedSelectedTemplateIndex; + } } } - bool templateChanged = !wasChanged && GUI.changed; - bool orgChanged = GUI.changed; GUI.changed = false; - var templateCustomKeys = Templates[GetTemplateIndex(templateProp.stringValue)].CustomKeys; - foreach (string key in templateCustomKeys) + + var templateCustomKeys = new string[]{}; + if (selectedTemplateIndex > -1) { - string value = PlayerSettings.GetTemplateCustomValue(key); - value = EditorGUILayout.TextField(PrettyTemplateKeyName(key), value); - PlayerSettings.SetTemplateCustomValue(key, value); + templateCustomKeys = Templates[GetTemplateIndex(templateProp.stringValue)].CustomKeys; + foreach (string key in templateCustomKeys) + { + string value = PlayerSettings.GetTemplateCustomValue(key); + value = EditorGUILayout.TextField(PrettyTemplateKeyName(key), value); + PlayerSettings.SetTemplateCustomValue(key, value); + } } if (GUI.changed) diff --git a/Editor/Mono/Inspector/RenderPipelineEditorUtility.cs b/Editor/Mono/Inspector/RenderPipelineEditorUtility.cs index 1b537c2ea9..498172f667 100644 --- a/Editor/Mono/Inspector/RenderPipelineEditorUtility.cs +++ b/Editor/Mono/Inspector/RenderPipelineEditorUtility.cs @@ -265,5 +265,22 @@ internal static bool DoesMaskContainRenderingLayersOutsideOfMaxBitCount(uint mas /// A string with the warning message about layers beyond the supported bit count being ignored. internal static string GetOutsideOfMaxBitCountWarningMessage(int bitCount) => $"Current mask contains layers outside of a supported range by active Render Pipeline. The active Render Pipeline only supports up to {bitCount} layers. Rendering Layers above {bitCount} are ignored."; + + internal static bool SupportPreview(Camera camera, out string reason) + { + if (!RenderPipelineManager.isCurrentPipelineValid) + { + //Thus we are in Built-in Render Pipeline. Preview are supported here. + if (camera == null || camera.Equals(null)) + { + reason = "Camera is null"; + return false; + } + reason = null; + return true; + } + + return RenderPipelineManager.currentPipeline.IsPreviewSupported(camera, out reason); + } } } diff --git a/Editor/Mono/Inspector/TagManagerInspector.cs b/Editor/Mono/Inspector/TagManagerInspector.cs index d261ef7a94..f4bb8c1d6f 100644 --- a/Editor/Mono/Inspector/TagManagerInspector.cs +++ b/Editor/Mono/Inspector/TagManagerInspector.cs @@ -229,6 +229,9 @@ void SortingLayersChanged(ChangeEvent evt) { serializedObject.ApplyModifiedProperties(); tagManager.UpdateSortingLayersOrder(); + + if (SortingLayer.onLayerChanged != null) + SortingLayer.onLayerChanged(); }; sortingLayers.onAdd += (listView) => { @@ -240,6 +243,9 @@ void SortingLayersChanged(ChangeEvent evt) if (SortingLayer.onLayerAdded != null) SortingLayer.onLayerAdded(SortingLayer.layers[listView.selectedIndex]); + + if (SortingLayer.onLayerChanged != null) + SortingLayer.onLayerChanged(); }; sortingLayers.onRemove += (listView) => { @@ -253,6 +259,9 @@ void SortingLayersChanged(ChangeEvent evt) serializedObject.ApplyModifiedProperties(); serializedObject.Update(); tagManager.UpdateSortingLayersOrder(); + + if (SortingLayer.onLayerChanged != null) + SortingLayer.onLayerChanged(); }; //TextFields in Array are not bind correctly so we need to refresh them manually content.TrackPropertyValue(sortingLayersProperty, sp => sortingLayers.RefreshItems()); diff --git a/Editor/Mono/InternalEditorUtility.bindings.cs b/Editor/Mono/InternalEditorUtility.bindings.cs index 0e2415b91f..0c2c7e35fa 100644 --- a/Editor/Mono/InternalEditorUtility.bindings.cs +++ b/Editor/Mono/InternalEditorUtility.bindings.cs @@ -293,14 +293,8 @@ public static void SaveToSerializedFileAndForget(Object[] obj, string path, bool extern public static DragAndDropVisualMode HierarchyWindowDrag([Unmarshalled] HierarchyProperty property, HierarchyDropFlags dropMode, Transform parentForDraggedObjects, bool perform); public static DragAndDropVisualMode HierarchyWindowDragByID(int dropTargetInstanceID, HierarchyDropFlags dropMode, Transform parentForDraggedObjects, bool perform) - { - // place at scene pivot unless we have Pref for placing at root or we dont have a scene view - Vector3 worldPosition = Vector3.zero; - if (!GOCreationCommands.s_PlaceObjectsAtWorldOrigin.value && SceneView.lastActiveSceneView != null) - worldPosition = SceneView.lastActiveSceneView.pivot; + => HierarchyWindowDragByID(dropTargetInstanceID, GOCreationCommands.GetNewObjectPosition(), dropMode, parentForDraggedObjects, perform); - return HierarchyWindowDragByID(dropTargetInstanceID, worldPosition, dropMode, parentForDraggedObjects, perform); - } [FreeFunction("InternalEditorUtilityBindings::HierarchyWindowDragByID")] extern internal static DragAndDropVisualMode HierarchyWindowDragByID(int dropTargetInstanceID, Vector3 worldPosition, HierarchyDropFlags dropMode, Transform parentForDraggedObjects, bool perform); @@ -630,6 +624,15 @@ public static string TextifyEvent(Event evt) case KeyCode.F13: text = "F13"; break; case KeyCode.F14: text = "F14"; break; case KeyCode.F15: text = "F15"; break; + case KeyCode.F16: text = "F16"; break; + case KeyCode.F17: text = "F17"; break; + case KeyCode.F18: text = "F18"; break; + case KeyCode.F19: text = "F19"; break; + case KeyCode.F20: text = "F20"; break; + case KeyCode.F21: text = "F21"; break; + case KeyCode.F22: text = "F22"; break; + case KeyCode.F23: text = "F23"; break; + case KeyCode.F24: text = "F24"; break; case KeyCode.Escape: text = "[esc]"; break; @@ -1008,5 +1011,8 @@ internal static void PrepareDragAndDropTesting(EditorWindow editorWindow) // using this method but if you really need it, consult Desktop team first. [StaticAccessor("GetAuxWindowManager()", StaticAccessorType.Dot)] internal static extern void RetainAuxWindows(); + + [FreeFunction] + internal static extern bool IsPlaybackEngineDisabled(string engineName); } } diff --git a/Editor/Mono/Modules/DefaultBuildProfileExtension.cs b/Editor/Mono/Modules/DefaultBuildProfileExtension.cs index 067aa5d7f2..0b50e41a5b 100644 --- a/Editor/Mono/Modules/DefaultBuildProfileExtension.cs +++ b/Editor/Mono/Modules/DefaultBuildProfileExtension.cs @@ -97,6 +97,10 @@ public virtual void OnDisable() public virtual bool ShouldDrawExplicitNullCheckbox() => false; public virtual bool ShouldDrawExplicitDivideByZeroCheckbox() => false; public virtual bool ShouldDrawExplicitArrayBoundsCheckbox() => false; + public virtual bool ShouldDrawInstallInBuildFolderCheckbox() + { + return Unsupported.IsSourceBuild() && PostprocessBuildPlayer.SupportsInstallInBuildFolder(m_BuildTarget); + } protected virtual string GetPlatformProfileInfoMessage() { @@ -261,7 +265,7 @@ public void ShowCommonBuildOptions(BuildProfileWorkflowState workflowState) } } - if (Unsupported.IsSourceBuild() && PostprocessBuildPlayer.SupportsInstallInBuildFolder(m_BuildTarget)) + if (ShouldDrawInstallInBuildFolderCheckbox()) { using (new EditorGUILayout.HorizontalScope()) { diff --git a/Editor/Mono/Modules/DerivedBuildTargetExtensionsProvider.cs b/Editor/Mono/Modules/DerivedBuildTargetExtensionsProvider.cs index 832ee87f5e..85fad0168e 100644 --- a/Editor/Mono/Modules/DerivedBuildTargetExtensionsProvider.cs +++ b/Editor/Mono/Modules/DerivedBuildTargetExtensionsProvider.cs @@ -55,7 +55,7 @@ internal ISettingEditorExtension CreateSettingsEditorExtension(GUID buildTarget, { if (m_DerivedBuildTargetExtensions.TryGetValue(buildTarget, out var derivedBuildTargetExtensions)) { - return derivedBuildTargetExtensions.SettingEditorExtension ?? baseSettingEditorExtension; + return derivedBuildTargetExtensions.CreateSettingEditorExtension() ?? baseSettingEditorExtension; } return baseSettingEditorExtension; } @@ -64,7 +64,7 @@ internal IBuildProfileExtension CreateBuildProfileExtension(GUID buildTarget, IB { if (m_DerivedBuildTargetExtensions.TryGetValue(buildTarget, out var derivedBuildTargetExtensions)) { - return derivedBuildTargetExtensions.BuildProfileExtension ?? baseBuildProfileExtension; + return derivedBuildTargetExtensions.CreateBuildProfileExtension() ?? baseBuildProfileExtension; } return baseBuildProfileExtension; } diff --git a/Editor/Mono/Modules/IDerivedBuildTargetExtensions.cs b/Editor/Mono/Modules/IDerivedBuildTargetExtensions.cs index ea946e5931..f8d4dc93a0 100644 --- a/Editor/Mono/Modules/IDerivedBuildTargetExtensions.cs +++ b/Editor/Mono/Modules/IDerivedBuildTargetExtensions.cs @@ -8,8 +8,8 @@ internal interface IDerivedBuildTargetExtensions { ICompilationExtension CompilationExtension { get; } IBuildPostprocessor BuildPostprocessor { get; } - IBuildProfileExtension BuildProfileExtension { get; } IDerivedBuildTarget DerivedBuildTarget { get; } - ISettingEditorExtension SettingEditorExtension { get; } + IBuildProfileExtension CreateBuildProfileExtension(); + ISettingEditorExtension CreateSettingEditorExtension(); } } diff --git a/Editor/Mono/ObjectPool/PoolManager.cs b/Editor/Mono/ObjectPool/PoolManager.cs new file mode 100644 index 0000000000..ad66b8bfe1 --- /dev/null +++ b/Editor/Mono/ObjectPool/PoolManager.cs @@ -0,0 +1,29 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +namespace UnityEditor.ObjectPool +{ + [InitializeOnLoad] + static class PoolManager + { + static PoolManager() => EditorApplication.playModeStateChanged += OnEditorStateChange; + + static void OnEditorStateChange(PlayModeStateChange stateChange) + { + if(!EditorSettings.enterPlayModeOptionsEnabled + || !EditorSettings.enterPlayModeOptions.HasFlag(EnterPlayModeOptions.DisableDomainReload)) + { + return; + } + + switch (stateChange) + { + case PlayModeStateChange.EnteredEditMode: + case PlayModeStateChange.EnteredPlayMode: + UnityEngine.Pool.PoolManager.Reset(); + break; + } + } + } +} diff --git a/Editor/Mono/Overlays/Overlay.cs b/Editor/Mono/Overlays/Overlay.cs index 392d5f5b2c..fbdc5d719e 100644 --- a/Editor/Mono/Overlays/Overlay.cs +++ b/Editor/Mono/Overlays/Overlay.cs @@ -562,17 +562,6 @@ internal void RebuildContent() bool isCollapsed = m_Collapsed || activeLayout == 0; var targetContainer = tempTargetContainer ?? container; - if (isCollapsed) - { - if (collapsedContent.parent != contentRoot) - contentRoot.Add(collapsedContent); - } - else - { - m_CurrentContent = CreateContent(m_ActiveLayout); - contentRoot.Add(m_CurrentContent); - } - m_ContentsChanged = true; m_ActiveLayout = m_ActiveLayout == 0 ? targetContainer.preferredLayout : m_ActiveLayout; @@ -596,6 +585,17 @@ internal void RebuildContent() rootVisualElement.EnableInClassList(k_Collapsed, isCollapsed); rootVisualElement.EnableInClassList(k_Expanded, !isCollapsed); + if (isCollapsed) + { + if (collapsedContent.parent != contentRoot) + contentRoot.Add(collapsedContent); + } + else + { + m_CurrentContent = CreateContent(m_ActiveLayout); + contentRoot.Add(m_CurrentContent); + } + // Disable drop zone previews when floating var dropZonesDisplay = !floating ? DisplayStyle.Flex : DisplayStyle.None; m_BeforeDropZone.style.display = dropZonesDisplay; diff --git a/Editor/Mono/Overlays/OverlayCanvas.cs b/Editor/Mono/Overlays/OverlayCanvas.cs index e675b6a774..c3ab274816 100644 --- a/Editor/Mono/Overlays/OverlayCanvas.cs +++ b/Editor/Mono/Overlays/OverlayCanvas.cs @@ -331,6 +331,8 @@ internal OverlayContainer GetDockZoneContainer(DockZone zone) internal Action afterOverlaysInitialized; internal event Action overlaysEnabledChanged; internal event Action overlaysSupportEnabledChanged; + internal event Action presetChanged; + internal event Action overlayListChanged; @@ -711,7 +713,7 @@ internal void CopySaveData(out SaveData[] saveData) saveData[i] = new SaveData(saveData[i]); } - internal void ApplyPreset(OverlayPreset preset) + internal void ApplyPreset(IOverlayPreset preset) { if (!preset.CanApplyToWindow(containerWindow.GetType())) { @@ -722,6 +724,8 @@ internal void ApplyPreset(OverlayPreset preset) m_LastAppliedPresetName = preset.name; ApplySaveData(preset.saveData); + preset.ApplyCustomData(this); + presetChanged?.Invoke(); } internal void ApplySaveData(SaveData[] saveData) @@ -995,8 +999,15 @@ public void RestoreOverlay(Overlay overlay, SaveData data = null) { if(data == null) data = FindSaveData(overlay); - - EditorJsonUtility.FromJsonOverwrite(data.contents, overlay); + + try + { + EditorJsonUtility.FromJsonOverwrite(data.contents, overlay); + } + catch (ArgumentException) + { + data.contents = string.Empty; + } #pragma warning disable 618 if(string.IsNullOrEmpty(data.contents)) @@ -1045,7 +1056,7 @@ void RestoreOverlays() // Clear OverlayContainer instances and set Overlay.displayed to false. RestoreOverlay expects that Overlay // is not present in VisualElement hierarchy. - foreach (var overlay in overlays) + foreach (var overlay in this.overlays) { overlay.displayed = false; overlay.container?.RemoveOverlay(overlay); @@ -1055,12 +1066,13 @@ void RestoreOverlays() // 1. Find and associate all Overlays with SaveData (using default SaveData if necessary) // 2. Sort in ascending order by SaveData.index // 3. Apply SaveData, insert Overlay in Container - var ordered = new List>(); + var overlays = new List>(); - foreach(var o in overlays) - ordered.Add(new Tuple(FindSaveData(o), o)); + foreach(var o in this.overlays) + overlays.Add(new Tuple(FindSaveData(o), o)); - foreach (var o in ordered.OrderBy(x => x.Item1.index)) + var ordered = overlays.OrderBy(x => x.Item1.index); + foreach (var o in ordered) RestoreOverlay(o.Item2, o.Item1); afterOverlaysInitialized?.Invoke(); diff --git a/Editor/Mono/Overlays/OverlayPreset.cs b/Editor/Mono/Overlays/OverlayPreset.cs index d21a80ef8c..64c2cc872b 100644 --- a/Editor/Mono/Overlays/OverlayPreset.cs +++ b/Editor/Mono/Overlays/OverlayPreset.cs @@ -7,7 +7,7 @@ namespace UnityEditor.Overlays { - sealed class OverlayPreset : ScriptableObject, ISerializationCallbackReceiver + sealed class OverlayPreset : ScriptableObject, IOverlayPreset, ISerializationCallbackReceiver { [SerializeField, HideInInspector] string m_RawWindowType; @@ -44,6 +44,8 @@ public void OnAfterDeserialize() targetWindowType = Type.GetType(m_RawWindowType); } + public void ApplyCustomData(OverlayCanvas canvas) {} + public bool CanApplyToWindow(Type windowType) { return targetWindowType != null && targetWindowType.IsAssignableFrom(windowType); diff --git a/Editor/Mono/Overlays/OverlayPresetManager.cs b/Editor/Mono/Overlays/OverlayPresetManager.cs index c070a099cd..0c4bf2b446 100644 --- a/Editor/Mono/Overlays/OverlayPresetManager.cs +++ b/Editor/Mono/Overlays/OverlayPresetManager.cs @@ -13,6 +13,36 @@ namespace UnityEditor.Overlays { + interface IOverlayPreset + { + SaveData[] saveData { get; } + Type targetWindowType { get; } + bool CanApplyToWindow(Type windowType); + void ApplyCustomData(OverlayCanvas canvas); + string name { get; } + } + + sealed class DefaultOverlayPreset : IOverlayPreset + { + readonly static SaveData[] m_EmptySave = new SaveData[0]; + + public SaveData[] saveData => m_EmptySave; + public Type targetWindowType => null; + + public bool CanApplyToWindow(Type windowType) => true; + + public void ApplyCustomData(OverlayCanvas canvas) + { + // We set these package overlays manually for backward compatibility now that we don't have a Default save file + if (canvas.TryGetOverlay("Scene View/Navmesh Display", out var overlay)) overlay.displayed = false; + if (canvas.TryGetOverlay("Scene View/Agent Display", out overlay)) overlay.displayed = false; + if (canvas.TryGetOverlay("Scene View/Obstacle Display", out overlay)) overlay.displayed = false; + if (canvas.TryGetOverlay("Scene View/Occlusion Culling", out overlay)) overlay.displayed = false; + } + + public string name => OverlayPresetManager.defaultPresetName; + } + // This is responsible for loading and saving OverlayPresets. It does not serialize presets in the manager, but // rather loads and saves from the various locations that presets can exist. sealed class OverlayPresetManager : ScriptableSingleton @@ -24,6 +54,7 @@ sealed class OverlayPresetManager : ScriptableSingleton const string k_FileExtension = "overlay"; const string k_PresetAssetsName = "OverlayPresets.asset"; + internal const string defaultPresetName = "Default"; static string k_PreferencesAssetPath => Path.Combine(InternalEditorUtility.unityPreferencesFolder, "OverlayPresets/" + k_PresetAssetsName); static string k_ResourcesAssetPath => Path.Combine(EditorApplication.applicationContentsPath, "Resources/OverlayPresets/" + k_PresetAssetsName); static string preferencesPath => FileUtil.CombinePaths(InternalEditorUtility.unityPreferencesFolder, "OverlayPresets"); @@ -87,7 +118,6 @@ internal static void RevertPreferencesPresetsToDefault() { if (File.Exists(k_PreferencesAssetPath)) FileUtil.DeleteFileOrDirectory(k_PreferencesAssetPath); - FileUtil.CopyFileOrDirectory(k_ResourcesAssetPath, k_PreferencesAssetPath); } static void SaveAllPreferences() @@ -141,15 +171,22 @@ public static bool Exists(Type windowType, string presetName) return TryGetPreset(windowType, presetName, out _); } - public static OverlayPreset GetDefaultPreset(Type windowType) + public static IOverlayPreset GetDefaultPreset(Type windowType) { - var presets = GetAllPresets(windowType); - return presets?.FirstOrDefault(); + if (TryGetPreset(windowType, defaultPresetName, out OverlayPreset preset)) + return preset; + + return new DefaultOverlayPreset(); } - internal static IEnumerable GetAllPresets(Type windowType) + internal static IEnumerable GetAllPresets(Type windowType) { - List presets = new List(); + List presets = new List(); + + // Add a default preset if the user hasn't overwritten it + if (!TryGetPreset(windowType, defaultPresetName, out OverlayPreset preset)) + presets.Add(new DefaultOverlayPreset()); + foreach (var i in windowType.GetInterfaces()) { if (loadedPresets.TryGetValue(i, out var p)) @@ -347,12 +384,16 @@ public static void GenerateMenu(IGenericMenu menu, string pathPrefix, EditorWind } }); - foreach (var preset in presets) + foreach (var rawPreset in presets) { - menu.AddItem(L10n.Tr($"{pathPrefix}Delete Preset/{preset.name}"), false, () => + // Only add the ability to delete asset presets and not the ones created from code + if (rawPreset is OverlayPreset preset) { - DeletePreset(preset); - }); + menu.AddItem(L10n.Tr($"{pathPrefix}Delete Preset/{preset.name}"), false, () => + { + DeletePreset(preset); + }); + } } menu.AddItem(L10n.Tr($"{pathPrefix}Revert All Saved Presets"), false, () => diff --git a/Editor/Mono/PlayerSettings.bindings.cs b/Editor/Mono/PlayerSettings.bindings.cs index 589f147317..ae2e72d1a8 100644 --- a/Editor/Mono/PlayerSettings.bindings.cs +++ b/Editor/Mono/PlayerSettings.bindings.cs @@ -11,6 +11,7 @@ using UnityEngine; using UnityEngine.Bindings; using UnityEditor.Modules; +using UnityEditor.Build.Profile; namespace UnityEditor { @@ -1021,6 +1022,8 @@ public static void SetScriptingDefineSymbols(NamedBuildTarget buildTarget, strin if (!string.IsNullOrEmpty(defines)) defines = string.Join(";", ScriptingDefinesHelper.ConvertScriptingDefineStringToArray(defines)); + BuildProfileContext.UpdateScriptingDefineSymbolsInActivePlayerSettingsOverride(buildTarget, defines); + SetScriptingDefineSymbolsInternal(buildTarget.TargetName, defines); } @@ -1808,6 +1811,12 @@ internal static extern bool iosCopyPluginsCodeInsteadOfSymlink * Referenced by PlayerSettingsEditor when reading/writing to a non-active player settings object. */ + [StaticAccessor("PlayerSettingsBindings", StaticAccessorType.DoubleColon)] + internal static extern string GetScriptingDefineSymbols_Internal(PlayerSettings instance, string buildTargetName); + + [StaticAccessor("PlayerSettingsBindings", StaticAccessorType.DoubleColon)] + internal static extern void SetScriptingDefineSymbols_Internal(PlayerSettings instance, string buildTargetName, string defines); + [StaticAccessor("PlayerSettingsBindings", StaticAccessorType.DoubleColon)] internal static extern void GetBatchingForPlatform_Internal(PlayerSettings instance, BuildTarget platform, out int staticBatching, out int dynamicBatching); diff --git a/Editor/Mono/PlayerSettingsAndroid.bindings.cs b/Editor/Mono/PlayerSettingsAndroid.bindings.cs index 06d1964ee9..88d0b00a6b 100644 --- a/Editor/Mono/PlayerSettingsAndroid.bindings.cs +++ b/Editor/Mono/PlayerSettingsAndroid.bindings.cs @@ -114,6 +114,9 @@ public enum AndroidSdkVersions // Android 15.0, API level 35 AndroidApiLevel35 = 35, + + // Android 16.0, API level 36 + AndroidApiLevel36 = 36, } // Preferred application install location diff --git a/Editor/Mono/Prefabs/PrefabImporterEditor.cs b/Editor/Mono/Prefabs/PrefabImporterEditor.cs index 4ad91d03bc..825cfa6fd6 100644 --- a/Editor/Mono/Prefabs/PrefabImporterEditor.cs +++ b/Editor/Mono/Prefabs/PrefabImporterEditor.cs @@ -43,7 +43,7 @@ struct TrackedAsset readonly List m_TempComponentsResults = new List(); readonly List m_DirtyPrefabAssets = new List(); - bool m_HasPendingChanges; + internal bool m_HasPendingChanges; public override bool showImportedObject { get { return !hasMissingScripts; } } @@ -91,24 +91,34 @@ private void ObjectChangeEventPublished(ref ObjectChangeEventStream stream) for (int i = 0; i < stream.length; ++i) { + int instanceId = 0; if (stream.GetEventType(i) == ObjectChangeKind.ChangeGameObjectOrComponentProperties) { stream.GetChangeGameObjectOrComponentPropertiesEvent(i, out var changeGameObjectOrComponent); - var asset = EditorUtility.InstanceIDToObject(changeGameObjectOrComponent.instanceId); - if (IsTargetAsset(asset)) - { - if (!EditorFocusMonitor.AreBindableElementsSelected() && readyToAutoSave) - { - SaveDirtyPrefabAssets(true); - } - else - { - m_HasPendingChanges = true; - EditorApplication.update += WaitToApplyChanges; - } + instanceId = changeGameObjectOrComponent.instanceId; + } + else if (stream.GetEventType(i) == ObjectChangeKind.ChangeGameObjectStructure) + { + stream.GetChangeGameObjectStructureEvent(i, out var changeGameObject); + instanceId = changeGameObject.instanceId; + } - return; + if (instanceId == 0) + continue; + + var asset = EditorUtility.InstanceIDToObject(instanceId); + if (IsTargetAsset(asset)) + { + if (CanSave()) + { + SaveDirtyPrefabAssets(true); } + else + { + m_HasPendingChanges = true; + EditorApplication.update += WaitToApplyChanges; + } + return; } } } @@ -140,6 +150,17 @@ void OnDestroy() SaveDirtyPrefabAssets(false); } + /// + /// Determines whether auto-saving of the Prefab Asset is currently allowed. + /// Auto-saving is disabled if a UI field is focused or the CurveEditorWindow, ColorPicker or GradientPicker is visible. + /// + /// Returns true if auto-saving is allowed; otherwise, returns false. + internal bool CanSave() => !EditorFocusMonitor.AreBindableElementsSelected() && + readyToAutoSave && + !CurveEditorWindow.visible && + !ColorPicker.visible && + !GradientPicker.visible; + void WaitToApplyChanges() { var time = EditorApplication.timeSinceStartup; @@ -147,7 +168,7 @@ void WaitToApplyChanges() { m_NextUpdate = time + 0.2; - if (!EditorFocusMonitor.AreBindableElementsSelected() && readyToAutoSave) + if (CanSave()) SaveDirtyPrefabAssets(true); } } @@ -161,6 +182,9 @@ internal void SaveDirtyPrefabAssets(bool reloadInspectors) if (assetTarget == null) return; + if (reloadInspectors && !CanSave()) + Debug.LogWarning("SaveDirtyPrefabAssets should not be called when CanSave is false and reloading inspectors."); + m_DirtyPrefabAssets.Clear(); foreach (var asset in assetTargets) { diff --git a/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesTreeView.cs b/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesTreeView.cs index a852336403..da34bddaaa 100644 --- a/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesTreeView.cs +++ b/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesTreeView.cs @@ -747,7 +747,7 @@ VisualElement CreateComparisonView() var comparisonView = new ScrollView { mode = ScrollViewMode.Vertical, - verticalScrollerVisibility = ScrollerVisibility.AlwaysVisible + verticalScrollerVisibility = ScrollerVisibility.Auto }; comparisonView.RegisterCallback(OnObjectChanged); diff --git a/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs b/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs index a2d494dca1..f3ade9e1d5 100644 --- a/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs +++ b/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs @@ -136,7 +136,10 @@ class SceneViewProperties public static readonly GUIContent enableFilteringWhileSearching = EditorGUIUtility.TrTextContent("Enable filtering while searching", "If enabled, searching will cause non-matching items in the scene view to be greyed out"); public static readonly GUIContent enableFilteringWhileLodGroupEditing = EditorGUIUtility.TrTextContent("Enable filtering while editing LOD groups", "If enabled, editing LOD groups will cause other objects in the scene view to be greyed out"); public static readonly GUIContent handlesLineThickness = EditorGUIUtility.TrTextContent("Line Thickness", "Thickness of manipulator tool handle lines"); - public static readonly GUIContent createObjectsAtWorldOrigin = EditorGUIUtility.TrTextContent("Create Objects at Origin", "Enable this preference to instantiate new 3D objects at World coordinates 0,0,0. Disable it to instantiate them at the Scene pivot (in front of the Scene view Camera)."); + public static readonly GUIContent placementMode = EditorGUIUtility.TrTextContent("3D Placement Mode", "Select where newly created 3D objects are placed in the scene."); + public static readonly GUIContent createObjectsAtWorldOrigin = EditorGUIUtility.TrTextContent("World Origin"); + public static readonly GUIContent createObjectsAtRaycastToScenePivot = EditorGUIUtility.TrTextContent("Scene Intersection"); + public static readonly GUIContent createObjectsAtScenePivot = EditorGUIUtility.TrTextContent("Scene Pivot"); public static readonly GUIContent enableConstrainProportionsScalingForNewObjects = EditorGUIUtility.TrTextContent("Create Objects with Constrained Proportions scale on", "If enabled, scale in the transform component will be set to constrain proportions for new GameObjects by default"); public static readonly GUIContent useInspectorExpandedStateContent = EditorGUIUtility.TrTextContent("Auto-hide gizmos", "Automatically hide gizmos of Components collapsed in the Inspector"); public static readonly GUIContent ignoreAlwaysRefreshWhenNotFocused = EditorGUIUtility.TrTextContent("Refresh the Scene view only when the Editor is in focus.", "If enabled, ignore the \"Always Refresh\" flag on the Scene view when the Editor is not the foregrounded application."); @@ -209,7 +212,7 @@ private struct GICacheSettings private float m_EditorTextSharpness = 0.0f; private bool m_AllowAlphaNumericHierarchy = false; private PrefabStage.Mode m_DefaultPrefabModeFromHierarchy = PrefabStage.Mode.InContext; - private bool m_Create3DObjectsAtOrigin = false; + private GOCreationCommands.PlacementMode m_CreatePlacementMode = GOCreationCommands.PlacementMode.SceneIntersection; private float m_ProgressDialogDelay = 3.0f; private bool m_GraphSnapping; private bool m_EnableExtendedDynamicHints @@ -542,8 +545,10 @@ private void ShowGeneral(string searchContext) // Refresh skin to get new font Unsupported.ClearSkinCache(); - UnityEditor.EditorUtility.RequestScriptReload(); - EditorApplication.RequestRepaintAllTexts(VersionChangeType.Layout | VersionChangeType.Repaint); + + // Force a domain reload to get arround caching issue + EditorUtility.RequestScriptReload(); + //EditorApplication.RequestRepaintAllTexts(VersionChangeType.Layout | VersionChangeType.Repaint); } } @@ -553,8 +558,11 @@ private void ShowGeneral(string searchContext) { EditorPrefs.SetInt("EditorTextRenderingMode", (int)m_EditorTextRenderingMode); EditorTextSettings.currentEditorTextRenderingMode = m_EditorTextRenderingMode; - EditorApplication.RequestRepaintAllTexts(VersionChangeType.Layout | VersionChangeType.Repaint); EditorApplication.UpdateEditorTextRenderingMode(m_EditorTextRenderingMode); + + // Force a domain reload to get arround caching issue (changing to bitmap might change the sizes that the code is not expecting to change) + //EditorApplication.RequestRepaintAllTexts(VersionChangeType.Layout | VersionChangeType.Repaint); + EditorUtility.RequestScriptReload(); } if (m_EditorTextRenderingMode == EditorTextRenderingMode.SDF) @@ -888,7 +896,42 @@ private void ShowSceneView(string searchContext) EditorGUI.BeginChangeCheck(); GUILayout.Label("General", EditorStyles.boldLabel); - m_Create3DObjectsAtOrigin = EditorGUILayout.Toggle(SceneViewProperties.createObjectsAtWorldOrigin, m_Create3DObjectsAtOrigin); + + var oldLabelWidth = EditorGUIUtility.labelWidth; + var toggleLabelWidth = EditorStyles.label.CalcSize(SceneViewProperties.ignoreAlwaysRefreshWhenNotFocused).x; + EditorGUIUtility.labelWidth = toggleLabelWidth; + + GUIContent PlacementModeToGUIContent(GOCreationCommands.PlacementMode mode) + { + if (mode == GOCreationCommands.PlacementMode.SceneIntersection) + return SceneViewProperties.createObjectsAtRaycastToScenePivot; + else if(mode == GOCreationCommands.PlacementMode.WorldOrigin) + return SceneViewProperties.createObjectsAtWorldOrigin; + else if(mode == GOCreationCommands.PlacementMode.ScenePivot) + return SceneViewProperties.createObjectsAtScenePivot; + + return GUIContent.none; + } + void AddItem(GenericMenu menu, GOCreationCommands.PlacementMode mode) + { + menu.AddItem(PlacementModeToGUIContent(mode), m_CreatePlacementMode == mode, mode => + { + m_CreatePlacementMode = (GOCreationCommands.PlacementMode)mode; + WritePreferences(); + }, mode); + } + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField(SceneViewProperties.placementMode); + if (EditorGUILayout.DropdownButton(PlacementModeToGUIContent(m_CreatePlacementMode), FocusType.Keyboard)) + { + var menu = new GenericMenu(); + AddItem(menu, GOCreationCommands.PlacementMode.SceneIntersection); + AddItem(menu, GOCreationCommands.PlacementMode.WorldOrigin); + AddItem(menu, GOCreationCommands.PlacementMode.ScenePivot); + menu.ShowAsContext(); + } + EditorGUILayout.EndHorizontal(); + m_EnableConstrainProportionsScalingForNewObjects = EditorGUILayout.Toggle(SceneViewProperties.enableConstrainProportionsScalingForNewObjects, m_EnableConstrainProportionsScalingForNewObjects); AnnotationUtility.useInspectorExpandedState = EditorGUILayout.Toggle(SceneViewProperties.useInspectorExpandedStateContent, AnnotationUtility.useInspectorExpandedState); SceneView.s_PreferenceIgnoreAlwaysRefreshWhenNotFocused.value = EditorGUILayout.Toggle(SceneViewProperties.ignoreAlwaysRefreshWhenNotFocused, SceneView.s_PreferenceIgnoreAlwaysRefreshWhenNotFocused); @@ -900,6 +943,8 @@ private void ShowSceneView(string searchContext) SceneView.s_PreferenceEnableFilteringWhileSearching.value = EditorGUILayout.Toggle(SceneViewProperties.enableFilteringWhileSearching, SceneView.s_PreferenceEnableFilteringWhileSearching); SceneView.s_PreferenceEnableFilteringWhileLodGroupEditing.value = EditorGUILayout.Toggle(SceneViewProperties.enableFilteringWhileLodGroupEditing, SceneView.s_PreferenceEnableFilteringWhileLodGroupEditing); + EditorGUIUtility.labelWidth = oldLabelWidth; + if (EditorGUI.EndChangeCheck()) { WritePreferences(); @@ -1199,7 +1244,7 @@ private void WritePreferences() EditorPrefs.SetInt("DefaultPrefabModeFromHierarchy", (int)m_DefaultPrefabModeFromHierarchy); EditorPrefs.SetFloat("EditorBusyProgressDialogDelay", m_ProgressDialogDelay); - GOCreationCommands.s_PlaceObjectsAtWorldOrigin.value = m_Create3DObjectsAtOrigin; + GOCreationCommands.s_PlacementMode = m_CreatePlacementMode; EditorPrefs.SetString("GpuDeviceName", m_GpuDevice); EditorPrefs.SetBool("GICacheEnableCustomPath", m_GICacheSettings.m_EnableCustomPath); @@ -1290,7 +1335,14 @@ private void ReadPreferences() m_AllowAlphaNumericHierarchy = EditorPrefs.GetBool("AllowAlphaNumericHierarchy", false); m_DefaultPrefabModeFromHierarchy = GetDefaultPrefabModeForHierarchy(); m_ProgressDialogDelay = EditorPrefs.GetFloat("EditorBusyProgressDialogDelay", 3.0f); - m_Create3DObjectsAtOrigin = GOCreationCommands.s_PlaceObjectsAtWorldOrigin; + + m_CreatePlacementMode = GOCreationCommands.s_PlacementMode; + var create3DObjectsAtOrigin = EditorPrefs.GetBool("Create3DObject.PlaceAtWorldOrigin", false); + if (create3DObjectsAtOrigin && !EditorPrefs.HasKey("Create3DObject.PlacementMode")) + { + // Parse old preference to try and respect old pref + GOCreationCommands.s_PlacementMode = m_CreatePlacementMode = GOCreationCommands.PlacementMode.WorldOrigin; + } m_GpuDevice = EditorPrefs.GetString("GpuDeviceName"); diff --git a/Editor/Mono/PresetLibraries/ColorPresetLibrary.cs b/Editor/Mono/PresetLibraries/ColorPresetLibrary.cs index f5124598e6..ced8fa9ca6 100644 --- a/Editor/Mono/PresetLibraries/ColorPresetLibrary.cs +++ b/Editor/Mono/PresetLibraries/ColorPresetLibrary.cs @@ -161,7 +161,7 @@ public void CreateDebugColors() m_Presets.Add(new ColorPreset(new Color(Random.Range(0.2f, 1f), Random.Range(0.2f, 1f), Random.Range(0.2f, 1f), 1f), "Preset Color " + i)); } - private static Texture2D CreateColorSwatchWithBorder(int width, int height, bool triangular) + internal static Texture2D CreateColorSwatchWithBorder(int width, int height, bool triangular) { Texture2D texture = new Texture2D(width, height, TextureFormat.RGBA32, false); texture.hideFlags = HideFlags.HideAndDontSave; diff --git a/Editor/Mono/ProjectBrowser/ProjectBrowser.cs b/Editor/Mono/ProjectBrowser/ProjectBrowser.cs index e6f1cf2a2b..da01aafa4c 100644 --- a/Editor/Mono/ProjectBrowser/ProjectBrowser.cs +++ b/Editor/Mono/ProjectBrowser/ProjectBrowser.cs @@ -1811,7 +1811,9 @@ void HandleCommandEvents() return; } // Check if event is delete on root folder - if (ShouldDiscardCommandsEventsForRootFolders()) + // Note that if the folder is in favorite, there is no need for root check. + // Because we only remove it from the favorite list, not delete the asset. + if (!SelectionIsFavorite() && ShouldDiscardCommandsEventsForRootFolders()) { Debug.LogFormat(LogType.Warning, LogOption.NoStacktrace, null, k_WarningRootFolderDeletionFormat, Event.current.commandName); return; @@ -3117,6 +3119,9 @@ protected virtual void ShowButton(Rect r) internal bool SelectionIsFavorite() { + if (m_FolderTree == null) + return false; + if (m_FolderTree.GetSelection().Length != 1) return false; diff --git a/Editor/Mono/ProjectBrowser/ProjectWindowUtil.cs b/Editor/Mono/ProjectBrowser/ProjectWindowUtil.cs index 4451152d23..5a3552674a 100644 --- a/Editor/Mono/ProjectBrowser/ProjectWindowUtil.cs +++ b/Editor/Mono/ProjectBrowser/ProjectWindowUtil.cs @@ -1097,7 +1097,13 @@ internal static bool DeleteAssets(List instanceIDs, bool askIfSure) infotext.AppendLine(""); infotext.AppendLine(L10n.Tr("You cannot undo the delete assets action.")); - containsMaterial &= AnyTargetMaterialHasChildren(paths); + if (containsMaterial) + { + // If the assets to be deleted contain a material, check for its children. + // Warning: AnyTargetMaterialHasChildren will load assets so it can be costly. + containsMaterial = AnyTargetMaterialHasChildren(paths); + } + if (containsMaterial) { infotext.AppendLine(); diff --git a/Editor/Mono/SceneModeWindows/DefaultLightingExplorerExtension.cs b/Editor/Mono/SceneModeWindows/DefaultLightingExplorerExtension.cs index 3f1cfe4372..3a91253d36 100644 --- a/Editor/Mono/SceneModeWindows/DefaultLightingExplorerExtension.cs +++ b/Editor/Mono/SceneModeWindows/DefaultLightingExplorerExtension.cs @@ -471,7 +471,9 @@ protected virtual LightingExplorerTableColumn[] GetEmissivesColumns() if (EditorGUI.EndChangeCheck()) { Material material = (Material)prop.serializedObject.targetObject; + Undo.RecordObject(material, $"Modify Emission Flags of {material.name}"); material.globalIlluminationFlags = giFlags; + EditorUtility.SetDirty(material); prop.serializedObject.Update(); } @@ -495,7 +497,9 @@ protected virtual LightingExplorerTableColumn[] GetEmissivesColumns() if (EditorGUI.EndChangeCheck()) { + Undo.RecordObject(material, $"Modify Emission Flags of {material.name}"); material.SetColor("_EmissionColor", newValue); + EditorUtility.SetDirty(material); } EditorGUI.EndProperty(); } @@ -511,7 +515,9 @@ protected virtual LightingExplorerTableColumn[] GetEmissivesColumns() Color color = sourceMaterial.GetColor("_EmissionColor"); Material targetMaterial = (Material)target.serializedObject.targetObject; + Undo.RecordObject(targetMaterial, $"Modify Emission Flags of {targetMaterial.name}"); targetMaterial.SetColor("_EmissionColor", color); + EditorUtility.SetDirty(targetMaterial); }) // 3: Color }; } diff --git a/Editor/Mono/SceneModeWindows/SceneModeUtility.cs b/Editor/Mono/SceneModeWindows/SceneModeUtility.cs index a7b132b262..d56ef25e7d 100644 --- a/Editor/Mono/SceneModeWindows/SceneModeUtility.cs +++ b/Editor/Mono/SceneModeWindows/SceneModeUtility.cs @@ -12,8 +12,42 @@ namespace UnityEditor { public static class SceneModeUtility { + class SceneModeData : ScriptableSingleton + { + public string focusTypeName = null; + public SceneHierarchyWindow hierarchyWindow = null; + } + private static Type s_FocusType = null; - private static SceneHierarchyWindow s_HierarchyWindow = null; + private static Type focusType + { + get + { + if(s_FocusType == null && focusTypeName != null) + s_FocusType = Type.GetType(focusTypeName); + + return s_FocusType; + } + set + { + s_FocusType = value; + focusTypeName = s_FocusType?.AssemblyQualifiedName; + } + } + + private static string focusTypeName + { + get => SceneModeData.instance.focusTypeName; + set => SceneModeData.instance.focusTypeName = value; + } + + private static SceneHierarchyWindow hierarchyWindow + { + get => SceneModeData.instance.hierarchyWindow; + set => SceneModeData.instance.hierarchyWindow = value; + } + + private static GUIContent s_NoneButtonContent = null; private class Styles @@ -56,15 +90,15 @@ public static void SearchForType(Type type) if (win) { - s_HierarchyWindow = win; + hierarchyWindow = win; if (type == null || type == typeof(GameObject)) { - s_FocusType = null; + focusType = null; win.ClearSearchFilter(); } else { - s_FocusType = type; + focusType = type; if (win.searchMode == SearchableEditorWindow.SearchMode.Name) win.searchMode = SearchableEditorWindow.SearchMode.All; win.SetSearchFilter("t:" + type.Name, win.searchMode, false); @@ -72,7 +106,7 @@ public static void SearchForType(Type type) } } else - s_FocusType = null; + focusType = null; } public static Type SearchBar(params Type[] types) @@ -83,8 +117,11 @@ public static Type SearchBar(params Type[] types) s_NoneButtonContent.text = "None"; } - if (s_FocusType != null && (s_HierarchyWindow == null || s_HierarchyWindow.m_SearchFilter != "t:" + s_FocusType.Name)) - s_FocusType = null; + if (s_FocusType != null && + (hierarchyWindow == null || hierarchyWindow.m_SearchFilter != "t:" + s_FocusType.Name)) + { + focusType = null; + } GUILayout.Label("Scene Filter:"); @@ -94,7 +131,7 @@ public static Type SearchBar(params Type[] types) GUIContent label = EditorGUIUtility.TempContent( "All", AssetPreview.GetMiniTypeThumbnail(typeof(GameObject))); - if (TypeButton(label, s_FocusType == null, styles.typeButton)) + if (TypeButton(label, focusType == null, styles.typeButton)) SceneModeUtility.SearchForType(null); } @@ -110,13 +147,13 @@ public static Type SearchBar(params Type[] types) icon = AssetPreview.GetMiniTypeThumbnail(type); string name = ObjectNames.NicifyVariableName(type.Name) + "s"; GUIContent label = EditorGUIUtility.TempContent(name, icon); - if (TypeButton(label, type == s_FocusType, styles.typeButton)) + if (TypeButton(label, type == focusType, styles.typeButton)) SceneModeUtility.SearchForType(type); } GUILayout.FlexibleSpace(); EditorGUILayout.EndHorizontal(); - return s_FocusType; + return focusType; } private static bool TypeButton(GUIContent label, bool selected, GUIStyle style) diff --git a/Editor/Mono/SceneView/RectSelection.cs b/Editor/Mono/SceneView/RectSelection.cs index 86989d17d7..52040b82ff 100644 --- a/Editor/Mono/SceneView/RectSelection.cs +++ b/Editor/Mono/SceneView/RectSelection.cs @@ -191,7 +191,7 @@ void Pick(SelectionType selectionType, Vector2 mousePos, Event evt) evt.Use(); } - public void OnGUI() + public void OnGUI(bool isLastActiveSceneView) { Event evt = Event.current; @@ -201,7 +201,9 @@ public void OnGUI() { case EventType.Layout: case EventType.MouseMove: - if (!Tools.viewToolActive) + // Only add default control if the Scene view is focused. Otherwise, can + // cause issues when multiple Scene views open. + if (!Tools.viewToolActive && isLastActiveSceneView) HandleUtility.AddDefaultControl(k_RectSelectionID); break; case EventType.MouseDown: diff --git a/Editor/Mono/SceneView/SceneOrientationGizmo.cs b/Editor/Mono/SceneView/SceneOrientationGizmo.cs index 134067cdaf..f53ca03877 100644 --- a/Editor/Mono/SceneView/SceneOrientationGizmo.cs +++ b/Editor/Mono/SceneView/SceneOrientationGizmo.cs @@ -12,7 +12,7 @@ using Object = UnityEngine.Object; using BlendMode = UnityEngine.Rendering.BlendMode; -[Overlay(typeof(SceneView), "Orientation", true, priority = (int)OverlayPriority.Orientation)] +[Overlay(typeof(SceneView), "Orientation", true, priority = (int)OverlayPriority.Orientation, defaultDockZone = DockZone.RightColumn, defaultDockPosition = DockPosition.Top, defaultDockIndex = 0)] [Icon("Icons/Overlays/OrientationGizmo.png")] sealed class SceneOrientationGizmo : IMGUIOverlay { diff --git a/Editor/Mono/SceneView/SceneView.cs b/Editor/Mono/SceneView/SceneView.cs index c6e84b264b..cd0e11cc33 100644 --- a/Editor/Mono/SceneView/SceneView.cs +++ b/Editor/Mono/SceneView/SceneView.cs @@ -1004,7 +1004,8 @@ public CameraSettings cameraSettings internal Vector2 GetDynamicClipPlanes() { float farClip = Mathf.Clamp(2000f * size, 1000f, k_MaxCameraFarClip); - return new Vector2(farClip * 0.000005f, farClip); + float nearClip = Mathf.Max(k_MinCameraNearClip, farClip * 0.000005f); + return new Vector2(nearClip, farClip); } internal SceneViewGrid sceneViewGrids @@ -1212,7 +1213,9 @@ internal enum DraggingLockedState private Object m_LastLockedObject; [SerializeField] - private DrawCameraMode m_LastDebugDrawMode = DrawCameraMode.GIContributorsReceivers; + private CameraMode m_LastDebugDrawMode = SceneRenderModeWindow.defaultCameraMode; + //internal for tests + internal CameraMode lastDebugDrawMode => m_LastDebugDrawMode; [SerializeField] bool m_ViewIsLockedToObject; @@ -1407,6 +1410,11 @@ public override void OnEnable() Selection.selectedObjectWasDestroyed += OnSelectedObjectWasDestroyed; Selection.nonSelectedObjectWasDestroyed += OnNonSelectedObjectWasDestroyed; Lightmapping.lightingDataUpdated += RepaintAll; + + // UUM-102090: as m_LastDebugDrawMode was serialized with an incorrect value at some point (UUM-96180), + // we ensure on enable that the serialized value is correctly set. + if (lastDebugDrawMode.drawMode == DrawCameraMode.GIContributorsReceivers) + m_LastDebugDrawMode = SceneRenderModeWindow.defaultCameraMode; onCameraModeChanged += delegate { if (cameraMode.drawMode == DrawCameraMode.ShadowCascades) sceneLighting = true; @@ -1414,7 +1422,7 @@ public override void OnEnable() // If this a draw mode for debugging purposes, take note of it, so we can toggle back and forth between it and the previous mode. if (cameraMode.drawMode != DrawCameraMode.Textured && cameraMode.drawMode != DrawCameraMode.Wireframe && cameraMode.drawMode != DrawCameraMode.TexturedWire) { - m_LastDebugDrawMode = cameraMode.drawMode; + m_LastDebugDrawMode = cameraMode; } }; @@ -1596,16 +1604,6 @@ internal void Awake() m_PreviousScene = lastActiveSceneView; } - [RequiredByNativeCode] - internal static void PlaceGameObjectInFrontOfSceneView(GameObject go) - { - if (s_SceneViews.Count >= 1) - { - SceneView view = lastActiveSceneView; - if (view) - view.MoveToView(go.transform); - } - } internal static void AlignCameraWithView(Camera camera) { if (s_SceneViews.Count >= 1) @@ -2062,6 +2060,12 @@ void HandleClickAndDragToFocus() else if (GUIUtility.hotControl == 0 && draggingLocked == DraggingLockedState.Dragging) draggingLocked = DraggingLockedState.LookAt; + // UUM-90436. UITK's MouseEnterEvent/MouseEnterWindowEvent callbacks that + // drive `viewportsUnderMouse` seem to not fire reliably when a pen tablet is used. + if (!sceneViewMotion.viewportsUnderMouse && + (evt.type == EventType.MouseDown || evt.type == EventType.MouseMove)) + sceneViewMotion.viewportsUnderMouse = true; + if (evt.type == EventType.MouseDown) { Tools.s_ButtonDown = evt.button; @@ -2802,12 +2806,15 @@ static void OpenActionMenu(ShortcutArguments args) if (mouseOverWindow?.GetType() != typeof(SceneView)) return; - var mousePos = Event.current.mousePosition; + var mousePos = PointerDeviceState.GetPointerPosition(PointerId.mousePointerId, ContextType.Editor); var ve = focusedWindow.rootVisualElement.panel.Pick(mousePos); if (ve == null) return; var context = args.context as SceneViewViewport; + if(context == null) + return; + if (ve == context.window.cameraViewVisualElement) { ContextMenuUtility.ShowActionMenu(); @@ -2822,17 +2829,23 @@ internal void SwitchToRenderMode(DrawCameraMode mode, bool sceneLighting = true) this.cameraMode = GetBuiltinCameraMode(mode); } + internal void SwitchToRenderMode(CameraMode mode, bool sceneLighting = true) + { + this.sceneLighting = sceneLighting; + this.cameraMode = mode; + } + internal void SwitchToUnlit() => SwitchToRenderMode(DrawCameraMode.Textured, false); internal void ToggleLastDebugDrawMode() { - if (cameraMode.drawMode == m_LastDebugDrawMode) + if (cameraMode.drawMode == lastDebugDrawMode.drawMode) { SwitchToRenderMode(DrawCameraMode.Textured); } else { - SwitchToRenderMode(m_LastDebugDrawMode); + SwitchToRenderMode(lastDebugDrawMode); } } @@ -3126,7 +3139,7 @@ void DrawRenderModeOverlay(Rect cameraRect) private void HandleSelectionAndOnSceneGUI() { - m_RectSelection.OnGUI(); + m_RectSelection.OnGUI(this == lastActiveSceneView); CallOnSceneGUI(); } diff --git a/Editor/Mono/SceneView/SceneViewOverlays.cs b/Editor/Mono/SceneView/SceneViewOverlays.cs index 7881ac9bd8..32a5de2f18 100644 --- a/Editor/Mono/SceneView/SceneViewOverlays.cs +++ b/Editor/Mono/SceneView/SceneViewOverlays.cs @@ -9,7 +9,7 @@ namespace UnityEditor { public partial class SceneView { - [Overlay(typeof(SceneView), k_OverlayID, k_DisplayName)] + [Overlay(typeof(SceneView), k_OverlayID, k_DisplayName, defaultDisplay = false)] internal class SceneViewIsolationOverlay : TransientSceneViewOverlay { public const string k_OverlayID = "Scene View/Scene Visibility"; diff --git a/Editor/Mono/SceneView/SceneViewPiercingMenu.cs b/Editor/Mono/SceneView/SceneViewPiercingMenu.cs index 9723fc308d..85c1eb7c2f 100644 --- a/Editor/Mono/SceneView/SceneViewPiercingMenu.cs +++ b/Editor/Mono/SceneView/SceneViewPiercingMenu.cs @@ -150,7 +150,9 @@ static void ShowSelectionPiercingMenu(SceneView sceneView, bool forceSubtractive if (selectionPiercingMenu.MenuItems().Count == 0) selectionPiercingMenu.AppendAction("Nothing to Select Under Pointer", null, DropdownMenuAction.Status.Disabled); - selectionPiercingMenu.DoDisplayEditorMenu(new Rect(Event.current.mousePosition, Vector2.zero)); +#pragma warning disable CS0618 // Type or member is obsolete + selectionPiercingMenu.DoDisplayEditorMenuFromImGUI(new Rect(Event.current.mousePosition, Vector2.zero)); +#pragma warning restore CS0618 // Type or member is obsolete } } } diff --git a/Editor/Mono/SceneView/SceneViewToolbars.cs b/Editor/Mono/SceneView/SceneViewToolbars.cs index 98dfdf547c..7527118e9a 100644 --- a/Editor/Mono/SceneView/SceneViewToolbars.cs +++ b/Editor/Mono/SceneView/SceneViewToolbars.cs @@ -16,7 +16,7 @@ namespace UnityEditor // - UIServiceEditor/SceneView/SceneViewToolbarElements.cs // - UIServiceEditor/EditorToolbar/ToolbarElements/BuiltinTools.cs - [Overlay(typeof(SceneView), k_Id, "Tools", true, priority = (int)OverlayPriority.Tools)] + [Overlay(typeof(SceneView), k_Id, "Tools", true, priority = (int)OverlayPriority.Tools, defaultDockZone = DockZone.LeftColumn, defaultDockPosition = DockPosition.Top, defaultLayout = Layout.VerticalToolbar, defaultDockIndex = 0)] [Icon("Icons/Overlays/ToolsToggle.png")] class TransformToolsOverlayToolBar : ToolbarOverlay { @@ -71,7 +71,7 @@ void UpdateViewToolIcon() } } - [Overlay(typeof(SceneView), k_Id, "View Options", true, priority = (int)OverlayPriority.ViewOptions)] + [Overlay(typeof(SceneView), k_Id, "View Options", true, priority = (int)OverlayPriority.ViewOptions, defaultDockIndex = 0, defaultDockZone = DockZone.TopToolbar, defaultDockPosition = DockPosition.Bottom)] [Icon("Icons/Overlays/ViewOptions.png")] class SceneViewToolBar : ToolbarOverlay { @@ -88,7 +88,7 @@ public SceneViewToolBar() : base( "SceneView/Gizmos") {} } - [Overlay(typeof(SceneView), k_Id, "Draw Modes", true, priority = (int)OverlayPriority.DrawModes, defaultDockIndex = 1, defaultDockPosition = DockPosition.Bottom, defaultLayout = Layout.HorizontalToolbar, defaultDockZone = DockZone.TopToolbar)] + [Overlay(typeof(SceneView), k_Id, "Draw Modes", true, priority = (int)OverlayPriority.DrawModes, defaultDockIndex = 1, defaultDockPosition = DockPosition.Bottom, defaultDockZone = DockZone.TopToolbar)] [Icon("Icons/Overlays/ViewOptions.png")] class SceneViewCameraModeToolbar : ToolbarOverlay { @@ -134,7 +134,7 @@ internal void UpdateIcon(SceneView.CameraMode cameraMode) } } - [Overlay(typeof(SceneView), k_Id, "Search", true, priority = (int)OverlayPriority.Search)] + [Overlay(typeof(SceneView), k_Id, "Search", false, priority = (int)OverlayPriority.Search, defaultDockZone = DockZone.TopToolbar, defaultDockPosition = DockPosition.Bottom, defaultDockIndex = 2)] [Icon("Icons/Overlays/SearchOverlay.png")] class SearchToolBar : Overlay, ICreateHorizontalToolbar { @@ -156,7 +156,7 @@ public IEnumerable toolbarElements } } - [Overlay(typeof(SceneView), k_Id, "Grid and Snap", true, priority = (int)OverlayPriority.GridAndSnap)] + [Overlay(typeof(SceneView), k_Id, "Grid and Snap", true, priority = (int)OverlayPriority.GridAndSnap, defaultDockZone = DockZone.TopToolbar, defaultDockPosition = DockPosition.Top, defaultDockIndex = 1)] [Icon("Icons/Overlays/GridAndSnap.png")] class GridAndSnapToolBar : ToolbarOverlay { diff --git a/Editor/Mono/Scripting/AsyncInstantiateManager.cs b/Editor/Mono/Scripting/AsyncInstantiateManager.cs new file mode 100644 index 0000000000..c716dfb9e6 --- /dev/null +++ b/Editor/Mono/Scripting/AsyncInstantiateManager.cs @@ -0,0 +1,38 @@ +// 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 UnityEngine; + +namespace UnityEditor +{ + [InitializeOnLoad] + static class AsyncInstantiateManager + { + static AsyncInstantiateManager() + { + EditorApplication.playModeStateChanged += OnPlayModeStateChanged; + } + + static void OnPlayModeStateChanged(PlayModeStateChange stateChange) + { + // Pending operations can only leak from playmode to editmode. + // Editmode executes AsyncInstantiate synchronously. + if (stateChange == PlayModeStateChange.EnteredPlayMode + || stateChange == PlayModeStateChange.EnteredEditMode + || stateChange == PlayModeStateChange.ExitingEditMode) + return; + + CancelPendingOperations(); + } + + static void CancelPendingOperations() + { + AsyncInstantiateOperation.s_GlobalCancellation.Cancel(); + + // Ensure new token when no domain reload happens + AsyncInstantiateOperation.s_GlobalCancellation = new CancellationTokenSource(); + } + } +} diff --git a/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/BeeScriptCompilation.cs b/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/BeeScriptCompilation.cs index 44c952a121..bfb0f8a5f5 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/BeeScriptCompilation.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/BeeScriptCompilation.cs @@ -13,7 +13,6 @@ using Unity.Profiling; using UnityEditor.Build.Player; using UnityEditor.Compilation; -using UnityEditor.PackageManager; using UnityEditor.Scripting.Compilers; using UnityEngine; using CompilerMessage = UnityEditor.Scripting.Compilers.CompilerMessage; @@ -34,7 +33,7 @@ public static ScriptCompilationData ScriptCompilationDataFor( BuildTarget buildTarget, bool buildingForEditor, bool enableScriptUpdater, - string[] extraScriptingDefines = null) + string[] extraScriptingDefines = null, ScriptAssemblySettings settings = null) { // Need to call AssemblyDataFrom before calling CompilationPipeline.GetScriptAssemblies, // as that acts on the same ScriptAssemblies, and modifies them with different build settings. @@ -55,12 +54,21 @@ public static ScriptCompilationData ScriptCompilationDataFor( if (LocalizationDatabase.currentEditorLanguage != SystemLanguage.English && EditorPrefs.GetBool("Editor.kEnableCompilerMessagesLocalization", false)) localization = LocalizationDatabase.GetCulture(LocalizationDatabase.currentEditorLanguage); - var assembliesToScanForTypeDB = new HashSet(); var searchPaths = new HashSet(BuildPlayerDataGenerator.GetStaticSearchPaths(buildTarget)); - var options = EditorScriptCompilationOptions.BuildingIncludingTestAssemblies; - if (buildingForEditor) - options |= EditorScriptCompilationOptions.BuildingForEditor; + EditorScriptCompilationOptions options; + + if (settings is null) + { + options = EditorScriptCompilationOptions.BuildingIncludingTestAssemblies; + if (buildingForEditor) + options |= EditorScriptCompilationOptions.BuildingForEditor; + } + else + { + options = settings.CompilationOptions; + } + foreach (var a in editorCompilation.GetAllScriptAssemblies(options, extraScriptingDefines)) { if (!a.Flags.HasFlag(AssemblyFlags.EditorOnly)) diff --git a/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/PotentiallyUpdatableErrorMessages.cs b/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/PotentiallyUpdatableErrorMessages.cs index 78c2b4f781..7d8328ba53 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/PotentiallyUpdatableErrorMessages.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/PotentiallyUpdatableErrorMessages.cs @@ -4,60 +4,92 @@ using System.Linq; using System.Text.RegularExpressions; -using Bee.BeeDriver; using Bee.BinLog; using Bee.Serialization; using NiceIO; -using ScriptCompilationBuildProgram.Data; using UnityEditor.Scripting.Compilers; +using UnityEngine; namespace UnityEditor.Scripting.ScriptCompilation { - static class PotentiallyUpdatableErrorMessages + internal static class PotentiallyUpdatableErrorMessages { - public static bool IsAnyPotentiallyUpdatable(CompilerMessage[] messages, NodeFinishedMessage nodeResult, ObjectsFromDisk dataFromBuildProgram) + public static bool IsAnyPotentiallyUpdatable(CompilerMessage[] messages, NodeFinishedMessage nodeResult, + ObjectsFromDisk dataFromBuildProgram) { - var matches = messages.Select(m => MicrosoftCSharpCompilerOutputParser.sCompilerOutput.Match(m.message)).Where(m => m.Success).ToArray(); - var typeNames = matches.Select(MissingTypeNameFor).Where(t => t != null).ToArray(); + var matches = messages + .Select(m => MicrosoftCSharpCompilerOutputParser.sCompilerOutput.Match(m.message)) + .Where(m => m.Success && IsPotentiallyUpdatableDiagnostic(m)) + .ToArray(); - if (!typeNames.Any()) + if (matches.Length == 0) return false; + var localizedCompilerMessages = Helpers.LocalizeCompilerMessages(dataFromBuildProgram); + var compilerMessageParser = GetCompilerMessageParser(); + + var typeNames = matches.Select(m => MissingTypeNameFor(m, compilerMessageParser)); + var assemblyData = Helpers.FindOutputDataAssemblyInfoFor(nodeResult, dataFromBuildProgram); var lines = new NPath(assemblyData.MovedFromExtractorFile).ReadAllLines(); return typeNames.Any(t => lines.Contains(t)); - } - private static readonly Regex sMissingMember = new Regex(@"[^'`]*['`](?[^']+)'[^'`]+['`](?[^']+)'", RegexOptions.ExplicitCapture | RegexOptions.Compiled); - private static readonly Regex sMissingType = new Regex(@"[^'`]*['`](?[^']+)'[^'`]+['`](?[^']+)'", RegexOptions.ExplicitCapture | RegexOptions.Compiled); - private static readonly Regex sUnknownTypeOrNamespace = new Regex(@"[^'`]*['`](?[^']+)'.*", RegexOptions.ExplicitCapture | RegexOptions.Compiled); - - private static string MissingTypeNameFor(Match match) - { - Regex TypeNameParserFor(string compilerErrorCode) + CompilerMessageParser GetCompilerMessageParser() { - switch (compilerErrorCode) - { - case "CS0117": - return sMissingMember; - case "CS0246": - case "CS0103": - return sUnknownTypeOrNamespace; - case "CS0234": - return sMissingType; - default: - return null; - } + if (!localizedCompilerMessages) return EnglishMessageParser; + + return AlternativeLanguageMessageParser.TargetLanguages.Any(l => l == LocalizationDatabase.currentEditorLanguage) + ? AlternativeLanguageMessageParser + : EnglishMessageParser; } - var id = match.Groups["id"].Value; - var typeNameParser = TypeNameParserFor(id); + static bool IsPotentiallyUpdatableDiagnostic(Match match) => EnglishMessageParser.For(match.Groups["id"].Value) != null; + } + + private static string MissingTypeNameFor(Match match, CompilerMessageParser compilerMessageParser) + { + var typeNameParser = compilerMessageParser.For(match.Groups["id"].Value); if (typeNameParser == null) return null; var matchedMessage = typeNameParser.Match(match.Groups["message"].Value); return !matchedMessage.Success ? null : matchedMessage.Groups["type_name"].Value; } + + private static CompilerMessageParser EnglishMessageParser = new( + //error CS0117: 'type_name' does not contain a definition for 'member_name' + new Regex("[^'`“]*['`“](?[^'`”]+)['`”][^'`“]+['`“](?[^'`”]+)['`”]", RegexOptions.ExplicitCapture | RegexOptions.Compiled), + + //error CS0234: The type or namespace name 'type_name' does not exist in the namespace 'namespace' (are you missing an assembly reference?) + new Regex("[^'`]*['`](?[^']+)'[^'`]+['`](?[^']+)'",RegexOptions.ExplicitCapture | RegexOptions.Compiled), + + //error CS0246: The type or namespace name 'type_name' could not be found (are you missing a using directive or an assembly reference?) + //error CS0103: The name 'type_name' does not exist in the current context + new Regex("[^'`“]*['`“](?[^'`”]+)['`”].*", RegexOptions.ExplicitCapture | RegexOptions.Compiled), + new[] { SystemLanguage.English } + ); + + // Parses compilers messages for languages that reports CS0234 in the format: text `name space name` text `type name` (for instance, simplified chinese) + private static CompilerMessageParser AlternativeLanguageMessageParser = EnglishMessageParser with + { + MissingType = new Regex("([^'`“]*['`“](?[^'`”]+)['`”][^'`“]+['`“](?[^'`”]+)['`”])", RegexOptions.ExplicitCapture | RegexOptions.Compiled), + TargetLanguages = new[] { SystemLanguage.ChineseTraditional, SystemLanguage.ChineseSimplified, SystemLanguage.Korean } + }; + + // We have a `CompilerMessageParser` for each language that produces the errors listed in the `EnglishMessageParser` one above + // in a different format. For now we only have the `AlternativeLanguageMessageParser` that covers Simplified Chinese, + // Traditional Chinese and Korean + private record struct CompilerMessageParser(Regex MissingMember, Regex MissingType, Regex UnknownTypeOrNamespace, params SystemLanguage[] TargetLanguages) + { + public Regex For(string diagnosticId) => diagnosticId switch + { + "CS0117" => MissingMember, + "CS0246" => UnknownTypeOrNamespace, + "CS0103" => UnknownTypeOrNamespace, + "CS0234" => MissingType, + _ => null + }; + } } } diff --git a/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/UnityScriptUpdater.cs b/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/UnityScriptUpdater.cs index eb40258215..97ae2ffabd 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/UnityScriptUpdater.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/UnityScriptUpdater.cs @@ -230,5 +230,11 @@ public static AssemblyData_Out FindOutputDataAssemblyInfoFor(NodeFinishedMessage var assemblyDataOut = scriptCompilationDataOut.Assemblies.FirstOrDefault(a => a.Path == outputfileForwardSlash); return assemblyDataOut ?? throw new ArgumentException($"Unable to find entry for {outputfileForwardSlash} in dataFromBuildProgram"); } + + public static bool LocalizeCompilerMessages(ObjectsFromDisk dataFromBuildProgram) + { + var scriptCompilationDataOut = dataFromBuildProgram.Get(); + return scriptCompilationDataOut.LocalizeCompilerMessages; + } } } diff --git a/Editor/Mono/Scripting/ScriptCompilation/EditorBuildRules.cs b/Editor/Mono/Scripting/ScriptCompilation/EditorBuildRules.cs index 4e673ecb00..975a0047c9 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/EditorBuildRules.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/EditorBuildRules.cs @@ -78,6 +78,19 @@ internal static int PathFilter(string scriptPath, string pathPrefix, string lowe return depth; } + private static string[] CombineDefineArrays(string[] defines, string[] extraPlayerDefines) + { + if (defines == null) + return null; + + extraPlayerDefines = extraPlayerDefines ?? Array.Empty(); + + var combined = new string[defines.Length + extraPlayerDefines.Length]; + Array.Copy(defines, combined, defines.Length); + Array.Copy(extraPlayerDefines, 0, combined, defines.Length, extraPlayerDefines.Length); + return combined; + } + public static Dictionary CreateTargetAssemblies(IEnumerable customScriptAssemblies) { if (customScriptAssemblies == null) @@ -98,7 +111,7 @@ public static Dictionary CreateTargetAssemblies(IEnumera customAssembly.PathPrefix, customAssembly.AdditionalPrefixes, path => PathFilter(path, customAssembly.PathPrefix, lowerPathPrefix, customAssembly.AdditionalPrefixes, lowerAdditionalPathPrefixes), - (settings, defines) => customAssembly.IsCompatibleWith(settings.BuildTarget, settings.Subtarget, settings.CompilationOptions, defines), + (settings, defines) => customAssembly.IsCompatibleWith(settings.BuildTarget, settings.Subtarget, settings.CompilationOptions, CombineDefineArrays(defines, settings.ExtraGeneralDefines)), customAssembly.CompilerOptions) { ExplicitPrecompiledReferences = customAssembly.PrecompiledReferences?.ToList() ?? new List(), diff --git a/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs b/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs index 75667305d9..c22da8312f 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs @@ -862,12 +862,6 @@ public CompileStatus CompileScriptsWithSettings(ScriptAssemblySettings scriptAss ? Constants.ScriptAssembliesAndTypeDBTarget : Constants.ScriptAssembliesTarget; - var extraScriptingDefines = new List(); - if(scriptAssemblySettings.BuildingForEditor) - extraScriptingDefines.Add("UNITY_EDITOR"); - if(scriptAssemblySettings.BuildingDevelopmentBuild) - extraScriptingDefines.Add("DEVELOPMENT_BUILD"); - buildRequest.DataForBuildProgram.Add(() => BeeScriptCompilation.ScriptCompilationDataFor( this, scriptAssemblies, @@ -876,7 +870,7 @@ public CompileStatus CompileScriptsWithSettings(ScriptAssemblySettings scriptAss buildTarget, scriptAssemblySettings.BuildingForEditor, !scriptAssemblySettings.BuildingWithoutScriptUpdater, - extraScriptingDefines.ToArray())); + scriptAssemblySettings.ExtraGeneralDefines, scriptAssemblySettings)); var cts = new CancellationTokenSource(); diff --git a/Editor/Mono/Settings/SettingsProvider.cs b/Editor/Mono/Settings/SettingsProvider.cs index 8f9197e4b2..02d9d5bf0a 100644 --- a/Editor/Mono/Settings/SettingsProvider.cs +++ b/Editor/Mono/Settings/SettingsProvider.cs @@ -23,6 +23,7 @@ public class SettingsProvider private string m_Label; private string m_Name; private HashSet m_Keywords; + bool m_Activated; internal SettingsWindow settingsWindow { get; set; } internal string[] pathTokens { get; } @@ -136,6 +137,29 @@ internal virtual void FocusLost() { } + internal void Activate(string searchContext, VisualElement rootElement) + { + // If OnActivate fails, it should not be considered activated + if (!m_Activated) + OnActivate(searchContext, rootElement); + m_Activated = true; + } + + internal void Deactivate() + { + if (m_Activated) + { + // Set activated=false first, so even if OnDeactivate fails it will be considered deactivated. + m_Activated = false; + OnDeactivate(); + } + } + + public override string ToString() + { + return $"{GetType().Name} - {settingsPath}"; + } + #region Helper public static IEnumerable GetSearchKeywordsFromGUIContentProperties() { diff --git a/Editor/Mono/Settings/SettingsTreeView.cs b/Editor/Mono/Settings/SettingsTreeView.cs index 3fb4f01bd8..5cee9bf00d 100644 --- a/Editor/Mono/Settings/SettingsTreeView.cs +++ b/Editor/Mono/Settings/SettingsTreeView.cs @@ -32,7 +32,7 @@ private static class Styles public SettingsProvider currentProvider { get; private set; } public string searchContext { get; set; } - public delegate void ProviderChangedHandler(SettingsProvider lastSelectedProvider, SettingsProvider newlySelectedProvider); + public delegate bool ProviderChangedHandler(SettingsProvider lastSelectedProvider, SettingsProvider newlySelectedProvider); public event ProviderChangedHandler currentProviderChanged; public SettingsTreeView(TreeViewState state, SettingsProvider[] providers) @@ -67,8 +67,8 @@ protected override bool CanMultiSelect(TreeViewItem item) protected override void SelectionChanged(IList selectedIds) { SettingsProvider selectedProvider = GetFirstValidProvider(selectedIds.Count > 0 ? selectedIds.First() : -1); - currentProviderChanged?.Invoke(currentProvider, selectedProvider); - currentProvider = selectedProvider; + if (currentProviderChanged?.Invoke(currentProvider, selectedProvider) ?? true) + currentProvider = selectedProvider; } protected SettingsProvider GetFirstValidProvider(int id) diff --git a/Editor/Mono/Settings/SettingsWindow.cs b/Editor/Mono/Settings/SettingsWindow.cs index fd381bb278..c458634c48 100644 --- a/Editor/Mono/Settings/SettingsWindow.cs +++ b/Editor/Mono/Settings/SettingsWindow.cs @@ -9,7 +9,6 @@ using System.Text.RegularExpressions; using UnityEditor.Experimental; using UnityEditor.IMGUI.Controls; -using UnityEditor.UIElements; using UnityEngine; using UnityEngine.UIElements; using UnityEditor.StyleSheets; @@ -24,7 +23,7 @@ internal class SettingsWindow : EditorWindow, IHasCustomMenu [SerializeField] private Vector2 m_PosRight; [SerializeField] private SettingsScope m_Scope; - [SerializeField] public float m_SplitterFlex = 0.2f; + [SerializeField] public float m_SplitterPos; [SerializeField] private string m_SearchText; [SerializeField] private TreeViewState m_TreeViewState; @@ -35,11 +34,32 @@ internal class SettingsWindow : EditorWindow, IHasCustomMenu private VisualElement m_TreeViewContainer; private VisualElement m_Toolbar; private bool m_ProviderChanging; + private bool m_ProviderReloadNeeded; private bool m_SearchFieldGiveFocus; const string k_SearchField = "SearchField"; private const string k_MainSplitterViewDataKey = "settings-main-splitter__view-data-key"; + internal bool GuiCreated => m_SettingsPanel != null; + + internal bool ProviderReloadNeeded => m_ProviderReloadNeeded; + + struct ProviderChangingScope : IDisposable + { + SettingsWindow m_Window; + + public ProviderChangingScope(SettingsWindow window) + { + m_Window = window; + window.m_ProviderChanging = true; + } + + public void Dispose() + { + m_Window.m_ProviderChanging = false; + } + } + private static class ImguiStyles { public static readonly GUIStyle header = "SettingsHeader"; @@ -106,8 +126,12 @@ public void AddItemsToMenu(GenericMenu menu) [VisibleToOtherModules("UnityEditor.UIBuilderModule")] internal void SelectProviderByName(string name, bool ignoreLastSelected = true) { - if (m_TreeView == null) - Init(); + if (m_SettingsPanel == null) + { + SaveCurrentProvider(name); + return; + } + var currentSelection = m_TreeView.GetSelection(); var selectionID = name.GetHashCode(); // Check if the section is already selected to avoid the scroll bar to reset at the top of the window. @@ -141,24 +165,26 @@ internal void OnEnable() { titleContent.image = EditorGUIUtility.IconContent("Settings").image; + SettingsService.settingsProviderChanged -= OnSettingsProviderChanged; + SettingsService.settingsProviderChanged += OnSettingsProviderChanged; + SettingsService.repaintAllSettingsWindow -= OnRepaintAllWindows; + SettingsService.repaintAllSettingsWindow += OnRepaintAllWindows; + Undo.undoRedoEvent -= OnUndoRedoPerformed; + Undo.undoRedoEvent += OnUndoRedoPerformed; + EditorApplication.playModeStateChanged -= OnPlayModeStateChanged; + EditorApplication.playModeStateChanged += OnPlayModeStateChanged; - SetupUI(); + InitProviders(); } internal void OnDisable() { if (m_Splitter != null && m_Splitter.childCount >= 1) { - var splitLeft = m_Splitter.Children().First(); - float flexGrow = splitLeft.resolvedStyle.flexGrow; - EditorPrefs.SetFloat(GetPrefKeyName(nameof(m_Splitter)), flexGrow); + EditorPrefs.SetFloat(GetPrefKeyName(nameof(m_SplitterPos)), m_Splitter.fixedPaneDimension); } - if (m_TreeView != null && m_TreeView.currentProvider != null) - { - m_TreeView.currentProvider.OnDeactivate(); - EditorPrefs.SetString(GetPrefKeyName(titleContent.text + "_current_provider"), m_TreeView.currentProvider.settingsPath); - } + DeactivateAndSaveCurrentProvider(); SettingsService.settingsProviderChanged -= OnSettingsProviderChanged; SettingsService.repaintAllSettingsWindow -= OnRepaintAllWindows; @@ -166,29 +192,78 @@ internal void OnDisable() EditorApplication.playModeStateChanged -= OnPlayModeStateChanged; } - internal void InitProviders() + void CreateGUI() { - if (m_Providers != null) - return; - Init(); - RestoreSelection(); + SetupGUI(); + } - SettingsService.settingsProviderChanged -= OnSettingsProviderChanged; - SettingsService.settingsProviderChanged += OnSettingsProviderChanged; + internal void Update() + { + if (m_ProviderReloadNeeded) + ReloadProviders(); + } - SettingsService.repaintAllSettingsWindow -= OnRepaintAllWindows; - SettingsService.repaintAllSettingsWindow += OnRepaintAllWindows; + void SetupGUI() + { + var root = rootVisualElement; + root.AddStyleSheetPath("StyleSheets/SettingsWindowCommon.uss"); + root.AddStyleSheetPath($"StyleSheets/SettingsWindow{(EditorGUIUtility.isProSkin ? "Dark" : "Light")}.uss"); - Undo.undoRedoEvent -= OnUndoRedoPerformed; - Undo.undoRedoEvent += OnUndoRedoPerformed; + root.style.flexDirection = FlexDirection.Column; - EditorApplication.playModeStateChanged -= OnPlayModeStateChanged; - EditorApplication.playModeStateChanged += OnPlayModeStateChanged; + m_Toolbar = new IMGUIContainer(DrawToolbar); + root.Add(m_Toolbar); + + m_SplitterPos = EditorPrefs.GetFloat(GetPrefKeyName(nameof(m_SplitterPos)), 150f); + m_Splitter = new TwoPaneSplitView(0, m_SplitterPos, TwoPaneSplitViewOrientation.Horizontal) + { + name = "SettingsSplitter", + viewDataKey = k_MainSplitterViewDataKey + }; + m_Splitter.AddToClassList("settings-splitter"); + root.Add(m_Splitter); + + m_TreeViewContainer = new IMGUIContainer(DrawTreeView) + { + focusOnlyIfHasFocusableControls = false, + }; + m_TreeViewContainer.AddToClassList("settings-tree-imgui-container"); + m_Splitter.Add(m_TreeViewContainer); + + m_SettingsPanel = new VisualElement(); + m_SettingsPanel.AddToClassList("settings-panel"); + m_Splitter.Add(m_SettingsPanel); + + + // Restore selection after setting the ProviderChanged callback so we can activate the initial selected provider + RestoreSelection(); + } + + internal void InitProviders() + { + m_Providers = SettingsService.FetchSettingsProviders(m_Scope); + foreach (var provider in m_Providers) + { + provider.settingsWindow = this; + if (!provider.icon) + { + provider.icon = EditorGUIUtility.FindTexture("UnityEditor/EditorSettings Icon"); + } + } + + if (m_TreeView != null) + { + m_TreeView.currentProviderChanged -= ProviderChanged; + } + m_TreeViewState = m_TreeViewState ?? new TreeViewState(); + m_TreeView = new SettingsTreeView(m_TreeViewState, m_Providers); + m_TreeView.searchString = m_SearchText = m_SearchText ?? string.Empty; + m_TreeView.currentProviderChanged += ProviderChanged; } internal void OnInspectorUpdate() { - m_TreeView?.currentProvider?.OnInspectorUpdate(); + m_TreeView.currentProvider?.OnInspectorUpdate(); } private void OnUndoRedoPerformed(in UndoRedoInfo info) @@ -205,7 +280,7 @@ private void OnPlayModeStateChanged(PlayModeStateChange state) { if (m_TreeView.currentProvider != null) { - if (state == PlayModeStateChange.ExitingPlayMode) + if (state == PlayModeStateChange.ExitingEditMode) { ProviderChanged(m_TreeView.currentProvider, null); } @@ -233,16 +308,28 @@ private void PrintProviderKeywords() private void OnSettingsProviderChanged() { + // Prevent infinite changing if (m_ProviderChanging) return; - Init(); + + m_ProviderReloadNeeded = true; + } + + void ReloadProviders() + { + // Prevent recursive changing + if (m_ProviderChanging) + return; + DeactivateAndSaveCurrentProvider(); + InitProviders(); RestoreSelection(); Repaint(); + m_ProviderReloadNeeded = false; } private void RestoreSelection() { - var lastSelectedProvider = EditorPrefs.GetString(GetPrefKeyName(titleContent.text + "_current_provider"), ""); + var lastSelectedProvider = GetSavedCurrentProvider(); if (!string.IsNullOrEmpty(lastSelectedProvider) && Array.Find(m_Providers, provider => provider.settingsPath == lastSelectedProvider) != null) { SelectProviderByName(lastSelectedProvider); @@ -253,42 +340,41 @@ private void RestoreSelection() } } - private void Init() - { - m_Providers = SettingsService.FetchSettingsProviders(m_Scope); - foreach (var provider in m_Providers) - { - provider.settingsWindow = this; - if (!provider.icon) - { - provider.icon = EditorGUIUtility.FindTexture("UnityEditor/EditorSettings Icon"); - } - } - - m_TreeViewState = m_TreeViewState ?? new TreeViewState(); - m_TreeView = new SettingsTreeView(m_TreeViewState, m_Providers); - m_TreeView.searchString = m_SearchText = m_SearchText ?? string.Empty; - RestoreSelection(); - m_TreeView.currentProviderChanged += ProviderChanged; - } - - private void ProviderChanged(SettingsProvider lastSelectedProvider, SettingsProvider newlySelectedProvider) + private bool ProviderChanged(SettingsProvider lastSelectedProvider, SettingsProvider newlySelectedProvider) { if (m_SettingsPanel == null) - return; + return false; - m_ProviderChanging = true; - lastSelectedProvider?.OnDeactivate(); + using var pcd = new ProviderChangingScope(this); + // If we fail to deactivate the last provider, still continue to select the new one. + try + { + lastSelectedProvider?.Deactivate(); + } + catch (Exception e) + { + Debug.LogException(e); + } m_SettingsPanel.Clear(); if (newlySelectedProvider != null) { - newlySelectedProvider?.OnActivate(m_SearchText, m_SettingsPanel); - EditorPrefs.SetString(GetPrefKeyName(titleContent.text + "_current_provider"), newlySelectedProvider.settingsPath); + // If activating the new provider fails, restore the last selected provider. + try + { + newlySelectedProvider?.Activate(m_SearchText, m_SettingsPanel); + EditorPrefs.SetString(GetPrefKeyName(titleContent.text + "_current_provider"), newlySelectedProvider.settingsPath); + } + catch (Exception e) + { + Debug.LogException(e); + RestoreSelection(); + return false; + } } SetupIMGUIForCurrentProviderIfNeeded(); - m_ProviderChanging = false; + return true; } internal void SetupIMGUIForCurrentProviderIfNeeded() @@ -349,51 +435,6 @@ private static void UpdateSearchHighlight(VisualElement container, string search }); } - private void SetupUI() - { - SetupWindowPosition(); - - var root = rootVisualElement; - root.AddStyleSheetPath("StyleSheets/SettingsWindowCommon.uss"); - root.AddStyleSheetPath($"StyleSheets/SettingsWindow{(EditorGUIUtility.isProSkin ? "Dark" : "Light")}.uss"); - - root.style.flexDirection = FlexDirection.Column; - - m_Toolbar = new IMGUIContainer(DrawToolbar); - root.Add(m_Toolbar); - - m_SplitterFlex = EditorPrefs.GetFloat(GetPrefKeyName(nameof(m_Splitter)), m_SplitterFlex); - m_Splitter = new TwoPaneSplitView - { - name = "SettingsSplitter", - viewDataKey = k_MainSplitterViewDataKey - }; - m_Splitter.AddToClassList("settings-splitter"); - root.Add(m_Splitter); - m_TreeViewContainer = new IMGUIContainer(DrawTreeView) - { - style = - { - flexGrow = m_SplitterFlex, - flexBasis = 0f - }, - focusOnlyIfHasFocusableControls = false, - }; - m_TreeViewContainer.AddToClassList("settings-tree-imgui-container"); - m_Splitter.Add(m_TreeViewContainer); - - m_SettingsPanel = new VisualElement() - { - style = - { - flexGrow = 1.0f - m_SplitterFlex, - flexBasis = 0f - } - }; - m_SettingsPanel.AddToClassList("settings-panel"); - m_Splitter.Add(m_SettingsPanel); - } - private void DrawToolbar() { GUILayout.BeginHorizontal(EditorStyles.toolbar); @@ -514,10 +555,8 @@ private void DrawFooterBar() private void DrawTreeView() { - if (m_TreeView == null) - InitProviders(); - - var splitterRect = m_Splitter.fixedPane.layout; + // Splitter's fixedPane might only be available in the next `GeometryChangedEvent`. + var splitterRect = m_Splitter.fixedPane?.layout ?? Rect.zero; var splitterPos = splitterRect.xMax; var treeWidth = splitterPos; using (var scrollViewScope = new GUILayout.ScrollViewScope(m_PosLeft, GUILayout.Width(splitterPos), GUILayout.MaxWidth(splitterPos), GUILayout.MinWidth(splitterPos))) @@ -533,6 +572,33 @@ private void HandleSearchFiltering() UpdateSearchHighlight(m_SettingsPanel, m_SearchText); } + void DeactivateAndSaveCurrentProvider() + { + if (m_TreeView.currentProvider != null) + { + using var _ = new ProviderChangingScope(this); + try + { + m_TreeView.currentProvider.Deactivate(); + SaveCurrentProvider(m_TreeView.currentProvider.settingsPath); + } + catch (Exception e) + { + Debug.LogException(e); + } + } + } + + string GetSavedCurrentProvider() + { + return EditorPrefs.GetString(GetPrefKeyName(titleContent.text + "_current_provider"), ""); + } + + void SaveCurrentProvider(string settingsPath) + { + EditorPrefs.SetString(GetPrefKeyName(titleContent.text + "_current_provider"), settingsPath); + } + [MenuItem("Edit/Project Settings...", false, 20000, false)] internal static void OpenProjectSettings() { @@ -587,7 +653,7 @@ internal static SettingsWindow Show(SettingsScope scopes, string settingsPath = if (!settingsWindow.hasFocus) { settingsWindow.Show(); - settingsWindow.InitProviders(); + settingsWindow.SetupWindowPosition(); settingsWindow.Focus(); ignoreLastSelection = true; } diff --git a/Editor/Mono/Shaders/ShaderKeywordFilterData.cs b/Editor/Mono/Shaders/ShaderKeywordFilterData.cs index bee4cdee91..aff8c49d87 100644 --- a/Editor/Mono/Shaders/ShaderKeywordFilterData.cs +++ b/Editor/Mono/Shaders/ShaderKeywordFilterData.cs @@ -216,8 +216,20 @@ internal static SettingsNode GatherFilterData(string nodeName, object containerO containerConstraints.AddParentConstraints(parentConstraints); } + // Reflect all fields from the container's type. We must manually traverse the type's inheritance chain + // since, otherwise, there's no way to expose private fields belonging to the type's base class. + const BindingFlags kFieldReflectionFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance + | BindingFlags.Static | BindingFlags.DeclaredOnly; + var fields = new List(); + var containerType = containerObject.GetType(); + while (containerType != null) + { + var declaredFields = containerType.GetFields(kFieldReflectionFlags); + fields.AddRange(declaredFields); + containerType = containerType.BaseType; + } + // Go through all fields that could potentially contain filter attributes (directly or through children) - var fields = containerObject.GetType().GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Static); foreach (var f in fields) { bool isConst = f.IsLiteral && !f.IsInitOnly; diff --git a/Editor/Mono/StateMachine.bindings.cs b/Editor/Mono/StateMachine.bindings.cs index dcb8dee6b2..791852a8b9 100644 --- a/Editor/Mono/StateMachine.bindings.cs +++ b/Editor/Mono/StateMachine.bindings.cs @@ -107,6 +107,7 @@ public AnimatorTransition() [NativeHeader("Editor/Src/Animation/Transition.h")] [NativeHeader("Editor/Src/Animation/StateMachine.bindings.h")] + [HelpURL("class-Transition")] public class AnimatorStateTransition : AnimatorTransitionBase { public AnimatorStateTransition() diff --git a/Editor/Mono/Text/BlurryTextMappingTable.cs b/Editor/Mono/Text/BlurryTextMappingTable.cs index 3a018f5eba..9dcfb034c8 100644 --- a/Editor/Mono/Text/BlurryTextMappingTable.cs +++ b/Editor/Mono/Text/BlurryTextMappingTable.cs @@ -18,9 +18,9 @@ internal class BlurryTextCaching [SerializeField] public List m_FontAssetCorrespondingFontAssets = new (); [SerializeField] - public List m_FontAssetPointSize = new (); + public List m_FontAssetPointSize = new (); - Dictionary, FontAsset> m_FontAssetPointSizeLookup = new (); + Dictionary, FontAsset> m_FontAssetPointSizeLookup = new (); public void InitializeLookups() { @@ -36,7 +36,7 @@ public void InitializeLookups() } } - public void Add(FontAsset srcFontAsset, float pointSize, FontAsset dstFontAsset) + public void Add(FontAsset srcFontAsset, int pointSize, FontAsset dstFontAsset) { m_SrcFontAsset.Add(srcFontAsset); m_FontAssetPointSize.Add(pointSize); @@ -45,7 +45,7 @@ public void Add(FontAsset srcFontAsset, float pointSize, FontAsset dstFontAsset) m_FontAssetPointSizeLookup.Add(Tuple.Create(pointSize, srcFontAsset), dstFontAsset); } - public FontAsset Find(FontAsset srcFontAsset, float pointSize) + public FontAsset Find(FontAsset srcFontAsset, int pointSize) { return m_FontAssetPointSizeLookup.TryGetValue(Tuple.Create(pointSize, srcFontAsset), out var dstFontAsset) ? dstFontAsset : null; } diff --git a/Editor/Mono/Text/EditorFontAssetFactory.cs b/Editor/Mono/Text/EditorFontAssetFactory.cs new file mode 100644 index 0000000000..640c259e75 --- /dev/null +++ b/Editor/Mono/Text/EditorFontAssetFactory.cs @@ -0,0 +1,118 @@ +// 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; +using UnityEngine; +using UnityEngine.TextCore.Text; + +namespace UnityEditor +{ + internal class EditorFontAssetFactory : FontAssetFactory + { + // Define platform-specific system font name + public static readonly string SystemFontName = + "Liberation Sans" + ; + + public class FontFamilyConfig + { + public string familyName { get; } + public string regularStyle { get; } + public string boldStyle { get; } + public string italicStyle { get; } + public string boldItalicStyle { get; } + + public FontFamilyConfig(string familyName, string regularStyle, string boldStyle, string italicStyle, string boldItalicStyle) + { + this.familyName = familyName; + this.regularStyle = regularStyle; + this.boldStyle = boldStyle; + this.italicStyle = italicStyle; + this.boldItalicStyle = boldItalicStyle; + } + + public static readonly FontFamilyConfig k_SystemFontConfig = new FontFamilyConfig( + SystemFontName, + "Regular", + "Bold", + "Italic", + "Bold Italic" + ); + + public static readonly FontFamilyConfig k_InterFontConfig = new FontFamilyConfig( + "Inter", + "Regular", + "Semi Bold", + "Italic", + "Semi Bold Italic" + ); + } + + internal static FontAsset CreateFontFamilyAssets(FontFamilyConfig config) + { + // Create main (regular) font asset + FontAsset regularFontAsset = CreateFontAsset(config.familyName, config.regularStyle, 90); + if (regularFontAsset == null) return null; + + // Create bold font asset + var boldFontAsset = CreateFontAsset(config.familyName, config.boldStyle, 90); + if (boldFontAsset != null) + { + regularFontAsset.fontWeightTable[7].regularTypeface = boldFontAsset; + } + + // Create italic font asset + var italicFontAsset = CreateFontAsset(config.familyName, config.italicStyle, 90); + if (italicFontAsset != null) + { + regularFontAsset.fontWeightTable[4].italicTypeface = italicFontAsset; + } + + // Create bold italic font asset + var boldItalicFontAsset = CreateFontAsset(config.familyName, config.boldItalicStyle, 90); + if (boldItalicFontAsset != null) + { + regularFontAsset.fontWeightTable[7].italicTypeface = boldItalicFontAsset; + } + + return regularFontAsset; + } + + internal static FontAsset CreateFontAsset(string fontFamily, string fontStyle, int fontSize) + { + FontAsset fontAsset = FontAsset.CreateFontAssetInternal(fontFamily, fontStyle, fontSize); + if (fontAsset != null) + { + fontAsset.InternalDynamicOS = true; + SetupFontAssetSettings(fontAsset); + } + + return fontAsset; + } + + internal static void RegisterFontWithPaths( + List fontReferences, + Dictionary fontLookup, + FontAsset fontAsset, + string[] paths) + { + if (fontAsset == null) return; + + foreach (string path in paths) + { + var font = EditorGUIUtility.Load(path) as Font; + if (font == null) continue; + + int id = font.GetHashCode(); + + if (fontLookup.ContainsKey(id)) + continue; + + fontReferences.Add(new TextSettings.FontReferenceMap(font, fontAsset)); + fontLookup.Add(id, fontAsset); + } + } + } +} + diff --git a/Editor/Mono/Text/EditorTextSettings.cs b/Editor/Mono/Text/EditorTextSettings.cs index 9f19bab7c8..609276317b 100644 --- a/Editor/Mono/Text/EditorTextSettings.cs +++ b/Editor/Mono/Text/EditorTextSettings.cs @@ -8,7 +8,6 @@ using UnityEngine; using UnityEngine.TextCore.Text; using UnityEditor.Experimental; -using UnityEngine.Assertions; using UnityEngine.UIElements; using TextGenerationSettings = UnityEngine.TextCore.Text.TextGenerationSettings; @@ -39,11 +38,11 @@ static EditorTextSettings() TextGenerationSettings.IsEditorTextRenderingModeBitmap = () => currentEditorTextRenderingMode == EditorTextRenderingMode.Bitmap; IMGUITextHandle.GetEditorTextSettings = () => defaultTextSettings; IMGUITextHandle.GetBlurryFontAssetMapping = GetBlurryFontAssetMapping; - UITKTextHandle.GetBlurryMapping = GetBlurryFontAssetMapping; + UITKTextHandle.GetBlurryFontAssetMapping = GetBlurryFontAssetMapping; UITKTextHandle.CanGenerateFallbackFontAssets = CanGenerateFallbackFontAssets; } - public static FontAsset GetBlurryFontAssetMapping(float pointSize, FontAsset ft) + public static FontAsset GetBlurryFontAssetMapping(int pointSize, FontAsset ft) { if (pointSize < k_MinSupportedPointSize || pointSize >= k_MaxSupportedPointSize * k_MaxSupportedScaledPointSize) return ft; @@ -96,6 +95,7 @@ internal static EditorTextSettings defaultTextSettings { UpdateLocalizationFontAsset(); UpdateDefaultTextStyleSheet(); + s_DefaultTextSettings.CreateDefaultEditorFontAsset(); GUISkin.m_SkinChanged += UpdateDefaultTextStyleSheet; } } @@ -104,6 +104,42 @@ internal static EditorTextSettings defaultTextSettings } } + void CreateDefaultEditorFontAsset() + { + if (m_FontLookup == null) + { + m_FontLookup = new Dictionary(); + InitializeFontReferenceLookup(); + } + + // The default Editor Font Asset have already been created. + if (m_FontReferences.Count > 0) + return; + + var systemFontConfig = EditorFontAssetFactory.FontFamilyConfig.k_SystemFontConfig; + FontAsset systemRegularFontAsset = EditorFontAssetFactory.CreateFontFamilyAssets(systemFontConfig); + EditorFontAssetFactory.RegisterFontWithPaths(m_FontReferences, m_FontLookup, systemRegularFontAsset, + new[] + { + FontPaths.System.Normal, + FontPaths.System.Big, + FontPaths.System.Small, + FontPaths.System.Warning + }); + + FontAsset systemBoldFontAsset = EditorFontAssetFactory.CreateFontAsset(EditorFontAssetFactory.SystemFontName, "Bold", 90); + EditorFontAssetFactory.RegisterFontWithPaths(m_FontReferences, m_FontLookup, systemBoldFontAsset, + new[] + { + FontPaths.System.NormalBold, + FontPaths.System.SmallBold + }); + + var interFontConfig = EditorFontAssetFactory.FontFamilyConfig.k_InterFontConfig; + FontAsset interRegularFontAsset = EditorFontAssetFactory.CreateFontFamilyAssets(interFontConfig); + EditorFontAssetFactory.RegisterFontWithPaths(m_FontReferences, m_FontLookup, interRegularFontAsset, new[] { FontPaths.Inter.Regular }); + } + const int k_MinSupportedPointSize = 5; const int k_MaxSupportedPointSize = 19; const int k_MaxSupportedScaledPointSize = 3; @@ -115,7 +151,7 @@ internal override List GetFallbackFontAssets(int textPixelSize = -1) return GetFallbackFontAssetsInternal(textPixelSize); } - static List GetFallbackFontAssetsInternal(float textPixelSize) + static List GetFallbackFontAssetsInternal(int textPixelSize) { var editorTextSettings = defaultTextSettings; var localFallback = editorTextSettings.fallbackFontAssets[0]; @@ -143,7 +179,7 @@ static List GetFallbackFontAssetsInternal(float textPixelSize) }; } - static bool CanGenerateFallbackFontAssets(float textPixelSize) + static bool CanGenerateFallbackFontAssets(int textPixelSize) { if (textPixelSize < k_MinSupportedPointSize || textPixelSize >= k_MaxSupportedPointSize * k_MaxSupportedScaledPointSize) return true; @@ -210,4 +246,21 @@ internal static void UpdateDefaultTextStyleSheet() internal static readonly string s_LightEditorTextStyleSheetPath = "UIPackageResources/Light Editor Text StyleSheet.asset"; } + internal static class FontPaths + { + public static class System + { + public const string Big = "Fonts/System/System Big.ttf"; + public const string Normal = "Fonts/System/System Normal.ttf"; + public const string NormalBold = "Fonts/System/System Normal Bold.ttf"; + public const string Small = "Fonts/System/System Small.ttf"; + public const string SmallBold = "Fonts/System/System Small Bold.ttf"; + public const string Warning = "Fonts/System/System Warning.ttf"; + } + + public static class Inter + { + public const string Regular = "Fonts/Inter/Inter-Regular.ttf"; + } + } } diff --git a/Editor/Mono/Tools/EditorToolCache.cs b/Editor/Mono/Tools/EditorToolCache.cs index 7f12b503d5..ea00aae5bb 100644 --- a/Editor/Mono/Tools/EditorToolCache.cs +++ b/Editor/Mono/Tools/EditorToolCache.cs @@ -287,28 +287,14 @@ public void InstantiateEditors(EditorToolContext ctx, List edit { editors.Clear(); - // If the shared tracker is locked, use fallback tracker instance so that the current selection is always + // If the shared tracker is locked, use fallback tracker instance so that the active selection is always // represented. Addresses case where a single locked inspector is open. var shared = ActiveEditorTracker.sharedTracker; var activeTracker = shared.isLocked ? ActiveEditorTracker.fallbackTracker : shared; - var propertyEditors = PropertyEditor.GetPropertyEditors(); - // Collect editor tools for the shared tracker first, then any locked inspectors or open properties editors + // We collect editors only for the tracker that represents the active selection. CollectEditorsForTracker(ctx, activeTracker, editors); - foreach (var propertyEditor in propertyEditors) - { - if (propertyEditor is InspectorWindow) - { - if ((propertyEditor as InspectorWindow).isLocked) - CollectEditorsForTracker(ctx, propertyEditor.tracker, editors); - } - else - { - CollectEditorsForTracker(ctx, propertyEditor.tracker, editors); - } - } - foreach (var editor in editors) editor.InstantiateEditor(); } diff --git a/Editor/Mono/Tools/EditorToolSettingsOverlay.cs b/Editor/Mono/Tools/EditorToolSettingsOverlay.cs index fa1f1318ae..eaea7ab29b 100644 --- a/Editor/Mono/Tools/EditorToolSettingsOverlay.cs +++ b/Editor/Mono/Tools/EditorToolSettingsOverlay.cs @@ -14,7 +14,7 @@ namespace UnityEditor.EditorTools { - [Overlay(typeof(SceneView), "Tool Settings", true, priority = (int)OverlayPriority.ToolSettings)] + [Overlay(typeof(SceneView), "Tool Settings", true, priority = (int)OverlayPriority.ToolSettings, defaultDockZone = DockZone.TopToolbar, defaultDockPosition = DockPosition.Top, defaultDockIndex = 0)] [Icon("Icons/Overlays/ToolSettings.png")] sealed class EditorToolSettingsOverlay : Overlay, ICreateToolbar, ICreateHorizontalToolbar, ICreateVerticalToolbar { diff --git a/Editor/Mono/Tools/EditorToolUtility.cs b/Editor/Mono/Tools/EditorToolUtility.cs index 6f4a32abc6..8fd596f292 100644 --- a/Editor/Mono/Tools/EditorToolUtility.cs +++ b/Editor/Mono/Tools/EditorToolUtility.cs @@ -234,7 +234,7 @@ internal static GUIContent GetToolbarIcon(T obj) where T : IEditor { if (obj == null) return GetIcon(typeof(T)); - if (obj is EditorTool tool && tool.toolbarIcon != null && tool.toolbarIcon.image != null) + if (obj is EditorTool tool && tool.toolbarIcon != null) return tool.toolbarIcon; return GetIcon(obj.GetType()); } diff --git a/Editor/Mono/TooltipView/TooltipView.cs b/Editor/Mono/TooltipView/TooltipView.cs index 53b7fc6f25..72cb44eae1 100644 --- a/Editor/Mono/TooltipView/TooltipView.cs +++ b/Editor/Mono/TooltipView/TooltipView.cs @@ -196,7 +196,7 @@ Vector2 Size // If when fitted to screen, the tooltip would overlap the hover area // (and thus potentially mouse) -- for example when the control is near // the bottom of screen, place it atop of the hover area instead. - var fittedToScreen = ContainerWindow.FitRectToScreen(popupPosition, true, true); + var fittedToScreen = ContainerWindow.FitRectToMouseScreen(popupPosition, true, null); if (fittedToScreen.Overlaps(m_hoverRect)) { popupPosition.y = m_hoverRect.y - m_optimalSize.y - 10.0f; diff --git a/Editor/Mono/UIElements/Controls/GradientField.cs b/Editor/Mono/UIElements/Controls/GradientField.cs index 894b1b25bb..a999e44779 100644 --- a/Editor/Mono/UIElements/Controls/GradientField.cs +++ b/Editor/Mono/UIElements/Controls/GradientField.cs @@ -154,6 +154,8 @@ internal static Gradient GradientCopy(Gradient other) VisualElement m_GradientTextureImage; readonly Background m_DefaultBackground = new Background(); + bool isShowingGradientPicker => GradientPicker.visible && rawValue != null && ReferenceEquals(GradientPicker.gradient, rawValue); + /// /// Constructor. /// @@ -232,6 +234,9 @@ protected override void ExecuteDefaultActionAtTarget(EventBase evt) void OnDetach() { + if (isShowingGradientPicker) + GradientPicker.CloseWindow(false); + if (style.backgroundImage.value.texture != null) { Object.DestroyImmediate(style.backgroundImage.value.texture); @@ -295,6 +300,12 @@ public override void SetValueWithoutNotify(Gradient newValue) rawValue.mode = GradientMode.Blend; } UpdateGradientTexture(); + + // Update the GradientPicker if it's open and not currently being interacted with. (UUM-100664) + if (isShowingGradientPicker && GUIUtility.hotControl == 0) + { + GradientPicker.RefreshGradientData(); + } } protected override void UpdateMixedValueContent() diff --git a/Editor/Mono/UIElements/Controls/PropertyField.cs b/Editor/Mono/UIElements/Controls/PropertyField.cs index b38fef551f..b1617fb822 100644 --- a/Editor/Mono/UIElements/Controls/PropertyField.cs +++ b/Editor/Mono/UIElements/Controls/PropertyField.cs @@ -408,7 +408,7 @@ private void ResetDecoratorDrawers(PropertyHandler handler) { var decoratorRect = new Rect(); decoratorRect.height = decorator.GetHeight(); - decoratorRect.width = resolvedStyle.width; + decoratorRect.width = ve.rect.width; decorator.OnGUI(decoratorRect); ve.style.height = decoratorRect.height; }); @@ -760,10 +760,26 @@ private TField ConfigureField(TField field, SerializedProperty p field.label = fieldLabel; ConfigureFieldStyles(field); + ConfigureTooltip(property, field); return field; } + private void ConfigureTooltip(SerializedProperty property, BaseField field) + { + var fieldInfo = ScriptAttributeUtility.GetFieldInfoFromProperty(property, out _); + + if (fieldInfo == null) + { + return; + } + + var hasTooltipAttribute = fieldInfo.IsDefined(typeof(TooltipAttribute), false); + + // Don't display elided tooltip if the property has a TooltipAttribute. + field.labelElement.displayTooltipWhenElided = !hasTooltipAttribute; + } + private VisualElement ConfigureLabelOnly(SerializedProperty property) { var wrapper = new VisualElement(); diff --git a/Editor/Mono/UIElements/Controls/Toolbar/IToolbarMenuElement.cs b/Editor/Mono/UIElements/Controls/Toolbar/IToolbarMenuElement.cs index 4fa347fe68..027ccbb8ba 100644 --- a/Editor/Mono/UIElements/Controls/Toolbar/IToolbarMenuElement.cs +++ b/Editor/Mono/UIElements/Controls/Toolbar/IToolbarMenuElement.cs @@ -45,7 +45,7 @@ public static void ShowMenu(this IToolbarMenuElement tbe) // windows are stuck to the left side of the screen as well as our menus) worldBound.x = 1f; } - tbe.menu.DoDisplayEditorMenu(worldBound); + tbe.menu.DoDisplayEditorMenu(worldBound, ve); } } } diff --git a/Editor/Mono/UIElements/EditorMenuExtensions.cs b/Editor/Mono/UIElements/EditorMenuExtensions.cs index 83bbc71ab3..ec834bc5fb 100644 --- a/Editor/Mono/UIElements/EditorMenuExtensions.cs +++ b/Editor/Mono/UIElements/EditorMenuExtensions.cs @@ -2,6 +2,7 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using System; using UnityEngine; using UnityEngine.UIElements; @@ -10,11 +11,11 @@ namespace UnityEditor.UIElements static class EditorMenuExtensions { // DropdownMenu - static IGenericMenu PrepareMenu(DropdownMenu menu, EventBase triggerEvent) + static GenericOSMenu PrepareMenu(DropdownMenu menu, EventBase triggerEvent) { menu.PrepareForDisplay(triggerEvent); - var genericMenu = new GenericOSMenu(); + var genericMenu = new GenericOSMenu(menu.allowDuplicateNames); foreach (var item in menu.MenuItems()) { var action = item as DropdownMenuAction; @@ -53,9 +54,17 @@ static IGenericMenu PrepareMenu(DropdownMenu menu, EventBase triggerEvent) return genericMenu; } - public static void DoDisplayEditorMenu(this DropdownMenu menu, Rect rect) + public static void DoDisplayEditorMenu(this DropdownMenu menu, Rect rect, VisualElement ve) { - PrepareMenu(menu, null).DropDown(rect); + PrepareMenu(menu, null).DropDown(rect, ve); + } + + // This is for backward compatibility with code triggering from imgui, but it wont allow spanning the menu from code (menu item, across window) + // Try using DoDisplayEditorMenu that takes an EventBase or an visualElement instead + [Obsolete("Use DoDisplayEditorMenu instead")] + public static void DoDisplayEditorMenuFromImGUI(this DropdownMenu menu, Rect rect) + { + PrepareMenu(menu, null).DropDownIMGUI(rect); } public static void DoDisplayEditorMenu(this DropdownMenu menu, EventBase triggerEvent) @@ -76,8 +85,29 @@ public static void DoDisplayEditorMenu(this DropdownMenu menu, EventBase trigger position = triggerEvent.elementTarget.layout.center; } - genericMenu.DropDown(new Rect(position, Vector2.zero)); + genericMenu.DropDown(new Rect(position, Vector2.zero), triggerEvent.elementTarget); + } + + public static Rect GUIToScreenRect(VisualElement targetElement, Rect position) + { + var panel = targetElement.elementPanel; + + if (panel.contextType == ContextType.Editor && panel.ownerObject is View view) + { + // Convert first the postion in the panel to the position in UI pixels as per the editor's window definition + // This will not work in test where we disconnect the panel DPI from the window DPI + + position.x *= panel.scale; + position.y *= panel.scale; + position.width *= panel.scale; + position.height *= panel.scale; + // Add the offset of window to get the position in screen space + // It include the position from the guiView to the root of the containerWindow and from the containerWindow to the screen + position.position += view.screenPosition.position; + } + + return position; } } @@ -85,9 +115,10 @@ internal class GenericOSMenu : IGenericMenu { GenericMenu m_GenericMenu; - public GenericOSMenu() + public GenericOSMenu(bool allowDuplicateNames = false) { m_GenericMenu = new GenericMenu(); + m_GenericMenu.allowDuplicateNames = allowDuplicateNames; } public GenericOSMenu(GenericMenu genericMenu) @@ -123,7 +154,20 @@ public void AddSeparator(string path) public void DropDown(Rect position, VisualElement targetElement = null, bool anchored = false) { - m_GenericMenu.DropDown(position); + if(targetElement is null || targetElement.panel == null) + { + Debug.LogError("Cannot show dropdown menu with a target visualElement not in a panel"); + return; + } + + position = EditorMenuExtensions.GUIToScreenRect(targetElement, position); + m_GenericMenu.DropDownScreenSpace(position, false); + } + + [Obsolete("Use DropDown(Rect position, VisualElement targetElement, bool anchored) instead")] + public void DropDownIMGUI(Rect position, bool anchored = false) + { + m_GenericMenu.DropDown(position, anchored); } } } diff --git a/Editor/Mono/UIElements/StyleSheets/StyleSheetImporterImpl.cs b/Editor/Mono/UIElements/StyleSheets/StyleSheetImporterImpl.cs index 73aa4f67a2..88bad3c25e 100644 --- a/Editor/Mono/UIElements/StyleSheets/StyleSheetImporterImpl.cs +++ b/Editor/Mono/UIElements/StyleSheets/StyleSheetImporterImpl.cs @@ -437,6 +437,10 @@ protected void VisitUrlFunction(PrimitiveTerm term) // explicit asset reference already loaded m_Context?.DependsOnArtifact(projectRelativePath); + + // Necessary to avoid the warning "Import of asset setup artifact dependency to but dependency isn't used + // and therefore not registered in the asset database". (UUM-68160) + AssetDatabase.LoadAssetAtPath(projectRelativePath, typeof(Object)); } else { diff --git a/Editor/Mono/UIElements/StyleSheets/URIHelpers.cs b/Editor/Mono/UIElements/StyleSheets/URIHelpers.cs index 8bd34eaa16..98ac4a7b29 100644 --- a/Editor/Mono/UIElements/StyleSheets/URIHelpers.cs +++ b/Editor/Mono/UIElements/StyleSheets/URIHelpers.cs @@ -198,7 +198,18 @@ public static URIValidationResponse ValidateAssetURL(string assetPath, string pa var query = ExtractUriQueryParameters(absoluteUri); - // note: we could relax this and support queries with only a guid parameter, resolving the main asset + // Guid only + if (query.hasGuid && !query.hasFileId && !query.hasType) + { + var pathFromGuid = AssetDatabase.GUIDToAssetPath(query.guid); + if (AssetExistsAtPath(pathFromGuid)) + { + response.resolvedProjectRelativePath = pathFromGuid; + response.result = URIValidationResult.OK; + return response; + } + } + if (query.hasAllReferenceParams) { // TODO: refactor to avoid the unnecessary JSON serialization diff --git a/External/NiceIO/NiceIO.cs b/External/NiceIO/NiceIO.cs index c43974d22f..5f559e04d8 100644 --- a/External/NiceIO/NiceIO.cs +++ b/External/NiceIO/NiceIO.cs @@ -16,6 +16,8 @@ using File = NiceIO.Do_Not_Use_File_Directly_Use_FileSystem_Active_Instead; using Directory = NiceIO.Do_Not_Use_Directory_Directly_Use_FileSystem_Active_Instead; +#nullable disable + namespace NiceIO { /// diff --git a/External/unitytls/builds/CSharp/BindingsUnity/TLSAgent.gen.bindings.cs b/External/unitytls/builds/CSharp/BindingsUnity/TLSAgent.gen.bindings.cs index e9d6371044..52b73949fc 100644 --- a/External/unitytls/builds/CSharp/BindingsUnity/TLSAgent.gen.bindings.cs +++ b/External/unitytls/builds/CSharp/BindingsUnity/TLSAgent.gen.bindings.cs @@ -81,21 +81,21 @@ internal static unsafe partial class Binding public const int UNITYTLS_SSL_HANDSHAKE_SERVER_FINISHED = 13; public const int UNITYTLS_SSL_HANDSHAKE_FLUSH_BUFFERS = 14; public const int UNITYTLS_SSL_HANDSHAKE_WRAPUP = 15; - public const int UNITYTLS_SSL_HANDSHAKE_OVER = 16; - public const int UNITYTLS_SSL_HANDSHAKE_SERVER_NEW_SESSION_TICKET = 17; - public const int UNITYTLS_SSL_HANDSHAKE_HELLO_VERIFY_REQUIRED = 18; + public const int UNITYTLS_SSL_HANDSHAKE_OVER = 27; + public const int UNITYTLS_SSL_HANDSHAKE_SERVER_NEW_SESSION_TICKET = 16; + public const int UNITYTLS_SSL_HANDSHAKE_HELLO_VERIFY_REQUIRED = 17; /// Number of constant definitions - public const int UNITYTLS_SSL_HANDSHAKE_COUNT = 19; + public const int UNITYTLS_SSL_HANDSHAKE_COUNT = 28; /// Deprecated: for backward compatibility only public const int UNITYTLS_SSL_HANDSHAKE_BEGIN = 0; /// Deprecated: for backward compatibility only - public const int UNITYTLS_SSL_HANDSHAKE_DONE = 16; + public const int UNITYTLS_SSL_HANDSHAKE_DONE = 27; /// Deprecated: for backward compatibility only public const int UNITYTLS_SSL_HANDSHAKE_HANDSHAKE_FLUSH_BUFFERS = 14; /// Deprecated: for backward compatibility only public const int UNITYTLS_SSL_HANDSHAKE_HANDSHAKE_WRAPUP = 15; /// Deprecated: for backward compatibility only - public const int UNITYTLS_SSL_HANDSHAKE_HANDSHAKE_OVER = 16; + public const int UNITYTLS_SSL_HANDSHAKE_HANDSHAKE_OVER = 27; [StructLayout(LayoutKind.Sequential)] public struct unitytls_errorstate { diff --git a/Modules/AIEditor/Visualization/NavMeshVisualizationSettings.bindings.cs b/Modules/AIEditor/Visualization/NavMeshVisualizationSettings.bindings.cs index 9efa862467..ea1060ce72 100644 --- a/Modules/AIEditor/Visualization/NavMeshVisualizationSettings.bindings.cs +++ b/Modules/AIEditor/Visualization/NavMeshVisualizationSettings.bindings.cs @@ -12,8 +12,6 @@ [assembly:InternalsVisibleTo("Unity.AI.Navigation.Editor")] -[assembly:InternalsVisibleTo("Unity.AI.Navigation.Editor")] - namespace UnityEditor.AI { [MovedFrom("UnityEditor")] diff --git a/Modules/Accessibility/Bindings/AccessibilityManager.bindings.cs b/Modules/Accessibility/Bindings/AccessibilityManager.bindings.cs index 53999e0681..5896688c44 100644 --- a/Modules/Accessibility/Bindings/AccessibilityManager.bindings.cs +++ b/Modules/Accessibility/Bindings/AccessibilityManager.bindings.cs @@ -53,7 +53,7 @@ public NotificationContext(ref AccessibilityNotificationContext nativeNotificati } } - static Queue s_AsyncNotificationContexts = new(); + internal static Queue asyncNotificationContexts = new(); /// /// Indicates whether our Accessibility support is implemented for the current platform. @@ -107,18 +107,18 @@ internal static void Internal_Initialize() static void Internal_Update() { // Prevent lock if empty. - if (s_AsyncNotificationContexts.Count == 0) + if (asyncNotificationContexts.Count == 0) return; NotificationContext[] contexts; - lock (s_AsyncNotificationContexts) + lock (asyncNotificationContexts) { - if (s_AsyncNotificationContexts.Count == 0) + if (asyncNotificationContexts.Count == 0) return; - contexts = s_AsyncNotificationContexts.ToArray(); - s_AsyncNotificationContexts.Clear(); + contexts = asyncNotificationContexts.ToArray(); + asyncNotificationContexts.Clear(); } using var amLock = GetExclusiveLock(); @@ -163,23 +163,22 @@ static void Internal_Update() } [RequiredByNativeCode] - static int[] Internal_GetRootNodeIds() + internal static int[] Internal_GetRootNodeIds() { var service = AssistiveSupport.GetService(); var rootNodes = service?.GetRootNodes(); if (rootNodes == null || rootNodes.Count == 0) + { return null; + } using (ListPool.Get(out var rootNodeIds)) { for (var i = 0; i < rootNodes.Count; i++) rootNodeIds.Add(rootNodes[i].id); - if (rootNodeIds.Count == 0) - return null; - - return rootNodeIds.ToArray(); + return rootNodeIds.Count == 0 ? null : rootNodeIds.ToArray(); } } @@ -199,33 +198,21 @@ internal static bool Internal_GetNode(int id, ref AccessibilityNodeData nodeData node.GetNodeData(ref nodeData); return true; } - + return false; } [RequiredByNativeCode] - static int Internal_GetNodeIdAt(float x, float y) + internal static int Internal_GetNodeIdAt(float x, float y) { var service = AssistiveSupport.GetService(); - if (service == null) - return AccessibilityNodeManager.k_InvalidNodeId; - - var rootNodes = service.GetRootNodes(); - - if (rootNodes.Count == 0) - return AccessibilityNodeManager.k_InvalidNodeId; - - if (service.TryGetNodeAt(x, y, out var node)) - { - return node.id; - } - - return AccessibilityNodeManager.k_InvalidNodeId; + return service != null && service.TryGetNodeAt(x, y, out var node) ? + node.id : AccessibilityNodeManager.k_InvalidNodeId; } [RequiredByNativeCode] - static void Internal_OnAccessibilityNotificationReceived(ref AccessibilityNotificationContext context) + internal static void Internal_OnAccessibilityNotificationReceived(ref AccessibilityNotificationContext context) { // Ignore the global notification and only rely on the per-node notification. if (context.notification == AccessibilityNotification.ElementFocused) @@ -236,9 +223,9 @@ static void Internal_OnAccessibilityNotificationReceived(ref AccessibilityNotifi internal static void QueueNotification(NotificationContext notification) { - lock (s_AsyncNotificationContexts) + lock (asyncNotificationContexts) { - s_AsyncNotificationContexts.Enqueue(notification); + asyncNotificationContexts.Enqueue(notification); } } diff --git a/Modules/AndroidJNI/AndroidJNI.bindings.cs b/Modules/AndroidJNI/AndroidJNI.bindings.cs index 757372651d..aeaf1a5fff 100644 --- a/Modules/AndroidJNI/AndroidJNI.bindings.cs +++ b/Modules/AndroidJNI/AndroidJNI.bindings.cs @@ -1141,9 +1141,14 @@ public static void SetStaticByteField(IntPtr clazz, IntPtr fieldID, Byte val) //--------------------------------------- - // Convert a managed array of System.Boolean to a Java array of boolean. [ThreadSafe] - public static extern IntPtr ToBooleanArray(Boolean[] array); + static extern IntPtr ConvertToBooleanArray(Boolean[] array); + // Convert a managed array of System.Boolean to a Java array of boolean. + public static IntPtr ToBooleanArray(Boolean[] array) + { + return array == null ? IntPtr.Zero : ConvertToBooleanArray(array); + } + [ThreadSafe] [Obsolete("AndroidJNI.ToByteArray is obsolete. Use AndroidJNI.ToSByteArray method instead")] public static extern IntPtr ToByteArray(Byte[] array); diff --git a/Modules/Animation/ScriptBindings/AnimatorOverrideController.bindings.cs b/Modules/Animation/ScriptBindings/AnimatorOverrideController.bindings.cs index 32a8ad2400..c53c3382b7 100644 --- a/Modules/Animation/ScriptBindings/AnimatorOverrideController.bindings.cs +++ b/Modules/Animation/ScriptBindings/AnimatorOverrideController.bindings.cs @@ -24,6 +24,7 @@ public class AnimationClipPair [NativeHeader("Modules/Animation/AnimatorOverrideController.h")] [NativeHeader("Modules/Animation/ScriptBindings/Animation.bindings.h")] [UsedByNativeCode] + [HelpURL("AnimatorOverrideController")] public class AnimatorOverrideController : RuntimeAnimatorController { public AnimatorOverrideController() diff --git a/Modules/AssetPipelineEditor/ImportSettings/AudioImporterInspector.cs b/Modules/AssetPipelineEditor/ImportSettings/AudioImporterInspector.cs index 5394dac23a..f18c69a9d3 100644 --- a/Modules/AssetPipelineEditor/ImportSettings/AudioImporterInspector.cs +++ b/Modules/AssetPipelineEditor/ImportSettings/AudioImporterInspector.cs @@ -376,7 +376,7 @@ private void OnSampleSettingGUI(BuildPlatform platform, SerializedProperty audio } } - if (BuildTargetDiscovery.TryGetBuildTarget(platform.defaultTarget, out IBuildTarget iBuildTarget) && (iBuildTarget.AudioPlatformProperties?.HasSampleRateSettings ?? true)) + if (!BuildTargetDiscovery.TryGetBuildTarget(platform.defaultTarget, out IBuildTarget iBuildTarget) || (iBuildTarget.AudioPlatformProperties?.HasSampleRateSettings ?? true)) { //Sample rate settings var sampleRateSettingProperty = audioImporterSampleSettings.FindPropertyRelative("sampleRateSetting"); diff --git a/Modules/BuildProfileEditor/BuildAutomation/BuildAutomationModalWindow.cs b/Modules/BuildProfileEditor/BuildAutomation/BuildAutomationModalWindow.cs index 7baa56f440..b710e18959 100644 --- a/Modules/BuildProfileEditor/BuildAutomation/BuildAutomationModalWindow.cs +++ b/Modules/BuildProfileEditor/BuildAutomation/BuildAutomationModalWindow.cs @@ -16,12 +16,13 @@ internal class BuildAutomationModalWindow : EditorWindow const string k_PackageId = "com.unity.services.cloud-build"; const string k_Uxml = "BuildProfile/UXML/BuildAutomationModalWindow.uxml"; - static readonly string s_Label_Install = L10n.Tr("Cloud Builds require the Build Automation package. Clicking install will add the package your project. It will also create a Build Profile so you can sign your builds with credentials from the Build Automation service."); - static readonly string s_Label_Credentials = L10n.Tr("Cloud Builds require Cloud Build credential settings to be added to the selected Build Profile."); + static readonly string s_Label_Install = L10n.Tr("Cloud Builds require the Build Automation package. Clicking install will add the package to your project. It will also add a Build Automation section to your Build Profile so you can customize the way your Cloud Builds are run."); + static readonly string s_Label_Settings = L10n.Tr("Cloud Builds require Build Automation settings to be added to the selected Build Profile."); static readonly string s_Button_Install = L10n.Tr("Install"); - static readonly string s_Button_Credentials = L10n.Tr("Add Settings"); + static readonly string s_Button_Settings = L10n.Tr("Add Settings"); + static readonly string s_Modal_Title = L10n.Tr("Build Automation"); - static readonly string s_Helpbox = L10n.Tr("Cloud Build is a pay-as-you-go service provided by Unity. You can start using it for free without a credit card on file, and we will notify you when you reach the limits of the free tier."); + static readonly string s_Helpbox = L10n.Tr("Build Automation is a pay-as-you-go service provided by Unity. You can start using it for free without a credit card on file, and we will notify you when you reach the limits of the free tier."); BuildProfileWindow m_ParentWindow; BuildProfile m_TargetProfile; @@ -52,7 +53,7 @@ public static void OnCloudBuildClicked(BuildProfile profile, BuildProfileWindow // Show modal automating cloud build workflow integration, // handle package installation and initial configuration object. - var window = GetWindow(); + var window = GetWindow(s_Modal_Title); window.minSize = new Vector2(600, 180); window.maxSize = new Vector2(600, 180); window.m_TargetProfile = profile; @@ -111,8 +112,8 @@ void SetInstallInfo() void SetAddCredentialsInfo() { - m_InfoLabel.text = s_Label_Credentials; - m_SubmitButton.text = s_Button_Credentials; + m_InfoLabel.text = s_Label_Settings; + m_SubmitButton.text = s_Button_Settings; m_SubmitButton.clicked += OnAddCredentialsObject; } diff --git a/Modules/BuildProfileEditor/Elements/PlatformListView.cs b/Modules/BuildProfileEditor/Elements/PlatformListView.cs index 48606e0b02..8b3b90f20f 100644 --- a/Modules/BuildProfileEditor/Elements/PlatformListView.cs +++ b/Modules/BuildProfileEditor/Elements/PlatformListView.cs @@ -200,7 +200,7 @@ internal void SelectBuildProfile(int index) { if (m_PlatformListView.selectedIndex >= 0) m_PlatformListView.ClearSelection(); - + ClearSharedSceneListSelection(); m_BuildProfilesListView.SetSelection(index); @@ -210,7 +210,7 @@ internal void SelectSharedSceneList() { if (m_PlatformListView.selectedIndex >= 0) m_PlatformListView.ClearSelection(); - + if (m_BuildProfilesListView.selectedIndex >= 0) m_BuildProfilesListView.ClearSelection(); @@ -237,7 +237,8 @@ internal void SelectActiveProfile() if (TrySelectClassicBasePlatform()) return; - Debug.LogWarning("[BuildProfile] Active profile not found in build profile window data source."); + // Disabled warning message. See PLAT-12653. + //Debug.LogWarning("[BuildProfile] Active profile not found in build profile window data source."); } bool TrySelectClassicPlatform() diff --git a/Modules/BuildProfileEditor/Handlers/BuildProfileDataSource.cs b/Modules/BuildProfileEditor/Handlers/BuildProfileDataSource.cs index 364f0221a3..9a0cd476ac 100644 --- a/Modules/BuildProfileEditor/Handlers/BuildProfileDataSource.cs +++ b/Modules/BuildProfileEditor/Handlers/BuildProfileDataSource.cs @@ -20,23 +20,24 @@ internal class BuildProfileDataSource : IDisposable const string k_AssetFolderPath = "Assets/Settings/Build Profiles"; - static string GetDefaultNewProfilePath(string platformDisplayName, string variantName = null) + static string GetDefaultNewProfilePath(string displayName, string variantName = null) { - if (variantName == null) - return $"{k_AssetFolderPath}/New {SanitizeFileName(platformDisplayName)} Profile.asset"; - return $"{k_AssetFolderPath}/New {SanitizeFileName(platformDisplayName)} Profile - {SanitizeFileName(variantName)}.asset"; + var assetFileName = string.IsNullOrEmpty(variantName) ? + $"{SanitizeFileName(displayName)}.asset" : + $"{SanitizeFileName(displayName)} - {SanitizeFileName(variantName)}.asset"; + // Truncate the length to max. 250 symbols, as supported by the asset database. + // Leave 3 symbols for the GenerateUniqueAssetPath() that adds " 1"(2,3...) in case + // an asset with such name already exists. + if (assetFileName.Length > BuildProfileModuleUtil.k_MaxAssetFileNameLength) + assetFileName = assetFileName.Substring(0, BuildProfileModuleUtil.k_MaxAssetFileNameLength); + return $"{k_AssetFolderPath}/{assetFileName}"; } - static string GetProfilePathWithProvidedName(string platformDisplayName, string variantName = null) + static string GetProfilePathWithProvidedName(GUID platformId, string customProfileName, string variantName = null) { - if (string.IsNullOrEmpty(platformDisplayName)) - { - return GetDefaultNewProfilePath(platformDisplayName, variantName); - } - // A valid name should be provided already but do the sanity check anyways. - if (variantName == null) - return $"{k_AssetFolderPath}/{SanitizeFileName(platformDisplayName)}.asset"; - return $"{k_AssetFolderPath}/{SanitizeFileName(platformDisplayName)}-{SanitizeFileName(variantName)}.asset"; + return GetDefaultNewProfilePath(string.IsNullOrEmpty(customProfileName) ? + BuildProfileModuleUtil.GetClassicPlatformDisplayName(platformId) : + customProfileName, variantName); } static string GetDefaultNewProfilePath(GUID platformGuid) @@ -89,10 +90,10 @@ internal List DuplicateProfiles(List profilesToDupli /// Create a new custom build profile asset with the user provided name. /// Ensure that custom build profile folders is created if it doesn't already exist. /// - internal static void CreateNewAssetWithName(GUID platformId, string platformDisplayName, string preconfiguredSettingsVariantName, int preconfiguredSettingsVariant, string[] packagesToAdd) + internal static void CreateNewAssetWithName(GUID platformId, string buildProfileName, string preconfiguredSettingsVariantName, int preconfiguredSettingsVariant, string[] packagesToAdd) { EnsureCustomBuildProfileFolderExists(); - BuildProfile.CreateInstance(platformId, GetProfilePathWithProvidedName(platformDisplayName, preconfiguredSettingsVariantName), preconfiguredSettingsVariant, packagesToAdd); + BuildProfile.CreateInstance(platformId, GetProfilePathWithProvidedName(platformId, buildProfileName, preconfiguredSettingsVariantName), preconfiguredSettingsVariant, packagesToAdd); } /// diff --git a/Modules/BuildProfileEditor/PlatformDiscoveryWindow.cs b/Modules/BuildProfileEditor/PlatformDiscoveryWindow.cs index bd22d7f39b..ef71f8c82e 100644 --- a/Modules/BuildProfileEditor/PlatformDiscoveryWindow.cs +++ b/Modules/BuildProfileEditor/PlatformDiscoveryWindow.cs @@ -24,6 +24,15 @@ internal class PlatformDiscoveryWindow : EditorWindow { const string k_Uxml = "BuildProfile/UXML/PlatformDiscoveryWindow.uxml"; + // Asset database says max file name length = 250. + // Then, the longest variation we have now is " - Desktop - Development" = 24 bytes long. + // We also need to account for the `.asset` extension (6 bytes) + // and potentially ` 1`(2,3..) unique name suffix (3 bytes ought to be enough for everyone TM) + // if suddenly a build profile with such a long name already exists. + // This makes for 33 bytes worth of overhead, but to be even more safe let's limit the name to 200 during creation. + // There are checks at a lower level too. + const int k_MaxBuildProfileNameLength = 200; + BuildProfileCard[] m_Cards; BuildProfileCard m_SelectedCard; Image m_SelectedCardImage; @@ -102,7 +111,7 @@ static void AddSelectedBuildProfiles(BuildProfileCard card, string[] packagesToA static void AddSingleBuildProfile(BuildProfileCard card, string preconfiguredSettingsVariantName, int preconfiguredSettingsVariant, string[] packagesToAdd) { - BuildProfileDataSource.CreateNewAssetWithName(card.platformId, card.displayName, preconfiguredSettingsVariantName, preconfiguredSettingsVariant, packagesToAdd); + BuildProfileDataSource.CreateNewAssetWithName(card.platformId, card.displayName.Trim(), preconfiguredSettingsVariantName, preconfiguredSettingsVariant, packagesToAdd); EditorAnalytics.SendAnalytic(new BuildProfileCreatedEvent(new BuildProfileCreatedEvent.Payload { creationType = BuildProfileCreatedEvent.CreationType.PlatformBrowser, @@ -190,8 +199,13 @@ public void CreateGUI() m_BuildProfileNameTextField.RegisterCallback(evt => { m_SelectedCard.displayName = m_BuildProfileNameTextField.value; + // truncate the name, just in case UI restrictions don't apply. + if (!string.IsNullOrEmpty(m_SelectedCard.displayName) && + m_SelectedCard.displayName.Length > k_MaxBuildProfileNameLength) + m_SelectedCard.displayName = m_SelectedCard.displayName.Substring(0, k_MaxBuildProfileNameLength); m_RenameOverlay.OnRenameEnd(); }); + m_BuildProfileNameTextField.maxLength = k_MaxBuildProfileNameLength; m_PackagesListView.itemsSource = Array.Empty(); m_PackagesListView.makeItem = PlatformPackageItem.Make; m_PackagesListView.bindItem = (VisualElement element, int index) => @@ -209,7 +223,7 @@ public void CreateGUI() packageSelectAll.text = TrText.selectAll; packageSelectAll.clicked += () => { - for (int i = 0; i < m_PackagesListView.itemsSource.Count; ++i) + for (int i = 0; i < m_PackagesListView.itemsSource.Count; ++i) { var item = m_PackagesListView.GetRootElementForIndex(i); if (item is not PlatformPackageItem packageItemVisualElement) @@ -233,6 +247,29 @@ public void CreateGUI() } }; + m_PackagesListView.itemsSourceChanged += () => + { + var packageList = m_PackagesListView.itemsSource as PlatformPackageEntry[]; + if (packageList == null) + { + Debug.LogWarning("Could not parse list of packages in PlatformDiscoveryWindow"); + return; + } + + bool allPackagesAreInstalledOrRequired = true; + foreach (var package in packageList) + { + if (!package.isInstalled && !package.required) + { + allPackagesAreInstalledOrRequired = false; + break; + } + } + + packageDeselectAll.SetEnabled(!allPackagesAreInstalledOrRequired); + packageSelectAll.SetEnabled(!allPackagesAreInstalledOrRequired); + }; + // Apply localized text to static elements. rootVisualElement.Q("toolbar-filter-all").text = TrText.all; m_SelectedDescriptionFoldout.text = TrText.description; diff --git a/Modules/BuildProfileEditor/TrText.cs b/Modules/BuildProfileEditor/TrText.cs index d940ce7f42..b57c1e4f64 100644 --- a/Modules/BuildProfileEditor/TrText.cs +++ b/Modules/BuildProfileEditor/TrText.cs @@ -57,7 +57,7 @@ internal class TrText L10n.Tr("Build Profiles can have custom player settings"); public static readonly string playerSettingsClassicInfo = L10n.Tr("Platforms use the global player settings. To customize player settings, create a Build Profile for this platform."); - public static readonly string customizePlayerSettingsButton = "Customize player settings"; + public static readonly string customizePlayerSettingsButton = "Customize Player Settings"; public static readonly string removePlayerSettingsDialogTitle = L10n.Tr("Remove Player Settings Overrides"); public static readonly string removePlayerSettingsDialogMessage = L10n.Tr("This will remove all Player Settings overrides"); public static readonly string resetPlayerSettingsDialogTitle = L10n.Tr("Reset Player Settings to Globals"); diff --git a/Modules/BuildReportingEditor/Managed/BuildFile.cs b/Modules/BuildReportingEditor/Managed/BuildFile.cs index 673cf09cae..4bb8ca7ec8 100644 --- a/Modules/BuildReportingEditor/Managed/BuildFile.cs +++ b/Modules/BuildReportingEditor/Managed/BuildFile.cs @@ -7,13 +7,19 @@ namespace UnityEditor.Build.Reporting { + ///Contains information about a single file produced by the build process. [NativeType(Header = "Modules/BuildReportingEditor/Public/BuildReport.h")] public struct BuildFile { + ///The unique indentifier of the build file. public uint id { get; } + ///The absolute path of the file produced by the build process. public string path { get; } + ///The role the file plays in the build output. + ///Use this field to understand what purpose a file serves within the built player. Common roles for files are captured by the members of the class. public string role { get; } + ///The total size of the file, in bytes. [NativeName("totalSize")] public ulong size { get; } diff --git a/Modules/BuildReportingEditor/Managed/BuildReport.bindings.cs b/Modules/BuildReportingEditor/Managed/BuildReport.bindings.cs index 532be3e47f..5a53cf8c6a 100644 --- a/Modules/BuildReportingEditor/Managed/BuildReport.bindings.cs +++ b/Modules/BuildReportingEditor/Managed/BuildReport.bindings.cs @@ -10,6 +10,110 @@ namespace UnityEditor.Build.Reporting { + ///The BuildReport API gives you information about the Unity build process. + ///A BuildReport object is returned by and can be used to discover information about the files output, the build steps taken, and other platform-specific information such as native code stripping. + /// + ///For AssetBundle builds the BuildReport is available by calling immediately after calling . + /// + /// s.name.Length + s.depth) + 3; + /// foreach (var step in buildReport.steps) + /// { + /// string rawStepOutput = new string('-', step.depth + 1) + ' ' + step.name; + /// sb.AppendLine($"{rawStepOutput.PadRight(maxWidth)}: {step.duration:g}"); + /// } + /// return sb.ToString(); + /// } + /// + /// public static string LogBuildMessages(BuildReport buildReport) + /// { + /// var sb = new StringBuilder(); + /// foreach (var step in buildReport.steps) + /// { + /// foreach (var message in step.messages) + /// // If desired, this logic could ignore any Info or Warning messages to focus on more serious messages + /// sb.AppendLine($"[{message.type}] {message.content}"); + /// } + /// + /// string messages = sb.ToString(); + /// if (messages.Length > 0) + /// return "Messages logged during Build:\n" + messages; + /// else + /// return ""; + /// } + ///} + /// + /// // For the purpose of demonstration, this callback logs different types of errors and forces a build failure + ///[BuildCallbackVersion(1)] + ///class MyTroublesomeBuildCallback : IProcessSceneWithReport + ///{ + /// public int callbackOrder { get { return 0; } } + /// public void OnProcessScene(UnityEngine.SceneManagement.Scene scene, BuildReport report) + /// { + /// Debug.Log("MyTroublesomeBuildCallback called for " + scene.name); + /// Debug.LogError("Logging an error"); + /// + /// throw new BuildFailedException("Forcing the build to stop"); + /// } + ///} + ///]]> + /// [NativeHeader("Runtime/Utilities/DateTime.h")] [NativeType(Header = "Modules/BuildReportingEditor/Public/BuildReport.h")] [NativeClass("BuildReporting::BuildReport")] @@ -22,23 +126,34 @@ private BuildReport() [System.Obsolete("Use GetFiles() method instead (UnityUpgradable) -> GetFiles()", true)] public BuildFile[] files => throw new NotSupportedException(); + ///Returns an array of all the files output by the build process. + ///The returned array is a copy and this method execution length scales linearly with number of files. + ///An array of all the files output by the build process. public extern BuildFile[] GetFiles(); + ///An array of all the s that took place during the build process. [NativeName("BuildSteps")] public extern BuildStep[] steps { get; } + ///A containing overall statistics and data about the build process. public extern BuildSummary summary { get; } + ///The object for the build. + ///The StrippingInfo object contains information about which native code modules in the engine are still present in the build, and the reasons why they are still present. + /// + ///This is only available when building for platforms that support code stripping. When building for other platforms, this property will be null. public StrippingInfo strippingInfo { get { return GetAppendices().SingleOrDefault(); } } + ///An array of all the generated by the build process. public PackedAssets[] packedAssets { get { return GetAppendicesByType(); } } + ///An optional array of generated by the build process if was used during the build. public ScenesUsingAssets[] scenesUsingAssets { get { return GetAppendicesByType(); } @@ -62,6 +177,12 @@ public ScenesUsingAssets[] scenesUsingAssets [NativeMethod("DeleteAllFiles")] internal extern void DeleteAllFileEntries(); + ///Returns a string summarizing any errors that occurred during the build + ///Convenience method for summarizing errors (or exceptions) that occurred during a build into a single line of text. + ///If no error was logged this returns an empty string. If a single error was logged this reports the error messages. Otherwise it reports the number of errors, for example "5 errors". + /// + ///Note: To examine all errors, warnings and other messages recorded during a build you can enumerating through the build and check . + ///And to retrieve the count of errors call . [FreeFunction("BuildReporting::SummarizeErrors", HasExplicitThis = true)] public extern string SummarizeErrors(); @@ -92,6 +213,9 @@ internal TAppendix[] GetAppendicesByType() where TAppendix : Object internal extern Object[] GetAllAppendices(); + ///Return the build report generated by the most recent Player or AssetBundle build + ///A BuildReport is generated when a Player build runs, or when is called. + ///The BuildReport is automatically saved to **Library/LastBuild.buildreport** and can be reloaded using this static method. [FreeFunction("BuildReporting::GetLatestReport")] public static extern BuildReport GetLatestReport(); diff --git a/Modules/BuildReportingEditor/Managed/BuildResult.cs b/Modules/BuildReportingEditor/Managed/BuildResult.cs index af40c6d732..22ce1e0814 100644 --- a/Modules/BuildReportingEditor/Managed/BuildResult.cs +++ b/Modules/BuildReportingEditor/Managed/BuildResult.cs @@ -6,11 +6,16 @@ namespace UnityEditor.Build.Reporting { + ///Describes the outcome of the build process. public enum BuildResult { + ///Indicates that the outcome of the build is in an unknown state. Unknown = 0, + ///Indicates that the build completed successfully. Succeeded = 1, + ///Indicates that the build failed. Failed = 2, + ///Indicates that the build was cancelled by the user. Cancelled = 3, } } diff --git a/Modules/BuildReportingEditor/Managed/BuildStep.cs b/Modules/BuildReportingEditor/Managed/BuildStep.cs index bd3cea5608..55159b3129 100644 --- a/Modules/BuildReportingEditor/Managed/BuildStep.cs +++ b/Modules/BuildReportingEditor/Managed/BuildStep.cs @@ -8,17 +8,27 @@ namespace UnityEditor.Build.Reporting { + ///Contains information about a single step in the build process. + /// + /// + /// [NativeType(Header = "Modules/BuildReportingEditor/Public/BuildReport.h")] public struct BuildStep { + ///The name of this build step. [NativeName("stepName")] public string name { get; } internal ulong durationTicks; + ///The total duration for this build step. public TimeSpan duration { get { return new TimeSpan((long)durationTicks); } } + ///All log messages recorded during this build step, in the order of which they occurred. + ///An array of structs. public BuildStepMessage[] messages { get; } + ///The nesting depth of the build step. + ///The build process is broken down into steps, and steps may themselves be broken down into sub-steps recursively. The nesting depth indicates how many higher-level build steps enclose this step. The step that represents the overall build process has depth 0, the sub-steps of that step have depth 1, and so on. public int depth { get; } public override string ToString() diff --git a/Modules/BuildReportingEditor/Managed/BuildStepMessage.cs b/Modules/BuildReportingEditor/Managed/BuildStepMessage.cs index b4fb96bc18..9d76dac554 100644 --- a/Modules/BuildReportingEditor/Managed/BuildStepMessage.cs +++ b/Modules/BuildReportingEditor/Managed/BuildStepMessage.cs @@ -9,10 +9,16 @@ namespace UnityEditor.Build.Reporting { + ///Contains information about a single log message recorded during the build process. + /// + /// + /// [NativeType(Header = "Modules/BuildReportingEditor/Public/BuildReport.h")] public struct BuildStepMessage { + ///The of the log message. public LogType type { get; } + ///The text content of the log message. public string content { get; } } } diff --git a/Modules/BuildReportingEditor/Managed/BuildSummary.cs b/Modules/BuildReportingEditor/Managed/BuildSummary.cs index fc803a89b7..85bf446535 100644 --- a/Modules/BuildReportingEditor/Managed/BuildSummary.cs +++ b/Modules/BuildReportingEditor/Managed/BuildSummary.cs @@ -7,35 +7,54 @@ namespace UnityEditor.Build.Reporting { + ///Contains overall summary information about a build. [NativeType(Header = "Modules/BuildReportingEditor/Managed/BuildSummary.bindings.h", CodegenOptions = CodegenOptions.Custom)] public struct BuildSummary { internal Int64 buildStartTimeTicks; + ///The time the build was started. public DateTime buildStartedAt { get { return new DateTime(buildStartTimeTicks); } } + ///The of the build. [NativeName("buildGUID")] public GUID guid { get; } + ///The platform that the build was created for. + ///See for possible values. public BuildTarget platform { get; } + ///The platform group the build was created for. + ///See for possible values. public BuildTargetGroup platformGroup { get; } internal int subtarget { get; } + ///The used for the build, as passed to . public BuildOptions options { get; } internal BuildAssetBundleOptions assetBundleOptions { get; } + ///The output path for the build, as provided to . public string outputPath { get; } internal uint crc { get; } + ///The total size of the build output, in bytes. public ulong totalSize { get; } internal UInt64 totalTimeTicks; + ///The total time taken by the build process. public TimeSpan totalTime { get { return new TimeSpan((long)totalTimeTicks); } } + ///The time the build ended. public DateTime buildEndedAt { get { return buildStartedAt + totalTime; } } + ///The total number of errors and exceptions recorded during the build process. public int totalErrors { get; } + ///The total number of warnings recorded during the build process. public int totalWarnings { get; } + ///The outcome of the build. + ///See for possible outcomes. [NativeName("buildResult")] public BuildResult result { get; } + ///The type of build. public BuildType buildType { get; } + ///Whether the multi-process option was enabled for the build. + /// public bool multiProcessEnabled { get; } private T ParseSubtarget() where T : Enum where S : Enum @@ -49,6 +68,9 @@ private T ParseSubtarget() where T : Enum where S : Enum return (T)(object)subtarget; } + ///The subtarget that the build was created for. + ///Valid values for type T are , and + ///Returns the subtarget value. public T GetSubtarget() where T : Enum { switch (platform) diff --git a/Modules/BuildReportingEditor/Managed/BuildType.cs b/Modules/BuildReportingEditor/Managed/BuildType.cs index 9f16d7d148..637d1e95ec 100644 --- a/Modules/BuildReportingEditor/Managed/BuildType.cs +++ b/Modules/BuildReportingEditor/Managed/BuildType.cs @@ -6,10 +6,14 @@ namespace UnityEditor.Build.Reporting { + ///Build type. + /// [Flags] public enum BuildType { + ///Indicates a Player build. Player = 1, + ///Indicates an Asset Bundle build. AssetBundle = 2 } } diff --git a/Modules/BuildReportingEditor/Managed/CommonRoles.bindings.cs b/Modules/BuildReportingEditor/Managed/CommonRoles.bindings.cs index 93584dcafc..4bbeec852a 100644 --- a/Modules/BuildReportingEditor/Managed/CommonRoles.bindings.cs +++ b/Modules/BuildReportingEditor/Managed/CommonRoles.bindings.cs @@ -4,78 +4,102 @@ using UnityEngine.Bindings; -namespace UnityEditor +namespace UnityEditor.Build.Reporting { - namespace Build.Reporting + ///This class provides constant values for some of the common roles used by files in the build. The role of each file in the build is available in . + [NativeType(Header = "Modules/BuildReportingEditor/Public/CommonRoles.h")] + public static class CommonRoles { - [NativeType(Header = "Modules/BuildReportingEditor/Public/CommonRoles.h")] - public static class CommonRoles - { - [NativeProperty("BuildReporting::CommonRoles::scene", true, TargetType.Field)] - public static extern string scene { get; } - - [NativeProperty("BuildReporting::CommonRoles::sharedAssets", true, TargetType.Field)] - public static extern string sharedAssets { get; } - - [NativeProperty("BuildReporting::CommonRoles::resourcesFile", true, TargetType.Field)] - public static extern string resourcesFile { get; } - - [NativeProperty("BuildReporting::CommonRoles::assetBundle", true, TargetType.Field)] - public static extern string assetBundle { get; } - - [NativeProperty("BuildReporting::CommonRoles::manifestAssetBundle", true, TargetType.Field)] - public static extern string manifestAssetBundle { get; } - - [NativeProperty("BuildReporting::CommonRoles::assetBundleTextManifest", true, TargetType.Field)] - public static extern string assetBundleTextManifest { get; } - - [NativeProperty("BuildReporting::CommonRoles::managedLibrary", true, TargetType.Field)] - public static extern string managedLibrary { get; } - - [NativeProperty("BuildReporting::CommonRoles::dependentManagedLibrary", true, TargetType.Field)] - public static extern string dependentManagedLibrary { get; } - - [NativeProperty("BuildReporting::CommonRoles::executable", true, TargetType.Field)] - public static extern string executable { get; } - - [NativeProperty("BuildReporting::CommonRoles::streamingResourceFile", true, TargetType.Field)] - public static extern string streamingResourceFile { get; } - - [NativeProperty("BuildReporting::CommonRoles::streamingAsset", true, TargetType.Field)] - public static extern string streamingAsset { get; } - - [NativeProperty("BuildReporting::CommonRoles::bootConfig", true, TargetType.Field)] - public static extern string bootConfig { get; } - - [NativeProperty("BuildReporting::CommonRoles::builtInResources", true, TargetType.Field)] - public static extern string builtInResources { get; } - - [NativeProperty("BuildReporting::CommonRoles::builtInShaders", true, TargetType.Field)] - public static extern string builtInShaders { get; } - - [NativeProperty("BuildReporting::CommonRoles::appInfo", true, TargetType.Field)] - public static extern string appInfo { get; } - - [NativeProperty("BuildReporting::CommonRoles::managedEngineAPI", true, TargetType.Field)] - public static extern string managedEngineApi { get; } - - [NativeProperty("BuildReporting::CommonRoles::monoRuntime", true, TargetType.Field)] - public static extern string monoRuntime { get; } - - [NativeProperty("BuildReporting::CommonRoles::monoConfig", true, TargetType.Field)] - public static extern string monoConfig { get; } - - [NativeProperty("BuildReporting::CommonRoles::debugInfo", true, TargetType.Field)] - public static extern string debugInfo { get; } - - [NativeProperty("BuildReporting::CommonRoles::globalGameManagers", true, TargetType.Field)] - public static extern string globalGameManagers { get; } - - [NativeProperty("BuildReporting::CommonRoles::crashHandler", true, TargetType.Field)] - public static extern string crashHandler { get; } - - [NativeProperty("BuildReporting::CommonRoles::engineLibrary", true, TargetType.Field)] - public static extern string engineLibrary { get; } - } + ///The value of a file that contains the packed content of a Scene. + [NativeProperty("BuildReporting::CommonRoles::scene", true, TargetType.Field)] + public static extern string scene { get; } + + ///The value of a file that contains asset objects which are shared between Scenes. Examples of asset objects are textures, models, and audio. + [NativeProperty("BuildReporting::CommonRoles::sharedAssets", true, TargetType.Field)] + public static extern string sharedAssets { get; } + + ///The value of the file that contains the contents of the project's "Resources" folder, packed into a single file. + [NativeProperty("BuildReporting::CommonRoles::resourcesFile", true, TargetType.Field)] + public static extern string resourcesFile { get; } + + ///The value of built AssetBundle files. + [NativeProperty("BuildReporting::CommonRoles::assetBundle", true, TargetType.Field)] + public static extern string assetBundle { get; } + + ///The value of a manifest AssetBundle, which is an AssetBundle that contains information about other AssetBundles and their dependencies. + [NativeProperty("BuildReporting::CommonRoles::manifestAssetBundle", true, TargetType.Field)] + public static extern string manifestAssetBundle { get; } + + ///The value of an AssetBundle manifest file, produced during the build process, that contains information about the bundle and its dependencies. + [NativeProperty("BuildReporting::CommonRoles::assetBundleTextManifest", true, TargetType.Field)] + public static extern string assetBundleTextManifest { get; } + + ///The value of a managed assembly, containing compiled script code. + [NativeProperty("BuildReporting::CommonRoles::managedLibrary", true, TargetType.Field)] + public static extern string managedLibrary { get; } + + ///The value of a managed library that is present in the build due to being a dependency of a . + [NativeProperty("BuildReporting::CommonRoles::dependentManagedLibrary", true, TargetType.Field)] + public static extern string dependentManagedLibrary { get; } + + ///The value of an executable - the file that will actually be launched on the target device. + [NativeProperty("BuildReporting::CommonRoles::executable", true, TargetType.Field)] + public static extern string executable { get; } + + ///The value of a file that contains streaming resource data. + ///For example, when a texture is packed into a build, only metadata about the texture is packed into the file - the actual content of the texture is packed into a streamingResourceFile, where it can be streamed into memory asynchronously at runtime. + [NativeProperty("BuildReporting::CommonRoles::streamingResourceFile", true, TargetType.Field)] + public static extern string streamingResourceFile { get; } + + ///The value of files that have been copied into the build without modification from the StreamingAssets folder in the project. + [NativeProperty("BuildReporting::CommonRoles::streamingAsset", true, TargetType.Field)] + public static extern string streamingAsset { get; } + + ///The value of the file that contains configuration information for the very early stages of engine startup. + [NativeProperty("BuildReporting::CommonRoles::bootConfig", true, TargetType.Field)] + public static extern string bootConfig { get; } + + ///The value of the file that contains built-in resources for the engine. + [NativeProperty("BuildReporting::CommonRoles::builtInResources", true, TargetType.Field)] + public static extern string builtInResources { get; } + + ///The value of the file that contains Unity's built-in shaders, such as the Standard shader. + [NativeProperty("BuildReporting::CommonRoles::builtInShaders", true, TargetType.Field)] + public static extern string builtInShaders { get; } + + ///The value of the file that provides config information used in Low Integrity mode on Windows. + [NativeProperty("BuildReporting::CommonRoles::appInfo", true, TargetType.Field)] + public static extern string appInfo { get; } + + ///The value of files that provide the managed API for Unity. + ///These files are referenced by your compiled script DLL and provide the interface layer between your scripts and the engine itself. + [NativeProperty("BuildReporting::CommonRoles::managedEngineAPI", true, TargetType.Field)] + public static extern string managedEngineApi { get; } + + ///The value of files that make up the Mono runtime itself. + [NativeProperty("BuildReporting::CommonRoles::monoRuntime", true, TargetType.Field)] + public static extern string monoRuntime { get; } + + ///The value of files that are used as configuration data by the Mono runtime. + [NativeProperty("BuildReporting::CommonRoles::monoConfig", true, TargetType.Field)] + public static extern string monoConfig { get; } + + ///The value of files that contain information for debuggers. + ///These files include debug information, such as symbols, for both managed assemblies and native executables and libraries. + [NativeProperty("BuildReporting::CommonRoles::debugInfo", true, TargetType.Field)] + public static extern string debugInfo { get; } + + ///The value of the file that contains global Project Settings data for the player. + [NativeProperty("BuildReporting::CommonRoles::globalGameManagers", true, TargetType.Field)] + public static extern string globalGameManagers { get; } + + ///The value of the executable that is used to capture crashes from the player. + [NativeProperty("BuildReporting::CommonRoles::crashHandler", true, TargetType.Field)] + public static extern string crashHandler { get; } + + ///The value of the main Unity runtime when it is built as a separate library. + ///On most platforms, the Unity runtime is also the main executable for the player, but on some platforms it is split into a separate file, allowing the main executable to be smaller and more easily customized. + [NativeProperty("BuildReporting::CommonRoles::engineLibrary", true, TargetType.Field)] + public static extern string engineLibrary { get; } } } diff --git a/Modules/BuildReportingEditor/Managed/PackedAssetInfo.cs b/Modules/BuildReportingEditor/Managed/PackedAssetInfo.cs index 843f2a5b33..3b971812f4 100644 --- a/Modules/BuildReportingEditor/Managed/PackedAssetInfo.cs +++ b/Modules/BuildReportingEditor/Managed/PackedAssetInfo.cs @@ -7,15 +7,157 @@ namespace UnityEditor.Build.Reporting { + ///Contains information about a range of bytes in a file in the build output. + ///A Packed Asset contains either the serialized binary representation of a Unity Object, + ///or the binary data of a texture, mesh, audio or video that belongs to a Unity object. + /// + ///Note: the "Packed Asset" name is somewhat misleading because the data is associated with a specific object within an Asset, not an entire Asset. + ///Some Assets contain just a single object, but in many cases it may contain an entire hierarchy of objects, each with its own PackedAssetInfo entry. + /// + /// (); + /// + /// var packedAssets = buildReport.packedAssets; + /// foreach(var packedAsset in packedAssets) + /// { + /// sb.AppendLine("Analyzing " + packedAsset.shortPath + "...."); + /// + /// var contents = packedAsset.contents; + /// foreach(var packedAssetInfo in contents) + /// { + /// // Path of the asset that contains this object + /// var path = packedAssetInfo.sourceAssetPath; + /// + /// if (string.IsNullOrEmpty(path)) + /// path = "Internal"; + /// + /// if (sourceAssetSize.ContainsKey(path)) + /// { + /// var existingEntry = sourceAssetSize[path]; + /// existingEntry.size += packedAssetInfo.packedSize; + /// existingEntry.objectCount++; + /// sourceAssetSize[path] = existingEntry; + /// } + /// else + /// { + /// sourceAssetSize[path] = new ContentEntry + /// { + /// size = packedAssetInfo.packedSize, + /// objectCount = 1 + /// }; + /// } + /// } + /// } + /// + /// sb.AppendLine("The Build contains the content from the following source assets:\n"); + /// + /// // Sort biggest to smallest + /// var sortedSourceAssetSize = sourceAssetSize.OrderByDescending(x => x.Value.size); + /// + /// // Note: for large builds there could be thousands or more different source assets, + /// // in which case it could be prudent to only show the top 10 or top 100 results. + /// for (int i = 0; i < sortedSourceAssetSize.Count(); i++) + /// { + /// var entry = sortedSourceAssetSize.ElementAt(i); + /// sb.AppendLine(" Asset: \"" + entry.Key + "\" Object Count: " + entry.Value.objectCount + " Size of Objects: " + entry.Value.size); + /// } + /// + /// return sb.ToString(); + /// } + ///} + ///]]> + /// + /// + /// [NativeType(Header = "Modules/BuildReportingEditor/Public/PackedAssets.h")] public struct PackedAssetInfo { + ///Local file id of the object + /// + ///For Serialized Files this is the local file id of the object in the file in the build output. It will be unique within that file. + ///During the builds ids are reassigned, so this id will be different from the id of the object in its source scene or asset. + /// + ///This ID is 0 for the content of .resS and .resource files. + /// [NativeName("fileID")] public long id { get; } + ///The type of the object whose serialized data is represented by the Packed Asset, such as , or . public Type type { get; } + ///The size in bytes of the Packed Asset. + ///This is the size prior to any compression. The actual size on disk can be smaller when the file is stored inside a Unity Archive (e.g. in the case of a AssetBundle, or a Player built with ). + /// + ///Note: there can be extra padding bytes inserted between Packed Assets, added for alignment purpose. + ///Because of this, the offset + packedSize may be slightly smaller than the offset of the next element in the PackedAsset array. + /// public ulong packedSize { get; } + ///The offset from the start of the file to the first byte of the range belonging to the Packed Asset. public ulong offset { get; } + ///The Global Unique Identifier (GUID) of the source Asset that the build process used to generate the packed Asset. + /// public GUID sourceAssetGUID { get; } + ///The file path to the source Asset that the build process used to generate the Packed Asset, relative to the Project directory. + ///Note: the same path may be repeated many times in the PackedAssets array, because PackedAssets track objects, and a single Asset can contain many objects. + /// + ///Note: Some packed Assets might not have a source Asset. For example a Sprite Atlas that is generated at build time. Also, AssetBundles contain generated objects that do not come from any source asset, e.g. the . [NativeName("buildTimeAssetPath")] public string sourceAssetPath { get; } } diff --git a/Modules/BuildReportingEditor/Managed/PackedAssets.bindings.cs b/Modules/BuildReportingEditor/Managed/PackedAssets.bindings.cs index 324a22f353..45a941888b 100644 --- a/Modules/BuildReportingEditor/Managed/PackedAssets.bindings.cs +++ b/Modules/BuildReportingEditor/Managed/PackedAssets.bindings.cs @@ -9,6 +9,68 @@ namespace UnityEditor.Build.Reporting { + ///An extension to the class that tracks how Assets contribute to the size of the build. + ///The build process generates a PackedAssets object for each Serialized File, resS and resource file generated by a build. Serialized Files contain serialized Unity Objects from scenes and assets. + /// + ///The PackedAsset information can be used to analyze how specific assets or types contribute to the size of the build, for example to calculate the total size of all textures. + /// + ///Example file names for Serialized Files in the output of a Player build are resources.assets, sharedassets0.assets and globalgamemanagers.assets. + ///Note: The content of scenes in the output of a Player build are written to Serialized Files with the names level0, level1, level2 etc. + ///However there are no PackedAsset objects generated for level files, nor for the globalgamemanager file. + /// + ///AssetBundles containing assets will have a single Serialized File, with a name like "CAB-b8befc517982290c55526f35cbb7f03d". AssetBundles containing scenes will contain multiple Serialized Files. + /// + ///The PackedAssets for a Serialized File records the size of the header in the overhead property, and then information about each object in the contents property. + /// + ///Files with the .resource contain audio or video data which is referenced by an or object inside the associated Serialized File. + ///For example level1.resource contains audio and video data from objects inside the Serialized File level1. + ///The PackedAsset for a .resource file records information about the originating asset for each blob of audio or video data in the .resource file. + /// + ///Similarly, the PackedAsset object for a .resS file records information about the size and origin of and data inside the file. + /// + ///Note: For large builds the PackedAsset objects can grow very large, and consume a significant amount of memory. When using this data it is recommended to do a single pass through the data to populate smaller data structures or to export it to another format, as required by external tools. + /// + /// + /// + /// [NativeType(Header = "Modules/BuildReportingEditor/Public/PackedAssets.h")] [NativeClass("BuildReporting::PackedAssets")] public sealed class PackedAssets : Object @@ -17,16 +79,28 @@ public sealed class PackedAssets : Object [Obsolete(fileObsoleteMessage, true)] public uint file => throw new NotSupportedException(fileObsoleteMessage); + ///The file name + ///For player builds this the filename relative to the Data folder of the build, for example "globalgamemanagers.assets.resS". + /// + ///For an AssetBundle this is the filename inside the Unity Archive file, for example "CAB-cc6c60ef8808e0fc6663136604321554" and "CAB-cc6c60ef8808e0fc6663136604321554.resS". + /// public string shortPath { get { return GetShortPath(); } } + ///The size of the header section of the referenced file. + ///Some file formats have no header, in which case this returns zero. public ulong overhead { get { return GetOverhead(); } } + ///An array of objects that holds information about the content portion of the referenced file. + ///A Serialized File is made up of a header section, and then a flat list of the objects, in their binary serialized form. + ///Each entry in the PackedAssetInfo array corresponds to one of these serialized objects, in the order they are saved in the file. + /// + ///Similarly a .resS or .resource file is made of a sequence of binary blobs of audio, video, texture, mesh or other large data types. The PackedAssetInfo array has an entry for each of these blobs. public PackedAssetInfo[] contents { get { return GetContents(); } diff --git a/Modules/BuildReportingEditor/Managed/ScenesUsingAsset.cs b/Modules/BuildReportingEditor/Managed/ScenesUsingAsset.cs index 4c717a8b4e..a30647a800 100644 --- a/Modules/BuildReportingEditor/Managed/ScenesUsingAsset.cs +++ b/Modules/BuildReportingEditor/Managed/ScenesUsingAsset.cs @@ -7,10 +7,13 @@ namespace UnityEditor.Build.Reporting { + ///Contains information about which scenes in a build have references to an Asset in the build. [NativeType(Header = "Modules/BuildReportingEditor/Public/ScenesUsingAssets.h")] public struct ScenesUsingAsset { + ///The asset path. public string assetPath { get; } + ///The list of scenes in the build referring to the asset, identified by a string containing the scene index in the list, as well as the scene path. public string[] scenePaths { get; } } } diff --git a/Modules/BuildReportingEditor/Managed/ScenesUsingAssets.bindings.cs b/Modules/BuildReportingEditor/Managed/ScenesUsingAssets.bindings.cs index 0b7d0f95d1..0908b36e7a 100644 --- a/Modules/BuildReportingEditor/Managed/ScenesUsingAssets.bindings.cs +++ b/Modules/BuildReportingEditor/Managed/ScenesUsingAssets.bindings.cs @@ -8,10 +8,13 @@ namespace UnityEditor.Build.Reporting { + ///An extension to the class that tracks which scenes in the build have references to a specific asset in the build. + ///The build process generates this information when is used during a build. [NativeType(Header = "Modules/BuildReportingEditor/Public/ScenesUsingAssets.h")] [NativeClass("BuildReporting::ScenesUsingAssets")] public sealed class ScenesUsingAssets : Object { + ///An array of that holds information about the Assets that are included in the build. public ScenesUsingAsset[] list { get { return GetList(); } diff --git a/Modules/BuildReportingEditor/Managed/StrippingInfo.cs b/Modules/BuildReportingEditor/Managed/StrippingInfo.cs index 0f3d88efa7..766ceea27c 100644 --- a/Modules/BuildReportingEditor/Managed/StrippingInfo.cs +++ b/Modules/BuildReportingEditor/Managed/StrippingInfo.cs @@ -12,10 +12,19 @@ namespace UnityEditor.Build.Reporting { + ///The StrippingInfo object contains information about which native code modules in the engine are still present in the build, and the reasons why they are still present. public class StrippingInfo : ScriptableObject, ISerializationCallbackReceiver { + ///The native engine modules that were included in the build. + ///You can pass each of these to to obtain information about what caused a module to be included in the build. public IEnumerable includedModules { get { return modules; } } + ///Returns the list of dependencies or reasons that caused the given entity to be included in the build. + ///The returned list of strings may include names of components, internal engine objects, other modules, or other human-readable reasons. To obtain further information, you can pass each string back into GetReasonsForIncluding again. + /// + ///For example, calling GetReasonsForIncluding("Physics Module") may return a list that includes "Rigidbody", and you can then call GetReasonsForIncluding("Rigidbody") to get more information about which Scenes or assets are using the Rigidbody component. + ///The name of an engine module, class, or other entity present in the build. + ///A list of modules, classes, or other entities that caused the provided entity to be included in the build. public IEnumerable GetReasonsForIncluding(string entityName) { HashSet deps; diff --git a/Modules/DeviceSimulatorEditor/Shims/ScreenSimulation.cs b/Modules/DeviceSimulatorEditor/Shims/ScreenSimulation.cs index 9a10c891fc..79a39cb1f9 100644 --- a/Modules/DeviceSimulatorEditor/Shims/ScreenSimulation.cs +++ b/Modules/DeviceSimulatorEditor/Shims/ScreenSimulation.cs @@ -481,7 +481,7 @@ public void Disable() public override float dpi => m_Screen.dpi; - public override Resolution currentResolution => new Resolution() { width = m_CurrentWidth, height = m_CurrentHeight }; + public override Resolution currentResolution => new Resolution() { width = m_CurrentWidth, height = m_CurrentHeight, refreshRateRatio = EditorScreen.currentResolution.refreshRateRatio }; public override Resolution[] resolutions => new[] { currentResolution }; diff --git a/Modules/DeviceSimulatorEditor/UserInterfaceController.cs b/Modules/DeviceSimulatorEditor/UserInterfaceController.cs index 4d6d606c8d..82d6f4ee22 100644 --- a/Modules/DeviceSimulatorEditor/UserInterfaceController.cs +++ b/Modules/DeviceSimulatorEditor/UserInterfaceController.cs @@ -130,6 +130,7 @@ public UserInterfaceController(DeviceSimulatorMain deviceSimulatorMain, VisualEl m_ScaleSlider.lowValue = kScaleMin; m_ScaleSlider.highValue = kScaleMax; m_Scale = serializedState.scale; + m_ScaleSlider.value = serializedState.scale; m_ScaleSlider.SetValueWithoutNotify(m_Scale); m_ScaleSlider.RegisterCallback>(SetScale); m_ScaleValueLabel = rootVisualElement.Q