diff --git a/Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/BeeBuildProgramCommon.Data.gen.csproj b/Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/BeeBuildProgramCommon.Data.gen.csproj
index 7ff0a1d95a..78ec3c7d98 100644
--- a/Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/BeeBuildProgramCommon.Data.gen.csproj
+++ b/Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/BeeBuildProgramCommon.Data.gen.csproj
@@ -6,7 +6,7 @@
false
false
latest
-
+ 1071
diff --git a/Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/Data.cs b/Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/Data.cs
index 027cdb62d9..53ea17e788 100644
--- a/Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/Data.cs
+++ b/Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/Data.cs
@@ -35,5 +35,6 @@ public class ConfigurationData
public string UnitySourceCodePath;
public bool AdvancedLicense;
public bool Batchmode;
+ public bool EmitDataForBeeWhy;
}
}
diff --git a/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/PlayerBuildProgramLibrary.Data.gen.csproj b/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/PlayerBuildProgramLibrary.Data.gen.csproj
index 5d8e5ddc83..ae35317b5e 100644
--- a/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/PlayerBuildProgramLibrary.Data.gen.csproj
+++ b/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/PlayerBuildProgramLibrary.Data.gen.csproj
@@ -6,7 +6,7 @@
false
false
latest
-
+ 1071
PlayerBuildProgramLibrary.Data
diff --git a/Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/ScriptCompilationBuildProgram.Data.gen.csproj b/Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/ScriptCompilationBuildProgram.Data.gen.csproj
index a84a752587..0360304bcd 100644
--- a/Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/ScriptCompilationBuildProgram.Data.gen.csproj
+++ b/Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/ScriptCompilationBuildProgram.Data.gen.csproj
@@ -6,7 +6,7 @@
false
false
latest
-
+ 1071
diff --git a/Editor/Mono/AssetDatabase/AssetDatabase.deprecated.cs b/Editor/Mono/AssetDatabase/AssetDatabase.deprecated.cs
index 3beae4af33..f3a119ab83 100644
--- a/Editor/Mono/AssetDatabase/AssetDatabase.deprecated.cs
+++ b/Editor/Mono/AssetDatabase/AssetDatabase.deprecated.cs
@@ -65,7 +65,7 @@ public partial class AssetDatabaseExperimental
[Obsolete("AssetDatabaseExperimental.IsCacheServerEnabled() has been deprecated. Use AssetDatabase.IsCacheServerEnabled() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.IsCacheServerEnabled(*)", false)]
public extern static bool IsCacheServerEnabled();
- [Obsolete("AssetDatabaseExperimental.SetImporterOverride() has been deprecated. Use AssetDatabase.SetImporterOverride() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.SetImporterOverride(*)", false)]
+ [Obsolete("AssetDatabaseExperimental.SetImporterOverride() has been deprecated. Use AssetDatabase.SetImporterOverride() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.SetImporterOverride(*)", true)]
public static void SetImporterOverride(string path)
where T : ScriptedImporter
{
@@ -73,11 +73,11 @@ public static void SetImporterOverride(string path)
}
[FreeFunction("AssetDatabase::GetImporterOverride")]
- [Obsolete("AssetDatabaseExperimental.GetImporterOverride() has been deprecated. Use AssetDatabase.GetImporterOverride() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.GetImporterOverride(*)", false)]
+ [Obsolete("AssetDatabaseExperimental.GetImporterOverride() has been deprecated. Use AssetDatabase.GetImporterOverride() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.GetImporterOverride(*)", true)]
extern public static System.Type GetImporterOverride(string path);
- [FreeFunction("AssetDatabase::GetAvailableImporterTypes")]
- [Obsolete("AssetDatabaseExperimental.GetAvailableImporterTypes() has been deprecated. Use AssetDatabase.GetAvailableImporterTypes() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.GetAvailableImporterTypes(*)", false)]
+ [FreeFunction("AssetDatabase::GetAvailableImporters")]
+ [Obsolete("AssetDatabaseExperimental.GetAvailableImporterTypes() has been deprecated. Use AssetDatabase.GetAvailableImporters() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.GetAvailableImporters(*)", true)]
extern public static Type[] GetAvailableImporterTypes(string path);
[FreeFunction("AcceleratorClientCanConnectTo")]
diff --git a/Editor/Mono/Commands/GOCreationCommands.cs b/Editor/Mono/Commands/GOCreationCommands.cs
index f231be4c0c..6caef996d3 100644
--- a/Editor/Mono/Commands/GOCreationCommands.cs
+++ b/Editor/Mono/Commands/GOCreationCommands.cs
@@ -411,6 +411,7 @@ static void CreateParticleSystem(MenuCommand menuCommand)
go.GetComponent().SetLocalEulerAngles(new Vector3(-90, 0, 0), RotationOrder.OrderZXY);
var renderer = go.GetComponent();
renderer.material = Material.GetDefaultParticleMaterial();
+ renderer.oldTrailMaterial = Material.GetDefaultLineMaterial(); // This trick means that when enabling the trails module for the first time, there is a default material assigned
Place(go, parent);
}
diff --git a/Editor/Mono/ContainerWindow.bindings.cs b/Editor/Mono/ContainerWindow.bindings.cs
index c51c5c25b2..43c3163335 100644
--- a/Editor/Mono/ContainerWindow.bindings.cs
+++ b/Editor/Mono/ContainerWindow.bindings.cs
@@ -25,7 +25,9 @@ internal enum ShowMode
// Like PopupMenu, but without keyboard focus
Tooltip = 6,
// Modal Utility window
- ModalUtility = 7
+ ModalUtility = 7,
+ // Show as fullscreen window
+ Fullscreen = 8
}
//[StaticAccessor("ContainerWindowBindings", StaticAccessorType.DoubleColon)]
@@ -63,6 +65,12 @@ public extern Rect position
[FreeFunction(k_ScriptingPrefix + "ToggleMaximize", HasExplicitThis = true)]
public extern void ToggleMaximize();
+ [FreeFunction(k_ScriptingPrefix + "ToggleFullscreen", HasExplicitThis = true)]
+ internal extern void ToggleFullscreen(int displayIndex = 0);
+
+ [FreeFunction(k_ScriptingPrefix + "IsFullscreen", HasExplicitThis = true)]
+ internal extern bool IsFullscreen();
+
[FreeFunction(k_ScriptingPrefix + "MoveInFrontOf", HasExplicitThis = true)]
public extern void MoveInFrontOf(ContainerWindow other);
@@ -76,6 +84,9 @@ public extern Rect position
[FreeFunction(k_ScriptingPrefix + "SendCaptionEvent", HasExplicitThis = true)]
public extern void SendCaptionEvent(bool mouseDown);
+ [FreeFunction(k_ScriptingPrefix + "GetDisplayId", HasExplicitThis = true)]
+ internal extern int GetDisplayId();
+
// Close the editor window.
[FreeFunction(k_ScriptingPrefix + "InternalClose", HasExplicitThis = true)]
public extern void InternalClose();
@@ -84,7 +95,7 @@ public extern Rect position
private extern void Internal_SetMinMaxSizes(Vector2 minSize, Vector2 maxSize);
[FreeFunction(k_ScriptingPrefix + "Internal_Show", HasExplicitThis = true, ThrowsException = true)]
- private extern void Internal_Show(Rect r, int showMode, Vector2 minSize, Vector2 maxSize);
+ private extern void Internal_Show(Rect r, int showMode, Vector2 minSize, Vector2 maxSize, int displayIndex = 0);
[FreeFunction(k_ScriptingPrefix + "Internal_BringLiveAfterCreation", HasExplicitThis = true)]
private extern void Internal_BringLiveAfterCreation(bool displayImmediately, bool setFocus, bool showMaximized);
diff --git a/Editor/Mono/ContainerWindow.cs b/Editor/Mono/ContainerWindow.cs
index 8825b034f8..a153eaca59 100644
--- a/Editor/Mono/ContainerWindow.cs
+++ b/Editor/Mono/ContainerWindow.cs
@@ -24,6 +24,9 @@ internal partial class ContainerWindow : ScriptableObject
[SerializeField] Vector2 m_MaxSize = new Vector2(8192, 8192);
[SerializeField] bool m_Maximized;
+ internal int m_DisplayIndex;
+ internal bool m_IsFullscreenContainer;
+
internal bool m_DontSaveToLayout = false;
private bool m_HasUnsavedChanges = false;
private List m_UnsavedEditorWindows;
@@ -144,20 +147,21 @@ internal void ShowPopupWithMode(ShowMode mode, bool giveFocus)
static Color skinBackgroundColor => EditorGUIUtility.isProSkin ? darkSkinColor : lightSkinColor;
// Show the editor window.
- public void Show(ShowMode showMode, bool loadPosition, bool displayImmediately, bool setFocus)
+ public void Show(ShowMode showMode, bool loadPosition, bool displayImmediately, bool setFocus, int displayIndex = 0)
{
try
{
if (showMode == ShowMode.MainWindow && s_MainWindow && s_MainWindow != this)
throw new InvalidOperationException("Trying to create a second main window from layout when one already exists.");
- bool useMousePos = showMode == ShowMode.AuxWindow;
+ bool useMousePos = showMode == ShowMode.AuxWindow || showMode == ShowMode.Fullscreen;
if (showMode == ShowMode.AuxWindow)
showMode = ShowMode.Utility;
if (showMode == ShowMode.Utility
|| showMode == ShowMode.ModalUtility
|| showMode == ShowMode.AuxWindow
+ || showMode == ShowMode.Fullscreen
|| IsPopup(showMode))
m_DontSaveToLayout = true;
@@ -171,7 +175,7 @@ public void Show(ShowMode showMode, bool loadPosition, bool displayImmediately,
var initialMaximizedState = m_Maximized;
- Internal_Show(m_PixelRect, m_ShowMode, m_MinSize, m_MaxSize);
+ Internal_Show(m_PixelRect, m_ShowMode, m_MinSize, m_MaxSize, displayIndex);
// Tell the main view its now in this window (quick hack to get platform-specific code to move its views to the right window)
if (m_RootView)
diff --git a/Editor/Mono/Display/EditorDisplayFullscreenSetting.cs b/Editor/Mono/Display/EditorDisplayFullscreenSetting.cs
new file mode 100644
index 0000000000..040b762cbc
--- /dev/null
+++ b/Editor/Mono/Display/EditorDisplayFullscreenSetting.cs
@@ -0,0 +1,42 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using System;
+using UnityEngine;
+
+namespace UnityEditor
+{
+ [Serializable]
+ internal class EditorDisplayFullscreenSetting
+ {
+ public enum Mode
+ {
+ DoNothing,
+ FullscreenOnPlaymode,
+ AlwaysFullscreen
+ }
+
+ public EditorDisplayFullscreenSetting(int id, string name)
+ {
+ displayId = id;
+ displayName = name;
+ mode = Mode.DoNothing;
+ enabled = false;
+ viewWindowTitle = string.Empty;
+ playModeViewSettings = null;
+ }
+
+ public string displayName;
+ public int displayId;
+
+ public bool enabled;
+
+ public Mode mode;
+
+ public string viewWindowTitle;
+
+ [SerializeReference]
+ public IPlayModeViewFullscreenSettings playModeViewSettings;
+ }
+}
diff --git a/Editor/Mono/Display/EditorDisplayManager.cs b/Editor/Mono/Display/EditorDisplayManager.cs
new file mode 100644
index 0000000000..b44ba6b521
--- /dev/null
+++ b/Editor/Mono/Display/EditorDisplayManager.cs
@@ -0,0 +1,267 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using System;
+using UnityEngine;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEditor.Modules;
+
+namespace UnityEditor
+{
+ internal enum DisplayAPIControlMode
+ {
+ FromEditor,
+ FromRuntime
+ }
+
+ [InitializeOnLoad]
+ internal class EditorDisplayManager : ScriptableSingleton
+ {
+ private PlayModeView[] m_views;
+ private int m_displayCount;
+ private DisplayAPIControlMode m_mode;
+ private int m_maxDisplays;
+ private BuildTarget m_currentBuildTarget;
+
+ static EditorDisplayManager()
+ {
+ UnsubscribeEditorDisplayCallback();
+ SubscribeEditorDisplayCallback();
+ }
+
+ private static void SubscribeEditorDisplayCallback()
+ {
+ Display.onGetSystemExt += GetSystemExtImpl;
+ Display.onGetRenderingExt += GetRenderingExtImpl;
+ Display.onGetRenderingBuffers += GetRenderingBuffersImpl;
+ Display.onSetRenderingResolution += SetRenderingResolutionImpl;
+ Display.onActivateDisplay += ActivateDisplayImpl;
+ Display.onSetParams += SetParamsImpl;
+ Display.onRelativeMouseAt += RelativeMouseAtImpl;
+ Display.onGetActive += GetActiveImpl;
+ Display.onRequiresBlitToBackbuffer += RequiresBlitToBackbufferImpl;
+ Display.onRequiresSrgbBlitToBackbuffer += RequiresSrgbBlitToBackbufferImpl;
+ }
+
+ private static void UnsubscribeEditorDisplayCallback()
+ {
+ Display.onGetSystemExt -= GetSystemExtImpl;
+ Display.onGetRenderingExt -= GetRenderingExtImpl;
+ Display.onGetRenderingBuffers -= GetRenderingBuffersImpl;
+ Display.onSetRenderingResolution -= SetRenderingResolutionImpl;
+ Display.onActivateDisplay -= ActivateDisplayImpl;
+ Display.onSetParams -= SetParamsImpl;
+ Display.onRelativeMouseAt -= RelativeMouseAtImpl;
+ Display.onGetActive -= GetActiveImpl;
+ Display.onRequiresBlitToBackbuffer -= RequiresBlitToBackbufferImpl;
+ Display.onRequiresSrgbBlitToBackbuffer -= RequiresSrgbBlitToBackbufferImpl;
+ }
+
+ private void OnEnable()
+ {
+ Initialize();
+ EditorApplication.update += OnUpdate;
+ }
+
+ private void OnDisable()
+ {
+ EditorApplication.update -= OnUpdate;
+ }
+
+ public void Initialize()
+ {
+ m_currentBuildTarget = EditorUserBuildSettings.activeBuildTarget;
+ m_displayCount = 0;
+
+ m_maxDisplays = ModuleManager.ShouldShowMultiDisplayOption() ?
+ GetDisplayNamesForBuildTarget(EditorUserBuildSettings.activeBuildTarget).Length : 1;
+
+ m_views = new PlayModeView[m_maxDisplays];
+ UpdateAssociatedPlayModeView();
+ }
+
+ private void OnUpdate()
+ {
+ if (m_currentBuildTarget != EditorUserBuildSettings.activeBuildTarget)
+ {
+ Initialize();
+ }
+ }
+
+ private void UpdateAssociatedPlayModeView()
+ {
+ for (var i = 0; i < m_maxDisplays; ++i)
+ {
+ var view = PlayModeView.GetAssociatedViewForTargetDisplay(i);
+ if (m_views[i] == view)
+ {
+ continue;
+ }
+
+ m_views[i] = view;
+ if (view != null)
+ {
+ EditorDisplayUtility.AddVirtualDisplay(i, (int)view.targetSize.x, (int)view.targetSize.y);
+ }
+ else
+ {
+ EditorDisplayUtility.RemoveVirtualDisplay(i);
+ }
+ }
+
+ UpdateDisplayList(false);
+ }
+
+ private void UpdateDisplayList(bool recreate)
+ {
+ recreate |= m_mode != EditorFullscreenController.DisplayAPIMode;
+ m_mode = EditorFullscreenController.DisplayAPIMode;
+
+ if (EditorFullscreenController.DisplayAPIMode == DisplayAPIControlMode.FromEditor)
+ {
+ var previousDisplayCount = m_displayCount;
+ for (var i = m_maxDisplays - 1; i >= 0; --i)
+ {
+ if (m_views[i] != null)
+ {
+ m_displayCount = i + 1;
+ recreate |= m_displayCount != previousDisplayCount;
+ break;
+ }
+ }
+ }
+ else
+ {
+ var nDisplays = EditorDisplayUtility.GetNumberOfConnectedDisplays();
+ recreate |= m_displayCount != nDisplays;
+ m_displayCount = nDisplays;
+ }
+
+ if (recreate)
+ {
+ var displayList = new IntPtr[m_displayCount];
+ for (var i = 0; i < m_displayCount; ++i)
+ {
+ displayList[i] = new IntPtr(i);
+ }
+
+ Display.RecreateDisplayList(displayList);
+ }
+ }
+
+ internal static GUIContent[] GetDisplayNamesForBuildTarget(BuildTarget buildTarget)
+ {
+ var platformDisplayNames = Modules.ModuleManager.GetDisplayNames(buildTarget.ToString());
+ return platformDisplayNames ?? DisplayUtility.GetGenericDisplayNames();
+ }
+
+ private static void GetSystemExtImpl(IntPtr nativeDisplay, out int w, out int h)
+ {
+ var manager = instance;
+ if (manager.m_mode == DisplayAPIControlMode.FromEditor)
+ {
+ var view = manager.m_views[(int)nativeDisplay];
+
+ if (view == null)
+ {
+ w = 0;
+ h = 0;
+ }
+ else
+ {
+ w = (int)view.position.width;
+ h = (int)view.position.height;
+ }
+ }
+ else
+ {
+ w = (int)EditorDisplayUtility.GetDisplayWidth((int)nativeDisplay);
+ h = (int)EditorDisplayUtility.GetDisplayHeight((int)nativeDisplay);
+ }
+ }
+
+ private static void GetRenderingExtImpl(IntPtr nativeDisplay, out int w, out int h)
+ {
+ var manager = instance;
+ if (manager.m_mode == DisplayAPIControlMode.FromEditor)
+ {
+ var view = manager.m_views[(int)nativeDisplay];
+
+ if (view == null)
+ {
+ w = 0;
+ h = 0;
+ }
+ else
+ {
+ w = (int)view.targetSize.x;
+ h = (int)view.targetSize.y;
+ }
+ }
+ else
+ {
+ w = (int)EditorDisplayUtility.GetDisplayWidth((int)nativeDisplay);
+ h = (int)EditorDisplayUtility.GetDisplayHeight((int)nativeDisplay);
+ }
+ }
+
+ private static void GetRenderingBuffersImpl(IntPtr nativeDisplay, out RenderBuffer color,
+ out RenderBuffer depth)
+ {
+ color = new RenderBuffer();
+ depth = new RenderBuffer();
+ }
+
+ private static void SetRenderingResolutionImpl(IntPtr nativeDisplay, int w, int h)
+ {
+ var manager = instance;
+ if (manager.m_mode == DisplayAPIControlMode.FromEditor)
+ {
+ var view = manager.m_views[(int)nativeDisplay];
+ if (view != null) {
+ view.SetPlayModeViewSize(new Vector2(w, h));
+ }
+ }
+ }
+
+ private static void ActivateDisplayImpl(IntPtr nativeDisplay, int width, int height, int refreshRate)
+ {
+ var manager = instance;
+ if (manager.m_mode == DisplayAPIControlMode.FromRuntime)
+ {
+ EditorFullscreenController.BeginFullscreen((int)nativeDisplay, width, height);
+ }
+ }
+
+ private static void SetParamsImpl(IntPtr nativeDisplay, int width, int height, int x, int y)
+ {
+ // do nothing.
+ }
+
+ private static int RelativeMouseAtImpl(int x, int y, out int rx, out int ry)
+ {
+ // TODO, unused?
+ rx = 0;
+ ry = 0;
+ return 0;
+ }
+
+ private static bool GetActiveImpl(IntPtr nativeDisplay)
+ {
+ var view = instance.m_views[(int)nativeDisplay];
+ return view != null;
+ }
+
+ private static bool RequiresBlitToBackbufferImpl(IntPtr nativeDisplay)
+ {
+ return false;
+ }
+
+ private static bool RequiresSrgbBlitToBackbufferImpl(IntPtr nativeDisplay)
+ {
+ return false;
+ }
+ }
+} // namespace
diff --git a/Editor/Mono/Display/EditorDisplaySettingsProfile.cs b/Editor/Mono/Display/EditorDisplaySettingsProfile.cs
new file mode 100644
index 0000000000..3cb40c15c5
--- /dev/null
+++ b/Editor/Mono/Display/EditorDisplaySettingsProfile.cs
@@ -0,0 +1,79 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using System;
+using UnityEngine;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace UnityEditor
+{
+ [Serializable]
+ internal class EditorDisplaySettingsProfile
+ {
+ [SerializeField] private string m_name;
+ [SerializeField] private List m_settings;
+ [SerializeField] private BuildTarget m_buildTarget;
+ [SerializeField] private DisplayAPIControlMode displayAPIMode;
+ [SerializeField] private bool m_displayAPIUseSystemConfiguration;
+
+ public string Name
+ {
+ get => m_name;
+ set => m_name = value;
+ }
+
+ public BuildTarget Target
+ {
+ get => m_buildTarget;
+ set => m_buildTarget = value;
+ }
+
+ public DisplayAPIControlMode DisplayAPIMode
+ {
+ get => displayAPIMode;
+ set => displayAPIMode = value;
+ }
+
+ public bool DisplayAPIUseSystemConfiguration
+ {
+ get => m_displayAPIUseSystemConfiguration;
+ set => m_displayAPIUseSystemConfiguration = value;
+ }
+
+ public List Settings => m_settings;
+
+ public EditorDisplaySettingsProfile(string name)
+ {
+ m_name = name;
+ m_buildTarget = EditorUserBuildSettings.activeBuildTarget;
+ displayAPIMode = DisplayAPIControlMode.FromEditor;
+ }
+
+ public EditorDisplayFullscreenSetting GetEditorDisplayFullscreenSetting(int displayId)
+ {
+ if (m_settings == null)
+ {
+ m_settings = new List();
+ }
+
+ return m_settings.FirstOrDefault(setting => setting.displayId == displayId);
+ }
+
+ public void AddEditorDisplayFullscreenSetting(EditorDisplayFullscreenSetting setting)
+ {
+ if (m_settings == null)
+ {
+ m_settings = new List();
+ }
+
+ m_settings.Add(setting);
+ }
+
+ public void RemoveEditorDisplayFullscreenSetting(EditorDisplayFullscreenSetting setting)
+ {
+ m_settings.Remove(setting);
+ }
+ }
+} // namespace
diff --git a/Editor/Mono/Display/EditorDisplayUtility.bindings.cs b/Editor/Mono/Display/EditorDisplayUtility.bindings.cs
new file mode 100644
index 0000000000..5119008563
--- /dev/null
+++ b/Editor/Mono/Display/EditorDisplayUtility.bindings.cs
@@ -0,0 +1,41 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using UnityEngine;
+using UnityEngine.Bindings;
+
+namespace UnityEditor
+{
+ [NativeHeader("Editor/Platform/Interface/GUIView.h")]
+ [NativeHeader("Runtime/Graphics/EditorDisplayManager.h")]
+ internal static partial class EditorDisplayUtility
+ {
+ [FreeFunction]
+ public static extern int GetNumberOfConnectedDisplays();
+
+ [FreeFunction]
+ public static extern void AddVirtualDisplay(int index, int width, int height);
+
+ [FreeFunction]
+ public static extern void RemoveVirtualDisplay(int index);
+
+ [FreeFunction]
+ public static extern void SetSortDisplayOrder(bool enabled);
+
+ [FreeFunction]
+ public static extern string GetDisplayName(int index);
+
+ [FreeFunction]
+ public static extern int GetDisplayId(int index);
+
+ [FreeFunction]
+ public static extern int GetDisplayWidth(int index);
+
+ [FreeFunction]
+ public static extern int GetDisplayHeight(int index);
+
+ [FreeFunction]
+ public static extern int GetMainDisplayId();
+ }
+}
diff --git a/Editor/Mono/Display/EditorFullscreenController.cs b/Editor/Mono/Display/EditorFullscreenController.cs
new file mode 100644
index 0000000000..30498d4654
--- /dev/null
+++ b/Editor/Mono/Display/EditorFullscreenController.cs
@@ -0,0 +1,1210 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using System;
+using UnityEngine;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using UnityEditor.Modules;
+using UnityEditor.ShortcutManagement;
+using UnityEditorInternal;
+using UnityEngine.Bindings;
+using UnityEngine.Scripting;
+
+namespace UnityEditor
+{
+ [InitializeOnLoad]
+ [FilePathAttribute("Library/EditorDisplaySettings.asset", FilePathAttribute.Location.ProjectFolder)]
+ internal class EditorFullscreenController : ScriptableSingleton
+ {
+ private const float kDisplayCheckIntervalSec = 1.0f;
+
+ [SerializeField] private List m_profiles = null;
+ [SerializeField] private bool m_isSortDisplayOrder = true;
+ [SerializeField] private bool m_showNotificationOnFullscreen = true;
+ [SerializeField] private bool m_showToolbarOnFullscreen = false;
+ [SerializeField] private int m_selectedProfileIndex;
+ [SerializeField] private EditorDisplayFullscreenSetting m_mainDisplaySetting;
+
+ private PlayModeStateChange m_state;
+ private EditorDisplayFullscreenSetting m_defaultSetting;
+
+ internal bool isPlaying => m_state == PlayModeStateChange.ExitingEditMode;
+
+ private float m_tLastTimeChecked;
+ private int m_numberOfConnectedDisplays;
+
+ private string[] m_displayNames;
+ private int[] m_displayIds;
+ private int m_mainDisplayId;
+
+ private EnumData m_buildTargetData;
+
+ private Dictionary m_AvailableWindowTypes;
+ private Dictionary m_DisplaySettings;
+ private List m_FullscreenContainerWindows;
+
+ // This is a stub for selecting the fullscreen option via the game view toolbar GUI.
+ // Instead of having configurable profiles, we'll use the main display setting that is
+ // created by default and modify which display we want to see it on via the toolbar GUI
+ // dropdown. In the future we may want to hook it up to the currently active display profile.
+
+ // This is a stub. in the future references to this should be replaced with the currently active profile.
+
+ internal static void SetSettingsForCurrentDisplay(int display)
+ {
+ var c = instance;
+
+ if (c.m_DisplaySettings == null)
+ {
+ c.m_DisplaySettings = new Dictionary();
+ }
+
+ if (c.m_DisplaySettings.ContainsKey(display))
+ {
+ c.m_mainDisplaySetting = c.m_DisplaySettings[display];
+ return;
+ }
+
+ var newDisplay = new EditorDisplayFullscreenSetting(c.m_DisplaySettings.Count + 1, "Main Display");
+ newDisplay.enabled = true;
+ newDisplay.playModeViewSettings = new GameViewFullscreenSettings();
+ newDisplay.viewWindowTitle = GetWindowTitle(typeof(GameView));
+
+ c.m_DisplaySettings.Add(display, newDisplay);
+ c.m_mainDisplaySetting = c.m_DisplaySettings[display];
+ }
+
+ internal static bool isFullscreenOnPlay
+ {
+ get => instance.m_mainDisplaySetting.mode == EditorDisplayFullscreenSetting.Mode.FullscreenOnPlaymode;
+ set => instance.SetFullscreenMainDisplay(value);
+ }
+
+ // This is a stub. Future references to this should be replaced with the currently active profile.
+ internal static bool isToolbarEnabledOnFullscreen
+ {
+ get
+ {
+ return (instance.m_mainDisplaySetting.playModeViewSettings is GameViewFullscreenSettings settings)
+ ? settings.ShowToolbar
+ : false;
+ }
+ set
+ {
+ instance.SetShowToolbarOnMainDisplay(value);
+ }
+ }
+
+ // This is a stub. Future references to this should be replaced with the currently active profile.
+ internal static int fullscreenDisplayId
+ {
+ get => instance.m_mainDisplaySetting.displayId;
+ set => instance.SetFullscreenDisplayId(value);
+ }
+
+ internal static int targetDisplayID
+ {
+ get
+ {
+ return (instance.m_mainDisplaySetting.playModeViewSettings is GameViewFullscreenSettings settings)
+ ? settings.DisplayNumber
+ : 0;
+ }
+ set
+ {
+ if (instance.m_mainDisplaySetting.playModeViewSettings is GameViewFullscreenSettings settings)
+ {
+ settings.DisplayNumber = value;
+ }
+ }
+ }
+
+ internal static bool enableVSync
+ {
+ get
+ {
+ return (instance.m_mainDisplaySetting.playModeViewSettings is GameViewFullscreenSettings settings)
+ ? settings.VsyncEnabled
+ : false;
+ }
+ set
+ {
+ if (instance.m_mainDisplaySetting.playModeViewSettings is GameViewFullscreenSettings settings)
+ {
+ settings.VsyncEnabled = value;
+ }
+ }
+ }
+
+ internal static int selectedSizeIndex
+ {
+ get
+ {
+ return (instance.m_mainDisplaySetting.playModeViewSettings is GameViewFullscreenSettings settings)
+ ? settings.SelectedSizeIndex
+ : 0;
+ }
+ set
+ {
+ if (instance.m_mainDisplaySetting.playModeViewSettings is GameViewFullscreenSettings settings)
+ {
+ settings.SelectedSizeIndex = value;
+ }
+ }
+ }
+
+ // This is a stub. Future references to this should be replaced with the currently active profile.
+ internal static Vector2 fullscreenDisplayRenderSize
+ {
+ get => instance.GetDisplayRenderSizeFromId(fullscreenDisplayId);
+ }
+
+ internal static DisplayAPIControlMode DisplayAPIMode
+ {
+ get
+ {
+ var c = instance;
+ if (c.m_profiles == null || c.m_profiles.Count <= 0)
+ {
+ c.InitializeProfile();
+ }
+
+ if (c.m_profiles == null || c.m_profiles.Count <= c.m_selectedProfileIndex)
+ return DisplayAPIControlMode.FromEditor;
+
+ return c.m_profiles[c.m_selectedProfileIndex].DisplayAPIMode;
+ }
+ }
+
+ internal static void OnEnterPlaymode()
+ {
+ var c = instance;
+ c.m_state = PlayModeStateChange.ExitingEditMode;
+ c.RefreshDisplayStateWithCurrentProfile();
+ }
+
+ internal static void OnExitPlaymode()
+ {
+ var c = instance;
+ c.m_state = PlayModeStateChange.ExitingPlayMode;
+ c.RefreshDisplayStateWithCurrentProfile();
+ }
+
+ internal static void SetMainDisplayPlayModeViewType(Type playModeViewType)
+ {
+ SetDisplayPlayModeViewType(instance.m_mainDisplaySetting, playModeViewType);
+ }
+
+ internal static int[] GetConnectedDisplayIds()
+ {
+ return instance.m_displayIds;
+ }
+
+ internal static string[] GetConnectedDisplayNames()
+ {
+ return instance.m_displayNames;
+ }
+
+ internal static string[] GetConnectedDisplayIdsAndNames()
+ {
+ int displayCount = instance.m_displayNames.Length;
+ string[] displayList = new string[displayCount];
+
+ for (int i = 0; i < displayCount; i++)
+ {
+ displayList[i] = i + ": " + instance.m_displayNames[i];
+ }
+ return displayList;
+ }
+
+ private void InitializeProfile()
+ {
+ if (m_profiles != null)
+ {
+ return;
+ }
+
+ m_mainDisplaySetting = new EditorDisplayFullscreenSetting(0, "Main Display");
+ m_mainDisplaySetting.enabled = true;
+ m_mainDisplaySetting.playModeViewSettings = new GameViewFullscreenSettings();
+ m_mainDisplaySetting.viewWindowTitle = GetWindowTitle(typeof(GameView));
+
+ m_profiles = new List();
+ var defaultProfile = new EditorDisplaySettingsProfile("Default");
+ defaultProfile.AddEditorDisplayFullscreenSetting(m_defaultSetting);
+
+ var displayAPIProfile = new EditorDisplaySettingsProfile("Simulate Standalone")
+ {
+ DisplayAPIMode = DisplayAPIControlMode.FromRuntime
+ };
+
+ for (var i = 0; i < m_displayNames.Length; ++i)
+ {
+ displayAPIProfile.AddEditorDisplayFullscreenSetting(CreateDefaultEditorDisplayFullscreenSetting(i));
+ }
+
+ m_profiles.Add(defaultProfile);
+ m_profiles.Add(displayAPIProfile);
+ m_selectedProfileIndex = 0;
+ }
+
+ private EditorDisplayFullscreenSetting CreateDefaultEditorDisplayFullscreenSetting(int displayNumber)
+ {
+ var setting = new EditorDisplayFullscreenSetting(m_displayIds[displayNumber], m_displayNames[displayNumber]) {enabled = true};
+ var gvSetting = new GameViewFullscreenSettings {DisplayNumber = displayNumber};
+ setting.playModeViewSettings = gvSetting;
+ setting.viewWindowTitle = GetWindowTitle(typeof(GameView));
+
+ return setting;
+ }
+
+ private void OnEnable()
+ {
+ EditorApplication.globalEventHandler += HandleToggleFullscreenKeyShortcut;
+
+ UpdateDisplayNamesAndIds();
+ GetInstalledBuildTargetData();
+
+ if (m_profiles == null)
+ {
+ InitializeProfile();
+ }
+
+ m_FullscreenContainerWindows = new List();
+ m_defaultSetting = new EditorDisplayFullscreenSetting(0, string.Empty);
+ m_defaultSetting.enabled = true;
+ m_defaultSetting.playModeViewSettings = new GameViewFullscreenSettings();
+ m_defaultSetting.viewWindowTitle = GetWindowTitle(typeof(GameView));
+
+ EditorDisplayUtility.SetSortDisplayOrder(m_isSortDisplayOrder);
+ RefreshDisplayStateWithCurrentProfile();
+
+ EditorApplication.update += CheckDisplayNumberChanged;
+ }
+
+ private void OnDisable()
+ {
+ EditorApplication.update -= CheckDisplayNumberChanged;
+ EditorApplication.globalEventHandler -= HandleToggleFullscreenKeyShortcut;
+ }
+
+ public void CheckDisplayNumberChanged()
+ {
+ var tNow = Time.realtimeSinceStartup;
+ if (tNow - m_tLastTimeChecked < kDisplayCheckIntervalSec)
+ return;
+
+ var ncDisplays = EditorDisplayUtility.GetNumberOfConnectedDisplays();
+ if (ncDisplays != m_numberOfConnectedDisplays)
+ {
+ UpdateDisplayNamesAndIds();
+ HandleDisplayProfileChange();
+ }
+
+ m_tLastTimeChecked = tNow;
+ }
+
+ private static void SetDisplayPlayModeViewType(EditorDisplayFullscreenSetting setting, Type playModeViewType)
+ {
+ setting.playModeViewSettings = CreatePlayModeViewSettingsForType(playModeViewType);
+ setting.viewWindowTitle = GetWindowTitle(playModeViewType);
+ }
+
+ private void HandleDisplayProfileChange()
+ {
+ // When hardware display configuration changes,
+ // always go back to default profile.
+ SetCurrentProfile(0);
+ RefreshDisplayStateWithCurrentProfile();
+ }
+
+ private void UpdateDisplayNamesAndIds()
+ {
+ m_numberOfConnectedDisplays = EditorDisplayUtility.GetNumberOfConnectedDisplays();
+ m_displayNames = new string[m_numberOfConnectedDisplays];
+ m_displayIds = new int[m_numberOfConnectedDisplays];
+ m_mainDisplayId = EditorDisplayUtility.GetMainDisplayId();
+
+ for (var i = 0; i < m_numberOfConnectedDisplays; ++i)
+ {
+ m_displayIds[i] = EditorDisplayUtility.GetDisplayId(i);
+ m_displayNames[i] = EditorDisplayUtility.GetDisplayName(i);
+ }
+
+ if (m_profiles != null && m_profiles.Count > 1)
+ {
+ var displayAPIProfile = m_profiles[1];
+
+ for (var i = 0; i < m_numberOfConnectedDisplays; ++i)
+ {
+ if (displayAPIProfile.Settings.Count <= i)
+ {
+ displayAPIProfile.AddEditorDisplayFullscreenSetting(CreateDefaultEditorDisplayFullscreenSetting(i));
+ }
+ else
+ {
+ displayAPIProfile.Settings[i].displayId = m_displayIds[i];
+ }
+ }
+ }
+ }
+
+ private void GetInstalledBuildTargetData()
+ {
+ var buildTargetData = EnumDataUtility.GetCachedEnumData(typeof(BuildTarget));
+ var installedBuildTargetCount =
+ (from BuildTarget target in buildTargetData.values
+ let @group = BuildPipeline.GetBuildTargetGroup(target)
+ where BuildPipeline.IsBuildTargetSupported(@group, target) select target).Count();
+
+ m_buildTargetData = new EnumData
+ {
+ values = new Enum[installedBuildTargetCount],
+ displayNames = new string[installedBuildTargetCount],
+ tooltip = new string[installedBuildTargetCount],
+ flagValues = new int[installedBuildTargetCount],
+ flags = buildTargetData.flags,
+ serializable = buildTargetData.serializable,
+ underlyingType = buildTargetData.underlyingType,
+ unsigned = buildTargetData.unsigned
+ };
+
+ for (int i = 0, j = 0; i < buildTargetData.values.Length; ++i)
+ {
+ var target = (BuildTarget) buildTargetData.values[i];
+ var group = BuildPipeline.GetBuildTargetGroup(target);
+ if (BuildPipeline.IsBuildTargetSupported(group, target))
+ {
+ m_buildTargetData.values[j] = buildTargetData.values[i];
+ m_buildTargetData.displayNames[j] = buildTargetData.displayNames[i];
+ m_buildTargetData.tooltip[j] = buildTargetData.tooltip[i];
+ m_buildTargetData.flagValues[j] = buildTargetData.flagValues[i];
+ ++j;
+ }
+ }
+ }
+
+ private EditorDisplayFullscreenSetting GetSettingForDisplay(int displayIndex)
+ {
+ if (m_profiles == null)
+ {
+ InitializeProfile();
+ }
+
+ var displayId = EditorDisplayUtility.GetDisplayId(displayIndex);
+ if (DisplayAPIMode == DisplayAPIControlMode.FromEditor &&
+ displayId == EditorDisplayUtility.GetMainDisplayId())
+ {
+ return m_mainDisplaySetting;
+ }
+
+ var s = m_profiles[m_selectedProfileIndex].GetEditorDisplayFullscreenSetting(displayId);
+ if (s == null || !s.enabled)
+ {
+ return m_defaultSetting;
+ }
+
+ return s;
+ }
+
+ private EditorDisplayFullscreenSetting GetSettingForDisplayById(int displayId)
+ {
+ return GetSettingForDisplay(GetDisplayIndexFromId(displayId));
+ }
+
+ private int GetDisplayIndexFromId(int displayId)
+ {
+ var nDisplays = EditorDisplayUtility.GetNumberOfConnectedDisplays();
+ for (var i = 0; i < nDisplays; ++i)
+ {
+ if (displayId == EditorDisplayUtility.GetDisplayId(i))
+ {
+ return i;
+ }
+ }
+ return 0;
+ }
+
+ private Vector2 GetDisplayRenderSizeFromId(int displayId)
+ {
+ int idx = GetDisplayIndexFromId(displayId);
+ int width = EditorDisplayUtility.GetDisplayWidth(idx);
+ int height = EditorDisplayUtility.GetDisplayHeight(idx);
+ Vector2 size = new Vector2(width, height);
+ return size;
+ }
+
+ private void RefreshDisplayStateWithCurrentProfile()
+ {
+ ApplySettings(m_mainDisplaySetting.displayId, m_mainDisplaySetting);
+ }
+
+ private static ContainerWindow FindContainerWindow(int displayIndex)
+ {
+ var windows = ContainerWindow.windows;
+
+ return windows.FirstOrDefault(w =>
+ w.m_IsFullscreenContainer &&
+ w.m_DisplayIndex == displayIndex);
+ }
+
+ internal static void ClearAllFullscreenWindows()
+ {
+ var windows = ContainerWindow.windows;
+ foreach (var w in windows)
+ {
+ if (w.m_IsFullscreenContainer)
+ {
+ w.Close();
+ }
+ }
+ }
+
+ internal static void BeginFullscreen(int displayIndex, int targetWidth, int targetHeight)
+ {
+ var containerWindow = FindContainerWindow(displayIndex);
+ if (containerWindow == null)
+ {
+ instance.BeginFullScreen(displayIndex, instance.GetSettingForDisplay(displayIndex), targetWidth, targetHeight);
+ }
+ }
+
+ [RequiredByNativeCode]
+ internal static void EndFullscreen(int displayIndex, bool closeWindow)
+ {
+ ContainerWindow containerWindow = null;
+ if (closeWindow)
+ {
+ containerWindow = FindContainerWindow(displayIndex);
+ }
+ instance.EndFullScreen(displayIndex, containerWindow);
+
+ if (displayIndex == 0)
+ {
+ instance.SetFullscreenMainDisplay(false, false);
+ }
+ }
+
+ private void ApplySettings(int displayIndex, EditorDisplayFullscreenSetting setting)
+ {
+ var containerWindow = FindContainerWindow(displayIndex);
+
+ if (containerWindow == null &&
+ (setting.mode == EditorDisplayFullscreenSetting.Mode.AlwaysFullscreen ||
+ (setting.mode == EditorDisplayFullscreenSetting.Mode.FullscreenOnPlaymode && instance.isPlaying)))
+ {
+ BeginFullScreen(displayIndex, setting);
+ return;
+ }
+
+ if (setting.mode == EditorDisplayFullscreenSetting.Mode.DoNothing ||
+ (setting.mode == EditorDisplayFullscreenSetting.Mode.FullscreenOnPlaymode && !instance.isPlaying))
+ {
+ EndFullScreen(displayIndex, containerWindow);
+ return;
+ }
+
+ if (containerWindow != null)
+ {
+ var hostView = (HostView)containerWindow.rootView;
+ var playModeView = (PlayModeView)hostView.actualView;
+
+ if (playModeView != null)
+ {
+ playModeView.ApplyEditorDisplayFullscreenSetting(setting.playModeViewSettings);
+ }
+ }
+ }
+
+ private void BeginFullScreen(int displayIndex, EditorDisplayFullscreenSetting setting, int targetWidth = 0, int targetHeight = 0)
+ {
+ var viewType = typeof(GameView);
+ if (setting.playModeViewSettings == null)
+ {
+ SetDisplayPlayModeViewType(setting, typeof(GameView));
+ }
+
+ var attributes = setting.playModeViewSettings.GetType().GetCustomAttributes(typeof(FullscreenSettingsForAttribute), false);
+ if (attributes.Length > 0)
+ {
+ var proposedType = ((FullscreenSettingsForAttribute) attributes[0]).AssignedType;
+ if (typeof(PlayModeView).IsAssignableFrom(proposedType))
+ {
+ viewType = proposedType;
+ }
+ else
+ {
+ Debug.LogError($"Type assigned for FullscreenSettingsFor is not a subclass of PlayModeView. PlayModeViewSettings={setting.playModeViewSettings.GetType()}, AssignedType{proposedType}");
+ }
+ }
+
+ var playModeView = ScriptableObject.CreateInstance(viewType) as PlayModeView;
+
+ // Now create a new hostView and container window (popup style -> no borders) and maximize it
+ var hostView = ScriptableObject.CreateInstance();
+ hostView.name = $"HostView {setting.displayName}";
+ hostView.actualView = playModeView;
+ playModeView.m_Parent = hostView;
+ playModeView.m_Parent.SetAsStartView();
+ playModeView.m_Parent.SetAsLastPlayModeView();
+ playModeView.isFullscreen = true;
+
+ var containerWindow = ScriptableObject.CreateInstance();
+ containerWindow.name = $"Unity Fullscreen Window {setting.displayName}";
+ containerWindow.m_DontSaveToLayout = true;
+ containerWindow.m_IsFullscreenContainer = true;
+ containerWindow.m_DisplayIndex = displayIndex;
+
+ // this ensures the fullscreen game view is shown on the same screen has the normal GameView is currently on
+ containerWindow.rootView = hostView;
+
+ playModeView.wantsMouseMove = true;
+ playModeView.MakeParentsSettingsMatchMe();
+
+ playModeView.ApplyEditorDisplayFullscreenSetting(setting.playModeViewSettings);
+ if (targetWidth != 0 && targetHeight != 0)
+ {
+ playModeView.targetSize = new Vector2(targetWidth, targetHeight);
+ }
+ containerWindow.Show(ShowMode.Fullscreen, false, true, true, displayIndex);
+ containerWindow.ToggleFullscreen(displayIndex);
+ hostView.Focus();
+
+ SuppressViewsFromRendering(containerWindow.GetDisplayId(), true);
+
+ if (instance.m_showNotificationOnFullscreen && setting == instance.m_mainDisplaySetting)
+ {
+ ShowFullscreenNotification(playModeView);
+ }
+ if (instance.m_showToolbarOnFullscreen && setting == instance.m_mainDisplaySetting)
+ {
+ SetShowToolbarOnMainDisplay(true);
+ }
+ m_FullscreenContainerWindows.Add(containerWindow);
+ }
+
+ private void EndFullScreen(int displayIndex, ContainerWindow w)
+ {
+ SuppressViewsFromRendering(EditorDisplayUtility.GetDisplayId(displayIndex), false);
+
+ if (w != null)
+ {
+ w.Close();
+ m_FullscreenContainerWindows.Remove(w);
+ }
+ }
+
+ internal static List GetFullscreenContainersForDisplayIndex(int displayIndex)
+ {
+ List containers = new List();
+ foreach (var cw in instance.m_FullscreenContainerWindows)
+ {
+ if (cw.m_DisplayIndex == displayIndex)
+ {
+ containers.Add(cw);
+ }
+ }
+
+ return containers;
+ }
+
+ private bool IsGoingFullscreenOnPlaymode(int displayId)
+ {
+ var setting = GetSettingForDisplayById(displayId);
+ return setting.mode != EditorDisplayFullscreenSetting.Mode.DoNothing;
+ }
+
+ private static void ShowFullscreenNotification(PlayModeView playView)
+ {
+ var binding = ShortcutManager.instance.GetShortcutBinding(kFullscreenToggle);
+
+ playView.ShowNotification(EditorGUIUtility.TextContentWithIcon(string.Format(Styles.disableFullscreenMainDisplayFormatContent.text, binding), "FullscreenNotification"));
+ playView.Repaint();
+ }
+
+ private void SuppressViewsFromRendering(int displayId, bool suppress)
+ {
+ var playModeViews = Resources.FindObjectsOfTypeAll(typeof(PlayModeView));
+ foreach (PlayModeView playModeView in playModeViews)
+ {
+ if (playModeView.m_Parent == null ||
+ playModeView.m_Parent.window == null ||
+ playModeView.m_Parent.window.m_IsFullscreenContainer)
+ {
+ // The fullscreen window should never suppress rendering.
+ playModeView.suppressRenderingForFullscreen = false;
+ continue;
+ }
+
+ var windowDisplayId = playModeView.m_Parent.window.GetDisplayId();
+ if (windowDisplayId == displayId)
+ {
+ // This play mode view is rendering on the same display we're going to fullscreen on. Always suppress.
+ playModeView.suppressRenderingForFullscreen = suppress;
+ continue;
+ }
+
+ if (playModeView.enterPlayModeBehavior == PlayModeView.EnterPlayModeBehavior.PlayFullscreen)
+ {
+ // This play mode view is going to spawn another fullscreen view. We should suppress rendering
+ // from this game view so as to not duplicate/double our number of rendered views.
+ playModeView.suppressRenderingForFullscreen = suppress;
+ continue;
+ }
+ }
+ }
+
+ private static void Save()
+ {
+ instance.Save(true);
+ }
+
+ private const string kBaseMenuPath = "Window/Displays";
+ private const string kFullscreenOnPlayMenu = kBaseMenuPath + "/Toggle Fullscreen";
+ private const string kShowToolbarOnFullscreenMenu = kBaseMenuPath + "/Show Toolbar on Fullscreen";
+ private const string kProfilePath = kBaseMenuPath + "/Profiles/";
+ public const string kFullscreenToggle = "Window/Fullscreen Game View";
+
+ void ToggleFullscreen()
+ {
+ var playModeViews = Resources.FindObjectsOfTypeAll(typeof(PlayModeView));
+ foreach (PlayModeView playModeView in playModeViews)
+ {
+ if (playModeView.m_Parent == null || playModeView.m_Parent.window == null)
+ continue;
+
+ if (playModeView.enterPlayModeBehavior != PlayModeView.EnterPlayModeBehavior.PlayFullscreen)
+ continue; // Do nothing with non-fullscreen game views.
+
+ int displayIdx = playModeView.fullscreenMonitorIdx;
+ if (m_DisplaySettings.ContainsKey(displayIdx))
+ {
+ var displaySetting = m_DisplaySettings[displayIdx];
+
+ if (displaySetting.mode == EditorDisplayFullscreenSetting.Mode.AlwaysFullscreen ||
+ displaySetting.mode == EditorDisplayFullscreenSetting.Mode.FullscreenOnPlaymode)
+ {
+ displaySetting.mode = EditorDisplayFullscreenSetting.Mode.DoNothing;
+ }
+ else
+ {
+ displaySetting.mode = EditorDisplayFullscreenSetting.Mode.AlwaysFullscreen;
+ }
+ instance.ApplySettings(displayIdx, displaySetting);
+ }
+ }
+ }
+
+ void HandleToggleFullscreenKeyShortcut()
+ {
+ if (!Application.isPlaying || m_DisplaySettings == null)
+ return;
+
+ var evt = Event.current;
+ var binding = ShortcutManager.instance.GetShortcutBinding("Window/Fullscreen Game View");
+ var keys = binding.keyCombinationSequence;
+
+ if (evt.type != EventType.KeyUp)
+ return;
+
+ if (keys.Where(x => x.keyCode == evt.keyCode).Count() <= 0)
+ return;
+
+ // Detect if the right key combinaison is active or not.
+ var shiftNecessary = keys.Where(x => (x.modifiers & ShortcutModifiers.Shift) == ShortcutModifiers.Shift).Count() > 0;
+ var containShift = (evt.modifiers & EventModifiers.Shift) == EventModifiers.Shift;
+
+ if (shiftNecessary && !containShift)
+ return;
+
+ var altNecessary = keys.Where(x => (x.modifiers & ShortcutModifiers.Alt) == ShortcutModifiers.Alt).Count() > 0;
+ var containAlt = (evt.modifiers & EventModifiers.Alt) == EventModifiers.Alt;
+
+ if (altNecessary && !containAlt)
+ return;
+
+ var ctrlNecessary = keys.Where(x => (x.modifiers & ShortcutModifiers.Control) == ShortcutModifiers.Control).Count() > 0;
+ var containCtrl = (evt.modifiers & EventModifiers.Control) == EventModifiers.Control;
+
+ if (ctrlNecessary && !containCtrl)
+ return;
+
+ ToggleFullscreen();
+ evt.Use();
+ }
+
+
+ [ClutchShortcutAttribute(kFullscreenToggle, KeyCode.F7, ShortcutModifiers.Shift | ShortcutModifiers.Control)]
+ internal static void FullscreenKeyHandler(ShortcutArguments args)
+ {
+ // The CTRL + SHIFT + F7 event doesn't work when a Game View is focused.
+ // It's a current limitation by the Shortcut Manager. Instead the kFullscreenToggle
+ // shortcut is handled by HandleToggleFullscreenKeyShortcut function which is a global
+ // event handler.
+ }
+
+ static EditorFullscreenController()
+ {
+ EditorApplication.update -= DelayReloadWindowDisplayMenu;
+ EditorApplication.update += DelayReloadWindowDisplayMenu;
+ }
+
+ private static void DelayReloadWindowDisplayMenu()
+ {
+ EditorApplication.update -= DelayReloadWindowDisplayMenu;
+ instance.ReloadWindowDisplayMenu();
+ EditorUtility.Internal_UpdateAllMenus();
+ }
+
+ internal void ReloadWindowDisplayMenu()
+ {
+ Menu.RemoveMenuItem(kBaseMenuPath);
+
+ var displayMenuItemPriority = 200;
+
+ Menu.AddMenuItem(kFullscreenOnPlayMenu, "", isFullscreenOnPlay, displayMenuItemPriority++, ToggleFullscreenMainDisplay, null);
+ Menu.AddMenuItem(kShowToolbarOnFullscreenMenu, "", isToolbarEnabledOnFullscreen, displayMenuItemPriority++, ToggleShowToolbarOnMainDisplay, null);
+
+ Menu.AddSeparator(kBaseMenuPath, displayMenuItemPriority++);
+
+ displayMenuItemPriority += 500;
+
+ for (var i = 0; i < m_profiles.Count; ++i)
+ {
+ var index = i;
+ var profile = m_profiles[i];
+
+ if (i == 0 || profile.Target == EditorUserBuildSettings.activeBuildTarget)
+ {
+ Menu.AddMenuItem(kProfilePath + profile.Name, "", m_selectedProfileIndex == i, displayMenuItemPriority++,
+ () => { SetCurrentProfile(index); }, () => !EditorApplication.isPlaying);
+ }
+ }
+ }
+
+ private static void SetCurrentProfile(int index)
+ {
+ if (instance.m_profiles.Count <= index)
+ {
+ return;
+ }
+
+ if (instance.m_selectedProfileIndex != index)
+ {
+ var previousMenuPath = kProfilePath + instance.m_profiles[instance.m_selectedProfileIndex].Name;
+ var currentMenuPath = kProfilePath + instance.m_profiles[index].Name;
+ Menu.SetChecked(previousMenuPath, false);
+ Menu.SetChecked(currentMenuPath, true);
+
+ instance.m_selectedProfileIndex = index;
+ }
+
+ instance.RefreshDisplayStateWithCurrentProfile();
+ }
+
+ private void ToggleFullscreenMainDisplay()
+ {
+ SetFullscreenMainDisplay(!isFullscreenOnPlay);
+ }
+
+ private void SetFullscreenMainDisplay(bool enabled, bool refresh = true)
+ {
+ if (isFullscreenOnPlay == enabled)
+ return;
+
+ m_mainDisplaySetting.mode = enabled
+ ? EditorDisplayFullscreenSetting.Mode.FullscreenOnPlaymode
+ : EditorDisplayFullscreenSetting.Mode.DoNothing;
+
+ Menu.SetChecked(kFullscreenOnPlayMenu, enabled);
+ if (refresh)
+ {
+ //RefreshDisplayStateWithCurrentProfile();
+ }
+ Save();
+ }
+
+ private void SetFullscreenDisplayId(int displayId)
+ {
+ m_mainDisplaySetting.displayId = displayId;
+ }
+
+ private void ToggleShowToolbarOnMainDisplay()
+ {
+ SetShowToolbarOnMainDisplay(!isToolbarEnabledOnFullscreen);
+ }
+
+ private void SetShowToolbarOnMainDisplay(bool enabled)
+ {
+ if (!(m_mainDisplaySetting.playModeViewSettings is GameViewFullscreenSettings gameViewSetting) ||
+ gameViewSetting.ShowToolbar == enabled)
+ {
+ return;
+ }
+
+ gameViewSetting.ShowToolbar = enabled;
+ Menu.SetChecked(kShowToolbarOnFullscreenMenu, enabled);
+ RefreshDisplayStateWithCurrentProfile();
+ Save();
+ }
+
+ private static string GetWindowTitle(Type type)
+ {
+ var attributes = type.GetCustomAttributes(typeof(EditorWindowTitleAttribute), true);
+ return attributes.Length > 0 ? ((EditorWindowTitleAttribute)attributes[0]).title : type.Name;
+ }
+
+ private Dictionary GetAvailableWindowTypes()
+ {
+ return m_AvailableWindowTypes ?? (m_AvailableWindowTypes = TypeCache.GetTypesDerivedFrom(typeof(PlayModeView)).OrderBy(GetWindowTitle).ToDictionary(t => t, GetWindowTitle));
+ }
+
+ private class Styles
+ {
+ public static readonly GUIContent sortDisplayOrderContent = EditorGUIUtility.TrTextContent("Sort Display Order (*Windows Only)");
+ public static readonly GUIContent showNotificationContent = EditorGUIUtility.TrTextContent("Show notification when entering fullscreen");
+ public static readonly GUIContent showToolbarOnFullscreenContent = EditorGUIUtility.TrTextContent("Show game view toolbar on fullscreen");
+
+ public static readonly GUIContent[] modePopupDisplayTexts =
+ {
+ EditorGUIUtility.TrTextContent("Do nothing"),
+ EditorGUIUtility.TrTextContent("Fullscreen on Playmode"),
+ EditorGUIUtility.TrTextContent("Always Fullscreen"),
+ };
+ public static readonly GUIContent addProfileContent = EditorGUIUtility.TrTextContent("Add Profile");
+ public static readonly GUIContent nameContent = EditorGUIUtility.TrTextContent("Name");
+ public static readonly GUIContent platformContent = EditorGUIUtility.TrTextContent("Platform");
+ public static readonly GUIContent disconnectedDisplaysContent = EditorGUIUtility.TrTextContent("Disconnected Displays");
+
+
+ public static readonly GUIContent mainDisplaySettingHelpTextContent = EditorGUIUtility.TrTextContent("Settings for main display is done from GameView.");
+ public static readonly GUIContent viewTypeContent = EditorGUIUtility.TrTextContent("View Type");
+ public static readonly GUIContent vsyncContent = EditorGUIUtility.TrTextContent("VSync");
+ public static readonly GUIContent gizmosContent = EditorGUIUtility.TrTextContent("Gizmos");
+ public static readonly GUIContent toolbarContent = EditorGUIUtility.TrTextContent("Toolbar");
+ public static readonly GUIContent statsContent = EditorGUIUtility.TrTextContent("Stats");
+ public static readonly GUIContent displaySettingsLabelContent = EditorGUIUtility.TrTextContent("Display Settings");
+
+ public static readonly GUIContent mainDisplayFormatContent = EditorGUIUtility.TrTextContent("{0} (Main Display)");
+ public static readonly GUIContent disconnectedDisplayFormatContent = EditorGUIUtility.TrTextContent("{0} (Disconnected)");
+ public static readonly GUIContent disableFullscreenMainDisplayFormatContent = EditorGUIUtility.TrTextContent("Press {0} to exit fullscreen.");
+
+ public static readonly GUIContent displayAPIMappingContent = EditorGUIUtility.TrTextContent("Standalone simulation monitor mapping");
+ }
+
+ private static void DrawPreferenceGUI(string searchContext)
+ {
+ if (instance.m_profiles == null)
+ {
+ instance.InitializeProfile();
+ }
+ GUILayout.Space(12);
+
+ EditorGUI.BeginChangeCheck();
+ instance.m_isSortDisplayOrder = EditorGUILayout.ToggleLeft(Styles.sortDisplayOrderContent, instance.m_isSortDisplayOrder);
+ if (EditorGUI.EndChangeCheck())
+ {
+ EditorDisplayUtility.SetSortDisplayOrder(instance.m_isSortDisplayOrder);
+ Save();
+ }
+
+ EditorGUI.BeginChangeCheck();
+ instance.m_showNotificationOnFullscreen = EditorGUILayout.ToggleLeft(Styles.showNotificationContent, instance.m_showNotificationOnFullscreen);
+ instance.m_showToolbarOnFullscreen = EditorGUILayout.ToggleLeft(Styles.showToolbarOnFullscreenContent, instance.m_showToolbarOnFullscreen);
+ if (EditorGUI.EndChangeCheck())
+ {
+ Save();
+ }
+
+ GUILayout.Space(12);
+
+
+ DrawDisplayAPIProfile();
+ //DrawProfilePreferencesGUI(); //TODO Enables GUI for display profiles.
+ }
+
+ private static void DrawProfilePreferencesGUI()
+ {
+ EditorDisplaySettingsProfile removingProfile = null;
+ EditorGUI.BeginChangeCheck();
+
+ // m_profiles[0] is always Default Profile.
+ // m_profiles[1] is always DisplayAPI Profile.
+ for(var i = 2; i < instance.m_profiles.Count; ++i)
+ {
+ if (!DrawPreferenceProfileGUI(instance.m_profiles[i]))
+ {
+ removingProfile = instance.m_profiles[i];
+ break;
+ }
+ GUILayout.Space(12);
+ }
+
+ if (removingProfile != null)
+ {
+ instance.m_profiles.Remove(removingProfile);
+ }
+
+ GUILayout.Space(12);
+
+ if (GUILayout.Button(Styles.addProfileContent, GUILayout.Width(150)))
+ {
+ var newProfile = new EditorDisplaySettingsProfile(instance.GetAppropriateNewProfileName());
+ instance.m_profiles.Add(newProfile);
+ }
+
+ GUILayout.Space(12);
+
+ if (EditorGUI.EndChangeCheck())
+ {
+ Save();
+ }
+ }
+
+ private string GetAppropriateNewProfileName()
+ {
+ const string kDefaultNewProfileName = "New Profile";
+ if (m_profiles.Find(p => p.Name == kDefaultNewProfileName) == null)
+ {
+ return kDefaultNewProfileName;
+ }
+
+ for (var i = 1;; ++i)
+ {
+ var name = $"New Profile ({i})";
+ if (m_profiles.Find(p => p.Name == name) == null)
+ {
+ return name;
+ }
+ }
+ }
+
+ private static void DrawDisplayAPIProfile()
+ {
+ if (!ModuleManager.ShouldShowMultiDisplayOption())
+ {
+ return;
+ }
+
+ using (new GUILayout.VerticalScope(EditorStyles.frameBox))
+ {
+ GUILayout.Label(Styles.displayAPIMappingContent, EditorStyles.boldLabel);
+ GUILayout.Space(12);
+
+ var displayNames = GetDisplayNamesForBuildTarget(EditorUserBuildSettings.activeBuildTarget);
+
+ for (var i = 0; i < instance.m_numberOfConnectedDisplays; ++i)
+ {
+ EditorGUILayout.LabelField(displayNames[i], GUIContent.Temp(instance.m_displayNames[i]));
+ }
+ }
+ GUILayout.Space(12);
+ }
+
+ private static bool DrawPreferenceProfileGUI(EditorDisplaySettingsProfile profile)
+ {
+ using (new GUILayout.VerticalScope(EditorStyles.frameBox))
+ {
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.Label(profile.Name, EditorStyles.boldLabel);
+ if (GUILayout.Button("-", GUILayout.Width(20)))
+ {
+ return false;
+ }
+ }
+
+ GUILayout.Space(8);
+
+ var newName = EditorGUILayout.TextField(Styles.nameContent, profile.Name, GUILayout.Width(400));
+ if (newName != profile.Name)
+ {
+ profile.Name = newName;
+ }
+
+ var selectedIndex = Array.IndexOf(instance.m_buildTargetData.values, profile.Target);
+ var newSelectedIndex = EditorGUILayout.Popup(Styles.platformContent, selectedIndex, instance.m_buildTargetData.displayNames, GUILayout.Width(400));
+ if (selectedIndex != newSelectedIndex)
+ {
+ profile.Target = (BuildTarget) instance.m_buildTargetData.values[newSelectedIndex];
+ }
+
+ // if selecting unsupported build target, then do not let settings to edit
+ if (selectedIndex < 0)
+ {
+ return true;
+ }
+
+ GUILayout.Space(8);
+
+ for (var i = 0; i < instance.m_displayIds.Length; ++i)
+ {
+ var id = instance.m_displayIds[i];
+ var isMainScreen = id == instance.m_mainDisplayId;
+
+ var setting = profile.GetEditorDisplayFullscreenSetting(id);
+ if (setting == null)
+ {
+ setting = new EditorDisplayFullscreenSetting(id, instance.m_displayNames[i]);
+ profile.AddEditorDisplayFullscreenSetting(setting);
+ }
+
+ DrawPlayModeViewSettingsGUI(profile.Target, setting, isMainScreen, false);
+ GUILayout.Space(8);
+ }
+
+ bool drawDisconnectedSettingsHeader = false;
+ // For Display Settings which is currently disconnected
+ EditorDisplayFullscreenSetting removingSetting = null;
+
+ foreach (var setting in profile.Settings)
+ {
+ if (instance.m_displayIds.Contains(setting.displayId))
+ {
+ continue;
+ }
+
+ if (!drawDisconnectedSettingsHeader)
+ {
+ GUILayout.Space(12);
+ GUILayout.Label(Styles.disconnectedDisplaysContent);
+ drawDisconnectedSettingsHeader = true;
+ }
+
+ if (!DrawPlayModeViewSettingsGUI(profile.Target, setting, false, true))
+ {
+ removingSetting = setting;
+ }
+ GUILayout.Space(8);
+ }
+
+ if (removingSetting != null)
+ {
+ profile.RemoveEditorDisplayFullscreenSetting(removingSetting);
+ }
+
+ GUILayout.Space(12);
+ }
+
+ return true;
+ }
+
+ private static bool DrawPlayModeViewSettingsGUI(BuildTarget target, EditorDisplayFullscreenSetting setting, bool isMainScreen, bool isDisconnected)
+ {
+ if (isMainScreen)
+ {
+ GUILayout.Label(string.Format(Styles.mainDisplayFormatContent.text, setting.displayName));
+ EditorGUILayout.HelpBox(Styles.mainDisplaySettingHelpTextContent);
+ return true;
+ }
+
+ var label = isDisconnected ? string.Format(Styles.disconnectedDisplayFormatContent.text, setting.displayName) : setting.displayName;
+
+ using (new GUILayout.HorizontalScope())
+ {
+ setting.enabled = EditorGUILayout.ToggleLeft(label, setting.enabled);
+ if (isDisconnected)
+ {
+ if (GUILayout.Button("-", GUILayout.Width(20)))
+ {
+ return false;
+ }
+ }
+ }
+
+ if (!setting.enabled)
+ {
+ return true;
+ }
+
+ using (new GUILayout.VerticalScope(EditorStyles.frameBox))
+ {
+ var availableTypes = instance.GetAvailableWindowTypes();
+ if (availableTypes.Count > 1)
+ {
+ var viewTitleNames = availableTypes.Values.ToList();
+ var viewIndex = viewTitleNames.IndexOf(setting.viewWindowTitle);
+ var newViewIndex = EditorGUILayout.Popup(
+ Styles.viewTypeContent, viewTitleNames.IndexOf(setting.viewWindowTitle),
+ viewTitleNames.ToArray(), GUILayout.Width(400));
+ if (newViewIndex != viewIndex)
+ {
+ setting.viewWindowTitle = viewTitleNames[newViewIndex];
+ setting.playModeViewSettings =
+ CreatePlayModeViewSettingsForType(availableTypes.Keys.ToList()[newViewIndex]);
+ }
+ }
+ else {
+ if (string.IsNullOrEmpty(setting.viewWindowTitle))
+ {
+ var typeNames = availableTypes.Values.ToList();
+ setting.viewWindowTitle = typeNames[0];
+ setting.playModeViewSettings =
+ CreatePlayModeViewSettingsForType(availableTypes.Keys.ToList()[0]);
+ }
+ EditorGUILayout.LabelField(Styles.viewTypeContent, new GUIContent(setting.viewWindowTitle), GUILayout.Width(300));
+ }
+ EditorGUILayout.Space();
+
+ setting.mode = (EditorDisplayFullscreenSetting.Mode)EditorGUILayout.Popup((int)setting.mode, Styles.modePopupDisplayTexts, GUILayout.Width(200));
+ if (setting.playModeViewSettings != null)
+ {
+ GUILayout.Space(4);
+ setting.playModeViewSettings.OnPreferenceGUI(target);
+ }
+ }
+
+ return true;
+ }
+
+ private static IPlayModeViewFullscreenSettings CreatePlayModeViewSettingsForType(Type playModeViewType)
+ {
+ foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) {
+ var settingsTypes = assembly.GetTypes()
+ .Where(t => !t.IsInterface)
+ .Where(t => typeof(IPlayModeViewFullscreenSettings).IsAssignableFrom(t));
+
+ foreach (var settingsType in settingsTypes)
+ {
+ var attributes = settingsType.GetCustomAttributes(typeof(FullscreenSettingsForAttribute), false);
+ if (attributes.Length > 0 && ((FullscreenSettingsForAttribute)attributes[0]).AssignedType == playModeViewType)
+ {
+ return (IPlayModeViewFullscreenSettings) settingsType.Assembly.CreateInstance(settingsType.FullName);
+ }
+ }
+ }
+ return null;
+ }
+
+ internal static GUIContent[] GetDisplayNamesForBuildTarget(BuildTarget buildTarget)
+ {
+ var platformDisplayNames = Modules.ModuleManager.GetDisplayNames(buildTarget.ToString());
+ return platformDisplayNames ?? DisplayUtility.GetGenericDisplayNames();
+ }
+
+
+ [SettingsProvider]
+ internal static SettingsProvider CreateEditorDisplaySettingUserPreference()
+ {
+ var provider = new SettingsProvider("Preferences/Display Settings", SettingsScope.User)
+ {
+ label = Styles.displaySettingsLabelContent.text,
+ guiHandler = DrawPreferenceGUI,
+ activateHandler = (s, element) =>
+ {
+ instance.UpdateDisplayNamesAndIds();
+ },
+ deactivateHandler = () =>
+ {
+ instance.RefreshDisplayStateWithCurrentProfile();
+ instance.ReloadWindowDisplayMenu();
+ },
+ keywords = SettingsProvider.GetSearchKeywordsFromGUIContentProperties()
+ };
+ return provider;
+ }
+ }
+} // namespace
diff --git a/Editor/Mono/Display/FullscreenSettingsForAttribute.cs b/Editor/Mono/Display/FullscreenSettingsForAttribute.cs
new file mode 100644
index 0000000000..0672466a95
--- /dev/null
+++ b/Editor/Mono/Display/FullscreenSettingsForAttribute.cs
@@ -0,0 +1,21 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using System;
+using UnityEngine.Bindings;
+
+namespace UnityEditor
+{
+ [AttributeUsage(AttributeTargets.Class, Inherited = false)]
+ [VisibleToOtherModules]
+ internal class FullscreenSettingsForAttribute : Attribute
+ {
+ public FullscreenSettingsForAttribute(Type assignedType)
+ {
+ AssignedType = assignedType;
+ }
+
+ public Type AssignedType { get; set; }
+ }
+}
diff --git a/Editor/Mono/Display/GameViewFullscreenSettings.cs b/Editor/Mono/Display/GameViewFullscreenSettings.cs
new file mode 100644
index 0000000000..5998df7044
--- /dev/null
+++ b/Editor/Mono/Display/GameViewFullscreenSettings.cs
@@ -0,0 +1,81 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using System;
+using UnityEngine;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEditor.ShortcutManagement;
+
+namespace UnityEditor
+{
+ [Serializable]
+ [FullscreenSettingsFor(typeof(GameView))]
+ internal class GameViewFullscreenSettings : IPlayModeViewFullscreenSettings
+ {
+ [SerializeField] private int m_displayNumber;
+ [SerializeField] private bool m_showToolbar;
+ [SerializeField] private bool m_vsyncEnabled;
+ [SerializeField] private bool m_showStats;
+ [SerializeField] private bool m_showGizmos;
+ [SerializeField] private int m_selectedSizeIndex;
+
+ public int DisplayNumber
+ {
+ get => m_displayNumber;
+ set => m_displayNumber = value;
+ }
+
+ public bool ShowToolbar
+ {
+ get => m_showToolbar;
+ set => m_showToolbar = value;
+ }
+
+ public bool VsyncEnabled
+ {
+ get => m_vsyncEnabled;
+ set => m_vsyncEnabled = value;
+ }
+
+ public bool ShowStats
+ {
+ get => m_showStats;
+ set => m_showStats = value;
+ }
+
+ public bool ShowGizmos
+ {
+ get => m_showGizmos;
+ set => m_showGizmos = value;
+ }
+
+ public int SelectedSizeIndex
+ {
+ get => m_selectedSizeIndex;
+ set => m_selectedSizeIndex = value;
+ }
+
+ private class Styles
+ {
+ public static readonly GUIContent vsyncContent = EditorGUIUtility.TrTextContent("VSync");
+ public static readonly GUIContent gizmosContent = EditorGUIUtility.TrTextContent("Gizmos");
+ public static readonly GUIContent toolbarContent = EditorGUIUtility.TrTextContent("Toolbar");
+ public static readonly GUIContent statsContent = EditorGUIUtility.TrTextContent("Stats");
+ }
+
+ public void OnPreferenceGUI(BuildTarget target)
+ {
+ using (new GUILayout.HorizontalScope())
+ {
+ m_displayNumber = EditorGUILayout.Popup(m_displayNumber, EditorFullscreenController.GetDisplayNamesForBuildTarget(target), GUILayout.Width(80));
+ GUILayout.Space(12);
+ m_vsyncEnabled = EditorGUILayout.ToggleLeft(Styles.vsyncContent, m_vsyncEnabled, GUILayout.Width(60));
+ m_showToolbar = EditorGUILayout.ToggleLeft(Styles.toolbarContent, m_showToolbar, GUILayout.Width(70));
+ m_showGizmos = EditorGUILayout.ToggleLeft(Styles.gizmosContent, m_showGizmos, GUILayout.Width(60));
+ m_showStats = EditorGUILayout.ToggleLeft(Styles.statsContent, m_showStats, GUILayout.Width(60));
+ }
+ }
+ }
+} // namespace
diff --git a/Editor/Mono/Display/IPlayModeViewFullscreenSettings.cs b/Editor/Mono/Display/IPlayModeViewFullscreenSettings.cs
new file mode 100644
index 0000000000..bd7c04cd5b
--- /dev/null
+++ b/Editor/Mono/Display/IPlayModeViewFullscreenSettings.cs
@@ -0,0 +1,21 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using System;
+using UnityEngine;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEditor.ShortcutManagement;
+
+namespace UnityEditor
+{
+ internal interface IPlayModeViewFullscreenSettings
+ {
+ int DisplayNumber { get; }
+
+ bool VsyncEnabled { get; }
+
+ void OnPreferenceGUI(BuildTarget target);
+ }
+} // namespace
diff --git a/Editor/Mono/DynamicHints/DynamicHintUtility.cs b/Editor/Mono/DynamicHints/DynamicHintUtility.cs
index 592cd9bd36..ea53ae2a00 100644
--- a/Editor/Mono/DynamicHints/DynamicHintUtility.cs
+++ b/Editor/Mono/DynamicHints/DynamicHintUtility.cs
@@ -158,13 +158,17 @@ internal static void DrawHint(Rect hintTriggerRect, Vector2 mousePosition, Asset
if (!hintTriggerRect.Contains(mousePosition) || !GUIClip.visibleRect.Contains(mousePosition)) { return; }
string assetPath = AssetDatabase.GUIDToAssetPath(assetReference.guid);
- var asset = AssetDatabase.LoadAssetAtPath(assetPath);
- var tooltipGenerators = TypeCache.GetMethodsWithAttribute();
+ var assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
- ScriptableObject scriptableObjectAsset = asset as ScriptableObject;
- if (scriptableObjectAsset != null)
+ if (assetType == null) //this means the object or its base script has been deleted and is "Missing"
{
- DynamicHintContent hint = GetDynamicHintContentOf(tooltipGenerators, scriptableObjectAsset);
+ return;
+ }
+
+ var hintGenerators = TypeCache.GetMethodsWithAttribute();
+ if (assetType.IsSubclassOf(typeof(ScriptableObject)))
+ {
+ DynamicHintContent hint = GetDynamicHintContentOf(hintGenerators, assetType, assetPath);
if (hint != null)
{
DrawMouseTooltip(hint, hintTriggerRect);
@@ -172,7 +176,7 @@ internal static void DrawHint(Rect hintTriggerRect, Vector2 mousePosition, Asset
return;
}
- if (asset as GameObject != null)
+ if (assetType == typeof(GameObject))
{
/* GameObjects can have multiple components with custom tooltips
* so for now we'll just display the first one.
@@ -180,10 +184,22 @@ internal static void DrawHint(Rect hintTriggerRect, Vector2 mousePosition, Asset
* 1) Implement a "priority system" (like OrderedCallbacks)
* 2) Display one big tooltip made up with all elements from custom tooltip
*/
- GameObject assetAsGameObject = asset as GameObject;
- foreach (Component component in assetAsGameObject.GetComponents())
+
+ GameObject assetGameObject = (GetLoadedObjectFromInstanceID(assetReference.instanceID) as GameObject);
+ if (!assetGameObject)
{
- DynamicHintContent hint = GetDynamicHintContentOf(tooltipGenerators, component);
+ /* this seems to happen non-deterministically at project startup depending of what the user is hovering when the editor opens,
+ * or while the user is scrolling a list of objects and hovers one of them casually, even if the object hovered is actually a
+ * GameObject.
+ * */
+ return;
+ }
+
+ foreach (var component in assetGameObject.GetComponents())
+ {
+ if (component == null) { continue; } //this means its script has been deleted and is "Missing"
+
+ DynamicHintContent hint = GetDynamicHintContentOf(hintGenerators, component.GetType(), string.Empty, component);
if (hint == null) { continue; }
DrawMouseTooltip(hint, hintTriggerRect);
@@ -192,21 +208,19 @@ internal static void DrawHint(Rect hintTriggerRect, Vector2 mousePosition, Asset
}
}
- static DynamicHintContent GetDynamicHintContentOf(TypeCache.MethodCollection creatorHandlers, T target)
+ static DynamicHintContent GetDynamicHintContentOf(TypeCache.MethodCollection hintGenerators, Type targetType, string assetPath = "", Component targetAsComponent = null)
{
- if (target == null) //this means the script has been deleted and is "Missing"
+ foreach (var hintGenerator in hintGenerators)
{
- return null;
- }
-
- Type targetType = target.GetType();
- foreach (var creationHandler in creatorHandlers)
- {
- foreach (var attribute in creationHandler.GetCustomAttributes(typeof(DynamicHintGeneratorAttribute), false))
+ foreach (var attribute in hintGenerator.GetCustomAttributes(typeof(DynamicHintGeneratorAttribute), false))
{
if (targetType == (attribute as DynamicHintGeneratorAttribute).m_Type)
{
- return creationHandler.Invoke(null, new[] { (object)target }) as DynamicHintContent;
+ var target = targetAsComponent ? targetAsComponent : AssetDatabase.LoadMainAssetAtPath(assetPath);
+ if (target)
+ {
+ return hintGenerator.Invoke(null, new[] { (object)target }) as DynamicHintContent;
+ }
}
}
}
diff --git a/Editor/Mono/EditorUIService.cs b/Editor/Mono/EditorUIService.cs
index 20ac27451b..31f7ffc440 100644
--- a/Editor/Mono/EditorUIService.cs
+++ b/Editor/Mono/EditorUIService.cs
@@ -5,6 +5,7 @@
using System;
using UnityEditor.ShortcutManagement;
using UnityEditor.UIElements;
+using UnityEditorInternal;
using UnityEngine;
using UnityEngine.UIElements;
@@ -37,6 +38,13 @@ internal interface IEditorUIService
// UIToolkit
void AddDefaultEditorStyleSheets(VisualElement ve);
+ VisualElement CreateUnityEventItem();
+ void BindUnityEventItem(VisualElement item, UnityEventDrawer.PropertyData propertyData, Func createMenuCallback, Func formatSelectedValueCallback, Func getArgumentCallback);
+ BindableElement CreateObjectField();
+ void SetObjectField(VisualElement element, UnityEngine.Object value, Type type, string label);
+ ListView CreateListViewBinding(SerializedObject serializedObject);
+ VisualElement CreateDropdownField(string[] options, Func getSelectedChoiceCallback);
+ VisualElement CreatePropertyField(SerializedProperty serializedProperty, string label);
BindableElement CreateFloatField(string name, Func onValidateValue = null, bool isDelayed = false);
BindableElement CreateDoubleField(string name, Func onValidateValue = null, bool isDelayed = false);
BindableElement CreateIntField(string name, Func onValidateValue = null, bool isDelayed = false);
diff --git a/Editor/Mono/GI/Lightmapping.bindings.cs b/Editor/Mono/GI/Lightmapping.bindings.cs
index e28d12c845..1335da13b8 100644
--- a/Editor/Mono/GI/Lightmapping.bindings.cs
+++ b/Editor/Mono/GI/Lightmapping.bindings.cs
@@ -660,6 +660,10 @@ public static void BakeMultipleScenes(string[] paths)
[FreeFunction]
[NativeHeader("Editor/Src/GI/EditorHelpers.h")]
extern static internal bool GetGeometryHash([NotNull("NullExceptionObject")] Renderer renderer, out Hash128 geometryHash);
+
+ [FreeFunction]
+ [NativeHeader("Editor/Src/GI/ExtractInstances.h")]
+ extern static internal bool IsRendererValid([NotNull("NullExceptionObject")] Renderer renderer);
}
}
diff --git a/Editor/Mono/GUI/EditorApplicationLayout.cs b/Editor/Mono/GUI/EditorApplicationLayout.cs
index f125a3fc99..e284d0ee04 100644
--- a/Editor/Mono/GUI/EditorApplicationLayout.cs
+++ b/Editor/Mono/GUI/EditorApplicationLayout.cs
@@ -23,13 +23,12 @@ namespace UnityEditor
{
internal class EditorApplicationLayout
{
- static private PlayModeView m_PlayModeView = null;
static private bool m_MaximizePending = false;
-
+ static List m_PlayModeViewList = null;
static internal bool IsInitializingPlaymodeLayout()
{
- return m_PlayModeView != null;
+ return m_PlayModeViewList != null && m_PlayModeViewList.Count > 0;
}
static internal void SetPlaymodeLayout()
@@ -40,6 +39,22 @@ static internal void SetPlaymodeLayout()
static internal void SetStopmodeLayout()
{
+ if (m_PlayModeViewList != null && m_PlayModeViewList.Count > 0)
+ {
+ var monitorNames = EditorFullscreenController.GetConnectedDisplayNames();
+ foreach (var playModeView in m_PlayModeViewList)
+ {
+ if (playModeView.fullscreenMonitorIdx >= monitorNames.Length)
+ continue;
+
+ EditorFullscreenController.SetSettingsForCurrentDisplay(playModeView.fullscreenMonitorIdx);
+ EditorFullscreenController.OnExitPlaymode();
+ }
+
+ m_PlayModeViewList.Clear();
+ m_PlayModeViewList = null;
+ }
+
WindowLayout.ShowAppropriateViewOnEnterExitPlaymode(false);
Toolbar.RepaintToolbar();
}
@@ -50,53 +65,104 @@ static internal void SetPausemodeLayout()
SetStopmodeLayout();
}
+ static internal void InitializePlaymodeViewList()
+ {
+ if (m_PlayModeViewList == null)
+ {
+ m_PlayModeViewList = new List();
+ }
+ else
+ {
+ m_PlayModeViewList.Clear();
+ }
+ }
+
static internal void InitPlaymodeLayout()
{
- m_PlayModeView = WindowLayout.ShowAppropriateViewOnEnterExitPlaymode(true) as PlayModeView;
- if (m_PlayModeView == null)
- return;
+ InitializePlaymodeViewList();
+ WindowLayout.ShowAppropriateViewOnEnterExitPlaymodeList(true, out m_PlayModeViewList);
- if (m_PlayModeView.enterPlayModeBehavior == PlayModeView.EnterPlayModeBehavior.PlayMaximized)
+ var fullscreenDetected = false;
+ var monitorNames = EditorFullscreenController.GetConnectedDisplayNames();
+
+ foreach (var playModeView in m_PlayModeViewList)
{
- if (m_PlayModeView.m_Parent is DockArea dockArea)
- {
- m_MaximizePending = WindowLayout.MaximizePrepare(dockArea.actualView);
+ if (playModeView == null)
+ continue;
- var gameView = dockArea.actualView as GameView;
- if (gameView != null)
- m_PlayModeView.m_Parent.EnableVSync(gameView.vSyncEnabled);
+ if (playModeView.fullscreenMonitorIdx >= monitorNames.Length)
+ continue;
+
+ if (playModeView.enterPlayModeBehavior == PlayModeView.EnterPlayModeBehavior.PlayFullscreen)
+ {
+ EditorFullscreenController.SetSettingsForCurrentDisplay(playModeView.fullscreenMonitorIdx);
+ EditorFullscreenController.isFullscreenOnPlay = true;
+ EditorFullscreenController.fullscreenDisplayId = playModeView.fullscreenMonitorIdx;
+ EditorFullscreenController.isToolbarEnabledOnFullscreen = false;
+ EditorFullscreenController.targetDisplayID = playModeView.targetDisplay;
+
+ if (playModeView.m_Parent is DockArea dockArea && dockArea.actualView is GameView gv)
+ {
+ playModeView.m_Parent.EnableVSync(gv.vSyncEnabled);
+ EditorFullscreenController.enableVSync = gv.vSyncEnabled;
+ EditorFullscreenController.selectedSizeIndex = gv.selectedSizeIndex;
+ }
+ fullscreenDetected = true;
+ }
+ else if (!fullscreenDetected)
+ {
+ EditorFullscreenController.isFullscreenOnPlay = false;
}
- }
- // Mark this PlayModeView window as the start view so the backend
- // can set size and mouseoffset properly for this view
- m_PlayModeView.m_Parent.SetAsStartView();
- m_PlayModeView.m_Parent.SetAsLastPlayModeView();
+ if (playModeView.enterPlayModeBehavior == PlayModeView.EnterPlayModeBehavior.PlayMaximized)
+ {
+ if (playModeView.m_Parent is DockArea dockArea)
+ {
+ m_MaximizePending = WindowLayout.MaximizePrepare(dockArea.actualView);
+ var gv = dockArea.actualView as GameView;
+ if (gv != null)
+ {
+ playModeView.m_Parent.EnableVSync(gv.vSyncEnabled);
+ }
+ }
+ }
- //GameView should be actively focussed If Playmode is entered in maximized state - case 1252097
- if (m_PlayModeView.maximized)
- m_PlayModeView.m_Parent.Focus();
+ EditorFullscreenController.OnEnterPlaymode();
- Toolbar.RepaintToolbar();
+ if (!EditorFullscreenController.isFullscreenOnPlay)
+ {
+ playModeView.m_Parent.SetAsStartView();
+ playModeView.m_Parent.SetAsLastPlayModeView();
+
+ if (playModeView.maximized)
+ {
+ playModeView.m_Parent.Focus();
+ }
+
+ if (playModeView is IGameViewOnPlayMenuUser)
+ {
+ if (((IGameViewOnPlayMenuUser)playModeView).playFocused)
+ {
+ playModeView.m_Parent.Focus();
+ }
+ }
+ }
+ Toolbar.RepaintToolbar();
+ }
}
static internal void FinalizePlaymodeLayout()
{
- if (m_PlayModeView != null)
+ foreach (var playModeView in m_PlayModeViewList)
{
- if (m_MaximizePending)
- WindowLayout.MaximizePresent(m_PlayModeView);
+ if (playModeView != null && playModeView.enterPlayModeBehavior == PlayModeView.EnterPlayModeBehavior.PlayMaximized)
+ {
+ if (m_MaximizePending)
+ WindowLayout.MaximizePresent(playModeView);
- m_PlayModeView.m_Parent.ClearStartView();
+ playModeView.m_Parent.ClearStartView();
+ }
}
-
- Clear();
- }
-
- static private void Clear()
- {
- m_MaximizePending = false;
- m_PlayModeView = null;
}
}
} // namespace
diff --git a/Editor/Mono/GUI/FlexibleMenu/FlexibleMenu.cs b/Editor/Mono/GUI/FlexibleMenu/FlexibleMenu.cs
index 8cdfc53eb2..b9bcabc7f5 100644
--- a/Editor/Mono/GUI/FlexibleMenu/FlexibleMenu.cs
+++ b/Editor/Mono/GUI/FlexibleMenu/FlexibleMenu.cs
@@ -18,7 +18,7 @@ class Styles
IFlexibleMenuItemProvider m_ItemProvider;
FlexibleMenuModifyItemUI m_ModifyItemUI;
- readonly Action m_ItemClickedCallback;
+ Action m_ItemClickedCallback;
Vector2 m_ScrollPosition = Vector2.zero;
bool m_ShowAddNewPresetItem;
int m_ShowEditWindowForIndex = -1;
@@ -34,8 +34,19 @@ class Styles
public int selectedIndex { get; set; }
protected float minTextWidth { get { return m_MinTextWidth; } set { m_MinTextWidth = value; ClearCachedWidth(); } }
+ // Note: 'itemClickedCallback' arguments is clicked index, clicked item object
+ public FlexibleMenu(IFlexibleMenuItemProvider itemProvider, int selectionIndex, Action itemClickedCallback)
+ {
+ Init(itemProvider, selectionIndex, null, itemClickedCallback);
+ }
+
// itemClickedCallback arguments is clicked index, clicked item object
public FlexibleMenu(IFlexibleMenuItemProvider itemProvider, int selectionIndex, FlexibleMenuModifyItemUI modifyItemUi, Action itemClickedCallback)
+ {
+ Init(itemProvider, selectionIndex, modifyItemUi, itemClickedCallback);
+ }
+
+ void Init(IFlexibleMenuItemProvider itemProvider, int selectionIndex, FlexibleMenuModifyItemUI modifyItemUi, Action itemClickedCallback)
{
m_ItemProvider = itemProvider;
m_ModifyItemUI = modifyItemUi;
@@ -43,6 +54,9 @@ public FlexibleMenu(IFlexibleMenuItemProvider itemProvider, int selectionIndex,
m_SeperatorIndices = m_ItemProvider.GetSeperatorIndices();
selectedIndex = selectionIndex;
m_ShowAddNewPresetItem = m_ModifyItemUI != null;
+
+ if (m_SeperatorIndices == null)
+ m_SeperatorIndices = new int[0];
}
public override Vector2 GetWindowSize()
@@ -70,7 +84,7 @@ public override void OnGUI(Rect rect)
Rect contentRect = new Rect(0, 0, 1, CalcSize().y);
m_ScrollPosition = GUI.BeginScrollView(rect, m_ScrollPosition, contentRect);
{
- float curY = 0f;
+ float curY = 2f;
for (int i = 0; i <= maxIndex; ++i)
{
int itemControlID = i + 1000000;
diff --git a/Editor/Mono/GUI/InternalEditorGUI.cs b/Editor/Mono/GUI/InternalEditorGUI.cs
index c495c3210f..1dfc7f46b3 100644
--- a/Editor/Mono/GUI/InternalEditorGUI.cs
+++ b/Editor/Mono/GUI/InternalEditorGUI.cs
@@ -243,6 +243,18 @@ internal static void GameViewSizePopup(Rect buttonRect, GameViewSizeGroupType gr
}
}
+ internal static void GameViewOnPlayPopup(Rect buttonRect, int selectedIndex, IGameViewOnPlayMenuUser gameView, GUIStyle guiStyle)
+ {
+ var text = GameViewOnPlayMenu.GetOnPlayBehaviorName(selectedIndex);
+
+ if (EditorGUI.DropdownButton(buttonRect, GUIContent.Temp(text), FocusType.Passive, guiStyle))
+ {
+ var menuData = new GameViewOnPlayMenuItemProvider();
+ var flexibleMenu = new GameViewOnPlayMenu(menuData, selectedIndex, null, gameView);
+ PopupWindow.Show(buttonRect, flexibleMenu);
+ }
+ }
+
public static void DrawRect(Rect rect, Color color)
{
if (Event.current.type != EventType.Repaint)
diff --git a/Editor/Mono/GUI/InternalEditorGUILayout.cs b/Editor/Mono/GUI/InternalEditorGUILayout.cs
index dba74acbd3..db0e20fb68 100644
--- a/Editor/Mono/GUI/InternalEditorGUILayout.cs
+++ b/Editor/Mono/GUI/InternalEditorGUILayout.cs
@@ -25,6 +25,12 @@ internal static void GameViewSizePopup(GameViewSizeGroupType groupType, int sele
EditorGUI.GameViewSizePopup(s_LastRect, groupType, selectedIndex, gameView, style);
}
+ internal static void GameViewOnPlayPopup(int selectedIndex, IGameViewOnPlayMenuUser gameView, GUIStyle style, params GUILayoutOption[] options)
+ {
+ s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options);
+ EditorGUI.GameViewOnPlayPopup(s_LastRect, selectedIndex, gameView, style);
+ }
+
internal static void SortingLayerField(GUIContent label, SerializedProperty layerID, GUIStyle style, GUIStyle labelStyle)
{
s_LastRect = EditorGUILayout.GetControlRect(false, EditorGUI.kSingleLineHeight, style);
diff --git a/Editor/Mono/GUI/LazyLoadReferenceField.cs b/Editor/Mono/GUI/LazyLoadReferenceField.cs
index 977aa31bfc..d94d2c43f7 100644
--- a/Editor/Mono/GUI/LazyLoadReferenceField.cs
+++ b/Editor/Mono/GUI/LazyLoadReferenceField.cs
@@ -3,6 +3,7 @@
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using UnityEngine;
+using UnityEngine.UIElements;
namespace UnityEditor
{
@@ -29,5 +30,18 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten
if (EditorGUI.EndChangeCheck())
property.objectReferenceValue = value;
}
+
+ public override VisualElement CreatePropertyGUI(SerializedProperty property)
+ {
+ ScriptAttributeUtility.GetFieldInfoFromProperty(property, out var fieldType);
+
+ var objectField = EditorUIService.instance.CreateObjectField();
+ var genericType = fieldType.GetGenericArguments()[0];
+
+ EditorUIService.instance.SetObjectField(objectField, property.objectReferenceValue, genericType, property.displayName);
+ objectField.bindingPath = property.propertyPath;
+
+ return objectField;
+ }
}
}
diff --git a/Editor/Mono/GUI/ReorderableList.cs b/Editor/Mono/GUI/ReorderableList.cs
index a6a3bf17ae..edc5547b5e 100644
--- a/Editor/Mono/GUI/ReorderableList.cs
+++ b/Editor/Mono/GUI/ReorderableList.cs
@@ -1305,7 +1305,7 @@ private void DoDraggingAndSelection(Rect listRect)
int k = 1;
while (prop1.NextVisible(true) && prop1.depth > depth && tempProp.NextVisible(true) && tempProp.depth > depth)
{
- if (prop1.hasVisibleChildren && tempProp.hasVisibleChildren)
+ if (prop1.hasVisibleChildren && tempProp.hasVisibleChildren && tempIsExpanded.Count > k)
{
prop1.isExpanded = tempIsExpanded[k];
k++;
diff --git a/Editor/Mono/GUI/TreeView/GameObjectTreeViewGUI.cs b/Editor/Mono/GUI/TreeView/GameObjectTreeViewGUI.cs
index 685ca915e3..671a8e3d6e 100644
--- a/Editor/Mono/GUI/TreeView/GameObjectTreeViewGUI.cs
+++ b/Editor/Mono/GUI/TreeView/GameObjectTreeViewGUI.cs
@@ -425,7 +425,7 @@ private void HandlePrefabInstanceOverrideStatus(GameObjectTreeViewItem goItem, R
if (!go)
return;
- if (PrefabUtility.IsOutermostPrefabInstanceRoot(go) && PrefabUtility.HasPrefabInstanceNonDefaultOverrides_CachedForUI(go))
+ if (PrefabUtility.IsOutermostPrefabInstanceRoot(go) && PrefabUtility.HasPrefabInstanceNonDefaultOverridesOrUnusedOverrides_CachedForUI(go))
{
Rect overridesMarkerRect = new Rect(rect.x + SceneVisibilityHierarchyGUI.utilityBarWidth, rect.y + 1, 2, rect.height - 2);
diff --git a/Editor/Mono/GUI/WindowLayout.cs b/Editor/Mono/GUI/WindowLayout.cs
index 07b1debcee..a39b2fe521 100644
--- a/Editor/Mono/GUI/WindowLayout.cs
+++ b/Editor/Mono/GUI/WindowLayout.cs
@@ -562,7 +562,9 @@ internal static void CheckWindowConsistency()
{
if (win.m_Parent == null)
{
- Debug.LogError("Invalid editor window " + win.GetType());
+ Debug.LogErrorFormat(
+ "Invalid editor window of type: {0}, title: {1}",
+ win.GetType(), win.titleContent.text);
}
}
}
@@ -668,6 +670,97 @@ internal static EditorWindow GetMaximizedWindow()
return null;
}
+ internal static EditorWindow ShowAppropriateViewOnEnterExitPlaymodeList(bool entering, out List allWindows)
+ {
+ allWindows = new List();
+
+ int[] map = new[] { 4, 3, 1, 2 };
+ var allWindowsBase = PlayModeView.GetAllPlayModeViewWindows()
+ .OrderBy(c => map[(int)(c.enterPlayModeBehavior)]).ToList();
+
+ foreach (var w in allWindowsBase)
+ {
+ allWindows.Add(w);
+ }
+
+ // Prevent trying to go into the same state as we're already in, as it will break things
+ if (WindowFocusState.instance.m_CurrentlyInPlayMode == entering)
+ return null;
+
+ WindowFocusState.instance.m_CurrentlyInPlayMode = entering;
+
+ EditorWindow window = null;
+ EditorWindow maximized = GetMaximizedWindow();
+
+ if (entering)
+ {
+ if (!GameView.openWindowOnEnteringPlayMode && !(PlayModeView.GetCorrectPlayModeViewToFocus() is PlayModeView))
+ return null;
+
+ WindowFocusState.instance.m_WasMaximizedBeforePlay = (maximized != null);
+
+ // If a view is already maximized before entering play mode,
+ // just keep that maximized view, no matter if it's the game view or some other.
+ // Trust that user has a good reason (desire by Ethan etc.)
+ if (maximized != null)
+ {
+ return maximized;
+ }
+ }
+ else
+ {
+ // If a view was already maximized before entering play mode,
+ // then it was kept when switching to play mode, and can simply still be kept when exiting
+ if (WindowFocusState.instance.m_WasMaximizedBeforePlay)
+ {
+ return maximized;
+ }
+ }
+
+ // Unmaximize if maximized
+ if (maximized)
+ Unmaximize(maximized);
+
+ // Try finding and focusing appropriate window/tab
+ window = TryFocusAppropriateWindow(entering);
+ if (window)
+ {
+ return window;
+ }
+
+ // If we are entering Play more and no Game View was found, create one
+ if (entering && PlayModeView.openWindowOnEnteringPlayMode)
+ {
+
+ // Try to create and focus a Game View tab docked together with the Scene View tab
+ EditorWindow sceneView = FindEditorWindowOfType(typeof(SceneView));
+ GameView gameView;
+ if (sceneView && sceneView.m_Parent is DockArea)
+ {
+ DockArea dock = sceneView.m_Parent as DockArea;
+ if (dock)
+ {
+ WindowFocusState.instance.m_LastWindowTypeInSameDock = sceneView.GetType().ToString();
+ gameView = ScriptableObject.CreateInstance();
+ dock.AddTab(gameView);
+
+ allWindows.Add(gameView);
+ return gameView;
+ }
+ }
+
+ // If no Scene View was found at all, just create a floating Game View
+ gameView = ScriptableObject.CreateInstance();
+ gameView.Show(true);
+ gameView.Focus();
+
+ allWindows.Add(gameView);
+ return gameView;
+ }
+
+ return window;
+ }
+
internal static EditorWindow ShowAppropriateViewOnEnterExitPlaymode(bool entering)
{
// Prevent trying to go into the same state as we're already in, as it will break things
@@ -894,6 +987,19 @@ internal static void MaximizeKeyHandler(ShortcutArguments args)
}
}
+ private static View FindRootSplitView(EditorWindow win)
+ {
+ View itor = win.m_Parent.parent;
+ View rootSplit = itor;
+ while (itor is SplitView)
+ {
+ rootSplit = itor;
+ itor = itor.parent;
+ }
+
+ return rootSplit;
+ }
+
public static void AddSplitViewAndChildrenRecurse(View splitview, ArrayList list)
{
list.Add(splitview);
@@ -942,14 +1048,8 @@ public static void Maximize(EditorWindow win)
public static bool MaximizePrepare(EditorWindow win)
{
- // Find Root SplitView
- View itor = win.m_Parent.parent;
- View rootSplit = itor;
- while (itor != null && itor is SplitView)
- {
- rootSplit = itor;
- itor = itor.parent;
- }
+ View rootSplit = FindRootSplitView(win);
+ View itor = rootSplit.parent;
// Make sure it has a dockarea
DockArea dockArea = win.m_Parent as DockArea;
diff --git a/Editor/Mono/GameView/GameView.cs b/Editor/Mono/GameView/GameView.cs
index 0aed888f91..2b9f97c1ab 100644
--- a/Editor/Mono/GameView/GameView.cs
+++ b/Editor/Mono/GameView/GameView.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 UnityEditorInternal;
using UnityEditor.SceneManagement;
@@ -15,6 +16,7 @@
using UnityEngine.XR;
using FrameCapture = UnityEngine.Apple.FrameCapture;
using FrameCaptureDestination = UnityEngine.Apple.FrameCaptureDestination;
+using UnityEditor.ShortcutManagement;
/*
The main GameView can be in the following states when entering playmode.
@@ -35,7 +37,7 @@ Floating GameView in separate window
namespace UnityEditor
{
[EditorWindowTitle(title = "Game", useTypeNameAsIconName = true)]
- internal class GameView : PlayModeView, IHasCustomMenu, IGameViewSizeMenuUser
+ internal class GameView : PlayModeView, IHasCustomMenu, IGameViewSizeMenuUser, IGameViewOnPlayMenuUser
{
const int kScaleSliderMinWidth = 30;
const int kScaleSliderMaxWidth = 150;
@@ -65,6 +67,7 @@ float maxScale
}
[SerializeField] bool m_VSyncEnabled;
+ [SerializeField] bool m_PlayFocused;
[SerializeField] bool m_Gizmos;
[SerializeField] bool m_Stats;
[SerializeField] int[] m_SelectedSizes = new int[0]; // We have a selection for each game view size group (e.g standalone, android etc)
@@ -80,10 +83,13 @@ float maxScale
[SerializeField] bool[] m_LowResolutionForAspectRatios = new bool[0];
[SerializeField] int m_XRRenderMode = 0;
[SerializeField] RenderTexture m_RenderTexture;
+ [SerializeField] bool m_showToolbar = true;
+ [SerializeField] bool m_showToolbarOnFullscreen = false;
int m_SizeChangeID = int.MinValue;
List m_DisplaySubsystems = new List();
+ PlayModeStateChange latestState = PlayModeStateChange.EnteredEditMode;
internal override bool liveReloadPreferenceDefault => true;
@@ -100,6 +106,9 @@ internal static class Styles
public static GUIContent clearEveryFrameContextMenuContent = EditorGUIUtility.TrTextContent("Clear Every Frame in Edit Mode");
public static GUIContent lowResAspectRatiosContextMenuContent = EditorGUIUtility.TrTextContent("Low Resolution Aspect Ratios");
public static GUIContent metalFrameCaptureContent = EditorGUIUtility.TrIconContent("FrameCapture", "Capture the current view and open in Xcode frame debugger");
+ public static GUIContent suppressMessage = EditorGUIUtility.TrTextContent("This GameView is suppressed from rendering during Fullscreen.");
+ public static GUIContent disableFullscreenMainDisplayFormatContent = EditorGUIUtility.TrTextContent("Press {0} to exit fullscreen.");
+
public static GUIContent renderdocContent;
public static GUIStyle gameViewBackgroundStyle;
@@ -111,8 +120,30 @@ static Styles()
gameViewBackgroundStyle = "GameViewBackground";
renderdocContent = EditorGUIUtility.TrIconContent("FrameCapture", UnityEditor.RenderDocUtil.openInRenderDocLabel);
}
+
+ public static readonly GUIStyle largeCenteredText = new GUIStyle(EditorStyles.label)
+ {
+ name = "large-centered-text",
+ richText = true,
+ wordWrap = true,
+ alignment = TextAnchor.MiddleCenter,
+ margin = new RectOffset(4, 4, 1, 4),
+ padding = new RectOffset(4, 4, 4, 4),
+ fontSize = 42
+ };
+ public static readonly GUIStyle smallCenteredText = new GUIStyle(EditorStyles.label)
+ {
+ name = "small-centered-text",
+ richText = true,
+ wordWrap = true,
+ alignment = TextAnchor.MiddleCenter,
+ margin = new RectOffset(4, 4, 1, 4),
+ padding = new RectOffset(4, 4, 4, 4),
+ fontSize = 12
+ };
}
+
static double s_LastScrollTime;
public GameView()
@@ -128,6 +159,81 @@ public GameView()
textureHideFlags = HideFlags.HideAndDontSave;
}
+ internal override void ApplyEditorDisplayFullscreenSetting(IPlayModeViewFullscreenSettings settings)
+ {
+ var gameviewSetting = settings as GameViewFullscreenSettings;
+ if (gameviewSetting == null)
+ {
+ return;
+ }
+
+ isFullscreen = true;
+
+ var changed = false;
+
+ if (ModuleManager.ShouldShowMultiDisplayOption())
+ {
+ if (targetDisplay != gameviewSetting.DisplayNumber)
+ {
+ targetDisplay = gameviewSetting.DisplayNumber;
+ changed = true;
+ }
+ }
+
+ if (m_showToolbarOnFullscreen != gameviewSetting.ShowToolbar)
+ {
+ m_showToolbarOnFullscreen = gameviewSetting.ShowToolbar;
+ changed = true;
+ }
+
+ if (m_Stats != gameviewSetting.ShowStats)
+ {
+ m_Stats = gameviewSetting.ShowStats;
+ changed = true;
+ }
+
+ if (m_Gizmos != gameviewSetting.ShowGizmos)
+ {
+ m_Gizmos = gameviewSetting.ShowGizmos;
+ changed = true;
+ }
+
+ if (canVSync)
+ {
+ if (m_VSyncEnabled != gameviewSetting.VsyncEnabled)
+ {
+ m_VSyncEnabled = gameviewSetting.VsyncEnabled;
+ SetVSync(m_VSyncEnabled);
+ changed = true;
+ }
+ }
+
+ if (selectedSizeIndex != gameviewSetting.SelectedSizeIndex)
+ {
+ selectedSizeIndex = gameviewSetting.SelectedSizeIndex;
+ changed = true;
+ }
+
+ if (changed)
+ {
+ UpdateZoomAreaAndParent();
+ }
+ }
+
+ private bool canVSync
+ {
+ get
+ {
+ var gfxDeviceType = SystemInfo.graphicsDeviceType;
+ return
+ gfxDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Metal ||
+ gfxDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Vulkan ||
+ gfxDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Direct3D11 ||
+ gfxDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Direct3D12 ||
+ gfxDeviceType == UnityEngine.Rendering.GraphicsDeviceType.OpenGLCore;
+ }
+ }
+
public bool lowResolutionForAspectRatios
{
get
@@ -161,7 +267,13 @@ public bool vSyncEnabled
}
}
- int selectedSizeIndex
+ public bool playFocused
+ {
+ get { return m_PlayFocused; }
+ set { m_PlayFocused = value; }
+ }
+
+ public int selectedSizeIndex
{
get
{
@@ -288,7 +400,11 @@ Rect targetInParent // Area of the render target in parent view space
float gameMouseScale { get { return EditorGUIUtility.pixelsPerPoint / m_ZoomArea.scale.y; } }
- private bool showToolbar { get; set; }
+ private bool showToolbar
+ {
+ get => isFullscreen ? m_showToolbarOnFullscreen : m_showToolbar;
+ set => m_showToolbar = value;
+ }
internal bool drawGizmos
{
@@ -536,6 +652,31 @@ private bool ShouldShowMetalFrameCaptureGUI()
|| FrameCapture.IsDestinationSupported(FrameCaptureDestination.GPUTraceDocument);
}
+ private void MirrorFullscreenToolbarSettings()
+ {
+ if (enterPlayModeBehavior != EnterPlayModeBehavior.PlayFullscreen)
+ return;
+
+ // Even though there is a 1:1 relationship between a fullscreen game view, and the "suppressed" game view that
+ // spawned it, it's not easy to distingush which game view controls which sub game view. Rather than brutally
+ // enforce the 1:1 relationship allow any game view with the selected fullscreen idx, to control the settings
+ // on that fullscreen container window.
+ int displayIdx = fullscreenMonitorIdx;
+ List fullscreenContainers = EditorFullscreenController.GetFullscreenContainersForDisplayIndex(displayIdx);
+ foreach (var cw in fullscreenContainers)
+ {
+ var fullscreenGameView = (cw.rootView as HostView).actualView as GameView;
+
+ fullscreenGameView.targetDisplay = this.targetDisplay;
+ fullscreenGameView.m_ZoomArea = this.m_ZoomArea;
+ fullscreenGameView.UpdateZoomAreaAndParent();
+ fullscreenGameView.m_Stats = this.m_Stats;
+ fullscreenGameView.m_Gizmos = this.m_Gizmos;
+
+ }
+
+ }
+
private void DoToolbarGUI()
{
if (Event.current.isKey || Event.current.type == EventType.Used)
@@ -545,19 +686,24 @@ private void DoToolbarGUI()
GUILayout.BeginHorizontal(EditorStyles.toolbar);
{
- var availableTypes = GetAvailableWindowTypes();
- if (availableTypes.Count > 1)
+ if (!isFullscreen)
{
- var typeNames = availableTypes.Values.ToList();
- var types = availableTypes.Keys.ToList();
- int viewIndex = EditorGUILayout.Popup(typeNames.IndexOf(GetWindowTitle(GetType())), typeNames.ToArray(),
- EditorStyles.toolbarPopup,
- GUILayout.Width(90));
- EditorGUILayout.Space();
- if (types[viewIndex] != typeof(GameView))
+ EditorGUI.BeginDisabled(enterPlayModeBehavior == EnterPlayModeBehavior.PlayFullscreen);
+ var availableTypes = GetAvailableWindowTypes();
+ if (availableTypes.Count > 1)
{
- SwapMainWindow(types[viewIndex]);
+ var typeNames = availableTypes.Values.ToList();
+ var types = availableTypes.Keys.ToList();
+ int viewIndex = EditorGUILayout.Popup(typeNames.IndexOf(titleContent.text), typeNames.ToArray(),
+ EditorStyles.toolbarPopup,
+ GUILayout.Width(90));
+ EditorGUILayout.Space();
+ if (types[viewIndex] != typeof(GameView))
+ {
+ SwapMainWindow(types[viewIndex]);
+ }
}
+ EditorGUI.EndDisabled();
}
if (ModuleManager.ShouldShowMultiDisplayOption())
@@ -645,11 +791,17 @@ private void DoToolbarGUI()
m_XRRenderMode = selectedMirrorViewBlitMode;
}
}
+ GUILayout.FlexibleSpace();
- enterPlayModeBehavior = (EnterPlayModeBehavior)EditorGUILayout.EnumPopup(enterPlayModeBehavior, EditorStyles.toolbarDropDown, GUILayout.Width(110));
+ // Don't display the fullscreen selection dropdown on gameview toolbars that are already fullscreen.
+ if (!isFullscreen)
+ {
+ EditorGUI.BeginDisabled(Application.isPlaying);
+ EditorGUILayout.GameViewOnPlayPopup(playModeBehaviorIdx, this, EditorStyles.toolbarDropDown);
+ EditorGUI.EndDisabled();
+ }
EditorUtility.audioMasterMute = GUILayout.Toggle(EditorUtility.audioMasterMute, Styles.muteContent, EditorStyles.toolbarButton);
-
m_Stats = GUILayout.Toggle(m_Stats, Styles.statsContent, EditorStyles.toolbarButton);
if (EditorGUILayout.DropDownToggle(ref m_Gizmos, Styles.gizmosContent, EditorStyles.toolbarDropDownToggleRight))
@@ -662,6 +814,7 @@ private void DoToolbarGUI()
}
}
GUILayout.EndHorizontal();
+ MirrorFullscreenToolbarSettings();
}
private int XRTranslateMirrorViewBlitModeToRenderMode(int mirrorViewBlitMode)
@@ -801,6 +954,7 @@ public void RenderToHMDOnly()
private void OnPlayModeStateChanged(PlayModeStateChange state)
{
+ latestState = state;
if (state == PlayModeStateChange.EnteredPlayMode)
{
// Enable vsync in play mode to get as much as possible frame rate consistency
@@ -865,6 +1019,12 @@ private void OnGUI()
if (HandleCommand(evt))
return;
+ if (suppressRenderingForFullscreen && latestState == PlayModeStateChange.EnteredPlayMode)
+ {
+ DrawDuringFullscreenBackground();
+ return;
+ }
+
if (position.size * EditorGUIUtility.pixelsPerPoint != m_LastWindowPixelSize) // pixelsPerPoint only reliable in OnGUI()
{
UpdateZoomAreaAndParent();
@@ -918,7 +1078,14 @@ private void OnGUI()
if (m_Parent)
{
var zoomedTarget = new Rect(targetInView.position + gameViewTarget.position, targetInView.size);
- SetParentGameViewDimensions(zoomedTarget, gameViewTarget, targetRenderSize);
+ if (!isFullscreen)
+ {
+ SetParentGameViewDimensions(zoomedTarget, gameViewTarget, targetRenderSize);
+ }
+ else
+ {
+ SetParentGameViewDimensions(zoomedTarget, gameViewTarget, EditorFullscreenController.fullscreenDisplayRenderSize);
+ }
}
var editorMousePosition = Event.current.mousePosition;
@@ -975,6 +1142,13 @@ private void OnGUI()
if (Event.current.isKey && (!EditorApplication.isPlaying || EditorApplication.isPaused))
return;
+ if (isFullscreen)
+ {
+ // Let the global eventhandler handle toggling fullscreen after this event (do not use the current event)
+ // We support fullscreen in both edit mode and play mode
+ return;
+ }
+
bool mousePosInGameViewRect = viewInWindow.Contains(Event.current.mousePosition);
// MouseDown events outside game view rect are not send to scripts but MouseUp events are (see below)
@@ -1042,5 +1216,42 @@ private void OnGUI()
if (m_Stats)
GameViewGUI.GameViewStatsGUI();
}
+
+ private void DrawDuringFullscreenBackground()
+ {
+ DoToolbarGUI();
+ EditorGUILayout.LabelField(Styles.suppressMessage, Styles.largeCenteredText);
+ var binding = ShortcutManager.instance.GetShortcutBinding(EditorFullscreenController.kFullscreenToggle);
+ string content = string.Format(Styles.disableFullscreenMainDisplayFormatContent.text, binding);
+ EditorGUILayout.LabelField(content, Styles.smallCenteredText);
+ }
+
+ void IGameViewOnPlayMenuUser.OnPlayPopupSelection(int indexClicked, object objectSelected)
+ {
+ playModeBehaviorIdx = indexClicked;
+ if (playModeBehaviorIdx == 0)
+ {
+ if (playFocused)
+ enterPlayModeBehavior = EnterPlayModeBehavior.PlayFocused;
+ else
+ enterPlayModeBehavior = EnterPlayModeBehavior.PlayUnfocused;
+ fullscreenMonitorIdx = PlayModeView.kFullscreenNone;
+ }
+ else if (playModeBehaviorIdx == 1)
+ {
+ enterPlayModeBehavior = EnterPlayModeBehavior.PlayMaximized;
+ fullscreenMonitorIdx = PlayModeView.kFullscreenNone;
+ }
+ else
+ {
+ enterPlayModeBehavior = EnterPlayModeBehavior.PlayFullscreen;
+ fullscreenMonitorIdx = GameViewOnPlayMenu.SelectedIndexToDisplayIndex(indexClicked);
+ if (fullscreenMonitorIdx == PlayModeView.kFullscreenInvalidIdx)
+ {
+ Debug.LogError("Invalid display index(" + fullscreenMonitorIdx + ") for selected index(" + indexClicked + ")");
+ fullscreenMonitorIdx = PlayModeView.kFullscreenNone;
+ }
+ }
+ }
}
}
diff --git a/Editor/Mono/GameView/GameViewOnPlayMenu.cs b/Editor/Mono/GameView/GameViewOnPlayMenu.cs
new file mode 100644
index 0000000000..aa619d42d6
--- /dev/null
+++ b/Editor/Mono/GameView/GameViewOnPlayMenu.cs
@@ -0,0 +1,140 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using UnityEngine;
+using System;
+using System.Collections.Generic;
+
+namespace UnityEditor
+{
+ internal class GameViewOnPlayMenu : FlexibleMenu
+ {
+ private static class Styles
+ {
+ public static readonly GUIContent gamePlayNormalContent = EditorGUIUtility.TrTextContent("Normally", "Play inside a game view docked inside unity");
+ public static readonly GUIContent gamePlayMaximizedContent = EditorGUIUtility.TrTextContent("Maximized", "Maximize the game view before entering play mode.");
+ public static readonly GUIContent gamePlayFullscreenContent = EditorGUIUtility.TrTextContent("Fullscreen on ", "Play the game view on a fullscreen monitor");
+ public static readonly GUIContent playFocusedToggleContent = EditorGUIUtility.TrTextContent("Focused", "Forcilby focus the game view when entering play mode.");
+ public static GUIContent vSyncToggleContent = EditorGUIUtility.TrTextContent("VSync", "Enable VSync only for the game view while in playmode.");
+ public static GUIContent vSyncUnsupportedContent = EditorGUIUtility.TrTextContent("No VSync", "VSync is not available because it is not supported by this device");
+ public static GUIContent gamePlayModeBehaviorLabelContent = EditorGUIUtility.TrTextContent("Enter Play Mode:");
+
+ public const float kMargin = 9f;
+ public const float kTopMargin = 7f;
+ public const int kNumberOfTogglesOnTop = 2;
+ public static float frameHeight => (kTopMargin * kNumberOfTogglesOnTop) + EditorGUI.kSingleLineHeight;
+ public static float contentOffset => frameHeight + EditorGUI.kControlVerticalSpacing;
+ }
+
+ // Number of on play behaviors that should always be visible. Fullscreen may or may not be supported and there may be multiple monitors.
+ // The base is 2, "Play Normally" (with focus as a checkbox) and "Play Maximized"
+ public const int kPlayModeBaseOptionCount = 2;
+ private readonly IGameViewOnPlayMenuUser m_GameView;
+ private bool m_ShowFullscreenOptions = true;
+
+ public GameViewOnPlayMenu(IFlexibleMenuItemProvider itemProvider, int selectionIndex, FlexibleMenuModifyItemUI modifyItemUi, IGameViewOnPlayMenuUser gameView, bool showFullscreenOptions = true)
+ : base(itemProvider, selectionIndex, modifyItemUi, gameView.OnPlayPopupSelection)
+ {
+ m_GameView = gameView;
+ m_ShowFullscreenOptions = showFullscreenOptions;
+ }
+
+ public override Vector2 GetWindowSize()
+ {
+ var playFocusedToggleSize = EditorStyles.toggle.CalcSize(Styles.playFocusedToggleContent);
+ var size = CalcSize();
+
+ size.x = Mathf.Max(size.x, playFocusedToggleSize.x + Styles.kMargin * 2);
+ size.y += Styles.frameHeight + EditorGUI.kControlVerticalSpacing;
+ return size;
+ }
+
+ private bool IsVSyncToggleVisible()
+ {
+ // Only show the vsync toggle for editor supported gfx device backend.
+ var gfxDeviceType = SystemInfo.graphicsDeviceType;
+ return gfxDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Metal ||
+ gfxDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Vulkan ||
+ gfxDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Direct3D11 ||
+ gfxDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Direct3D12 ||
+ gfxDeviceType == UnityEngine.Rendering.GraphicsDeviceType.OpenGLCore;
+ }
+
+ private void DoVSyncToggle()
+ {
+ if (IsVSyncToggleVisible())
+ {
+ m_GameView.vSyncEnabled = GUILayout.Toggle(m_GameView.vSyncEnabled, Styles.vSyncToggleContent);
+ }
+ else
+ {
+ m_GameView.vSyncEnabled = false;
+ GUILayout.Label(Styles.vSyncUnsupportedContent, EditorStyles.miniLabel);
+ }
+ }
+
+ private void OnPlayFocusedToggleChanged(bool newValue)
+ {
+ List playViewList;
+ WindowLayout.ShowAppropriateViewOnEnterExitPlaymodeList(true, out playViewList);
+
+ foreach (PlayModeView playView in playViewList)
+ {
+ if (playView != (m_GameView as PlayModeView))
+ {
+ ((IGameViewOnPlayMenuUser)playView).playFocused = false;
+ }
+ }
+
+ m_GameView.playFocused = newValue;
+ }
+
+ public override void OnGUI(Rect rect)
+ {
+ var frameRect = new Rect(rect.x, rect.y, rect.width, rect.height);
+ GUI.Label(frameRect, "", EditorStyles.viewBackground);
+
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(15); // Move everything slightly right so it doesn't overlap with our "repaint indicator"
+ bool playFocuedToggle = GUILayout.Toggle(m_GameView.playFocused, Styles.playFocusedToggleContent);
+ if (playFocuedToggle != m_GameView.playFocused)
+ {
+ OnPlayFocusedToggleChanged(playFocuedToggle);
+ }
+ DoVSyncToggle();
+ GUILayout.EndHorizontal();
+
+ GUILayout.Label(Styles.gamePlayModeBehaviorLabelContent, EditorStyles.boldLabel);
+
+ rect.height = rect.height - Styles.contentOffset;
+ rect.y = rect.y + Styles.contentOffset;
+
+ base.OnGUI(rect);
+ }
+
+ public static string GetOnPlayBehaviorName(int selectedIndex)
+ {
+ if (selectedIndex == 0)
+ return Styles.gamePlayNormalContent.text;
+ if (selectedIndex == 1)
+ return Styles.gamePlayMaximizedContent.text;
+
+ int displayIdx = SelectedIndexToDisplayIndex(selectedIndex);
+ var connectedDisplay = EditorFullscreenController.GetConnectedDisplayNames();
+
+ var displayName = (displayIdx >= connectedDisplay.Length)
+ ? "Invalid monitor"
+ : connectedDisplay[displayIdx];
+
+ return Styles.gamePlayFullscreenContent.text + displayIdx + ":" + displayName;
+ }
+
+ public static int SelectedIndexToDisplayIndex(int selectedIndex)
+ {
+ if (selectedIndex <= 1)
+ return -1; //Invalid fullscreen selection
+ return selectedIndex - kPlayModeBaseOptionCount;
+ }
+ }
+}
diff --git a/Editor/Mono/GameView/GameViewOnPlayMenuItemProvider.cs b/Editor/Mono/GameView/GameViewOnPlayMenuItemProvider.cs
new file mode 100644
index 0000000000..40102b6cac
--- /dev/null
+++ b/Editor/Mono/GameView/GameViewOnPlayMenuItemProvider.cs
@@ -0,0 +1,122 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using System;
+using UnityEngine;
+
+namespace UnityEditor
+{
+ internal class SimulationOnPlayMenuItemProvider : IFlexibleMenuItemProvider
+ {
+ public int Count()
+ {
+ // The simulator window only supports "play normally (focuced/unfocused) and play maximized
+ // fullscreen support may be added in the future.
+ int optionCount = GameViewOnPlayMenu.kPlayModeBaseOptionCount;
+ return optionCount;
+ }
+
+ public object GetItem(int index)
+ {
+ return null;
+ }
+
+ public int Add(object obj)
+ {
+ throw new NotImplementedException("Operation not supported");
+ }
+
+ public void Replace(int index, object obj)
+ {
+ throw new NotImplementedException("Operation not supported");
+ }
+
+ public void Remove(int index)
+ {
+ throw new NotImplementedException("Operation not supported");
+ }
+
+ public object Create()
+ {
+ throw new NotImplementedException("Operation not supported");
+ }
+
+ public void Move(int index, int destIndex, bool insertAfterDestIndex)
+ {
+ throw new NotImplementedException("Operation not supported");
+ }
+
+ public string GetName(int index)
+ {
+ return GameViewOnPlayMenu.GetOnPlayBehaviorName(index);
+ }
+
+ public bool IsModificationAllowed(int index)
+ {
+ return false;
+ }
+
+ public int[] GetSeperatorIndices()
+ {
+ return null;
+ }
+ }
+
+ internal class GameViewOnPlayMenuItemProvider : IFlexibleMenuItemProvider
+ {
+ public int Count()
+ {
+ // "Play Normal" and "Play Maximized" should always be options.
+ // The play fullscreen option will be added per-display.
+ int optionCount = GameViewOnPlayMenu.kPlayModeBaseOptionCount;
+ optionCount += EditorFullscreenController.GetConnectedDisplayNames().Length;
+ return optionCount;
+ }
+
+ public object GetItem(int index)
+ {
+ return null;
+ }
+
+ public int Add(object obj)
+ {
+ throw new NotImplementedException("Operation not supported");
+ }
+
+ public void Replace(int index, object obj)
+ {
+ throw new NotImplementedException("Operation not supported");
+ }
+
+ public void Remove(int index)
+ {
+ throw new NotImplementedException("Operation not supported");
+ }
+
+ public object Create()
+ {
+ throw new NotImplementedException("Operation not supported");
+ }
+
+ public void Move(int index, int destIndex, bool insertAfterDestIndex)
+ {
+ throw new NotImplementedException("Operation not supported");
+ }
+
+ public string GetName(int index)
+ {
+ return GameViewOnPlayMenu.GetOnPlayBehaviorName(index);
+ }
+
+ public bool IsModificationAllowed(int index)
+ {
+ return false;
+ }
+
+ public int[] GetSeperatorIndices()
+ {
+ return null;
+ }
+ }
+}
diff --git a/Editor/Mono/GameView/GameViewSizeMenu.cs b/Editor/Mono/GameView/GameViewSizeMenu.cs
index ccb3b036ac..b518ce473d 100644
--- a/Editor/Mono/GameView/GameViewSizeMenu.cs
+++ b/Editor/Mono/GameView/GameViewSizeMenu.cs
@@ -10,16 +10,11 @@ namespace UnityEditor
// Resolution/Aspect ratio menu for the GameView, with an optional toggle for low-resolution aspect ratios
internal class GameViewSizeMenu : FlexibleMenu
{
- static class Styles
- {
- public static GUIContent vSyncToggleContent = EditorGUIUtility.TrTextContent("VSync (Game view only)", "Enable VSync only for the game view while in playmode.");
- }
-
const float kTopMargin = 7f;
const float kMargin = 9f;
IGameViewSizeMenuUser m_GameView;
- float frameHeight { get { return kTopMargin * 2 + EditorGUI.kSingleLineHeight * (IsVSyncToggleVisible() ? 2 : 1); } }
+ float frameHeight { get { return kTopMargin * 2 + EditorGUI.kSingleLineHeight; } }
float contentOffset { get { return frameHeight + EditorGUI.kControlVerticalSpacing; } }
public GameViewSizeMenu(IFlexibleMenuItemProvider itemProvider, int selectionIndex, FlexibleMenuModifyItemUI modifyItemUi, IGameViewSizeMenuUser gameView)
@@ -37,25 +32,6 @@ public override Vector2 GetWindowSize()
return size;
}
- private bool IsVSyncToggleVisible()
- {
- // Only show the vsync toggle for editor supported gfx device backend.
- var gfxDeviceType = SystemInfo.graphicsDeviceType;
- return gfxDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Metal ||
- gfxDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Vulkan ||
- gfxDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Direct3D11 ||
- gfxDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Direct3D12 ||
- gfxDeviceType == UnityEngine.Rendering.GraphicsDeviceType.OpenGLCore;
- }
-
- private void DoVSyncToggle(Rect rect)
- {
- if (!IsVSyncToggleVisible())
- return;
- var toggleRect = new Rect(rect.xMin, rect.yMax + 2, rect.width, EditorGUI.kSingleLineHeight);
- m_GameView.vSyncEnabled = GUI.Toggle(toggleRect, m_GameView.vSyncEnabled, Styles.vSyncToggleContent);
- }
-
public override void OnGUI(Rect rect)
{
var frameRect = new Rect(rect.x, rect.y, rect.width, frameHeight);
@@ -68,7 +44,6 @@ public override void OnGUI(Rect rect)
GUI.enabled = true;
- DoVSyncToggle(toggleRect);
rect.height = rect.height - contentOffset;
rect.y = rect.y + contentOffset;
diff --git a/Editor/Mono/GameView/IGameViewOnPlayMenuUser.cs b/Editor/Mono/GameView/IGameViewOnPlayMenuUser.cs
new file mode 100644
index 0000000000..e0771deca4
--- /dev/null
+++ b/Editor/Mono/GameView/IGameViewOnPlayMenuUser.cs
@@ -0,0 +1,13 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+namespace UnityEditor
+{
+ internal interface IGameViewOnPlayMenuUser
+ {
+ void OnPlayPopupSelection(int indexClicked, object objectSelected);
+ bool playFocused { get; set; }
+ bool vSyncEnabled { get; set; }
+ }
+}
diff --git a/Editor/Mono/GameView/IGameViewSizeMenuUser.cs b/Editor/Mono/GameView/IGameViewSizeMenuUser.cs
index ff43698b8a..d2ed188a87 100644
--- a/Editor/Mono/GameView/IGameViewSizeMenuUser.cs
+++ b/Editor/Mono/GameView/IGameViewSizeMenuUser.cs
@@ -9,6 +9,5 @@ internal interface IGameViewSizeMenuUser
void SizeSelectionCallback(int indexClicked, object objectSelected);
bool lowResolutionForAspectRatios { get; set; }
bool forceLowResolutionAspectRatios { get; }
- bool vSyncEnabled { get; set; }
}
}
diff --git a/Editor/Mono/Inspector/MeshPreview.cs b/Editor/Mono/Inspector/MeshPreview.cs
index 5d3c92e339..c2fc742641 100644
--- a/Editor/Mono/Inspector/MeshPreview.cs
+++ b/Editor/Mono/Inspector/MeshPreview.cs
@@ -684,6 +684,7 @@ void OnDropDownAction(Material mat, int mode, bool flatUVs)
m_Settings.activeMaterial.SetFloat("_Mode", (float)mode);
m_Settings.activeMaterial.SetFloat("_UVChannel", 0.0f);
+ m_Settings.activeMaterial.SetFloat("_Cull", flatUVs ? (float)CullMode.Off : (float)CullMode.Back);
}
void MeshPreviewZoom(Rect rect, Event evt)
diff --git a/Editor/Mono/Inspector/MinMaxGradientPropertyDrawer.cs b/Editor/Mono/Inspector/MinMaxGradientPropertyDrawer.cs
index c80c886a31..91f46dade6 100644
--- a/Editor/Mono/Inspector/MinMaxGradientPropertyDrawer.cs
+++ b/Editor/Mono/Inspector/MinMaxGradientPropertyDrawer.cs
@@ -5,13 +5,15 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
+using UnityEngine.UIElements;
+using UnityEditor.UIElements;
namespace UnityEditorInternal
{
[CustomPropertyDrawer(typeof(ParticleSystem.MinMaxGradient))]
public class MinMaxGradientPropertyDrawer : PropertyDrawer
{
- class PropertyData
+ internal class PropertyData
{
public SerializedProperty mode;
public SerializedProperty gradientMin;
@@ -128,5 +130,12 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten
break;
}
}
+
+ public override VisualElement CreatePropertyGUI(SerializedProperty property)
+ {
+ Init(property);
+
+ return new MinMaxGradientField(m_Property, property.localizedDisplayName);
+ }
}
}
diff --git a/Editor/Mono/Inspector/MonoScriptInspector.cs b/Editor/Mono/Inspector/MonoScriptInspector.cs
index 5eeabec187..094786b1f5 100644
--- a/Editor/Mono/Inspector/MonoScriptInspector.cs
+++ b/Editor/Mono/Inspector/MonoScriptInspector.cs
@@ -234,6 +234,10 @@ void CachePreview()
if (text.Length >= kMaxChars)
text = text.Substring(0, kMaxChars) + "...\n\n<...etc...>";
}
+ else
+ {
+ text = $"{EditorUtility.FormatBytes(m_TextAsset.dataSize)} size .bytes file";
+ }
}
m_CachedPreview = new GUIContent(text);
diff --git a/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs b/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs
index b24cff0f57..db289c1e49 100644
--- a/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs
+++ b/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs
@@ -230,7 +230,6 @@ class SettingsContent
public static readonly GUIContent shaderPrecisionModel = EditorGUIUtility.TrTextContent("Shader precision model*", "Mobile targets prefer lower precision by default to improve performance, but your rendering pipeline may prefer full precision by default and to optimize against lower precision cases explicitly.");
public static readonly GUIContent[] shaderPrecisionModelOptions = { EditorGUIUtility.TrTextContent("Use platform defaults for sampler precision"), EditorGUIUtility.TrTextContent("Use full sampler precision by default, lower precision explicitly declared") };
public static readonly GUIContent stereo360CaptureCheckbox = EditorGUIUtility.TrTextContent("360 Stereo Capture*");
- public static readonly GUIContent uploadClearedTextureDataAfterCreationFromScript = EditorGUIUtility.TrTextContent("Upload Cleared Texture Data", "When set, if you create a Texture from script the initial data is cleared and automatically uploaded to video memory. This was the default behavior in previous Unity versions. In most cases this upload is not needed and wastes bandwith because the GPU data gets overwritten anyway. You typically upload (by calling Apply) or copy new data into the new texture right after you create it. Only turn this on for debugging purposes or if your project depends on having the GPU texture data cleared.");
public static readonly GUIContent forceSRGBBlit = EditorGUIUtility.TrTextContent("Force SRGB blit", "Force SRGB blit for Linear color space.");
public static readonly GUIContent notApplicableInfo = EditorGUIUtility.TrTextContent("Not applicable for this platform.");
@@ -413,7 +412,6 @@ PlayerSettingsIconsEditor iconsEditor
SerializedProperty m_LegacyClampBlendShapeWeights;
SerializedProperty m_AndroidEnableTango;
SerializedProperty m_Enable360StereoCapture;
- SerializedProperty m_UploadClearedTextureDataAfterCreationFromScript;
SerializedProperty m_VirtualTexturingSupportEnabled;
SerializedProperty m_ShaderPrecisionModel;
@@ -620,7 +618,6 @@ void OnEnable()
m_RequireES32 = FindPropertyAssert("openGLRequireES32");
m_LegacyClampBlendShapeWeights = FindPropertyAssert("legacyClampBlendShapeWeights");
- m_UploadClearedTextureDataAfterCreationFromScript = FindPropertyAssert("uploadClearedTextureDataAfterCreationFromScript");
m_AndroidEnableTango = FindPropertyAssert("AndroidEnableTango");
SerializedProperty property = FindPropertyAssert("vrSettings");
@@ -3066,7 +3063,6 @@ private void OtherSectionLegacyGUI(BuildPlatform platform)
GUILayout.Label(SettingsContent.legacyTitle, EditorStyles.boldLabel);
EditorGUILayout.PropertyField(m_LegacyClampBlendShapeWeights, SettingsContent.legacyClampBlendShapeWeights);
- EditorGUILayout.PropertyField(m_UploadClearedTextureDataAfterCreationFromScript, SettingsContent.uploadClearedTextureDataAfterCreationFromScript);
EditorGUILayout.Space();
}
diff --git a/Editor/Mono/Inspector/RendererLightingSettings.cs b/Editor/Mono/Inspector/RendererLightingSettings.cs
index 6932236e62..3e3bd14d8f 100644
--- a/Editor/Mono/Inspector/RendererLightingSettings.cs
+++ b/Editor/Mono/Inspector/RendererLightingSettings.cs
@@ -80,6 +80,8 @@ static class Styles
public static readonly GUIContent resolutionTooLowWarning = EditorGUIUtility.TrTextContent("Precompute/indirect resolution for this terrain is probably too low. If the Clustering stage takes a long time, try using a higher realtime/indirect resolution setting in the Lighting window or assign LightmapParameters that use a higher resolution setting.");
public static readonly GUIContent giNotEnabledInfo = EditorGUIUtility.TrTextContent("Lightmapping settings are currently disabled. Enable Baked Global Illumination or Realtime Global Illumination to display these settings.");
public static readonly GUIContent isPresetInfo = EditorGUIUtility.TrTextContent("The Contribute Global Illumination property cannot be stored in a preset.");
+ public static readonly GUIContent giMeshNotValid = EditorGUIUtility.TrTextContent("It is not possible to generate lighting for this Mesh because it is missing the required attribute(s). Ensure that this Mesh has normals, vertices, and texture coordinates.");
+ public static readonly GUIContent giMeshNotValidMultiple = EditorGUIUtility.TrTextContent("It is not possible to generate lighting for these Meshes because one or more of them are missing the required attribute(s). Ensure that all the Meshes you've selected have normals, vertices, and texture coordinates.");
public static readonly GUIContent openPreview = EditorGUIUtility.TrTextContent("Open Preview");
public static readonly GUIStyle openPreviewStyle = EditorStyles.objectFieldThumb.name + "LightmapPreviewOverlay";
@@ -432,6 +434,7 @@ bool ContributeGISettings()
EditorGUI.BeginChangeCheck();
contributeGI = EditorGUILayout.Toggle(Styles.contributeGI, contributeGI);
+
if (EditorGUI.EndChangeCheck())
{
SceneModeUtility.SetStaticFlags(m_GameObjectsSerializedObject.targetObjects, (int)StaticEditorFlags.ContributeGI, contributeGI);
@@ -439,6 +442,11 @@ bool ContributeGISettings()
m_GameObjectsSerializedObject.Update();
}
+ // show a warning if not all renderers are valid, even when not active or enabled
+ if (contributeGI || mixedValue)
+ if (m_Renderers != null && !m_Renderers.All(Lightmapping.IsRendererValid))
+ EditorGUILayout.HelpBox(m_Renderers.Length > 1 ? Styles.giMeshNotValidMultiple.text : Styles.giMeshNotValid.text, MessageType.Warning);
+
EditorGUI.showMixedValue = false;
return contributeGI && !mixedValue;
diff --git a/Editor/Mono/Inspector/ReorderableListWrapper.cs b/Editor/Mono/Inspector/ReorderableListWrapper.cs
index b0c7608afa..cdbe537458 100644
--- a/Editor/Mono/Inspector/ReorderableListWrapper.cs
+++ b/Editor/Mono/Inspector/ReorderableListWrapper.cs
@@ -61,7 +61,7 @@ internal SerializedProperty Property
public static string GetPropertyIdentifier(SerializedProperty serializedProperty)
{
- return serializedProperty.propertyPath + serializedProperty.serializedObject.targetObject.GetInstanceID();
+ return serializedProperty.propertyPath + (GUIView.current?.nativeHandle.ToInt32() ?? -1);
}
ReorderableListWrapper() {}
diff --git a/Editor/Mono/Inspector/UnityEventDrawer.cs b/Editor/Mono/Inspector/UnityEventDrawer.cs
index 97e1222ed8..dca303a963 100644
--- a/Editor/Mono/Inspector/UnityEventDrawer.cs
+++ b/Editor/Mono/Inspector/UnityEventDrawer.cs
@@ -12,12 +12,23 @@
using UnityEngine.Events;
using Object = UnityEngine.Object;
using UnityEngine.Pool;
+using UnityEngine.UIElements;
namespace UnityEditorInternal
{
[CustomPropertyDrawer(typeof(UnityEventBase), true)]
public class UnityEventDrawer : PropertyDrawer
{
+ internal struct PropertyData
+ {
+ public SerializedProperty mode;
+ public SerializedProperty arguments;
+ public SerializedProperty callState;
+ public SerializedProperty listenerTarget;
+ public SerializedProperty methodName;
+ public SerializedProperty objectArgument;
+ }
+
protected class State
{
internal ReorderableList m_ReorderableList;
@@ -33,6 +44,7 @@ protected class State
internal const string kArgumentsPath = "m_Arguments";
internal const string kModePath = "m_Mode";
internal const string kMethodNamePath = "m_MethodName";
+ internal const string kCallsPath = "m_PersistentCalls.m_Calls";
//ArgumentCache paths
internal const string kFloatArgument = "m_FloatArgument";
@@ -48,6 +60,15 @@ protected class State
private static readonly char[] kDotSeparator = { '.' };
private static readonly char[] kClosingSquareBraceSeparator = { ']' };
+ // uss names
+ internal const string kUssClassName = "unity-event";
+ internal const string kLeftColumnClassName = kUssClassName + "__left-column";
+ internal const string kRightColumnClassName = kUssClassName + "__right-column";
+ internal const string kContainerClassName = kUssClassName + "__container";
+ internal const string kHeaderClassName = kUssClassName + "__header";
+ internal const string kListViewScrollViewClassName = kUssClassName + "__list-view-scroll-view";
+ internal const string kListViewItemClassName = kUssClassName + "__list-view-item";
+
string m_Text;
UnityEventBase m_DummyEvent;
SerializedProperty m_Prop;
@@ -91,7 +112,7 @@ private State GetState(SerializedProperty prop)
if (state == null)
state = new State();
- SerializedProperty listenersArray = prop.FindPropertyRelative("m_PersistentCalls.m_Calls");
+ SerializedProperty listenersArray = prop.FindPropertyRelative(kCallsPath);
state.m_ReorderableList =
new ReorderableList(prop.serializedObject, listenersArray, true, true, true, true)
{
@@ -132,6 +153,179 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten
state.lastSelectedIndex = m_LastSelectedIndex;
}
+ private ListView CreateListView(SerializedProperty property)
+ {
+ var listView = EditorUIService.instance.CreateListViewBinding(property.serializedObject);
+ listView.showAddRemoveFooter = true;
+ listView.reorderMode = ListViewReorderMode.Animated;
+ listView.showBorder = true;
+ listView.showFoldoutHeader = false;
+ listView.showBoundCollectionSize = false;
+ listView.showAlternatingRowBackgrounds = AlternatingRowBackground.None;
+ listView.fixedItemHeight = GetElementHeight();
+
+ var propertyRelative = property.FindPropertyRelative(kCallsPath);
+ listView.bindingPath = propertyRelative.propertyPath;
+
+ listView.makeItem += () =>
+ {
+ return EditorUIService.instance.CreateUnityEventItem();
+ };
+
+ listView.bindItem += (VisualElement element, int i) =>
+ {
+ if (i >= propertyRelative.arraySize)
+ {
+ return;
+ }
+
+ var pListener = propertyRelative.GetArrayElementAtIndex(i);
+ var arguments = pListener.FindPropertyRelative(kArgumentsPath);
+ var listenerTarget = pListener.FindPropertyRelative(kInstancePath);
+
+ var propertyData = new PropertyData()
+ {
+ mode = pListener.FindPropertyRelative(kModePath),
+ arguments = arguments,
+ callState = pListener.FindPropertyRelative(kCallStatePath),
+ listenerTarget = listenerTarget,
+ methodName = pListener.FindPropertyRelative(kMethodNamePath),
+ objectArgument = arguments.FindPropertyRelative(kObjectArgument)
+ };
+
+
+ Func createMenuCallback = () =>
+ {
+ var genericMenu = BuildPopupList(listenerTarget.objectReferenceValue, m_DummyEvent, pListener);
+ return genericMenu;
+ };
+ Func formatSelectedValueCallback = (value) =>
+ {
+ return GetFunctionDropdownText(pListener);
+ };
+ Func getArgumentCallback = () =>
+ {
+ return GetArgument(pListener);
+ };
+
+ EditorUIService.instance.BindUnityEventItem(element, propertyData, createMenuCallback, formatSelectedValueCallback, getArgumentCallback);
+ };
+
+ return listView;
+ }
+
+ private SerializedProperty GetArgument(SerializedProperty pListener)
+ {
+ var listenerTarget = pListener.FindPropertyRelative(kInstancePath);
+ var methodName = pListener.FindPropertyRelative(kMethodNamePath);
+ var mode = pListener.FindPropertyRelative(kModePath);
+ var arguments = pListener.FindPropertyRelative(kArgumentsPath);
+
+ SerializedProperty argument;
+ var modeEnum = GetMode(mode);
+ //only allow argument if we have a valid target / method
+ if (listenerTarget.objectReferenceValue == null || string.IsNullOrEmpty(methodName.stringValue))
+ modeEnum = PersistentListenerMode.Void;
+
+ switch (modeEnum)
+ {
+ case PersistentListenerMode.Float:
+ argument = arguments.FindPropertyRelative(kFloatArgument);
+ break;
+ case PersistentListenerMode.Int:
+ argument = arguments.FindPropertyRelative(kIntArgument);
+ break;
+ case PersistentListenerMode.Object:
+ argument = arguments.FindPropertyRelative(kObjectArgument);
+ break;
+ case PersistentListenerMode.String:
+ argument = arguments.FindPropertyRelative(kStringArgument);
+ break;
+ case PersistentListenerMode.Bool:
+ argument = arguments.FindPropertyRelative(kBoolArgument);
+ break;
+ default:
+ argument = arguments.FindPropertyRelative(kIntArgument);
+ break;
+ }
+
+ return argument;
+ }
+
+ public override VisualElement CreatePropertyGUI(SerializedProperty property)
+ {
+ m_Prop = property;
+ m_Text = property.displayName;
+ m_DummyEvent = GetDummyEvent(m_Prop);
+
+ var listViewContainer = new VisualElement();
+ listViewContainer.AddToClassList(kContainerClassName);
+
+ var header = new Label();
+ header.text = GetHeaderText();
+ header.AddToClassList(kHeaderClassName);
+
+ var listView = CreateListView(property);
+ listView.scrollView.AddToClassList(kListViewScrollViewClassName);
+
+ listViewContainer.Add(header);
+ listViewContainer.Add(listView);
+
+ return listViewContainer;
+ }
+
+ private float GetElementHeight()
+ {
+ return EditorGUI.kSingleLineHeight * 2 + EditorGUI.kControlVerticalSpacing + kExtraSpacing;
+ }
+
+ private string GetHeaderText()
+ {
+ return (string.IsNullOrEmpty(m_Text) ? "Event" : m_Text) + GetEventParams(m_DummyEvent);
+ }
+
+ private string GetFunctionDropdownText(SerializedProperty pListener)
+ {
+ var listenerTarget = pListener.FindPropertyRelative(kInstancePath);
+ var methodName = pListener.FindPropertyRelative(kMethodNamePath);
+ var mode = pListener.FindPropertyRelative(kModePath);
+ var arguments = pListener.FindPropertyRelative(kArgumentsPath);
+ var desiredArgTypeName = arguments.FindPropertyRelative(kObjectArgumentAssemblyTypeName).stringValue;
+ var desiredType = typeof(Object);
+ if (!string.IsNullOrEmpty(desiredArgTypeName))
+ desiredType = Type.GetType(desiredArgTypeName, false) ?? typeof(Object);
+
+ var buttonLabel = new StringBuilder();
+ if (listenerTarget.objectReferenceValue == null || string.IsNullOrEmpty(methodName.stringValue))
+ {
+ buttonLabel.Append(kNoFunctionString);
+ }
+ else if (!IsPersistantListenerValid(m_DummyEvent, methodName.stringValue, listenerTarget.objectReferenceValue, GetMode(mode), desiredType))
+ {
+ var instanceString = "UnknownComponent";
+ var instance = listenerTarget.objectReferenceValue;
+ if (instance != null)
+ instanceString = instance.GetType().Name;
+
+ buttonLabel.Append(string.Format("", instanceString, methodName.stringValue));
+ }
+ else
+ {
+ buttonLabel.Append(listenerTarget.objectReferenceValue.GetType().Name);
+
+ if (!string.IsNullOrEmpty(methodName.stringValue))
+ {
+ buttonLabel.Append(".");
+ if (methodName.stringValue.StartsWith("set_"))
+ buttonLabel.Append(methodName.stringValue.Substring(4));
+ else
+ buttonLabel.Append(methodName.stringValue);
+ }
+ }
+
+ return buttonLabel.ToString();
+ }
+
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
//TODO: Also we need to have a constructor or initializer called for this property Drawer, before OnGUI or GetPropertyHeight
@@ -167,13 +361,13 @@ public void OnGUI(Rect position)
protected virtual void SetupReorderableList(ReorderableList list)
{
// Two standard lines with standard spacing between and extra spacing below to better separate items visually.
- list.elementHeight = EditorGUI.kSingleLineHeight * 2 + EditorGUI.kControlVerticalSpacing + kExtraSpacing;
+ list.elementHeight = GetElementHeight();
}
protected virtual void DrawEventHeader(Rect headerRect)
{
headerRect.height = EditorGUI.kSingleLineHeight;
- string text = (string.IsNullOrEmpty(m_Text) ? "Event" : m_Text) + GetEventParams(m_DummyEvent);
+ string text = GetHeaderText();
GUI.Label(headerRect, text);
}
@@ -213,34 +407,12 @@ protected virtual void DrawEvent(Rect rect, int index, bool isActive, bool isFoc
methodName.stringValue = null;
}
- SerializedProperty argument;
+ SerializedProperty argument = GetArgument(pListener);
var modeEnum = GetMode(mode);
//only allow argument if we have a valid target / method
if (listenerTarget.objectReferenceValue == null || string.IsNullOrEmpty(methodName.stringValue))
modeEnum = PersistentListenerMode.Void;
- switch (modeEnum)
- {
- case PersistentListenerMode.Float:
- argument = arguments.FindPropertyRelative(kFloatArgument);
- break;
- case PersistentListenerMode.Int:
- argument = arguments.FindPropertyRelative(kIntArgument);
- break;
- case PersistentListenerMode.Object:
- argument = arguments.FindPropertyRelative(kObjectArgument);
- break;
- case PersistentListenerMode.String:
- argument = arguments.FindPropertyRelative(kStringArgument);
- break;
- case PersistentListenerMode.Bool:
- argument = arguments.FindPropertyRelative(kBoolArgument);
- break;
- default:
- argument = arguments.FindPropertyRelative(kIntArgument);
- break;
- }
-
var desiredArgTypeName = arguments.FindPropertyRelative(kObjectArgumentAssemblyTypeName).stringValue;
var desiredType = typeof(Object);
if (!string.IsNullOrEmpty(desiredArgTypeName))
@@ -267,33 +439,7 @@ protected virtual void DrawEvent(Rect rect, int index, bool isActive, bool isFoc
}
else
{
- var buttonLabel = new StringBuilder();
- if (listenerTarget.objectReferenceValue == null || string.IsNullOrEmpty(methodName.stringValue))
- {
- buttonLabel.Append(kNoFunctionString);
- }
- else if (!IsPersistantListenerValid(m_DummyEvent, methodName.stringValue, listenerTarget.objectReferenceValue, GetMode(mode), desiredType))
- {
- var instanceString = "UnknownComponent";
- var instance = listenerTarget.objectReferenceValue;
- if (instance != null)
- instanceString = instance.GetType().Name;
-
- buttonLabel.Append(string.Format("", instanceString, methodName.stringValue));
- }
- else
- {
- buttonLabel.Append(listenerTarget.objectReferenceValue.GetType().Name);
-
- if (!string.IsNullOrEmpty(methodName.stringValue))
- {
- buttonLabel.Append(".");
- if (methodName.stringValue.StartsWith("set_"))
- buttonLabel.Append(methodName.stringValue.Substring(4));
- else
- buttonLabel.Append(methodName.stringValue);
- }
- }
+ var buttonLabel = GetFunctionDropdownText(pListener);
buttonContent = GUIContent.Temp(buttonLabel.ToString());
}
diff --git a/Editor/Mono/Inspector/VisualElements/MinMaxGradientField.cs b/Editor/Mono/Inspector/VisualElements/MinMaxGradientField.cs
new file mode 100644
index 0000000000..ea530d83e4
--- /dev/null
+++ b/Editor/Mono/Inspector/VisualElements/MinMaxGradientField.cs
@@ -0,0 +1,123 @@
+// Unity C# reference source
+// Copyright (c) Unity Technologies. For terms of use, see
+// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
+
+using System;
+using UnityEditorInternal;
+using UnityEngine;
+using UnityEngine.UIElements;
+
+namespace UnityEditor.UIElements
+{
+ internal class MinMaxGradientField : BaseField
+ {
+ public new static readonly string ussClassName = "unity-min-max-gradient-field";
+ public static readonly string visualInputUssClass = ussClassName + "__visual-input";
+ public static readonly string dropdownFieldUssClass = ussClassName + "__dropdown-field";
+ public static readonly string dropdownInputUssClass = ussClassName + "__dropdown-input";
+ public static readonly string gradientContainerUssClass = ussClassName + "__gradient-container";
+ public static readonly string colorFieldUssClass = ussClassName + "__color-field";
+ public static readonly string multipleValuesLabelUssClass = ussClassName + "__multiple-values-label";
+
+ public readonly string[] stringModes = new[]
+ {
+ L10n.Tr("Color"),
+ L10n.Tr("Gradient"),
+ L10n.Tr("Random Between Two Colors"),
+ L10n.Tr("Random Between Two Gradients"),
+ L10n.Tr("Random Color")
+ };
+
+ VisualElement m_ColorMin;
+ VisualElement m_ColorMax;
+ VisualElement m_GradientMin;
+ VisualElement m_GradientMax;
+ VisualElement m_ModeDropdown;
+ VisualElement m_GradientsContainer;
+ VisualElement m_MixedValueTypeLabel;
+
+ public MinMaxGradientField()
+ : this(null, null) { }
+
+ public MinMaxGradientField(MinMaxGradientPropertyDrawer.PropertyData propertyData, string label) : base(label, null)
+ {
+ m_ColorMin = EditorUIService.instance.CreatePropertyField(propertyData.colorMin, "");
+ m_ColorMin.AddToClassList(colorFieldUssClass);
+ m_ColorMax = EditorUIService.instance.CreatePropertyField(propertyData.colorMax, "");
+ m_ColorMax.AddToClassList(colorFieldUssClass);
+ m_GradientMin = EditorUIService.instance.CreatePropertyField(propertyData.gradientMin, "");
+ m_GradientMax = EditorUIService.instance.CreatePropertyField(propertyData.gradientMax, "");
+ m_ModeDropdown = EditorUIService.instance.CreateDropdownField(stringModes, () => propertyData.mode.intValue);
+ m_ModeDropdown.AddToClassList(dropdownFieldUssClass);
+ m_MixedValueTypeLabel = new Label("\u2014");
+ m_MixedValueTypeLabel.AddToClassList(multipleValuesLabelUssClass);
+
+ var dropdownInput = m_ModeDropdown.Q(null, "unity-popup-field__input");
+ dropdownInput.AddToClassList(dropdownInputUssClass);
+
+ m_GradientsContainer = new VisualElement();
+ m_GradientsContainer.AddToClassList(gradientContainerUssClass);
+ m_GradientsContainer.Add(m_GradientMin);
+ m_GradientsContainer.Add(m_GradientMax);
+
+ visualInput.AddToClassList(visualInputUssClass);
+ visualInput.Add(m_ColorMin);
+ visualInput.Add(m_ColorMax);
+ visualInput.Add(m_GradientsContainer);
+ visualInput.Add(m_MixedValueTypeLabel);
+ visualInput.Add(m_ModeDropdown);
+
+ m_ModeDropdown.RegisterCallback>(e =>
+ {
+ var index = Array.IndexOf(stringModes, e.newValue);
+ var mode = (MinMaxGradientState)index;
+
+ propertyData.mode.intValue = index;
+ propertyData.mode.serializedObject.ApplyModifiedProperties();
+ UpdateFieldsDisplay(propertyData.mode);
+ });
+
+ UpdateFieldsDisplay(propertyData.mode);
+ }
+
+ private void UpdateFieldsDisplay(SerializedProperty mode)
+ {
+ var hasMixedValues = mode.hasMultipleDifferentValues;
+
+ m_ColorMin.style.display = DisplayStyle.None;
+ m_ColorMax.style.display = DisplayStyle.None;
+ m_GradientMin.style.display = DisplayStyle.None;
+ m_GradientMax.style.display = DisplayStyle.None;
+ m_GradientsContainer.style.display = DisplayStyle.None;
+ m_MixedValueTypeLabel.style.display = hasMixedValues ? DisplayStyle.Flex : DisplayStyle.None;
+
+ if (hasMixedValues)
+ {
+ return;
+ }
+
+ var modeValue = (MinMaxGradientState)mode.intValue;
+
+ switch (modeValue)
+ {
+ case MinMaxGradientState.k_Color:
+ m_ColorMax.style.display = DisplayStyle.Flex;
+ break;
+ case MinMaxGradientState.k_Gradient:
+ case MinMaxGradientState.k_RandomColor:
+ m_GradientsContainer.style.display = DisplayStyle.Flex;
+ m_GradientMax.style.display = DisplayStyle.Flex;
+ break;
+ case MinMaxGradientState.k_RandomBetweenTwoColors:
+ m_ColorMin.style.display = DisplayStyle.Flex;
+ m_ColorMax.style.display = DisplayStyle.Flex;
+ break;
+ case MinMaxGradientState.k_RandomBetweenTwoGradients:
+ m_GradientsContainer.style.display = DisplayStyle.Flex;
+ m_GradientMin.style.display = DisplayStyle.Flex;
+ m_GradientMax.style.display = DisplayStyle.Flex;
+ break;
+ }
+ }
+ }
+}
diff --git a/Editor/Mono/InternalEditorUtility.cs b/Editor/Mono/InternalEditorUtility.cs
index 4c82e8bdd2..5cedce2850 100644
--- a/Editor/Mono/InternalEditorUtility.cs
+++ b/Editor/Mono/InternalEditorUtility.cs
@@ -295,8 +295,10 @@ internal static List GetNewSelection(ref AssetReference clickedEntry, List<
if (!allowMultiSelection)
useShift = useActionKey = false;
+ int firstIndex;
+ int lastIndex;
// Toggle selected node from selection
- if (useActionKey)
+ if (useActionKey && !useShift)
{
var newSelection = new List(selectedInstanceIDs);
if (newSelection.Contains(clickedEntry.instanceID))
@@ -320,8 +322,6 @@ internal static List GetNewSelection(ref AssetReference clickedEntry, List<
return new List(selectedInstanceIDs);
}
- int firstIndex;
- int lastIndex;
if (!GetFirstAndLastSelected(allEntryInstanceIDs, selectedInstanceIDs, out firstIndex, out lastIndex))
{
// We had no selection
@@ -371,30 +371,48 @@ internal static List GetNewSelection(ref AssetReference clickedEntry, List<
var addExisting = false;
bool usingArrowKeys = Event.current != null ? Event.current.keyCode == KeyCode.DownArrow || Event.current.keyCode == KeyCode.UpArrow : false;
+ var clickedInTheMiddle = lastIndex > newIndex && firstIndex < newIndex;
if (selectedInstanceIDs.Count > 1)
{
- var clicked = allEntryInstanceIDs.IndexOf(clickedInstanceID);
- if (dir > 0)
+ var clickedID = allEntryInstanceIDs[newIndex];
+ var noGapsInSelection = (allEntryInstanceIDs.Count - firstIndex + selectedInstanceIDs.Count) == allEntryInstanceIDs.Count - lastIndex;
+ var isInSelection = selectedInstanceIDs.Contains(clickedID);
+ // if the newly clicked item is already selected,
+ // we treat this as a combination of selecting items from the highest selected item to the clicked item
+ // or from the lowest selected item to the clicked item depending on the direction of the selection,
+ // e.g. if we select item 1 and shift-select item 5, then shift-select item 3, we'll have items 1 to 3 selected
+ if (isInSelection || noGapsInSelection || clickedInTheMiddle)
{
- if (clicked > lastIndex)
+ from = dir > 0 ? firstIndex : newIndex;
+ to = dir > 0 ? newIndex : lastIndex;
+
+ // if we clicked in-between the lowest and highest selected indices of a selection containing gaps
+ // and the item was not already in the selection
+ // make sure that the new selection is added to the currently existing one
+ if (clickedInTheMiddle && !noGapsInSelection && !isInSelection)
+ addExisting = true;
+ }
+ else if (dir > 0)
+ {
+ if (newIndex > lastIndex)
{
from = lastIndex + 1;
- to = clicked;
+ to = newIndex;
addExisting = true;
}
else
{
- from = clicked;
+ from = newIndex;
to = lastIndex;
}
}
else if (dir < 0)
{
- if (clicked < firstIndex)
+ if (newIndex < firstIndex)
{
- from = clicked;
+ from = newIndex;
to = firstIndex - 1;
addExisting = true;
@@ -402,7 +420,7 @@ internal static List GetNewSelection(ref AssetReference clickedEntry, List<
else
{
from = firstIndex;
- to = clicked;
+ to = newIndex;
}
}
}
@@ -434,8 +452,6 @@ internal static List GetNewSelection(ref AssetReference clickedEntry, List<
}
}
- // Outcomment to debug
- //Debug.Log (clickedEntry + ", firstIndex " + firstIndex + ", lastIndex " + lastIndex + ", newIndex " + newIndex + " " + ", lastClickedIndex " + prevIndex + ", from " + from + ", to " + to);
if (allEntryGuids == null)
{
List allSelectedInstanceIDs = new List();
@@ -444,6 +460,9 @@ internal static List GetNewSelection(ref AssetReference clickedEntry, List<
{
allSelectedInstanceIDs.AddRange(selectedInstanceIDs.GetRange(0, selectedInstanceIDs.Count));
allSelectedInstanceIDs.AddRange(allEntryInstanceIDs.GetRange(from, to - from + 1));
+
+ if (clickedInTheMiddle)
+ allSelectedInstanceIDs = allSelectedInstanceIDs.Distinct().ToList();
}
else
{
@@ -460,11 +479,13 @@ internal static List GetNewSelection(ref AssetReference clickedEntry, List<
// in the last entry being duplicated later on
// thus when selecting upwards we add the existing selection - 1;
// when selecting downwards don't add the last index from the new selection
- if (addExisting || !usingArrowKeys)
+ if (addExisting && !usingArrowKeys)
{
List allSelectedInstanceIDs = new List();
allSelectedInstanceIDs.AddRange(selectedInstanceIDs.GetRange(0, selectedInstanceIDs.Count));
allSelectedInstanceIDs.AddRange(allEntryInstanceIDs.GetRange(from, to - from + 1));
+ if (clickedInTheMiddle)
+ allSelectedInstanceIDs = allSelectedInstanceIDs.Distinct().ToList();
return allSelectedInstanceIDs;
}
diff --git a/Editor/Mono/Media/Bindings/MediaDecoder.bindings.cs b/Editor/Mono/Media/Bindings/MediaDecoder.bindings.cs
index 76456d45d8..d29afbe895 100644
--- a/Editor/Mono/Media/Bindings/MediaDecoder.bindings.cs
+++ b/Editor/Mono/Media/Bindings/MediaDecoder.bindings.cs
@@ -36,11 +36,13 @@ public MediaDecoder(VideoClip clip)
public bool GetNextFrame(Texture2D tex, out MediaTime time)
{
+ ThrowIfDisposed();
return Internal_MediaDecoder_GetNextFrame(m_Ptr, tex, out time);
}
public int GetNextSamples(ushort trackIndex, NativeArray interleavedSamples)
{
+ ThrowIfDisposed();
unsafe
{
return Internal_MediaDecoder_GetNextSamples(
@@ -50,11 +52,13 @@ public int GetNextSamples(ushort trackIndex, NativeArray interleavedSampl
public bool SetPosition(MediaTime time)
{
+ ThrowIfDisposed();
return Internal_MediaDecoder_SetPosition(m_Ptr, time);
}
public string[] GetCustomDependencies()
{
+ ThrowIfDisposed();
return Internal_MediaDecoder_GetCustomDependencies(m_Ptr);
}
@@ -86,6 +90,12 @@ private IntPtr Create(VideoClip clip)
return ptr;
}
+ private void ThrowIfDisposed()
+ {
+ if (m_Ptr == IntPtr.Zero)
+ throw new ObjectDisposedException("MediaDecoder");
+ }
+
[NativeHeader("Editor/Mono/Media/Bindings/MediaDecoder.bindings.h")]
[FreeFunction]
extern private static IntPtr Internal_MediaDecoder_Create(string filePath);
diff --git a/Editor/Mono/Media/Bindings/MediaEncoder.bindings.cs b/Editor/Mono/Media/Bindings/MediaEncoder.bindings.cs
index f1fab06483..37c373658d 100644
--- a/Editor/Mono/Media/Bindings/MediaEncoder.bindings.cs
+++ b/Editor/Mono/Media/Bindings/MediaEncoder.bindings.cs
@@ -243,6 +243,7 @@ public MediaEncoder(string filePath, AudioTrackAttributes audioAttrs)
unsafe public bool AddFrame(
int width, int height, int rowBytes, TextureFormat format, NativeArray data)
{
+ ThrowIfDisposed();
return Internal_AddFrameRaw(
m_ThisPtr, width, height, rowBytes, format, data.GetUnsafeReadOnlyPtr(), data.Length, MediaTime.Invalid);
}
@@ -250,22 +251,26 @@ unsafe public bool AddFrame(
unsafe public bool AddFrame(
int width, int height, int rowBytes, TextureFormat format, NativeArray data, MediaTime time)
{
+ ThrowIfDisposed();
return Internal_AddFrameRaw(
m_ThisPtr, width, height, rowBytes, format, data.GetUnsafeReadOnlyPtr(), data.Length, time);
}
public bool AddFrame(Texture2D texture)
{
+ ThrowIfDisposed();
return Internal_AddFrame(m_ThisPtr, texture, MediaTime.Invalid);
}
public bool AddFrame(Texture2D texture, MediaTime time)
{
+ ThrowIfDisposed();
return Internal_AddFrame(m_ThisPtr, texture, time);
}
unsafe public bool AddSamples(ushort trackIndex, NativeArray interleavedSamples)
{
+ ThrowIfDisposed();
return Internal_AddSamples(
m_ThisPtr, trackIndex, interleavedSamples.GetUnsafeReadOnlyPtr(),
interleavedSamples.Length);
@@ -349,6 +354,12 @@ private IntPtr Create(
}
}
+ private void ThrowIfDisposed()
+ {
+ if (m_ThisPtr == IntPtr.Zero)
+ throw new ObjectDisposedException("MediaEncoder");
+ }
+
[FreeFunction]
extern private unsafe static IntPtr Internal_Create(
string filePath, void* videoAttrs, AudioTrackAttributes[] audioAttrs);
diff --git a/Editor/Mono/PlayModeView/PlayModeView.cs b/Editor/Mono/PlayModeView/PlayModeView.cs
index cd95481698..d4fbe5b3d0 100644
--- a/Editor/Mono/PlayModeView/PlayModeView.cs
+++ b/Editor/Mono/PlayModeView/PlayModeView.cs
@@ -6,6 +6,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using Unity.Collections;
using UnityEditor.Modules;
using UnityEditorInternal;
using UnityEngine;
@@ -26,7 +27,9 @@ internal static void RepaintAll()
internal abstract class PlayModeView : EditorWindow
{
static List s_PlayModeViews = new List();
- static PlayModeView s_LastFocused;
+
+ private static PlayModeView s_LastFocused;
+
static PlayModeView s_RenderingView;
private readonly string m_ViewsCache = Path.GetFullPath(Directory.GetCurrentDirectory() + "/Library/PlayModeViewStates/");
@@ -43,7 +46,13 @@ internal abstract class PlayModeView : EditorWindow
[SerializeField] HideFlags m_TextureHideFlags = HideFlags.HideAndDontSave;
[SerializeField] bool m_RenderIMGUI;
[SerializeField] EnterPlayModeBehavior m_EnterPlayModeBehavior;
+ [SerializeField] int m_fullscreenMonitorIdx = 0;
+ [SerializeField] int m_playModeBehaviorIdx = 0;
[SerializeField] bool m_UseMipMap;
+ [SerializeField] bool m_isFullscreen;
+ [SerializeField] bool m_suppressRenderingForFullscreen;
+
+ private const int k_MaxSupportedDisplays = 8;
private Dictionary m_AvailableWindowTypes;
@@ -62,14 +71,11 @@ protected bool showGizmos
}
}
- protected int targetDisplay
+ public int targetDisplay
{
get { return m_TargetDisplay; }
- set
+ protected set
{
- if (m_TargetDisplay != value)
- SetDisplayViewSize(value, m_TargetSize);
-
m_TargetDisplay = value;
}
}
@@ -80,7 +86,7 @@ protected Color clearColor
set { m_ClearColor = value; }
}
- protected Vector2 targetSize
+ internal Vector2 targetSize
{
get { return m_TargetSize; }
set
@@ -119,6 +125,21 @@ public EnterPlayModeBehavior enterPlayModeBehavior
set => SetPlayModeWindowsStates(value);
}
+ public const int kFullscreenInvalidIdx = -1;
+ public const int kFullscreenNone = 0;
+
+ public int fullscreenMonitorIdx
+ {
+ get => m_fullscreenMonitorIdx;
+ set => m_fullscreenMonitorIdx = value;
+ }
+
+ public int playModeBehaviorIdx
+ {
+ get => m_playModeBehaviorIdx;
+ set => m_playModeBehaviorIdx = value;
+ }
+
[Obsolete("PlayModeView.maximizeOnPlay is obsolete. Use PlayModeView.enterPlayModeBehavior instead")]
public bool maximizeOnPlay
{
@@ -126,6 +147,21 @@ public bool maximizeOnPlay
set { m_EnterPlayModeBehavior = value ? EnterPlayModeBehavior.PlayMaximized : EnterPlayModeBehavior.PlayFocused; }
}
+ public bool isFullscreen
+ {
+ get { return m_isFullscreen; }
+ set { m_isFullscreen = value; }
+ }
+
+ internal bool suppressRenderingForFullscreen
+ {
+ get { return m_suppressRenderingForFullscreen; }
+ set
+ {
+ m_suppressRenderingForFullscreen = value;
+ }
+ }
+
protected bool useMipMap
{
get { return m_UseMipMap; }
@@ -190,11 +226,14 @@ protected RenderTexture RenderView(Vector2 mousePosition, bool clearTexture)
using (var renderingView = new RenderingView(this))
{
SetPlayModeViewSize(targetSize);
+ // This should be called configure virtual display or sth
+ EditorDisplayUtility.AddVirtualDisplay(targetDisplay, (int)targetSize.x, (int)targetSize.y);
+ // EditorDisplayManager.UpdateVirtualDisplay(this);
var currentTargetDisplay = 0;
if (ModuleManager.ShouldShowMultiDisplayOption())
{
// Display Targets can have valid targets from 0 to 7.
- System.Diagnostics.Debug.Assert(targetDisplay < 8, "Display Target is Out of Range");
+ System.Diagnostics.Debug.Assert(targetDisplay < k_MaxSupportedDisplays, "Display Target is Out of Range");
currentTargetDisplay = targetDisplay;
}
@@ -252,6 +291,8 @@ protected void SwapMainWindow(Type type)
throw new ArgumentException("Type should derive from " + typeof(PlayModeView).Name);
if (type.Name != GetType().Name)
{
+ EditorFullscreenController.SetMainDisplayPlayModeViewType(type);
+
var serializedViews = ListsToDictionary(m_SerializedViewNames, m_SerializedViewValues);
// Clear serialized views so they wouldn't be serialized again
@@ -369,6 +410,11 @@ internal static PlayModeView GetMainPlayModeView()
return s_LastFocused;
}
+ internal static List GetAllPlayModeViewWindows()
+ {
+ return s_PlayModeViews;
+ }
+
internal static PlayModeView GetCorrectPlayModeViewToFocus()
{
if (s_PlayModeViews != null)
@@ -395,7 +441,6 @@ private static void RemoveDisabledWindows()
{
if (s_PlayModeViews == null)
return;
-
s_PlayModeViews.RemoveAll(window => window == null);
}
@@ -451,11 +496,27 @@ protected void SetFocus(bool focused)
m_Parent.SetMainPlayModeViewSize(targetSize);
Display.activeEditorGameViewTarget = m_TargetDisplay;
s_LastFocused = this;
+ // AddLastView(s_LastFocused);
Repaint();
}
SetDisplayViewSize(m_TargetDisplay, m_TargetSize);
}
+ internal virtual void ApplyEditorDisplayFullscreenSetting(IPlayModeViewFullscreenSettings settings)
+ {
+ m_isFullscreen = true;
+
+ if (ModuleManager.ShouldShowMultiDisplayOption())
+ {
+ if (targetDisplay != settings.DisplayNumber)
+ {
+ targetDisplay = settings.DisplayNumber;
+ }
+ }
+
+ SetVSync(settings.VsyncEnabled);
+ }
+
[RequiredByNativeCode]
internal static void IsPlayModeViewOpen(out bool isPlayModeViewOpen)
{
@@ -475,22 +536,55 @@ public enum EnterPlayModeBehavior
{
PlayFocused,
PlayMaximized,
- PlayUnfocused
+ PlayUnfocused,
+ PlayFullscreen
}
void SetPlayModeWindowsStates(EnterPlayModeBehavior behavior)
{
- if (m_EnterPlayModeBehavior == behavior)
- return;
+ var isFullscreen = (behavior == EnterPlayModeBehavior.PlayFullscreen);
+
+ this.m_EnterPlayModeBehavior = behavior;
+ this.fullscreenMonitorIdx = isFullscreen
+ ? GameViewOnPlayMenu.SelectedIndexToDisplayIndex(this.playModeBehaviorIdx)
+ : -1;
foreach (var view in s_PlayModeViews)
{
- view.m_EnterPlayModeBehavior = view == this ? behavior : EnterPlayModeBehavior.PlayUnfocused;
+ if (view == this)
+ {
+ continue;
+ }
+ if (behavior == EnterPlayModeBehavior.PlayMaximized && view.m_EnterPlayModeBehavior == EnterPlayModeBehavior.PlayMaximized)
+ {
+ // Only one play mode view can be maximized at a time.
+ view.m_EnterPlayModeBehavior = EnterPlayModeBehavior.PlayUnfocused;
+ view.playModeBehaviorIdx = 0;
+ view.fullscreenMonitorIdx = PlayModeView.kFullscreenNone;
+ }
+ else if (behavior == EnterPlayModeBehavior.PlayFullscreen && view.m_EnterPlayModeBehavior == EnterPlayModeBehavior.PlayFullscreen)
+ {
+ // We can have multiple fullscreen views, so long as they're not on the same monitor
+ if (this.fullscreenMonitorIdx == view.fullscreenMonitorIdx)
+ {
+ view.m_EnterPlayModeBehavior = EnterPlayModeBehavior.PlayUnfocused;
+ view.playModeBehaviorIdx = 0;
+ view.fullscreenMonitorIdx = PlayModeView.kFullscreenNone;
+ }
+ }
+
view.OnEnterPlayModeBehaviorChange();
view.Repaint();
}
}
protected virtual void OnEnterPlayModeBehaviorChange() {}
+ internal static PlayModeView GetAssociatedViewForTargetDisplay(int targetDisplay)
+ {
+ return s_PlayModeViews.Where(v => v.targetDisplay == targetDisplay && !v.m_suppressRenderingForFullscreen)
+ .OrderByDescending(v => v.isFullscreen)
+ .ThenByDescending(v => v.hasFocus)
+ .FirstOrDefault();
+ }
}
}
diff --git a/Editor/Mono/PlayerSettings.bindings.cs b/Editor/Mono/PlayerSettings.bindings.cs
index 22af9efea7..4b068ba447 100644
--- a/Editor/Mono/PlayerSettings.bindings.cs
+++ b/Editor/Mono/PlayerSettings.bindings.cs
@@ -1529,8 +1529,6 @@ public static bool useDirect3D11
// Defines whether the BlendShape weight range in SkinnedMeshRenderers is clamped
public static extern bool legacyClampBlendShapeWeights { get; set; }
- public static extern bool uploadClearedTextureDataAfterCreationFromScript { get; set; }
-
// If enabled, metal API validation will be turned on in the editor
[NativeProperty("MetalAPIValidation")]
public static extern bool enableMetalAPIValidation
diff --git a/Editor/Mono/PlayerSettingsSwitch.bindings.cs b/Editor/Mono/PlayerSettingsSwitch.bindings.cs
index 4a2d896f03..d210b19bd2 100644
--- a/Editor/Mono/PlayerSettingsSwitch.bindings.cs
+++ b/Editor/Mono/PlayerSettingsSwitch.bindings.cs
@@ -315,6 +315,21 @@ public static string NMETAOverrideFullPath
}
}
+ public static string[] compilerFlags
+ {
+ get
+ {
+ return compilerFlagsInternal.Split(new char[] {' '});
+ }
+ set
+ {
+ compilerFlagsInternal = string.Join(" ", value);
+ }
+ }
+
+ [NativeProperty("switchCompilerFlags", TargetType.Function)]
+ extern private static string compilerFlagsInternal { get; set; }
+
//Application ID (shows up in Application meta file)
[NativeProperty("switchApplicationID", TargetType.Function)]
extern public static string applicationID { get; set; }
diff --git a/Editor/Mono/PlayerSettingsWebGL.bindings.cs b/Editor/Mono/PlayerSettingsWebGL.bindings.cs
index f296ecb758..ff43d35507 100644
--- a/Editor/Mono/PlayerSettingsWebGL.bindings.cs
+++ b/Editor/Mono/PlayerSettingsWebGL.bindings.cs
@@ -45,6 +45,13 @@ public enum WebGLDebugSymbolMode
Embedded = 2
}
+ public enum WebGLMemoryGrowthMode
+ {
+ None = 0,
+ Linear = 1,
+ Geometric = 2
+ }
+
public sealed partial class PlayerSettings : UnityEngine.Object
{
[NativeHeader("Editor/Mono/PlayerSettingsWebGL.bindings.h")]
@@ -176,6 +183,48 @@ public extern static WebGLWasmArithmeticExceptions wasmArithmeticExceptions
[StaticAccessor("GetPlayerSettings().GetEditorOnly()", StaticAccessorType.Dot)] get;
[StaticAccessor("GetPlayerSettings().GetEditorOnlyForUpdate()", StaticAccessorType.Dot)] set;
}
+
+ [NativeProperty("webGLInitialMemorySize", TargetType.Field)]
+ public extern static int initialMemorySize
+ {
+ [StaticAccessor("GetPlayerSettings().GetEditorOnly()", StaticAccessorType.Dot)] get;
+ [StaticAccessor("GetPlayerSettings().GetEditorOnlyForUpdate()", StaticAccessorType.Dot)] set;
+ }
+
+ [NativeProperty("webGLMaximumMemorySize", TargetType.Field)]
+ public extern static int maximumMemorySize
+ {
+ [StaticAccessor("GetPlayerSettings().GetEditorOnly()", StaticAccessorType.Dot)] get;
+ [StaticAccessor("GetPlayerSettings().GetEditorOnlyForUpdate()", StaticAccessorType.Dot)] set;
+ }
+
+ [NativeProperty("webGLMemoryGrowthMode", TargetType.Field)]
+ public extern static WebGLMemoryGrowthMode memoryGrowthMode
+ {
+ [StaticAccessor("GetPlayerSettings().GetEditorOnly()", StaticAccessorType.Dot)] get;
+ [StaticAccessor("GetPlayerSettings().GetEditorOnlyForUpdate()", StaticAccessorType.Dot)] set;
+ }
+
+ [NativeProperty("webGLMemoryLinearGrowthStep", TargetType.Field)]
+ public extern static int linearMemoryGrowthStep
+ {
+ [StaticAccessor("GetPlayerSettings().GetEditorOnly()", StaticAccessorType.Dot)] get;
+ [StaticAccessor("GetPlayerSettings().GetEditorOnlyForUpdate()", StaticAccessorType.Dot)] set;
+ }
+
+ [NativeProperty("webGLMemoryGeometricGrowthStep", TargetType.Field)]
+ public extern static float geometricMemoryGrowthStep
+ {
+ [StaticAccessor("GetPlayerSettings().GetEditorOnly()", StaticAccessorType.Dot)] get;
+ [StaticAccessor("GetPlayerSettings().GetEditorOnlyForUpdate()", StaticAccessorType.Dot)] set;
+ }
+
+ [NativeProperty("webGLMemoryGeometricGrowthCap", TargetType.Field)]
+ public extern static int memoryGeometricGrowthCap
+ {
+ [StaticAccessor("GetPlayerSettings().GetEditorOnly()", StaticAccessorType.Dot)] get;
+ [StaticAccessor("GetPlayerSettings().GetEditorOnlyForUpdate()", StaticAccessorType.Dot)] set;
+ }
}
}
}
diff --git a/Editor/Mono/Prefabs/PrefabInstanceChangedListener.cs b/Editor/Mono/Prefabs/PrefabInstanceChangedListener.cs
index b1a948230b..cfea25586e 100644
--- a/Editor/Mono/Prefabs/PrefabInstanceChangedListener.cs
+++ b/Editor/Mono/Prefabs/PrefabInstanceChangedListener.cs
@@ -109,6 +109,7 @@ private static void OnGameObjectChanged(GameObject go)
return;
PrefabUtility.ClearPrefabInstanceNonDefaultOverridesCache(go);
+ PrefabUtility.ClearPrefabInstanceUnusedOverridesCache(go);
}
}
}
diff --git a/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesWindow.cs b/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesWindow.cs
index fdd4bbcd02..387308d12f 100644
--- a/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesWindow.cs
+++ b/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesWindow.cs
@@ -8,20 +8,23 @@
using UnityEngine;
using UnityEditor.IMGUI.Controls;
using UnityEditor.SceneManagement;
+using System.Text;
+using static UnityEditor.GameObjectTreeViewGUI;
namespace UnityEditor
{
internal class PrefabOverridesWindow : PopupWindowContent
{
RectOffset k_TreeViewPadding = new RectOffset(0, 0, 4, 4);
- const float k_HeaderHeight = 40f;
+ const float k_HeaderHeight = 60f;
const float k_ButtonWidth = 120;
const float k_ButtonWidthVariant = 135;
const float k_HeaderLeftMargin = 6;
const float k_NoOverridesLabelHeight = 26f;
+ const float k_UnusedOverridesButtonHeight = 26f;
const float k_ApplyButtonHeight = 36f;
const float k_HelpBoxHeight = 40f;
-
+ const float k_RowPadding = 6;
GameObject[] m_SelectedGameObjects = null;
// TreeView not used when there are multiple Prefabs.
@@ -34,8 +37,8 @@ internal class PrefabOverridesWindow : PopupWindowContent
GUIContent m_ApplyAllContent = new GUIContent();
GUIContent m_RevertSelectedContent = new GUIContent();
GUIContent m_ApplySelectedContent = new GUIContent();
- float m_ButtonWidth;
+ float m_ButtonWidth;
bool m_AnyOverrides;
bool m_HasApplicableOverrides;
bool m_InvalidComponentOnInstance;
@@ -43,7 +46,7 @@ internal class PrefabOverridesWindow : PopupWindowContent
bool m_Immutable;
bool m_InvalidComponentOnAsset;
bool m_HasManagedReferencesWithMissingTypesOnAsset;
-
+ bool m_UnusedOverridesExist;
static class Styles
{
public static GUIContent revertAllContent = EditorGUIUtility.TrTextContent("Revert All", "Revert all overrides.");
@@ -52,8 +55,12 @@ static class Styles
public static GUIContent applySelectedContent = EditorGUIUtility.TrTextContent("Apply Selected", "Apply selected overrides to Prefab source '{0}'.");
public static GUIContent applyAllToBaseContent = EditorGUIUtility.TrTextContent("Apply All to Base", "Apply all overrides to base Prefab source '{0}'.");
public static GUIContent applySelectedToBaseContent = EditorGUIUtility.TrTextContent("Apply Selected to Base", "Apply selected overrides to base Prefab source '{0}'.");
- public static GUIContent instanceLabel = EditorGUIUtility.TrTextContent("Overrides to");
+ public static GUIContent titleLabelDefault = EditorGUIUtility.TrTextContent("Review, Revert or Apply Overrides");
+ public static GUIContent titleLabelNoApply = EditorGUIUtility.TrTextContent("Review or Revert Overrides");
+ public static GUIContent noOverridesText = EditorGUIUtility.TrTextContent("No overrides");
+ public static GUIContent instanceLabel = EditorGUIUtility.TrTextContent("on");
public static GUIContent contextLabel = EditorGUIUtility.TrTextContent("in");
+ public static GUIContent removeUnusedOverridesButtonContent = EditorGUIUtility.TrTextContentWithIcon("Unused overrides", EditorGUIUtility.LoadIcon("Clear"));
public static string nonApplicableTooltip = L10n.Tr("There are no overrides that can be applied to Prefab source '{0}'.");
@@ -61,9 +68,7 @@ static class Styles
public static GUIContent infoMultipleNoApply = EditorGUIUtility.TrTextContent("Multiple Prefabs selected. Cannot show overrides.\nApplying is not possible for one or more Prefabs. Select individual Prefabs for details.");
// Messages related to the overrides list.
- public static GUIContent infoModel = EditorGUIUtility.TrTextContent("Click on individual items to review and revert.\nApplying to a Model Prefab is not possible.");
- public static GUIContent infoDefault = EditorGUIUtility.TrTextContent("Click on individual items to review, revert and apply.");
- public static GUIContent infoNoApply = EditorGUIUtility.TrTextContent("Click on individual items to review and revert.");
+ public static GUIContent infoModel = EditorGUIUtility.TrTextContent("Applying to a Model Prefab is not possible.");
// Messages related to reasons for inability to apply.
public static GUIContent warningInvalidAsset = EditorGUIUtility.TrTextContent("The Prefab file contains an invalid script. Applying is not possible. Enter Prefab Mode and remove or recover the script.");
@@ -72,11 +77,20 @@ static class Styles
public static GUIContent warningImmutable = EditorGUIUtility.TrTextContent("The Prefab file is immutable. Applying is not possible.");
public static GUIStyle boldRightAligned;
+ public static GUIStyle rightAligned;
+ public static GUIStyle removeOverridesButtonLineStyle = "TV Line";
+ public static GUIStyle removeOverridesButtonSelectionStyle = "TV Selection";
static Styles()
{
boldRightAligned = new GUIStyle(EditorStyles.boldLabel);
boldRightAligned.alignment = TextAnchor.MiddleRight;
+
+ rightAligned = new GUIStyle(EditorStyles.label);
+ rightAligned.alignment = TextAnchor.MiddleRight;
+
+ removeOverridesButtonLineStyle.alignment = TextAnchor.MiddleLeft;
+ removeOverridesButtonLineStyle.padding.left = 7;
}
}
@@ -154,6 +168,8 @@ internal void RefreshStatus(bool reloadTreeView = true)
// "No overrides". Case 1197800.
if (m_TreeView != null && !m_TreeView.hasModifications)
m_AnyOverrides = false;
+
+ m_UnusedOverridesExist = PrefabUtility.HavePrefabInstancesUnusedOverrides(m_SelectedGameObjects);
}
void UpdateStatusChecks(GameObject prefabInstanceRoot)
@@ -185,6 +201,11 @@ bool IsShowingActionButton()
return m_AnyOverrides;
}
+ bool IsShowingUnusedOverridesButton()
+ {
+ return m_UnusedOverridesExist;
+ }
+
bool HasMultiSelection()
{
return m_SelectedGameObjects.Length > 1;
@@ -213,7 +234,8 @@ public override Vector2 GetWindowSize()
if (!IsShowingActionButton())
{
- height += k_NoOverridesLabelHeight;
+ if (!IsShowingUnusedOverridesButton())
+ height += k_NoOverridesLabelHeight;
}
else
{
@@ -222,20 +244,36 @@ public override Vector2 GetWindowSize()
height += k_TreeViewPadding.top + Mathf.Min(k_MaxAllowedTreeViewHeight, m_TreeView.totalHeight) + k_TreeViewPadding.bottom;
width = Mathf.Max(Mathf.Min(m_TreeView.maxItemWidth, k_MaxAllowedTreeViewWidth), width);
}
+ else
+ {
+ if (!m_AnyOverrides)
+ height += k_NoOverridesLabelHeight;
+ else if (IsShowingUnusedOverridesButton())
+ height += k_HelpBoxHeight - 8;
+ else
+ height += k_HelpBoxHeight - 6;
+ }
- height += k_ApplyButtonHeight + k_HelpBoxHeight;
+ height += k_ApplyButtonHeight;
if (IsShowingApplyWarning())
height += k_HelpBoxHeight; // A second help box in this case.
}
+ if (IsShowingUnusedOverridesButton())
+ {
+ height += k_UnusedOverridesButtonHeight;
+ if (IsShowingActionButton())
+ height += k_RowPadding;
+ }
+
// Width should be no smaller than minimum width, but we could potentially improve
// width handling by making it expand if needed based on tree view content.
return new Vector2(width, height);
}
Color headerBgColor { get { return EditorGUIUtility.isProSkin ? new Color(0.5f, 0.5f, 0.5f, 0.2f) : new Color(0.9f, 0.9f, 0.9f, 0.6f); } }
-
+ Color horizontalLineColor = new Color(0.5f, 0.5f, 0.5f, 0.3f);
public override void OnGUI(Rect rect)
{
// Escape closes the window
@@ -248,28 +286,51 @@ public override void OnGUI(Rect rect)
Rect headerRect = GUILayoutUtility.GetRect(20, 10000, k_HeaderHeight, k_HeaderHeight);
EditorGUI.DrawRect(headerRect, headerBgColor);
- float labelSize = EditorStyles.boldLabel.CalcSize(Styles.instanceLabel).x;
+ float titleLabelSize = 0;
+ if (m_ModelPrefab || m_Immutable || m_InvalidComponentOnInstance)
+ {
+ titleLabelSize = EditorStyles.boldLabel.CalcSize(Styles.titleLabelNoApply).x;
+ Rect titleLabelRect = new Rect(headerRect.x + k_HeaderLeftMargin, headerRect.y, titleLabelSize, headerRect.height);
+ titleLabelRect.height = EditorGUIUtility.singleLineHeight;
+ GUI.Label(titleLabelRect, Styles.titleLabelNoApply, Styles.boldRightAligned);
+ }
+ else
+ {
+ titleLabelSize = EditorStyles.boldLabel.CalcSize(Styles.titleLabelDefault).x;
+ Rect titleLabelRect = new Rect(headerRect.x + k_HeaderLeftMargin, headerRect.y, titleLabelSize, headerRect.height);
+ titleLabelRect.height = EditorGUIUtility.singleLineHeight;
+ GUI.Label(titleLabelRect, Styles.titleLabelDefault, Styles.boldRightAligned);
+ }
+
+ float labelSize = EditorStyles.label.CalcSize(Styles.instanceLabel).x;
headerRect.height = EditorGUIUtility.singleLineHeight;
- Rect labelRect = new Rect(headerRect.x + k_HeaderLeftMargin, headerRect.y, labelSize, headerRect.height);
+ Rect labelRect = new Rect(headerRect.x + k_HeaderLeftMargin, headerRect.y + 20, labelSize, headerRect.height);
Rect contentRect = headerRect;
contentRect.xMin = labelRect.xMax;
+ contentRect.y = labelRect.y;
- GUI.Label(labelRect, Styles.instanceLabel, Styles.boldRightAligned);
- GUI.Label(contentRect, m_InstanceContent, EditorStyles.boldLabel);
+ GUI.Label(labelRect, Styles.instanceLabel, Styles.rightAligned);
+ GUI.Label(contentRect, m_InstanceContent, EditorStyles.label);
labelRect.y += EditorGUIUtility.singleLineHeight;
contentRect.y += EditorGUIUtility.singleLineHeight;
- GUI.Label(labelRect, Styles.contextLabel, Styles.boldRightAligned);
- GUI.Label(contentRect, m_StageContent, EditorStyles.boldLabel);
-
- GUILayout.Space(k_TreeViewPadding.top);
+ GUI.Label(labelRect, Styles.contextLabel, Styles.rightAligned);
+ GUI.Label(contentRect, m_StageContent, EditorStyles.label);
// If we know there are no overrides and thus no meaningful actions we just show that and nothing more.
if (!IsShowingActionButton())
{
- EditorGUILayout.LabelField("No Overrides");
+ if (m_UnusedOverridesExist)
+ {
+ DrawUnusedOverridesButton();
+ GUILayout.Space(k_RowPadding);
+ }
+ else
+ {
+ EditorGUILayout.LabelField(Styles.noOverridesText);
+ }
return;
}
@@ -280,21 +341,34 @@ public override void OnGUI(Rect rect)
EditorGUILayout.HelpBox(Styles.infoMultipleNoApply.text, MessageType.Info);
else
EditorGUILayout.HelpBox(Styles.infoMultiple.text, MessageType.Info);
+
+ if (m_UnusedOverridesExist)
+ {
+ DrawUnusedOverridesButton();
+ GUILayout.Space(k_RowPadding);
+ }
+ else
+ {
+ GUILayout.Space(2);
+ }
}
else
{
+ GUILayout.Space(k_TreeViewPadding.top);
+
if (m_AnyOverrides)
{
Rect treeViewRect = GUILayoutUtility.GetRect(100, 10000, 0, 10000);
m_TreeView.OnGUI(treeViewRect);
- // Display info message telling user they can click on individual items for more detailed actions.
+ if (m_UnusedOverridesExist)
+ {
+ DrawUnusedOverridesButton();
+ GUILayout.Space(k_RowPadding);
+ }
+
if (m_ModelPrefab)
EditorGUILayout.HelpBox(Styles.infoModel.text, MessageType.Info);
- else if (m_Immutable || m_InvalidComponentOnInstance)
- EditorGUILayout.HelpBox(Styles.infoNoApply.text, MessageType.Info);
- else
- EditorGUILayout.HelpBox(Styles.infoDefault.text, MessageType.Info);
}
if (IsShowingApplyWarning())
@@ -316,6 +390,7 @@ public override void OnGUI(Rect rect)
// Display action buttons (Revert All and Apply All)
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
+
using (new EditorGUI.DisabledScope(m_InvalidComponentOnAsset || m_HasManagedReferencesWithMissingTypesOnAsset))
{
if (m_TreeView != null && m_TreeView.GetSelection().Count > 1)
@@ -371,6 +446,44 @@ public override void OnGUI(Rect rect)
}
GUILayout.EndHorizontal();
+ GUILayout.Space(k_RowPadding);
+ }
+
+ void DrawUnusedOverridesButton()
+ {
+ Rect buttonRect = GUILayoutUtility.GetRect(100, 10000, k_NoOverridesLabelHeight, k_NoOverridesLabelHeight);
+
+ if (Event.current.type == EventType.Repaint)
+ {
+ if (UnusedOverridesViewPopup.s_IsOpen)
+ Styles.removeOverridesButtonSelectionStyle.Draw(buttonRect, false, false, true, true);
+
+ Rect buttonBorder = new Rect(buttonRect.x, buttonRect.y, buttonRect.width, 1);
+ EditorGUI.DrawRect(buttonBorder, horizontalLineColor);// Upper border.
+
+ buttonBorder.y = buttonRect.y + buttonRect.height;
+ EditorGUI.DrawRect(buttonBorder, horizontalLineColor);// Lower border.
+
+ Styles.removeOverridesButtonLineStyle.Draw(buttonRect, Styles.removeUnusedOverridesButtonContent, false, false, UnusedOverridesViewPopup.s_IsOpen, true);
+ }
+
+ var isHovered = buttonRect.Contains(UnityEngine.Event.current.mousePosition);
+ if (isHovered)
+ {
+ GUIView.current.MarkHotRegion(GUIClip.UnclipToWindow(buttonRect));
+
+ using (new GUI.BackgroundColorScope(GameObjectStyles.hoveredBackgroundColor))
+ {
+ GUI.Label(buttonRect, GUIContent.none, GameObjectStyles.hoveredItemBackgroundStyle);
+ }
+ }
+
+ if (GUI.Button(buttonRect, GUIContent.none, GUIStyle.none))
+ {
+ PopupWindowWithoutFocus.Show(buttonRect,
+ new UnusedOverridesViewPopup(m_SelectedGameObjects, this),
+ new[] { PopupLocation.Left, PopupLocation.Right });
+ }
}
struct ApplyAllUndo
@@ -524,4 +637,264 @@ void UpdateText(Texture assetIcon, string assetName)
m_ApplySelectedContent.tooltip = string.Format(applySelectedContent.tooltip, assetName);
}
}
+
+ internal class UnusedOverridesViewPopup : PopupWindowContent
+ {
+ const float k_HeaderHeight = 25f;
+ const float k_BodyLineHeight = 16f;
+ const float k_ButtonWidth = 80f;
+ const float k_ViewWidthPadding = 20f;
+ const float k_BodyTextPadding = 10f;
+ const float k_BodyTextPaddingSmall = 6f;
+ const int k_LeftPaddingWidth = 10;
+ const int k_RemoveButtonRightMargin = 6;
+ const int k_MaxEntries = 3;
+
+ Vector2 m_ViewSize = new Vector2(250f, k_HeaderHeight);
+
+ static class Styles
+ {
+ public static GUIStyle borderStyle = new GUIStyle("grey_border");
+ public static GUIStyle headerLabel = new GUIStyle(EditorStyles.boldLabel);
+ public static GUIStyle headerStyle = new GUIStyle(EditorStyles.boldLabel);
+ public static GUIStyle bodyStyle = new GUIStyle(EditorStyles.label);
+ public static GUIStyle logHintStyle = new GUIStyle(EditorStyles.label);
+ public static GUIStyle headerGroupStyle = new GUIStyle();
+ public static GUIContent headerContentBaseSingular = EditorGUIUtility.TrTextContent("unused override");
+ public static GUIContent headerContentBasePlural = EditorGUIUtility.TrTextContent("unused overrides");
+ public static GUIContent editorLogHint = EditorGUIUtility.TrTextContent("Details will be written to the Editor log.");
+ public static GUIContent buttonContent = EditorGUIUtility.TrTextContent("Remove");
+ public static GUIContent headerContent = EditorGUIUtility.TrTextContent("{0} unused overrides");
+ public static GUIContent headerContentSingular = EditorGUIUtility.TrTextContent("1 unused override");
+ public static GUIContent extraOverridesContent = EditorGUIUtility.TrTextContent("and {0} others");
+ public static GUIContent extraInstancesContent = EditorGUIUtility.TrTextContent("on {0} instances");
+ public static GUIContent pathOnInstanceContent = EditorGUIUtility.TrTextContent("on");
+
+ static Styles()
+ {
+ headerLabel.padding = new RectOffset(3, 3, 3, 3);
+
+ headerGroupStyle.padding = new RectOffset(0, 0, 3, 3);
+
+ headerStyle.alignment = TextAnchor.MiddleLeft;
+ headerStyle.padding.left = k_LeftPaddingWidth;
+ headerStyle.padding.top = 1;
+
+ bodyStyle.alignment = TextAnchor.UpperLeft;
+ bodyStyle.padding.left = k_LeftPaddingWidth;
+ bodyStyle.padding.top = 1;
+
+ logHintStyle.alignment = TextAnchor.MiddleLeft;
+ logHintStyle.padding.left = k_LeftPaddingWidth;
+ logHintStyle.padding.top = 1;
+ }
+ }
+
+ public static bool s_IsOpen;
+ PrefabOverridesWindow m_Owner;
+ GameObject[] m_SelectedGameObjects;
+ PrefabUtility.InstanceOverridesInfo[] m_InstanceOverridesInfos;
+ PrefabUtility.InstanceOverridesInfo m_SingleInstanceWithUnusedMods;
+ List m_OverridesContent;
+ GUIContent m_RemainingOverridesInfo = null;
+ string m_HeaderText = string.Empty;
+ int m_SelectedInstanceCount = 0;
+ int m_AffectedInstanceCount = 0;
+ int m_UnusedOverridesCount = 0;
+ int m_UsedOverridesCount = 0;
+
+ public UnusedOverridesViewPopup(GameObject[] selectedGameObjects, PrefabOverridesWindow owner)
+ {
+ m_SelectedGameObjects = selectedGameObjects;
+ m_Owner = owner;
+
+ m_InstanceOverridesInfos = PrefabUtility.GetPrefabInstancesOverridesInfos(m_SelectedGameObjects);
+
+ CalculateStatistics();
+
+ float logHintWidth = GetTextWidth(Styles.editorLogHint.text, Styles.bodyStyle);
+ float headerWidth = BuildHeaderText();
+ float maxSummaryLineWidth = BuildMultilineSummary();
+
+ float maxWidth = (headerWidth > logHintWidth) ? headerWidth : logHintWidth;
+ if (maxSummaryLineWidth > maxWidth)
+ maxWidth = maxSummaryLineWidth;
+
+ m_ViewSize.x = maxWidth + k_ViewWidthPadding;
+
+ var lineHeight = GetTextHeight("a", Styles.bodyStyle);
+ float height = k_HeaderHeight + k_BodyTextPadding + (lineHeight * m_OverridesContent.Count) + (k_BodyTextPadding * 2) + k_BodyTextPaddingSmall;
+ height += (m_RemainingOverridesInfo != null) ? k_BodyTextPaddingSmall + EditorStyles.label.lineHeight : 0;
+ m_ViewSize.y = height;
+ }
+
+ public override void OnOpen()
+ {
+ s_IsOpen = true;
+ }
+
+ public override void OnClose()
+ {
+ s_IsOpen = false;
+ base.OnClose();
+ }
+
+ public override Vector2 GetWindowSize()
+ {
+ return new Vector2(m_ViewSize.x, m_ViewSize.y + k_HeaderHeight);
+ }
+
+ public override void OnGUI(Rect rect)
+ {
+ Rect headerRect = new Rect(rect.x, rect.y, rect.width, k_HeaderHeight);
+ DrawHeader(headerRect);
+
+ Rect bodyRect = new Rect(rect.x, rect.y + k_HeaderHeight + k_BodyTextPadding, rect.width, rect.height - k_HeaderHeight - k_BodyLineHeight);
+ GUILayout.BeginArea(bodyRect);
+
+ foreach (GUIContent lineContent in m_OverridesContent)
+ {
+ GUILayout.Label(lineContent.text, Styles.bodyStyle);
+ }
+
+ if (m_RemainingOverridesInfo != null)
+ {
+ GUILayout.Space(k_BodyTextPaddingSmall);
+ GUILayout.Label(m_RemainingOverridesInfo, Styles.bodyStyle);
+ }
+
+ GUILayout.Space(k_BodyTextPadding * 2);
+ GUILayout.Label(Styles.editorLogHint, Styles.bodyStyle);
+ GUILayout.Space(k_BodyTextPaddingSmall);
+ GUILayout.EndArea();
+ }
+
+ void DrawHeader(Rect rect)
+ {
+ EditorGUI.LabelField(rect, m_HeaderText, Styles.headerStyle);
+ GUI.Label(new Rect(rect.x, rect.y, rect.width, rect.height), GUIContent.none, Styles.borderStyle);
+
+ DrawRemoveButton(rect);
+ }
+
+ void DrawRemoveButton(Rect rect)
+ {
+ GUILayout.BeginArea(rect);
+ GUILayout.BeginHorizontal(Styles.headerGroupStyle);
+ GUILayout.FlexibleSpace();
+
+ if (GUILayout.Button(Styles.buttonContent, EditorStyles.miniButton, GUILayout.Width(k_ButtonWidth)))
+ {
+ PrefabUtility.RemovePrefabInstanceUnusedOverrides(m_InstanceOverridesInfos);
+ editorWindow.Close();
+ m_Owner.RefreshStatus();
+ GUIUtility.ExitGUI();
+ }
+ GUILayout.Space(k_BodyTextPaddingSmall);
+ GUILayout.EndHorizontal();
+ GUILayout.EndArea();
+ }
+
+ void CalculateStatistics()
+ {
+ m_SingleInstanceWithUnusedMods = m_InstanceOverridesInfos[0];
+ m_SelectedInstanceCount = m_InstanceOverridesInfos.Count();
+
+ if (m_SelectedInstanceCount > 1)
+ {
+ foreach (PrefabUtility.InstanceOverridesInfo instanceMods in m_InstanceOverridesInfos)
+ {
+ if (!instanceMods.unusedMods.Any())
+ continue;
+
+ m_AffectedInstanceCount++;
+ m_UnusedOverridesCount += instanceMods.unusedMods.Length;
+ m_UsedOverridesCount += instanceMods.usedMods.Length;
+ m_SingleInstanceWithUnusedMods = instanceMods;
+ }
+ }
+ else
+ {
+ m_AffectedInstanceCount = 1;
+ m_UnusedOverridesCount = m_SingleInstanceWithUnusedMods.unusedMods.Length;
+ m_UsedOverridesCount = m_SingleInstanceWithUnusedMods.usedMods.Length;
+ }
+ }
+
+ float BuildHeaderText()
+ {
+ if (m_UnusedOverridesCount > 1)
+ m_HeaderText = string.Format(Styles.headerContent.text, m_UnusedOverridesCount);
+ else
+ m_HeaderText = Styles.headerContentSingular.text;
+
+ return GetTextWidth(m_HeaderText, Styles.headerStyle) + k_ViewWidthPadding + k_ButtonWidth;
+ }
+
+ float BuildMultilineSummary()
+ {
+ m_OverridesContent = new List();
+ int remainingAffectedInstanceCount = m_AffectedInstanceCount;
+ int totalLineEntries = 0;
+ float maxLineWidth = 0;
+ string summaryItems = string.Empty;
+
+ foreach (PrefabUtility.InstanceOverridesInfo instanceMods in m_InstanceOverridesInfos)
+ {
+ int entriesFromThisInstance = 0;
+ bool addedLines = false;
+ foreach (PropertyModification mod in instanceMods.unusedMods)
+ {
+ if (totalLineEntries >= k_MaxEntries)
+ break;
+
+ string itemText = mod.propertyPath + " " + Styles.pathOnInstanceContent.text + " " + instanceMods.instance.name;
+
+ GUIContent lineContent = new GUIContent(itemText);
+ m_OverridesContent.Add(lineContent);
+
+ float w = GetTextWidth(lineContent.text, Styles.bodyStyle);
+ if (w > maxLineWidth)
+ maxLineWidth = w;
+
+ entriesFromThisInstance++;
+ totalLineEntries++;
+ addedLines = true;
+ }
+
+ if (addedLines && instanceMods.unusedMods.Length <= entriesFromThisInstance)
+ remainingAffectedInstanceCount--;
+ }
+
+ int remainingUnusedOverridesCount = m_UnusedOverridesCount - k_MaxEntries;
+ if (remainingUnusedOverridesCount > 0)
+ {
+ string remainingOverridesInfo = string.Format(Styles.extraOverridesContent.text, remainingUnusedOverridesCount);
+
+ if (remainingAffectedInstanceCount > 1)
+ remainingOverridesInfo += " " + string.Format(Styles.extraInstancesContent.text, remainingAffectedInstanceCount);
+
+ remainingOverridesInfo += ".";
+ m_RemainingOverridesInfo = new GUIContent(remainingOverridesInfo);
+
+ float w = GetTextWidth(m_RemainingOverridesInfo.text, Styles.bodyStyle);
+ if (w > maxLineWidth)
+ maxLineWidth = w;
+ }
+
+ return maxLineWidth;
+ }
+
+ float GetTextWidth(string text, GUIStyle style)
+ {
+ var content = GUIContent.Temp(text);
+ return style.CalcSize(content).x;
+ }
+
+ float GetTextHeight(string text, GUIStyle style)
+ {
+ var content = GUIContent.Temp(text);
+ return style.CalcSize(content).y;
+ }
+ }
}
diff --git a/Editor/Mono/Prefabs/PrefabUtility.bindings.cs b/Editor/Mono/Prefabs/PrefabUtility.bindings.cs
index 193b07321f..a112cc2919 100644
--- a/Editor/Mono/Prefabs/PrefabUtility.bindings.cs
+++ b/Editor/Mono/Prefabs/PrefabUtility.bindings.cs
@@ -53,12 +53,24 @@ public sealed partial class PrefabUtility
[StaticAccessor("PrefabUtilityBindings", StaticAccessorType.DoubleColon)]
extern internal static bool HasObjectOverride(Object componentOrGameObject, bool includeDefaultOverrides = false);
+ [StaticAccessor("PrefabUtilityBindings", StaticAccessorType.DoubleColon)]
+ extern internal static bool HasPrefabInstanceUnusedOverrides_Internal(GameObject gameObject);
+
+ [StaticAccessor("PrefabUtilityBindings", StaticAccessorType.DoubleColon)]
+ extern internal static string TryGetCurrentPropertyPathFromOldPropertyPath_Internal(GameObject gameObject, Object target, string propertyPath);
+
[StaticAccessor("PrefabUtilityBindings", StaticAccessorType.DoubleColon)]
extern internal static bool HasPrefabInstanceNonDefaultOverrides_CachedForUI_Internal(GameObject gameObject);
+ [StaticAccessor("PrefabUtilityBindings", StaticAccessorType.DoubleColon)]
+ extern internal static bool HasPrefabInstanceUnusedOverrides_CachedForUI_Internal(GameObject gameObject);
+
[StaticAccessor("PrefabUtilityBindings", StaticAccessorType.DoubleColon)]
extern internal static void ClearPrefabInstanceNonDefaultOverridesCache_Internal(GameObject gameObject);
+ [StaticAccessor("PrefabUtilityBindings", StaticAccessorType.DoubleColon)]
+ extern internal static void ClearPrefabInstanceUnusedOverridesCache_Internal(GameObject gameObject);
+
// Extract all modifications that are applied to the prefab instance compared to the parent prefab.
[StaticAccessor("PrefabUtilityBindings", StaticAccessorType.DoubleColon)]
extern public static PropertyModification[] GetPropertyModifications(Object targetPrefab);
diff --git a/Editor/Mono/Prefabs/PrefabUtility.cs b/Editor/Mono/Prefabs/PrefabUtility.cs
index 68a8c80013..23b0f0a117 100644
--- a/Editor/Mono/Prefabs/PrefabUtility.cs
+++ b/Editor/Mono/Prefabs/PrefabUtility.cs
@@ -1060,7 +1060,7 @@ private static bool IsPrefabInstanceObjectOf(Object instance, Object source)
return false;
}
- private static void RemoveRemovedComponentOverridesWhichAreNull(Object prefabInstanceObject)
+ internal static void RemoveRemovedComponentOverridesWhichAreNull(Object prefabInstanceObject)
{
var removedComponents = PrefabUtility.GetRemovedComponents(prefabInstanceObject);
var filteredRemovedComponents = (from c in removedComponents where c != null select c).ToArray();
@@ -2216,6 +2216,22 @@ internal static bool HasPrefabInstanceNonDefaultOverrides_CachedForUI(GameObject
return HasPrefabInstanceNonDefaultOverrides_CachedForUI_Internal(gameObject);
}
+ internal static bool HasPrefabInstanceUnusedOverrides_CachedForUI(GameObject gameObject)
+ {
+ if (gameObject == null)
+ throw new ArgumentNullException(nameof(gameObject));
+
+ return HasPrefabInstanceUnusedOverrides_CachedForUI_Internal(gameObject);
+ }
+
+ internal static bool HasPrefabInstanceNonDefaultOverridesOrUnusedOverrides_CachedForUI(GameObject gameObject)
+ {
+ if (gameObject == null)
+ throw new ArgumentNullException(nameof(gameObject));
+
+ return HasPrefabInstanceNonDefaultOverrides_CachedForUI_Internal(gameObject) || HasPrefabInstanceUnusedOverrides_CachedForUI_Internal(gameObject);
+ }
+
internal static void ClearPrefabInstanceNonDefaultOverridesCache(GameObject gameObject)
{
if (gameObject == null)
@@ -2223,6 +2239,21 @@ internal static void ClearPrefabInstanceNonDefaultOverridesCache(GameObject game
ClearPrefabInstanceNonDefaultOverridesCache_Internal(gameObject);
}
+ internal static void ClearPrefabInstanceUnusedOverridesCache(GameObject gameObject)
+ {
+ if (gameObject == null)
+ throw new ArgumentNullException(nameof(gameObject));
+
+ ClearPrefabInstanceUnusedOverridesCache_Internal(gameObject);
+ }
+
+ internal static bool HasPrefabInstanceUnusedOverrides(GameObject gameObject)
+ {
+ if (gameObject == null)
+ throw new ArgumentNullException(nameof(gameObject));
+
+ return HasPrefabInstanceUnusedOverrides_Internal(gameObject);
+ }
// Same as IsPropertyOverrideDefaultOverrideComparedToAnySource, but checks if it's the case for all overrides
// on the object.
@@ -2594,6 +2625,251 @@ static List GetComponentsWhichThisDependsOn(Component component, List
return dependencies;
}
+ internal readonly struct InstanceOverridesInfo
+ {
+ public InstanceOverridesInfo(GameObject prefabInstance, PropertyModification[] usedMods, PropertyModification[] unusedMods)
+ {
+ this.instance = prefabInstance;
+ this.usedMods = usedMods;
+ this.unusedMods = unusedMods;
+ }
+
+ public GameObject instance { get; }
+ public PropertyModification[] usedMods { get; }
+ public PropertyModification[] unusedMods { get; }
+ }
+
+ internal static bool HavePrefabInstancesUnusedOverrides(GameObject[] gameObjects)
+ {
+ if (gameObjects == null || !gameObjects.Any())
+ return false;
+
+ foreach (GameObject go in gameObjects)
+ {
+ var outerMostPrefabInstance = PrefabUtility.GetOutermostPrefabInstanceRoot(go);
+
+ if (PrefabUtility.HasPrefabInstanceUnusedOverrides_Internal(outerMostPrefabInstance))
+ return true;
+ }
+
+ return false;
+ }
+
+ internal static InstanceOverridesInfo[] GetPrefabInstancesOverridesInfos(GameObject[] selectedGameObjects)
+ {
+ if (selectedGameObjects == null || !selectedGameObjects.Any())
+ return new InstanceOverridesInfo[] { };
+
+ List allInstanceMods = new List();
+
+ foreach (GameObject go in selectedGameObjects)
+ {
+ var outerMostPrefabInstance = PrefabUtility.GetOutermostPrefabInstanceRoot(go);
+
+ if (PrefabUtility.HasPrefabInstanceNonDefaultOverrides_CachedForUI(outerMostPrefabInstance) || PrefabUtility.HasPrefabInstanceUnusedOverrides(outerMostPrefabInstance))
+ {
+ InstanceOverridesInfo instancePropMods = GetPrefabInstanceOverridesInfo(outerMostPrefabInstance);
+ allInstanceMods.Add(instancePropMods);
+ }
+ }
+
+ return allInstanceMods.ToArray();
+ }
+
+ internal static InstanceOverridesInfo GetPrefabInstanceOverridesInfo(GameObject selectedGameObject)
+ {
+ if (selectedGameObject == null)
+ return new InstanceOverridesInfo();
+
+ List invalidModifications = new List();
+ List validModifications = new List();
+ SerializedObject serializedObject = null;
+ Object prevTarget = null;
+ HashSet propertyPathSet = new HashSet();
+
+ PropertyModification[] mods = PrefabUtility.GetPropertyModifications(selectedGameObject);
+
+ foreach (PropertyModification mod in mods)
+ {
+ if (mod.target == null)
+ {
+ invalidModifications.Add(mod);
+ continue;
+ }
+
+ if (PrefabUtility.IsDefaultOverride(mod))
+ {
+ validModifications.Add(mod);
+ continue;
+ }
+
+ if (serializedObject == null || mod.target != prevTarget)
+ {
+ serializedObject = new SerializedObject(mod.target);
+ prevTarget = mod.target;
+
+ propertyPathSet.Clear();
+
+ SerializedProperty property = serializedObject.GetIterator();
+ while (property.Next(property.hasChildren))
+ propertyPathSet.Add(property.propertyPath);
+ }
+
+ if (!propertyPathSet.Contains(mod.propertyPath))
+ {
+ string currPath = TryGetCurrentPropertyPathFromOldPropertyPath_Internal(selectedGameObject, mod.target, mod.propertyPath);
+
+ if (currPath != null && currPath.Length > 0)
+ validModifications.Add(mod);
+ else
+ invalidModifications.Add(mod);
+ }
+ else
+ {
+ validModifications.Add(mod);
+ }
+ }
+
+ return new InstanceOverridesInfo(selectedGameObject, validModifications.ToArray(), invalidModifications.ToArray());
+ }
+
+ internal static bool DoRemovePrefabInstanceUnusedOverridesDialog(InstanceOverridesInfo[] instanceOverridesInfos)
+ {
+ string titleCheckForUnusedOverrides = EditorGUIUtility.TrTextContent("Check for unused overrides").text;
+ string msgNoOverridesWereFound = EditorGUIUtility.TrTextContent("No unused overrides were found.").text;
+
+ string title = titleCheckForUnusedOverrides;
+ string message = string.Empty;
+
+ if (instanceOverridesInfos == null || !instanceOverridesInfos.Any())
+ {
+ title = titleCheckForUnusedOverrides;
+ message = msgNoOverridesWereFound;
+ EditorUtility.DisplayDialog(title, message, L10n.Tr("OK"));
+ return false;
+ }
+
+ string titleRemoveUnusedOverrides = EditorGUIUtility.TrTextContent("Remove unused overrides?").text;
+ string msgDetailsWrittenToTheLog = EditorGUIUtility.TrTextContent("Details will be written to the Editor log.").text;
+ string msgUsedOverridesCount = EditorGUIUtility.TrTextContent("Used overrides count").text;
+
+ string msgAskRemoveMultipleOverridesFromMultipleInstances = EditorGUIUtility.TrTextContent("Do you want to remove {0} unused overrides from {1} Prefab instances?").text;
+ string msgAskRemoveSingleOverrideFromMultipleInstances = EditorGUIUtility.TrTextContent("Do you want to remove 1 unused override from {0} Prefab instances?").text;
+ string msgAskRemoveMultipleOverridesFromSingleInstance = EditorGUIUtility.TrTextContent("Do you want to remove {0} unused overrides from '{1}'?").text;
+ string msgAskRemoveSingleOverrideFromSingleInstance = EditorGUIUtility.TrTextContent("Do you want to remove 1 unused override from '{0}'?").text;
+
+ PrefabUtility.InstanceOverridesInfo currInstanceWithUnusedMods = instanceOverridesInfos[0];
+ int affectedInstanceCount = 0;
+ int unusedOverridesCount = 0;
+ int usedOverridesCount = 0;
+ int selectedInstanceCount = instanceOverridesInfos.Length;
+
+ if (selectedInstanceCount > 1)
+ {
+ foreach (PrefabUtility.InstanceOverridesInfo instanceMods in instanceOverridesInfos)
+ {
+ if (!instanceMods.unusedMods.Any())
+ continue;
+
+ affectedInstanceCount++;
+ unusedOverridesCount += instanceMods.unusedMods.Length;
+ usedOverridesCount += instanceMods.usedMods.Length;
+ currInstanceWithUnusedMods = instanceMods;
+ }
+
+ if (unusedOverridesCount > 0)
+ {
+ if (affectedInstanceCount > 1)
+ {
+ if (unusedOverridesCount > 1)
+ message = string.Format(msgAskRemoveMultipleOverridesFromMultipleInstances, unusedOverridesCount, affectedInstanceCount);
+ else
+ message = string.Format(msgAskRemoveSingleOverrideFromMultipleInstances, affectedInstanceCount);
+ }
+ else// Single instance
+ {
+ if (unusedOverridesCount > 1)
+ message = string.Format(msgAskRemoveMultipleOverridesFromSingleInstance, unusedOverridesCount, currInstanceWithUnusedMods.instance.name);
+ else
+ message = string.Format(msgAskRemoveSingleOverrideFromSingleInstance, currInstanceWithUnusedMods.instance.name);
+ }
+ }
+ }
+ else// Single selection
+ {
+ unusedOverridesCount = currInstanceWithUnusedMods.unusedMods.Length;
+ usedOverridesCount = currInstanceWithUnusedMods.usedMods.Length;
+ if (unusedOverridesCount > 0)
+ {
+ affectedInstanceCount = 1;
+ if (unusedOverridesCount > 1)
+ message = string.Format(msgAskRemoveMultipleOverridesFromSingleInstance, unusedOverridesCount, currInstanceWithUnusedMods.instance.name);
+ else
+ message = string.Format(msgAskRemoveSingleOverrideFromSingleInstance, currInstanceWithUnusedMods.instance.name);
+ }
+ }
+
+ if (unusedOverridesCount > 0)
+ {
+ title = titleRemoveUnusedOverrides;
+ message += "\n\n" + msgDetailsWrittenToTheLog;
+ if (EditorUtility.DisplayDialog(title, message, L10n.Tr("Yes"), L10n.Tr("No")))
+ return true;
+ }
+ else
+ {
+ title = titleCheckForUnusedOverrides;
+ message = msgNoOverridesWereFound;
+ EditorUtility.DisplayDialog(title, message, L10n.Tr("OK"));
+ return false;
+ }
+
+ return false;
+ }
+
+ internal static void RemovePrefabInstanceUnusedOverrides(InstanceOverridesInfo[] instanceOverridesInfos)
+ {
+ foreach (InstanceOverridesInfo ipmods in instanceOverridesInfos)
+ PrefabUtility.RemovePrefabInstanceUnusedOverrides(ipmods);
+ }
+
+ private static void RemovePrefabInstanceUnusedOverrides(InstanceOverridesInfo iovInfo)
+ {
+ if (iovInfo.unusedMods.Any())
+ {
+ Undo.RegisterCompleteObjectUndo(iovInfo.instance, "Remove unused overrides");
+
+ SetPropertyModifications(iovInfo.instance, iovInfo.usedMods);
+ PrefabUtility.LogRemovedOverrides(iovInfo.instance, iovInfo.unusedMods);
+
+ PrefabUtility.RemoveRemovedComponentOverridesWhichAreNull(iovInfo.instance);
+ }
+ }
+
+ internal static void LogRemovedOverrides(GameObject instance, PropertyModification[] mods)
+ {
+ if (mods.Length == 0)
+ return;
+
+ System.Text.StringBuilder info = new System.Text.StringBuilder();
+ info.AppendLine("");
+
+ if (mods.Length > 1)
+ info.AppendLine("Removed " + mods.Length + " unused overrides from instance '" + instance.name + "':");
+ else
+ info.AppendLine("Removed 1 unused override from instance '" + instance.name + "':");
+
+ foreach (PropertyModification mod in mods)
+ {
+ if (mod.target == null)
+ info.AppendLine(" '" + mod.propertyPath + "' refers to a non-existent object.");
+ else
+ info.AppendLine(" '" + mod.propertyPath + "' refers to a non-existent property.");
+ }
+
+ System.Console.WriteLine(info.ToString(), instance);
+ }
+
internal static class Analytics
{
public enum ApplyScope
diff --git a/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs b/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs
index 1d10f6ace4..72c6ff09eb 100644
--- a/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs
+++ b/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs
@@ -884,7 +884,6 @@ internal static bool DeleteAssets(List instanceIDs, bool askIfSure)
return false;
}
- bool unpackPrefabs = false;
var paths = GetMainPathsOfAssets(instanceIDs).ToList();
if (paths.Count == 0)
@@ -902,82 +901,34 @@ internal static bool DeleteAssets(List instanceIDs, bool askIfSure)
title = L10n.Tr("Delete selected asset?");
}
- int maxCount = 3;
- bool containsPrefab = false;
-
var infotext = new StringBuilder();
- for (int i = 0; i < paths.Count; ++i)
+ int pathsCount = Mathf.Min(3, paths.Count);
+ for (int i = 0; i < pathsCount; ++i)
{
- if (i < maxCount)
- {
- infotext.AppendLine(paths[i]);
- }
-
- if (paths[i].EndsWith(".prefab", StringComparison.OrdinalIgnoreCase))
- {
- containsPrefab = true;
- if (i >= maxCount)
- {
- break;
- }
- }
+ infotext.AppendLine(paths[i]);
}
- if (paths.Count > maxCount)
+ if (paths.Count > pathsCount)
{
infotext.AppendLine("...");
}
infotext.AppendLine("");
infotext.AppendLine(L10n.Tr("You cannot undo the delete assets action."));
- if (containsPrefab)
+ if (!EditorUtility.DisplayDialog(title, infotext.ToString(), L10n.Tr("Delete"), L10n.Tr("Cancel")))
{
- infotext.AppendLine();
- infotext.AppendLine("One or more files are Prefabs. Would you like to unpack their instances in all open scenes?");
- int dialogOptionIndex = EditorUtility.DisplayDialogComplex(title, infotext.ToString(), L10n.Tr("Delete and unpack"), L10n.Tr("Delete only"), L10n.Tr("Cancel"));
- if (dialogOptionIndex == 2)
- {
- return false;
- }
-
- // Delete and unpack option
- if (dialogOptionIndex == 0)
- {
- unpackPrefabs = true;
- }
- }
- else
- {
- if (!EditorUtility.DisplayDialog(title, infotext.ToString(), L10n.Tr("Delete"), L10n.Tr("Cancel")))
- {
- return false;
- }
+ return false;
}
}
bool success = true;
+ List failedPaths = new List();
AssetDatabase.StartAssetEditing();
-
- string[] pathArray = new string[paths.Count];
- for (int i = 0; i < pathArray.Length; i++)
- {
- pathArray[i] = paths[i];
- if (unpackPrefabs && paths[i].EndsWith(".prefab", StringComparison.OrdinalIgnoreCase))
- {
- var prefabRoot = AssetDatabase.LoadMainAssetAtPath(paths[i]) as GameObject;
- if (prefabRoot != null)
- {
- PrefabUtility.UnpackAllInstancesOfPrefab(prefabRoot, PrefabUnpackMode.OutermostRoot, InteractionMode.UserAction);
- }
- }
- }
-
- List failedPaths = new List();
- if (!AssetDatabase.MoveAssetsToTrash(pathArray, failedPaths))
+ if (!AssetDatabase.MoveAssetsToTrash(paths.ToArray(), failedPaths))
success = false;
-
AssetDatabase.StopAssetEditing();
+
if (!success)
{
var vcsOffline = false;
diff --git a/Editor/Mono/SceneHierarchy.cs b/Editor/Mono/SceneHierarchy.cs
index 27c10dacdf..6f1ca61372 100644
--- a/Editor/Mono/SceneHierarchy.cs
+++ b/Editor/Mono/SceneHierarchy.cs
@@ -12,6 +12,7 @@
using UnityEditor.ShortcutManagement;
using UnityEditorInternal;
using UnityEngine.Assertions;
+using System.Text;
namespace UnityEditor
{
@@ -1294,6 +1295,7 @@ void CreateGameObjectContextClick(GenericMenu menu, int contextClickedItemID)
{
menu.AddItem(EditorGUIUtility.TrTextContent("Prefab/Unpack"), false, UnpackPrefab);
menu.AddItem(EditorGUIUtility.TrTextContent("Prefab/Unpack Completely"), false, UnpackPrefabCompletely);
+ menu.AddItem(EditorGUIUtility.TrTextContent("Prefab/Check for Unused Overrides"), false, RemoveSelectedPrefabInstanceUnusedOverrides);
}
GameObject[] selectedGameObjects = Selection.transforms.Select(t => t.gameObject).ToArray();
@@ -1463,6 +1465,9 @@ internal void CreateSceneHeaderContextClick(GenericMenu menu, Scene scene)
menu.AddDisabledItem(addSceneContent);
}
+ menu.AddSeparator("");
+ menu.AddItem(EditorGUIUtility.TrTextContent("Prefab/Check for Unused Overrides"), false, RemoveAllPrefabInstancesUnusedOverridesFromSceneForMenuItem, scene);
+
// Set the context of each MenuItem to the current selection, so the created gameobjects will be added as children
// Sets includeCreateEmptyChild to false, since that item is superfluous here (the normal "Create Empty" is added as a child anyway)
if (scene.isLoaded)
@@ -1643,6 +1648,63 @@ void UnpackPrefabCompletely()
}
}
+ void RemoveAllPrefabInstancesUnusedOverridesFromSceneForMenuItem(object userData)
+ {
+ RemoveAllPrefabInstancesUnusedOverridesFromScene((Scene)userData);
+ }
+
+ void RemoveSelectedPrefabInstanceUnusedOverrides()
+ {
+ PrefabUtility.InstanceOverridesInfo[] instanceOverrideInfos = PrefabUtility.GetPrefabInstancesOverridesInfos(Selection.gameObjects);
+
+ AskUserToRemovePrefabInstanceUnusedOverrides(instanceOverrideInfos);
+ }
+
+ void RemoveAllPrefabInstancesUnusedOverridesFromScene(Scene scene)
+ {
+ if (!scene.IsValid())
+ return;
+
+ PrefabUtility.InstanceOverridesInfo[] instanceOverridesInfos = null;
+
+ List gos = GetScenePrefabInstancesWithNonDefaultOverrides(scene);
+ if (gos != null && gos.Any())
+ instanceOverridesInfos = PrefabUtility.GetPrefabInstancesOverridesInfos(gos.ToArray());
+
+ AskUserToRemovePrefabInstanceUnusedOverrides(instanceOverridesInfos);
+ }
+
+ bool AskUserToRemovePrefabInstanceUnusedOverrides(PrefabUtility.InstanceOverridesInfo[] instanceOverridesInfos)
+ {
+ if (PrefabUtility.DoRemovePrefabInstanceUnusedOverridesDialog(instanceOverridesInfos))
+ {
+ PrefabUtility.RemovePrefabInstanceUnusedOverrides(instanceOverridesInfos);
+ return true;
+ }
+
+ return false;
+ }
+
+ List GetScenePrefabInstancesWithNonDefaultOverrides(Scene scene)
+ {
+ List gameObjects = new List();
+ TransformVisitor visitor = new TransformVisitor();
+
+ var roots = scene.GetRootGameObjects();
+ foreach (var root in roots)
+ {
+ visitor.VisitAll(root.transform, (transform, list) => {
+ GameObject go = transform.gameObject;
+ if (PrefabUtility.IsOutermostPrefabInstanceRoot(go) && PrefabUtility.HasPrefabInstanceNonDefaultOverrides_CachedForUI(go))
+ {
+ gameObjects.Add(go);
+ }
+ }, null);
+ }
+
+ return gameObjects;
+ }
+
void SetSceneActive(object userData)
{
Scene scene = (Scene)userData;
diff --git a/Editor/Mono/SceneView/SceneView.cs b/Editor/Mono/SceneView/SceneView.cs
index 4f4f142f2c..1c3ebaad00 100644
--- a/Editor/Mono/SceneView/SceneView.cs
+++ b/Editor/Mono/SceneView/SceneView.cs
@@ -2288,7 +2288,6 @@ void DoOnGUI()
}
LegacyOverlayPreOnGUI();
-
s_CurrentDrawingSceneView = this;
Event evt = Event.current;
diff --git a/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/UnityBeeDriver.cs b/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/UnityBeeDriver.cs
index 4eb596a26d..9bc55a836e 100644
--- a/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/UnityBeeDriver.cs
+++ b/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/UnityBeeDriver.cs
@@ -141,7 +141,8 @@ public static BeeDriver Make(RunnableProgram buildProgram, string dagName,
UnityVersionNumeric = new BeeBuildProgramCommon.Data.Version(Application.unityVersionVer, Application.unityVersionMaj, Application.unityVersionMin),
UnitySourceCodePath = Unsupported.IsSourceBuild(false) ? Unsupported.GetBaseUnityDeveloperFolder() : null,
AdvancedLicense = PlayerSettings.advancedLicense,
- Batchmode = InternalEditorUtility.inBatchMode
+ Batchmode = InternalEditorUtility.inBatchMode,
+ EmitDataForBeeWhy = (Debug.GetDiagnosticSwitch("EmitDataForBeeWhy").value as bool?)?? false,
});
return result;
}
diff --git a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/BuilderExternalPackages.cs b/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/BuilderExternalPackages.cs
index 3d60949857..e5a9441453 100644
--- a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/BuilderExternalPackages.cs
+++ b/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/BuilderExternalPackages.cs
@@ -1,7 +1,7 @@
-using UnityEditor;
-using UnityEngine;
using PackageInfo = UnityEditor.PackageManager.PackageInfo;
using System.Linq;
+using UnityEditor;
+using UnityEngine;
namespace Unity.UI.Builder
{
diff --git a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Document/BuilderDocument.cs b/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Document/BuilderDocument.cs
index 785e577c45..77e227bcda 100644
--- a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Document/BuilderDocument.cs
+++ b/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Document/BuilderDocument.cs
@@ -6,7 +6,6 @@
using System;
using System.IO;
-
namespace Unity.UI.Builder
{
class BuilderDocument : ScriptableObject, IBuilderSelectionNotifier, ISerializationCallbackReceiver, IBuilderPerFileAssetPostprocessor
diff --git a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Document/BuilderDocumentOpenUXML.cs b/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Document/BuilderDocumentOpenUXML.cs
index e774f13cbf..254beaaa1a 100644
--- a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Document/BuilderDocumentOpenUXML.cs
+++ b/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Document/BuilderDocumentOpenUXML.cs
@@ -261,7 +261,6 @@ public void RefreshStyle(VisualElement documentRootElement)
if (m_CurrentDocumentRootElement == null)
m_CurrentDocumentRootElement = documentRootElement;
-
StyleCache.ClearStyleCache();
UnityEngine.UIElements.StyleSheets.StyleSheetCache.ClearCaches();
foreach (var openUSS in m_OpenUSSFiles)
@@ -355,14 +354,12 @@ void RemoveLegacyStyleSheetsFromRootAssets()
}
}
-
public void AddStyleSheetsToAllRootElements(string newUssPath = null, int newUssIndex = 0)
{
var rootVEA = visualTreeAsset.GetRootUXMLElement();
AddStyleSheetsToRootAsset(rootVEA, newUssPath, newUssIndex);
}
-
void RemoveStyleSheetFromLists(int ussIndex)
{
var openUSSFile = m_OpenUSSFiles[ussIndex];
@@ -720,7 +717,6 @@ public void OnPostProcessAsset(string assetPath)
public void HierarchyChanged(VisualElement element)
{
hasUnsavedChanges = true;
-
}
public void StylingChanged()
@@ -892,6 +888,7 @@ void ClearCanvasDocumentRootElement(VisualElement documentRootElement)
internal void ResetCanvasDocumentRootElementStyleSheets(VisualElement documentRootElement)
{
documentRootElement.styleSheets.Clear();
+
// Load stylesheets specific to the document element.
var documentSheet = BuilderPackageUtilities.LoadAssetAtPath(BuilderConstants.UIBuilderPackagePath + "/Viewport/BuilderDocument.uss");
var documentThemeSheet = EditorGUIUtility.isProSkin
@@ -900,6 +897,17 @@ internal void ResetCanvasDocumentRootElementStyleSheets(VisualElement documentRo
documentRootElement.styleSheets.Add(documentSheet);
documentRootElement.styleSheets.Add(documentThemeSheet);
+
+ // Restore the active theme stylesheet
+ if (documentRootElement.HasProperty(BuilderConstants.ElementLinkedActiveThemeStyleSheetVEPropertyName))
+ {
+ var activeThemeStyleSheet = documentRootElement.GetProperty(BuilderConstants.ElementLinkedActiveThemeStyleSheetVEPropertyName) as StyleSheet;
+
+ if (activeThemeStyleSheet != null)
+ {
+ documentRootElement.styleSheets.Add(activeThemeStyleSheet);
+ }
+ }
}
void ReloadDocumentToCanvas(VisualElement documentRootElement)
@@ -954,7 +962,6 @@ void ReloadStyleSheetsToCanvas(VisualElement documentRootElement)
{
m_CurrentDocumentRootElement = documentRootElement;
-
// Refresh styles.
RefreshStyle(documentRootElement);
}
diff --git a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Explorer/BuilderExplorer.cs b/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Explorer/BuilderExplorer.cs
index 44077b0f0d..bbac318276 100644
--- a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Explorer/BuilderExplorer.cs
+++ b/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Explorer/BuilderExplorer.cs
@@ -90,11 +90,8 @@ public BuilderExplorer(
contextMenuManipulator, ElementSelectionChanged, highlightOverlayPainter);
m_ElementHierarchyView.style.flexGrow = 1;
Add(m_ElementHierarchyView);
-
- // Enable horizontal scrolling.
-
// Make sure the Hierarchy View gets focus when the pane gets focused.
- primaryFocusable = m_ElementHierarchyView.Q();
+ primaryFocusable = m_ElementHierarchyView;
UpdateHierarchyAndSelection(false);
m_ShouldRebuildHierarchyOnStyleChange = true;
diff --git a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Explorer/BuilderExplorerItem.cs b/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Explorer/BuilderExplorerItem.cs
index 36c27d3b32..ecf5c9de60 100644
--- a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Explorer/BuilderExplorerItem.cs
+++ b/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Explorer/BuilderExplorerItem.cs
@@ -64,8 +64,9 @@ void FocusOnRenameTextField()
nameLabel?.AddToClassList(BuilderConstants.HiddenStyleClassName);
labelContainer?.AddToClassList(BuilderConstants.HiddenStyleClassName);
- var baseInput = renameTextfield.Q(TextField.textInputUssName);
+ var baseInput = renameTextfield.Q(TextField.textInputUssName).Q();
if (baseInput.focusController != null)
+ // Since renameTextfield isn't attached to a panel yet, we are using DoFocusChange() to bypass canGrabFocus.
baseInput.focusController.DoFocusChange(baseInput);
renameTextfield.SelectAll();
diff --git a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Explorer/BuilderStyleSheets.cs b/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Explorer/BuilderStyleSheets.cs
index ed8ae3071c..a1308f8579 100644
--- a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Explorer/BuilderStyleSheets.cs
+++ b/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Explorer/BuilderStyleSheets.cs
@@ -76,7 +76,7 @@ public BuilderStyleSheets(
m_NewSelectorTextInputField.RegisterCallback((evt) =>
{
var input = evt.target as VisualElement;
- var field = input.parent as TextField;
+ var field = GetTextFieldParent(input);
m_FieldFocusStep = FieldFocusStep.FocusedFromStandby;
if (field.text == BuilderConstants.ExplorerInExplorerNewClassSelectorInfoMessage || m_ShouldRefocusSelectorFieldOnBlur)
{
@@ -85,7 +85,7 @@ public BuilderStyleSheets(
}
ShowTooltip();
- });
+ }, TrickleDown.TrickleDown);
m_NewSelectorTextField.RegisterCallback>((evt) =>
{
@@ -99,7 +99,9 @@ public BuilderStyleSheets(
m_NewSelectorTextField.SelectRange(m_NewSelectorTextField.value.Length, m_NewSelectorTextField.value.Length);
});
- m_NewSelectorTextInputField.RegisterCallback((evt) =>
+ // Since MouseDown captures the mouse, we need to RegisterCallback directly on the target in order to intercept the event.
+ // This could be replaced by setting selectAllOnMouseUp to false.
+ m_NewSelectorTextInputField.Q().RegisterCallback((evt) =>
{
// We want to prevent the default action on mouse up in KeyboardTextEditor, but only when
// the field selection behaviour was changed by us.
@@ -113,12 +115,15 @@ public BuilderStyleSheets(
{
m_NewSelectorTextField.SelectRange(m_NewSelectorTextField.value.Length, m_NewSelectorTextField.value.Length);
});
- });
+
+ evt.PreventDefault();
+ evt.StopImmediatePropagation();
+ }, TrickleDown.TrickleDown);
m_NewSelectorTextInputField.RegisterCallback((evt) =>
{
var input = evt.target as VisualElement;
- var field = input.parent as TextField;
+ var field = GetTextFieldParent(input);
if (m_ShouldRefocusSelectorFieldOnBlur)
{
field.schedule.Execute(PostEnterRefocus);
@@ -134,7 +139,7 @@ public BuilderStyleSheets(
}
HideTooltip();
- });
+ }, TrickleDown.TrickleDown);
// Setup New USS Menu.
m_AddUSSMenu = parent.Q("add-uss-menu");
@@ -152,6 +157,11 @@ public BuilderStyleSheets(
RegisterCallback(e => AdjustPosition());
}
+
+ TextField GetTextFieldParent(VisualElement ve)
+ {
+ return ve.GetFirstAncestorOfType();
+ }
protected override bool IsSelectedItemValid(VisualElement element)
{
@@ -299,8 +309,7 @@ void HideTooltip()
void UpdateNewSelectorFieldEnabledStateFromDocument()
{
- bool enabled = true;
- m_NewSelectorTextField.SetEnabled(enabled);
+ m_NewSelectorTextField.SetEnabled(true);
SetUpAddUSSMenu();
}
diff --git a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Inspector/BuilderInspectorAttributes.cs b/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Inspector/BuilderInspectorAttributes.cs
index e548ad1f2d..8ea2202e21 100644
--- a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Inspector/BuilderInspectorAttributes.cs
+++ b/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Inspector/BuilderInspectorAttributes.cs
@@ -347,12 +347,10 @@ object GetCustomValueAbstract(string attributeName)
{
if (attributeName == "show-horizontal-scroller")
{
-// Use old API only if version is >= 2019_4 and <= 2020_3
return scrollView.horizontalScrollerVisibility != ScrollerVisibility.Hidden;
}
else if (attributeName == "show-vertical-scroller")
{
-// Use old API only if version is >= 2019_4 and <= 2020_3
return scrollView.verticalScrollerVisibility != ScrollerVisibility.Hidden;
}
else if (attributeName == "touch-scroll-type")
diff --git a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Inspector/BuilderInspectorLocalStyles.cs b/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Inspector/BuilderInspectorLocalStyles.cs
index 6fc2b8b50b..f04fb57ab2 100644
--- a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Inspector/BuilderInspectorLocalStyles.cs
+++ b/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Inspector/BuilderInspectorLocalStyles.cs
@@ -1,12 +1,11 @@
-using UnityEngine.UIElements;
-using System.Collections.Generic;
using System;
-using UnityEngine.Assertions;
-
+using System.Collections.Generic;
using System.Linq;
-using UnityEditor;
+using UnityEngine.Assertions;
using UnityEngine.Pool;
+using UnityEngine.UIElements;
using UnityEngine.UIElements.StyleSheets;
+using UnityEditor;
namespace Unity.UI.Builder
{
@@ -31,9 +30,6 @@ public BuilderInspectorLocalStyles(BuilderInspector inspector, BuilderInspectorS
m_LocalStylesSection = m_Inspector.Q("inspector-local-styles-foldout");
- // We need to hide new Text Asset style property fields in any Unity version older than 2021.1.
- m_LocalStylesSection.Query(className: "unity-builder-no-font-asset-property-container").ForEach(e => e.style.display = DisplayStyle.None);
-
var styleCategories = m_LocalStylesSection.Query(
className: "unity-builder-inspector__style-category-foldout").ToList();
@@ -303,6 +299,5 @@ void GenerateTransitionPropertiesContent()
TransitionPropertyDropdownContent.Content = content;
}
-
}
}
diff --git a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Inspector/BuilderInspectorStyleFields.cs b/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Inspector/BuilderInspectorStyleFields.cs
index a719556be4..e4e35b4a94 100644
--- a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Inspector/BuilderInspectorStyleFields.cs
+++ b/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Inspector/BuilderInspectorStyleFields.cs
@@ -1,16 +1,15 @@
-using UnityEngine;
-using UnityEngine.UIElements;
+using Object = UnityEngine.Object;
using System;
-using System.Reflection;
using System.Collections.Generic;
using System.Linq;
+using System.Reflection;
+using UnityEditor;
using UnityEditor.UIElements;
-using Object = UnityEngine.Object;
+using UnityEngine;
using UnityEngine.Assertions;
-using UnityEditor;
using UnityEngine.Pool;
using UnityEngine.TextCore.Text;
-
+using UnityEngine.UIElements;
using UnityEngine.UIElements.StyleSheets;
namespace Unity.UI.Builder
@@ -60,13 +59,14 @@ public List GetOrCreateFieldListForStyleName(string styleName)
return fieldList;
}
- public static StyleProperty GetStyleProperty(StyleRule rule, string styleName)
+ public static StyleProperty GetLastStyleProperty(StyleRule rule, string styleName)
{
if (rule == null)
return null;
- foreach (var property in rule.properties)
+ for (var i = rule.properties.Length - 1; i >= 0; --i)
{
+ var property = rule.properties[i];
if (property.name == styleName)
return property;
}
@@ -255,6 +255,7 @@ public void BindStyleField(BuilderStyleRow styleRow, string styleName, VisualEle
var choices = new List();
var labels = new List();
var enumType = enumValue.GetType();
+
foreach (Enum item in Enum.GetValues(enumType))
{
var typeName = item.ToString();
@@ -264,6 +265,7 @@ public void BindStyleField(BuilderStyleRow styleRow, string styleName, VisualEle
// do support it, we'll have to live with a bit of a hack here.
if (typeName == "Scroll")
continue;
+
var label = string.Empty;
if (typeName == "Auto")
label = "AUTO";
@@ -507,7 +509,7 @@ public void RefreshStyleField(string styleName, VisualElement fieldElement)
var val = field.GetValue(currentVisualElement.computedStyle, null);
var valType = val == null ? typeof(object) : val.GetType();
var cSharpStyleName = ConvertUssStyleNameToCSharpStyleName(styleName);
- var styleProperty = GetStyleProperty(currentRule, cSharpStyleName);
+ var styleProperty = GetLastStyleProperty(currentRule, cSharpStyleName);
bool useStyleProperty = styleProperty != null && !styleProperty.IsVariable();
if (IsComputedStyleFloat(val) && fieldElement is FloatField)
@@ -903,7 +905,7 @@ void UpdateOverrideStyles(VisualElement fieldElement, StyleProperty stylePropert
bool isRowOverride = false;
foreach (var styleField in styleFields)
{
- if (GetStyleProperty(currentRule, styleField.bindingPath) != null)
+ if (GetLastStyleProperty(currentRule, styleField.bindingPath) != null)
{
isRowOverride = true;
styleField.RemoveFromClassList(BuilderConstants.InspectorLocalStyleResetClassName);
@@ -1046,7 +1048,7 @@ void RefreshStyleFoldoutNumberField(FoldoutNumberField foldoutElement)
foreach (var path in foldoutElement.bindingPathArray)
{
var cSharpStyleName = ConvertUssStyleNameToCSharpStyleName(path);
- var styleProperty = GetStyleProperty(currentRule, cSharpStyleName);
+ var styleProperty = GetLastStyleProperty(currentRule, cSharpStyleName);
var field = FindStylePropertyInfo(path);
if (field == null)
@@ -1068,7 +1070,7 @@ void RefreshStyleFoldoutColorField(FoldoutColorField foldoutElement)
foreach (var path in foldoutElement.bindingPathArray)
{
var cSharpStyleName = ConvertUssStyleNameToCSharpStyleName(path);
- var styleProperty = GetStyleProperty(currentRule, cSharpStyleName);
+ var styleProperty = GetLastStyleProperty(currentRule, cSharpStyleName);
var field = FindStylePropertyInfo(path);
if (field == null)
@@ -1184,7 +1186,7 @@ bool OnFieldVariableChangeImplInBatch(string newValue, string styleName, int ind
manipulator.SetVariableAtIndex(index, newValue);
}
- if (styleName != "transition-property" && null != GetStyleProperty(currentRule, "transition-property"))
+ if (styleName != "transition-property" && null != GetLastStyleProperty(currentRule, "transition-property"))
{
var transitionData = currentVisualElement.computedStyle.transitionData.Read();
var maxCount = Mathf.Max(transitionData.MaxCount(), manipulator.GetValuesCount());
@@ -1192,6 +1194,7 @@ bool OnFieldVariableChangeImplInBatch(string newValue, string styleName, int ind
if (null == manipulator.styleProperty)
TransferTransitionComputedData(manipulator, transitionData.transitionProperty, StyleValueType.Enum);
}
+
return isNewValue;
}
}
@@ -1279,7 +1282,7 @@ DropdownMenuAction.Status CanUnsetStyleProperties(IEnumerable fie
{
foreach (var path in foldout.bindingPathArray)
{
- styleProperty = styleSheet.FindProperty(currentRule, path);
+ styleProperty = styleSheet.FindLastProperty(currentRule, path);
if (normalStatusCondition(styleProperty))
{
return DropdownMenuAction.Status.Normal;
@@ -1291,7 +1294,7 @@ DropdownMenuAction.Status CanUnsetStyleProperties(IEnumerable fie
var styleName = fieldElement.GetProperty(BuilderConstants.InspectorStylePropertyNameVEPropertyName) as string;
if (!string.IsNullOrEmpty(styleName))
{
- styleProperty = styleSheet.FindProperty(currentRule, styleName);
+ styleProperty = styleSheet.FindLastProperty(currentRule, styleName);
if (normalStatusCondition(styleProperty))
return DropdownMenuAction.Status.Normal;
}
@@ -1423,7 +1426,7 @@ void NotifyStyleChanges(List styles = null, bool selfNotify = false)
StyleProperty GetOrCreateStylePropertyByStyleName(string styleName)
{
- var styleProperty = styleSheet.FindProperty(currentRule, styleName);
+ var styleProperty = styleSheet.FindLastProperty(currentRule, styleName);
if (styleProperty == null)
styleProperty = styleSheet.AddProperty(currentRule, styleName);
@@ -1890,7 +1893,6 @@ void OnFieldObjectValueChange(IEventHandler target, Object newValue, Object prev
void OnFieldValueChangeFont(ChangeEvent