diff --git a/Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/BeeBuildProgramCommon.Data.gen.csproj b/Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/BeeBuildProgramCommon.Data.gen.csproj index 92891ca1c2..2a20527d3f 100644 --- a/Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/BeeBuildProgramCommon.Data.gen.csproj +++ b/Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/BeeBuildProgramCommon.Data.gen.csproj @@ -7,7 +7,7 @@ false false latest - 1071 + 1701 diff --git a/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/Data.cs b/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/Data.cs index 4996a3ff91..35507fb75a 100644 --- a/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/Data.cs +++ b/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/Data.cs @@ -47,6 +47,7 @@ public class PlayerBuildConfig public string ApplicationIdentifier; public string Architecture; public ScriptingBackend ScriptingBackend; + public bool NoGUID; public bool InstallIntoBuildsFolder; public bool GenerateIdeProject; public bool Development; @@ -60,6 +61,7 @@ public class PlayerBuildConfig public class BuiltFilesOutput { public string[] Files = new string[0]; + public string BootConfigArtifact; } public class LinkerConfig @@ -97,6 +99,8 @@ public class Il2CppConfig public string SysRootPath; public string ToolChainPath; public string RelativeDataPath; + public bool GenerateUsymFile; + public string UsymtoolPath; } public class Services diff --git a/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/PlayerBuildProgramLibrary.Data.gen.csproj b/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/PlayerBuildProgramLibrary.Data.gen.csproj index 324c757f81..ca26ba36b7 100644 --- a/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/PlayerBuildProgramLibrary.Data.gen.csproj +++ b/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/PlayerBuildProgramLibrary.Data.gen.csproj @@ -7,7 +7,7 @@ false false latest - 1071 + 1701 PlayerBuildProgramLibrary.Data diff --git a/Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/ScriptCompilationBuildProgram.Data.gen.csproj b/Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/ScriptCompilationBuildProgram.Data.gen.csproj index 7edeeb057f..010f8a8c43 100644 --- a/Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/ScriptCompilationBuildProgram.Data.gen.csproj +++ b/Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/ScriptCompilationBuildProgram.Data.gen.csproj @@ -7,7 +7,7 @@ false false latest - 1071 + 1701 diff --git a/Editor/Mono/ActiveEditorTracker.bindings.cs b/Editor/Mono/ActiveEditorTracker.bindings.cs index 631817a74f..e903abcfbd 100644 --- a/Editor/Mono/ActiveEditorTracker.bindings.cs +++ b/Editor/Mono/ActiveEditorTracker.bindings.cs @@ -59,7 +59,7 @@ public override int GetHashCode() public Editor[] activeEditors { get { return (Editor[])Internal_GetActiveEditors(this); } } [FreeFunction] - internal static extern void Internal_GetActiveEditorsNonAlloc(ActiveEditorTracker self, Editor[] editors); + internal static extern void Internal_GetActiveEditorsNonAlloc(ActiveEditorTracker self, [Unmarshalled] Editor[] editors); // List version internal void GetObjectsLockedByThisTracker(List lockedObjects) diff --git a/Editor/Mono/Animation/AnimationUtility.bindings.cs b/Editor/Mono/Animation/AnimationUtility.bindings.cs index 668fd87bb7..f26cf83f7e 100644 --- a/Editor/Mono/Animation/AnimationUtility.bindings.cs +++ b/Editor/Mono/Animation/AnimationUtility.bindings.cs @@ -134,7 +134,7 @@ public static AnimationClip[] GetAnimationClips(GameObject gameObject) extern internal static AnimationClip[] GetAnimationClipsInAnimationPlayer([NotNull] GameObject gameObject); // Sets the array of AnimationClips to be referenced in the Animation component - extern public static void SetAnimationClips([NotNull] Animation animation, AnimationClip[] clips); + extern public static void SetAnimationClips([NotNull] Animation animation, [Unmarshalled] AnimationClip[] clips); public static EditorCurveBinding[] GetAnimatableBindings(GameObject targetObject, GameObject root) { @@ -201,7 +201,7 @@ public static Type PropertyModificationToEditorCurveBinding(PropertyModification extern public static ObjectReferenceKeyframe[] GetObjectReferenceCurve([NotNull] AnimationClip clip, EditorCurveBinding binding); - public static void SetObjectReferenceCurve(AnimationClip clip, EditorCurveBinding binding, ObjectReferenceKeyframe[] keyframes) + public static void SetObjectReferenceCurve(AnimationClip clip, EditorCurveBinding binding, [Unmarshalled]ObjectReferenceKeyframe[] keyframes) { Internal_SetObjectReferenceCurve(clip, binding, keyframes, true); Internal_InvokeOnCurveWasModified(clip, binding, keyframes != null ? CurveModifiedType.CurveModified : CurveModifiedType.CurveDeleted); @@ -232,7 +232,7 @@ internal static void SetObjectReferenceCurveNoSync(AnimationClip clip, EditorCur } [NativeThrows] - extern private static void Internal_SetObjectReferenceCurve([NotNull] AnimationClip clip, EditorCurveBinding binding, ObjectReferenceKeyframe[] keyframes, bool updateMuscleClip); + extern private static void Internal_SetObjectReferenceCurve([NotNull] AnimationClip clip, EditorCurveBinding binding, [Unmarshalled] ObjectReferenceKeyframe[] keyframes, bool updateMuscleClip); extern public static AnimationCurve GetEditorCurve([NotNull] AnimationClip clip, EditorCurveBinding binding); @@ -270,6 +270,8 @@ internal static void SetEditorCurveNoSync(AnimationClip clip, EditorCurveBinding [NativeThrows] extern private static void Internal_SetEditorCurve([NotNull] AnimationClip clip, EditorCurveBinding binding, AnimationCurve curve, bool syncEditorCurves); + extern internal static bool IsDiscreteIntBinding(EditorCurveBinding binding); + extern internal static void SyncEditorCurves([NotNull] AnimationClip clip); private static void Internal_InvokeOnCurveWasModified(AnimationClip clip, EditorCurveBinding binding, CurveModifiedType type) diff --git a/Editor/Mono/Animation/AnimationWindow/AnimationWindowControl.cs b/Editor/Mono/Animation/AnimationWindow/AnimationWindowControl.cs index da5fb4aa72..bbb79c3da4 100644 --- a/Editor/Mono/Animation/AnimationWindow/AnimationWindowControl.cs +++ b/Editor/Mono/Animation/AnimationWindow/AnimationWindowControl.cs @@ -920,7 +920,7 @@ private List GetKeys(PropertyModification[] modificatio int keyIndex = curve.GetKeyframeIndex(state.time); if (keyIndex >= 0) { - keys.Add(curve.m_Keyframes[keyIndex]); + keys.Add(curve.keyframes[keyIndex]); } } } diff --git a/Editor/Mono/Animation/AnimationWindow/AnimationWindowCurve.cs b/Editor/Mono/Animation/AnimationWindow/AnimationWindowCurve.cs index 966a51b453..c7d0cf939e 100644 --- a/Editor/Mono/Animation/AnimationWindow/AnimationWindowCurve.cs +++ b/Editor/Mono/Animation/AnimationWindow/AnimationWindowCurve.cs @@ -15,7 +15,7 @@ internal class AnimationWindowCurve : IComparable, IEquata { public const float timeEpsilon = 0.00001f; - public List m_Keyframes; + private List m_Keyframes; private EditorCurveBinding m_Binding; private int m_BindingHashCode; @@ -46,6 +46,8 @@ internal class AnimationWindowCurve : IComparable, IEquata public bool animationIsEditable { get { return m_SelectionBinding != null ? m_SelectionBinding.animationIsEditable : true; } } public int selectionID { get { return m_SelectionBinding != null ? m_SelectionBinding.id : 0; } } + public IReadOnlyList keyframes => m_Keyframes; + private object defaultValue { get @@ -190,17 +192,10 @@ public AnimationCurve ToAnimationCurve() AnimationCurve animationCurve = new AnimationCurve(); List keys = new List(); - float lastFrameTime = float.MinValue; - for (int i = 0; i < length; i++) { - // Make sure we don't get two keyframes in an exactly the same time. We just ignore those. - if (Mathf.Abs(m_Keyframes[i].time - lastFrameTime) > AnimationWindowCurve.timeEpsilon) - { - Keyframe newKeyframe = m_Keyframes[i].ToKeyframe(); - keys.Add(newKeyframe); - lastFrameTime = m_Keyframes[i].time; - } + Keyframe newKeyframe = m_Keyframes[i].ToKeyframe(); + keys.Add(newKeyframe); } animationCurve.keys = keys.ToArray(); @@ -212,17 +207,10 @@ public ObjectReferenceKeyframe[] ToObjectCurve() int length = m_Keyframes.Count; List keys = new List(); - float lastFrameTime = float.MinValue; - for (int i = 0; i < length; i++) { - // Make sure we don't get two keyframes in an exactly the same time. We just ignore those. - if (Mathf.Abs(m_Keyframes[i].time - lastFrameTime) > AnimationWindowCurve.timeEpsilon) - { - ObjectReferenceKeyframe newKeyframe = m_Keyframes[i].ToObjectReferenceKeyframe(); - lastFrameTime = newKeyframe.time; - keys.Add(newKeyframe); - } + ObjectReferenceKeyframe newKeyframe = m_Keyframes[i].ToObjectReferenceKeyframe(); + keys.Add(newKeyframe); } keys.Sort((a, b) => a.time.CompareTo(b.time)); @@ -301,6 +289,11 @@ public void RemoveKeyframe(AnimationKeyTime time) } } + public void RemoveKeyframe(AnimationWindowKeyframe keyframe) + { + m_Keyframes.Remove(keyframe); + } + public bool HasKeyframe(AnimationKeyTime time) { return GetKeyframeIndex(time) != -1; diff --git a/Editor/Mono/Animation/AnimationWindow/AnimationWindowEvent.cs b/Editor/Mono/Animation/AnimationWindow/AnimationWindowEvent.cs index 613cfa17ff..4b3cce3606 100644 --- a/Editor/Mono/Animation/AnimationWindow/AnimationWindowEvent.cs +++ b/Editor/Mono/Animation/AnimationWindow/AnimationWindowEvent.cs @@ -13,10 +13,46 @@ namespace UnityEditor { - internal struct AnimationWindowEventMethod + /// + /// Holds the context for AnimationEvent editing. + /// + class AnimationEventEditorState { - public string name; - public Type parameterType; + static bool s_ShowOverloadedFunctionsDetails = true; + static bool s_ShowDuplicatedFunctionsDetails = true; + + bool m_ShowOverloadedFunctionsDetails = s_ShowOverloadedFunctionsDetails; + bool m_ShowDuplicatedFunctionsDetails = s_ShowDuplicatedFunctionsDetails; + + /// + /// Used to track whether or not to show extra details about duplicated function names found in among the potential supported functions + /// + public bool ShowOverloadedFunctionsDetails + { + get => m_ShowOverloadedFunctionsDetails; + set + { + m_ShowOverloadedFunctionsDetails = s_ShowOverloadedFunctionsDetails = value; + } + } + + /// + /// Used to track whether or not to show extra details about overloaded function names found in among the potential supported functions + /// + public bool ShowDuplicatedFunctionsDetails + { + get => m_ShowDuplicatedFunctionsDetails; + set + { + m_ShowDuplicatedFunctionsDetails = s_ShowDuplicatedFunctionsDetails = value; + } + } + + public AnimationEventEditorState() + { + m_ShowOverloadedFunctionsDetails = s_ShowOverloadedFunctionsDetails; + m_ShowDuplicatedFunctionsDetails = s_ShowDuplicatedFunctionsDetails; + } } internal class AnimationWindowEvent : ScriptableObject diff --git a/Editor/Mono/Animation/AnimationWindow/AnimationWindowEventInspector.cs b/Editor/Mono/Animation/AnimationWindow/AnimationWindowEventInspector.cs index 126b3879a1..54ad4942ad 100644 --- a/Editor/Mono/Animation/AnimationWindow/AnimationWindowEventInspector.cs +++ b/Editor/Mono/Animation/AnimationWindow/AnimationWindowEventInspector.cs @@ -5,7 +5,6 @@ using System.Linq; using UnityEngine; using UnityEditor; -using System.Collections; using System.Collections.Generic; using System.Reflection; using System; @@ -17,15 +16,18 @@ namespace UnityEditor [CanEditMultipleObjects] internal class AnimationWindowEventInspector : Editor { + public static GUIContent s_OverloadWarning = EditorGUIUtility.TrTextContent("Some functions were overloaded in MonoBehaviour components and may not work as intended if used with Animation Events!"); + public static GUIContent s_DuplicatesWarning = EditorGUIUtility.TrTextContent("Some functions have the same name across several Monobehaviour components and may not work as intended if used with Animation Events!"); + const string kNotSupportedPostFix = " (Function Not Supported)"; const string kNoneSelected = "(No Function Selected)"; - public static GUIContent s_OverloadWarning = EditorGUIUtility.TrTextContent("Some functions were overloaded in MonoBehaviour components and may not work as intended if used with Animation Events!"); + AnimationEventEditorState m_State = new(); public override void OnInspectorGUI() { var awes = targets.Select(o => o as AnimationWindowEvent).ToArray(); - OnEditAnimationEvents(awes); + OnEditAnimationEvents(awes, m_State); } protected override void OnHeaderGUI() @@ -34,12 +36,17 @@ protected override void OnHeaderGUI() DrawHeaderGUI(this, targetTitle); } - public static void OnEditAnimationEvent(AnimationWindowEvent awe) + public static void OnEditAnimationEvent(AnimationWindowEvent awe, AnimationEventEditorState state) { - OnEditAnimationEvents(new AnimationWindowEvent[] {awe}); + OnEditAnimationEvents(new AnimationWindowEvent[] {awe}, state); } - public static void OnEditAnimationEvents(AnimationWindowEvent[] awEvents) + // These are used so we don't alloc new lists on every call + static List supportedMethods; + static List overloads; + static List duplicates; + + public static void OnEditAnimationEvents(AnimationWindowEvent[] awEvents, AnimationEventEditorState state) { AnimationWindowEventData data = GetData(awEvents); if (data.events == null || data.selectedEvents == null || data.selectedEvents.Length == 0) @@ -53,64 +60,60 @@ public static void OnEditAnimationEvents(AnimationWindowEvent[] awEvents) if (data.root != null) { - List methods = new List(); - HashSet overloads = new HashSet(); - CollectSupportedMethods(data.root, methods, overloads); + supportedMethods ??= new List(); + overloads ??= new List(); + duplicates ??= new List(); - var methodsFormatted = new List(methods.Count); + supportedMethods.Clear(); + overloads.Clear(); + duplicates.Clear(); + CollectSupportedMethods(data.root, supportedMethods, overloads, duplicates); - for (int i = 0; i < methods.Count; ++i) - { - AnimationWindowEventMethod method = methods[i]; + int selected = supportedMethods.FindIndex(method => method.Name == firstEvent.functionName); - string postFix = " ( )"; - if (method.parameterType != null) - { - if (method.parameterType == typeof(float)) - postFix = " ( float )"; - else if (method.parameterType == typeof(int)) - postFix = " ( int )"; - else - postFix = string.Format(" ( {0} )", method.parameterType.Name); - } + // A non-empty array used for rendering the contents of the popup + // It is of size 1 greater than the list of supported methods to account for the "None" option + string[] methodsFormatted = new string[supportedMethods.Count + 1]; - methodsFormatted.Add(method.name + postFix); + for (int i = 0; i < supportedMethods.Count; ++i) + { + AnimationMethodMap methodMap = supportedMethods[i]; + string menuPath = methodMap.methodMenuPath; + methodsFormatted[i] = menuPath; } - int notSupportedIndex = methods.Count; - int selected = methods.FindIndex(method => method.name == firstEvent.functionName); + // Add a final option to set the function to no selected function + int notSupportedIndex = supportedMethods.Count; if (selected == -1) { - selected = methods.Count; - - AnimationWindowEventMethod newMethod = new AnimationWindowEventMethod(); - newMethod.name = firstEvent.functionName; - newMethod.parameterType = null; - - methods.Add(newMethod); + selected = notSupportedIndex; + // Display that the current function is not supported if applicable if (string.IsNullOrEmpty(firstEvent.functionName)) - methodsFormatted.Add(kNoneSelected); + methodsFormatted[notSupportedIndex] = kNoneSelected; else - methodsFormatted.Add(firstEvent.functionName + kNotSupportedPostFix); + methodsFormatted[notSupportedIndex] = firstEvent.functionName + kNotSupportedPostFix; + + var emptyMethodMap = new AnimationMethodMap(); + supportedMethods.Add(emptyMethodMap); } EditorGUIUtility.labelWidth = 130; EditorGUI.showMixedValue = !singleFunctionName; int wasSelected = singleFunctionName ? selected : -1; - selected = EditorGUILayout.Popup("Function: ", selected, methodsFormatted.ToArray()); + selected = EditorGUILayout.Popup("Function: ", selected, methodsFormatted); if (wasSelected != selected && selected != -1 && selected != notSupportedIndex) { foreach (var evt in data.selectedEvents) { - evt.functionName = methods[selected].name; + evt.functionName = supportedMethods[selected].Name; evt.stringParameter = string.Empty; } } EditorGUI.showMixedValue = false; - var selectedParameter = methods[selected].parameterType; + var selectedParameter = supportedMethods[selected].parameterType; if (singleFunctionName && selectedParameter != null) { @@ -127,6 +130,24 @@ public static void OnEditAnimationEvents(AnimationWindowEvent[] awEvents) { EditorGUILayout.Space(); EditorGUILayout.HelpBox(s_OverloadWarning.text, MessageType.Warning, true); + state.ShowOverloadedFunctionsDetails = EditorGUILayout.Foldout(state.ShowOverloadedFunctionsDetails, "Show Details"); + if (state.ShowOverloadedFunctionsDetails) + { + string overloadedFunctionDetails = "Overloaded Functions: \n" + GetFormattedMethodsText(overloads); + GUILayout.Label(overloadedFunctionDetails, EditorStyles.helpBox); + } + } + + if (duplicates.Count > 0) + { + EditorGUILayout.Space(); + EditorGUILayout.HelpBox(s_DuplicatesWarning.text, MessageType.Warning, true); + state.ShowDuplicatedFunctionsDetails = EditorGUILayout.Foldout(state.ShowDuplicatedFunctionsDetails, "Show Details"); + if (state.ShowDuplicatedFunctionsDetails) + { + string duplicatedFunctionDetails = "Duplicated Functions: \n" + GetFormattedMethodsText(duplicates); + GUILayout.Label(duplicatedFunctionDetails, EditorStyles.helpBox); + } } } else @@ -152,7 +173,7 @@ public static void OnEditAnimationEvents(AnimationWindowEvent[] awEvents) using (new EditorGUI.DisabledScope(true)) { AnimationEvent dummyEvent = new AnimationEvent(); - DoEditRegularParameters(new AnimationEvent[] {dummyEvent}, typeof(AnimationEvent)); + DoEditRegularParameters(new AnimationEvent[] { dummyEvent }, typeof(AnimationEvent)); } } } @@ -161,6 +182,44 @@ public static void OnEditAnimationEvents(AnimationWindowEvent[] awEvents) SetData(awEvents, data); } + static string GetFormattedMethodsText(List methods) + { + string text = ""; + foreach (AnimationMethodMap methodMap in methods) + { + text += string.Format("{0}.{1} ( {2} )\n", methodMap.sourceBehaviour.GetType().Name, methodMap.Name, GetTypeName(methodMap.parameterType)); + } + text = text.Trim(); + return text; + } + + static string GetTypeName(Type t) + { + if (t == null) + return ""; + if (t == typeof(int)) + return "int"; + if (t == typeof(float)) + return "float"; + if (t == typeof(string)) + return "string"; + if (t == typeof(bool)) + return "bool"; + return t.Name; + } + + static string GetFormattedMethodName(AnimationMethodMap methodMap) + { + string targetName = methodMap.sourceBehaviour.GetType().Name; + string methodName = methodMap.Name; + string args = GetTypeName(methodMap.parameterType); + + if (methodName.StartsWith("set_") || methodName.StartsWith("get_")) + return string.Format("{0}/Properties/{1} ( {2} )", targetName, methodName, args); + else + return string.Format("{0}/Methods/{1} ( {2} )", targetName, methodName, args); + } + public static void OnDisabledAnimationEvent() { AnimationEvent dummyEvent = new AnimationEvent(); @@ -168,11 +227,13 @@ public static void OnDisabledAnimationEvent() using (new EditorGUI.DisabledScope(true)) { dummyEvent.functionName = EditorGUILayout.TextField(EditorGUIUtility.TrTextContent("Function"), dummyEvent.functionName); - DoEditRegularParameters(new AnimationEvent[] {dummyEvent}, typeof(AnimationEvent)); + DoEditRegularParameters(new AnimationEvent[] { dummyEvent }, typeof(AnimationEvent)); } } - public static void CollectSupportedMethods(GameObject gameObject, List supportedMethods, HashSet overloadedMethods) + static Dictionary> s_TypeAnimationMethodMapCache = new Dictionary>(); + + static void CollectSupportedMethods(GameObject gameObject, List supportedMethods, List overloadedMethods, List duplicatedMethods) { if (gameObject == null) return; @@ -187,59 +248,99 @@ public static void CollectSupportedMethods(GameObject gameObject, List validMethods)) { - MethodInfo method = methods[i]; - string name = method.Name; + var pendingValidMethods = new List(); + MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly ); + for (int i = 0; i < methods.Length; i++) + { + MethodInfo method = methods[i]; + string name = method.Name; - if (!IsSupportedMethodName(name)) - continue; + if (!IsSupportedMethodName(name)) + continue; - ParameterInfo[] parameters = method.GetParameters(); - if (parameters.Length > 1) - continue; + ParameterInfo[] parameters = method.GetParameters(); + if (parameters.Length > 1) + continue; - Type parameterType = null; + Type parameterType = null; - if (parameters.Length == 1) - { - parameterType = parameters[0].ParameterType; - if (!(parameterType == typeof(string) || - parameterType == typeof(float) || - parameterType == typeof(int) || - parameterType == typeof(AnimationEvent) || - parameterType == typeof(UnityEngine.Object) || - parameterType.IsSubclassOf(typeof(UnityEngine.Object)) || - parameterType.IsEnum)) - continue; + if (parameters.Length == 1) + { + parameterType = parameters[0].ParameterType; + if (!(parameterType == typeof(string) || + parameterType == typeof(float) || + parameterType == typeof(int) || + parameterType == typeof(AnimationEvent) || + parameterType == typeof(UnityEngine.Object) || + parameterType.IsSubclassOf(typeof(UnityEngine.Object)) || + parameterType.IsEnum)) + continue; + } + + AnimationMethodMap newMethodMap = new AnimationMethodMap + { + sourceBehaviour = behaviour, + methodInfo = method, + parameterType = parameterType + }; + + newMethodMap.methodMenuPath = GetFormattedMethodName(newMethodMap); + + pendingValidMethods.Add(newMethodMap); } - AnimationWindowEventMethod newMethod = new AnimationWindowEventMethod(); - newMethod.name = method.Name; - newMethod.parameterType = parameterType; + validMethods = pendingValidMethods.AsReadOnly(); + s_TypeAnimationMethodMapCache.Add(type, validMethods); + } + foreach (var method in validMethods) + { // Since AnimationEvents only stores method name, it can't handle functions with multiple overloads. - // Only retrieve first found function, but discard overloads. - int existingMethodIndex = supportedMethods.FindIndex(m => m.name == name); + // or functions with the same name across multiple monobehaviours + // Only retrieve first found method, and discard overloads and duplicate names. + int existingMethodIndex = supportedMethods.FindIndex(m => m.Name == method.Name); if (existingMethodIndex != -1) { // The method is only ambiguous if it has a different signature to the one we saw before - if (supportedMethods[existingMethodIndex].parameterType != parameterType) + if (supportedMethods[existingMethodIndex].parameterType != method.parameterType) + { + overloadedMethods.Add(method); + } + // Otherwise, there is another monobehaviour with the same method name. + else { - overloadedMethods.Add(name); + duplicatedMethods.Add(method); } } else { - supportedMethods.Add(newMethod); + supportedMethods.Add(method); } } + type = type.BaseType; } } } + /// + /// Maps the methodInfo and paramter type of a considered animation method to a source monobeheaviour. + /// Mimics the structure of + /// + struct AnimationMethodMap + { + public Object sourceBehaviour; + public MethodInfo methodInfo; + public Type parameterType; + + // Used for caching + public string methodMenuPath; + + public string Name => methodInfo?.Name ?? ""; + } + public static string FormatEvent(GameObject root, AnimationEvent evt) { if (string.IsNullOrEmpty(evt.functionName)) @@ -272,7 +373,7 @@ public static string FormatEvent(GameObject root, AnimationEvent evt) if (method == null) continue; - var parameterTypes = method.GetParameters().Select(p => p.ParameterType); + var parameterTypes = method.GetParameters(); return evt.functionName + FormatEventArguments(parameterTypes, evt); } @@ -381,15 +482,15 @@ private static bool IsSupportedMethodName(string name) return name != "Main" && name != "Start" && name != "Awake" && name != "Update"; } - private static string FormatEventArguments(IEnumerable paramTypes, AnimationEvent evt) + private static string FormatEventArguments(ParameterInfo[] paramTypes, AnimationEvent evt) { - if (!paramTypes.Any()) + if (paramTypes.Length == 0) return " ( )"; - if (paramTypes.Count() > 1) + if (paramTypes.Length > 1) return kNotSupportedPostFix; - var paramType = paramTypes.First(); + var paramType = paramTypes[0].ParameterType; if (paramType == typeof(string)) return " ( \"" + evt.stringParameter + "\" )"; @@ -426,6 +527,9 @@ private struct AnimationWindowEventData public AnimationEvent[] selectedEvents; } + + // this are used so we don't alloc new lists on every call + static List getDataSelectedEvents; private static AnimationWindowEventData GetData(AnimationWindowEvent[] awEvents) { var data = new AnimationWindowEventData(); @@ -444,14 +548,15 @@ private static AnimationWindowEventData GetData(AnimationWindowEvent[] awEvents) if (data.events != null) { - List selectedEvents = new List(); + getDataSelectedEvents ??= new List(); + getDataSelectedEvents.Clear(); foreach (var awEvent in awEvents) { if (awEvent.eventIndex >= 0 && awEvent.eventIndex < data.events.Length) - selectedEvents.Add(data.events[awEvent.eventIndex]); + getDataSelectedEvents.Add(data.events[awEvent.eventIndex]); } - data.selectedEvents = selectedEvents.ToArray(); + data.selectedEvents = getDataSelectedEvents.ToArray(); } return data; @@ -481,7 +586,7 @@ private static void SetData(AnimationWindowEvent[] awEvents, AnimationWindowEven static void ResetValues(MenuCommand command) { AnimationWindowEvent awEvent = command.context as AnimationWindowEvent; - AnimationWindowEvent[] awEvents = new AnimationWindowEvent[] {awEvent}; + AnimationWindowEvent[] awEvents = new AnimationWindowEvent[] { awEvent }; AnimationWindowEventData data = GetData(awEvents); if (data.events == null || data.selectedEvents == null || data.selectedEvents.Length == 0) diff --git a/Editor/Mono/Animation/AnimationWindow/AnimationWindowHierarchyGUI.cs b/Editor/Mono/Animation/AnimationWindow/AnimationWindowHierarchyGUI.cs index 1d1ae47c7e..1154cf213e 100644 --- a/Editor/Mono/Animation/AnimationWindow/AnimationWindowHierarchyGUI.cs +++ b/Editor/Mono/Animation/AnimationWindow/AnimationWindowHierarchyGUI.cs @@ -342,7 +342,7 @@ private void DoValueField(Rect rect, AnimationWindowHierarchyNode node, int row) if (curve.valueType == typeof(bool)) { - value = GUI.Toggle(valueFieldRect, m_HierarchyItemValueControlIDs[row], (float)value != 0, GUIContent.none, EditorStyles.toggle) ? 1f : 0f; + value = GUI.Toggle(valueFieldRect, m_HierarchyItemValueControlIDs[row], Convert.ToSingle(value) != 0f, GUIContent.none, EditorStyles.toggle) ? 1f : 0f; } else { @@ -365,7 +365,7 @@ private void DoValueField(Rect rect, AnimationWindowHierarchyNode node, int row) valueFieldRect, valueFieldDragRect, id, - (int)value, + Convert.ToInt32(value), EditorGUI.kIntFieldFormatString, m_AnimationSelectionTextField, true, @@ -382,7 +382,7 @@ private void DoValueField(Rect rect, AnimationWindowHierarchyNode node, int row) valueFieldRect, valueFieldDragRect, id, - (float)value, + Convert.ToSingle(value), "g5", m_AnimationSelectionTextField, true); @@ -392,7 +392,8 @@ private void DoValueField(Rect rect, AnimationWindowHierarchyNode node, int row) Event.current.Use(); } - if (float.IsInfinity((float)value) || float.IsNaN((float)value)) + var floatValue = Convert.ToSingle(value); + if (float.IsInfinity(floatValue) || float.IsNaN(floatValue)) value = 0f; } } @@ -491,7 +492,7 @@ private void DoCurveColorIndicator(Rect rect, AnimationWindowHierarchyNode node) { foreach (var curve in node.curves) { - if (curve.m_Keyframes.Any(key => state.time.ContainsTime(key.time))) + if (curve.keyframes.Any(key => state.time.ContainsTime(key.time))) { hasKey = true; } diff --git a/Editor/Mono/Animation/AnimationWindow/AnimationWindowKeyframe.cs b/Editor/Mono/Animation/AnimationWindow/AnimationWindowKeyframe.cs index cbdfa58b5e..e9fa64f7fa 100644 --- a/Editor/Mono/Animation/AnimationWindow/AnimationWindowKeyframe.cs +++ b/Editor/Mono/Animation/AnimationWindow/AnimationWindowKeyframe.cs @@ -149,9 +149,9 @@ public int GetHash() public int GetIndex() { - for (int i = 0; i < curve.m_Keyframes.Count; i++) + for (int i = 0; i < curve.keyframes.Count; i++) { - if (curve.m_Keyframes[i] == this) + if (curve.keyframes[i] == this) { return i; } @@ -167,11 +167,11 @@ public Keyframe ToKeyframe() // case 1395978 // Negative int values converted to float create NaN values. Limiting discrete int values to only positive values // until we rewrite the animation backend with dedicated int curves. - floatValue = UnityEngine.Animations.DiscreteEvaluationAttributeUtilities.ConvertDiscreteIntToFloat(Math.Max((int)value, 0)); + floatValue = UnityEngine.Animations.DiscreteEvaluationAttributeUtilities.ConvertDiscreteIntToFloat(Math.Max(Convert.ToInt32(value), 0)); } else { - floatValue = (float)value; + floatValue = Convert.ToSingle(value); } var keyframe = new Keyframe(time, floatValue, inTangent, outTangent); diff --git a/Editor/Mono/Animation/AnimationWindow/AnimationWindowState.cs b/Editor/Mono/Animation/AnimationWindow/AnimationWindowState.cs index c7fc0557f5..d0c4120b18 100644 --- a/Editor/Mono/Animation/AnimationWindow/AnimationWindowState.cs +++ b/Editor/Mono/Animation/AnimationWindow/AnimationWindowState.cs @@ -514,7 +514,7 @@ private void SaveSelectedKeys(string undoLabel) List toBeDeleted = new List(); // If selected keys are dragged over non-selected keyframe at exact same time, then delete the unselected ones underneath - foreach (AnimationWindowKeyframe other in snapshot.curve.m_Keyframes) + foreach (AnimationWindowKeyframe other in snapshot.curve.keyframes) { // Keyframe is in selection, skip. if (snapshot.selectedKeys.Exists(liveEditKey => liveEditKey.key == other)) @@ -529,7 +529,7 @@ private void SaveSelectedKeys(string undoLabel) foreach (AnimationWindowKeyframe deletedKey in toBeDeleted) { - snapshot.curve.m_Keyframes.Remove(deletedKey); + snapshot.curve.RemoveKeyframe(deletedKey); } } @@ -984,7 +984,7 @@ public AnimationWindowKeyframe activeKeyframe { foreach (AnimationWindowCurve curve in filteredCurves) { - foreach (AnimationWindowKeyframe keyframe in curve.m_Keyframes) + foreach (AnimationWindowKeyframe keyframe in curve.keyframes) { if (keyframe.GetHash() == m_ActiveKeyframeHash) m_ActiveKeyframeCache = keyframe; @@ -1009,7 +1009,7 @@ public List selectedKeys m_SelectedKeysCache = new List(); foreach (AnimationWindowCurve curve in filteredCurves) { - foreach (AnimationWindowKeyframe keyframe in curve.m_Keyframes) + foreach (AnimationWindowKeyframe keyframe in curve.keyframes) { if (KeyIsSelected(keyframe)) { @@ -1139,7 +1139,7 @@ public void DeleteKeys(List keys) curves.Add(keyframe.curve); UnselectKey(keyframe); - keyframe.curve.m_Keyframes.Remove(keyframe); + keyframe.curve.RemoveKeyframe(keyframe); } SaveCurves(activeAnimationClip, curves, kEditCurveUndoLabel); @@ -1162,7 +1162,7 @@ public void StartLiveEdit() { LiveEditCurve snapshot = new LiveEditCurve(); snapshot.curve = selectedKey.curve; - foreach (AnimationWindowKeyframe key in selectedKey.curve.m_Keyframes) + foreach (AnimationWindowKeyframe key in selectedKey.curve.keyframes) { LiveEditKeyframe liveEditKey = new LiveEditKeyframe(); liveEditKey.keySnapshot = new AnimationWindowKeyframe(key); @@ -1400,7 +1400,7 @@ public void CopyAllActiveCurves() { foreach (AnimationWindowCurve curve in activeCurves) { - foreach (AnimationWindowKeyframe keyframe in curve.m_Keyframes) + foreach (AnimationWindowKeyframe keyframe in curve.keyframes) { s_KeyframeClipboard.Add(new AnimationWindowKeyframe(keyframe)); } @@ -1476,14 +1476,16 @@ public void PasteKeys() // Only allow pasting of key frame from numerical curves to numerical curves or from pptr curves to pptr curves. if ((newKeyframe.time >= 0.0f) && (newKeyframe.curve != null) && (newKeyframe.curve.isPPtrCurve == keyframe.curve.isPPtrCurve)) { - if (newKeyframe.curve.HasKeyframe(AnimationKeyTime.Time(newKeyframe.time, newKeyframe.curve.clip.frameRate))) - newKeyframe.curve.RemoveKeyframe(AnimationKeyTime.Time(newKeyframe.time, newKeyframe.curve.clip.frameRate)); + var keyTime = AnimationKeyTime.Time(newKeyframe.time, newKeyframe.curve.clip.frameRate); + + if (newKeyframe.curve.HasKeyframe(keyTime)) + newKeyframe.curve.RemoveKeyframe(keyTime); // When copy-pasting multiple keyframes (curve), its a continous thing. This is why we delete the existing keyframes in the pasted range. if (lastTargetCurve == newKeyframe.curve) newKeyframe.curve.RemoveKeysAtRange(lastTime, newKeyframe.time); - newKeyframe.curve.m_Keyframes.Add(newKeyframe); + newKeyframe.curve.AddKeyframe(newKeyframe, keyTime); SelectKey(newKeyframe); // TODO: Optimize to only save curve once instead once per keyframe SaveCurve(newKeyframe.curve.clip, newKeyframe.curve, kEditCurveUndoLabel); @@ -1838,7 +1840,7 @@ public float clipFrameRate // Reposition all keyframes to match the new sampling rate foreach (var curve in allCurves) { - foreach (var key in curve.m_Keyframes) + foreach (var key in curve.keyframes) { int frame = AnimationKeyTime.Time(key.time, clipFrameRate).frame; key.time = AnimationKeyTime.Frame(frame, value).time; diff --git a/Editor/Mono/Animation/AnimationWindow/AnimationWindowUtility.cs b/Editor/Mono/Animation/AnimationWindow/AnimationWindowUtility.cs index 89ae6d3ddf..09e02c7084 100644 --- a/Editor/Mono/Animation/AnimationWindow/AnimationWindowUtility.cs +++ b/Editor/Mono/Animation/AnimationWindow/AnimationWindowUtility.cs @@ -280,7 +280,7 @@ public static AnimationWindowKeyframe AddKeyframeToCurve(AnimationWindowCurve cu } else if (type == typeof(bool) || type == typeof(float) || type == typeof(int)) { - Keyframe tempKey = new Keyframe(time.time, (float)value); + Keyframe tempKey = new Keyframe(time.time, Convert.ToSingle(value)); if (type == typeof(bool)) { AnimationUtility.SetKeyLeftTangentMode(ref tempKey, TangentMode.Constant); @@ -888,7 +888,7 @@ public static float GetNextKeyframeTime(AnimationWindowCurve[] curves, float cur foreach (AnimationWindowCurve curve in curves) { - foreach (AnimationWindowKeyframe keyframe in curve.m_Keyframes) + foreach (AnimationWindowKeyframe keyframe in curve.keyframes) { AnimationKeyTime keyTime = AnimationKeyTime.Time(keyframe.time, frameRate); if (keyTime.frame <= candidateKeyTime.frame && keyTime.frame >= nextTime.frame) @@ -914,7 +914,7 @@ public static float GetPreviousKeyframeTime(AnimationWindowCurve[] curves, float foreach (AnimationWindowCurve curve in curves) { - foreach (AnimationWindowKeyframe keyframe in curve.m_Keyframes) + foreach (AnimationWindowKeyframe keyframe in curve.keyframes) { AnimationKeyTime keyTime = AnimationKeyTime.Time(keyframe.time, frameRate); if (keyTime.frame >= candidateKeyTime.frame && keyTime.frame <= previousTime.frame) @@ -1299,8 +1299,8 @@ public static AnimationWindowKeyframe CurveSelectionToAnimationWindowKeyframe(Cu { int curveID = curve.GetHashCode(); if (curveID == curveSelection.curveID) - if (curve.m_Keyframes.Count > curveSelection.key) - return curve.m_Keyframes[curveSelection.key]; + if (curve.keyframes.Count > curveSelection.key) + return curve.keyframes[curveSelection.key]; } return null; diff --git a/Editor/Mono/Animation/AnimationWindow/DopeLine.cs b/Editor/Mono/Animation/AnimationWindow/DopeLine.cs index c576b1cf2c..da6b52f5f0 100644 --- a/Editor/Mono/Animation/AnimationWindow/DopeLine.cs +++ b/Editor/Mono/Animation/AnimationWindow/DopeLine.cs @@ -98,7 +98,7 @@ public List keys { m_Keys = new List(); foreach (AnimationWindowCurve curve in m_Curves) - foreach (AnimationWindowKeyframe key in curve.m_Keyframes) + foreach (AnimationWindowKeyframe key in curve.keyframes) m_Keys.Add(key); m_Keys.Sort((a, b) => a.time.CompareTo(b.time)); diff --git a/Editor/Mono/Animation/AnimationWindow/DopeSheetEditor.cs b/Editor/Mono/Animation/AnimationWindow/DopeSheetEditor.cs index eb2bb1b48a..ae4b219977 100644 --- a/Editor/Mono/Animation/AnimationWindow/DopeSheetEditor.cs +++ b/Editor/Mono/Animation/AnimationWindow/DopeSheetEditor.cs @@ -806,7 +806,7 @@ private void AssignSpriteToSpriteRenderer(AnimationWindowCurve curve) if (rootGameObject == null) return; - var hasValidCurve = curve.m_Keyframes.Count > 0 && curve.binding.type == typeof(SpriteRenderer); + var hasValidCurve = curve.keyframes.Count > 0 && curve.binding.type == typeof(SpriteRenderer); if (!hasValidCurve) return; @@ -815,7 +815,7 @@ private void AssignSpriteToSpriteRenderer(AnimationWindowCurve curve) if (!hasValidSpriteRenderer) return; - var keyframe = curve.m_Keyframes[0]; + var keyframe = curve.keyframes[0]; var sprite = keyframe.value as Sprite; if (sprite != null) { @@ -1126,12 +1126,12 @@ public void FrameSelected() { foreach (AnimationWindowCurve curve in state.activeCurves) { - int keyCount = curve.m_Keyframes.Count; + int keyCount = curve.keyframes.Count; if (keyCount > 1) { - Vector2 pt1 = new Vector2(curve.m_Keyframes[0].time, 0.0f); - Vector2 pt2 = new Vector2(curve.m_Keyframes[keyCount - 1].time, 0.0f); + Vector2 pt1 = new Vector2(curve.keyframes[0].time, 0.0f); + Vector2 pt2 = new Vector2(curve.keyframes[keyCount - 1].time, 0.0f); if (firstKey) { diff --git a/Editor/Mono/Animation/EditorCurveBinding.bindings.cs b/Editor/Mono/Animation/EditorCurveBinding.bindings.cs index b8e5a4bfbb..8c64e62385 100644 --- a/Editor/Mono/Animation/EditorCurveBinding.bindings.cs +++ b/Editor/Mono/Animation/EditorCurveBinding.bindings.cs @@ -8,6 +8,7 @@ using UnityEngine.Playables; using UnityEngine.Scripting.APIUpdating; using UnityEngine.Internal; +using UnityEngine; namespace UnityEditor { @@ -128,6 +129,16 @@ static public EditorCurveBinding DiscreteCurve(string inPath, System.Type inType binding.m_isSerializeReferenceCurve = 0; binding.m_isUnknownCurve = 0; + if (!AnimationUtility.IsDiscreteIntBinding(binding)) + { + Debug.LogWarning( + $"Property [" + inPropertyName + "] is not a supported discrete curve binding. " + + "Discrete curves only support [" + typeof(Enum) + "] and [" + typeof(int) + " with the `DiscreteEvaluation` attribute]."); + + binding.m_isDiscreteCurve = 0; + binding.m_isUnknownCurve = 1; + } + return binding; } diff --git a/Editor/Mono/Animation/TransitionPreview.cs b/Editor/Mono/Animation/TransitionPreview.cs index 1c2433fb7a..8a7e0da84f 100644 --- a/Editor/Mono/Animation/TransitionPreview.cs +++ b/Editor/Mono/Animation/TransitionPreview.cs @@ -269,7 +269,7 @@ private void ResampleTransition(AnimatorStateTransition transition, AvatarMask l AnimatorStateInfo currentState = m_AvatarPreview.Animator.GetCurrentAnimatorStateInfo(m_LayerIndex); m_LeftStateWeightA = currentState.normalizedTime; m_LeftStateTimeA = currentTime; - while (!hasFinished && currentTime < maxDuration) + while (!hasFinished) { m_AvatarPreview.Animator.Update(stepTime); @@ -284,7 +284,7 @@ private void ResampleTransition(AnimatorStateTransition transition, AvatarMask l hasStarted = true; } - if (hasTransitioned && currentTime >= maxDuration) + if (hasTransitioned || currentTime >= maxDuration) { hasFinished = true; } @@ -303,7 +303,7 @@ private void ResampleTransition(AnimatorStateTransition transition, AvatarMask l m_LeftStateTimeB = currentTime; } - if (hasTransitioned) + if (hasTransitioned || hasFinished) { m_RightStateWeightB = currentState.normalizedTime; m_RightStateTimeB = currentTime; @@ -329,6 +329,11 @@ private void ResampleTransition(AnimatorStateTransition transition, AvatarMask l float leftDuration = (m_LeftStateTimeB - m_LeftStateTimeA) / (m_LeftStateWeightB - m_LeftStateWeightA); float rightDuration = (m_RightStateTimeB - m_RightStateTimeA) / (m_RightStateWeightB - m_RightStateWeightA); + // Ensure step times make sense based on these timings + // If step time is too small, the samping will take too long + currentStateStepTime = Mathf.Max(currentStateStepTime, leftDuration / 600.0f); + nextStateStepTime = Mathf.Max(nextStateStepTime, rightDuration / 600.0f); + if (m_MustSampleMotions) { // Do this as infrequently as possible diff --git a/Editor/Mono/AnimatorController.bindings.cs b/Editor/Mono/AnimatorController.bindings.cs index eee994fcb2..40803d59f3 100644 --- a/Editor/Mono/AnimatorController.bindings.cs +++ b/Editor/Mono/AnimatorController.bindings.cs @@ -33,6 +33,7 @@ extern public AnimatorControllerLayer[] layers [FreeFunction(Name = "AnimatorControllerBindings::GetLayers", HasExplicitThis = true)] get; [FreeFunction(Name = "AnimatorControllerBindings::SetLayers", HasExplicitThis = true, ThrowsException = true)] + [param: Unmarshalled] set; } @@ -41,6 +42,7 @@ extern public AnimatorControllerParameter[] parameters [FreeFunction(Name = "AnimatorControllerBindings::GetParameters", HasExplicitThis = true)] get; [FreeFunction(Name = "AnimatorControllerBindings::SetParameters", HasExplicitThis = true, ThrowsException = true)] + [param: Unmarshalled] set; } @@ -133,6 +135,6 @@ internal extern bool isAssetBundled extern internal ScriptableObject[] Internal_GetEffectiveBehaviours([NotNull] AnimatorState state, int layerIndex); [FreeFunction(Name = "AnimatorControllerBindings::Internal_SetEffectiveBehaviours", HasExplicitThis = true)] - extern internal void Internal_SetEffectiveBehaviours([NotNull] AnimatorState state, int layerIndex, ScriptableObject[] behaviours); + extern internal void Internal_SetEffectiveBehaviours([NotNull] AnimatorState state, int layerIndex, [Unmarshalled] ScriptableObject[] behaviours); } } diff --git a/Editor/Mono/Annotation/SceneFXWindow.cs b/Editor/Mono/Annotation/SceneFXWindow.cs index 4b7a97b2e6..c8b3b6eb94 100644 --- a/Editor/Mono/Annotation/SceneFXWindow.cs +++ b/Editor/Mono/Annotation/SceneFXWindow.cs @@ -4,6 +4,7 @@ using System; using UnityEngine; +using UnityEngine.Rendering; namespace UnityEditor { @@ -74,6 +75,12 @@ private void Draw(Rect rect) DrawListElement(drawPos, "Skybox", state.showSkybox, value => state.showSkybox = value); drawPos.y += EditorGUI.kSingleLineHeight; + if(SupportedRenderingFeatures.active.supportsClouds) + { + DrawListElement(drawPos, "Clouds", state.showClouds, value => state.showClouds = value); + drawPos.y += EditorGUI.kSingleLineHeight; + } + DrawListElement(drawPos, "Fog", state.showFog, value => state.showFog = value); drawPos.y += EditorGUI.kSingleLineHeight; diff --git a/Editor/Mono/AssemblyHelper.cs b/Editor/Mono/AssemblyHelper.cs index 73a0f4acab..afd649332e 100644 --- a/Editor/Mono/AssemblyHelper.cs +++ b/Editor/Mono/AssemblyHelper.cs @@ -7,14 +7,11 @@ using System.Linq; using System.Reflection; using System.Collections.Generic; -using System.Diagnostics; using Mono.Cecil; -using UnityEditor.Build; using UnityEditor.Modules; using UnityEditorInternal; using UnityEngine; using System.Runtime.InteropServices; -using UnityEditor.VisualStudioIntegration; using UnityEngine.Scripting; using Debug = UnityEngine.Debug; using Unity.Profiling; @@ -24,181 +21,11 @@ namespace UnityEditor { - internal partial class AssemblyHelper + internal class AssemblyHelper { static Dictionary managedToDllType = new Dictionary(); static BuildPlayerDataExtractor m_BuildPlayerDataExtractor = new BuildPlayerDataExtractor(); - static public string[] GetNamesOfAssembliesLoadedInCurrentDomain() - { - var assemblies = AppDomain.CurrentDomain.GetAssemblies(); - var locations = new List(); - foreach (var a in assemblies) - { - try - { - locations.Add(a.Location); - } - catch (NotSupportedException) - { - //we have some "dynamic" assmeblies that do not have a filename - } - } - return locations.ToArray(); - } - - static public string ExtractInternalAssemblyName(string path) - { - try - { - AssemblyDefinition definition = AssemblyDefinition.ReadAssembly(path); - return definition.Name.Name; - } - catch - { - return ""; - } - } - - static AssemblyDefinition GetAssemblyDefinitionCached(string path, Dictionary cache) - { - if (cache.ContainsKey(path)) - return cache[path]; - - AssemblyDefinition definition = AssemblyDefinition.ReadAssembly(path); - cache[path] = definition; - return definition; - } - - static private bool CouldBelongToDotNetOrWindowsRuntime(string assemblyPath) - { - return assemblyPath.IndexOf("mscorlib.dll") != -1 || - assemblyPath.IndexOf("System.") != -1 || - assemblyPath.IndexOf("Microsoft.") != -1 || - assemblyPath.IndexOf("Windows.") != -1 || - assemblyPath.IndexOf("WinRTLegacy.dll") != -1 || - assemblyPath.IndexOf("platform.dll") != -1; - } - - static private bool IgnoreAssembly(string assemblyPath, BuildTarget target, ScriptingImplementation scriptingImplementation) - { -#pragma warning disable 618 - if (target == BuildTarget.WSAPlayer || scriptingImplementation == ScriptingImplementation.CoreCLR) - { - if (CouldBelongToDotNetOrWindowsRuntime(assemblyPath)) - return true; - } - else if (target == BuildTarget.XboxOne) - { - var profile = PlayerSettings.GetApiCompatibilityLevel(NamedBuildTarget.XboxOne); - if (profile == ApiCompatibilityLevel.NET_4_6 || profile == ApiCompatibilityLevel.NET_Standard_2_0) - { - if (CouldBelongToDotNetOrWindowsRuntime(assemblyPath)) - return true; - } - } - - return IsInternalAssembly(assemblyPath); - } - - static private void AddReferencedAssembliesRecurse(string assemblyPath, List alreadyFoundAssemblies, string[] allAssemblyPaths, string[] foldersToSearch, Dictionary cache, BuildTarget target, ScriptingImplementation scriptingImplementation) - { - if (IgnoreAssembly(assemblyPath, target, scriptingImplementation)) - return; - - if (!File.Exists(assemblyPath)) - return; - - AssemblyDefinition assembly = GetAssemblyDefinitionCached(assemblyPath, cache); - if (assembly == null) - throw new System.ArgumentException("Referenced Assembly " + Path.GetFileName(assemblyPath) + " could not be found!"); - - // Ignore it if we already added the assembly - if (alreadyFoundAssemblies.IndexOf(assemblyPath) != -1) - return; - - alreadyFoundAssemblies.Add(assemblyPath); - - var architectureSpecificPlugins = PluginImporter.GetImporters(target).Where(i => - { - var cpu = i.GetPlatformData(target, "CPU"); - return !string.IsNullOrEmpty(cpu) && !string.Equals(cpu, "AnyCPU", StringComparison.InvariantCultureIgnoreCase); - }).Select(i => Path.GetFileName(i.assetPath)).Distinct(); - - // Go through all referenced assemblies - foreach (AssemblyNameReference referencedAssembly in assembly.MainModule.AssemblyReferences) - { - // Special cases for Metro - if (referencedAssembly.Name == "BridgeInterface") continue; - if (referencedAssembly.Name == "WinRTBridge") continue; - if (referencedAssembly.Name == "UnityEngineProxy") continue; - if (IgnoreAssembly(referencedAssembly.Name + ".dll", target, scriptingImplementation)) continue; - - string foundPath = FindAssemblyName(referencedAssembly.FullName, referencedAssembly.Name, allAssemblyPaths, foldersToSearch, cache); - - if (foundPath == "") - { - // Ignore architecture specific plugin references - var found = false; - foreach (var extension in new[] { ".dll", ".winmd" }) - { - if (architectureSpecificPlugins.Any(p => string.Equals(p, referencedAssembly.Name + extension, StringComparison.InvariantCultureIgnoreCase))) - { - found = true; - break; - } - } - if (found) - continue; - throw new System.ArgumentException(string.Format("The Assembly {0} is referenced by {1} ('{2}'). But the dll is not allowed to be included or could not be found.", - referencedAssembly.Name, - assembly.MainModule.Assembly.Name.Name, - assemblyPath)); - } - - AddReferencedAssembliesRecurse(foundPath, alreadyFoundAssemblies, allAssemblyPaths, foldersToSearch, cache, target, scriptingImplementation); - } - } - - static string FindAssemblyName(string fullName, string name, string[] allAssemblyPaths, string[] foldersToSearch, Dictionary cache) - { - // Search in provided assemblies - for (int i = 0; i < allAssemblyPaths.Length; i++) - { - if (!File.Exists(allAssemblyPaths[i])) - continue; - - AssemblyDefinition definition = GetAssemblyDefinitionCached(allAssemblyPaths[i], cache); - if (definition.MainModule.Assembly.Name.Name == name) - return allAssemblyPaths[i]; - } - - // Search in GAC - foreach (string folder in foldersToSearch) - { - string pathInGacFolder = Path.Combine(folder, name + ".dll"); - if (File.Exists(pathInGacFolder)) - return pathInGacFolder; - } - return ""; - } - - [RequiredByNativeCode] - static public string[] FindAssembliesReferencedBy(string[] paths, string[] foldersToSearch, BuildTarget target, ScriptingImplementation scriptingImplementation) - { - List unique = new List(); - string[] allAssemblyPaths = paths; - - var cache = new Dictionary(); - for (int i = 0; i < paths.Length; i++) - AddReferencedAssembliesRecurse(paths[i], unique, allAssemblyPaths, foldersToSearch, cache, target, scriptingImplementation); - - for (int i = 0; i < paths.Length; i++) - unique.Remove(paths[i]); - - return unique.ToArray(); - } - static public bool IsUnityEngineModule(AssemblyDefinition assembly) { return assembly.CustomAttributes.Any(a => a.AttributeType.FullName == typeof(UnityEngineModuleAssembly).FullName); @@ -209,33 +36,6 @@ static public bool IsUnityEngineModule(Assembly assembly) return assembly.GetCustomAttributes(typeof(UnityEngineModuleAssembly), false).Length > 0; } - private static bool IsTypeAUserExtendedScript(TypeReference type) - { - if (type == null || type.FullName == "System.Object") - return false; - - try - { - var typeDefinition = type.Resolve(); - var attributes = typeDefinition.CustomAttributes; - for (var i = 0; i < attributes.Count; i++) - { - if (attributes[i].Constructor.DeclaringType.FullName == "UnityEngine.ExtensionOfNativeClassAttribute") - return true; - } - - if (typeDefinition.BaseType != null) - return IsTypeAUserExtendedScript(typeDefinition.BaseType); - } - catch (AssemblyResolutionException) - { - // just eat exception if we fail to load assembly here. - // failure should be handled better in other places. - } - - return false; - } - public static string[] GetDefaultAssemblySearchPaths() { // Add the path to all available precompiled assemblies @@ -359,36 +159,6 @@ public static bool IsInternalAssembly(string file) return ModuleUtils.GetAdditionalReferencesForUserScripts().Any(p => p.Equals(file)); } - const int kDefaultDepth = 10; - internal static ICollection FindAssemblies(string basePath) - { - return FindAssemblies(basePath, kDefaultDepth); - } - - internal static ICollection FindAssemblies(string basePath, int maxDepth) - { - var assemblies = new List(); - - if (0 == maxDepth) - return assemblies; - - try - { - DirectoryInfo directory = new DirectoryInfo(basePath); - assemblies.AddRange(directory.GetFiles() - .Where(file => IsManagedAssembly(file.FullName)) - .Select(file => file.FullName)); - foreach (DirectoryInfo subdirectory in directory.GetDirectories()) - assemblies.AddRange(FindAssemblies(subdirectory.FullName, maxDepth - 1)); - } - catch (Exception) - { - // Return what we have now - } - - return assemblies; - } - /// /// Performs a depth-first-search topological sort on the input assemblies, /// based on the outgoing assembly references from each assembly. The diff --git a/Editor/Mono/AssemblyInfo/AssemblyInfo.cs b/Editor/Mono/AssemblyInfo/AssemblyInfo.cs index 2682979939..bae9fb7fec 100644 --- a/Editor/Mono/AssemblyInfo/AssemblyInfo.cs +++ b/Editor/Mono/AssemblyInfo/AssemblyInfo.cs @@ -7,6 +7,7 @@ // ADD_NEW_PLATFORM_HERE [assembly: InternalsVisibleTo("Unity.LiveNotes")] +[assembly: InternalsVisibleTo("Unity.Audio.Tests")] [assembly: InternalsVisibleTo("Unity.Burst")] [assembly: InternalsVisibleTo("Unity.Burst.Editor")] [assembly: InternalsVisibleTo("Unity.Cloud.Collaborate.Editor")] @@ -29,7 +30,6 @@ [assembly: InternalsVisibleTo("Unity.Timeline.Editor")] [assembly: InternalsVisibleTo("Unity.PackageManagerUI.Develop.Editor")] [assembly: InternalsVisibleTo("Unity.DeviceSimulator.Editor")] - [assembly: InternalsVisibleTo("Unity.Timeline.EditorTests")] [assembly: InternalsVisibleTo("UnityEditor.Graphs")] [assembly: InternalsVisibleTo("UnityEditor.UWP.Extensions")] @@ -50,7 +50,6 @@ [assembly: InternalsVisibleTo("UnityEditor.WindowsStandalone.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.OSXStandalone.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.Lumin.Extensions")] -[assembly: InternalsVisibleTo("UnityEditor.Stadia.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.GameCoreScarlett.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.GameCoreXboxOne.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.GameCoreCommon.Extensions")] @@ -130,6 +129,7 @@ [assembly: InternalsVisibleTo("UnityEditor.TextCoreTextEngineModule")] [assembly: InternalsVisibleTo("Unity.TextMeshPro.Editor")] [assembly: InternalsVisibleTo("Unity.Animation.Editor.AnimationWindow")] +[assembly: InternalsVisibleTo("Unity.RenderPipelines.Multiple_SRP.EditorTests")] [assembly: InternalsVisibleTo("Unity.SceneTemplate.Editor")] [assembly: InternalsVisibleTo("com.unity.purchasing.udp.Editor")] diff --git a/Editor/Mono/AssemblyValidation.cs b/Editor/Mono/AssemblyValidation.cs index 16f2d3315e..d6d55f3a55 100644 --- a/Editor/Mono/AssemblyValidation.cs +++ b/Editor/Mono/AssemblyValidation.cs @@ -137,6 +137,34 @@ public static Error[] ValidateAssemblies(string[] assemblyPaths, bool enableLogg return errors; } + [RequiredByNativeCode] + internal static Error[] ValidateRoslynAnalyzers(string[] analyzerPaths) + { + var readerParameters = new ReaderParameters + { + ReadingMode = ReadingMode.Deferred + }; + List errors = new List(); + foreach(var analyzer in analyzerPaths) + { + using (var analyzerDefinition = AssemblyDefinition.ReadAssembly(analyzer, readerParameters)) + { + var netstandardVersion = analyzerDefinition.MainModule.AssemblyReferences.Where(r => r.Name == "netstandard").FirstOrDefault(); + if (netstandardVersion != null && netstandardVersion.Version >= new Version(2, 1)) + { + errors.Add(new Error + { + assemblyPath = analyzer, + flags = ErrorFlags.ReferenceHasErrors, + message = $"{analyzerDefinition.Name.Name} references {netstandardVersion}. A roslyn analyzer should reference netstandard version 2.0" + }); + } + + } + } + return errors.ToArray(); + } + [RequiredByNativeCode] public static Error[] ValidateAssemblyDefinitionFiles() { diff --git a/Editor/Mono/AssetDatabase/AssetDatabaseSearching.cs b/Editor/Mono/AssetDatabase/AssetDatabaseSearching.cs index c3bd1ded72..fedd4d13e0 100644 --- a/Editor/Mono/AssetDatabase/AssetDatabaseSearching.cs +++ b/Editor/Mono/AssetDatabase/AssetDatabaseSearching.cs @@ -88,7 +88,7 @@ private static IEnumerator FindInFolders(SearchFilter searchFilter, Func subAsse texImporter.sRGBTexture = false; // extra texture does not contain color data, hence shouldn't be sRGB. texImporter.SaveAndReimport(); } + + } } } diff --git a/Editor/Mono/AssetPipeline/TextureImporter.bindings.cs b/Editor/Mono/AssetPipeline/TextureImporter.bindings.cs index 09252985c2..77c87e4adf 100644 --- a/Editor/Mono/AssetPipeline/TextureImporter.bindings.cs +++ b/Editor/Mono/AssetPipeline/TextureImporter.bindings.cs @@ -468,7 +468,6 @@ public void SetTextureSettings(TextureImporterSettings src) internal extern bool removeMatte { get; set; } public extern bool ignorePngGamma { get; set; } - internal static readonly int MaxTextureSizeAllowedForReadable = 8192; //keep in sync with TextureImporter.h // This is for remapping Sprite that are renamed. extern internal bool GetNameFromInternalIDMap(long id, ref string name); diff --git a/Editor/Mono/AssetPreviewUpdater.cs b/Editor/Mono/AssetPreviewUpdater.cs index bed2f81cdc..d0c3538ae6 100644 --- a/Editor/Mono/AssetPreviewUpdater.cs +++ b/Editor/Mono/AssetPreviewUpdater.cs @@ -3,6 +3,8 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.Scripting; namespace UnityEditor { @@ -16,14 +18,11 @@ public static Texture2D CreatePreviewForAsset(Object obj, Object[] subAssets, st // Generate a preview texture for an asset public static Texture2D CreatePreview(Object obj, Object[] subAssets, string assetPath, int width, int height) { - if (obj == null) - return null; - - System.Type type = CustomEditorAttributes.FindCustomEditorType(obj, false); + var type = CustomEditorAttributes.FindCustomEditorType(obj, false); if (type == null) return null; - System.Reflection.MethodInfo info = type.GetMethod("RenderStaticPreview"); + var info = type.GetMethod("RenderStaticPreview"); if (info == null) { Debug.LogError("Fail to find RenderStaticPreview base method"); @@ -33,13 +32,20 @@ public static Texture2D CreatePreview(Object obj, Object[] subAssets, string ass if (info.DeclaringType == typeof(Editor)) return null; - - Editor editor = Editor.CreateEditor(obj); - + var editor = Editor.CreateEditor(obj); if (editor == null) return null; - Texture2D tex = editor.RenderStaticPreview(assetPath, subAssets, width, height); + //Check that Render Pipeline is ready + //Beware: AssetImportWorkers have their own Render Pipeline instance. Render Pipeline will be separately created for each one of them. + var pipelineWasNotInitialized = !RenderPipelineManager.pipelineSwitchCompleted; + + //We always keep this call to initialize Render Pipeline when Render Pipeline was not ready + var previewTexture = editor.RenderStaticPreview(assetPath, subAssets, width, height); + + //If after render our Render Pipeline is initialized we re-render to have a valid result + if (pipelineWasNotInitialized && RenderPipelineManager.pipelineSwitchCompleted) + previewTexture = editor.RenderStaticPreview(assetPath, subAssets, width, height); // For debugging we write the preview to a file (keep) //{ @@ -50,8 +56,7 @@ public static Texture2D CreatePreview(Object obj, Object[] subAssets, string ass //} Object.DestroyImmediate(editor); - - return tex; + return previewTexture; } } } diff --git a/Editor/Mono/AssetStore/AssetStoreWindow.cs b/Editor/Mono/AssetStore/AssetStoreWindow.cs index f0533b3eaa..028877f5ae 100644 --- a/Editor/Mono/AssetStore/AssetStoreWindow.cs +++ b/Editor/Mono/AssetStore/AssetStoreWindow.cs @@ -13,9 +13,6 @@ namespace UnityEditor [EditorWindowTitle(title = "Asset Store", icon = "Asset Store")] internal class AssetStoreWindow : EditorWindow { - // Use this for initialization - // Index at 1499 because "Package Manager" is 1500, pairing tools for user to get external content - [MenuItem("Window/Asset Store", false, 1499)] public static AssetStoreWindow Init() { if (EditorPrefs.GetBool("AlwaysOpenAssetStoreInBrowser", false)) @@ -32,14 +29,22 @@ public static AssetStoreWindow Init() } } - private static void OpenAssetStoreInBrowser() + [MenuItem("Window/Asset Store", false, 1497)] + public static void OpenAssetStoreInBrowser() { string assetStoreUrl = UnityConnect.instance.GetConfigurationURL(CloudConfigUrl.CloudAssetStoreUrl); - if (UnityEditor.Connect.UnityConnect.instance.loggedIn) - UnityEditor.Connect.UnityConnect.instance.OpenAuthorizedURLInWebBrowser(assetStoreUrl); + assetStoreUrl += "?utm_source=unity-editor-window-menu&utm_medium=desktop-app"; + if (UnityConnect.instance.loggedIn) + UnityConnect.instance.OpenAuthorizedURLInWebBrowser(assetStoreUrl); else Application.OpenURL(assetStoreUrl); } + [MenuItem("Window/My Assets", false, 1498)] + public static void OpenMyAssetsInPackageManager() + { + PackageManagerWindow.SelectPackageAndFilterStatic(string.Empty, PackageManager.UI.Internal.PackageFilterTab.AssetStore); + } + public void OnEnable() { this.antiAliasing = 4; diff --git a/Editor/Mono/AttributeHelper.cs b/Editor/Mono/AttributeHelper.cs index a7aa63dac9..95d72e45d3 100644 --- a/Editor/Mono/AttributeHelper.cs +++ b/Editor/Mono/AttributeHelper.cs @@ -145,7 +145,7 @@ struct MonoCreateAssetItem } [RequiredByNativeCode] - static MonoCreateAssetItem[] ExtractCreateAssetMenuItems(Assembly assembly) + static MonoCreateAssetItem[] ExtractCreateAssetMenuItems() { var result = new List(); @@ -166,6 +166,11 @@ static MonoCreateAssetItem[] ExtractCreateAssetMenuItems(Assembly assembly) if (!System.IO.Path.HasExtension(fileName)) fileName = fileName + ".asset"; + // trim the trailing space from the menu name: + // 1. visually it is hard to differentialte menu names with or without spaces. + // 2. when asset menu is searched, it will search the trimmed menu name, so it will create a edge case where a menu name with space could not be found after creation. + menuItemName = menuItemName.TrimEnd(); + var item = new MonoCreateAssetItem { menuItem = menuItemName, diff --git a/Editor/Mono/Audio/Mixer/GUI/AudioMixerWindow.cs b/Editor/Mono/Audio/Mixer/GUI/AudioMixerWindow.cs index 199bacfb82..2975edd030 100644 --- a/Editor/Mono/Audio/Mixer/GUI/AudioMixerWindow.cs +++ b/Editor/Mono/Audio/Mixer/GUI/AudioMixerWindow.cs @@ -238,8 +238,14 @@ static List FindAllAudioMixerControllers() foreach (var prop in AssetDatabase.FindAllAssets(new SearchFilter() { classNames = new[] { "AudioMixerController" } })) { var controller = prop.pptrValue as AudioMixerController; + if (controller) - result.Add(controller); + { + if (controller.HasValidSnapshots()) + result.Add(controller); + else + Debug.LogError($"Can not display audio mixer window for '{controller.name}' as it could not be properly initialized. The mixer asset is possibly corrupted."); + } } return result; } @@ -341,6 +347,12 @@ public void UndoRedoPerformed(in UndoRedoInfo info) void OnMixerControllerChanged() { + if (m_Controller != null && !m_Controller.HasValidSnapshots()) + { + Debug.LogError($"Can not display audio mixer window for '{m_Controller.name}' as it could not be properly initialized. The mixer asset is possibly corrupted."); + return; + } + if (m_Controller) m_Controller.ClearEventHandlers(); @@ -369,9 +381,15 @@ void DetectControllerChange() AudioMixerController oldController = m_Controller; if (Selection.activeObject is AudioMixerController) m_Controller = Selection.activeObject as AudioMixerController; + if (m_Controller != oldController) { - OnMixerControllerChanged(); + if (m_Controller.HasValidSnapshots()) + OnMixerControllerChanged(); + else + { + Debug.LogError($"Can not display audio mixer window for '{m_Controller.name}' as it could not be properly initialized. The mixer asset is possibly corrupted."); + } } } diff --git a/Editor/Mono/BuildPipeline.bindings.cs b/Editor/Mono/BuildPipeline.bindings.cs index 9d3bc9cd6e..2fe82e481f 100644 --- a/Editor/Mono/BuildPipeline.bindings.cs +++ b/Editor/Mono/BuildPipeline.bindings.cs @@ -183,7 +183,10 @@ public enum BuildAssetBundleOptions //AssetBundleAllowEditorOnlyScriptableObjects = 1 << 14, //Removes the Unity Version number in the Archive File & Serialized File headers during the build. - AssetBundleStripUnityVersion = 32768 // 1 << 15 + AssetBundleStripUnityVersion = 32768, // 1 << 15 + + // Calculate bundle hash on the bundle content + UseContentHash = 65536 // 1 << 16 } // Keep in sync with CanAppendBuild in EditorUtility.h @@ -381,6 +384,11 @@ private static BuildReport BuildPlayer(string[] scenes, string locationPathName, { return BuildPlayerInternal(scenes, locationPathName, assetBundleManifestPath, buildTargetGroup, target, subtarget, options, extraScriptingDefines); } + catch (System.ArgumentException argumentException) + { + Debug.LogException(argumentException); + return null; + } catch (System.Exception exception) { // In some case BuildPlayer might let a null reference exception fall through. Prevent data loss by just exiting. @@ -498,8 +506,8 @@ public static string BuildStreamedSceneAssetBundle(string[] levels, string locat private static BuildReport BuildPlayerInternal(string[] levels, string locationPathName, string assetBundleManifestPath, BuildTargetGroup buildTargetGroup, BuildTarget target, int subtarget, BuildOptions options, string[] extraScriptingDefines) { - if (!BuildPlayerWindow.DefaultBuildMethods.IsBuildPathValid(locationPathName)) - throw new Exception("Invalid Build Path: " + locationPathName); + if (!BuildPlayerWindow.DefaultBuildMethods.IsBuildPathValid(locationPathName, out var msg)) + throw new ArgumentException($"Invalid build path: '{locationPathName}'. {msg}"); return BuildPlayerInternalNoCheck(levels, locationPathName, assetBundleManifestPath, buildTargetGroup, target, subtarget, options, extraScriptingDefines, false); } @@ -738,6 +746,7 @@ private static bool DoesBuildTargetSupportPlayerConnectionPlayerToEditor(BuildTa targetPlatform == BuildTarget.StandaloneWindows || targetPlatform == BuildTarget.StandaloneWindows64 || targetPlatform == BuildTarget.StandaloneLinux64 || + targetPlatform == BuildTarget.iOS || // Android: support connection from player to Editor in both cases // connecting to 127.0.0.1 (when both Editor and Android are on localhost using USB cable) // connecting to , the Android and PC has to be on the same subnet diff --git a/Editor/Mono/BuildPipeline/BuildPlatform.cs b/Editor/Mono/BuildPipeline/BuildPlatform.cs index 794017dd2a..08dc117a97 100644 --- a/Editor/Mono/BuildPipeline/BuildPlatform.cs +++ b/Editor/Mono/BuildPipeline/BuildPlatform.cs @@ -191,10 +191,5 @@ public List GetValidPlatforms() { return GetValidPlatforms(false); } - - public static string[] GetValidPlatformNames() - { - return instance.GetValidPlatforms().ConvertAll(platform => platform.name).ToArray(); - } } } diff --git a/Editor/Mono/BuildPipeline/DesktopStandaloneBuildWindowExtension.cs b/Editor/Mono/BuildPipeline/DesktopStandaloneBuildWindowExtension.cs index 08000cf330..76f9f4afa6 100644 --- a/Editor/Mono/BuildPipeline/DesktopStandaloneBuildWindowExtension.cs +++ b/Editor/Mono/BuildPipeline/DesktopStandaloneBuildWindowExtension.cs @@ -19,15 +19,26 @@ internal abstract class DesktopStandaloneBuildWindowExtension : DefaultBuildWind protected bool m_HasMonoPlayers; protected bool m_HasIl2CppPlayers; protected bool m_HasCoreCLRPlayers; - protected bool m_HasServerPlayers; + protected bool m_HasServerMonoPlayers; + protected bool m_HasServerIl2CppPlayers; protected bool m_IsRunningOnHostPlatform; + public bool MonoPlayersInstalled(NamedBuildTarget namedBuildTarget) + { + return namedBuildTarget == NamedBuildTarget.Server ? m_HasServerMonoPlayers : m_HasMonoPlayers; + } + + public bool Il2CppPlayersInstalled(NamedBuildTarget namedBuildTarget) + { + return namedBuildTarget == NamedBuildTarget.Server ? m_HasServerIl2CppPlayers : m_HasIl2CppPlayers; + } + public static void SetArchitectureForPlatform(BuildTarget buildTarget, OSArchitecture architecture) { EditorUserBuildSettings.SetPlatformSettings(BuildPipeline.GetBuildTargetName(buildTarget), EditorUserBuildSettings.kSettingArchitecture, architecture.ToString().ToLower()); } - public DesktopStandaloneBuildWindowExtension(bool hasMonoPlayers, bool hasIl2CppPlayers, bool hasCoreCLRPlayers, bool hasServerPlayers) + public DesktopStandaloneBuildWindowExtension(bool hasMonoPlayers, bool hasIl2CppPlayers, bool hasCoreCLRPlayers, bool hasServerMonoPlayers, bool hasServerIl2CppPlayers) { SetupStandaloneSubtargets(); @@ -35,7 +46,8 @@ public DesktopStandaloneBuildWindowExtension(bool hasMonoPlayers, bool hasIl2Cpp m_HasIl2CppPlayers = hasIl2CppPlayers; m_HasCoreCLRPlayers = hasCoreCLRPlayers; m_HasMonoPlayers = hasMonoPlayers; - m_HasServerPlayers = hasServerPlayers; + m_HasServerMonoPlayers = hasServerMonoPlayers; + m_HasServerIl2CppPlayers = hasServerIl2CppPlayers; } private void SetupStandaloneSubtargets() @@ -84,30 +96,6 @@ struct BuildTargetInfo public OSArchitecture architecture; } - private static Dictionary GetArchitecturesForPlatform(BuildTarget target) - { - switch (target) - { - case BuildTarget.StandaloneWindows: - case BuildTarget.StandaloneWindows64: - return new Dictionary - { - { EditorGUIUtility.TrTextContent("Intel 64-bit"), new BuildTargetInfo - { - buildTarget = BuildTarget.StandaloneWindows64, - architecture = OSArchitecture.x64 - }}, - { EditorGUIUtility.TrTextContent("Intel 32-bit"), new BuildTargetInfo - { - buildTarget = BuildTarget.StandaloneWindows, - architecture = OSArchitecture.x86 - }}, - }; - default: - return null; - } - } - private static BuildTarget DefaultTargetForPlatform(BuildTarget target) { switch (target) @@ -186,30 +174,7 @@ public override void ShowPlatformBuildOptions() int selectedIndex = Math.Max(0, Array.IndexOf(m_StandaloneSubtargets, DefaultTargetForPlatform(selectedTarget))); int newIndex = EditorGUILayout.Popup(m_StandaloneTarget, selectedIndex, m_StandaloneSubtargetStrings); - if (newIndex == selectedIndex) - { - Dictionary architectures = GetArchitecturesForPlatform(selectedTarget); - if (null != architectures) - { - // Display architectures for the current target platform - GUIContent[] architectureNames = new List(architectures.Keys).ToArray(); - int selectedArchitecture = 0; - - // Grab m_Architecture index for currently selected target - foreach (var architecture in architectures) - { - if (architecture.Value.buildTarget == selectedTarget) - { - selectedArchitecture = System.Math.Max(0, System.Array.IndexOf(architectureNames, architecture.Key)); - break; - } - } - - selectedArchitecture = EditorGUILayout.Popup(m_Architecture, selectedArchitecture, architectureNames); - newTarget = architectures[architectureNames[selectedArchitecture]]; - } - } - else + if (newIndex != selectedIndex) { newTarget = DefaultArchitectureForTarget(m_StandaloneSubtargets[newIndex]); } @@ -223,7 +188,6 @@ public override void ShowPlatformBuildOptions() } ShowArchitectureSpecificOptions(); - ShowBackendErrorIfNeeded(); } @@ -248,12 +212,15 @@ protected virtual string GetCannotBuildPlayerInCurrentSetupError() if (namedBuildTarget == NamedBuildTarget.Server) { - if(!m_HasServerPlayers) - return $"Dedicated Server support for {GetHostPlatformName()} is not installed."; + if (scriptingBackend == ScriptingImplementation.Mono2x && !m_HasServerMonoPlayers) + return $"Dedicated Server support (Mono) for {GetHostPlatformName()} is not installed."; if (scriptingBackend == ScriptingImplementation.IL2CPP && !m_IsRunningOnHostPlatform) return string.Format("{0} IL2CPP player can only be built on {0}.", GetHostPlatformName()); + if (scriptingBackend == ScriptingImplementation.IL2CPP && !m_HasServerIl2CppPlayers) + return $"Dedicated Server support (IL2CPP) for {GetHostPlatformName()} is not installed."; + return null; } @@ -261,7 +228,7 @@ protected virtual string GetCannotBuildPlayerInCurrentSetupError() { case ScriptingImplementation.Mono2x: { - if (!m_HasMonoPlayers) + if (!MonoPlayersInstalled(namedBuildTarget)) return "Currently selected scripting backend (Mono) is not installed."; break; } @@ -276,7 +243,7 @@ protected virtual string GetCannotBuildPlayerInCurrentSetupError() { if (!m_IsRunningOnHostPlatform) return string.Format("{0} IL2CPP player can only be built on {0}.", GetHostPlatformName()); - if (!m_HasIl2CppPlayers) + if (!Il2CppPlayersInstalled(namedBuildTarget)) return "Currently selected scripting backend (IL2CPP) is not installed."; break; } @@ -286,8 +253,6 @@ protected virtual string GetCannotBuildPlayerInCurrentSetupError() } } - - return null; } diff --git a/Editor/Mono/BuildPipeline/DesktopStandalonePostProcessor.cs b/Editor/Mono/BuildPipeline/DesktopStandalonePostProcessor.cs index f958200bbb..7daf4ab71d 100644 --- a/Editor/Mono/BuildPipeline/DesktopStandalonePostProcessor.cs +++ b/Editor/Mono/BuildPipeline/DesktopStandalonePostProcessor.cs @@ -5,6 +5,7 @@ using System; using UnityEditor; using UnityEditor.Build; +using UnityEditor.Build.Reporting; using UnityEditor.Modules; using UnityEditorInternal; using UnityEngine; @@ -22,7 +23,7 @@ protected virtual string GetVariationName(BuildPostProcessArgs args) GetPlatformString(args), GetServer(args) ? "server" : "player", GetDevelopment(args) ? "development" : "nondevelopment", - GetScriptingBackend(args).ToString().ToLower()); + GetScriptingBackend(args).ToString().ToLowerInvariant()); } protected bool GetServer(BuildPostProcessArgs args) => @@ -66,9 +67,9 @@ protected DesktopStandalonePostProcessor(bool hasMonoPlayers, bool hasIl2CppPlay m_HasServerCoreCLRPlayers = hasServerCoreCLRPlayers; } - public override string PrepareForBuild(BuildOptions options, BuildTarget target) + public override string PrepareForBuild(BuildPlayerOptions buildOptions) { - var namedBuildTarget = NamedBuildTarget.FromActiveSettings(target); + var namedBuildTarget = NamedBuildTarget.FromActiveSettings(buildOptions.target); var isServer = namedBuildTarget == NamedBuildTarget.Server; switch (PlayerSettings.GetScriptingBackend(namedBuildTarget)) @@ -96,7 +97,7 @@ public override string PrepareForBuild(BuildOptions options, BuildTarget target) return $"Unknown scripting backend: {PlayerSettings.GetScriptingBackend(namedBuildTarget)}"; } - return base.PrepareForBuild(options, target); + return base.PrepareForBuild(buildOptions); } internal class ScriptingImplementations : DefaultScriptingImplementations diff --git a/Editor/Mono/BuildPipeline/Il2Cpp/IL2CPPUtils.cs b/Editor/Mono/BuildPipeline/Il2Cpp/IL2CPPUtils.cs index 46b8d4f9e5..a860e27b47 100644 --- a/Editor/Mono/BuildPipeline/Il2Cpp/IL2CPPUtils.cs +++ b/Editor/Mono/BuildPipeline/Il2Cpp/IL2CPPUtils.cs @@ -458,6 +458,54 @@ internal static string GetExePath(string toolName) return $"{deployDirectory}/{expectedToolExecutableName}"; } + internal static string GetLibrarySearchPaths(string name, string tfm, string customRoot = null) + { + var il2CppFolder = GetIl2CppFolder(out var isDevelopmentLocation); + var expectedToolExecutableName = $"{name}.dll"; + + if (isDevelopmentLocation) + { + // Locating the correct development build to use is a little tricky. Complications come from + // 1) We don't know if the Debug or Release build is desired. To overcome this we will pick whichever was modified most recently + + var topLevel = il2CppFolder; + if (customRoot != null) + topLevel = Path.Combine(topLevel, customRoot); + + var toolBinDirectory = Path.Combine(topLevel, name, "bin").ToNPath(); + var candidates = toolBinDirectory.Files($"*{expectedToolExecutableName}", recurse: true) + .Where(f => f.Parent.FileName == tfm) + .OrderByDescending(f => f.GetLastWriteTimeUtc()) + .ToArray(); + + if (candidates.Length == 0) + throw new InvalidOperationException($"{name} does not appear to be built in {il2CppFolder}"); + + return candidates[0].Parent.ToString(); + } + + throw new ArgumentException($"Could not locate assembly for {name}"); + } + + internal static string ConstructBeeLibrarySearchPath() + { + var projectBinDirs = new[] + { + GetLibrarySearchPaths("Unity.Options", "netstandard2.0", "repos/UnityOptions"), + GetLibrarySearchPaths("Unity.Linker.Api", "netstandard2.0"), + GetLibrarySearchPaths("Unity.IL2CPP.Api", "netstandard2.0"), + GetLibrarySearchPaths("Unity.Api.Attributes", "netstandard2.0"), + GetLibrarySearchPaths("Unity.IL2CPP.Bee.IL2CPPExeCompileCppBuildProgram.Data", "netstandard2.0"), + GetLibrarySearchPaths("Unity.IL2CPP.Bee.BuildLogic", "net6.0"), + // Now the quirky part. We need to locate the platform build logic assemblies. + // While il2cpp will have these during some build scenarios (IDE build or build.pl) + // the project that will always have all of the is il2cpp-compile + GetExePath("il2cpp-compile").ToNPath().Parent + }; + + return projectBinDirs.Aggregate(string.Empty, (accum, next) => $"{accum}{Path.PathSeparator}{next}"); + } + internal static string GetAdditionalArguments() { var arguments = new List(); diff --git a/Editor/Mono/BuildPipeline/NamedBuildTarget.cs b/Editor/Mono/BuildPipeline/NamedBuildTarget.cs index 85fec0e406..5d6e08be5e 100644 --- a/Editor/Mono/BuildPipeline/NamedBuildTarget.cs +++ b/Editor/Mono/BuildPipeline/NamedBuildTarget.cs @@ -41,9 +41,11 @@ namespace UnityEditor.Build public static readonly NamedBuildTarget WebGL = new NamedBuildTarget("WebGL"); public static readonly NamedBuildTarget WindowsStoreApps = new NamedBuildTarget("Windows Store Apps"); public static readonly NamedBuildTarget PS4 = new NamedBuildTarget("PS4"); + public static readonly NamedBuildTarget PS5 = new NamedBuildTarget("PS5"); public static readonly NamedBuildTarget XboxOne = new NamedBuildTarget("XboxOne"); public static readonly NamedBuildTarget tvOS = new NamedBuildTarget("tvOS"); public static readonly NamedBuildTarget NintendoSwitch = new NamedBuildTarget("Nintendo Switch"); + [System.Obsolete("Stadia has been removed in 2023.1")] public static readonly NamedBuildTarget Stadia = new NamedBuildTarget("Stadia"); public static readonly NamedBuildTarget LinuxHeadlessSimulation = new NamedBuildTarget("LinuxHeadlessSimulation"); [System.Obsolete("CloudRendering is deprecated, please use LinuxHeadlessSimulation (UnityUpgradable) -> LinuxHeadlessSimulation", false)] @@ -98,8 +100,6 @@ public static NamedBuildTarget FromBuildTargetGroup(BuildTargetGroup buildTarget return NamedBuildTarget.tvOS; case BuildTargetGroup.Switch: return NamedBuildTarget.NintendoSwitch; - case BuildTargetGroup.Stadia: - return NamedBuildTarget.Stadia; case BuildTargetGroup.LinuxHeadlessSimulation: return NamedBuildTarget.LinuxHeadlessSimulation; case BuildTargetGroup.EmbeddedLinux: diff --git a/Editor/Mono/BuildPipeline/PostprocessBuildPlayer.cs b/Editor/Mono/BuildPipeline/PostprocessBuildPlayer.cs index 697ad5a5f4..f8ce4c306c 100644 --- a/Editor/Mono/BuildPipeline/PostprocessBuildPlayer.cs +++ b/Editor/Mono/BuildPipeline/PostprocessBuildPlayer.cs @@ -82,12 +82,12 @@ internal static string GetStreamingAssetsBundleManifestPath() } [RequiredByNativeCode] - static public string PrepareForBuild(BuildOptions options, BuildTargetGroup targetGroup, BuildTarget target) + static public string PrepareForBuild(BuildPlayerOptions buildOptions) { - var postprocessor = ModuleManager.GetBuildPostProcessor(targetGroup, target); + var postprocessor = ModuleManager.GetBuildPostProcessor(buildOptions.targetGroup, buildOptions.target); if (postprocessor == null) return null; - return postprocessor.PrepareForBuild(options, target); + return postprocessor.PrepareForBuild(buildOptions); } static public string GetExtensionForBuildTarget(BuildTargetGroup targetGroup, BuildTarget target, int subtarget, BuildOptions options) @@ -284,7 +284,10 @@ static public void Postprocess(BuildTargetGroup targetGroup, BuildTarget target, // Rethrow exceptions during build postprocessing as BuildFailedException, so we don't pretend the build was fine. throw new BuildFailedException(e); } - report.AddAppendix(props); + if (props != null) + { + report.AddAppendix(props); + } return; } diff --git a/Editor/Mono/BuildPlayerSceneTreeView.cs b/Editor/Mono/BuildPlayerSceneTreeView.cs index c2aacb8a9c..115c8792d7 100644 --- a/Editor/Mono/BuildPlayerSceneTreeView.cs +++ b/Editor/Mono/BuildPlayerSceneTreeView.cs @@ -13,21 +13,22 @@ internal class BuildPlayerSceneTreeViewItem : TreeViewItem { private const string kAssetsFolder = "Assets/"; private const string kSceneExtension = ".unity"; - public static int kInvalidCounter = -1; + private string m_FullName; + public bool active; public int counter; - public string fullName; - public GUID guid; - public void UpdateName() + public string fullName { - var name = AssetDatabase.GUIDToAssetPath(guid.ToString()); - if (name != fullName) + get => m_FullName; + set { - fullName = name; + if (m_FullName == value) + return; - displayName = fullName; + m_FullName = value; + displayName = m_FullName; if (displayName.StartsWith(kAssetsFolder)) displayName = displayName.Remove(0, kAssetsFolder.Length); var ext = displayName.LastIndexOf(kSceneExtension); @@ -35,13 +36,21 @@ public void UpdateName() displayName = displayName.Substring(0, ext); } } + public GUID guid; + + public void UpdateName() + { + var name = AssetDatabase.GUIDToAssetPath(guid.ToString()); + if (!string.IsNullOrEmpty(name) && name != fullName) + fullName = name; + } - public BuildPlayerSceneTreeViewItem(int id, int depth, GUID g, bool state) : base(id, depth) + public BuildPlayerSceneTreeViewItem(EditorBuildSettingsScene scene) : base(scene.guid.GetHashCode(), 0) { - active = state; + active = scene.enabled; counter = kInvalidCounter; - guid = g; - fullName = ""; + guid = scene.guid; + fullName = scene.path; UpdateName(); } } @@ -71,7 +80,7 @@ protected override TreeViewItem BuildRoot() List scenes = new List(EditorBuildSettings.scenes); foreach (var sc in scenes) { - var item = new BuildPlayerSceneTreeViewItem(sc.guid.GetHashCode(), 0, sc.guid, sc.enabled); + var item = new BuildPlayerSceneTreeViewItem(sc); root.AddChild(item); } return root; @@ -314,6 +323,11 @@ public EditorBuildSettingsScene[] GetSceneList() { var sceneItem = rootItem.children[index] as BuildPlayerSceneTreeViewItem; sceneList[index] = new EditorBuildSettingsScene(sceneItem.fullName, sceneItem.active); + + // If the scene was deleted AssetPathToGUID may not work and the guid will be lost + // In that case restore it to the previous value + if (sceneList[index].guid.Empty() && !sceneItem.guid.Empty()) + sceneList[index].guid = sceneItem.guid; } return sceneList; } diff --git a/Editor/Mono/BuildPlayerWindow.cs b/Editor/Mono/BuildPlayerWindow.cs index 49a8f1b160..4788522ffa 100644 --- a/Editor/Mono/BuildPlayerWindow.cs +++ b/Editor/Mono/BuildPlayerWindow.cs @@ -47,6 +47,7 @@ class Styles public string noModuleLoaded = L10n.Tr("No {0} module loaded."); public GUIContent openDownloadPage = EditorGUIUtility.TrTextContent("Open Download Page"); public GUIContent installModuleWithHub = EditorGUIUtility.TrTextContent("Install with Unity Hub"); + public string EditorWillNeedToBeReloaded = L10n.Tr("Note: Editor will need to be restarted to load any newly installed modules"); public string infoText = L10n.Tr("{0} is not included in your Unity Pro license. Your {0} build will include a Unity Personal Edition splash screen.\n\nYou must be eligible to use Unity Personal Edition to use this build option. Please refer to our EULA for further information."); public GUIContent eula = EditorGUIUtility.TrTextContent("Eula"); public string addToYourPro = L10n.Tr("Add {0} to your Unity Pro license"); @@ -76,7 +77,7 @@ public GUIContent GetDownloadErrorForTarget(BuildTarget target) public GUIContent explicitNullChecks = EditorGUIUtility.TrTextContent("Explicit Null Checks"); public GUIContent explicitDivideByZeroChecks = EditorGUIUtility.TrTextContent("Divide By Zero Checks"); public GUIContent explicitArrayBoundsChecks = EditorGUIUtility.TrTextContent("Array Bounds Checks"); - public GUIContent learnAboutUnityCloudBuild = EditorGUIUtility.TrTextContent("Learn about Unity Cloud Build"); + public GUIContent learnAboutUnityCloudBuild = EditorGUIUtility.TrTextContent("Learn about Unity Build Automation"); public GUIContent compressionMethod = EditorGUIUtility.TrTextContent("Compression Method", "Compression applied to Player data (scenes and resources).\nDefault - none or default platform compression.\nLZ4 - fast compression suitable for Development Builds.\nLZ4HC - higher compression rate variance of LZ4, causes longer build times. Works best for Release Builds."); public readonly GUIContent assetImportOverrides = EditorGUIUtility.TrTextContent("Asset Import Overrides", "Asset import overrides for local development. Reducing maximum texture size or compression settings can speed up asset imports and platform switches."); @@ -239,6 +240,10 @@ void AddOpenScenes() for (int i = 0; i < SceneManager.sceneCount; i++) { Scene scene = SceneManager.GetSceneAt(i); + + if (EditorSceneManager.IsAuthoringScene(scene)) + continue; + if (scene.path.Length == 0 && !EditorSceneManager.SaveScene(scene, "", false)) continue; @@ -542,73 +547,6 @@ static bool IsAnyStandaloneModuleLoaded() ModuleManager.IsPlatformSupportLoadedByBuildTarget(BuildTarget.StandaloneWindows); } - static bool IsColorSpaceValid(BuildPlatform platform) - { - if (PlayerSettings.colorSpace == ColorSpace.Linear) - { - var hasMinGraphicsAPI = true; - - var apis = PlayerSettings.GetGraphicsAPIs(platform.defaultTarget); - if (platform.namedBuildTarget == NamedBuildTarget.Android) - { - hasMinGraphicsAPI = (apis.Contains(GraphicsDeviceType.Vulkan) || apis.Contains(GraphicsDeviceType.OpenGLES3)) && !apis.Contains(GraphicsDeviceType.OpenGLES2); - } - else if (platform.namedBuildTarget == NamedBuildTarget.iOS || platform.namedBuildTarget == NamedBuildTarget.tvOS) - { - hasMinGraphicsAPI = !apis.Contains(GraphicsDeviceType.OpenGLES3) && !apis.Contains(GraphicsDeviceType.OpenGLES2); - } - else if (platform.namedBuildTarget == NamedBuildTarget.WebGL) - { - // must have OpenGLES3-only - hasMinGraphicsAPI = apis.Contains(GraphicsDeviceType.OpenGLES3) && !apis.Contains(GraphicsDeviceType.OpenGLES2); - } - - return hasMinGraphicsAPI; - } - else - { - return true; - } - } - - static bool IsHDRCubemapEncodingValid(BuildPlatform platform) - { - var encoding = PlayerSettings.GetHDRCubemapEncodingQualityForPlatformGroup(platform.namedBuildTarget.ToBuildTargetGroup()); - return IsGITextureEncodingValid(platform, encoding == HDRCubemapEncodingQuality.Low); - } - - static bool IsLightmapEncodingValid(BuildPlatform platform) - { - var encoding = PlayerSettings.GetLightmapEncodingQualityForPlatformGroup(platform.namedBuildTarget.ToBuildTargetGroup()); - return IsGITextureEncodingValid(platform, encoding == LightmapEncodingQuality.Low); - } - - static bool IsGITextureEncodingValid(BuildPlatform platform, bool isLowQuality) - { - if (isLowQuality) - return true; - - var hasMinGraphicsAPI = true; - - if (platform.namedBuildTarget == NamedBuildTarget.iOS) - { - var apis = PlayerSettings.GetGraphicsAPIs(BuildTarget.iOS); - hasMinGraphicsAPI = apis.Contains(GraphicsDeviceType.Metal) && !apis.Contains(GraphicsDeviceType.OpenGLES3) && !apis.Contains(GraphicsDeviceType.OpenGLES2); - } - else if (platform.namedBuildTarget == NamedBuildTarget.tvOS) - { - var apis = PlayerSettings.GetGraphicsAPIs(BuildTarget.tvOS); - hasMinGraphicsAPI = apis.Contains(GraphicsDeviceType.Metal) && !apis.Contains(GraphicsDeviceType.OpenGLES3) && !apis.Contains(GraphicsDeviceType.OpenGLES2); - } - else if (platform.namedBuildTarget == NamedBuildTarget.Android) - { - var apis = PlayerSettings.GetGraphicsAPIs(BuildTarget.Android); - hasMinGraphicsAPI = (apis.Contains(GraphicsDeviceType.Vulkan) || apis.Contains(GraphicsDeviceType.OpenGLES3)) && !apis.Contains(GraphicsDeviceType.OpenGLES2); - } - - return hasMinGraphicsAPI; - } - static bool IsVirtualTexturingSettingsValid(BuildPlatform platform) { if (!PlayerSettings.GetVirtualTexturingSupportEnabled()) @@ -798,6 +736,7 @@ void ShowBuildTargetSettings() Help.BrowseURL(url); } } + GUILayout.Label(styles.EditorWillNeedToBeReloaded, EditorStyles.wordWrappedMiniLabel); GUIBuildButtons(false, false, false, platform, postprocessor); return; } @@ -1070,25 +1009,7 @@ private static void GUIBuildButtons(IBuildWindowExtension buildWindowExtension, // Disable the 'Build' and 'Build And Run' buttons when the project setup doesn't satisfy the platform requirements if (enableBuildButton && enableBuildAndRunButton) { - if (!IsColorSpaceValid(platform)) - { - enableBuildAndRunButton = false; - enableBuildButton = false; - EditorGUILayout.HelpBox(styles.invalidColorSpaceMessage); - } - else if (!IsLightmapEncodingValid(platform)) - { - enableBuildAndRunButton = false; - enableBuildButton = false; - EditorGUILayout.HelpBox(styles.invalidLightmapEncodingMessage); - } - else if (!IsHDRCubemapEncodingValid(platform)) - { - enableBuildAndRunButton = false; - enableBuildButton = false; - EditorGUILayout.HelpBox(styles.invalidHDRCubemapEncodingMessage); - } - else if (!IsVirtualTexturingSettingsValid(platform)) + if (!IsVirtualTexturingSettingsValid(platform)) { enableBuildAndRunButton = false; enableBuildButton = false; diff --git a/Editor/Mono/BuildPlayerWindowBuildMethods.cs b/Editor/Mono/BuildPlayerWindowBuildMethods.cs index 3c93a95af5..7f78736505 100644 --- a/Editor/Mono/BuildPlayerWindowBuildMethods.cs +++ b/Editor/Mono/BuildPlayerWindowBuildMethods.cs @@ -9,6 +9,7 @@ using System.Collections; using System.IO; using System; +using System.Linq; using UnityEditor.Build.Reporting; using UnityEditor.Connect; using UnityEditor.Profiling; @@ -313,7 +314,7 @@ internal static BuildPlayerOptions GetBuildPlayerOptionsInternal(bool askForBuil EditorBuildSettingsScene[] editorScenes = EditorBuildSettings.scenes; foreach (EditorBuildSettingsScene scene in editorScenes) { - if (scene.enabled) + if (scene.enabled && !string.IsNullOrEmpty(scene.path)) scenesList.Add(scene.path); } @@ -359,10 +360,26 @@ private static bool PickBuildLocation(BuildTargetGroup targetGroup, BuildTarget } string title = "Build " + BuildPlatforms.instance.GetBuildTargetDisplayName(targetGroup, target, subtarget); - string path = EditorUtility.SaveBuildPanel(target, title, defaultFolder, defaultName, extension, out updateExistingBuild); - if (path == string.Empty) - return false; + string path; + bool isValidPath = false; + do + { + path = EditorUtility.SaveBuildPanel(target, title, defaultFolder, defaultName, extension, out updateExistingBuild); + if (path == string.Empty) + return false; + + if (IsBuildPathValid(path, out var msg)) + { + isValidPath = true; + } + else if (!EditorUtility.DisplayDialog("Invalid build path", msg, "Ok", "Cancel")) + { + Debug.LogError($"Invalid build path: '{path}'. {msg}"); + return false; + } + + } while (!isValidPath); if (isWindowsStandalone) { @@ -370,9 +387,6 @@ private static bool PickBuildLocation(BuildTargetGroup targetGroup, BuildTarget path = Path.Combine(path, Paths.MakeValidFileName(PlayerSettings.productName) + '.' + extension); } - if (!IsBuildPathValid(path)) - return false; - // Enforce extension if needed if (extension != string.Empty && FileUtil.GetPathExtension(path).ToLower() != extension) path += '.' + extension; @@ -413,14 +427,14 @@ private static string NormalizePath(string path) fullPath = string.IsNullOrEmpty(fullPath) ? string.Empty : Path.GetFullPath(fullPath); - fullPath = fullPath.ToLower(); if (Path.DirectorySeparatorChar == '/') return fullPath; return fullPath.Replace(Path.DirectorySeparatorChar, '/'); } - internal static bool IsBuildPathValid(string path) + internal static bool IsBuildPathValid(string path, out string errorMessage) { + errorMessage = default; var cleanedPath = NormalizePath(path); if (cleanedPath.Equals(string.Empty) && IsInstallInBuildFolderOption()) @@ -428,22 +442,37 @@ internal static bool IsBuildPathValid(string path) var basePath = NormalizePath(Application.dataPath + "/../"); - var assetsPath = NormalizePath(basePath + "/Assets"); - var settingsPath = NormalizePath(basePath + "/ProjectSettings"); - var tempPath = NormalizePath(basePath + "/Temp"); - var libraryPath = NormalizePath(basePath + "/Library"); - var userSettingsPath = NormalizePath(basePath + "/UserSettings"); - var userDesktopPath = NormalizePath(Environment.GetFolderPath(Environment.SpecialFolder.Desktop)); + // Allow build into the Temp folder (used by Unity TestRunner) + var invalidPaths = new[] + { + NormalizePath(basePath + "/Assets"), + NormalizePath(basePath + "/ProjectSettings"), + NormalizePath(basePath + "/Library"), + NormalizePath(basePath + "/Packages"), + NormalizePath(basePath + "/UserSettings") + }; + + var invalidPath = invalidPaths.FirstOrDefault(p => cleanedPath.Contains(p, StringComparison.OrdinalIgnoreCase)); + if (!string.IsNullOrEmpty(invalidPath)) + { + var dirName = Path.GetFileName(invalidPath); + errorMessage = $"The '{dirName}' directory is an internal work directory of Unity and " + + "projects should not be built inside it. Please choose another directory for the build output."; + return false; + } - if (basePath.Contains(cleanedPath) || - cleanedPath == assetsPath || - cleanedPath == settingsPath || - cleanedPath == tempPath || - cleanedPath == libraryPath || - cleanedPath == userSettingsPath || - cleanedPath == userDesktopPath) + if (cleanedPath.Equals(basePath, StringComparison.OrdinalIgnoreCase)) + { + errorMessage = "The project root directory should not be used as a build output directory. " + + "Please create a subdirectory for the build output."; + return false; + } + + var userDesktopPath = NormalizePath(Environment.GetFolderPath(Environment.SpecialFolder.Desktop)); + if (cleanedPath.Equals(userDesktopPath, StringComparison.OrdinalIgnoreCase)) { - Debug.LogError("Invalid build path: " + cleanedPath); + errorMessage = "The desktop directory should not be used as a build output directory. " + + "Please create a subdirectory for the build output."; return false; } diff --git a/Editor/Mono/BuildTarget.cs b/Editor/Mono/BuildTarget.cs index c54218c403..eed1434c7e 100644 --- a/Editor/Mono/BuildTarget.cs +++ b/Editor/Mono/BuildTarget.cs @@ -121,6 +121,7 @@ public enum BuildTarget [System.Obsolete("Lumin has been removed in 2022.2")] Lumin = 39, + [System.Obsolete("Stadia has been removed in 2023.1")] Stadia = 40, [System.Obsolete("CloudRendering is deprecated, please use LinuxHeadlessSimulation (UnityUpgradable) -> LinuxHeadlessSimulation", false)] diff --git a/Editor/Mono/BuildTargetConverter.cs b/Editor/Mono/BuildTargetConverter.cs index 1b71ce181e..7f32bd4a99 100644 --- a/Editor/Mono/BuildTargetConverter.cs +++ b/Editor/Mono/BuildTargetConverter.cs @@ -46,8 +46,6 @@ internal static class BuildTargetConverter return RuntimePlatform.GameCoreXboxSeries; case BuildTarget.GameCoreXboxOne: return RuntimePlatform.GameCoreXboxOne; - case BuildTarget.Stadia: - return RuntimePlatform.Stadia; case BuildTarget.EmbeddedLinux: return RuntimePlatform.EmbeddedLinuxArm64; case BuildTarget.QNX: diff --git a/Editor/Mono/BuildTargetGroup.cs b/Editor/Mono/BuildTargetGroup.cs index ccd4e6699c..6466bd45f0 100644 --- a/Editor/Mono/BuildTargetGroup.cs +++ b/Editor/Mono/BuildTargetGroup.cs @@ -98,6 +98,7 @@ public enum BuildTargetGroup [Obsolete("Lumin has been removed in 2022.2")] Lumin = 28, + [Obsolete("Stadia has been removed in 2023.1")] Stadia = 29, [System.Obsolete("CloudRendering is deprecated, please use LinuxHeadlessSimulation (UnityUpgradable) -> LinuxHeadlessSimulation", false)] diff --git a/Editor/Mono/Clipboard/Clipboard.cs b/Editor/Mono/Clipboard/Clipboard.cs index 85cd6d777a..7d9a17970f 100644 --- a/Editor/Mono/Clipboard/Clipboard.cs +++ b/Editor/Mono/Clipboard/Clipboard.cs @@ -18,6 +18,72 @@ internal static class Clipboard { static ClipboardState m_State = new ClipboardState(); + public static bool hasLong + { + get + { + FetchState(); + m_State.FetchLong(); + return m_State.m_HasLong.Value; + } + } + + public static long longValue + { + get + { + FetchState(); + m_State.FetchLong(); + return m_State.m_ValueLong; + } + + set => EditorGUIUtility.systemCopyBuffer = value.ToString(); + } + + public static bool hasUlong + { + get + { + FetchState(); + m_State.FetchUlong(); + return m_State.m_HasUlong.Value; + } + } + + public static ulong uLongValue + { + get + { + FetchState(); + m_State.FetchUlong(); + return m_State.m_ValueUlong; + } + + set => EditorGUIUtility.systemCopyBuffer = value.ToString(); + } + + public static bool hasUint + { + get + { + FetchState(); + m_State.FetchUint(); + return m_State.m_HasUint.Value; + } + } + + public static uint uIntValue + { + get + { + FetchState(); + m_State.FetchUint(); + return m_State.m_ValueUint; + } + + set => EditorGUIUtility.systemCopyBuffer = value.ToString(); + } + public static bool hasInteger { get diff --git a/Editor/Mono/Clipboard/ClipboardContextMenu.cs b/Editor/Mono/Clipboard/ClipboardContextMenu.cs index b0d30b69e5..649a46d0cb 100644 --- a/Editor/Mono/Clipboard/ClipboardContextMenu.cs +++ b/Editor/Mono/Clipboard/ClipboardContextMenu.cs @@ -164,10 +164,64 @@ internal static void SetupPropertyCopyPaste(SerializedProperty property, Generic } break; case SerializedPropertyType.Integer: - SetupAction(property, menu, evt, - p => Clipboard.integerValue = p.intValue, - p => Clipboard.hasInteger, - p => p.intValue = Clipboard.integerValue); + { + switch (property.numericType) + { + case SerializedPropertyNumericType.Int64: + SetupAction(property, menu, evt, + p => Clipboard.longValue = p.longValue, + p => Clipboard.hasLong, + p => p.longValue = Clipboard.longValue); + break; + + case SerializedPropertyNumericType.UInt64: + SetupAction(property, menu, evt, + p => Clipboard.uLongValue = p.ulongValue, + p => Clipboard.hasUlong, + p => p.ulongValue = Clipboard.uLongValue); + break; + + case SerializedPropertyNumericType.UInt32: + SetupAction(property, menu, evt, + p => Clipboard.uIntValue = p.uintValue, + p => Clipboard.hasUint, + p => p.uintValue = Clipboard.uIntValue); + break; + + case SerializedPropertyNumericType.UInt16: + SetupAction(property, menu, evt, + p => Clipboard.uIntValue = (System.UInt16)p.uintValue, + p => Clipboard.hasUint, + p => p.uintValue = (System.UInt16)Clipboard.uIntValue); + break; + + case SerializedPropertyNumericType.Int16: + SetupAction(property, menu, evt, + p => Clipboard.integerValue = (System.Int16)p.intValue, + p => Clipboard.hasInteger, + p => p.intValue = (System.UInt16)Clipboard.integerValue); + break; + case SerializedPropertyNumericType.UInt8: + SetupAction(property, menu, evt, + p => Clipboard.uIntValue = (System.Byte)p.uintValue, + p => Clipboard.hasUint, + p => p.uintValue = (System.Byte)Clipboard.uIntValue); + break; + case SerializedPropertyNumericType.Int8: + SetupAction(property, menu, evt, + p => Clipboard.integerValue = (System.Byte)p.intValue, + p => Clipboard.hasInteger, + p => p.intValue = (System.Byte)Clipboard.integerValue); + break; + + default: + SetupAction(property, menu, evt, + p => Clipboard.integerValue = p.intValue, + p => Clipboard.hasInteger, + p => p.intValue = Clipboard.integerValue); + break; + } + } break; case SerializedPropertyType.Float: SetupAction(property, menu, evt, diff --git a/Editor/Mono/Clipboard/ClipboardParser.cs b/Editor/Mono/Clipboard/ClipboardParser.cs index e3c9134e83..56ca046e64 100644 --- a/Editor/Mono/Clipboard/ClipboardParser.cs +++ b/Editor/Mono/Clipboard/ClipboardParser.cs @@ -187,6 +187,30 @@ public static bool ParseQuaternion(string text, out Quaternion res) return int.TryParse(text, out res); } + internal static bool? ParseLong(string text, out long res) + { + res = 0; + if (string.IsNullOrEmpty(text)) + return false; + return long.TryParse(text, out res); + } + + internal static bool? ParseUlong(string text, out ulong res) + { + res = 0; + if (string.IsNullOrEmpty(text)) + return false; + return ulong.TryParse(text, out res); + } + + internal static bool? ParseUint(string text, out uint res) + { + res = 0; + if(string.IsNullOrEmpty(text)) + return false; + return uint.TryParse(text, out res); + } + internal static bool? ParseFloat(string text, out float res) { res = 0; diff --git a/Editor/Mono/Clipboard/ClipboardState.cs b/Editor/Mono/Clipboard/ClipboardState.cs index b55d4cd7a0..45d13f5463 100644 --- a/Editor/Mono/Clipboard/ClipboardState.cs +++ b/Editor/Mono/Clipboard/ClipboardState.cs @@ -167,6 +167,33 @@ internal void FetchInteger() m_HasInteger = ClipboardParser.ParseInteger(m_RawContents, out m_ValueInteger); } + internal bool? m_HasLong; + internal long m_ValueLong; + + internal void FetchLong() + { + if (!m_HasLong.HasValue) + m_HasLong = ClipboardParser.ParseLong(m_RawContents, out m_ValueLong); + } + + internal bool? m_HasUlong; + internal ulong m_ValueUlong; + + internal void FetchUlong() + { + if (!m_HasUlong.HasValue) + m_HasUlong = ClipboardParser.ParseUlong(m_RawContents, out m_ValueUlong); + } + + internal bool? m_HasUint; + internal uint m_ValueUint; + + internal void FetchUint() + { + if (!m_HasUint.HasValue) + m_HasUint = ClipboardParser.ParseUint(m_RawContents, out m_ValueUint); + } + internal bool? m_HasFloat; internal float m_ValueFloat; internal void FetchFloat() diff --git a/Editor/Mono/Collab/CloudBuildStatus.cs b/Editor/Mono/Collab/CloudBuildStatus.cs deleted file mode 100644 index 6a88be7158..0000000000 --- a/Editor/Mono/Collab/CloudBuildStatus.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Unity C# reference source -// Copyright (c) Unity Technologies. For terms of use, see -// https://unity3d.com/legal/licenses/Unity_Reference_Only_License - - -using System; -using System.Runtime.InteropServices; -using UnityEngine.Scripting; - -namespace UnityEditor.Collaboration -{ - // Keep internal and undocumented until we expose more functionality - //*undocumented - [StructLayout(LayoutKind.Sequential)] - [UsedByNativeCode] - internal struct CloudBuildStatus - { - private string m_Platform; - private bool m_Complete; - private bool m_Successful; - - internal CloudBuildStatus(string platform = "", bool complete = false, bool success = false) - { - m_Platform = platform; - m_Complete = complete; - m_Successful = success; - } - - public string platform { get { return m_Platform; } } - public bool complete { get { return m_Complete; } } - public bool success { get { return m_Successful; } } - } -} diff --git a/Editor/Mono/Collab/Collab.bindings.cs b/Editor/Mono/Collab/Collab.bindings.cs deleted file mode 100644 index be25871f3b..0000000000 --- a/Editor/Mono/Collab/Collab.bindings.cs +++ /dev/null @@ -1,290 +0,0 @@ -// Unity C# reference source -// Copyright (c) Unity Technologies. For terms of use, see -// https://unity3d.com/legal/licenses/Unity_Reference_Only_License - -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using UnityEditor.Connect; -using UnityEngine; -using UnityEngine.Bindings; -using UnityEngine.Scripting; - -namespace UnityEditor.Collaboration -{ - [NativeHeader("Editor/Src/Collab/CollabInfo.h")] - [StructLayout(LayoutKind.Sequential)] - internal struct CollabInfo - { - public bool ready { get { return m_Ready; } } - public bool update { get { return m_Update; } } - public bool publish { get { return m_Publish; } } - public bool inProgress { get { return m_InProgress; } } - public bool maintenance { get { return m_Maintenance; } } - public bool conflict { get { return m_Conflict; } } - public bool refresh { get { return m_Refresh; } } - public bool seat { get { return m_HasSeat; } } - public string tip { get { return m_Tip; } } - - public bool Equals(CollabInfo other) - { - return m_Update == other.m_Update && - m_Publish == other.m_Publish && - m_InProgress == other.m_InProgress && - m_Maintenance == other.m_Maintenance && - m_Conflict == other.m_Conflict && - m_Refresh == other.m_Refresh && - m_HasSeat == other.m_HasSeat && - m_Ready == other.m_Ready && - string.Equals(m_Tip, other.m_Tip); - } - - bool m_Update; - bool m_Publish; - bool m_InProgress; - bool m_Maintenance; - bool m_Conflict; - bool m_Refresh; - bool m_HasSeat; - bool m_Ready; - string m_Tip; - } - - [NativeHeader("Editor/Src/Collab/Collab.h")] - [NativeHeader("Editor/Src/Collab/Collab.bindings.h")] - [StructLayout(LayoutKind.Sequential)] - [StaticAccessor("Collab::Get()", StaticAccessorType.Arrow)] - partial class Collab - { - [NativeMethod("Get")] - static extern IntPtr GetNativeCollab(); - - public extern CollabInfo collabInfo { get; } - - public static extern int GetRevisionsData( - bool withChanges, int startIndex, int numRevisions); - - public static extern int GetSingleRevisionData(bool withChanges, string id); - - public static extern RevisionsData PopulateRevisionsData(IntPtr nativeData); - public static extern Revision PopulateSingleRevisionData(IntPtr nativeData); - - public extern void SetSeat(bool value); - - public extern void RefreshSeatAvailabilityAsync(); - - public extern string GetProjectGUID(); - - public extern bool ShouldDoInitialCommit(); - - [NativeMethod("DiffFileWithBaseAsync")] - public extern void ShowDifferences(string path); - - public extern void SendNotification(); - - public extern void SetError(int errorCode); - - public extern void ClearError(int errorCode); - - public extern void ClearErrors(); - - public extern void ForceRefresh(bool refreshAssetDatabase); - - public extern void SetCollabEnabledForCurrentProject(bool enabled); - - public extern bool IsCollabEnabledForCurrentProject(); - - public extern bool IsAssetIgnored(string path); - - public extern bool ShouldTrackAsset(string path); - - [ThreadAndSerializationSafe] - public extern string GetProjectPath(); - - [ThreadAndSerializationSafe] - public extern bool IsConnected(); - - [ThreadAndSerializationSafe] - public extern bool AnyJobRunning(); - - [ThreadAndSerializationSafe] - public extern bool JobRunning(int a_jobID); - - public extern CollabStates GetAssetState(string guid); - public extern CollabStates GetSelectedAssetState(); - public extern CollabStateID GetCollabState(); - - [FreeFunction(HasExplicitThis = true)] - public extern bool ValidateSelectiveCommit(); - - [FreeFunction(HasExplicitThis = true, ThrowsException = true)] - public extern void Disconnect(); - - [FreeFunction(HasExplicitThis = true, ThrowsException = true)] - public extern void CancelJobByType(int jobType, bool forceCancel); - - [FreeFunction(HasExplicitThis = true, ThrowsException = true)] - public extern void DoInitialCommit(); - - [FreeFunction(HasExplicitThis = true, ThrowsException = true)] - public extern void Update(string revisionID, bool updateToRevision); - - [FreeFunction(HasExplicitThis = true, ThrowsException = true)] - public extern void RevertFile(string path, bool forceOverwrite); - - [FreeFunction(HasExplicitThis = true, ThrowsException = true)] - public extern void RevertFiles(ChangeItem[] changeItems, bool forceOverwrite); - - [FreeFunction(HasExplicitThis = true, ThrowsException = true)] - public extern void LaunchConflictExternalMerge(string path); - - [FreeFunction(HasExplicitThis = true, ThrowsException = true)] - public extern void ShowConflictDifferences(string path); - - [FreeFunction(HasExplicitThis = true, ThrowsException = true)] - public extern void ResyncSnapshot(); - - [FreeFunction(HasExplicitThis = true, ThrowsException = true)] - public extern void GoBackToRevision(string revisionID, bool updateToRevision); - - [FreeFunction(HasExplicitThis = true, ThrowsException = true)] - public extern void ResyncToRevision(string revisionID); - - [NativeMethod("GetConflictsManager().GetAllConflicts")] - public extern Change[] GetCollabConflicts(); - - // Conflict Management - [NativeMethod("GetConflictsManager().CheckConflictsResolvedExternal")] - public extern void CheckConflictsResolvedExternal(); - - [NativeMethod("GetTestHelper().AreTestsRunning")] - public extern bool AreTestsRunning(); - - [NativeMethod("GetTestHelper().SetTestsRunning")] - public extern void SetTestsRunning(bool running); - - [NativeMethod("GetTestHelper().ClearOperationsFailure")] - public extern void ClearAllFailures(); - - [NativeMethod("GetTestHelper().UnmarkOperationFailure")] - public extern void ClearNextOperationFailure(); - - [NativeMethod("GetTestHelper().UnmarkOperationFailureForFile")] - public extern void ClearNextOperationFailureForFile(string path); - - [NativeMethod("GetTestHelper().GetGUIDForTests")] - public extern string GetGUIDForTests(); - - [NativeMethod("GetTestHelper().NewGUIDForTests")] - public extern void NewGUIDForTests(); - - [NativeMethod("GetTestHelper().MarkOperationFailure")] - public extern void FailNextOperation(Collab.Operation operation, int code); - - [NativeMethod("GetTestHelper().MarkOperationTimeOut")] - public extern void TimeOutNextOperation(Collab.Operation operation, int timeOutSec); - - [NativeMethod("GetTestHelper().MarkOperationFailureForFile")] - public extern void FailNextOperationForFile(string path, Collab.Operation operation, int code); - - [NativeMethod("GetTestHelper().MarkOperationTimeOutForFile")] - public extern void TimeOutNextOperationForFile(string path, Collab.Operation operation, int timeOutSec); - - [FreeFunction(HasExplicitThis = true, ThrowsException = true)] - public extern void TestPostSoftLockAsCollaborator(string projectGuid, string projectPath, string machineGuid, - string assetGuid); - - [FreeFunction(HasExplicitThis = true, ThrowsException = true)] - public extern void TestClearSoftLockAsCollaborator(string projectGuid, string projectPath, string machineGuid, - string softLockHash); - - // Private helper methods for bindings - [NativeMethod("GetConflictsManager().SetConflictsState")] - extern bool SetConflictsResolved(string[] paths, CollabStates state); - - [NativeMethod("OnAssetBundleNameChanged")] - extern void OnPostprocessAssetbundleNameChanged(string assetPath, string previousAssetBundleName, string newAssetBundleName); - - [NativeMethod("GetError")] - internal extern bool GetErrorInternal(int errorFilter, out UnityErrorInfo info); - - [NativeMethod(HasExplicitThis = true, ThrowsException = true)] - extern Change[] GetChangesToPublishInternal(); - - [NativeMethod(HasExplicitThis = true, ThrowsException = true)] - extern ChangeItem[] GetChangeItemsToPublishInternal_V2(); - - [NativeMethod(HasExplicitThis = true, ThrowsException = true, IsThreadSafe = true)] - extern void SetChangesToPublishInternal(ChangeItem[] changes); - - [NativeMethod(HasExplicitThis = true, ThrowsException = true, IsThreadSafe = true)] - extern Change[] GetSelectedChangesInternal(); - - [NativeMethod(HasExplicitThis = true, ThrowsException = true, IsThreadSafe = true)] - extern ChangeItem[] GetSelectedChangeItemsInternal_V2(); - - [NativeMethod(HasExplicitThis = true, ThrowsException = true, IsThreadSafe = true)] - extern void UpdateChangesToPublish(); - - [NativeMethod(Name = "GetJobProgress", HasExplicitThis = true, ThrowsException = true)] - extern bool GetJobProgressInternal([Out] ProgressInfo info, int jobId); - - [NativeMethod(HasExplicitThis = true, ThrowsException = true)] - public extern void Publish(string comment, bool useSelectedAssets, bool confirmMatchesPrevious); - - [NativeMethod(HasExplicitThis = true, ThrowsException = true)] - public extern void PublishAssetsAsync(string comment, ChangeItem[] changes); - - [NativeMethod(HasExplicitThis = true, ThrowsException = true, IsThreadSafe = true)] - public extern void ClearSelectedChangesToPublish(); - - [NativeMethod(HasExplicitThis = true, ThrowsException = true, IsThreadSafe = true)] - public extern void SendCollabInfoNotification(); - - [NativeMethod(HasExplicitThis = true, ThrowsException = true)] - public extern SoftLock[] GetSoftLocks(string assetGuid); - } - - // keep in sync with CollabSettingType in C++ - internal enum CollabSettingType - { - InProgressEnabled = 0, - InProgressProjectEnabled = 1, - InProgressGlobalEnabled = 2 - } - - // keep in sync with CollabSettingStatus in C++ - internal enum CollabSettingStatus - { - None = 0, - Available = 1 - } - - [NativeHeader("Editor/Src/Collab/CollabSettingsManager.h")] - [StaticAccessor("GetCollabSettingsManager()", StaticAccessorType.Dot)] - internal class CollabSettingsManager - { - [RequiredByNativeCode] - static void NotifyStatusListeners(CollabSettingType type, CollabSettingStatus status) - { - if (statusNotifier[type] != null) - statusNotifier[type](type, status); - } - - public delegate void SettingStatusChanged(CollabSettingType type, CollabSettingStatus status); - public static Dictionary statusNotifier = new Dictionary(); - - static CollabSettingsManager() - { - foreach (CollabSettingType type in Enum.GetValues(typeof(CollabSettingType))) - statusNotifier[type] = null; - } - - public static extern bool IsAvailable(CollabSettingType type); - - public static extern bool inProgressEnabled - { - get; - } - } -} diff --git a/Editor/Mono/Collab/Collab.cs b/Editor/Mono/Collab/Collab.cs deleted file mode 100644 index 0056fbac88..0000000000 --- a/Editor/Mono/Collab/Collab.cs +++ /dev/null @@ -1,620 +0,0 @@ -// Unity C# reference source -// Copyright (c) Unity Technologies. For terms of use, see -// https://unity3d.com/legal/licenses/Unity_Reference_Only_License - -using System; -using System.IO; -using System.Collections.Generic; -using UnityEngine; -using UnityEngine.Scripting; -using UnityEditor.Web; -using UnityEditorInternal; -using UnityEditor.Connect; -using UnityEditor.SceneManagement; - -namespace UnityEditor.Collaboration -{ - internal delegate void StateChangedDelegate(CollabInfo info); - internal delegate void RevisionChangedDelegate(CollabInfo info, string rev, string action); - internal delegate void SetErrorDelegate(UnityErrorInfo error); - internal delegate void ErrorDelegate(); - internal delegate bool ShowToolbarAtPositionDelegate(Rect screenRect); - internal delegate bool IsToolbarVisibleDelegate(); - internal delegate void ShowHistoryWindowDelegate(); - internal delegate void ShowChangesWindowDelegate(); - internal delegate void CloseToolbarDelegate(); - internal delegate void ChangesChangedDelegate(Change[] changes, bool isFiltered); - internal delegate void ChangeItemsChangedDelegate(ChangeItem[] changes, bool isFiltered); - - //*undocumented - // We want to raise this exception from Cpp code but it fails - //public class CollabException: Exception - //{ - // public CollabException(string message) : base(message) - // { - // } - //} - - [InitializeOnLoad] - internal partial class Collab - { - // Pointer to native Collab object used by automatic bindings system. -#pragma warning disable 414 // The private field is assigned but its value is never used - IntPtr m_nativeCollab = IntPtr.Zero; - - public event StateChangedDelegate StateChanged; - - public event StateChangedDelegate RevisionUpdated; - public event RevisionChangedDelegate RevisionUpdated_V2; - - public event StateChangedDelegate JobsCompleted; - - public event ErrorDelegate ErrorOccurred; - public event SetErrorDelegate ErrorOccurred_V2; - - public event ErrorDelegate ErrorCleared; - - public event ChangeItemsChangedDelegate ChangeItemsChanged; - public event ChangeItemsChangedDelegate SelectedChangeItemsChanged; - public event StateChangedDelegate CollabInfoChanged; - - // Toolbar delegates - public static ShowToolbarAtPositionDelegate ShowToolbarAtPosition = null; - public static IsToolbarVisibleDelegate IsToolbarVisible = null; - public static CloseToolbarDelegate CloseToolbar = null; - - // Preferences link delegates - public static ShowHistoryWindowDelegate ShowHistoryWindow = null; - public static ShowChangesWindowDelegate ShowChangesWindow = null; - - private static Collab s_Instance; - private static bool s_IsFirstStateChange = true; - - [SerializeField] - public CollabFilters collabFilters = new CollabFilters(); - - public String projectBrowserSingleSelectionPath { get; set; } - - public String projectBrowserSingleMetaSelectionPath { get; set; } - - public string[] currentProjectBrowserSelection; - - // Must keep in sync with C++ in CollabCommon.h - [Flags] - public enum Operation - { - Noop = 0, - Publish = 1 << 0, - Update = 1 << 1, - Revert = 1 << 2, - GoBack = 1 << 3, - Restore = 1 << 4, - Diff = 1 << 5, - ConflictDiff = 1 << 6, - Exclude = 1 << 7, - Include = 1 << 8, - ChooseMine = 1 << 9, - ChooseTheirs = 1 << 10, - ExternalMerge = 1 << 11, - } - - // Must keep in sync with C++ in CollabCommon.h - // Explicit uint so "Any" state works with C++ - [Flags] - public enum CollabStates : uint - { - kCollabNone = 0, - kCollabLocal = 1, - - kCollabSynced = 1 << 1, - kCollabOutOfSync = 1 << 2, - kCollabIgnored = 1 << 3, - kCollabCheckedOutLocal = 1 << 4, - kCollabCheckedOutRemote = 1 << 5, - kCollabDeletedLocal = 1 << 6, - kCollabDeletedRemote = 1 << 7, - kCollabAddedLocal = 1 << 8, - kCollabAddedRemote = 1 << 9, - kCollabConflicted = 1 << 10, - kCollabMovedLocal = 1 << 11, - kCollabMovedRemote = 1 << 12, - kCollabUpdating = 1 << 13, - kCollabReadOnly = 1 << 14, - kCollabMetaFile = 1 << 15, - kCollabUseMine = 1 << 16, - kCollabUseTheir = 1 << 17, - kCollabMerged = 1 << 18, - kCollabPendingMerge = 1 << 19, - kCollabFolderMetaFile = 1 << 20, - KCollabContentChanged = 1 << 21, - KCollabContentConflicted = 1 << 22, - KCollabContentDeleted = 1 << 23, - - // always keep most significant - kCollabInvalidState = 1 << 30, - - kAnyLocalChanged = (kCollabAddedLocal | kCollabCheckedOutLocal | kCollabDeletedLocal | kCollabMovedLocal), - kAnyLocalEdited = (kCollabAddedLocal | kCollabCheckedOutLocal | kCollabMovedLocal), - kCollabAny = 0xFFFFFFFF - } - - internal enum CollabStateID { None, Uninitialized, Initialized } - - public static string[] clientType = - { - "Cloud Server", - "Mock Server" - }; - - internal static string editorPrefCollabClientType = "CollabConfig_Client"; - - public static string GetProjectClientType() - { - var cvalue = EditorUserSettings.GetConfigValue(editorPrefCollabClientType); - return string.IsNullOrEmpty(cvalue) ? clientType[0] : cvalue; - } - - public static Collab instance - { - get - { - return s_Instance; - } - } - - // Instance of VersionControl interface implementation - private static IVersionControl s_VersionControlInstance; - - public static void SetVersionControl(IVersionControl instance) - { - s_VersionControlInstance = instance; - // Initialize version control based on whether collab is enabled - if (s_Instance != null) - { - if (s_Instance.IsCollabEnabledForCurrentProject()) - { - instance.OnEnableVersionControl(); - } - else - { - instance.OnDisableVersionControl(); - } - } - } - - [UsedByNativeCode] - internal static bool HasVersionControl() - { - return s_VersionControlInstance != null; - } - - [UsedByNativeCode] - internal static void ShowChangesWindowView() - { - if (ShowChangesWindow != null) - { - ShowChangesWindow(); - } - } - - [UsedByNativeCode] - static bool SupportsDownloads() - { - if (s_VersionControlInstance != null) - { - return s_VersionControlInstance.SupportsDownloads(); - } - else - { - return false; - } - } - - [UsedByNativeCode] - static bool OnEnableVersionControl() - { - if (s_VersionControlInstance != null) - { - return s_VersionControlInstance.OnEnableVersionControl(); - } - else - { - return true; - } - } - - [UsedByNativeCode] - static void OnDisableVersionControl() - { - if (s_VersionControlInstance != null) - { - s_VersionControlInstance.OnDisableVersionControl(); - } - } - - [UsedByNativeCode] - static ChangeItem[] GetChanges() - { - if (s_VersionControlInstance != null) - { - return s_VersionControlInstance.GetChanges(); - } - else - { - return null; - } - } - - [UsedByNativeCode] - static void MergeDownloadedFiles(bool isFullDownload) - { - if (s_VersionControlInstance != null) - { - s_VersionControlInstance.MergeDownloadedFiles(isFullDownload); - } - } - - [UsedByNativeCode] - static bool SupportsAsyncChanges() - { - if (s_VersionControlInstance != null) - { - return s_VersionControlInstance.SupportsAsyncChanges(); - } - else - { - return false; - } - } - - internal static CollabStates GetAssetState(string assetGuid, string assetPath) - { - if (s_VersionControlInstance != null) - { - return s_VersionControlInstance.GetAssetState(assetGuid, assetPath); - } - else - { - return instance.GetAssetState(assetGuid); - } - } - - public void RefreshAvailableLocalChangesSynchronous() - { - IVersionControl_V2 vc_v2 = s_VersionControlInstance as IVersionControl_V2; - - // If our VersionControlInstance isn't v2, this whole method is a no-op - if (vc_v2 != null) - { - vc_v2.RefreshAvailableLocalChangesSynchronous(); - UpdateChangesToPublish(); - } - } - - // Static constructor for Collab - static Collab() - { - s_Instance = new Collab(); - s_Instance.projectBrowserSingleSelectionPath = string.Empty; - s_Instance.projectBrowserSingleMetaSelectionPath = string.Empty; - s_Instance.m_nativeCollab = GetNativeCollab(); - ObjectListArea.postAssetIconDrawCallback += CollabProjectHook.OnProjectWindowIconOverlay; - AssetsTreeViewGUI.postAssetIconDrawCallback += CollabProjectHook.OnProjectBrowserNavPanelIconOverlay; - InitializeSoftlocksViewController(); - CollabSettingsManager.statusNotifier[CollabSettingType.InProgressEnabled] += OnSettingStatusChanged; - CollabSettingsManager.statusNotifier[CollabSettingType.InProgressEnabled] += SoftlockViewController.Instance.softLockFilters.OnSettingStatusChanged; - } - - public static void OnSettingStatusChanged(CollabSettingType type, CollabSettingStatus status) - { - InitializeSoftlocksViewController(); - } - - public static bool InitializeSoftlocksViewController() - { - if (!CollabSettingsManager.IsAvailable(CollabSettingType.InProgressEnabled)) - return false; - - if (CollabSettingsManager.inProgressEnabled) - SoftlockViewController.Instance.TurnOn(); - else - SoftlockViewController.Instance.TurnOff(); - return true; - } - - - public bool GetError(UnityConnect.UnityErrorFilter errorFilter, out UnityErrorInfo info) - { - return GetErrorInternal((int)errorFilter, out info) && info.code > 0; - } - - public void CancelJob(int jobType) - { - try - { - CancelJobByType(jobType, false); - } - catch (Exception ex) - { - UnityEngine.Debug.Log("Cannot cancel job, reason:" + ex.Message); - } - } - - public void UpdateEditorSelectionCache() - { - var result = new List(); - - foreach (var elem in Selection.assetGUIDsDeepSelection) - { - var path = AssetDatabase.GUIDToAssetPath(elem); - result.Add(path); - - var meta = path + ".meta"; - if (File.Exists(meta)) - { - result.Add(meta); - } - } - currentProjectBrowserSelection = result.ToArray(); - } - - public CollabInfo GetCollabInfo() - { - return collabInfo; - } - - public static bool IsDiffToolsAvailable() - { - return InternalEditorUtility.GetAvailableDiffTools().Length > 0; - } - - public void SaveAssets() - { - AssetDatabase.SaveAssets(); - } - - public static void SwitchToDefaultMode() - { - bool in2D = EditorSettings.defaultBehaviorMode == EditorBehaviorMode.Mode2D; - var sv = SceneView.lastActiveSceneView; - if (sv != null && sv.in2DMode != in2D) - { - sv.in2DMode = in2D; - } - } - - public void ShowInProjectBrowser(string filterString) - { - collabFilters.ShowInProjectBrowser(filterString); - } - - public bool SetConflictsResolvedMine(string[] paths) - { - return SetConflictsResolved(paths, CollabStates.kCollabUseMine); - } - - public bool SetConflictsResolvedTheirs(string[] paths) - { - return SetConflictsResolved(paths, CollabStates.kCollabUseTheir); - } - - public PublishInfo GetChangesToPublish() - { - Change[] changes = GetChangesToPublishInternal(); - bool isFiltered = false; - - if (SupportsAsyncChanges()) - { - changes = GetSelectedChangesInternal(); - if (Toolbar.isLastShowRequestPartial) - { - isFiltered = true; - } - } - - return new PublishInfo() - { - changes = changes, - filter = isFiltered - }; - } - - public PublishInfo_V2 GetChangesToPublish_V2() - { - ChangeItem[] changes = GetChangeItemsToPublishInternal_V2(); - - return new PublishInfo_V2() - { - changes = changes, - filter = false - }; - } - - public void SetChangesToPublish(ChangeItem[] changes) - { - SetChangesToPublishInternal(changes); - } - - public ProgressInfo GetJobProgress(int jobId) - { - ProgressInfo info = new ProgressInfo(); - if (GetJobProgressInternal(info, jobId)) - return info; - - return null; - } - - private static void OnStateChanged() - { - // register only once - if (s_IsFirstStateChange) - { - s_IsFirstStateChange = false; - UnityConnect.instance.StateChanged += OnUnityConnectStateChanged; - } - var handler = instance.StateChanged; - if (handler != null) - { - handler(instance.collabInfo); - } - } - - [RequiredByNativeCode] - private static void OnRevisionUpdated(string revisionId, string action) - { - var handler = instance.RevisionUpdated; - if (handler != null) - { - handler(instance.collabInfo); - } - - var handler_v2 = instance.RevisionUpdated_V2; - if (handler_v2 != null) - { - handler_v2(instance.collabInfo, revisionId, action); - } - } - - [RequiredByNativeCode] - private static void OnChangeItemsChanged(ChangeItem[] changes, bool isFiltered) - { - var handler = instance.ChangeItemsChanged; - if (handler != null) - { - handler(changes, isFiltered); - } - } - - [RequiredByNativeCode] - private static void OnSelectedChangeItemsChanged(ChangeItem[] changeItems, bool isFiltered) - { - var handler = instance.SelectedChangeItemsChanged; - if (handler != null) - { - handler(changeItems, isFiltered); - } - } - - [RequiredByNativeCode] - private static void OnCollabInfoChanged() - { - var handler = instance.CollabInfoChanged; - if (handler != null) - { - handler(instance.collabInfo); - } - } - - [RequiredByNativeCode] - private static void SetCollabError(int code, int priority, int behavior, string msg, string shortmsg, string codeStr) - { - var handler = instance.ErrorOccurred; - - if (handler != null) - { - handler(); - } - var handler_v2 = instance.ErrorOccurred_V2; - - if (handler_v2 != null) - { - handler_v2(new UnityErrorInfo() { code = code, priority = priority, behaviour = behavior, msg = msg, shortMsg = shortmsg, codeStr = codeStr }); - } - } - - [RequiredByNativeCode] - private static void ClearCollabError() - { - var handler = instance.ErrorCleared; - if (handler != null) - handler(); - } - - private static void OnJobsCompleted() - { - var handler = instance.JobsCompleted; - if (handler != null) - { - handler(instance.collabInfo); - } - CollabTesting.OnJobsCompleted(); - } - - private static void PublishDialog(string changelist) - { - if (!EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) - { - return; - } - - var dialog = CollabPublishDialog.ShowCollabWindow(changelist); - - if (dialog.Options.DoPublish) - Collab.instance.Publish(dialog.Options.Comments, true, false); - } - - private static void CannotPublishDialog(string infoMessage) - { - CollabCannotPublishDialog.ShowCollabWindow(infoMessage); - } - - private static void OnUnityConnectStateChanged(ConnectInfo state) - { - instance.SendNotification(); - } - - public static void OnProgressEnabledSettingStatusChanged(CollabSettingType type, CollabSettingStatus status) - { - if (type == CollabSettingType.InProgressEnabled && status == CollabSettingStatus.Available) - { - if (CollabSettingsManager.inProgressEnabled) - { - SoftlockViewController.Instance.softLockFilters.ShowInFavoriteSearchFilters(); - } - - CollabSettingsManager.statusNotifier[CollabSettingType.InProgressEnabled] -= OnProgressEnabledSettingStatusChanged; - } - } - - - [RequiredByNativeCode] - static void OnCollabEnabledForCurrentProject(bool enabled) - { - if (enabled) - { - instance.StateChanged += instance.collabFilters.OnCollabStateChanged; - instance.collabFilters.ShowInFavoriteSearchFilters(); - if (CollabSettingsManager.IsAvailable(CollabSettingType.InProgressEnabled)) - { - if (CollabSettingsManager.inProgressEnabled) - { - SoftlockViewController.Instance.softLockFilters.ShowInFavoriteSearchFilters(); - } - } - else - { - CollabSettingsManager.statusNotifier[CollabSettingType.InProgressEnabled] -= OnProgressEnabledSettingStatusChanged; - CollabSettingsManager.statusNotifier[CollabSettingType.InProgressEnabled] += OnProgressEnabledSettingStatusChanged; - } - } - else - { - instance.StateChanged -= instance.collabFilters.OnCollabStateChanged; - instance.collabFilters.HideFromFavoriteSearchFilters(); - SoftlockViewController.Instance.softLockFilters.HideFromFavoriteSearchFilters(); - CollabSettingsManager.statusNotifier[CollabSettingType.InProgressEnabled] -= OnProgressEnabledSettingStatusChanged; - - if (ProjectBrowser.s_LastInteractedProjectBrowser != null) - { - if (ProjectBrowser.s_LastInteractedProjectBrowser.Initialized() && ProjectBrowser.s_LastInteractedProjectBrowser.IsTwoColumns()) - { - int instanceID = AssetDatabase.GetMainAssetInstanceID("assets"); - ProjectBrowser.s_LastInteractedProjectBrowser.SetFolderSelection(new int[] { instanceID }, true); - } - ProjectBrowser.s_LastInteractedProjectBrowser.SetSearch(""); - ProjectBrowser.s_LastInteractedProjectBrowser.Repaint(); - } - } - } - } -} diff --git a/Editor/Mono/Collab/CollabChange.cs b/Editor/Mono/Collab/CollabChange.cs deleted file mode 100644 index 1297fdb044..0000000000 --- a/Editor/Mono/Collab/CollabChange.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Unity C# reference source -// Copyright (c) Unity Technologies. For terms of use, see -// https://unity3d.com/legal/licenses/Unity_Reference_Only_License - - -using System; -using System.Runtime.InteropServices; -using UnityEngine.Bindings; - -namespace UnityEditor.Collaboration -{ - // Keep internal and undocumented until we expose more functionality - //*undocumented - [StructLayout(LayoutKind.Sequential)] - [NativeType(CodegenOptions = CodegenOptions.Custom, Header = "Editor/Src/Collab/CollabChange.h", - IntermediateScriptingStructName = "ScriptingCollabChange")] - [NativeHeader("Editor/Src/Collab/Collab.bindings.h")] - [NativeAsStruct] - internal class Change - { - [Flags] - public enum RevertableStates : uint - { - Revertable = 1 << 0, - NotRevertable = 1 << 1, - - Revertable_File = 1 << 2, - Revertable_Folder = 1 << 3, - Revertable_EmptyFolder = 1 << 4, - - NotRevertable_File = 1 << 5, - NotRevertable_Folder = 1 << 6, - NotRevertable_FileAdded = 1 << 7, - NotRevertable_FolderAdded = 1 << 8, - NotRevertable_FolderContainsAdd = 1 << 9, - - // do not exceed Javascript Number range - InvalidRevertableState = (uint)1 << 31 - } - - string m_Path; - Collab.CollabStates m_State; - RevertableStates m_RevertableState; - string m_RelatedTo; - string m_LocalStatus; - string m_RemoteStatus; - string m_ResolveStatus; - - Change() {} - - public string path { get { return m_Path; } } - public Collab.CollabStates state { get { return m_State; } } - public bool isRevertable { get { return HasRevertableState(RevertableStates.Revertable); } } - public RevertableStates revertableState { get { return m_RevertableState; } } - public string relatedTo { get { return m_RelatedTo; } } - - public bool isMeta { get { return HasState(Collab.CollabStates.kCollabMetaFile); } } - public bool isConflict { get { return HasState(Collab.CollabStates.kCollabConflicted) || HasState(Collab.CollabStates.kCollabPendingMerge); } } - public bool isFolderMeta { get { return HasState(Collab.CollabStates.kCollabFolderMetaFile); } } - public bool isResolved { get { return HasState(Collab.CollabStates.kCollabUseMine) || HasState(Collab.CollabStates.kCollabUseTheir) || HasState(Collab.CollabStates.kCollabMerged); } } - - public string localStatus { get { return m_LocalStatus; } } - public string remoteStatus { get { return m_RemoteStatus; } } - public string resolveStatus { get { return m_ResolveStatus; } } - - internal bool HasState(Collab.CollabStates states) - { - return (m_State & states) != 0; - } - - internal bool HasRevertableState(RevertableStates revertableStates) - { - return (m_RevertableState & revertableStates) != 0; - } - } - - internal class PublishInfo - { - public Change[] changes; - public bool filter; - } -} diff --git a/Editor/Mono/Collab/CollabChangeAction.cs b/Editor/Mono/Collab/CollabChangeAction.cs deleted file mode 100644 index 8d65911b51..0000000000 --- a/Editor/Mono/Collab/CollabChangeAction.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Unity C# reference source -// Copyright (c) Unity Technologies. For terms of use, see -// https://unity3d.com/legal/licenses/Unity_Reference_Only_License - - -using System; -using System.Runtime.InteropServices; -using UnityEngine.Scripting; - -namespace UnityEditor.Collaboration -{ - // Keep internal and undocumented until we expose more functionality - //*undocumented - [StructLayout(LayoutKind.Sequential)] - [UsedByNativeCode] - internal struct ChangeAction - { - private string m_Path; - private string m_Action; - - public ChangeAction(string path = "", string action = "") - { - m_Path = path; - m_Action = action; - } - - public string path { get { return m_Path; } } - public string action { get { return m_Action; } } - } -} diff --git a/Editor/Mono/Collab/CollabChangeItem.cs b/Editor/Mono/Collab/CollabChangeItem.cs index cfca4a5ab9..ad380552c0 100644 --- a/Editor/Mono/Collab/CollabChangeItem.cs +++ b/Editor/Mono/Collab/CollabChangeItem.cs @@ -6,29 +6,3 @@ using System.Runtime.InteropServices; using UnityEngine.Bindings; -namespace UnityEditor.Collaboration -{ - [StructLayout(LayoutKind.Sequential)] - [NativeType(CodegenOptions = CodegenOptions.Custom, Header = "Editor/Src/Collab/CollabClient.h", - IntermediateScriptingStructName = "ScriptingCollabChangeItem")] - [NativeHeader("Editor/Src/Collab/Collab.bindings.h")] - [NativeAsStruct] - internal class ChangeItem - { - public string Path { get; set; } - public Change.RevertableStates RevertableState { get; set; } - public string RelatedTo { get; set; } - public string RevisionId { get; set; } - public string Hash { get; set; } - public Collab.CollabStates State { get; set; } - public long Size { get; set; } - public string DownloadPath { get; set; } - public string FromPath { get; set; } - } - - internal class PublishInfo_V2 - { - public ChangeItem[] changes; - public bool filter; - } -} diff --git a/Editor/Mono/Collab/CollabDialogs.cs b/Editor/Mono/Collab/CollabDialogs.cs deleted file mode 100644 index 155f8766e1..0000000000 --- a/Editor/Mono/Collab/CollabDialogs.cs +++ /dev/null @@ -1,146 +0,0 @@ -// Unity C# reference source -// Copyright (c) Unity Technologies. For terms of use, see -// https://unity3d.com/legal/licenses/Unity_Reference_Only_License - -using System; -using System.IO; -using System.Collections.Generic; -using UnityEngine; -using UnityEditor; -using UnityEditor.Web; -using UnityEditorInternal; -using UnityEditor.Connect; - -namespace UnityEditor.Collaboration -{ - internal struct PublishDialogOptions - { - public string Comments; - public bool DoPublish; - } - - internal class CollabPublishDialog : EditorWindow - { - public static CollabPublishDialog ShowCollabWindow(string changelist) - { - CollabPublishDialog dialog = ScriptableObject.CreateInstance(); - dialog.Changelist = changelist; - - var rect = new Rect(100, 100, 600, 225); - dialog.minSize = new Vector2(rect.width, rect.height); - dialog.maxSize = new Vector2(rect.width, rect.height); - dialog.position = rect; - dialog.ShowModal(); - - dialog.m_Parent.window.m_DontSaveToLayout = true; - - return dialog; - } - - static GUIContent DescribeChangesText = EditorGUIUtility.TrTextContent("Describe your changes here"); - static GUIContent ChangeAssetsText = EditorGUIUtility.TrTextContent("Changed assets:"); - static GUIContent PublishText = EditorGUIUtility.TrTextContent("Publish"); - static GUIContent CancelText = EditorGUIUtility.TrTextContent("Cancel"); - - public Vector2 scrollView; - public string Changelist; - - public CollabPublishDialog() - { - Options.Comments = ""; - } - - public void OnGUI() - { - GUILayout.BeginVertical(); - GUILayout.Label(DescribeChangesText); - Options.Comments = GUILayout.TextArea(Options.Comments, 1000, GUILayout.MinHeight(80)); - - - GUILayout.Label(ChangeAssetsText); - scrollView = EditorGUILayout.BeginScrollView(scrollView, false, false); - GUIStyle style = new GUIStyle(); - Vector2 textSize = style.CalcSize(new GUIContent(Changelist)); - EditorGUILayout.SelectableLabel(Changelist, EditorStyles.textField, GUILayout.ExpandHeight(true), GUILayout.MinHeight(textSize.y)); - EditorGUILayout.EndScrollView(); - - GUILayout.FlexibleSpace(); - - GUILayout.BeginHorizontal(); - GUILayout.FlexibleSpace(); - - if (GUILayout.Button(CancelText)) - { - Options.DoPublish = false; - Close(); - } - - if (GUILayout.Button(PublishText)) - { - Options.DoPublish = true; - Close(); - } - - GUILayout.EndHorizontal(); - GUILayout.EndVertical(); - } - - public PublishDialogOptions Options; - } - - internal class CollabCannotPublishDialog : EditorWindow - { - public static CollabCannotPublishDialog ShowCollabWindow(string infoMessage) - { - CollabCannotPublishDialog dialog = ScriptableObject.CreateInstance(); - dialog.InfoMessage = infoMessage; - - var rect = new Rect(100, 100, 600, 150); - dialog.minSize = new Vector2(rect.width, rect.height); - dialog.maxSize = new Vector2(rect.width, rect.height); - dialog.position = rect; - dialog.ShowModal(); - - dialog.m_Parent.window.m_DontSaveToLayout = true; - - return dialog; - } - - static GUIContent WarningText = EditorGUIUtility.TextContent(string.Format( - "Files that have been moved or in a changed folder cannot be selectively published, " + - "please use the Publish option in the collab window to publish all your changes.")); - static GUIContent IssuesText = EditorGUIUtility.TrTextContent("Issues:"); - static GUIContent AcceptText = EditorGUIUtility.TrTextContent("Accept"); - - public Vector2 scrollPosition; - public string InfoMessage; - - public void OnGUI() - { - GUILayout.BeginVertical(); - GUI.skin.label.wordWrap = true; - - GUILayout.BeginVertical(); - GUILayout.Label(WarningText); - - GUILayout.Label(IssuesText); - - scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition); - GUIStyle warnStyle = new GUIStyle(); - warnStyle.normal.textColor = new Color(1f, 0.28f, 0f); - GUILayout.Label(string.Format(InfoMessage), warnStyle); - GUILayout.EndScrollView(); - - GUILayout.EndVertical(); - - GUILayout.BeginHorizontal(); - GUILayout.FlexibleSpace(); - - if (GUILayout.Button(AcceptText)) - Close(); - GUILayout.EndHorizontal(); - - GUILayout.EndVertical(); - } - } -} diff --git a/Editor/Mono/Collab/CollabEditorHooks.cs b/Editor/Mono/Collab/CollabEditorHooks.cs deleted file mode 100644 index 89965e7b3f..0000000000 --- a/Editor/Mono/Collab/CollabEditorHooks.cs +++ /dev/null @@ -1,196 +0,0 @@ -// Unity C# reference source -// Copyright (c) Unity Technologies. For terms of use, see -// https://unity3d.com/legal/licenses/Unity_Reference_Only_License - -using System; -using System.Collections.Generic; -using System.IO; -using UnityEngine; -using UnityEditor.Web; -using UnityEditor.Connect; - -namespace UnityEditor.Collaboration -{ - // Display hooks for the main project window. Icons are overlayed to show the version control state. - internal class CollabProjectHook - { - // GUI callback for each item visible in the project window/object list area - public static void OnProjectWindowIconOverlay(Rect iconRect, string guid, bool isListMode) - { - DrawProjectBrowserIconOverlay(iconRect, guid, isListMode); - } - - // Draw icons in the Favorites/Asset Folder area of the project browser - public static void OnProjectBrowserNavPanelIconOverlay(Rect iconRect, string guid) - { - DrawProjectBrowserIconOverlay(iconRect, guid, true); - } - - private static void DrawProjectBrowserIconOverlay(Rect iconRect, string guid, bool isListMode) - { - if (Collab.instance.IsCollabEnabledForCurrentProject()) - { - Collab.CollabStates assetState = GetAssetState(guid); - Overlay.DrawOverlays(assetState, iconRect, isListMode); - } - } - - public static Collab.CollabStates GetAssetState(String assetGuid) - { - if (!Collab.instance.IsCollabEnabledForCurrentProject()) - { - return Collab.CollabStates.kCollabNone; - } - - Collab.CollabStates assetState = Collab.GetAssetState(assetGuid, AssetDatabase.GUIDToAssetPath(assetGuid)); - return assetState; - } - } - - internal class Overlay - { - public const double k_OverlaySizeOnSmallIcon = 0.6; - public const double k_OverlaySizeOnLargeIcon = 0.35; - - private static readonly Dictionary s_Overlays = new Dictionary(); - - protected static void LoadOverlays() - { - // Order of priority must match GetLocalStatus (CollabClient.h) - s_Overlays.Clear(); - s_Overlays.Add(Collab.CollabStates.kCollabIgnored, EditorGUIUtility.IconContent("CollabExclude Icon")); - s_Overlays.Add(Collab.CollabStates.kCollabConflicted, EditorGUIUtility.IconContent("CollabConflict Icon")); - s_Overlays.Add(Collab.CollabStates.kCollabPendingMerge, EditorGUIUtility.IconContent("CollabConflict Icon")); - s_Overlays.Add(Collab.CollabStates.kCollabMovedLocal, EditorGUIUtility.IconContent("CollabMoved Icon")); - s_Overlays.Add(Collab.CollabStates.kCollabCheckedOutLocal | Collab.CollabStates.kCollabMovedLocal, EditorGUIUtility.IconContent("CollabMoved Icon")); - s_Overlays.Add(Collab.CollabStates.kCollabCheckedOutLocal, EditorGUIUtility.IconContent("CollabEdit Icon")); - s_Overlays.Add(Collab.CollabStates.kCollabAddedLocal, EditorGUIUtility.IconContent("CollabCreate Icon")); - s_Overlays.Add(Collab.CollabStates.kCollabDeletedLocal, EditorGUIUtility.IconContent("CollabDeleted Icon")); - - // The folder overlay should take precedence on the folder content's status. - s_Overlays.Add(Collab.CollabStates.KCollabContentConflicted, EditorGUIUtility.IconContent("CollabChangesConflict Icon")); - s_Overlays.Add(Collab.CollabStates.KCollabContentChanged, EditorGUIUtility.IconContent("CollabChanges Icon")); - s_Overlays.Add(Collab.CollabStates.KCollabContentDeleted, EditorGUIUtility.IconContent("CollabChangesDeleted Icon")); - } - - protected static bool AreOverlaysLoaded() - { - if (s_Overlays.Count == 0) - return false; - - foreach (var icon in s_Overlays.Values) - { - if (icon == null) - return false; - } - - return true; - } - - protected static Collab.CollabStates GetOverlayStateForAsset(Collab.CollabStates assetStates) - { - foreach (var state in s_Overlays.Keys) - { - if (HasState(assetStates, state)) - return state; - } - - return Collab.CollabStates.kCollabNone; - } - - protected static void DrawOverlayElement(Collab.CollabStates singleState, Rect itemRect) - { - GUIContent content; - if (s_Overlays.TryGetValue(singleState, out content)) - { - Texture overlay = content.image; - if (overlay != null) - { - GUI.DrawTexture(itemRect, overlay, ScaleMode.ScaleToFit); - } - } - } - - protected static bool HasState(Collab.CollabStates assetStates, Collab.CollabStates includesState) - { - return ((assetStates & includesState) == includesState); - } - - public static void DrawOverlays(Collab.CollabStates assetState, Rect itemRect, bool isListMode) - { - if (assetState == Collab.CollabStates.kCollabInvalidState || assetState == Collab.CollabStates.kCollabNone) - return; - - if (Event.current.type != EventType.Repaint) - return; - - if (!AreOverlaysLoaded()) - LoadOverlays(); - - var state = GetOverlayStateForAsset(assetState); - DrawOverlayElement(state, GetRectForTopRight(itemRect, GetScale(itemRect, isListMode))); - } - - // Return a new Rect with its width and height scaled, and converted to the ceiling. - public static Rect ScaleRect(Rect rect, double scale) - { - Rect scaledRect = new Rect(rect); - scaledRect.width = Convert.ToInt32(Math.Ceiling(rect.width * scale)); - scaledRect.height = Convert.ToInt32(Math.Ceiling(rect.height * scale)); - return scaledRect; - } - - public static double GetScale(Rect rect, bool isListMode) - { - double scale = k_OverlaySizeOnLargeIcon; - if (isListMode) - { - scale = k_OverlaySizeOnSmallIcon; - } - return scale; - } - - public static Rect GetRectForTopRight(Rect projectBrowserDrawRect, double scale) - { - Rect scaledRect = ScaleRect(projectBrowserDrawRect, scale); - scaledRect.x += (projectBrowserDrawRect.width - scaledRect.width); - return scaledRect; - } - - public static Rect GetRectForBottomRight(Rect projectBrowserDrawRect, double scale) - { - Rect scaledRect = ScaleRect(projectBrowserDrawRect, scale); - scaledRect.x += (projectBrowserDrawRect.width - scaledRect.width); - scaledRect.y += (projectBrowserDrawRect.height - scaledRect.height); - return scaledRect; - } - } - - internal static class TextureUtility - { - public static Texture2D LoadTextureFromApplicationContents(string path) - { - var tex = new Texture2D(2, 2); - - string resourcesPath = Path.Combine(Path.Combine(Path.Combine(EditorApplication.applicationContentsPath, "Resources"), "Collab"), "overlays"); - path = Path.Combine(resourcesPath, path); - - try - { - using (var fs = File.OpenRead(path)) - { - var bytes = new byte[fs.Length]; - fs.Read(bytes, 0, (int)fs.Length); - if (!tex.LoadImage(bytes)) return null; - } - } - catch (Exception) - { - Debug.LogWarning("Collab Overlay Texture load fail, path: " + path); - return null; - } - - return tex; - } - } -} diff --git a/Editor/Mono/Collab/CollabFilters.cs b/Editor/Mono/Collab/CollabFilters.cs deleted file mode 100644 index 9a2373fc4a..0000000000 --- a/Editor/Mono/Collab/CollabFilters.cs +++ /dev/null @@ -1,169 +0,0 @@ -// Unity C# reference source -// Copyright (c) Unity Technologies. For terms of use, see -// https://unity3d.com/legal/licenses/Unity_Reference_Only_License - - -using System; -using System.Collections.Generic; -using System.Linq; -using UnityEditor; -using UnityEngine; - -namespace UnityEditor.Collaboration -{ - internal abstract class AbstractFilters - { - [SerializeField] - private List m_Filters; - - public List filters { get { return m_Filters; } set { m_Filters = value; } } - - public abstract void InitializeFilters(); - - public bool ContainsSearchFilter(string name, string searchString) - { - foreach (var filter in filters) - { - if (filter[0] == name && filter[1] == searchString) - return true; - } - return false; - } - - public void ShowInFavoriteSearchFilters() - { - if (SavedSearchFilters.GetRootInstanceID() == 0) - { - SavedSearchFilters.AddInitializedListener(ShowInFavoriteSearchFilters); - return; - } - - SavedSearchFilters.RemoveInitializedListener(ShowInFavoriteSearchFilters); - int prevInstanceID = 0; - foreach (var filter in filters) - { - int instanceID = SavedSearchFilters.GetFilterInstanceID(filter[0], filter[1]); - if (instanceID == 0) - { - SearchFilter searchFilter = SearchFilter.CreateSearchFilterFromString(filter[1]); - if (prevInstanceID == 0) - prevInstanceID = SavedSearchFilters.AddSavedFilter(filter[0], searchFilter, 64); - else - prevInstanceID = SavedSearchFilters.AddSavedFilterAfterInstanceID(filter[0], searchFilter, 64, prevInstanceID, false); - } - } - - SavedSearchFilters.RefreshSavedFilters(); - - foreach (ProjectBrowser pb in ProjectBrowser.GetAllProjectBrowsers()) - { - pb.Repaint(); - } - } - - public void HideFromFavoriteSearchFilters() - { - SavedSearchFilters.RefreshSavedFilters(); - - foreach (ProjectBrowser pb in ProjectBrowser.GetAllProjectBrowsers()) - { - pb.Repaint(); - } - } - } - - internal class CollabFilters : AbstractFilters - { - [SerializeField] - private bool m_SearchFilterWasSet = false; - - public override void InitializeFilters() - { - filters = new List() - { - new string[] { "All Modified", "v:any" }, - new string[] { "All Conflicts", "v:conflicted" }, - new string[] { "All Excluded" , "v:ignored"}, - }; - } - - public CollabFilters() - { - InitializeFilters(); - } - - public void ShowInProjectBrowser(string filterString) - { - ProjectBrowser browser = ProjectBrowser.s_LastInteractedProjectBrowser; - if (browser == null) - { - List browsers = ProjectBrowser.GetAllProjectBrowsers(); - if (browsers != null && browsers.Count > 0) - { - browser = browsers.First(); - } - } - - if (!string.IsNullOrEmpty(filterString)) - { - if (browser == null) - { - browser = EditorWindow.GetWindow(); - ShowInFavoriteSearchFilters(); - browser.RepaintImmediately(); - } - - m_SearchFilterWasSet = true; - - string filterSearchString = "v:" + filterString; - if (browser.IsTwoColumns()) - { - foreach (var filter in filters) - { - if (filterSearchString == filter[1]) - { - int instanceID = SavedSearchFilters.GetFilterInstanceID(filter[0], filterSearchString); - if (instanceID > ProjectWindowUtil.k_FavoritesStartInstanceID) - { - browser.SetFolderSelection(new int[] { instanceID }, true); - break; - } - } - } - } - - browser.SetSearch(filterSearchString); - browser.Repaint(); - browser.Focus(); - } - else - { - if (m_SearchFilterWasSet) - { - if (browser != null) - { - if (browser.IsTwoColumns()) - { - int instanceID = AssetDatabase.GetMainAssetInstanceID("assets"); - browser.SetFolderSelection(new int[] { instanceID }, true); - } - browser.SetSearch(""); - browser.Repaint(); - } - } - m_SearchFilterWasSet = false; - } - } - - public void OnCollabStateChanged(CollabInfo info) - { - if (!info.ready || info.inProgress || info.maintenance) - return; - - foreach (ProjectBrowser pb in ProjectBrowser.GetAllProjectBrowsers()) - { - pb.RefreshSearchIfFilterContains("v:"); - } - } - } -} diff --git a/Editor/Mono/Collab/CollabProgressInfo.cs b/Editor/Mono/Collab/CollabProgressInfo.cs deleted file mode 100644 index 5727a41a7d..0000000000 --- a/Editor/Mono/Collab/CollabProgressInfo.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Unity C# reference source -// Copyright (c) Unity Technologies. For terms of use, see -// https://unity3d.com/legal/licenses/Unity_Reference_Only_License - -using System.Runtime.InteropServices; -using UnityEngine.Bindings; - -namespace UnityEditor.Collaboration -{ - // Keep internal and undocumented until we expose more functionality - //*undocumented - [StructLayout(LayoutKind.Sequential)] - [NativeType(CodegenOptions = CodegenOptions.Custom, Header = "Editor/Src/Collab/CollabProgressInfo.h", - IntermediateScriptingStructName = "ScriptingCollabProgressInfo")] - [NativeHeader("Editor/Src/Collab/Collab.bindings.h")] - [NativeAsStruct] - internal class ProgressInfo - { - public enum ProgressType : uint - { - None = 0, - Count = 1, - Percent = 2, - Both = 3 - } - - int m_JobId; - string m_Title; - string m_ExtraInfo; - ProgressType m_ProgressType; - int m_Percentage; - int m_CurrentCount; - int m_TotalCount; - bool m_Completed; - bool m_Cancelled; - bool m_CanCancel; - string m_LastErrorString; - ulong m_LastError; - - public int jobId { get { return m_JobId; } } - public string title { get { return m_Title; } } - public string extraInfo { get { return m_ExtraInfo; } } - public int currentCount { get { return m_CurrentCount; } } - public int totalCount { get { return m_TotalCount; } } - public bool completed { get { return m_Completed; } } - public bool cancelled { get { return m_Cancelled; } } - public bool canCancel { get { return m_CanCancel; } } - public string lastErrorString { get { return m_LastErrorString; } } - public ulong lastError { get { return m_LastError; } } - - public int percentComplete - { - get - { - if (m_ProgressType == ProgressType.Percent || m_ProgressType == ProgressType.Both) - { - return m_Percentage; - } - - if (m_ProgressType == ProgressType.Count) - { - if (m_TotalCount == 0) return 0; - return (m_CurrentCount * 100) / m_TotalCount; - } - return 0; - } - } - - public bool isProgressTypeCount { get { return (m_ProgressType == ProgressType.Count || m_ProgressType == ProgressType.Both); } } - public bool isProgressTypePercent { get { return (m_ProgressType == ProgressType.Percent || m_ProgressType == ProgressType.Both); } } - public bool errorOccured { get { return (m_LastError != 0); } } - } -} diff --git a/Editor/Mono/Collab/CollabRevision.cs b/Editor/Mono/Collab/CollabRevision.cs deleted file mode 100644 index d00b479d95..0000000000 --- a/Editor/Mono/Collab/CollabRevision.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Unity C# reference source -// Copyright (c) Unity Technologies. For terms of use, see -// https://unity3d.com/legal/licenses/Unity_Reference_Only_License - -using System.Runtime.InteropServices; -using UnityEngine.Bindings; -using UnityEngine.Scripting; - -namespace UnityEditor.Collaboration -{ - // Keep internal and undocumented until we expose more functionality - //*undocumented - [StructLayout(LayoutKind.Sequential)] - [UsedByNativeCode] - internal struct Revision - { - [NativeName("m_CommitterName")] - private string m_AuthorName; - [NativeName("m_CommitterEmail")] - private string m_Author; - private string m_Comment; - private string m_RevisionID; - private string m_Reference; - private ulong m_TimeStamp; - // Whether this revision has been obtained by the client - private bool m_IsObtained; - private ChangeAction[] m_Entries; - private CloudBuildStatus[] m_BuildStatuses; - - internal Revision(string revisionID = "", string authorName = "", string author = "", - string comment = "", string reference = "", ulong timeStamp = 0, - bool isObtained = false, ChangeAction[] entries = null, - CloudBuildStatus[] buildStatuses = null) - { - m_AuthorName = authorName; - m_Author = author; - m_Comment = comment; - m_RevisionID = revisionID; - m_Reference = reference; - m_TimeStamp = timeStamp; - m_IsObtained = isObtained; - m_Entries = entries ?? new ChangeAction[0]; - m_BuildStatuses = buildStatuses ?? new CloudBuildStatus[0]; - } - - public string authorName { get { return m_AuthorName; } } - public string author { get { return m_Author; } } - public string comment { get { return m_Comment; } } - public string revisionID { get { return m_RevisionID; } } - public string reference { get { return m_Reference; } } - public ulong timeStamp { get { return m_TimeStamp; } } - public bool isObtained { get { return m_IsObtained; } } - public ChangeAction[] entries { get { return m_Entries; } } - public CloudBuildStatus[] buildStatuses { get { return m_BuildStatuses; } } - } -} diff --git a/Editor/Mono/Collab/CollabRevisionsData.cs b/Editor/Mono/Collab/CollabRevisionsData.cs deleted file mode 100644 index 0c13f70010..0000000000 --- a/Editor/Mono/Collab/CollabRevisionsData.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Unity C# reference source -// Copyright (c) Unity Technologies. For terms of use, see -// https://unity3d.com/legal/licenses/Unity_Reference_Only_License - -using System.Runtime.InteropServices; -using UnityEngine.Bindings; -using UnityEngine.Scripting; - -namespace UnityEditor.Collaboration -{ - // Keep internal and undocumented until we expose more functionality - //*undocumented - [StructLayout(LayoutKind.Sequential)] - [UsedByNativeCode] - internal struct RevisionsData - { - private int m_RevisionsInRepo; - private int m_RevisionOffset; - private int m_ReturnedRevisions; - private Revision[] m_Revisions; - - public int RevisionsInRepo {get { return m_RevisionsInRepo; }} - public int RevisionOffset {get { return m_RevisionOffset; }} - public int ReturnedRevisions {get { return m_ReturnedRevisions; }} - public Revision[] Revisions {get { return m_Revisions; }} - } -} diff --git a/Editor/Mono/Collab/CollabTesting.cs b/Editor/Mono/Collab/CollabTesting.cs index 38b0e268a3..06c451a530 100644 --- a/Editor/Mono/Collab/CollabTesting.cs +++ b/Editor/Mono/Collab/CollabTesting.cs @@ -64,8 +64,6 @@ public static void Execute() if (_enumerator == null) return; - if (Collab.instance.AnyJobRunning()) - return; try { diff --git a/Editor/Mono/Collab/CollabToUVCSBridge.cs b/Editor/Mono/Collab/CollabToUVCSBridge.cs new file mode 100644 index 0000000000..2291d35ca6 --- /dev/null +++ b/Editor/Mono/Collab/CollabToUVCSBridge.cs @@ -0,0 +1,530 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +// This file was introduced as part of the collab removal, it supports any versions of com.unity.collab-proxy under 1.17.7 +// It allows for an error free upgrade by provinding mock classes for the removed ones. + +using System; +using System.Diagnostics; +using System.Collections.Generic; + +using UnityEditor.Connect; +using UnityEditor.PackageManager; +using UnityEngine; + +#pragma warning disable 0067 +#pragma warning disable 0618 +namespace UnityEditor.Collaboration +{ + internal static class LogObsolete + { + static bool s_Initialized; + static bool s_NeedsLogging; + static Stopwatch s_Stopwatch = Stopwatch.StartNew(); + + internal static void Log() + { + if (!s_Initialized) + s_NeedsLogging = IsObsolete("com.unity.collab-proxy", "1.1"); + + s_Initialized = true; + + if (!s_NeedsLogging) + return; + + if (s_Stopwatch.ElapsedMilliseconds < 1000) + return; + + UnityEngine.Debug.unityLogger.LogWarning( + "com.unity.collab-proxy", + "This version of the package is not supported, please upgrade to the latest version. https://unity.com/solutions/version-control"); + + s_Stopwatch.Restart(); + } + + internal static bool IsObsolete(string packageName, string version) + { + UnityEditor.PackageManager.PackageInfo package = null; + try + { + package = UnityEditor.PackageManager.PackageInfo.FindForPackageName(packageName); + } + catch + { + return false; + } + + if (package == null) + return false; + + if (package.version == null) + return false; + + return package.version.StartsWith(version); + } + } + + internal class Collab + { + static Collab s_instance = null; + public static Collab instance { + get + { + if (s_instance == null) + s_instance = new Collab(); + + LogObsolete.Log(); + return s_instance; + } + } + + Collab() + { + } + + [Flags] + public enum Operation + { + Noop = 0, + Publish = 1 << 0, + Update = 1 << 1, + Revert = 1 << 2, + GoBack = 1 << 3, + Restore = 1 << 4, + Diff = 1 << 5, + ConflictDiff = 1 << 6, + Exclude = 1 << 7, + Include = 1 << 8, + ChooseMine = 1 << 9, + ChooseTheirs = 1 << 10, + ExternalMerge = 1 << 11, + } + + [Flags] + public enum CollabStates : uint + { + kCollabNone = 0, + kCollabLocal = 1, + kCollabSynced = 1 << 1, + kCollabOutOfSync = 1 << 2, + kCollabIgnored = 1 << 3, + kCollabCheckedOutLocal = 1 << 4, + kCollabCheckedOutRemote = 1 << 5, + kCollabDeletedLocal = 1 << 6, + kCollabDeletedRemote = 1 << 7, + kCollabAddedLocal = 1 << 8, + kCollabAddedRemote = 1 << 9, + kCollabConflicted = 1 << 10, + kCollabMovedLocal = 1 << 11, + kCollabMovedRemote = 1 << 12, + kCollabUpdating = 1 << 13, + kCollabReadOnly = 1 << 14, + kCollabMetaFile = 1 << 15, + kCollabUseMine = 1 << 16, + kCollabUseTheir = 1 << 17, + kCollabMerged = 1 << 18, + kCollabPendingMerge = 1 << 19, + kCollabFolderMetaFile = 1 << 20, + KCollabContentChanged = 1 << 21, + KCollabContentConflicted = 1 << 22, + KCollabContentDeleted = 1 << 23, + kCollabInvalidState = 1 << 30, + kAnyLocalChanged = (kCollabAddedLocal | kCollabCheckedOutLocal | kCollabDeletedLocal | kCollabMovedLocal), + kAnyLocalEdited = (kCollabAddedLocal | kCollabCheckedOutLocal | kCollabMovedLocal), + kCollabAny = 0xFFFFFFFF + } + + internal enum CollabStateID { None, Uninitialized, Initialized } + + internal static class BindingsMarshaller + { + public static IntPtr ConvertToNative(Collab collab){ return IntPtr.Zero; } + } + + public event StateChangedDelegate StateChanged; + public event StateChangedDelegate RevisionUpdated; + public event RevisionChangedDelegate RevisionUpdated_V2; + public event StateChangedDelegate JobsCompleted; + public event ErrorDelegate ErrorOccurred; + public event SetErrorDelegate ErrorOccurred_V2; + public event ErrorDelegate ErrorCleared; + public event ChangeItemsChangedDelegate ChangeItemsChanged; + public event ChangeItemsChangedDelegate SelectedChangeItemsChanged; + public event StateChangedDelegate CollabInfoChanged; + public static int GetRevisionsData(bool withChanges, int startIndex, int numRevisions){ return 0; } + public static int GetSingleRevisionData(bool withChanges, string id){ return 0; } + public static RevisionsData PopulateRevisionsData(IntPtr nativeData){ return new RevisionsData(); } + public static Revision PopulateSingleRevisionData(IntPtr nativeData){ return new Revision(); } + public static ShowToolbarAtPositionDelegate ShowToolbarAtPosition = null; + public static IsToolbarVisibleDelegate IsToolbarVisible = null; + public static CloseToolbarDelegate CloseToolbar = null; + public static ShowHistoryWindowDelegate ShowHistoryWindow = null; + public static ShowChangesWindowDelegate ShowChangesWindow = null; + public static string[] clientType = Array.Empty(); + internal static string editorPrefCollabClientType = string.Empty; + public static string GetProjectClientType() { return string.Empty; } + public static void SetVersionControl(IVersionControl instance){} + internal static bool HasVersionControl(){ return false; } + internal static void ShowChangesWindowView(){} + internal static CollabStates GetAssetState(string assetGuid, string assetPath){ return (CollabStates)0; } + public static void OnSettingStatusChanged(CollabSettingType type, CollabSettingStatus status){} + public static bool InitializeSoftlocksViewController(){ return false; } + public static bool IsDiffToolsAvailable(){ return false; } + public static void SwitchToDefaultMode(){} + public static void OnProgressEnabledSettingStatusChanged(CollabSettingType type, CollabSettingStatus status){} + + public CollabInfo collabInfo { get; } + public void SetSeat(bool value){} + public void RefreshSeatAvailabilityAsync(){} + public string GetProjectGUID(){ return string.Empty; } + public bool ShouldDoInitialCommit(){ return false; } + public void ShowDifferences(string path){} + public void SendNotification(){} + public void SetError(int errorCode){} + public void ClearError(int errorCode){} + public void ClearErrors(){} + public void ForceRefresh(bool refreshAssetDatabase){} + public void SetCollabEnabledForCurrentProject(bool enabled){} + public bool IsCollabEnabledForCurrentProject(){ return false; } + public bool IsAssetIgnored(string path){ return false; } + public bool ShouldTrackAsset(string path){ return false; } + public string GetProjectPath(){ return string.Empty; } + public bool IsConnected(){ return false; } + public bool AnyJobRunning(){ return false; } + public bool JobRunning(int a_jobID){ return false; } + public CollabStates GetAssetState(string guid){ return (CollabStates)0; } + public CollabStates GetSelectedAssetState(){ return (CollabStates)0; } + public CollabStateID GetCollabState(){ return (CollabStateID)0; } + public bool ValidateSelectiveCommit(){ return false; } + public void Disconnect(){} + public void CancelJobByType(int jobType, bool forceCancel){} + public void DoInitialCommit(){} + public void Update(string revisionID, bool updateToRevision){} + public void RevertFile(string path, bool forceOverwrite){} + public void RevertFiles(ChangeItem[] changeItems, bool forceOverwrite){} + public void LaunchConflictExternalMerge(string path){} + public void ShowConflictDifferences(string path){} + public void ResyncSnapshot(){} + public void GoBackToRevision(string revisionID, bool updateToRevision){} + public void ResyncToRevision(string revisionID){} + public Change[] GetCollabConflicts(){ return Array.Empty(); } + public void CheckConflictsResolvedExternal(){} + public bool AreTestsRunning(){ return false; } + public void SetTestsRunning(bool running){} + public void ClearAllFailures(){} + public void ClearNextOperationFailure(){} + public void ClearNextOperationFailureForFile(string path){} + public string GetGUIDForTests(){ return string.Empty; } + public void NewGUIDForTests(){} + public void FailNextOperation(Collab.Operation operation, int code){} + public void TimeOutNextOperation(Collab.Operation operation, int timeOutSec){} + public void FailNextOperationForFile(string path, Collab.Operation operation, int code){} + public void TimeOutNextOperationForFile(string path, Collab.Operation operation, int timeOutSec){} + public void TestPostSoftLockAsCollaborator(string projectGuid, string projectPath, string machineGuid, string assetGuid){} + public void TestClearSoftLockAsCollaborator(string projectGuid, string projectPath, string machineGuid, string softLockHash){} + internal bool GetErrorInternal(int errorFilter, out UnityErrorInfo info){ info = new UnityErrorInfo(); return false; } + public void Publish(string comment, bool useSelectedAssets, bool confirmMatchesPrevious){} + public void PublishAssetsAsync(string comment, ChangeItem[] changes){} + public void ClearSelectedChangesToPublish(){} + public void SendCollabInfoNotification(){} + public CollabFilters collabFilters = new CollabFilters(); + public String projectBrowserSingleSelectionPath { get; set; } + public String projectBrowserSingleMetaSelectionPath { get; set; } + public string[] currentProjectBrowserSelection; + public void RefreshAvailableLocalChangesSynchronous(){} + public bool GetError(UnityConnect.UnityErrorFilter errorFilter, out UnityErrorInfo info){ info = new UnityErrorInfo(); return false; } + public void CancelJob(int jobType){} + public void UpdateEditorSelectionCache(){} + public CollabInfo GetCollabInfo(){ return new CollabInfo(); } + public void SaveAssets(){} + public void ShowInProjectBrowser(string filterString){} + public bool SetConflictsResolvedMine(string[] paths){ return false; } + public bool SetConflictsResolvedTheirs(string[] paths){ return false; } + public PublishInfo GetChangesToPublish() { return new PublishInfo(); } + public PublishInfo_V2 GetChangesToPublish_V2() { return new PublishInfo_V2(); } + public void SetChangesToPublish(ChangeItem[] changes){} + public ProgressInfo GetJobProgress(int jobId){ return new ProgressInfo(); } + } + + internal enum CollabSettingType + { + InProgressEnabled = 0, + InProgressProjectEnabled = 1, + InProgressGlobalEnabled = 2 + } + + internal enum CollabSettingStatus + { + None = 0, + Available = 1 + } + + internal class CollabSettingsManager + { + public delegate void SettingStatusChanged(CollabSettingType type, CollabSettingStatus status); + public static Dictionary statusNotifier = new Dictionary(); + + static CollabSettingsManager(){} + + public static bool IsAvailable(CollabSettingType type) + { + return false; + } + + public static bool inProgressEnabled { get; } + } + + internal class ProgressInfo + { + public enum ProgressType : uint + { + None = 0, + Count = 1, + Percent = 2, + Both = 3 + } + + public int jobId { get { return 0; } } + public string title { get { return string.Empty; } } + public string extraInfo { get { return string.Empty; } } + public int currentCount { get { return 0; } } + public int totalCount { get { return 0; } } + public bool completed { get { return true; } } + public bool cancelled { get { return false; } } + public bool canCancel { get { return false; } } + public string lastErrorString { get { return string.Empty; } } + public ulong lastError { get { return 0; } } + public int percentComplete { get{ return 0; } } + public bool isProgressTypeCount { get { return false; } } + public bool isProgressTypePercent { get { return false; } } + public bool errorOccured { get { return false; } } + } + + internal class ChangeItem + { + public string Path { get; set; } + public Change.RevertableStates RevertableState { get; set; } + public string RelatedTo { get; set; } + public string RevisionId { get; set; } + public string Hash { get; set; } + public Collab.CollabStates State { get; set; } + public long Size { get; set; } + public string DownloadPath { get; set; } + public string FromPath { get; set; } + } + + internal class PublishInfo + { + public Change[] changes; + public bool filter; + } + + internal class PublishInfo_V2 + { + public ChangeItem[] changes; + public bool filter; + } + + internal class RevisionsResult + { + public List Revisions = new List(); + public int RevisionsInRepo = -1; + public int Count { get { return 0; } } + + public void Clear(){} + } + + internal interface IRevisionsService + { + event RevisionsDelegate FetchRevisionsCallback; + void GetRevisions(int offset, int count); + string tipRevision { get; } + string currentUser { get; } + } + + internal class RevisionsService : IRevisionsService + { + public event RevisionsDelegate FetchRevisionsCallback; + public event SingleRevisionDelegate FetchSingleRevisionCallback; + + public string tipRevision { get { return string.Empty; } } + public string currentUser { get { return string.Empty; } } + + public RevisionsService(Collab collabInstance, UnityConnect connectInstance) + { + } + + public void GetRevisions(int offset, int count){} + + public void GetRevision(string revId){} + } + + internal class Change + { + public enum RevertableStates : uint + { + Revertable = 1 << 0, + NotRevertable = 1 << 1, + Revertable_File = 1 << 2, + Revertable_Folder = 1 << 3, + Revertable_EmptyFolder = 1 << 4, + NotRevertable_File = 1 << 5, + NotRevertable_Folder = 1 << 6, + NotRevertable_FileAdded = 1 << 7, + NotRevertable_FolderAdded = 1 << 8, + NotRevertable_FolderContainsAdd = 1 << 9, + InvalidRevertableState = (uint)1 << 31 + } + + public string path { get { return string.Empty; } } + public Collab.CollabStates state { get { return (Collab.CollabStates)0; } } + public bool isRevertable { get { return false; } } + public RevertableStates revertableState { get { return (RevertableStates)0; } } + public string relatedTo { get { return string.Empty; } } + public bool isMeta { get { return false; } } + public bool isConflict { get { return false; } } + public bool isFolderMeta { get { return false; } } + public bool isResolved { get { return false; } } + public string localStatus { get { return string.Empty; } } + public string remoteStatus { get { return string.Empty; } } + public string resolveStatus { get { return string.Empty; } } + + internal bool HasState(Collab.CollabStates states) + { + return false; + } + + internal bool HasRevertableState(RevertableStates revertableStates) + { + return false; + } + } + + internal abstract class AbstractFilters + { + public List filters { get; set;} + public abstract void InitializeFilters(); + public bool ContainsSearchFilter(string name, string searchString){ return false; } + public void ShowInFavoriteSearchFilters(){} + public void HideFromFavoriteSearchFilters(){} + } + + internal class CollabFilters : AbstractFilters + { + public override void InitializeFilters(){} + public void ShowInProjectBrowser(string filterString){} + public void OnCollabStateChanged(CollabInfo info){} + } + + + internal delegate void StateChangedDelegate(CollabInfo info); + internal delegate void RevisionChangedDelegate(CollabInfo info, string rev, string action); + internal delegate void SetErrorDelegate(UnityErrorInfo error); + internal delegate void ErrorDelegate(); + internal delegate bool ShowToolbarAtPositionDelegate(Rect screenRect); + internal delegate bool IsToolbarVisibleDelegate(); + internal delegate void ShowHistoryWindowDelegate(); + internal delegate void ShowChangesWindowDelegate(); + internal delegate void CloseToolbarDelegate(); + internal delegate void ChangesChangedDelegate(Change[] changes, bool isFiltered); + internal delegate void ChangeItemsChangedDelegate(ChangeItem[] changes, bool isFiltered); + delegate void RevisionsDelegate(RevisionsResult revisionsResult); + delegate void SingleRevisionDelegate(Revision? revision); + + internal struct CollabInfo + { + public bool ready { get { return true; } } + public bool update { get { return false; } } + public bool publish { get { return false; } } + public bool inProgress { get { return false; } } + public bool maintenance { get { return false; } } + public bool conflict { get { return false; } } + public bool refresh { get { return false; } } + public bool seat { get { return false; } } + public string tip { get { return string.Empty; } } + public bool Equals(CollabInfo other){ return false; } + } + + internal struct ChangeAction + { + public ChangeAction(string path = "", string action = ""){} + public string path { get { return string.Empty; } } + public string action { get { return string.Empty; } } + } + + internal struct Revision + { + internal Revision(string revisionID = "", string authorName = "", string author = "", string comment = "", string reference = "", ulong timeStamp = 0, bool isObtained = false, ChangeAction[] entries = null, CloudBuildStatus[] buildStatuses = null){} + public string authorName { get { return string.Empty; } } + public string author { get { return string.Empty; } } + public string comment { get { return string.Empty; } } + public string revisionID { get { return string.Empty; } } + public string reference { get { return string.Empty; } } + public ulong timeStamp { get { return 0; } } + public bool isObtained { get { return false; } } + public ChangeAction[] entries { get { return Array.Empty(); } } + public CloudBuildStatus[] buildStatuses { get { return Array.Empty(); } } + } + + internal struct CloudBuildStatus + { + internal CloudBuildStatus(string platform = "", bool complete = false, bool success = false){} + public string platform { get { return string.Empty; } } + public bool complete { get { return false; } } + public bool success { get { return false; } } + } + + internal struct RevisionData + { + public string id; + public int index; + public DateTime timeStamp; + public string authorName; + public string comment; + public bool obtained; + public bool current; + public bool inProgress; + public bool enabled; + public BuildState buildState; + public int buildFailures; + public ICollection changes; + public int changesTotal; + public bool changesTruncated; + } + + internal struct RevisionsData + { + public int RevisionsInRepo {get { return 0; }} + public int RevisionOffset {get { return 0; }} + public int ReturnedRevisions {get { return 0; }} + public Revision[] Revisions {get { return Array.Empty(); }} + } + + internal enum HistoryState + { + Error, + Offline, + Maintenance, + LoggedOut, + NoSeat, + Disabled, + Waiting, + Ready, + } + + internal enum BuildState + { + None, + Configure, + Success, + Failed, + InProgress, + } + + internal struct ChangeData + { + public string path; + public string action; + } +} diff --git a/Editor/Mono/Collab/IVersionControl.cs b/Editor/Mono/Collab/IVersionControl.cs index e636800498..14a9ad00bd 100644 --- a/Editor/Mono/Collab/IVersionControl.cs +++ b/Editor/Mono/Collab/IVersionControl.cs @@ -10,9 +10,7 @@ internal interface IVersionControl bool SupportsAsyncChanges(); bool OnEnableVersionControl(); void OnDisableVersionControl(); - ChangeItem[] GetChanges(); void MergeDownloadedFiles(bool isFullDownload); - Collab.CollabStates GetAssetState(string assetGuid, string assetPath); } internal interface IVersionControl_V2 : IVersionControl diff --git a/Editor/Mono/Collab/RevisionsService.cs b/Editor/Mono/Collab/RevisionsService.cs deleted file mode 100644 index 56bea66492..0000000000 --- a/Editor/Mono/Collab/RevisionsService.cs +++ /dev/null @@ -1,101 +0,0 @@ -// Unity C# reference source -// Copyright (c) Unity Technologies. For terms of use, see -// https://unity3d.com/legal/licenses/Unity_Reference_Only_License - -using System; -using System.Collections.Generic; -using UnityEditor.Collaboration; -using UnityEditor.Connect; -using UnityEngine; -using UnityEngine.Scripting; - -namespace UnityEditor.Collaboration -{ - delegate void RevisionsDelegate(RevisionsResult revisionsResult); - delegate void SingleRevisionDelegate(Revision? revision); - internal class RevisionsResult - { - public List Revisions = new List(); - public int RevisionsInRepo = -1; - - public int Count { get { return Revisions.Count; } } - - public void Clear() - { - Revisions.Clear(); - RevisionsInRepo = -1; - } - } - - internal interface IRevisionsService - { - event RevisionsDelegate FetchRevisionsCallback; - void GetRevisions(int offset, int count); - string tipRevision { get; } - string currentUser { get; } - } - - internal class RevisionsService : IRevisionsService - { - public event RevisionsDelegate FetchRevisionsCallback; - public event SingleRevisionDelegate FetchSingleRevisionCallback; - - protected Collab collab; - protected UnityConnect connect; - private static RevisionsService instance; - - public string tipRevision { get { return collab.collabInfo.tip; } } - public string currentUser { get { return connect.GetUserInfo().userName; } } - - public RevisionsService(Collab collabInstance, UnityConnect connectInstance) - { - collab = collabInstance; - connect = connectInstance; - instance = this; - } - - public void GetRevisions(int offset, int count) - { - // Only send down request for the desired data. - Collab.GetRevisionsData(true, offset, count); - } - - public void GetRevision(string revId) - { - Collab.GetSingleRevisionData(true, revId); - } - - [RequiredByNativeCode] - private static void onFetchSingleRevision(IntPtr ptr) - { - Revision? ret = null; - if (instance.FetchSingleRevisionCallback != null && ptr != IntPtr.Zero) - { - Revision nativeStruct = Collab.PopulateSingleRevisionData(ptr); - // this copies the content as it's a struct not a class. - ret = nativeStruct; - } - - instance.FetchSingleRevisionCallback(ret); - } - - [RequiredByNativeCode] - private static void OnFetchRevisions(IntPtr nativeData) - { - RevisionsService service = instance; - if (service == null || service.FetchRevisionsCallback == null) - return; - - RevisionsResult history = null; - if (nativeData != IntPtr.Zero) - { - RevisionsData data = Collab.PopulateRevisionsData(nativeData); - history = new RevisionsResult(); - history.Revisions.AddRange(data.Revisions); - history.RevisionsInRepo = data.RevisionsInRepo; - } - - service.FetchRevisionsCallback(history); - } - } -} diff --git a/Editor/Mono/Collab/Softlocks/CollabSoftLocks.cs b/Editor/Mono/Collab/Softlocks/CollabSoftLocks.cs deleted file mode 100644 index 44574720dc..0000000000 --- a/Editor/Mono/Collab/Softlocks/CollabSoftLocks.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Unity C# reference source -// Copyright (c) Unity Technologies. For terms of use, see -// https://unity3d.com/legal/licenses/Unity_Reference_Only_License - - -using System.Runtime.InteropServices; -using UnityEngine.Bindings; -using UnityEngine.Scripting; - -namespace UnityEditor.Collaboration -{ - // Keep internal and undocumented until we expose more functionality - //*undocumented - [StructLayout(LayoutKind.Sequential)] - [NativeType(CodegenOptions = CodegenOptions.Custom, Header = "Editor/Src/Collab/Softlocks/CollabSoftLock.h", - IntermediateScriptingStructName = "ScriptingSoftLock")] - [NativeHeader("Editor/Src/Collab/Collab.bindings.h")] - [NativeAsStruct] - internal class SoftLock - { - string m_UserID; - string m_MachineID; - string m_DisplayName; - ulong m_TimeStamp; - string m_Hash; - - SoftLock() {} - - public string userID { get { return m_UserID; } } - public string machineID { get { return m_MachineID; } } - public string displayName { get { return m_DisplayName; } } - public ulong timeStamp { get { return m_TimeStamp; } } - public string hash { get { return m_Hash; } } - } -} - diff --git a/Editor/Mono/Collab/Softlocks/SoftlockData.cs b/Editor/Mono/Collab/Softlocks/SoftlockData.cs deleted file mode 100644 index 759a931cd0..0000000000 --- a/Editor/Mono/Collab/Softlocks/SoftlockData.cs +++ /dev/null @@ -1,166 +0,0 @@ -// Unity C# reference source -// Copyright (c) Unity Technologies. For terms of use, see -// https://unity3d.com/legal/licenses/Unity_Reference_Only_License - - -using System; -using System.Collections.Generic; -using UnityEngine.SceneManagement; -using UnityEngine.Scripting; - -namespace UnityEditor.Collaboration -{ - // Access and query the current set of softlock data. - internal static class SoftLockData - { - internal delegate void OnSoftlockUpdate(string[] assetGUIDs); - internal static OnSoftlockUpdate SoftlockSubscriber = null; - - // Invoked from C++ - [RequiredByNativeCode] - public static void SetSoftlockChanges(string[] assetGUIDs) - { - if (null != SoftlockSubscriber) - { - SoftlockSubscriber(assetGUIDs); - } - } - - // Returns whether the given object has soft lock support - // (i.e. is tracked in the back-end for simultaneous changes). - // Note: Scene is supported, but isn't a Unity Object. - public static bool AllowsSoftLocks(UnityEngine.Object unityObject) - { - if (unityObject == null) - { - throw new ArgumentNullException("unityObject"); - } - - bool supportsSoftLocks = false; - if (unityObject.GetType().Equals(typeof(SceneAsset))) - { - supportsSoftLocks = true; - } - else - { - supportsSoftLocks = IsPrefab(unityObject); - } - return supportsSoftLocks; - } - - public static bool IsPrefab(UnityEngine.Object unityObject) - { - return PrefabUtility.IsPartOfAnyPrefab(unityObject); - } - - public static bool IsPrefab(string assetGUID) - { - bool isPrefab = false; - UnityEngine.Object unityObject; - if (AssetAccess.TryGetAssetFromGUID(assetGUID, out unityObject)) - { - isPrefab = IsPrefab(unityObject); - } - return isPrefab; - } - - // Soft locks are present when collab is enabled and other users are - // editing the given object. - // Failure: assigns false to 'hasSoftLocks', returns false. - // Success: assigns true or false to 'hasSoftLocks', returns true. - public static bool TryHasSoftLocks(UnityEngine.Object objectWithGUID, out bool hasSoftLocks) - { - string assetGuid = null; - AssetAccess.TryGetAssetGUIDFromObject(objectWithGUID, out assetGuid); - bool success = TryHasSoftLocks(assetGuid, out hasSoftLocks); - return success; - } - - public static bool TryHasSoftLocks(string assetGuid, out bool hasSoftLocks) - { - hasSoftLocks = false; - bool success = false; - int count = 0; - - if (TryGetSoftlockCount(assetGuid, out count)) - { - success = true; - hasSoftLocks = (count > 0); - } - return success; - } - - // Provides the number of additional users editing the given scene. - // Failure: assigns 0 to count, return false. - // Success: assigns a value in [0, n] to count, returns true. - public static bool TryGetSoftlockCount(Scene scene, out int count) - { - bool success = false; - count = 0; - - if (!scene.IsValid()) - { - return false; - } - string assetGUID = AssetDatabase.AssetPathToGUID(scene.path); - success = TryGetSoftlockCount(assetGUID, out count); - return success; - } - - // Provides the number of additional users editing the given object. - // Failure: assigns 0 to count, return false. - // Success: assigns a value in [0, n] to count, returns true. - public static bool TryGetSoftlockCount(UnityEngine.Object objectWithGUID, out int count) - { - string assetGUID = null; - AssetAccess.TryGetAssetGUIDFromObject(objectWithGUID, out assetGUID); - bool success = TryGetSoftlockCount(assetGUID, out count); - return success; - } - - // Provides the number of additional users editing the given 'assetGUID'. - // Failure: assigns 0 to count, return false. - // Success: assigns a value in [0, n] to count, returns true. - public static bool TryGetSoftlockCount(string assetGuid, out int count) - { - bool success = false; - count = 0; - List softLocks = null; - - if (TryGetLocksOnAssetGUID(assetGuid, out softLocks)) - { - count = softLocks.Count; - success = true; - } - return success; - } - - // Provides a list of 'SoftLock' items, representing - // the additional users editing the given assetGUID. - // Failure: assigns empty list to 'softLocks', return false. - // Success: assigns the retrieved list to 'softLocks', return true. (May be empty). - public static bool TryGetLocksOnAssetGUID(string assetGuid, out List softLocks) - { - if (assetGuid == null) - { - throw new ArgumentNullException("assetGuid"); - } - - if (!Collab.instance.IsCollabEnabledForCurrentProject() || assetGuid.Length == 0) - { - softLocks = new List(); - return false; - } - - SoftLock[] _softlocks = Collab.instance.GetSoftLocks(assetGuid); - softLocks = new List(); - - for (int index = 0; index < _softlocks.Length; index++) - { - softLocks.Add(_softlocks[index]); - } - return true; - } - } -} - diff --git a/Editor/Mono/Collab/Softlocks/SoftlockFilter.cs b/Editor/Mono/Collab/Softlocks/SoftlockFilter.cs deleted file mode 100644 index a1a56d4571..0000000000 --- a/Editor/Mono/Collab/Softlocks/SoftlockFilter.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Unity C# reference source -// Copyright (c) Unity Technologies. For terms of use, see -// https://unity3d.com/legal/licenses/Unity_Reference_Only_License - - -using System; -using System.Collections.Generic; -using System.Linq; -using UnityEditor; - -namespace UnityEditor.Collaboration -{ - internal class SoftLockFilters : AbstractFilters - { - public override void InitializeFilters() - { - filters = new List() - { - new string[] { "All In Progress" , "s:inprogress"}, - }; - } - - public SoftLockFilters() - { - InitializeFilters(); - } - - public void OnSettingStatusChanged(CollabSettingType type, CollabSettingStatus status) - { - if (type == CollabSettingType.InProgressEnabled && (status == CollabSettingStatus.Available)) - { - if (Collab.instance.IsCollabEnabledForCurrentProject() && CollabSettingsManager.inProgressEnabled) - ShowInFavoriteSearchFilters(); - else - HideFromFavoriteSearchFilters(); - } - } - } -} diff --git a/Editor/Mono/Collab/Softlocks/SoftlockUIData.cs b/Editor/Mono/Collab/Softlocks/SoftlockUIData.cs deleted file mode 100644 index e0d8c1df20..0000000000 --- a/Editor/Mono/Collab/Softlocks/SoftlockUIData.cs +++ /dev/null @@ -1,207 +0,0 @@ -// Unity C# reference source -// Copyright (c) Unity Technologies. For terms of use, see -// https://unity3d.com/legal/licenses/Unity_Reference_Only_License - - -using System; -using System.Collections.Generic; -using UnityEngine; -using UnityEngine.SceneManagement; -using UnityEditor.SceneManagement; -using UnityEditor.Web; - -namespace UnityEditor.Collaboration -{ - // Composes Softlock data into structures used by the UI. - internal static class SoftLockUIData - { - private static Dictionary s_ImageCache = new Dictionary(); - private static Dictionary s_ImageNameCache = new Dictionary(); - private const string kIconMipSuffix = " Icon"; - - public enum SectionEnum - { - None, - Inspector, - Scene, - ProjectBrowser - } - - #region General - - // Provides the names of all additional users editing the asset - // with the given 'assetGuid'. - // Defaults to an empty list. - public static List GetLocksNamesOnAsset(string assetGuid) - { - List softLocks = null; - List names = new List(); - - if (SoftLockData.TryGetLocksOnAssetGUID(assetGuid, out softLocks)) - { - foreach (SoftLock softLock in softLocks) - { - names.Add(softLock.displayName); - } - } - return names; - } - - #endregion - #region Scene - - // Provides the names of all additional users editing the scene. - // Defaults to an empty list. - public static List GetLocksNamesOnScene(Scene scene) - { - List names = GetLockNamesOnScenePath(scene.path); - return names; - } - - public static List GetLockNamesOnScenePath(string scenePath) - { - string assetGuid = AssetDatabase.AssetPathToGUID(scenePath); - List names = GetLocksNamesOnAsset(assetGuid); - return names; - } - - public static string GetSceneNameFromPath(string scenePath) - { - string name = ""; - if (null != scenePath) - { - name = scenePath; - } - return name; - } - - // Provides the names of all additional users editing each scene. - // Defaults to an empty list, and may contain empty sub-lists. - public static List> GetLockNamesOnScenes(List scenes) - { - List> namesByScene = new List>(); - - if (scenes == null) - { - return namesByScene; - } - - foreach (Scene scene in scenes) - { - List names = GetLocksNamesOnScene(scene); - namesByScene.Add(names); - } - return namesByScene; - } - - // For each iteration, returns the pair (scene name : list of other users' names). - public static IEnumerable>> GetLockNamesOnOpenScenes() - { - if (Collab.instance.IsCollabEnabledForCurrentProject()) - { - for (int sceneIndex = 0; sceneIndex < EditorSceneManager.sceneCount; sceneIndex++) - { - Scene scene = SceneManager.GetSceneAt(sceneIndex); - List names = GetLocksNamesOnScene(scene); - string sceneName = scene.name; - if (String.IsNullOrEmpty(sceneName)) - { - // Default for unnamed scenes. - sceneName = "Untitled"; - } - KeyValuePair> sceneData = new KeyValuePair>(sceneName, names); - yield return sceneData; - } - } - } - - public static int CountOfLocksOnOpenScenes() - { - int count = 0; - - foreach (KeyValuePair> sceneData in GetLockNamesOnOpenScenes()) - { - count += sceneData.Value.Count; - } - return count; - } - - #endregion - #region Game Object - - // The usernames of additional people editing the given 'objectWithGUID'. - // Defaults to an empty list. - public static List GetLockNamesOnObject(UnityEngine.Object objectWithGUID) - { - string assetGUID = null; - AssetAccess.TryGetAssetGUIDFromObject(objectWithGUID, out assetGUID); - List names = GetLocksNamesOnAsset(assetGUID); - return names; - } - - #endregion - #region Icons - - // The icon for the particular section in the editor. - // Defaults to null. - public static Texture GetIconForSection(SectionEnum section) - { - string iconName = IconNameForSection(section); - Texture texture = GetIconForName(iconName); - return texture; - } - - private static string IconNameForSection(SectionEnum section) - { - string iconName; - if (!s_ImageNameCache.TryGetValue(section, out iconName)) - { - switch (section) - { - case SectionEnum.Inspector: - case SectionEnum.Scene: - iconName = "SoftlockInline.png"; - break; - - case SectionEnum.ProjectBrowser: - iconName = String.Format("SoftlockProjectBrowser{0}", kIconMipSuffix); - break; - - default: - return null; - } - s_ImageNameCache.Add(section, iconName); - } - return iconName; - } - - private static Texture GetIconForName(string fileName) - { - if (String.IsNullOrEmpty(fileName)) - { - return null; - } - - Texture texture; - // Note: a previous texture may have been destroyed - // by the system on the c++ side. - if (!s_ImageCache.TryGetValue(fileName, out texture) || texture == null) - { - if (fileName.EndsWith(kIconMipSuffix)) - { - texture = EditorGUIUtility.FindTexture(fileName) as Texture; - } - else - { - texture = EditorGUIUtility.LoadIconRequired(fileName) as Texture; - } - s_ImageCache.Remove(fileName); - s_ImageCache.Add(fileName, texture); - } - return texture; - } - - #endregion - } -} - diff --git a/Editor/Mono/Collab/Softlocks/SoftlockViewController.cs b/Editor/Mono/Collab/Softlocks/SoftlockViewController.cs deleted file mode 100644 index e4de3221f8..0000000000 --- a/Editor/Mono/Collab/Softlocks/SoftlockViewController.cs +++ /dev/null @@ -1,533 +0,0 @@ -// 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.Collections.Generic; -using UnityEditor.Collaboration; -using UnityEngine.SceneManagement; -using UnityEditor.SceneManagement; -using System; -using UnityEditor.Web; - -namespace UnityEditor -{ - // Displays the Softlocks UI in the various areas of the Editor. - internal class SoftlockViewController - { - private static SoftlockViewController s_Instance; - public GUIStyle k_Style = null; - public GUIStyle k_StyleEmpty = GUIStyle.none; // For tooltips only. - public GUIContent k_Content = null; - - // Stores UI strings for reuse and Editor (inspector) references to trigger - // a repaint when softlock data changes. - private SoftlockViewController.Cache m_Cache = null; - - private const string k_TooltipHeader = "Unpublished changes by:"; - private const string k_TooltipPrefabHeader = "Unpublished Prefab changes by:"; - private const string k_TooltipNamePrefix = " \n \u2022 "; // u2022 displays a • (bullet point) - - private SoftlockViewController() {} - ~SoftlockViewController() {} - - [SerializeField] - private SoftLockFilters m_SoftLockFilters = new SoftLockFilters(); - - public SoftLockFilters softLockFilters { get { return m_SoftLockFilters; } } - - public static SoftlockViewController Instance - { - get - { - if (s_Instance == null) - { - s_Instance = new SoftlockViewController(); - s_Instance.m_Cache = new Cache(); - } - return s_Instance; - } - } - - // Initialises dependencies. - public void TurnOn() - { - RegisterDataDelegate(); - RegisterDrawDelegates(); - Repaint(); - } - - public void TurnOff() - { - UnregisterDataDelegate(); - UnregisterDrawDelegates(); - } - - private void UnregisterDataDelegate() - { - SoftLockData.SoftlockSubscriber -= Instance.OnSoftlockUpdate; - } - - private void RegisterDataDelegate() - { - UnregisterDataDelegate(); - SoftLockData.SoftlockSubscriber += Instance.OnSoftlockUpdate; - } - - private void UnregisterDrawDelegates() - { - ObjectListArea.postAssetIconDrawCallback -= Instance.DrawProjectBrowserGridUI; - ObjectListArea.postAssetLabelDrawCallback -= Instance.DrawProjectBrowserListUI; - Editor.OnPostIconGUI -= Instance.DrawInspectorUI; - GameObjectTreeViewGUI.OnPostHeaderGUI -= Instance.DrawSceneUI; - } - - // Connects to the areas of the Editor that display softlocks. - private void RegisterDrawDelegates() - { - UnregisterDrawDelegates(); - ObjectListArea.postAssetIconDrawCallback += Instance.DrawProjectBrowserGridUI; - ObjectListArea.postAssetLabelDrawCallback += Instance.DrawProjectBrowserListUI; - AssetsTreeViewGUI.postAssetLabelDrawCallback += Instance.DrawSingleColumnProjectBrowserUI; - Editor.OnPostIconGUI += Instance.DrawInspectorUI; - GameObjectTreeViewGUI.OnPostHeaderGUI += Instance.DrawSceneUI; - } - - // Returns true when the 'editor' supports Softlock UI and the - // user has Collaborate permissions. - private bool HasSoftlockSupport(Editor editor) - { - if (!Collab.instance.IsCollabEnabledForCurrentProject() || editor == null || editor.targets.Length > 1) - { - return false; - } - - if (editor.target == null || !SoftLockData.AllowsSoftLocks(editor.target)) - { - return false; - } - - // Support Scene and Game object Inspector headers, not others like MaterialEditor. - bool hasSupport = true; - Type editorType = editor.GetType(); - - if (editorType != typeof(GameObjectInspector) && editorType != typeof(GenericInspector)) - { - hasSupport = false; - } - - return hasSupport; - } - - private bool HasSoftlocks(string assetGUID) - { - if (!Collab.instance.IsCollabEnabledForCurrentProject()) - { - return false; - } - - bool hasSoftLocks; - bool isValid = (SoftLockData.TryHasSoftLocks(assetGUID, out hasSoftLocks) && hasSoftLocks); - return isValid; - } - - // Redraws softlock UI associated with the given list of 'assetGUIDs'. - public void OnSoftlockUpdate(string[] assetGUIDs) - { - // Remove cached UI for the assetGUIDs before triggered a redraw. - m_Cache.InvalidateAssetGUIDs(assetGUIDs); - Repaint(); - } - - // Repaints all the areas where softlocks are displayed. - public void Repaint() - { - RepaintInspectors(); - RepaintSceneHierarchy(); - RepaintProjectBrowsers(); - } - - private void RepaintSceneHierarchy() - { - List sceneUIs = SceneHierarchyWindow.GetAllSceneHierarchyWindows(); - foreach (SceneHierarchyWindow sceneUI in sceneUIs) - { - sceneUI.Repaint(); - } - } - - private void RepaintInspectors() - { - foreach (Editor editor in m_Cache.GetEditors()) - { - // Does not repaint when editor is not visible, but the editor's - // "DockArea" tab will redraw either way. - editor.Repaint(); - } - } - - private void RepaintProjectBrowsers() - { - foreach (ProjectBrowser pb in ProjectBrowser.GetAllProjectBrowsers()) - { - pb.RefreshSearchIfFilterContains("s:"); - pb.Repaint(); - } - } - - // Draws in the Hierarchy header, left of the context menu. - public float DrawSceneUI(Rect availableRect, string scenePath) - { - string assetGUID = AssetDatabase.AssetPathToGUID(scenePath); - if (!HasSoftlocks(assetGUID)) - { - return availableRect.xMax; - } - - int lockCount; - SoftLockData.TryGetSoftlockCount(assetGUID, out lockCount); - - GUIContent content = GetGUIContent(); - content.image = SoftLockUIData.GetIconForSection(SoftLockUIData.SectionEnum.Scene); - content.text = GetDisplayCount(lockCount); - content.tooltip = Instance.GetTooltip(assetGUID); - - Vector2 contentSize = GetStyle().CalcSize(content); - Rect drawRect = new Rect(availableRect.position, contentSize); - const int kRightMargin = 4; - drawRect.x = (availableRect.width - drawRect.width) - kRightMargin; - EditorGUI.LabelField(drawRect, content); - - return drawRect.xMin; - } - - // Assigned as a callback to Editor.OnPostHeaderGUI - // Draws the Scene Inspector (Editor.cs) as well as the Game Object Inspector (GameObjectInspector.cs) - private void DrawInspectorUI(Editor editor, Rect drawRect) - { - if (!HasSoftlockSupport(editor)) - { - return; - } - - m_Cache.StoreEditor(editor); - string assetGUID = null; - AssetAccess.TryGetAssetGUIDFromObject(editor.target, out assetGUID); - - if (!HasSoftlocks(assetGUID)) - { - return; - } - - Texture icon = SoftLockUIData.GetIconForSection(SoftLockUIData.SectionEnum.ProjectBrowser); - if (icon != null) - { - DrawIconWithTooltips(drawRect, icon, assetGUID); - } - } - - // Assigned callback to ObjectListArea.OnPostAssetDrawDelegate. - // Draws either overtop of the project browser asset (when in grid view). - private void DrawProjectBrowserGridUI(Rect iconRect, string assetGUID, bool isListMode) - { - if (isListMode || !HasSoftlocks(assetGUID)) - { - return; - } - - Rect drawRect = Rect.zero; - Texture icon = SoftLockUIData.GetIconForSection(SoftLockUIData.SectionEnum.ProjectBrowser); - if (icon != null) - { - drawRect = Overlay.GetRectForBottomRight(iconRect, Overlay.k_OverlaySizeOnLargeIcon); - DrawIconWithTooltips(drawRect, icon, assetGUID); - } - } - - // Should draw only in listMode and expects 'drawRect' to be the designed space for the icon, - // and not the entire row. - private bool DrawProjectBrowserListUI(Rect drawRect, string assetGUID, bool isListMode) - { - if (!isListMode || !HasSoftlocks(assetGUID)) - { - return false; - } - - // center icon. - Rect iconRect = drawRect; - iconRect.width = drawRect.height; - iconRect.x = (float)Math.Round(drawRect.center.x - (iconRect.width / 2F)); - return DrawInProjectBrowserListMode(iconRect, assetGUID); - } - - // Expects 'drawRect' to be the available width of the row. - private bool DrawSingleColumnProjectBrowserUI(Rect drawRect, string assetGUID) - { - if (ProjectBrowser.s_LastInteractedProjectBrowser.IsTwoColumns() || !HasSoftlocks(assetGUID)) - { - return false; - } - - Rect iconRect = drawRect; - iconRect.width = drawRect.height; - float spacingFromEnd = (iconRect.width / 2F); - iconRect.x = (float)Math.Round(drawRect.xMax - iconRect.width - spacingFromEnd); - return DrawInProjectBrowserListMode(iconRect, assetGUID); - } - - private bool DrawInProjectBrowserListMode(Rect iconRect, string assetGUID) - { - Texture icon = SoftLockUIData.GetIconForSection(SoftLockUIData.SectionEnum.ProjectBrowser); - bool didDraw = false; - if (icon != null) - { - DrawIconWithTooltips(iconRect, icon, assetGUID); - didDraw = true; - } - return didDraw; - } - - private void DrawIconWithTooltips(Rect iconRect, Texture icon, string assetGUID) - { - GUI.DrawTexture(iconRect, icon, ScaleMode.ScaleToFit); - DrawTooltip(iconRect, GetTooltip(assetGUID)); - } - - private void DrawTooltip(Rect frame, string tooltip) - { - GUIContent content = GetGUIContent(); - content.tooltip = tooltip; - GUI.Label(frame, content, k_StyleEmpty); - } - - #region String Helpers - - // Returns a string formatted as a vertical list of names with a heading. - private string GetTooltip(string assetGUID) - { - string formattedText; - if (!m_Cache.TryGetTooltipForGUID(assetGUID, out formattedText)) - { - List softLockNames = SoftLockUIData.GetLocksNamesOnAsset(assetGUID); - string tooltipHeaderText = (SoftLockData.IsPrefab(assetGUID) ? k_TooltipPrefabHeader : k_TooltipHeader); - formattedText = tooltipHeaderText; - - foreach (string name in softLockNames) - { - formattedText += k_TooltipNamePrefix + name + " "; - } - m_Cache.StoreTooltipForGUID(assetGUID, formattedText); - } - return formattedText; - } - - // Retrieves a previously generated string from cache - // or creates a string displaying the given 'count' surrounded by brackets. - // e.g. "(0)" - private static string GetDisplayCount(int count) - { - string totalLocksText; - if (!Instance.m_Cache.TryGetDisplayCount(count, out totalLocksText)) - { - totalLocksText = count.ToString(); - Instance.m_Cache.StoreDisplayCount(count, totalLocksText); - } - return totalLocksText; - } - - // When the given 'text' exceeds the given 'width', out-of-bound characters - // are removed as well as a few more to display a trailing ellipsis. - // If 'text' does not exceed width, text is returned. - private string FitTextToWidth(string text, float width, GUIStyle style) - { - int characterCountVisible = style.GetNumCharactersThatFitWithinWidth(text, width); - if (characterCountVisible > 1 && characterCountVisible != text.Length) - { - string ellipsedText; - int characterLength = (characterCountVisible - 1); - if (!Instance.m_Cache.TryGetEllipsedNames(text, characterLength, out ellipsedText)) - { - ellipsedText = text.Substring(0, characterLength) + (" \u2026"); // 'horizontal ellipsis' (U+2026) is: ... - Instance.m_Cache.StoreEllipsedNames(text, ellipsedText, characterLength); - } - return ellipsedText; - } - return text; - } - - #endregion - #region GUI Content - - public GUIContent GetGUIContent() - { - if (k_Content == null) - { - k_Content = new GUIContent(); - } - - k_Content.tooltip = string.Empty; - k_Content.text = null; - k_Content.image = null; - - return k_Content; - } - - public GUIStyle GetStyle() - { - if (k_Style == null) - { - k_Style = new GUIStyle(EditorStyles.label); - k_Style.normal.background = null; - } - return k_Style; - } - - #endregion - - // Stores UI strings for reuse and Editors as WeakReferences. - private class Cache - { - private List m_EditorReferences = new List(); - private List m_CachedWeakReferences = new List(); - private static Dictionary s_CachedStringCount = new Dictionary(); - private Dictionary m_AssetGUIDToTooltip = new Dictionary(); - private Dictionary> m_NamesListToEllipsedNames = new Dictionary>(); - - public Cache() {} - - // Removes cached strings references by the given 'assetGUIDs'. - public void InvalidateAssetGUIDs(string[] assetGUIDs) - { - for (int index = 0; index < assetGUIDs.Length; index++) - { - string assetGUID = assetGUIDs[index]; - m_AssetGUIDToTooltip.Remove(assetGUID); - } - } - - // Failure: assigns empty string ("") to 'ellipsedNames', returns false. - // Success: assigns the cached string to 'ellipsedNames', returns true. - public bool TryGetEllipsedNames(string allNames, int characterLength, out string ellipsedNames) - { - Dictionary ellipsedVersions; - if (m_NamesListToEllipsedNames.TryGetValue(allNames, out ellipsedVersions)) - { - return ellipsedVersions.TryGetValue(characterLength, out ellipsedNames); - } - ellipsedNames = ""; - return false; - } - - // 'allNames' and 'characterLength' will be the keys to access the cached 'ellipsedNames' - // see TryGetEllipsedNames() for retrieval. - public void StoreEllipsedNames(string allNames, string ellipsedNames, int characterLength) - { - Dictionary ellipsedVersions; - if (!m_NamesListToEllipsedNames.TryGetValue(allNames, out ellipsedVersions)) - { - ellipsedVersions = new Dictionary(); - } - ellipsedVersions[characterLength] = ellipsedNames; - m_NamesListToEllipsedNames[allNames] = ellipsedVersions; - } - - // Failure: assigns empty string ("") to 'tooltipText', returns false. - // Success: assigns the cached string to 'tooltipText', returns true. - public bool TryGetTooltipForGUID(string assetGUID, out string tooltipText) - { - return m_AssetGUIDToTooltip.TryGetValue(assetGUID, out tooltipText); - } - - // 'assetGUID' will be the key to access the cached 'tooltipText' - // see TryGetTooltipForGUID() for retrieval. - public void StoreTooltipForGUID(string assetGUID, string tooltipText) - { - m_AssetGUIDToTooltip[assetGUID] = tooltipText; - } - - // Failure: assigns empty string ("") to 'displayText', returns false. - // Success: assigns the cached string to 'displayText', returns true. - public bool TryGetDisplayCount(int count, out string displayText) - { - return s_CachedStringCount.TryGetValue(count, out displayText); - } - - // 'count' will be the key to access the cached 'displayText' - // see TryGetDisplayCount() for retrieval. - public void StoreDisplayCount(int count, string displayText) - { - s_CachedStringCount.Add(count, displayText); - } - - // Contains at most the list of all previously given Editors - // via StoreEditor(). Garbage collected Editor(s) will be missing. - public List GetEditors() - { - List editors = new List(); - - for (int index = 0; index < m_EditorReferences.Count; index++) - { - WeakReference reference = m_EditorReferences[index]; - Editor editor = reference.Target as Editor; - - if (editor == null) - { - m_EditorReferences.RemoveAt(index); - m_CachedWeakReferences.Add(reference); - index--; - } - else - { - editors.Add(editor); - } - } - return editors; - } - - // Stores the Editor in a WeakReference. - public void StoreEditor(Editor editor) - { - bool canAdd = true; - - // Check for duplicates and purge any null targets. - for (int index = 0; canAdd && (index < m_EditorReferences.Count); index++) - { - WeakReference reference = m_EditorReferences[index]; - Editor storedEditor = reference.Target as Editor; - - if (storedEditor == null) - { - m_EditorReferences.RemoveAt(index); - m_CachedWeakReferences.Add(reference); - index--; - } - else if (storedEditor == editor) - { - canAdd = false; - break; - } - } - - if (canAdd) - { - WeakReference editorReference; - - // Reuse any old WeakReference if available. - if (m_CachedWeakReferences.Count > 0) - { - editorReference = m_CachedWeakReferences[0]; - m_CachedWeakReferences.RemoveAt(0); - } - else - { - editorReference = new WeakReference(null); - } - editorReference.Target = editor; - m_EditorReferences.Add(editorReference); - } - } - } - } -} - diff --git a/Editor/Mono/Collab/Views/ICollabHistoryWindow.cs b/Editor/Mono/Collab/Views/ICollabHistoryWindow.cs deleted file mode 100644 index ab312c85a4..0000000000 --- a/Editor/Mono/Collab/Views/ICollabHistoryWindow.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Unity C# reference source -// Copyright (c) Unity Technologies. For terms of use, see -// https://unity3d.com/legal/licenses/Unity_Reference_Only_License - -using System; -using System.Collections.Generic; - -namespace UnityEditor.Collaboration -{ - internal delegate void PageChangeAction(int page); - internal delegate void RevisionAction(string revisionId, bool updateToRevision); - internal delegate void ShowBuildAction(string revisionId); - - internal enum HistoryState - { - Error, - Offline, - Maintenance, - LoggedOut, - NoSeat, - Disabled, - Waiting, - Ready, - } - - internal enum BuildState - { - None, - Configure, - Success, - Failed, - InProgress, - } - - internal struct RevisionData - { - public string id; - public int index; - public DateTime timeStamp; - public string authorName; - public string comment; - - // Whether this revision is on the client - public bool obtained; - public bool current; - public bool inProgress; - public bool enabled; - - public BuildState buildState; - public int buildFailures; - - public ICollection changes; - public int changesTotal; - public bool changesTruncated; - } - - internal struct ChangeData - { - public string path; - public string action; - } - - internal interface ICollabHistoryWindow - { - void UpdateState(HistoryState state, bool force); - void UpdateRevisions(IEnumerable items, string tip, int totalRevisions, int currentPage); - - bool revisionActionsEnabled { get; set; } - int itemsPerPage { set; } - string inProgressRevision { get; set; } - PageChangeAction OnPageChangeAction { set; } - RevisionAction OnGoBackAction { set; } - RevisionAction OnUpdateAction { set; } - RevisionAction OnRestoreAction { set; } - ShowBuildAction OnShowBuildAction { set; } - Action OnShowServicesAction { set; } - } -} diff --git a/Editor/Mono/Commands/CommandService.cs b/Editor/Mono/Commands/CommandService.cs index a78ca32515..10f56233d8 100644 --- a/Editor/Mono/Commands/CommandService.cs +++ b/Editor/Mono/Commands/CommandService.cs @@ -152,8 +152,16 @@ private static IEnumerable ScanAttributes() var commands = new List(); foreach (var mi in TypeCache.GetMethodsWithAttribute()) { - if (!(Delegate.CreateDelegate(typeof(CommandHandler), mi) is CommandHandler callback)) + CommandHandler callback = null; + try + { + callback = (CommandHandler)Delegate.CreateDelegate(typeof(CommandHandler), mi); + } + catch(Exception e) + { + Debug.LogError($"Cannot create CommandHandler from Attribute: {mi.Name} {e.Message}"); continue; + } foreach (var attr in mi.GetCustomAttributes()) { diff --git a/Editor/Mono/Commands/GOCreationCommands.cs b/Editor/Mono/Commands/GOCreationCommands.cs index 6caef996d3..4f7aed86df 100644 --- a/Editor/Mono/Commands/GOCreationCommands.cs +++ b/Editor/Mono/Commands/GOCreationCommands.cs @@ -196,6 +196,12 @@ internal static void CreateEmptyParent() } SceneHierarchyWindow.lastInteractedHierarchyWindow.SetExpanded(go.GetInstanceID(), true); + + // Ensure empty parent after reparenting jumps into rename mode if needed UUM-15042 + if (SceneHierarchyWindow.s_EnterRenameModeForNewGO) + { + SceneHierarchyWindow.FrameAndRenameNewGameObject(); + } } // Set back default parent object if we have one diff --git a/Editor/Mono/ConsoleWindow.cs b/Editor/Mono/ConsoleWindow.cs index 2c3bc2ad0b..b151101d0a 100644 --- a/Editor/Mono/ConsoleWindow.cs +++ b/Editor/Mono/ConsoleWindow.cs @@ -156,7 +156,7 @@ internal static void UpdateLogStyleFixedHeights() ListViewState m_ListView; string m_ActiveText = ""; StringBuilder m_CopyString; - private int m_LastPingedEntry = -1; + private int m_LastPingedEntryRow = -1; bool m_DevBuild; int m_CallstackTextStart = 0; private Mode m_ActiveMode = Mode.None; @@ -453,18 +453,19 @@ void SetActiveEntry(LogEntry entry) m_ActiveMode = (Mode)entry.mode; entry.callstackTextStartUTF8 = entry.message.Length; m_CallstackTextStart = entry.callstackTextStartUTF16; + var entryRow = LogEntries.GetEntryRowIndex(entry.globalLineIndex); // ping object referred by the log entry - if (entry.instanceID != 0 && m_LastPingedEntry != entry.globalLineIndex) + if (entry.instanceID != 0 && m_LastPingedEntryRow != entryRow) { EditorGUIUtility.PingObject(entry.instanceID); - m_LastPingedEntry = entry.globalLineIndex; + m_LastPingedEntryRow = entryRow; } } else { m_CallstackTextStart = 0; m_ActiveText = string.Empty; - m_LastPingedEntry = -1; + m_LastPingedEntryRow = -1; m_ListView.row = -1; m_CopyString.Clear(); m_ActiveMode = Mode.None; @@ -882,10 +883,7 @@ internal static string StacktraceWithHyperlinks(string stacktraceText, int calls filePathPart.Substring(lineIndex + 1, (endLineIndex) - (lineIndex + 1)); string filePath = filePathPart.Substring(0, lineIndex); - textWithHyperlinks.Append(lines[i].Substring(0, filePathIndex)); - textWithHyperlinks.Append(""); - textWithHyperlinks.Append(filePath + ":" + lineString); - textWithHyperlinks.Append(")\n"); + textWithHyperlinks.Append($"{lines[i].Substring(0, filePathIndex)}{filePath}:{lineString})\n"); continue; // continue to evade the default case } diff --git a/Editor/Mono/ContainerWindow.bindings.cs b/Editor/Mono/ContainerWindow.bindings.cs index 43c3163335..86e3df08c1 100644 --- a/Editor/Mono/ContainerWindow.bindings.cs +++ b/Editor/Mono/ContainerWindow.bindings.cs @@ -26,8 +26,6 @@ internal enum ShowMode Tooltip = 6, // Modal Utility window ModalUtility = 7, - // Show as fullscreen window - Fullscreen = 8 } //[StaticAccessor("ContainerWindowBindings", StaticAccessorType.DoubleColon)] @@ -44,6 +42,8 @@ public extern Rect position [FreeFunction(k_ScriptingPrefix + "SetPosition", HasExplicitThis = true)] set; } + [FreeFunction(k_ScriptingPrefix + "SetFreeze", HasExplicitThis = true)] + public extern void SetFreeze(bool freeze); public extern bool maximized {[FreeFunction(k_ScriptingPrefix + "IsWindowMaximized", HasExplicitThis = true)] get; } [FreeFunction(k_ScriptingPrefix + "SetAlpha", HasExplicitThis = true)] @@ -65,12 +65,6 @@ 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); @@ -84,9 +78,6 @@ 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(); @@ -95,7 +86,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, int displayIndex = 0); + private extern void Internal_Show(Rect r, int showMode, Vector2 minSize, Vector2 maxSize); [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 f35f18db94..5aa556b559 100644 --- a/Editor/Mono/ContainerWindow.cs +++ b/Editor/Mono/ContainerWindow.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System; using System.Linq; +using UnityEngine.Scripting; namespace UnityEditor { @@ -24,9 +25,7 @@ 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_IsForceTitleBar; + internal bool m_IsMppmCloneWindow; internal bool m_DontSaveToLayout = false; private bool m_HasUnsavedChanges = false; @@ -39,7 +38,6 @@ internal partial class ContainerWindow : ScriptableObject internal const float kButtonWidth = 16f, kButtonHeight = 16f; static internal bool macEditor => Application.platform == RuntimePlatform.OSXEditor; - static internal bool linuxEditor => Application.platform == RuntimePlatform.LinuxEditor; static internal bool s_Modal = false; private static ContainerWindow s_MainWindow; @@ -47,28 +45,16 @@ internal partial class ContainerWindow : ScriptableObject private static class Styles { - // Title Bar Buttons (Non) - public static GUIStyle buttonMin = "WinBtnMinMac"; - public static GUIStyle buttonClose = macEditor ? "WinBtnCloseMac" : "WinBtnClose"; - public static GUIStyle buttonMax = macEditor ? "WinBtnMaxMac" : "WinBtnMax"; - public static GUIStyle buttonRestore = macEditor ? "WinBtnRestoreMac" : "WinBtnRestore"; - public static float borderSize => macEditor ? osxBorderSize : winBorderSize; public static float buttonMargin => macEditor ? osxBorderMargin : winBorderMargin; - public static SVC buttonTop = new SVC("--container-window-button-top-margin"); - private static SVC winBorderSize = new SVC("--container-window-buttons-right-margin-win"); private static SVC osxBorderSize = new SVC("--container-window-buttons-right-margin-osx"); private static SVC winBorderMargin = new SVC("--container-window-button-left-right-margin-win"); private static SVC osxBorderMargin = new SVC("--container-window-button-left-right-margin-osx"); } - - private const float kButtonCountOSX = 0; - private const float kButtonCountWin = 2; static internal float buttonHorizontalSpace => (kButtonWidth + Styles.buttonMargin * 2f); - static internal float buttonStackWidth => buttonHorizontalSpace * (macEditor || linuxEditor ? kButtonCountOSX : kButtonCountWin) + Styles.borderSize; - + static internal float buttonStackWidth => Styles.borderSize; public ContainerWindow() { m_PixelRect = new Rect(0, 0, 400, 300); @@ -148,21 +134,20 @@ 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, int displayIndex = 0) + public void Show(ShowMode showMode, bool loadPosition, bool displayImmediately, bool setFocus) { 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 || showMode == ShowMode.Fullscreen; + bool useMousePos = showMode == ShowMode.AuxWindow; 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; @@ -176,7 +161,7 @@ public void Show(ShowMode showMode, bool loadPosition, bool displayImmediately, var initialMaximizedState = m_Maximized; - Internal_Show(m_PixelRect, m_ShowMode, m_MinSize, m_MaxSize, displayIndex); + Internal_Show(m_PixelRect, m_ShowMode, m_MinSize, m_MaxSize); // 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) @@ -371,11 +356,13 @@ internal void InternalCloseWindow() } DestroyImmediate(this, true); + EditorWindow.UpdateWindowMenuListing(); } - internal bool InternalIsForceTitleBar() + [RequiredByNativeCode] + internal bool IsMultiplayerClone() { - return m_IsForceTitleBar; + return m_IsMppmCloneWindow; } private static List FindUnsavedChanges(View view) @@ -657,142 +644,5 @@ internal Rect GetDropDownRect(Rect buttonRect, Vector2 minSize, Vector2 maxSize) return PopupLocationHelper.GetDropDownRect(buttonRect, minSize, maxSize, this); } - public void HandleWindowDecorationEnd(Rect windowPosition) - { - // No Op - } - - public void HandleWindowDecorationStart(Rect windowPosition) - { - if (!macEditor && !linuxEditor) - { - bool hasTitleBar = (windowPosition.y == 0 && (showMode != ShowMode.Utility && showMode != ShowMode.MainWindow) && !isPopup); - - if (!hasTitleBar) - return; - - bool hasWindowButtons = Mathf.Abs(windowPosition.xMax - position.width) < 2; - if (hasWindowButtons) - { - GUIStyle min = Styles.buttonMin; - GUIStyle close = Styles.buttonClose; - GUIStyle maxOrRestore = maximized ? Styles.buttonRestore : Styles.buttonMax; - - BeginTitleBarButtons(windowPosition); - if (TitleBarButton(close)) - { - if (InternalRequestClose()) - { - Close(); - GUIUtility.ExitGUI(); - } - } - - var canMaximize = m_MaxSize.x == 0 || m_MaxSize.y == 0 || m_MaxSize.x >= Screen.currentResolution.width || m_MaxSize.y >= Screen.currentResolution.height; - EditorGUI.BeginDisabled(!canMaximize); - if (TitleBarButton(maxOrRestore)) - ToggleMaximize(); - EditorGUI.EndDisabled(); - } - - DragTitleBar(new Rect(0, 0, position.width, kTitleHeight)); - } - } - - private void BeginTitleBarButtons(Rect windowPosition) - { - m_ButtonCount = 0; - m_TitleBarWidth = windowPosition.width; - } - - private bool TitleBarButton(GUIStyle style) - { - var buttonRect = new Rect(m_TitleBarWidth - Styles.borderSize - (buttonHorizontalSpace * ++m_ButtonCount), Styles.buttonTop, kButtonWidth, kButtonHeight); - var guiView = rootView as GUIView; - if (guiView == null) - { - var splitView = rootView as SplitView; - if (splitView != null) - guiView = splitView.children.Length > 0 ? splitView.children[0] as GUIView : null; - } - if (guiView != null) - guiView.MarkHotRegion(GUIClip.UnclipToWindow(buttonRect)); - - return GUI.Button(buttonRect, GUIContent.none, style); - } - - // Snapping windows - private static Vector2 s_LastDragMousePos; - private float startDragDpi; - - // Indicates that we are using the native title bar caption dragging. - private bool m_DraggingNativeTitleBarCaption = false; - - private void DragTitleBar(Rect titleBarRect) - { - int id = GUIUtility.GetControlID(FocusType.Passive); - Event evt = Event.current; - - switch (evt.GetTypeForControl(id)) - { - case EventType.Repaint: - if (m_DraggingNativeTitleBarCaption) - m_DraggingNativeTitleBarCaption = false; - EditorGUIUtility.AddCursorRect(titleBarRect, MouseCursor.Arrow); - break; - case EventType.MouseDown: - // If the mouse is inside the title bar rect, we say that we're the hot control - if (titleBarRect.Contains(evt.mousePosition) && GUIUtility.hotControl == 0 && evt.button == 0) - { - Event.current.Use(); - m_DraggingNativeTitleBarCaption = true; - SendCaptionEvent(m_DraggingNativeTitleBarCaption); - } - break; - case EventType.MouseUp: - if (m_DraggingNativeTitleBarCaption) - break; - - if (GUIUtility.hotControl == id) - { - GUIUtility.hotControl = 0; - Event.current.Use(); - Unsupported.SetAllowCursorLock(true, Unsupported.DisallowCursorLockReasons.SizeMove); - } - break; - case EventType.MouseDrag: - if (m_DraggingNativeTitleBarCaption) - break; - - if (GUIUtility.hotControl == id) - { - Vector2 mousePos = evt.mousePosition; - if (startDragDpi != GUIUtility.pixelsPerPoint) - { - // We ignore this mouse event when changing screens in multi monitor setups with - // different dpi scalings as funky things might/will happen - startDragDpi = GUIUtility.pixelsPerPoint; - s_LastDragMousePos = mousePos; - } - else - { - Vector2 movement = mousePos - s_LastDragMousePos; - - float minimumDelta = 1.0f / GUIUtility.pixelsPerPoint; - - if (Mathf.Abs(movement.x) >= minimumDelta || Mathf.Abs(movement.y) >= minimumDelta) - { - Rect dragPosition = position; - dragPosition.x += movement.x; - dragPosition.y += movement.y; - position = dragPosition; - - GUI.changed = true; - } - } - } - break; - } - } } } //namespace diff --git a/Editor/Mono/CustomEditorAttributes.bindings.cs b/Editor/Mono/CustomEditorAttributes.bindings.cs index 7dfb49fbf6..7387e02d03 100644 --- a/Editor/Mono/CustomEditorAttributes.bindings.cs +++ b/Editor/Mono/CustomEditorAttributes.bindings.cs @@ -10,9 +10,9 @@ namespace UnityEditor; internal partial class CustomEditorAttributes { [RequiredByNativeCode] - internal static Type FindCustomEditorType(Object o, bool multiEdit) + internal static Type FindCustomEditorType(Object obj, bool multiEdit) { - return FindCustomEditorTypeByType(o.GetType(), multiEdit); + return obj == null ? null : FindCustomEditorTypeByType(obj.GetType(), multiEdit); } [RequiredByNativeCode] diff --git a/Editor/Mono/CustomEditorAttributes.cs b/Editor/Mono/CustomEditorAttributes.cs index b866edb95d..73e46af47e 100644 --- a/Editor/Mono/CustomEditorAttributes.cs +++ b/Editor/Mono/CustomEditorAttributes.cs @@ -212,11 +212,13 @@ static bool TryGatherRenderPipelineTypes([DisallowNull] Type type, [DisallowNull } } +#pragma warning disable CS0618 if (inspectAttr is CustomEditorForRenderPipelineAttribute attr) { results = new[] { attr.renderPipelineType }; return true; } +#pragma warning restore CS0618 results = null; return true; diff --git a/Editor/Mono/CustomInspectorStubs.cs b/Editor/Mono/CustomInspectorStubs.cs index 0793e52640..0a54fe5bed 100644 --- a/Editor/Mono/CustomInspectorStubs.cs +++ b/Editor/Mono/CustomInspectorStubs.cs @@ -68,10 +68,18 @@ private InputManager() {} [SettingsProvider] internal static SettingsProvider CreateProjectSettingsProvider() { - var provider = AssetSettingsProvider.CreateProviderFromAssetPath( - "Project/Input Manager", "ProjectSettings/InputManager.asset", - SettingsProvider.GetSearchKeywordsFromPath("ProjectSettings/InputManager.asset")); - return provider; + // The new input system adds objects to InputManager.asset. This means we can't use AssetSettingsProvider.CreateProviderFromAssetPath + // as it will load *all* objects at that path and try to create an editor for it. + // NOTE: When the input system package is uninstalled, InputManager.asset will contain serialized MonoBehaviour objects for which + // the C# classes are no longer available. They will thus not load correctly and appear as null entries. + var obj = AssetDatabase.LoadAssetAtPath("ProjectSettings/InputManager.asset"); + if (obj != null && obj.name == "InputManager") + { + var provider = AssetSettingsProvider.CreateProviderFromObject("Project/Input Manager", obj, + SettingsProvider.GetSearchKeywordsFromPath("ProjectSettings/InputManager.asset")); + return provider; + } + return null; } } diff --git a/Editor/Mono/DataMode.cs b/Editor/Mono/DataMode.cs index fb934d692c..4ce5f777c4 100644 --- a/Editor/Mono/DataMode.cs +++ b/Editor/Mono/DataMode.cs @@ -4,15 +4,15 @@ using System; using System.Collections.Generic; - +using System.Linq; +using UnityEngine; using UnityObject = UnityEngine.Object; using DataModeSupportHandler = UnityEditor.DeclareDataModeSupportAttribute.DataModeSupportHandler; namespace UnityEditor { /// - /// Options for the different modes of an that implements - /// or . + /// Options for the different modes of an . /// // // Dev note: @@ -26,150 +26,260 @@ namespace UnityEditor // Sincerely, // The #dots-editor team // + [Serializable] public enum DataMode // Values must be kept in sync with `DataMode.h` { /// - /// Represents a situation or context in which the usage of data modes is not applicable. + /// Represents a situation or context in which the usage of is not applicable. /// /// - /// This mode informs the docking area that the data modes switch should now be displayed. + /// This mode disables the DataMode switch in the docking area. /// Disabled = 0, /// - /// Uses a mode where only authoring data is available. + /// Uses this mode where only authoring data is available. /// /// /// In this mode, only authoring data is available. When exiting Play mode, Unity retains authoring data. /// Authoring = 1, /// - /// Uses a mode where a mix of authoring and runtime data is available. + /// Uses this mode where a mix of authoring and runtime data is available. /// /// - /// In this mode, a mixture of authoring and runtime data is available. **Important:** When exiting Play mode, - /// Unity loses runtime data. However, it retains any authoring data. + /// In this mode, a mixture of authoring and runtime data is available. + /// When exiting Play mode, Unity loses runtime data. However, it retains any authoring data. /// Mixed = 2, /// - /// Uses a mode where only runtime data is available. + /// Uses this mode where only runtime data is available. /// /// - /// In this mode, only runtime data is available. **Important:** When exiting Play mode, Unity loses runtime - /// data. + /// In this mode, only runtime data is available. When exiting Play mode, Unity loses runtime data. /// Runtime = 3 } /// - /// Implement this interface to allow an to handle changes. + /// Container for the different parameters of the event. + /// + /// DataMode to which the should change. + /// Whether the change was initiated by the DataMode switcher UI + /// at the top-right of the Editor window. + public readonly struct DataModeChangeEventArgs + { + public readonly DataMode nextDataMode; + public readonly bool changedThroughUI; + + public DataModeChangeEventArgs(DataMode nextDataMode, bool changedThroughUI) + { + this.nextDataMode = nextDataMode; + this.changedThroughUI = changedThroughUI; + } + } + + /// + /// Interface with which any can interact with functionalities. + /// To obtain an instance, use > /// /// - /// This interface displays a switch in the docking area when the window is visible and lists the supported modes in - /// the contextual menu for that window. Use this interface if your window only needs to react to direct user - /// interactions with the data mode switch or the contextual menu. If your window needs to change its state based on - /// other factors, like entering or exiting play mode, you should implement - /// instead. + /// This interface displays a switch in the docking area when the window is visible and has + /// more than one supported DataModes. /// - public interface IDataModeHandler + public interface IDataModeController { /// - /// Returns the currently active for the implementor . + /// Returns the currently active for the that + /// owns this instance of IDataModeController. + /// + DataMode dataMode { get; } + + /// + /// Event for subscribing to changes. /// /// - /// Unity does not serialize or store this value. It is the window's responsibility to do so. + /// This method accepts >. + /// For example, you can register to this method to update the contents of the window for the given data mode. /// - DataMode dataMode { get; } + event Action dataModeChanged; /// - /// - /// A list of the s the supports. - /// - /// - /// That list of the s the supports varies based - /// on a number of factors, so it should only contain the modes available to the current context. + /// Updates the list of s that the supports, + /// and sets the preferred DataMode to be used when the DataMode switcher UI is set to Automatic. + /// + /// + /// That list of the DataModes the Editor window supports varies based on a number of factors, + /// so it should only contain the DataModes available to the current context. /// For example, a window might support the and /// modes when in Edit mode, and the and modes when /// in Play mode. A common pattern for that case is to store two lists internally and use /// to select which one to return. - /// - /// - IReadOnlyList supportedDataModes { get; } - - /// - /// Unity calls this method automatically before any call to is made. If the - /// method returns false, is called instead. - /// - /// - /// The for which support is being tested. + /// A list of the supported DataModes. + /// + /// Preferred DataMode to use given the current context when the DataMode switcher UI is set to Automatic. /// - /// - /// Whether the currently supports the specified . - /// - bool IsDataModeSupported(DataMode mode); - - /// - /// Unity calls this method automatically when a user clicks the switch in the docking - /// area tied to the implementing . - /// - /// - /// This method informs the window to change its to whatever mode should come after - /// the current. In most cases, a window only supports two data modes at a time, but it is possible to support - /// all three. Also, a window that supports all three modes might want the switch to only toggle between two - /// specific modes and rely on the contextual menu to change to the third mode. /// - void SwitchToNextDataMode(); + void UpdateSupportedDataModes(IList supportedDataMode, DataMode preferredDataMode); /// - /// Unity calls this method automatically whenever the Editor wants an to be in a - /// specific . + /// Requests a change for the . /// /// - /// By convention, Unity always calls before calling this method. If the - /// data mode is not supported, is called instead. + /// If the DataMode switcher UI is currently set to Automatic, the Editor window also + /// changes to that preferred DataMode. + /// > /// - /// - /// The explicit data mode to which the Editor window should change. + /// + /// The DataMode to which the Editor window should change. /// - void SwitchToDataMode(DataMode mode); + /// + /// Whether the Editor window has accepted the requested DataMode change. + /// > + bool TryChangeDataMode(DataMode newDataMode); + } - /// - /// Unity calls this method automatically whenever going to a requested is impossible - /// because of the result of . - /// - /// - /// This method is a fallback to make sure the is always in a valid state. - /// - /// + // DataModeController handles DataMode related actions internally. + // Each Editor window has a DataModeController instance. + [Serializable] + internal sealed class DataModeController : IDataModeController + { + static readonly DataMode[] k_DefaultModes = Array.Empty(); + + public event Action dataModeChanged; + + [SerializeField] DataMode m_DataMode = DataMode.Disabled; + public DataMode dataMode + { + get => m_DataMode; + private set => m_DataMode = value; + } + + [SerializeField] DataMode m_PreferredDataMode = DataMode.Disabled; + public DataMode preferredDataMode + { + get => m_PreferredDataMode; + private set => m_PreferredDataMode = value; + } + + [SerializeField] DataMode[] m_SupportedDataModes = k_DefaultModes; + public IList supportedDataModes + { + get => m_SupportedDataModes; + private set => m_SupportedDataModes = value.ToArray(); + } + + [SerializeField] internal bool isAutomatic = true; + + readonly List m_DataModeSanitizationCache = new List(3); // Number of modes, minus `Disabled` + + public void UpdateSupportedDataModes(IList supported, DataMode preferred) + { + SanitizeSupportedDataModesList(supported.ToList(), m_DataModeSanitizationCache); + + supportedDataModes = m_DataModeSanitizationCache.Count != 0 ? m_DataModeSanitizationCache : k_DefaultModes; + + preferredDataMode = supportedDataModes.Count switch + { + 0 => DataMode.Disabled, + 1 => supportedDataModes[0], + _ => supportedDataModes.Contains(preferred) ? preferred : supportedDataModes[0] + }; + + if (!isAutomatic || dataMode == preferredDataMode) + return; + + // Recover if automatic + dataMode = preferredDataMode; + dataModeChanged?.Invoke(new DataModeChangeEventArgs(dataMode, false)); + } + + static void SanitizeSupportedDataModesList(IReadOnlyList originalList, List sanitizedList) + { + sanitizedList.Clear(); + + foreach (var mode in originalList) + { + if (mode == DataMode.Disabled) + continue; // Never list `DataMode.Disabled` + + if (sanitizedList.Contains(mode)) + continue; // Prevent duplicate entries + + sanitizedList.Add(mode); + } + + // Ensure we are displaying the data modes in a predefined order, regardless of + // the order in which the user defined their list. + sanitizedList.Sort(); + } + + public bool ShouldDrawDataModesSwitch() + { + return dataMode != DataMode.Disabled + // We don't want to show DataMode switch if there are not + // at least 2 modes supported at the current moment. + && supportedDataModes.Count > 1; + } + + public bool TryChangeDataMode(DataMode newDataMode) + { + // Only change if currently in automatic mode + if (!isAutomatic || dataMode == newDataMode || !supportedDataModes.Contains(newDataMode)) + return false; + + dataMode = newDataMode; + dataModeChanged?.Invoke(new DataModeChangeEventArgs(newDataMode, false)); + return true; + } + + // Invoked when user interacts with the DataMode dropdown menu, for internal use only. + internal void SwitchToAutomatic() + { + if (isAutomatic) + return; + + isAutomatic = true; + + if (dataMode == preferredDataMode) + return; + + // If the DataMode is not supported in current context, we fall back to default one. + dataMode = preferredDataMode; + dataModeChanged?.Invoke(new DataModeChangeEventArgs(dataMode, true)); + } + + // Invoked when user interacts with the DataMode dropdown men, for internal use only. + internal void SwitchToStickyDataMode(DataMode stickyDataMode) + { + isAutomatic = false; + + if (dataMode == stickyDataMode) + return; + + dataMode = supportedDataModes.Contains(stickyDataMode) + ? stickyDataMode + : preferredDataMode; + + dataModeChanged?.Invoke(new DataModeChangeEventArgs(dataMode, true)); + } + } + + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + [Obsolete("IDataModeHandler has been deprecated, please use EditorWindow.dataModeController instead.", false)] + public interface IDataModeHandler + { + DataMode dataMode { get; } + IReadOnlyList supportedDataModes { get; } + bool IsDataModeSupported(DataMode mode); + void SwitchToNextDataMode(); + void SwitchToDataMode(DataMode mode); void SwitchToDefaultDataMode(); } - /// - /// Implement this interface to allow an to handle changes and - /// alter its internally. - /// - /// - /// This interface displays a switch in the docking area when the window is visible and lists the supported modes in - /// the contextual menu for that window. Use this interface if your window needs to control its mode internally - /// based on factors other than the user directly interacting with the data mode switch or the contextual menu, for - /// example, entering or exiting Play mode. If your window does not need to control its own mode, use - /// instead. - /// + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + [Obsolete("IDataModeHandlerAndDispatcher has been deprecated, please use EditorWindow.dataModeController instead.", false)] public interface IDataModeHandlerAndDispatcher : IDataModeHandler { - /// - /// - /// Calls the methods in its invocation list when the changes due to an external factor - /// and passes the new data mode as an argument. - /// - /// - /// An external factor refers to any action which results in a data mode change that Unity did not initiate - /// directly through calling either , - /// , or . - /// - /// - /// For example, when entering or exiting Play mode, some windows might want to force a data mode switch. - /// - /// event Action dataModeChanged; } diff --git a/Editor/Mono/Delayer.cs b/Editor/Mono/Delayer.cs index df2fd4910d..342d242cd4 100644 --- a/Editor/Mono/Delayer.cs +++ b/Editor/Mono/Delayer.cs @@ -10,7 +10,7 @@ class Delayer { private long m_LastExecutionTime; private Action m_Action; - private readonly double m_DebounceDelay; + private readonly long m_DebounceDelay; private object m_Context; private readonly bool m_IsThrottle; private readonly bool m_FirstExecuteImmediate; @@ -63,7 +63,7 @@ public void Execute(object context = null) private Delayer(Action action, double delay, bool isThrottle, bool firstExecuteImmediate) { m_Action = action; - m_DebounceDelay = delay; + m_DebounceDelay = TimeSpan.FromSeconds(delay).Ticks; m_IsThrottle = isThrottle; m_FirstExecuteImmediate = firstExecuteImmediate; } @@ -74,41 +74,44 @@ public void Dispose() EditorApplication.tick -= Throttle; m_Context = null; m_Action = null; + m_DelayInProgress = false; } private void Debounce() { - m_DelayInProgress = false; - EditorApplication.tick -= Debounce; var currentTime = DateTime.UtcNow.Ticks; if (m_LastExecutionTime != 0 && DelayHasPassed(currentTime)) { + m_DelayInProgress = false; + EditorApplication.tick -= Debounce; m_Action?.Invoke(m_Context); m_LastExecutionTime = 0; } else { - EditorApplication.tick += Debounce; + if (!m_DelayInProgress) + EditorApplication.tick += Debounce; m_DelayInProgress = true; } } private void Throttle() { - m_DelayInProgress = false; - EditorApplication.tick -= Throttle; var currentTime = DateTime.UtcNow.Ticks; if (m_FirstExecuteImmediate) { if (m_LastExecutionTime == 0 || DelayHasPassed(currentTime)) { + m_DelayInProgress = false; + EditorApplication.tick -= Throttle; m_Action?.Invoke(m_Context); m_LastExecutionTime = currentTime; } else { - EditorApplication.tick += Throttle; + if (!m_DelayInProgress) + EditorApplication.tick += Throttle; m_DelayInProgress = true; } } @@ -116,6 +119,8 @@ private void Throttle() { if (m_LastExecutionTime != 0 && DelayHasPassed(currentTime)) { + m_DelayInProgress = false; + EditorApplication.tick -= Throttle; m_Action?.Invoke(m_Context); m_LastExecutionTime = 0; } @@ -123,7 +128,8 @@ private void Throttle() { if (m_LastExecutionTime == 0) m_LastExecutionTime = currentTime; - EditorApplication.tick += Throttle; + if (!m_DelayInProgress) + EditorApplication.tick += Throttle; m_DelayInProgress = true; } } @@ -132,7 +138,7 @@ private void Throttle() private bool DelayHasPassed(long currentTime) { var timeSpan = new TimeSpan(currentTime - m_LastExecutionTime); - return timeSpan.TotalSeconds >= m_DebounceDelay; + return timeSpan.Ticks >= m_DebounceDelay; } } } diff --git a/Editor/Mono/Display/EditorDisplayFullscreenSetting.cs b/Editor/Mono/Display/EditorDisplayFullscreenSetting.cs deleted file mode 100644 index 040b762cbc..0000000000 --- a/Editor/Mono/Display/EditorDisplayFullscreenSetting.cs +++ /dev/null @@ -1,42 +0,0 @@ -// 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 deleted file mode 100644 index 36064520ae..0000000000 --- a/Editor/Mono/Display/EditorDisplayManager.cs +++ /dev/null @@ -1,267 +0,0 @@ -// 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, Mathf.RoundToInt(view.targetSize.x), Mathf.RoundToInt(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, RefreshRate 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 deleted file mode 100644 index 3cb40c15c5..0000000000 --- a/Editor/Mono/Display/EditorDisplaySettingsProfile.cs +++ /dev/null @@ -1,79 +0,0 @@ -// 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 deleted file mode 100644 index 5119008563..0000000000 --- a/Editor/Mono/Display/EditorDisplayUtility.bindings.cs +++ /dev/null @@ -1,41 +0,0 @@ -// 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 deleted file mode 100644 index 07e6aa8612..0000000000 --- a/Editor/Mono/Display/EditorFullscreenController.cs +++ /dev/null @@ -1,1237 +0,0 @@ -// 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 bool m_buildTargetDataInitialized; - - 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(); - m_buildTargetDataInitialized = false; - - 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() - { - if (m_buildTargetDataInitialized) - return; - - 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; - } - } - - m_buildTargetDataInitialized = true; - } - - 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.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(); - - playModeView.m_Parent.SetAsStartView(); - playModeView.m_Parent.SetAsLastPlayModeView(); - 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); - } - - // Refocus main window after ending fullscreen. - var mainWindow = WindowLayout.FindMainWindow(); - if (mainWindow != null && mainWindow.rootView != null) { - var hostView = mainWindow.rootView as HostView; - if (hostView != null) - hostView.Focus(); - } - } - - 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; - playModeView.SetPlayModeView(true); - 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; - playModeView.SetPlayModeView(!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; - playModeView.SetPlayModeView(!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"; - private static float sLastToggleShortcutTriggerTime = 0f; - private static float kToggleShortcutTriggerTimeoutInSeconds = 1f; - - 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; - - // OSX will animate windows moving to fullscreen and toggling fullscreen again during this animation will - // cause a slew of bugs. Rather than lock this shortcut until the op is completed, instead provide a reasonable - // timeout for triggering this shortcut again. - if (sLastToggleShortcutTriggerTime + kToggleShortcutTriggerTimeoutInSeconds < Time.realtimeSinceStartup) - { - sLastToggleShortcutTriggerTime = Time.realtimeSinceStartup; - 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; - } - - instance.GetInstalledBuildTargetData(); - - 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 deleted file mode 100644 index 0672466a95..0000000000 --- a/Editor/Mono/Display/FullscreenSettingsForAttribute.cs +++ /dev/null @@ -1,21 +0,0 @@ -// 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 deleted file mode 100644 index 5998df7044..0000000000 --- a/Editor/Mono/Display/GameViewFullscreenSettings.cs +++ /dev/null @@ -1,81 +0,0 @@ -// 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 deleted file mode 100644 index bd7c04cd5b..0000000000 --- a/Editor/Mono/Display/IPlayModeViewFullscreenSettings.cs +++ /dev/null @@ -1,21 +0,0 @@ -// 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/EditorApplication.cs b/Editor/Mono/EditorApplication.cs index a7592073b0..d58fb26130 100644 --- a/Editor/Mono/EditorApplication.cs +++ b/Editor/Mono/EditorApplication.cs @@ -244,6 +244,8 @@ public static event Action projectChanged internal static CallbackFunction assetBundleNameChanged; + internal static CallbackFunction fileMenuSaved; + // Delegate for changed keyboard modifier keys. public static CallbackFunction modifierKeysChanged; @@ -270,7 +272,8 @@ public static event Action playModeStateChanged // Returns true when the pressed keys are defined in the Trigger internal static Func doPressedKeysTriggerAnyShortcut; - internal static event Action focusChanged; + public static event Action focusChanged; + public static bool isFocused { get; private set; } // Windows were reordered internal static CallbackFunction windowsReordered; @@ -342,7 +345,7 @@ internal static ApplicationTitleDescriptor GetApplicationTitleDescriptor() var desc = new ApplicationTitleDescriptor( isTemporaryProject ? PlayerSettings.productName : Path.GetFileName(Path.GetDirectoryName(Application.dataPath)), - InternalEditorUtility.GetUnityDisplayVersion(), + (Unsupported.IsSourceBuild() || Unsupported.IsDeveloperMode()) ? InternalEditorUtility.GetUnityDisplayVersion() : Application.unityVersion, activeSceneName, BuildPipeline.GetBuildTargetGroupDisplayName(BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget)), Coverage.enabled @@ -389,6 +392,15 @@ internal static void RequestRepaintAllViews() view.Repaint(); } + internal static void RequestRepaintAllTexts() + { + foreach (GUIView view in Resources.FindObjectsOfTypeAll(typeof(GUIView))) + { + view.Repaint(); + view.RepaintUITKText(); + } + } + static void Internal_CallHierarchyHasChanged() { #pragma warning disable 618 @@ -445,6 +457,12 @@ static void Internal_PlayModeStateChanged(PlayModeStateChange state) evt(state); } + [RequiredByNativeCode] + internal static void Internal_FileMenuSaved() + { + fileMenuSaved?.Invoke(); + } + static void Internal_CallKeyboardModifiersChanged() { modifierKeysChanged?.Invoke(); @@ -475,9 +493,10 @@ static void Internal_CallGlobalEventHandler() } [RequiredByNativeCode] - static void Internal_FocusChanged(bool isFocused) + static void Internal_FocusChanged(bool hasFocus) { - focusChanged?.Invoke(isFocused); + isFocused = hasFocus; + focusChanged?.Invoke(hasFocus); } [MenuItem("File/New Scene %n", priority = 150)] diff --git a/Editor/Mono/EditorAssemblies.cs b/Editor/Mono/EditorAssemblies.cs index 3eae35f854..a8c2d5fae7 100644 --- a/Editor/Mono/EditorAssemblies.cs +++ b/Editor/Mono/EditorAssemblies.cs @@ -115,7 +115,6 @@ private static void ProcessInitializeOnLoadAttributes(Type[] types) foreach (Type type in sortedTypes) { - using (new EditorPerformanceMarker($"InitializeOnLoad {type.Name}", type).Auto()) using (_profilerMarkerProcessInitializeOnLoadAttributes.Auto(reportTimes, () => type.AssemblyQualifiedName)) { try @@ -136,7 +135,6 @@ private static void ProcessInitializeOnLoadMethodAttributes() bool reportTimes = (bool)Debug.GetDiagnosticSwitch("EnableDomainReloadTimings").value; foreach (var method in TypeCache.GetMethodsWithAttribute()) { - using (new EditorPerformanceMarker($"InitializeOnLoad {method.DeclaringType?.Name}.{method.Name}", method.DeclaringType).Auto()) using (_profilerMarkerProcessInitializeOnLoadMethodAttributes.Auto(reportTimes, () => $"{method.DeclaringType?.FullName}::{method.Name}")) { try diff --git a/Editor/Mono/EditorBuildSettings.bindings.cs b/Editor/Mono/EditorBuildSettings.bindings.cs index 82f99a4035..9993a1e82b 100644 --- a/Editor/Mono/EditorBuildSettings.bindings.cs +++ b/Editor/Mono/EditorBuildSettings.bindings.cs @@ -45,7 +45,7 @@ public EditorBuildSettingsScene(GUID guid, bool enabled) public GUID guid { get { return m_guid; } set { m_guid = value; } } public static string[] GetActiveSceneList(EditorBuildSettingsScene[] scenes) { - return scenes.Where(scene => scene.enabled).Select(scene => scene.path).ToArray(); + return scenes.Where(scene => scene.enabled && !string.IsNullOrEmpty(scene.path)).Select(scene => scene.path).ToArray(); } public int CompareTo(object obj) @@ -74,27 +74,10 @@ private static void SceneListChanged() public static EditorBuildSettingsScene[] scenes { - get - { - var result = GetEditorBuildSettingsScenes(); - foreach (var scene in result) - { - if (scene.guid.Empty()) - { - scene.guid = new GUID(AssetDatabase.AssetPathToGUID(scene.path)); - } - else - { - scene.path = AssetDatabase.GUIDToAssetPath(scene.guid.ToString()); - } - } - return result; - } - set - { - SetEditorBuildSettingsScenes(value); - } + get => GetEditorBuildSettingsScenes(); + set => SetEditorBuildSettingsScenes(value); } + static extern EditorBuildSettingsScene[] GetEditorBuildSettingsScenes(); static extern void SetEditorBuildSettingsScenes(EditorBuildSettingsScene[] scenes); diff --git a/Editor/Mono/EditorGUI.cs b/Editor/Mono/EditorGUI.cs index 9aa30d07a8..efeb8774c5 100644 --- a/Editor/Mono/EditorGUI.cs +++ b/Editor/Mono/EditorGUI.cs @@ -15,7 +15,6 @@ using UnityEditor.Scripting.ScriptCompilation; using Object = UnityEngine.Object; using Event = UnityEngine.Event; -using UnityEditor.Build; using UnityEditor.StyleSheets; using UnityEditor.VersionControl; using UnityEngine.Internal; @@ -25,6 +24,7 @@ using System.Reflection; using Unity.Profiling; using UnityEngine.Experimental.Rendering; +using UnityEngine.UIElements; namespace UnityEditor { @@ -155,7 +155,6 @@ private enum DragCandidateState private static readonly GUIContent s_PositionLabel = EditorGUIUtility.TrTextContent("Position"); private static readonly GUIContent s_SizeLabel = EditorGUIUtility.TrTextContent("Size"); internal static GUIContent s_PleasePressAKey = EditorGUIUtility.TrTextContent("[Please press a key]"); - private static string s_PrefabInContextPreviewValuesTooltip = L10n.Tr("This property is previewing the overridden value on the Prefab instance.\n\nTo edit this property, open this Prefab Asset in isolation by pressing the modifier key [Alt] while you open it."); internal static readonly GUIContent s_ClipingPlanesLabel = EditorGUIUtility.TrTextContent("Clipping Planes", "The distances from the Camera where rendering starts and stops."); internal static readonly GUIContent[] s_NearAndFarLabels = { EditorGUIUtility.TrTextContent("Near", "The closest point to the Camera where drawing occurs."), EditorGUIUtility.TrTextContent("Far", "The furthest point from the Camera that drawing occurs.") }; @@ -208,6 +207,9 @@ private enum DragCandidateState internal static SavedBool s_ShowRepaintDots = new SavedBool("ShowRepaintDots", true); + internal static readonly Regex s_ATagRegex = new Regex(@"(?<=\b="")[^""]*"); + internal static readonly Regex s_LinkTagRegex = new Regex(@"(?<=\b=')[^']*"); + static class Styles { public static Texture2D prefabOverlayAddedIcon = EditorGUIUtility.LoadIcon("PrefabOverlayAdded Icon"); @@ -520,7 +522,7 @@ internal bool IsEditingControl(int id) return GUIUtility.keyboardControl == id && controlID == id && s_ActuallyEditing && GUIView.current.hasFocus; } - public virtual void BeginEditing(int id, string newText, Rect _position, GUIStyle _style, bool _multiline, bool passwordField) + public virtual void BeginEditing(int id, string newText, Rect position, GUIStyle style, bool multiline, bool passwordField) { if (IsEditingControl(id)) { @@ -532,9 +534,9 @@ public virtual void BeginEditing(int id, string newText, Rect _position, GUIStyl activeEditor = this; controlID = id; text = s_OriginalText = newText; - multiline = _multiline; - style = _style; - position = _position; + isMultiline = multiline; + this.position = position; + this.style = style; isPasswordField = passwordField; s_ActuallyEditing = true; scrollOffset = Vector2.zero; @@ -982,12 +984,18 @@ internal static string DoTextField(RecycledTextEditor editor, int id, Rect posit // If the editor is already set up, we just need to sync position, etc... if (editor.IsEditingControl(id)) { + // Fast path flag to ensure that we only update the scroll offset if the text area grew (dynamic height) + bool requireUpdateScrollOffset = editor.position.height != position.height; editor.position = position; editor.style = style; editor.controlID = id; - editor.multiline = multiline; + editor.isMultiline = multiline; editor.isPasswordField = passwordField; editor.DetectFocusChange(); + editor.UpdateTextHandle(); + + if (requireUpdateScrollOffset) + editor.UpdateScrollOffset(); } else if (EditorGUIUtility.editingTextField || (evt.GetTypeForControl(id) == EventType.ExecuteCommand && evt.commandName == EventCommandNames.NewKeyboardFocus)) { @@ -1119,7 +1127,12 @@ internal static string DoTextField(RecycledTextEditor editor, int id, Rect posit { // Extract hyperlink info Dictionary hyperLinkData; - if (HasClickedOnHyperlink(text, editor.cursorIndex, editor, out hyperLinkData)) // Check if the cursor is between hyperlink tags and store the hyperlink info (tag arguments in a dictionary) + + if (editor.HasClickedOnHREF(Event.current.mousePosition, out string href)) + { + Application.OpenURL(href); + } + else if (HasClickedOnHyperlink(editor, out hyperLinkData)) // Check if the cursor is between hyperlink tags and store the hyperlink info (tag arguments in a dictionary) { // Raise event with the info var window = GUIView.current is HostView hostView ? hostView.actualView : null; @@ -1240,7 +1253,7 @@ internal static string DoTextField(RecycledTextEditor editor, int id, Rect posit { if (editor.IsEditingControl(id)) { - if (style == EditorStyles.toolbarSearchField || style == EditorStyles.searchField) + if (style == EditorStyles.toolbarSearchField || style == EditorStyles.searchField || style.name.Contains("SearchText")) { s_OriginalText = ""; } @@ -1358,7 +1371,6 @@ internal static string DoTextField(RecycledTextEditor editor, int id, Rect posit drawText = passwordField ? "".PadRight(text.Length, '*') : text; } - if (!string.IsNullOrEmpty(s_UnitString) && !passwordField) drawText += " " + s_UnitString; @@ -1392,6 +1404,10 @@ internal static string DoTextField(RecycledTextEditor editor, int id, Rect posit EditorGUIUtility.AddCursorRect(cursorRect, MouseCursor.Text); } + break; + case EventType.ScrollWheel: + // Scroll offset might need to be updated + editor.UpdateScrollOffset(); break; } @@ -1401,9 +1417,6 @@ internal static string DoTextField(RecycledTextEditor editor, int id, Rect posit GUIUtility.textFieldInput = EditorGUIUtility.editingTextField; } - // Scroll offset might need to be updated - editor.UpdateScrollOffsetIfNeeded(evt); - changed = false; if (mayHaveChanged) { @@ -1441,58 +1454,33 @@ internal static string DoTextField(RecycledTextEditor editor, int id, Rect posit return origText; } - private static bool HasClickedOnHyperlink(string text, int cursorIndex, RecycledTextEditor editor, out Dictionary hyperLinkData) + private static bool HasClickedOnHyperlink(RecycledTextEditor editor, out Dictionary hyperLinkData) { - Vector2 mousePosition = Event.current.mousePosition; hyperLinkData = new Dictionary(); - if (cursorIndex > 0) - { - bool hitHyperlink = false; - foreach (var rect in editor.GetHyperlinksRect()) - { - if (rect.Contains(mousePosition)) - { - hitHyperlink = true; - break; - } - } - - if (hitHyperlink) - { - int indexBeginTag = text.Substring(0, editor.cursorIndex) - .LastIndexOf("= 0) - { - int indexBeginTagClose = - text.Substring(indexBeginTag, cursorIndex - indexBeginTag).IndexOf('>'); + Vector2 mousePosition = Event.current.mousePosition; + if (!editor.HasClickedOnLink(mousePosition, out string link)) + return false; - string beginTag = text.Substring(indexBeginTag, indexBeginTagClose); - // Regex to find the attribute value - Regex regex = new Regex(@"(?<=\b="")[^""]*"); - MatchCollection matches = regex.Matches(beginTag); + MatchCollection matches = s_ATagRegex.Matches(link); + if (matches.Count == 0) + matches = s_LinkTagRegex.Matches(link); - int endPreviousAttributeIndex = 0; - // for each attribute we need to find the attribute name - foreach (Match match in matches) - { - // We are only working on the text between the previous attribute and the current - string namePart = beginTag.Substring(endPreviousAttributeIndex, - (match.Index - 2) - endPreviousAttributeIndex); // -2 is the character before =" - int indexName = namePart.LastIndexOf(' ') + 1; - string name = namePart.Substring(indexName); - // Add the name of the attribute and its value in the dictionary - hyperLinkData.Add(name, match.Value); - - endPreviousAttributeIndex = match.Index + match.Value.Length + 1; - } + int endPreviousAttributeIndex = 0; + // for each attribute we need to find the attribute name + foreach (Match match in matches) + { + // We are only working on the text between the previous attribute and the current + string namePart = link.Substring(endPreviousAttributeIndex, + (match.Index - 2) - endPreviousAttributeIndex); // -2 is the character before =" + int indexName = namePart.LastIndexOf(' ') + 1; + string name = namePart.Substring(indexName); + // Add the name of the attribute and its value in the dictionary + hyperLinkData.Add(name, match.Value); - return true; - } - } + endPreviousAttributeIndex = match.Index + match.Value.Length + 1; } - return false; + + return true; } public static event Action hyperLinkClicked; @@ -2054,7 +2042,7 @@ internal static string ScrollableTextAreaInternal(Rect position, string text, re else { //Move the Editor offset to match our scrollbar - s_RecycledEditor.scrollOffset.y = scrollPosition.y; + s_RecycledEditor.scrollOffset = scrollPosition; } } bool dummy; @@ -2433,7 +2421,7 @@ internal static void GetInitialValue(ref NumberFieldValue value) if (value.isDouble) { double oldValue = default; - if (UINumericFieldsUtils.StringToDouble(s_OriginalText, out oldValue) && oldValue != value.doubleVal) + if (UINumericFieldsUtils.TryConvertStringToDouble(s_OriginalText, out oldValue) && oldValue != value.doubleVal) { value.doubleVal = oldValue; return; @@ -2442,7 +2430,7 @@ internal static void GetInitialValue(ref NumberFieldValue value) else { long oldValue = default; - if (UINumericFieldsUtils.StringToLong(s_OriginalText, out oldValue) && oldValue != value.longVal) + if (UINumericFieldsUtils.TryConvertStringToLong(s_OriginalText, out oldValue) && oldValue != value.longVal) { value.longVal = oldValue; return; @@ -2520,7 +2508,7 @@ internal static bool StringToDouble(string str, out double value) static void StringToDouble(string str, ref NumberFieldValue value) { - value.success = UINumericFieldsUtils.StringToDouble(str, out value.doubleVal, out value.expression); + value.success = UINumericFieldsUtils.TryConvertStringToDouble(str, out value.doubleVal, out value.expression); } internal static bool StringToLong(string str, out long value) @@ -2534,7 +2522,7 @@ internal static bool StringToLong(string str, out long value) static void StringToLong(string str, ref NumberFieldValue value) { value.expression = null; - value.success = UINumericFieldsUtils.StringToLong(str, out value.longVal, out value.expression); + value.success = UINumericFieldsUtils.TryConvertStringToLong(str, out value.longVal, out value.expression); } internal static int ArraySizeField(Rect position, GUIContent label, int value, GUIStyle style) @@ -2915,7 +2903,7 @@ internal static float Slider(Rect position, GUIContent label, float value, float var id = GUIUtility.GetControlID(s_SliderHash, FocusType.Keyboard, position); var controlRect = PrefixLabel(position, id, label); var dragZone = LabelHasContent(label) ? EditorGUIUtility.DragZoneRect(position) : new Rect(); // Ensure dragzone is empty when we have no label - return DoSlider(controlRect, dragZone, id, value, sliderMin, sliderMax, kFloatFieldFormatString, textFieldMin, textFieldMax, 1f, + return DoSlider(controlRect, dragZone, id, value, sliderMin, sliderMax, kFloatFieldFormatString, textFieldMin, textFieldMax, 1f, 1f, textfieldStyle, sliderStyle, thumbStyle, sliderBackground, thumbStyleExtent); } @@ -2939,13 +2927,31 @@ internal static float PowerSlider(Rect position, GUIContent label, float sliderV return DoSlider(controlRect, dragZone, id, sliderValue, leftValue, rightValue, kFloatFieldFormatString, textfieldStyle, power); } + internal static int LogarithmicIntSlider(Rect position, string label, int sliderValue, int leftValue, int rightValue, int logbase, int textFieldMin, int textFieldMax) + { + return LogarithmicIntSlider(position, EditorGUIUtility.TempContent(label), sliderValue, leftValue, rightValue, logbase, textFieldMin, textFieldMax); + } + + internal static int LogarithmicIntSlider(Rect position, GUIContent label, int sliderValue, int leftValue, int rightValue, int logbase, int textFieldMin, int textFieldMax) + { + return LogarithmicIntSlider(position, label, sliderValue, leftValue, rightValue, EditorStyles.numberField, logbase, textFieldMin, textFieldMax); + } + + internal static int LogarithmicIntSlider(Rect position, GUIContent label, int sliderValue, int leftValue, int rightValue, GUIStyle textfieldStyle, int logbase, int textFieldMin, int textFieldMax) + { + int id = GUIUtility.GetControlID(s_SliderHash, FocusType.Keyboard, position); + Rect controlRect = PrefixLabel(position, id, label); + Rect dragZone = LabelHasContent(label) ? EditorGUIUtility.DragZoneRect(position) : new Rect(); // Ensure dragzone is empty when we have no label + return Mathf.RoundToInt(DoSlider(controlRect, dragZone, id, (float) sliderValue, (float) leftValue, (float) rightValue, kIntFieldFormatString, power:1, logbase: logbase, textFieldMin: textFieldMin, textFieldMax: textFieldMax)); + } + private static float PowPreserveSign(float f, float p) { var result = Mathf.Pow(Mathf.Abs(f), p); return f < 0.0f ? -result : result; } - internal static GenericMenu FillPropertyContextMenu(SerializedProperty property, SerializedProperty linkedProperty = null, GenericMenu menu = null) + internal static GenericMenu FillPropertyContextMenu(SerializedProperty property, SerializedProperty linkedProperty = null, GenericMenu menu = null, VisualElement element = null) { if (property == null) return null; @@ -3054,7 +3060,9 @@ internal static GenericMenu FillPropertyContextMenu(SerializedProperty property, info.properties = properties; info.assetPath = AssetDatabase.GetAssetPath(sourceObject); GameObject rootObject = PrefabUtility.GetRootGameObject(sourceObject); - if (!PrefabUtility.IsPartOfPrefabThatCanBeAppliedTo(rootObject) || !PrefabUtility.CanPropertyBeAppliedToTarget(property, rootObject)) + if (EditorUtility.IsPersistent(targetObject) + || !PrefabUtility.IsPartOfPrefabThatCanBeAppliedTo(rootObject) + || !PrefabUtility.CanPropertyBeAppliedToTarget(property, rootObject)) pm.AddDisabledItem(menuItemContent); else pm.AddItem(menuItemContent, false, TargetChoiceHandler.ApplyPrefabPropertyOverride, info); @@ -3120,6 +3128,7 @@ internal static GenericMenu FillPropertyContextMenu(SerializedProperty property, pm.AddItem(EditorGUIUtility.TrTextContent("Duplicate Array Element"), false, (a) => { var list = ReorderableList.GetReorderableListFromSerializedProperty(parentArrayProperty); + var listView = element?.GetFirstAncestorOfType(); // If we have a ReorderableList associated with this property lets use list selection array // and apply this action to all selected elements thus having better integration with @@ -3147,6 +3156,10 @@ internal static GenericMenu FillPropertyContextMenu(SerializedProperty property, ReorderableList.InvalidateExistingListCaches(); } + else if (listView != null && listView.selectedIndices.Any()) + { + DuplicateListViewItems(listView, parentArrayProperty); + } else // Non reorderable { if (parentArrayIndex >= 0 && parentArrayIndex < parentArrayProperty.arraySize) @@ -3173,6 +3186,7 @@ internal static GenericMenu FillPropertyContextMenu(SerializedProperty property, pm.AddItem(EditorGUIUtility.TrTextContent("Delete Array Element"), false, (a) => { var list = ReorderableList.GetReorderableListFromSerializedProperty(parentArrayProperty); + var listView = element?.GetFirstAncestorOfType(); // If we have a ReorderableList associated with this property lets use list selection array // and apply this action to all selected elements thus having better integration with @@ -3196,6 +3210,10 @@ internal static GenericMenu FillPropertyContextMenu(SerializedProperty property, list.onChangedCallback(list); ReorderableList.InvalidateExistingListCaches(); } + else if (listView != null && listView.selectedIndices.Any()) + { + DeleteListViewItems(listView, parentArrayProperty); + } else // Non reorderable { if (parentArrayIndex >= 0 && parentArrayIndex < parentArrayProperty.arraySize) @@ -3269,6 +3287,60 @@ internal static GenericMenu FillPropertyContextMenu(SerializedProperty property, return pm; } + internal static void DeleteListViewItems(ListView listView, SerializedProperty parentArrayProperty) + { + var previousSelectedIndices = new List(listView.selectedIndices); + previousSelectedIndices.Sort(); + listView.ClearSelection(); + + for (int i = previousSelectedIndices.Count - 1; i >= 0; i--) + { + var index = previousSelectedIndices.ElementAt(i); + if (index >= listView.itemsSource.Count) continue; + + SerializedProperty resolvedProperty = parentArrayProperty.GetArrayElementAtIndex(index); + if (resolvedProperty != null) + { + if (!TargetChoiceHandler.DeleteArrayElement(resolvedProperty)) continue; + } + } + } + + internal static void DuplicateListViewItems(ListView listView, SerializedProperty parentArrayProperty) + { + var previousSelectedIndices = new List(listView.selectedIndices); + previousSelectedIndices.Sort(); + var newSelectedIndices = new List(); + listView.ClearSelection(); + + for (int i = previousSelectedIndices.Count - 1; i >= 0; i--) + { + var index = previousSelectedIndices.ElementAt(i); + if (index >= listView.itemsSource.Count) continue; + + SerializedProperty resolvedProperty = parentArrayProperty.GetArrayElementAtIndex(index); + if (resolvedProperty != null) + { + if (!TargetChoiceHandler.DuplicateArrayElement(resolvedProperty)) continue; + } + + // need to update the rest of the selected indices as an element was added + for (int j = i + 1; j < previousSelectedIndices.Count; j++) + { + previousSelectedIndices[j]++; + } + + for (int j = 0; j < newSelectedIndices.Count; j++) + { + newSelectedIndices[j]++; + } + + newSelectedIndices.Add(previousSelectedIndices[i] + 1); + } + + listView.SetSelection(newSelectedIndices); + } + internal static void GoToPrefab(string assetPath, GameObject openedFromInstance) { // When this function is called from a Property Context Menu on the Overrides pop-up window, we need to make ensure that the correct GameObject @@ -3333,11 +3405,11 @@ internal static void Slider(Rect position, SerializedProperty property, float sl EndProperty(); } - internal static int IntSlider(Rect position, int value, int leftValue, int rightValue, float power = -1, + internal static int IntSlider(Rect position, int value, int leftValue, int rightValue, float power = -1, float logbase = 1, GUIStyle textfieldStyle = null, GUIStyle sliderStyle = null, GUIStyle thumbStyle = null, Texture2D sliderBackground = null, GUIStyle thumbStyleExtent = null) { int id = GUIUtility.GetControlID(s_SliderHash, FocusType.Keyboard, position); - return Mathf.RoundToInt(DoSlider(IndentedRect(position), EditorGUIUtility.DragZoneRect(position), id, value, leftValue, rightValue, kIntFieldFormatString, power, + return Mathf.RoundToInt(DoSlider(IndentedRect(position), EditorGUIUtility.DragZoneRect(position), id, value, leftValue, rightValue, kIntFieldFormatString, power, logbase, textfieldStyle ?? EditorStyles.numberField, sliderStyle ?? GUI.skin.horizontalSlider, thumbStyle ?? GUI.skin.horizontalSliderThumb, @@ -3406,24 +3478,29 @@ internal static void DoTwoLabels(Rect rect, GUIContent leftLabel, GUIContent rig labelStyle.alignment = oldAlignment; } - internal static float DoSlider(Rect position, Rect dragZonePosition, int id, float value, float left, float right, string formatString, float power = 1f) + internal static float DoSlider(Rect position, Rect dragZonePosition, int id, float value, float left, float right, string formatString, float power = 1f, float logbase = 1f) + { + return DoSlider(position, dragZonePosition, id, value, left, right, formatString, power, logbase, EditorStyles.numberField, GUI.skin.horizontalSlider, GUI.skin.horizontalSliderThumb, null, GUI.skin.horizontalSliderThumbExtent); + } + + internal static float DoSlider(Rect position, Rect dragZonePosition, int id, float value, float left, float right, string formatString, float textFieldMin, float textFieldMax, float power = 1f, float logbase = 1f) { - return DoSlider(position, dragZonePosition, id, value, left, right, formatString, power, EditorStyles.numberField, GUI.skin.horizontalSlider, GUI.skin.horizontalSliderThumb, null, GUI.skin.horizontalSliderThumbExtent); + return DoSlider(position, dragZonePosition, id, value, left, right, formatString, textFieldMin, textFieldMax, power, logbase, EditorStyles.numberField, GUI.skin.horizontalSlider, GUI.skin.horizontalSliderThumb, null, GUI.skin.horizontalSliderThumbExtent); } - internal static float DoSlider(Rect position, Rect dragZonePosition, int id, float value, float left, float right, string formatString, GUIStyle textfieldStyle, float power = 1f) + internal static float DoSlider(Rect position, Rect dragZonePosition, int id, float value, float left, float right, string formatString, GUIStyle textfieldStyle, float power = 1f, float logbase = 1f) { - return DoSlider(position, dragZonePosition, id, value, left, right, formatString, power, textfieldStyle, GUI.skin.horizontalSlider, GUI.skin.horizontalSliderThumb, null, GUI.skin.horizontalSliderThumbExtent); + return DoSlider(position, dragZonePosition, id, value, left, right, formatString, power, logbase, textfieldStyle, GUI.skin.horizontalSlider, GUI.skin.horizontalSliderThumb, null, GUI.skin.horizontalSliderThumbExtent); } - private static float DoSlider(Rect position, Rect dragZonePosition, int id, float value, float left, float right, string formatString, float power, GUIStyle textfieldStyle, GUIStyle sliderStyle, GUIStyle thumbStyle, Texture2D sliderBackground, GUIStyle thumbStyleExtent) + private static float DoSlider(Rect position, Rect dragZonePosition, int id, float value, float left, float right, string formatString, float power, float logbase, GUIStyle textfieldStyle, GUIStyle sliderStyle, GUIStyle thumbStyle, Texture2D sliderBackground, GUIStyle thumbStyleExtent) { - return DoSlider(position, dragZonePosition, id, value, left, right, formatString, left, right, power, textfieldStyle, sliderStyle, thumbStyle, sliderBackground, thumbStyleExtent); + return DoSlider(position, dragZonePosition, id, value, left, right, formatString, left, right, power, logbase, textfieldStyle, sliderStyle, thumbStyle, sliderBackground, thumbStyleExtent); } private static float DoSlider( Rect position, Rect dragZonePosition, int id, float value, float sliderMin, float sliderMax, string formatString, float textFieldMin - , float textFieldMax, float power, GUIStyle textfieldStyle, GUIStyle sliderStyle, GUIStyle thumbStyle, Texture2D sliderBackground, GUIStyle thumbStyleExtent + , float textFieldMax, float power, float logbase, GUIStyle textfieldStyle, GUIStyle sliderStyle, GUIStyle thumbStyle, Texture2D sliderBackground, GUIStyle thumbStyleExtent ) { int sliderId = GUIUtility.GetControlID(s_SliderKnobHash, FocusType.Passive, position); @@ -3454,6 +3531,12 @@ private static float DoSlider( remapRight = PowPreserveSign(sliderMax, 1f / power); newSliderValue = PowPreserveSign(value, 1f / power); } + else if (logbase != 1f) + { + remapLeft = Mathf.Log(sliderMin, logbase); + remapRight = Mathf.Log(sliderMax, logbase); + newSliderValue = Mathf.Log(value, logbase); + } Rect sliderRect = new Rect(position.x, position.y, sWidth, position.height); @@ -3469,6 +3552,11 @@ private static float DoSlider( newSliderValue = PowPreserveSign(newSliderValue, power); newSliderValue = Mathf.Clamp(newSliderValue, Mathf.Min(sliderMin, sliderMax), Mathf.Max(sliderMin, sliderMax)); } + else if (logbase != 1f) + { + newSliderValue = Mathf.Pow(logbase, newSliderValue); + } + // Do slider labels if present if (EditorGUIUtility.sliderLabels.HasLabels()) @@ -3867,7 +3955,7 @@ public static int GetSelectedValueForControl(int controlID, int selected) } if (instance.m_ControlID == controlID) { - GUI.changed = selected != instance.m_SelectedIndex; + GUI.changed = showMixedValue || selected != instance.m_SelectedIndex; selected = instance.m_SelectedIndex; instance = null; evt.Use(); @@ -3886,15 +3974,8 @@ internal void SetEnumValueDelegate(object userData, string[] options, int select } } - internal static int DoPopup(Rect position, int controlID, int selected, GUIContent[] popupValues, GUIStyle style) - { - return DoPopup(position, controlID, selected, popupValues, null, style); - } - - internal static int DoPopup(Rect position, int controlID, int selected, GUIContent[] popupValues, Func checkEnabled, GUIStyle style) + internal static GUIContent GetPopupButtonContent(int selected, GUIContent[] popupValues) { - selected = PopupCallbackInfo.GetSelectedValueForControl(controlID, selected); - GUIContent buttonContent; if (showMixedValue) { @@ -3907,8 +3988,21 @@ internal static int DoPopup(Rect position, int controlID, int selected, GUIConte else { buttonContent = popupValues[selected]; + buttonContent.text = EditorUtility.ParseMenuName(popupValues[selected].text); } + return buttonContent; + } + + internal static int DoPopup(Rect position, int controlID, int selected, GUIContent[] popupValues, GUIStyle style) + { + return DoPopup(position, controlID, selected, popupValues, null, style); + } + + internal static int DoPopup(Rect position, int controlID, int selected, GUIContent[] popupValues, Func checkEnabled, GUIStyle style) + { + selected = PopupCallbackInfo.GetSelectedValueForControl(controlID, selected); + Event evt = Event.current; switch (evt.type) { @@ -3921,7 +4015,7 @@ internal static int DoPopup(Rect position, int controlID, int selected, GUIConte } BeginHandleMixedValueContentColor(); - style.Draw(position, buttonContent, controlID, false, position.Contains(Event.current.mousePosition)); + style.Draw(position, GetPopupButtonContent(selected, popupValues), controlID, false, position.Contains(Event.current.mousePosition)); EndHandleMixedValueContentColor(); style.font = originalFont; @@ -4176,9 +4270,7 @@ public static Enum EnumFlagsField(Rect position, GUIContent label, Enum enumValu public static Enum EnumFlagsField(Rect position, GUIContent label, Enum enumValue, [DefaultValue("false")] bool includeObsolete, [DefaultValue("null")] GUIStyle style = null) { - int changedFlags; - bool changedToValue; - return EnumFlagsField(position, label, enumValue, includeObsolete, out changedFlags, out changedToValue, style ?? EditorStyles.popup); + return EnumFlagsField(position, label, enumValue, includeObsolete, out _, out _, style ?? EditorStyles.popup); } // Internal version that also gives you back which flags were changed and what they were changed to. @@ -4572,11 +4664,19 @@ internal static Vector3 LinkedVector3Field(Rect position, GUIContent label, GUI GUIContent copy = label; Rect fullLabelRect = position; - if(proportionalScaleProperty != null) + BeginChangeCheck(); + + if (proportionalScaleProperty != null) + { BeginPropertyInternal(fullLabelRect, label, proportionalScaleProperty); + } + var scalePropertyId = -1; if (property != null) + { label = BeginPropertyInternal(position, label, property); + scalePropertyId = GUIUtility.keyboardControl; + } SerializedProperty copiedProperty = property == null ? property : property.Copy(); var toggle = EditorStyles.toggle.CalcSize(GUIContent.none); @@ -4605,13 +4705,26 @@ internal static Vector3 LinkedVector3Field(Rect position, GUIContent label, GUI position.width -= toggle.x + kDefaultSpacing; position.height = kSingleLineHeight; - var newValue = LinkedVector3Field(position, value, proportionalScale, mixedValues, initialScale, ref axisModified, copiedProperty); + if (proportionalScaleProperty != null) + { + EndProperty(); + } if (property != null) - EndProperty(); + { + // Note: due to how both the scale + constrainScale property drawn and handled in a custom fashion, the lastcontrolId never correspond + // to the scaleProperty. Also s_PendingPropertyKeyboardHandling is nullifed by the constrainScale property. + // Make it work for now but I feel this whole system is super brittle. + // This will be hopefully fixed up when we use uitk to create these editors. - if(proportionalScaleProperty != null) + var lastId = EditorGUIUtility.s_LastControlID; + EditorGUIUtility.s_LastControlID = scalePropertyId; + s_PendingPropertyKeyboardHandling = property; EndProperty(); + EditorGUIUtility.s_LastControlID = lastId; + } + + var newValue = LinkedVector3Field(position, value, proportionalScale, mixedValues, initialScale, ref axisModified, copiedProperty); return newValue; } @@ -4630,8 +4743,8 @@ static Vector3 LinkedVector3Field(Rect position, Vector3 value, bool proportiona s_Vector3Floats[1] = value.y; s_Vector3Floats[2] = value.z; position.height = kSingleLineHeight; - BeginChangeCheck(); - LockingMultiFloatFieldInternal(position, proportionalScale, mixedValues, s_XYZLabels, s_Vector3Floats, new float[] {initialScale.x, initialScale.y, initialScale.z}, property, EditorGUI.CalcPrefixLabelWidth(s_XYZLabels[0]) + 3); + const float kPrefixWidthOffset = 3.65f; + LockingMultiFloatFieldInternal(position, proportionalScale, mixedValues, s_XYZLabels, s_Vector3Floats, new float[] {initialScale.x, initialScale.y, initialScale.z}, property, EditorGUI.CalcPrefixLabelWidth(s_XYZLabels[0]) + kPrefixWidthOffset); if (EndChangeCheck()) { valueAfterChangeCheck.x = s_Vector3Floats[0]; @@ -6043,7 +6156,7 @@ internal static void DoInspectorTitlebar(Rect position, int id, bool foldout, Ob } break; case EventType.Repaint: - textStyle.Draw(textRect, EditorGUIUtility.TempContent(ObjectNames.GetInspectorTitle(targetObjs[0])), hovered, pressed, foldout, hasFocus); + textStyle.Draw(textRect, EditorGUIUtility.TempContent(ObjectNames.GetInspectorTitle(targetObjs[0], targetObjs.Length > 1)), hovered, pressed, foldout, hasFocus); if (EditorGUIUtility.comparisonViewMode == EditorGUIUtility.ComparisonViewMode.None) { EditorStyles.optionsButtonStyle.Draw(settingsRect, GUIContent.none, id, foldout, settingsRect.Contains(Event.current.mousePosition)); @@ -6277,7 +6390,7 @@ internal static bool HelpIconButton(Rect position, Object[] objs) } else { - content.tooltip = string.Format("Open Reference for {0}.", helpTopic); + content.tooltip = string.Format("Open Reference for {0}.", ObjectNames.NicifyVariableName(helpTopic)); } } @@ -6818,28 +6931,11 @@ internal static GUIContent BeginPropertyInternal(Rect totalPosition, GUIContent animatedColor.a *= GUI.backgroundColor.a; GUI.backgroundColor = animatedColor; } - else + else if (PrefabUtility.IsPropertyBeingDrivenByPrefabStage(property)) { - Object target = property.serializedObject.targetObject; - GameObject go = PrefabUtility.GetGameObject(target); - if (go != null && go.scene.IsValid() && EditorSceneManager.IsPreviewScene(go.scene)) - { - var prefabStage = SceneManagement.PrefabStageUtility.GetCurrentPrefabStage(); - if (prefabStage != null && prefabStage.mode == SceneManagement.PrefabStage.Mode.InContext) - { - var propertyPath = property.propertyPath; - ScriptableObject driver = prefabStage; - if ( - (DrivenPropertyManagerInternal.IsDriving(driver, target, propertyPath)) - || - ((target is Transform || target is ParticleSystem || property.propertyType == SerializedPropertyType.Color) && DrivenPropertyManagerInternal.IsDrivingPartial(driver, target, propertyPath))) - { - GUI.enabled = false; - if (isCollectingTooltips) - s_PropertyFieldTempContent.tooltip = s_PrefabInContextPreviewValuesTooltip; - } - } - } + GUI.enabled = false; + if (isCollectingTooltips) + s_PropertyFieldTempContent.tooltip = PrefabStage.s_PrefabInContextPreviewValuesTooltip; } GUI.enabled &= property.editable; @@ -6988,6 +7084,8 @@ private static void DoPropertyFieldKeyboardHandling(SerializedProperty property) // Copy & Paste if (evt.commandName == EventCommandNames.Copy || evt.commandName == EventCommandNames.Paste) { + if (evt.commandName == EventCommandNames.Paste) + GUI.changed = true; ClipboardContextMenu.SetupPropertyCopyPaste(property, menu: null, evt: evt); } } @@ -9048,2517 +9146,4 @@ internal static bool IsObjectPartOfTargetAssemblies(Object obj) } } } - - // Auto-layouted version of [[EditorGUI]] - sealed partial class EditorGUILayout - { - // @TODO: Make private (and rename to not claim it's a constant). Shouldn't really be used outside of EditorGUI. - // Places that use this directly should likely use GetControlRect instead. - internal static float kLabelFloatMinW => EditorGUIUtility.labelWidth + EditorGUIUtility.fieldWidth + EditorGUI.kSpacing; - - internal static float kLabelFloatMaxW => EditorGUIUtility.labelWidth + EditorGUIUtility.fieldWidth + EditorGUI.kSpacing; - - internal static Rect s_LastRect; - - internal const float kPlatformTabWidth = 30; - - internal static SavedBool s_SelectedDefault = new SavedBool("Platform.ShownDefaultTab", true); - - static GUIStyle s_TabOnlyOne; - static GUIStyle s_TabFirst; - static GUIStyle s_TabMiddle; - static GUIStyle s_TabLast; - - [ExcludeFromDocs] - public static bool Foldout(bool foldout, string content) - { - return Foldout(foldout, content, EditorStyles.foldout); - } - - public static bool Foldout(bool foldout, string content, [DefaultValue("EditorStyles.foldout")] GUIStyle style) - { - return Foldout(foldout, EditorGUIUtility.TempContent(content), false, style); - } - - [ExcludeFromDocs] - public static bool Foldout(bool foldout, GUIContent content) - { - return Foldout(foldout, content, EditorStyles.foldout); - } - - public static bool Foldout(bool foldout, GUIContent content, [DefaultValue("EditorStyles.foldout")] GUIStyle style) - { - return Foldout(foldout, content, false, style); - } - - [ExcludeFromDocs] - public static bool Foldout(bool foldout, string content, bool toggleOnLabelClick) - { - return Foldout(foldout, content, toggleOnLabelClick, EditorStyles.foldout); - } - - public static bool Foldout(bool foldout, string content, bool toggleOnLabelClick, [DefaultValue("EditorStyles.foldout")] GUIStyle style) - { - return Foldout(foldout, EditorGUIUtility.TempContent(content), toggleOnLabelClick, style); - } - - [ExcludeFromDocs] - public static bool Foldout(bool foldout, GUIContent content, bool toggleOnLabelClick) - { - return Foldout(foldout, content, toggleOnLabelClick, EditorStyles.foldout); - } - - public static bool Foldout(bool foldout, GUIContent content, bool toggleOnLabelClick, [DefaultValue("EditorStyles.foldout")] GUIStyle style) - { - return FoldoutInternal(foldout, content, toggleOnLabelClick, style); - } - - [ExcludeFromDocs] - public static void PrefixLabel(string label) - { - GUIStyle followingStyle = "Button"; - PrefixLabel(label, followingStyle); - } - - public static void PrefixLabel(string label, [DefaultValue("\"Button\"")] GUIStyle followingStyle) - { - PrefixLabel(EditorGUIUtility.TempContent(label), followingStyle, EditorStyles.label); - } - - public static void PrefixLabel(string label, GUIStyle followingStyle, GUIStyle labelStyle) - { - PrefixLabel(EditorGUIUtility.TempContent(label), followingStyle, labelStyle); - } - - [ExcludeFromDocs] - public static void PrefixLabel(GUIContent label) - { - GUIStyle followingStyle = "Button"; - PrefixLabel(label, followingStyle); - } - - public static void PrefixLabel(GUIContent label, [DefaultValue("\"Button\"")] GUIStyle followingStyle) - { - PrefixLabel(label, followingStyle, EditorStyles.label); - } - - // Make a label in front of some control. - public static void PrefixLabel(GUIContent label, GUIStyle followingStyle, GUIStyle labelStyle) - { - PrefixLabelInternal(label, followingStyle, labelStyle); - } - - public static void LabelField(string label, params GUILayoutOption[] options) - { - LabelField(GUIContent.none, EditorGUIUtility.TempContent(label), EditorStyles.label, options); - } - - public static void LabelField(string label, GUIStyle style, params GUILayoutOption[] options) - { - LabelField(GUIContent.none, EditorGUIUtility.TempContent(label), style, options); - } - - public static void LabelField(GUIContent label, params GUILayoutOption[] options) - { - LabelField(GUIContent.none, label, EditorStyles.label, options); - } - - public static void LabelField(GUIContent label, GUIStyle style, params GUILayoutOption[] options) - { - LabelField(GUIContent.none, label, style, options); - } - - public static void LabelField(string label, string label2, params GUILayoutOption[] options) - { - LabelField(new GUIContent(label), EditorGUIUtility.TempContent(label2), EditorStyles.label, options); - } - - public static void LabelField(string label, string label2, GUIStyle style, params GUILayoutOption[] options) - { - LabelField(new GUIContent(label), EditorGUIUtility.TempContent(label2), style, options); - } - - public static void LabelField(GUIContent label, GUIContent label2, params GUILayoutOption[] options) - { - LabelField(label, label2, EditorStyles.label, options); - } - - // Make a label field. (Useful for showing read-only info.) - public static void LabelField(GUIContent label, GUIContent label2, GUIStyle style, params GUILayoutOption[] options) - { - if (!style.wordWrap) - { - // If we don't need word wrapping, just allocate the standard space to avoid corner case layout issues - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, options); - EditorGUI.LabelField(r, label, label2, style); - } - else - { - BeginHorizontal(); - PrefixLabel(label, style); - Rect r = GUILayoutUtility.GetRect(label2, style, options); - int oldIndent = EditorGUI.indentLevel; - EditorGUI.indentLevel = 0; - EditorGUI.LabelField(r, label2, style); - EditorGUI.indentLevel = oldIndent; - EndHorizontal(); - } - } - - public static bool LinkButton(string label, params GUILayoutOption[] options) - { - return LinkButton(EditorGUIUtility.TempContent(label), options); - } - - public static bool LinkButton(GUIContent label, params GUILayoutOption[] options) - { - var position = s_LastRect = GUILayoutUtility.GetRect(label, EditorStyles.linkLabel, options); - - Handles.color = EditorStyles.linkLabel.normal.textColor; - Handles.DrawLine(new Vector3(position.xMin + EditorStyles.linkLabel.padding.left, position.yMax), new Vector3(position.xMax - EditorStyles.linkLabel.padding.right, position.yMax)); - Handles.color = Color.white; - - EditorGUIUtility.AddCursorRect(position, MouseCursor.Link); - - return GUI.Button(position, label, EditorStyles.linkLabel); - } - - public static bool Toggle(bool value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetToggleRect(false, options); - return EditorGUI.Toggle(r, value); - } - - public static bool Toggle(string label, bool value, params GUILayoutOption[] options) - { - return Toggle(EditorGUIUtility.TempContent(label), value, options); - } - - public static bool Toggle(GUIContent label, bool value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetToggleRect(true, options); - return EditorGUI.Toggle(r, label, value); - } - - public static bool Toggle(bool value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetToggleRect(false, options); - return EditorGUI.Toggle(r, value, style); - } - - public static bool Toggle(string label, bool value, GUIStyle style, params GUILayoutOption[] options) - { - return Toggle(EditorGUIUtility.TempContent(label), value, style, options); - } - - // Make a toggle. - public static bool Toggle(GUIContent label, bool value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetToggleRect(true, options); - return EditorGUI.Toggle(r, label, value, style); - } - - public static bool ToggleLeft(string label, bool value, params GUILayoutOption[] options) - { - return ToggleLeft(EditorGUIUtility.TempContent(label), value, options); - } - - public static bool ToggleLeft(GUIContent label, bool value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, options); - return EditorGUI.ToggleLeft(r, label, value); - } - - public static bool ToggleLeft(string label, bool value, GUIStyle labelStyle, params GUILayoutOption[] options) - { - return ToggleLeft(EditorGUIUtility.TempContent(label), value, labelStyle, options); - } - - // Make a toggle with the label on the right. - public static bool ToggleLeft(GUIContent label, bool value, GUIStyle labelStyle, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, options); - return EditorGUI.ToggleLeft(r, label, value, labelStyle); - } - - public static string TextField(string text, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.textField, options); - return EditorGUI.TextField(r, text); - } - - public static string TextField(string text, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.TextField(r, text, style); - } - - public static string TextField(string label, string text, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.textField, options); - return EditorGUI.TextField(r, label, text); - } - - public static string TextField(string label, string text, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.TextField(r, label, text, style); - } - - public static string TextField(GUIContent label, string text, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.textField, options); - return EditorGUI.TextField(r, label, text); - } - - // Make a text field. - public static string TextField(GUIContent label, string text, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.TextField(r, label, text, style); - } - - public static string DelayedTextField(string text, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.textField, options); - return EditorGUI.DelayedTextField(r, text); - } - - public static string DelayedTextField(string text, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.DelayedTextField(r, text, style); - } - - public static string DelayedTextField(string label, string text, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.textField, options); - return EditorGUI.DelayedTextField(r, label, text); - } - - public static string DelayedTextField(string label, string text, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.DelayedTextField(r, label, text, style); - } - - public static string DelayedTextField(GUIContent label, string text, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.textField, options); - return EditorGUI.DelayedTextField(r, label, text); - } - - // Make a delayed text field. - public static string DelayedTextField(GUIContent label, string text, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.DelayedTextField(r, label, text, style); - } - - public static void DelayedTextField(SerializedProperty property, params GUILayoutOption[] options) - { - DelayedTextField(property, null, options); - } - - // Make a delayed text field. - internal static void DelayedTextField(SerializedProperty property, GUIContent label, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(EditorGUI.LabelHasContent(label), EditorGUI.kSingleLineHeight, EditorStyles.textField, options); - EditorGUI.DelayedTextFieldHelper(r, property, label, style); - } - - public static void DelayedTextField(SerializedProperty property, GUIContent label, params GUILayoutOption[] options) - { - DelayedTextField(property, label, EditorStyles.textField, options); - } - - internal static string ToolbarSearchField(string text, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GUILayoutUtility.GetRect(0, kLabelFloatMaxW * 1.5f, EditorGUI.kSingleLineHeight, EditorGUI.kSingleLineHeight, EditorStyles.toolbarSearchField, options); - int i = 0; - return EditorGUI.ToolbarSearchField(r, null, ref i, text); - } - - internal static string ToolbarSearchField(string text, string[] searchModes, ref int searchMode, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GUILayoutUtility.GetRect(0, kLabelFloatMaxW * 1.5f, EditorGUI.kSingleLineHeight, EditorGUI.kSingleLineHeight, EditorStyles.toolbarSearchField, options); - return EditorGUI.ToolbarSearchField(r, searchModes, ref searchMode, text); - } - - public static string TextArea(string text, params GUILayoutOption[] options) - { return TextArea(text, EditorStyles.textField, options); } - // Make a text area. - public static string TextArea(string text, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GUILayoutUtility.GetRect(EditorGUIUtility.TempContent(text), style, options); - return EditorGUI.TextArea(r, text, style); - } - - public static void SelectableLabel(string text, params GUILayoutOption[] options) - { - SelectableLabel(text, EditorStyles.label, options); - } - - // Make a selectable label field. (Useful for showing read-only info that can be copy-pasted.) - public static void SelectableLabel(string text, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight * 2, style, options); - EditorGUI.SelectableLabel(r, text, style); - } - - internal static Event KeyEventField(Event e, params GUILayoutOption[] options) - { - Rect r = GUILayoutUtility.GetRect(EditorGUI.s_PleasePressAKey, GUI.skin.textField, options); - return EditorGUI.KeyEventField(r, e); - } - - public static string PasswordField(string password, params GUILayoutOption[] options) - { - return PasswordField(password, EditorStyles.textField, options); - } - - public static string PasswordField(string password, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.PasswordField(r, password, style); - } - - public static string PasswordField(string label, string password, params GUILayoutOption[] options) - { - return PasswordField(EditorGUIUtility.TempContent(label), password, EditorStyles.textField, options); - } - - public static string PasswordField(string label, string password, GUIStyle style, params GUILayoutOption[] options) - { - return PasswordField(EditorGUIUtility.TempContent(label), password, style, options); - } - - public static string PasswordField(GUIContent label, string password, params GUILayoutOption[] options) - { - return PasswordField(label, password, EditorStyles.textField, options); - } - - // Make a text field where the user can enter a password. - public static string PasswordField(GUIContent label, string password, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.PasswordField(r, label, password, style); - } - - // Peak smoothing should be handled by client. Input: value and peak is normalized values (0 - 1). - internal static void VUMeterHorizontal(float value, float peak, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); - EditorGUI.VUMeter.HorizontalMeter(r, value, peak, EditorGUI.VUMeter.horizontalVUTexture, Color.grey); - } - - // Auto-smoothing of peak - internal static void VUMeterHorizontal(float value, ref EditorGUI.VUMeter.SmoothingData data, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); - EditorGUI.VUMeter.HorizontalMeter(r, value, ref data, EditorGUI.VUMeter.horizontalVUTexture, Color.grey); - } - - public static float FloatField(float value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); - return EditorGUI.FloatField(r, value); - } - - public static float FloatField(float value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.FloatField(r, value, style); - } - - public static float FloatField(string label, float value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); - return EditorGUI.FloatField(r, label, value); - } - - public static float FloatField(string label, float value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.FloatField(r, label, value, style); - } - - public static float FloatField(GUIContent label, float value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); - return EditorGUI.FloatField(r, label, value); - } - - // Make a text field for entering float values. - public static float FloatField(GUIContent label, float value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.FloatField(r, label, value, style); - } - - public static float DelayedFloatField(float value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); - return EditorGUI.DelayedFloatField(r, value); - } - - public static float DelayedFloatField(float value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.DelayedFloatField(r, value, style); - } - - public static float DelayedFloatField(string label, float value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); - return EditorGUI.DelayedFloatField(r, label, value); - } - - public static float DelayedFloatField(string label, float value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.DelayedFloatField(r, label, value, style); - } - - public static float DelayedFloatField(GUIContent label, float value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); - return EditorGUI.DelayedFloatField(r, label, value); - } - - // Make a delayed text field for entering float values. - public static float DelayedFloatField(GUIContent label, float value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.DelayedFloatField(r, label, value, style); - } - - public static void DelayedFloatField(SerializedProperty property, params GUILayoutOption[] options) - { - DelayedFloatField(property, null, options); - } - - // Make a delayed text field for entering float values. - public static void DelayedFloatField(SerializedProperty property, GUIContent label, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(EditorGUI.LabelHasContent(label), EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); - EditorGUI.DelayedFloatField(r, property, label); - } - - public static double DoubleField(double value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); - return EditorGUI.DoubleField(r, value); - } - - public static double DoubleField(double value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.DoubleField(r, value, style); - } - - public static double DoubleField(string label, double value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); - return EditorGUI.DoubleField(r, label, value); - } - - public static double DoubleField(string label, double value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.DoubleField(r, label, value, style); - } - - public static double DoubleField(GUIContent label, double value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); - return EditorGUI.DoubleField(r, label, value); - } - - // Make a text field for entering double values. - public static double DoubleField(GUIContent label, double value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.DoubleField(r, label, value, style); - } - - public static double DelayedDoubleField(double value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); - return EditorGUI.DelayedDoubleField(r, value); - } - - public static double DelayedDoubleField(double value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.DelayedDoubleField(r, value, style); - } - - public static double DelayedDoubleField(string label, double value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); - return EditorGUI.DelayedDoubleField(r, label, value); - } - - public static double DelayedDoubleField(string label, double value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.DelayedDoubleField(r, label, value, style); - } - - public static double DelayedDoubleField(GUIContent label, double value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); - return EditorGUI.DelayedDoubleField(r, label, value); - } - - // Make a delayed text field for entering double values. - public static double DelayedDoubleField(GUIContent label, double value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.DelayedDoubleField(r, label, value, style); - } - - public static int IntField(int value, params GUILayoutOption[] options) - { - return IntField(value, EditorStyles.numberField, options); - } - - public static int IntField(int value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.IntField(r, value, style); - } - - public static int IntField(string label, int value, params GUILayoutOption[] options) - { - return IntField(label, value, EditorStyles.numberField, options); - } - - public static int IntField(string label, int value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.IntField(r, label, value, style); - } - - public static int IntField(GUIContent label, int value, params GUILayoutOption[] options) - { - return IntField(label, value, EditorStyles.numberField, options); - } - - // Make a text field for entering integers. - public static int IntField(GUIContent label, int value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.IntField(r, label, value, style); - } - - public static int DelayedIntField(int value, params GUILayoutOption[] options) - { - return DelayedIntField(value, EditorStyles.numberField, options); - } - - public static int DelayedIntField(int value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.DelayedIntField(r, value, style); - } - - public static int DelayedIntField(string label, int value, params GUILayoutOption[] options) - { - return DelayedIntField(label, value, EditorStyles.numberField, options); - } - - public static int DelayedIntField(string label, int value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.DelayedIntField(r, label, value, style); - } - - public static int DelayedIntField(GUIContent label, int value, params GUILayoutOption[] options) - { - return DelayedIntField(label, value, EditorStyles.numberField, options); - } - - // Make a text field for entering integers. - public static int DelayedIntField(GUIContent label, int value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.DelayedIntField(r, label, value, style); - } - - public static void DelayedIntField(SerializedProperty property, params GUILayoutOption[] options) - { - DelayedIntField(property, null, options); - } - - // Make a text field for entering integers. - public static void DelayedIntField(SerializedProperty property, GUIContent label, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(EditorGUI.LabelHasContent(label), EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); - EditorGUI.DelayedIntField(r, property, label); - } - - public static long LongField(long value, params GUILayoutOption[] options) - { - return LongField(value, EditorStyles.numberField, options); - } - - public static long LongField(long value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.LongField(r, value, style); - } - - public static long LongField(string label, long value, params GUILayoutOption[] options) - { - return LongField(label, value, EditorStyles.numberField, options); - } - - public static long LongField(string label, long value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.LongField(r, label, value, style); - } - - public static long LongField(GUIContent label, long value, params GUILayoutOption[] options) - { - return LongField(label, value, EditorStyles.numberField, options); - } - - // Make a text field for entering integers. - public static long LongField(GUIContent label, long value, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.LongField(r, label, value, style); - } - - public static float Slider(float value, float leftValue, float rightValue, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetSliderRect(false, options); - return EditorGUI.Slider(r, value, leftValue, rightValue); - } - - public static float Slider(string label, float value, float leftValue, float rightValue, params GUILayoutOption[] options) - { - return Slider(EditorGUIUtility.TempContent(label), value, leftValue, rightValue, options); - } - - // Make a slider the user can drag to change a value between a min and a max. - public static float Slider(GUIContent label, float value, float leftValue, float rightValue, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetSliderRect(true, options); - return EditorGUI.Slider(r, label, value, leftValue, rightValue); - } - - internal static float Slider(GUIContent label, float value, float sliderLeftValue, float sliderRightValue, float textLeftValue, float textRightValue, params GUILayoutOption[] options) - { - return Slider(label, value, sliderLeftValue, sliderRightValue, textLeftValue, textRightValue, EditorStyles.numberField, - GUI.skin.horizontalSlider, GUI.skin.horizontalSliderThumb, null, GUI.skin.horizontalSliderThumbExtent, options); - } - - static void GetSliderParts(GUIStyle baseStyle, ref GUIStyle textFieldStyle, ref GUIStyle thumbStyle, ref GUIStyle thumbExtentStyle) - { - string baseName = baseStyle.name; - thumbStyle = GUI.skin.FindStyle(baseName + "Thumb") ?? thumbStyle; - thumbExtentStyle = GUI.skin.FindStyle(baseName + "ThumbExtent") ?? thumbExtentStyle; - textFieldStyle = GUI.skin.FindStyle(baseName + "TextField") ?? textFieldStyle; - } - - static void GetHorizontalSliderParts(GUIStyle baseStyle, out GUIStyle textFieldStyle, out GUIStyle thumbStyle, out GUIStyle thumbExtentStyle) - { - thumbStyle = GUI.skin.horizontalSliderThumb; - thumbExtentStyle = GUI.skin.horizontalSliderThumbExtent; - textFieldStyle = EditorStyles.numberField; - - GetSliderParts(baseStyle, ref textFieldStyle, ref thumbStyle, ref thumbExtentStyle); - } - - static void GetVerticalSliderParts(GUIStyle baseStyle, out GUIStyle textFieldStyle, out GUIStyle thumbStyle, out GUIStyle thumbExtentStyle) - { - thumbStyle = GUI.skin.verticalSliderThumb; - thumbExtentStyle = GUI.skin.verticalSliderThumbExtent; - textFieldStyle = EditorStyles.numberField; - - GetSliderParts(baseStyle, ref textFieldStyle, ref thumbStyle, ref thumbExtentStyle); - } - - internal static float Slider(GUIContent label, float value, float sliderLeftValue, float sliderRightValue, float textLeftValue, float textRightValue, GUIStyle sliderStyle, params GUILayoutOption[] options) - { - GUIStyle sliderThumbStyle, sliderThumbStyleExtent, sliderTextFieldStyle; - - GetHorizontalSliderParts(sliderStyle, out sliderTextFieldStyle, out sliderThumbStyle, out sliderThumbStyleExtent); - - return Slider(label, value, sliderLeftValue, sliderRightValue, textLeftValue, textRightValue, sliderTextFieldStyle, sliderStyle - , sliderThumbStyle, null, sliderThumbStyleExtent); - } - - internal static float Slider(GUIContent label, float value, float sliderLeftValue, float sliderRightValue, float textLeftValue, float textRightValue - , GUIStyle sliderTextField, GUIStyle sliderStyle, GUIStyle sliderThumbStyle, Texture2D sliderBackground, GUIStyle sliderThumbStyleExtent, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetSliderRect(true, sliderStyle, options); - return EditorGUI.Slider(r, label, value, sliderLeftValue, sliderRightValue, textLeftValue, textRightValue, sliderTextField, sliderStyle, sliderThumbStyle, sliderBackground, sliderThumbStyleExtent); - } - - public static void Slider(SerializedProperty property, float leftValue, float rightValue, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetSliderRect(false, options); - EditorGUI.Slider(r, property, leftValue, rightValue); - } - - public static void Slider(SerializedProperty property, float leftValue, float rightValue, string label, params GUILayoutOption[] options) - { - Slider(property, leftValue, rightValue, EditorGUIUtility.TempContent(label), options); - } - - // Make a slider the user can drag to change a value between a min and a max. - public static void Slider(SerializedProperty property, float leftValue, float rightValue, GUIContent label, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetSliderRect(true, options); - EditorGUI.Slider(r, property, leftValue, rightValue, label); - } - - internal static void Slider(SerializedProperty property, float sliderLeftValue, float sliderRightValue, float textLeftValue, float textRightValue, GUIContent label, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetSliderRect(true, options); - EditorGUI.Slider(r, property, sliderLeftValue, sliderRightValue, textLeftValue, textRightValue, label); - } - - internal static float PowerSlider(string label, float value, float leftValue, float rightValue, float power, params GUILayoutOption[] options) - { - return PowerSlider(EditorGUIUtility.TempContent(label), value, leftValue, rightValue, power, options); - } - - // Make a power slider the user can drag to change a value between a min and a max. - internal static float PowerSlider(GUIContent label, float value, float leftValue, float rightValue, float power, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetSliderRect(true, options); - return EditorGUI.PowerSlider(r, label, value, leftValue, rightValue, power); - } - - public static int IntSlider(int value, int leftValue, int rightValue, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetSliderRect(false, options); - return EditorGUI.IntSlider(r, value, leftValue, rightValue); - } - - internal static int IntSlider(int value, int leftValue, int rightValue, float power, GUIStyle sliderStyle, params GUILayoutOption[] options) - { - GUIStyle sliderThumbStyle, sliderThumbStyleExtent, sliderTextFieldStyle; - - GetHorizontalSliderParts(sliderStyle, out sliderTextFieldStyle, out sliderThumbStyle, out sliderThumbStyleExtent); - - return IntSlider(value, leftValue, rightValue, power, sliderTextFieldStyle, sliderStyle, sliderThumbStyle, null, sliderThumbStyleExtent, options); - } - - internal static int IntSlider(int value, int leftValue, int rightValue, float power, - GUIStyle textfieldStyle, GUIStyle sliderStyle, GUIStyle thumbStyle, Texture2D sliderBackground, GUIStyle thumbStyleExtent, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetSliderRect(false, sliderStyle, options); - return EditorGUI.IntSlider(r, value, leftValue, rightValue, power, textfieldStyle, sliderStyle, thumbStyle, sliderBackground, thumbStyleExtent); - } - - public static int IntSlider(string label, int value, int leftValue, int rightValue, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetSliderRect(true, options); - return EditorGUI.IntSlider(r, label, value, leftValue, rightValue); - } - - // Make a slider the user can drag to change an integer value between a min and a max. - public static int IntSlider(GUIContent label, int value, int leftValue, int rightValue, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetSliderRect(true, options); - return EditorGUI.IntSlider(r, label, value, leftValue, rightValue); - } - - public static void IntSlider(SerializedProperty property, int leftValue, int rightValue, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetSliderRect(false, options); - EditorGUI.IntSlider(r, property, leftValue, rightValue, property.displayName); - } - - public static void IntSlider(SerializedProperty property, int leftValue, int rightValue, string label, params GUILayoutOption[] options) - { - IntSlider(property, leftValue, rightValue, EditorGUIUtility.TempContent(label), options); - } - - // Make a slider the user can drag to change an integer value between a min and a max. - public static void IntSlider(SerializedProperty property, int leftValue, int rightValue, GUIContent label, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetSliderRect(true, options); - EditorGUI.IntSlider(r, property, leftValue, rightValue, label); - } - - public static void MinMaxSlider(ref float minValue, ref float maxValue, float minLimit, float maxLimit, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetSliderRect(false, options); - EditorGUI.MinMaxSlider(r, ref minValue, ref maxValue, minLimit, maxLimit); - } - - public static void MinMaxSlider(string label, ref float minValue, ref float maxValue, float minLimit, float maxLimit, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetSliderRect(true, options); - EditorGUI.MinMaxSlider(r, label, ref minValue, ref maxValue, minLimit, maxLimit); - } - - // Make a special slider the user can use to specify a range between a min and a max. - public static void MinMaxSlider(GUIContent label, ref float minValue, ref float maxValue, float minLimit, float maxLimit, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetSliderRect(true, options); - EditorGUI.MinMaxSlider(r, label, ref minValue, ref maxValue, minLimit, maxLimit); - } - - public static int Popup(int selectedIndex, string[] displayedOptions, params GUILayoutOption[] options) - { - return Popup(selectedIndex, displayedOptions, EditorStyles.popup, options); - } - - public static int Popup(int selectedIndex, string[] displayedOptions, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.Popup(r, selectedIndex, displayedOptions, style); - } - - public static int Popup(int selectedIndex, GUIContent[] displayedOptions, params GUILayoutOption[] options) - { - return Popup(selectedIndex, displayedOptions, EditorStyles.popup, options); - } - - public static int Popup(int selectedIndex, GUIContent[] displayedOptions, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.Popup(r, selectedIndex, displayedOptions, style); - } - - public static int Popup(string label, int selectedIndex, string[] displayedOptions, params GUILayoutOption[] options) - { - return Popup(label, selectedIndex, displayedOptions, EditorStyles.popup, options); - } - - public static int Popup(GUIContent label, int selectedIndex, string[] displayedOptions, params GUILayoutOption[] options) - { - return Popup(label, selectedIndex, displayedOptions, EditorStyles.popup, options); - } - - public static int Popup(string label, int selectedIndex, string[] displayedOptions, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.Popup(r, label, selectedIndex, displayedOptions, style); - } - - public static int Popup(GUIContent label, int selectedIndex, GUIContent[] displayedOptions, params GUILayoutOption[] options) - { - return Popup(label, selectedIndex, displayedOptions, EditorStyles.popup, options); - } - - // Make a generic popup selection field. - public static int Popup(GUIContent label, int selectedIndex, GUIContent[] displayedOptions, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.Popup(r, label, selectedIndex, displayedOptions, style); - } - - internal static int Popup(GUIContent label, int selectedIndex, string[] displayedOptions, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.Popup(r, label, selectedIndex, displayedOptions, style); - } - - internal static void Popup(SerializedProperty property, GUIContent[] displayedOptions, params GUILayoutOption[] options) - { - Popup(property, displayedOptions, null, options); - } - - internal static void Popup(SerializedProperty property, GUIContent[] displayedOptions, GUIContent label, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.popup, options); - EditorGUI.Popup(r, property, displayedOptions, label); - } - - public static Enum EnumPopup(Enum selected, params GUILayoutOption[] options) - { - return EnumPopup(selected, EditorStyles.popup, options); - } - - public static Enum EnumPopup(Enum selected, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.EnumPopup(r, selected, style); - } - - public static Enum EnumPopup(string label, Enum selected, params GUILayoutOption[] options) - { - return EnumPopup(label, selected, EditorStyles.popup, options); - } - - public static Enum EnumPopup(string label, Enum selected, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.EnumPopup(r, GUIContent.Temp(label), selected, null, false, style); - } - - public static Enum EnumPopup(GUIContent label, Enum selected, params GUILayoutOption[] options) - { - return EnumPopup(label, selected, EditorStyles.popup, options); - } - - // Make an enum popup selection field. - public static Enum EnumPopup(GUIContent label, Enum selected, GUIStyle style, params GUILayoutOption[] options) - { - return EnumPopup(label, selected, null, false, style, options); - } - - public static Enum EnumPopup(GUIContent label, Enum selected, Func checkEnabled, bool includeObsolete, params GUILayoutOption[] options) - { - return EnumPopup(label, selected, checkEnabled, includeObsolete, EditorStyles.popup, options); - } - - public static Enum EnumPopup(GUIContent label, Enum selected, Func checkEnabled, bool includeObsolete, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.EnumPopup(r, label, selected, checkEnabled, includeObsolete, style); - } - - public static int IntPopup(int selectedValue, string[] displayedOptions, int[] optionValues, params GUILayoutOption[] options) - { - return IntPopup(selectedValue, displayedOptions, optionValues, EditorStyles.popup, options); - } - - public static int IntPopup(int selectedValue, string[] displayedOptions, int[] optionValues, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.IntPopup(r, selectedValue, displayedOptions, optionValues, style); - } - - public static int IntPopup(int selectedValue, GUIContent[] displayedOptions, int[] optionValues, params GUILayoutOption[] options) - { - return IntPopup(selectedValue, displayedOptions, optionValues, EditorStyles.popup, options); - } - - public static int IntPopup(int selectedValue, GUIContent[] displayedOptions, int[] optionValues, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.IntPopup(r, GUIContent.none, selectedValue, displayedOptions, optionValues, style); - } - - public static int IntPopup(string label, int selectedValue, string[] displayedOptions, int[] optionValues, params GUILayoutOption[] options) - { - return IntPopup(label, selectedValue, displayedOptions, optionValues, EditorStyles.popup, options); - } - - public static int IntPopup(string label, int selectedValue, string[] displayedOptions, int[] optionValues, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.IntPopup(r, label, selectedValue, displayedOptions, optionValues, style); - } - - public static int IntPopup(GUIContent label, int selectedValue, GUIContent[] displayedOptions, int[] optionValues, params GUILayoutOption[] options) - { - return IntPopup(label, selectedValue, displayedOptions, optionValues, EditorStyles.popup, options); - } - - // Make an integer popup selection field. - public static int IntPopup(GUIContent label, int selectedValue, GUIContent[] displayedOptions, int[] optionValues, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.IntPopup(r, label, selectedValue, displayedOptions, optionValues, style); - } - - public static void IntPopup(SerializedProperty property, GUIContent[] displayedOptions, int[] optionValues, params GUILayoutOption[] options) - { - IntPopup(property, displayedOptions, optionValues, null, options); - } - - // Make an integer popup selection field. - public static void IntPopup(SerializedProperty property, GUIContent[] displayedOptions, int[] optionValues, GUIContent label, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.popup, options); - EditorGUI.IntPopup(r, property, displayedOptions, optionValues, label); - } - - [Obsolete("This function is obsolete and the style is not used.")] - public static void IntPopup(SerializedProperty property, GUIContent[] displayedOptions, int[] optionValues, GUIContent label, GUIStyle style, params GUILayoutOption[] options) - { - IntPopup(property, displayedOptions, optionValues, label, options); - } - - public static string TagField(string tag, params GUILayoutOption[] options) - { - return TagField(tag, EditorStyles.popup, options); - } - - public static string TagField(string tag, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.TagField(r, tag, style); - } - - public static string TagField(string label, string tag, params GUILayoutOption[] options) - { - return TagField(EditorGUIUtility.TempContent(label), tag, EditorStyles.popup, options); - } - - public static string TagField(string label, string tag, GUIStyle style, params GUILayoutOption[] options) - { - return TagField(EditorGUIUtility.TempContent(label), tag, style, options); - } - - public static string TagField(GUIContent label, string tag, params GUILayoutOption[] options) - { - return TagField(label, tag, EditorStyles.popup, options); - } - - // Make a tag selection field. - public static string TagField(GUIContent label, string tag, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.TagField(r, label, tag, style); - } - - public static int LayerField(int layer, params GUILayoutOption[] options) - { - return LayerField(layer, EditorStyles.popup, options); - } - - public static int LayerField(int layer, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.LayerField(r, layer, style); - } - - public static int LayerField(string label, int layer, params GUILayoutOption[] options) - { - return LayerField(EditorGUIUtility.TempContent(label), layer, EditorStyles.popup, options); - } - - public static int LayerField(string label, int layer, GUIStyle style, params GUILayoutOption[] options) - { - return LayerField(EditorGUIUtility.TempContent(label), layer, style, options); - } - - public static int LayerField(GUIContent label, int layer, params GUILayoutOption[] options) - { - return LayerField(label, layer, EditorStyles.popup, options); - } - - // Make a layer selection field. - public static int LayerField(GUIContent label, int layer, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.LayerField(r, label, layer, style); - } - - public static int MaskField(GUIContent label, int mask, string[] displayedOptions, GUIStyle style, params GUILayoutOption[] options) - { - var r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.MaskField(r, label, mask, displayedOptions, style); - } - - public static int MaskField(string label, int mask, string[] displayedOptions, GUIStyle style, params GUILayoutOption[] options) - { - var r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.MaskField(r, label, mask, displayedOptions, style); - } - - public static int MaskField(GUIContent label, int mask, string[] displayedOptions, params GUILayoutOption[] options) - { - var r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.popup, options); - return EditorGUI.MaskField(r, label, mask, displayedOptions, EditorStyles.popup); - } - - public static int MaskField(string label, int mask, string[] displayedOptions, params GUILayoutOption[] options) - { - var r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.popup, options); - return EditorGUI.MaskField(r, label, mask, displayedOptions, EditorStyles.popup); - } - - public static int MaskField(int mask, string[] displayedOptions, GUIStyle style, params GUILayoutOption[] options) - { - var r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.MaskField(r, mask, displayedOptions, style); - } - - // Make a field for masks. - public static int MaskField(int mask, string[] displayedOptions, params GUILayoutOption[] options) - { - var r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.popup, options); - return EditorGUI.MaskField(r, mask, displayedOptions, EditorStyles.popup); - } - - public static Enum EnumFlagsField(Enum enumValue, params GUILayoutOption[] options) - { - return EnumFlagsField(enumValue, EditorStyles.popup, options); - } - - public static Enum EnumFlagsField(Enum enumValue, GUIStyle style, params GUILayoutOption[] options) - { - var position = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.EnumFlagsField(position, enumValue, style); - } - - public static Enum EnumFlagsField(string label, Enum enumValue, params GUILayoutOption[] options) - { - return EnumFlagsField(label, enumValue, EditorStyles.popup, options); - } - - public static Enum EnumFlagsField(string label, Enum enumValue, GUIStyle style, params GUILayoutOption[] options) - { - return EnumFlagsField(EditorGUIUtility.TempContent(label), enumValue, style, options); - } - - public static Enum EnumFlagsField(GUIContent label, Enum enumValue, params GUILayoutOption[] options) - { - return EnumFlagsField(label, enumValue, EditorStyles.popup, options); - } - - public static Enum EnumFlagsField(GUIContent label, Enum enumValue, GUIStyle style, params GUILayoutOption[] options) - { - var position = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.EnumFlagsField(position, label, enumValue, style); - } - - public static Enum EnumFlagsField(GUIContent label, Enum enumValue, bool includeObsolete, params GUILayoutOption[] options) - { - return EnumFlagsField(label, enumValue, includeObsolete, EditorStyles.popup, options); - } - - public static Enum EnumFlagsField(GUIContent label, Enum enumValue, bool includeObsolete, GUIStyle style, params GUILayoutOption[] options) - { - var position = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.EnumFlagsField(position, label, enumValue, includeObsolete, style); - } - - [Obsolete("Check the docs for the usage of the new parameter 'allowSceneObjects'.")] - public static Object ObjectField(Object obj, Type objType, params GUILayoutOption[] options) - { - return ObjectField(obj, objType, true, options); - } - - public static Object ObjectField(Object obj, Type objType, Object targetBeingEdited, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, options); - return EditorGUI.ObjectField(r, obj, objType, targetBeingEdited); - } - - public static Object ObjectField(Object obj, Type objType, bool allowSceneObjects, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, options); - return EditorGUI.ObjectField(r, obj, objType, allowSceneObjects); - } - - [Obsolete("Check the docs for the usage of the new parameter 'allowSceneObjects'.")] - public static Object ObjectField(string label, Object obj, Type objType, params GUILayoutOption[] options) - { - return ObjectField(label, obj, objType, true, options); - } - - public static Object ObjectField(string label, Object obj, Type objType, Object targetBeingEdited, params GUILayoutOption[] options) - { - return ObjectField(EditorGUIUtility.TempContent(label), obj, objType, targetBeingEdited, options); - } - - public static Object ObjectField(string label, Object obj, Type objType, bool allowSceneObjects, params GUILayoutOption[] options) - { - return ObjectField(EditorGUIUtility.TempContent(label), obj, objType, allowSceneObjects, options); - } - - [Obsolete("Check the docs for the usage of the new parameter 'allowSceneObjects'.")] - public static Object ObjectField(GUIContent label, Object obj, Type objType, params GUILayoutOption[] options) - { - return ObjectField(label, obj, objType, true, options); - } - - // Make an object field. You can assign objects either by drag'n drop objects or by selecting an object using the Object Picker. - public static Object ObjectField(GUIContent label, Object obj, Type objType, Object targetBeingEdited, params GUILayoutOption[] options) - { - var height = EditorGUIUtility.HasObjectThumbnail(objType) ? EditorGUI.kObjectFieldThumbnailHeight : EditorGUI.kSingleLineHeight; - Rect r = s_LastRect = GetControlRect(true, height, options); - return EditorGUI.ObjectField(r, label, obj, objType, targetBeingEdited); - } - - // Make an object field. You can assign objects either by drag'n drop objects or by selecting an object using the Object Picker. - public static Object ObjectField(GUIContent label, Object obj, Type objType, bool allowSceneObjects, params GUILayoutOption[] options) - { - var height = EditorGUIUtility.HasObjectThumbnail(objType) ? EditorGUI.kObjectFieldThumbnailHeight : EditorGUI.kSingleLineHeight; - Rect r = s_LastRect = GetControlRect(true, height, options); - return EditorGUI.ObjectField(r, label, obj, objType, allowSceneObjects); - } - - public static void ObjectField(SerializedProperty property, params GUILayoutOption[] options) - { - ObjectField(property, (GUIContent)null, options); - } - - public static void ObjectField(SerializedProperty property, GUIContent label, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.objectField, options); - EditorGUI.ObjectField(r, property, label); - } - - public static void ObjectField(SerializedProperty property, Type objType, params GUILayoutOption[] options) - { - ObjectField(property, objType, null, options); - } - - // Make an object field. You can assign objects either by drag'n drop objects or by selecting an object using the Object Picker. - public static void ObjectField(SerializedProperty property, Type objType, GUIContent label, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.objectField, options); - EditorGUI.ObjectField(r, property, objType, label); - } - - internal static void ObjectField(SerializedProperty property, Type objType, GUIContent label, EditorGUI.ObjectFieldValidator validator, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.objectField, options); - EditorGUI.ObjectField(r, property, objType, label, EditorStyles.objectField, validator); - } - - internal static Object MiniThumbnailObjectField(GUIContent label, Object obj, Type objType, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, options); - return EditorGUI.MiniThumbnailObjectField(r, label, obj, objType); - } - - public static Vector2 Vector2Field(string label, Vector2 value, params GUILayoutOption[] options) - { - return Vector2Field(EditorGUIUtility.TempContent(label), value, options); - } - - // Make an X & Y field for entering a [[Vector2]]. - public static Vector2 Vector2Field(GUIContent label, Vector2 value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.GetPropertyHeight(SerializedPropertyType.Vector2, label), EditorStyles.numberField, options); - return EditorGUI.Vector2Field(r, label, value); - } - - public static Vector3 Vector3Field(string label, Vector3 value, params GUILayoutOption[] options) - { - return Vector3Field(EditorGUIUtility.TempContent(label), value, options); - } - - // Make an X, Y & Z field for entering a [[Vector3]]. - public static Vector3 Vector3Field(GUIContent label, Vector3 value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.GetPropertyHeight(SerializedPropertyType.Vector3, label), EditorStyles.numberField, options); - return EditorGUI.Vector3Field(r, label, value); - } - - // Make an X, Y & Z field for entering a [[Vector3]], with a "lock" - internal static Vector3 LinkedVector3Field(GUIContent label, Vector3 value, ref bool proportionalScale, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.GetPropertyHeight(SerializedPropertyType.Vector3, label), EditorStyles.numberField, options); - return EditorGUI.LinkedVector3Field(r, label, value, ref proportionalScale); - } - - // Make an X, Y & Z field for entering a [[Vector3]], with a "lock" - internal static Vector3 LinkedVector3Field(GUIContent label, Vector3 value, Vector3 initialValue, ref bool proportionalScale, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.GetPropertyHeight(SerializedPropertyType.Vector3, label), EditorStyles.numberField, options); - int axisModified = 0;// Use X as default modified axis - return EditorGUI.LinkedVector3Field(r, label, GUIContent.none, value, ref proportionalScale, initialValue, 0, ref axisModified, null); - } - - // Make an X, Y, Z & W field for entering a [[Vector4]]. - public static Vector4 Vector4Field(string label, Vector4 value, params GUILayoutOption[] options) - { - return Vector4Field(EditorGUIUtility.TempContent(label), value, options); - } - - // Make an X, Y, Z & W field for entering a [[Vector4]]. - public static Vector4 Vector4Field(GUIContent label, Vector4 value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.GetPropertyHeight(SerializedPropertyType.Vector4, label), EditorStyles.numberField, options); - return EditorGUI.Vector4Field(r, label, value); - } - - public static Vector2Int Vector2IntField(string label, Vector2Int value, params GUILayoutOption[] options) - { - return Vector2IntField(EditorGUIUtility.TempContent(label), value, options); - } - - // Make an X & Y field for entering a [[Vector2Int]]. - public static Vector2Int Vector2IntField(GUIContent label, Vector2Int value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.GetPropertyHeight(SerializedPropertyType.Vector2Int, label), EditorStyles.numberField, options); - return EditorGUI.Vector2IntField(r, label, value); - } - - public static Vector3Int Vector3IntField(string label, Vector3Int value, params GUILayoutOption[] options) - { - return Vector3IntField(EditorGUIUtility.TempContent(label), value, options); - } - - // Make an X, Y & Z field for entering a [[Vector3Int]]. - public static Vector3Int Vector3IntField(GUIContent label, Vector3Int value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.GetPropertyHeight(SerializedPropertyType.Vector3Int, label), EditorStyles.numberField, options); - return EditorGUI.Vector3IntField(r, label, value); - } - - public static Rect RectField(Rect value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.GetPropertyHeight(SerializedPropertyType.Rect, GUIContent.none), EditorStyles.numberField, options); - return EditorGUI.RectField(r, value); - } - - public static Rect RectField(string label, Rect value, params GUILayoutOption[] options) - { - return RectField(EditorGUIUtility.TempContent(label), value, options); - } - - // Make an X, Y, W & H field for entering a [[Rect]]. - public static Rect RectField(GUIContent label, Rect value, params GUILayoutOption[] options) - { - bool hasLabel = EditorGUI.LabelHasContent(label); - float height = EditorGUI.GetPropertyHeight(SerializedPropertyType.Rect, label); - Rect r = s_LastRect = GetControlRect(hasLabel, height, EditorStyles.numberField, options); - return EditorGUI.RectField(r, label, value); - } - - public static RectInt RectIntField(RectInt value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.GetPropertyHeight(SerializedPropertyType.RectInt, GUIContent.none), EditorStyles.numberField, options); - return EditorGUI.RectIntField(r, value); - } - - public static RectInt RectIntField(string label, RectInt value, params GUILayoutOption[] options) - { - return RectIntField(EditorGUIUtility.TempContent(label), value, options); - } - - // Make an X, Y, W & H field for entering a [[RectInt]]. - public static RectInt RectIntField(GUIContent label, RectInt value, params GUILayoutOption[] options) - { - bool hasLabel = EditorGUI.LabelHasContent(label); - float height = EditorGUI.GetPropertyHeight(SerializedPropertyType.RectInt, label); - Rect r = s_LastRect = GetControlRect(hasLabel, height, EditorStyles.numberField, options); - return EditorGUI.RectIntField(r, label, value); - } - - public static Bounds BoundsField(Bounds value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.GetPropertyHeight(SerializedPropertyType.Bounds, GUIContent.none), EditorStyles.numberField, options); - return EditorGUI.BoundsField(r, value); - } - - public static Bounds BoundsField(string label, Bounds value, params GUILayoutOption[] options) - { - return BoundsField(EditorGUIUtility.TempContent(label), value, options); - } - - // Make Center & Extents field for entering a [[Bounds]]. - public static Bounds BoundsField(GUIContent label, Bounds value, params GUILayoutOption[] options) - { - bool hasLabel = EditorGUI.LabelHasContent(label); - float height = EditorGUI.GetPropertyHeight(SerializedPropertyType.Bounds, label); - Rect r = s_LastRect = GetControlRect(hasLabel, height, EditorStyles.numberField, options); - return EditorGUI.BoundsField(r, label, value); - } - - public static BoundsInt BoundsIntField(BoundsInt value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.GetPropertyHeight(SerializedPropertyType.BoundsInt, GUIContent.none), EditorStyles.numberField, options); - return EditorGUI.BoundsIntField(r, value); - } - - public static BoundsInt BoundsIntField(string label, BoundsInt value, params GUILayoutOption[] options) - { - return BoundsIntField(EditorGUIUtility.TempContent(label), value, options); - } - - // Make Center & Extents field for entering a [[BoundsInt]]. - public static BoundsInt BoundsIntField(GUIContent label, BoundsInt value, params GUILayoutOption[] options) - { - bool hasLabel = EditorGUI.LabelHasContent(label); - float height = EditorGUI.GetPropertyHeight(SerializedPropertyType.BoundsInt, label); - Rect r = s_LastRect = GetControlRect(hasLabel, height, EditorStyles.numberField, options); - return EditorGUI.BoundsIntField(r, label, value); - } - - // Make a property field that look like a multi property field (but is made up of individual properties) - internal static void PropertiesField(GUIContent label, SerializedProperty[] properties, GUIContent[] propertyLabels, float propertyLabelsWidth, params GUILayoutOption[] options) - { - bool hasLabel = EditorGUI.LabelHasContent(label); - float height = EditorGUI.kSingleLineHeight * properties.Length + EditorGUI.kVerticalSpacingMultiField * (properties.Length - 1); - Rect r = s_LastRect = GetControlRect(hasLabel, height, EditorStyles.numberField, options); - EditorGUI.PropertiesField(r, label, properties, propertyLabels, propertyLabelsWidth); - } - - internal static int CycleButton(int selected, GUIContent[] contents, GUIStyle style, params GUILayoutOption[] options) - { - if (GUILayout.Button(contents[selected], style, options)) - { - selected++; - if (selected >= contents.Length) - selected = 0; - } - return selected; - } - - public static Color ColorField(Color value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); - return EditorGUI.ColorField(r, value); - } - - public static Color ColorField(string label, Color value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); - return EditorGUI.ColorField(r, label, value); - } - - // Make a field for selecting a [[Color]]. - public static Color ColorField(GUIContent label, Color value, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); - return EditorGUI.ColorField(r, label, value); - } - -#pragma warning disable 612 - [Obsolete("Use EditorGUILayout.ColorField(GUIContent label, Color value, bool showEyedropper, bool showAlpha, bool hdr, params GUILayoutOption[] options)")] - public static Color ColorField( - GUIContent label, Color value, bool showEyedropper, bool showAlpha, bool hdr, ColorPickerHDRConfig hdrConfig, params GUILayoutOption[] options - ) - { - return ColorField(label, value, showEyedropper, showAlpha, hdr); - } - -#pragma warning restore 612 - - public static Color ColorField(GUIContent label, Color value, bool showEyedropper, bool showAlpha, bool hdr, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); - return EditorGUI.ColorField(r, label, value, showEyedropper, showAlpha, hdr); - } - - public static AnimationCurve CurveField(AnimationCurve value, params GUILayoutOption[] options) - { - // TODO Change style - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); - return EditorGUI.CurveField(r, value); - } - - public static AnimationCurve CurveField(string label, AnimationCurve value, params GUILayoutOption[] options) - { - // TODO Change style - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); - return EditorGUI.CurveField(r, label, value); - } - - public static AnimationCurve CurveField(GUIContent label, AnimationCurve value, params GUILayoutOption[] options) - { - // TODO Change style - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); - return EditorGUI.CurveField(r, label, value); - } - - // Variants with settings - public static AnimationCurve CurveField(AnimationCurve value, Color color, Rect ranges, params GUILayoutOption[] options) - { - // TODO Change style - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); - return EditorGUI.CurveField(r, value, color, ranges); - } - - public static AnimationCurve CurveField(string label, AnimationCurve value, Color color, Rect ranges, params GUILayoutOption[] options) - { - // TODO Change style - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); - return EditorGUI.CurveField(r, label, value, color, ranges); - } - - // Make a field for editing an [[AnimationCurve]]. - public static AnimationCurve CurveField(GUIContent label, AnimationCurve value, Color color, Rect ranges, params GUILayoutOption[] options) - { - // TODO Change style - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); - return EditorGUI.CurveField(r, label, value, color, ranges); - } - - public static void CurveField(SerializedProperty property, Color color, Rect ranges, params GUILayoutOption[] options) - { - CurveField(property, color, ranges, null, options); - } - - // Make a field for editing an [[AnimationCurve]]. - public static void CurveField(SerializedProperty property, Color color, Rect ranges, GUIContent label, params GUILayoutOption[] options) - { - // TODO Change style - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); - EditorGUI.CurveField(r, property, color, ranges, label); - } - - public static bool InspectorTitlebar(bool foldout, Object targetObj) - { - return InspectorTitlebar(foldout, targetObj, true); - } - - public static bool InspectorTitlebar(bool foldout, Object targetObj, bool expandable) - { - return EditorGUI.InspectorTitlebar(GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.inspectorTitlebar), foldout, - targetObj, expandable); - } - - // Make an inspector-window-like titlebar. - public static bool InspectorTitlebar(bool foldout, Object[] targetObjs) - { - return InspectorTitlebar(foldout, targetObjs, true); - } - - public static bool InspectorTitlebar(bool foldout, Object[] targetObjs, bool expandable) - { - return EditorGUI.InspectorTitlebar(GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.inspectorTitlebar), foldout, - targetObjs, expandable); - } - - public static bool InspectorTitlebar(bool foldout, Editor editor) - { - return EditorGUI.InspectorTitlebar(GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.inspectorTitlebar), foldout, - editor); - } - - public static void InspectorTitlebar(Object[] targetObjs) - { - EditorGUI.InspectorTitlebar(GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.inspectorTitlebar), targetObjs); - } - - // Make a foldout with a toggle and title - internal static bool ToggleTitlebar(bool foldout, GUIContent label, ref bool toggleValue) - { - return EditorGUI.ToggleTitlebar(GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.inspectorTitlebar), label, foldout, ref toggleValue); - } - - internal static bool ToggleTitlebar(bool foldout, GUIContent label, SerializedProperty property) - { - bool toggleValue = property.boolValue; - EditorGUI.BeginChangeCheck(); - foldout = EditorGUI.ToggleTitlebar(GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.inspectorTitlebar), label, foldout, ref toggleValue); - if (EditorGUI.EndChangeCheck()) - property.boolValue = toggleValue; - - return foldout; - } - - internal static bool FoldoutTitlebar(bool foldout, GUIContent label, bool skipIconSpacing) - { - return FoldoutTitlebar(foldout, label, skipIconSpacing, EditorStyles.inspectorTitlebar, EditorStyles.inspectorTitlebarText); - } - - internal static bool FoldoutTitlebar(bool foldout, GUIContent label, bool skipIconSpacing, GUIStyle baseStyle, GUIStyle textStyle) - { - return EditorGUI.FoldoutTitlebar(GUILayoutUtility.GetRect(GUIContent.none, baseStyle, GUILayout.ExpandWidth(true)), label, foldout, skipIconSpacing, baseStyle, textStyle); - } - - // Make a label with a foldout arrow to the left of it. - internal static bool FoldoutInternal(bool foldout, GUIContent content, bool toggleOnLabelClick, GUIStyle style) - { - Rect r = s_LastRect = GUILayoutUtility.GetRect(EditorGUIUtility.fieldWidth, EditorGUIUtility.fieldWidth, EditorGUI.kSingleLineHeight, EditorGUI.kSingleLineHeight, style); - return EditorGUI.Foldout(r, foldout, content, toggleOnLabelClick, style); - } - - internal static uint LayerMaskField(UInt32 layers, GUIContent label, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, options); - return EditorGUI.LayerMaskField(r, layers, label); - } - - internal static LayerMask LayerMaskField(LayerMask layers, GUIContent label, params GUILayoutOption[] options) - { - var rect = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, options); - return EditorGUI.LayerMaskField(rect, layers, label); - } - - internal static void LayerMaskField(SerializedProperty property, GUIContent label, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, options); - EditorGUI.LayerMaskField(r, property, label); - } - - public static void HelpBox(string message, MessageType type) - { - LabelField(GUIContent.none, EditorGUIUtility.TempContent(message, EditorGUIUtility.GetHelpIcon(type)), EditorStyles.helpBox); - } - - // Make a help box with a message to the user. - public static void HelpBox(string message, MessageType type, bool wide) - { - LabelField(wide ? GUIContent.none : EditorGUIUtility.blankContent, - EditorGUIUtility.TempContent(message, EditorGUIUtility.GetHelpIcon(type)), - EditorStyles.helpBox); - } - - // Make a help box with a message to the user. - public static void HelpBox(GUIContent content, bool wide = true) - { - LabelField(wide ? GUIContent.none : EditorGUIUtility.blankContent, - content, - EditorStyles.helpBox); - } - - // Make a label in front of some control. - internal static void PrefixLabelInternal(GUIContent label, GUIStyle followingStyle, GUIStyle labelStyle) - { - float p = followingStyle.margin.left; - if (!EditorGUI.LabelHasContent(label)) - { - GUILayoutUtility.GetRect(EditorGUI.indent - p, EditorGUI.kSingleLineHeight, followingStyle, GUILayout.ExpandWidth(false)); - return; - } - - Rect r = GUILayoutUtility.GetRect(EditorGUIUtility.labelWidth - p, EditorGUI.kSingleLineHeight, followingStyle, GUILayout.ExpandWidth(false)); - r.xMin += EditorGUI.indent; - EditorGUI.HandlePrefixLabel(r, r, label, 0, labelStyle); - } - - // Make a small space between the previous control and the following. - public static void Space() - { - Space(EditorGUI.kDefaultSpacing, true); - } - - public static void Space(float width) - { - Space(width, true); - } - - public static void Space(float width, bool expand) - { - GUILayoutUtility.GetRect(width, width, GUILayout.ExpandWidth(expand)); - } - - //[System.Obsolete ("Use Space() instead")] - // Make this function Obsolete when someone has time to _rename_ all - // the Standard Packages to Space(), as currently it shows tons of - // warnings. - // Same for the graphic tests. - // *undoc* - public static void Separator() - { - Space(); - } - - public class ToggleGroupScope : GUI.Scope - { - public bool enabled { get; protected set; } - - public ToggleGroupScope(string label, bool toggle) - { - enabled = BeginToggleGroup(label, toggle); - } - - public ToggleGroupScope(GUIContent label, bool toggle) - { - enabled = BeginToggleGroup(label, toggle); - } - - protected override void CloseScope() - { - EndToggleGroup(); - } - } - - public static bool BeginToggleGroup(string label, bool toggle) - { - return BeginToggleGroup(EditorGUIUtility.TempContent(label), toggle); - } - - // Begin a vertical group with a toggle to enable or disable all the controls within at once. - public static bool BeginToggleGroup(GUIContent label, bool toggle) - { - toggle = ToggleLeft(label, toggle, EditorStyles.boldLabel); - EditorGUI.BeginDisabled(!toggle); - GUILayout.BeginVertical(); - - return toggle; - } - - // Close a group started with ::ref::BeginToggleGroup - public static void EndToggleGroup() - { - GUILayout.EndVertical(); - EditorGUI.EndDisabled(); - } - - public class HorizontalScope : GUI.Scope - { - public Rect rect { get; protected set; } - - public HorizontalScope(params GUILayoutOption[] options) - { - rect = BeginHorizontal(options); - } - - public HorizontalScope(GUIStyle style, params GUILayoutOption[] options) - { - rect = BeginHorizontal(style, options); - } - - internal HorizontalScope(GUIContent content, GUIStyle style, params GUILayoutOption[] options) - { - rect = BeginHorizontal(content, style, options); - } - - protected override void CloseScope() - { - EndHorizontal(); - } - } - - public static Rect BeginHorizontal(params GUILayoutOption[] options) - { - return BeginHorizontal(GUIContent.none, GUIStyle.none, options); - } - - // Begin a horizontal group and get its rect back. - public static Rect BeginHorizontal(GUIStyle style, params GUILayoutOption[] options) - { - return BeginHorizontal(GUIContent.none, style, options); - } - - // public static Rect BeginHorizontal (string text, params GUILayoutOption[] options) { return BeginHorizontal (EditorGUIUtility.TempContent (text), GUIStyle.none, options); } - // public static Rect BeginHorizontal (Texture image, params GUILayoutOption[] options) { return BeginHorizontal (EditorGUIUtility.TempContent (image), GUIStyle.none, options); } - // public static Rect BeginHorizontal (GUIContent content, params GUILayoutOption[] options) { return BeginHorizontal (content, GUIStyle.none, options); } - // public static Rect BeginHorizontal (string text, GUIStyle style, params GUILayoutOption[] options) { return BeginHorizontal (EditorGUIUtility.TempContent (text), style, options); } - // public static Rect BeginHorizontal (Texture image, GUIStyle style, params GUILayoutOption[] options) { return BeginHorizontal (EditorGUIUtility.TempContent (image), style, options); } - internal static Rect BeginHorizontal(GUIContent content, GUIStyle style, params GUILayoutOption[] options) - { - GUILayoutGroup g = GUILayoutUtility.BeginLayoutGroup(style, options, typeof(GUILayoutGroup)); - g.isVertical = false; - if (style != GUIStyle.none || content != GUIContent.none) - { - GUI.Box(g.rect, GUIContent.none, style); - } - return g.rect; - } - - // Close a group started with BeginHorizontal - public static void EndHorizontal() - { - GUILayout.EndHorizontal(); - } - - public class VerticalScope : GUI.Scope - { - public Rect rect { get; protected set; } - - public VerticalScope(params GUILayoutOption[] options) - { - rect = BeginVertical(options); - } - - public VerticalScope(GUIStyle style, params GUILayoutOption[] options) - { - rect = BeginVertical(style, options); - } - - internal VerticalScope(GUIContent content, GUIStyle style, params GUILayoutOption[] options) - { - rect = BeginVertical(content, style, options); - } - - protected override void CloseScope() - { - EndVertical(); - } - } - - public static Rect BeginVertical(params GUILayoutOption[] options) - { - return BeginVertical(GUIContent.none, GUIStyle.none, options); - } - - // Begin a vertical group and get its rect back. - public static Rect BeginVertical(GUIStyle style, params GUILayoutOption[] options) - { - return BeginVertical(GUIContent.none, style, options); - } - - // public static Rect BeginVertical (string text, params GUILayoutOption[] options) { return BeginVertical (EditorGUIUtility.TempContent (text), GUIStyle.none, options); } - // public static Rect BeginVertical (Texture image, params GUILayoutOption[] options) { return BeginVertical (EditorGUIUtility.TempContent (image), GUIStyle.none, options); } - // public static Rect BeginVertical (GUIContent content, params GUILayoutOption[] options) { return BeginVertical (content, GUIStyle.none, options); } - // public static Rect BeginVertical (string text, GUIStyle style, params GUILayoutOption[] options) { return BeginVertical (EditorGUIUtility.TempContent (text), style, options); } - // public static Rect BeginVertical (Texture image, GUIStyle style, params GUILayoutOption[] options) { return BeginVertical (EditorGUIUtility.TempContent (image), style, options); } - internal static Rect BeginVertical(GUIContent content, GUIStyle style, params GUILayoutOption[] options) - { - GUILayoutGroup g = GUILayoutUtility.BeginLayoutGroup(style, options, typeof(GUILayoutGroup)); - g.isVertical = true; - if (style != GUIStyle.none || content != GUIContent.none) - { - GUI.Box(g.rect, GUIContent.none, style); - } - return g.rect; - } - - // Close a group started with BeginVertical - public static void EndVertical() - { - GUILayout.EndVertical(); - } - - public class ScrollViewScope : GUI.Scope - { - public Vector2 scrollPosition { get; protected set; } - public bool handleScrollWheel { get; set; } - - public ScrollViewScope(Vector2 scrollPosition, params GUILayoutOption[] options) - { - handleScrollWheel = true; - this.scrollPosition = BeginScrollView(scrollPosition, options); - } - - public ScrollViewScope(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical, params GUILayoutOption[] options) - { - handleScrollWheel = true; - this.scrollPosition = BeginScrollView(scrollPosition, alwaysShowHorizontal, alwaysShowVertical, options); - } - - public ScrollViewScope(Vector2 scrollPosition, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, params GUILayoutOption[] options) - { - handleScrollWheel = true; - this.scrollPosition = BeginScrollView(scrollPosition, horizontalScrollbar, verticalScrollbar, options); - } - - public ScrollViewScope(Vector2 scrollPosition, GUIStyle style, params GUILayoutOption[] options) - { - handleScrollWheel = true; - this.scrollPosition = BeginScrollView(scrollPosition, style, options); - } - - public ScrollViewScope(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options) - { - handleScrollWheel = true; - this.scrollPosition = BeginScrollView(scrollPosition, alwaysShowHorizontal, alwaysShowVertical, horizontalScrollbar, verticalScrollbar, background, options); - } - - internal ScrollViewScope(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, params GUILayoutOption[] options) - { - handleScrollWheel = true; - this.scrollPosition = BeginScrollView(scrollPosition, alwaysShowHorizontal, alwaysShowVertical, horizontalScrollbar, verticalScrollbar, options); - } - - protected override void CloseScope() - { - EndScrollView(handleScrollWheel); - } - } - - public static Vector2 BeginScrollView(Vector2 scrollPosition, params GUILayoutOption[] options) - { - return BeginScrollView(scrollPosition, false, false, GUI.skin.horizontalScrollbar, GUI.skin.verticalScrollbar, GUI.skin.scrollView, options); - } - - public static Vector2 BeginScrollView(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical, params GUILayoutOption[] options) - { - return BeginScrollView(scrollPosition, alwaysShowHorizontal, alwaysShowVertical, GUI.skin.horizontalScrollbar, GUI.skin.verticalScrollbar, GUI.skin.scrollView, options); - } - - public static Vector2 BeginScrollView(Vector2 scrollPosition, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, params GUILayoutOption[] options) - { - return BeginScrollView(scrollPosition, false, false, horizontalScrollbar, verticalScrollbar, GUI.skin.scrollView, options); - } - - public static Vector2 BeginScrollView(Vector2 scrollPosition, GUIStyle style, params GUILayoutOption[] options) - { - string name = style.name; - - GUIStyle vertical = GUI.skin.FindStyle(name + "VerticalScrollbar") ?? GUI.skin.verticalScrollbar; - GUIStyle horizontal = GUI.skin.FindStyle(name + "HorizontalScrollbar") ?? GUI.skin.horizontalScrollbar; - return BeginScrollView(scrollPosition, false, false, horizontal, vertical, style, options); - } - - internal static Vector2 BeginScrollView(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, params GUILayoutOption[] options) - { - return BeginScrollView(scrollPosition, alwaysShowHorizontal, alwaysShowVertical, horizontalScrollbar, verticalScrollbar, GUI.skin.scrollView, options); - } - - // Begin an automatically layouted scrollview. - public static Vector2 BeginScrollView(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options) - { - GUIScrollGroup g = (GUIScrollGroup)GUILayoutUtility.BeginLayoutGroup(background, null, typeof(GUIScrollGroup)); - if (Event.current.type == EventType.Layout) - { - g.resetCoords = true; - g.isVertical = true; - g.stretchWidth = 1; - g.stretchHeight = 1; - g.verticalScrollbar = verticalScrollbar; - g.horizontalScrollbar = horizontalScrollbar; - g.ApplyOptions(options); - } - return EditorGUIInternal.DoBeginScrollViewForward(g.rect, scrollPosition, new Rect(0, 0, g.clientWidth, g.clientHeight), alwaysShowHorizontal, alwaysShowVertical, horizontalScrollbar, verticalScrollbar, background); - } - - internal class VerticalScrollViewScope : GUI.Scope - { - public Vector2 scrollPosition { get; protected set; } - public bool handleScrollWheel { get; set; } - - public VerticalScrollViewScope(Vector2 scrollPosition, params GUILayoutOption[] options) - { - handleScrollWheel = true; - this.scrollPosition = BeginVerticalScrollView(scrollPosition, options); - } - - public VerticalScrollViewScope(Vector2 scrollPosition, bool alwaysShowVertical, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options) - { - handleScrollWheel = true; - this.scrollPosition = BeginVerticalScrollView(scrollPosition, alwaysShowVertical, verticalScrollbar, background, options); - } - - protected override void CloseScope() - { - EndScrollView(handleScrollWheel); - } - } - - internal static Vector2 BeginVerticalScrollView(Vector2 scrollPosition, params GUILayoutOption[] options) - { - return BeginVerticalScrollView(scrollPosition, false, GUI.skin.verticalScrollbar, GUI.skin.scrollView, options); - } - - // Begin an automatically layouted scrollview. - internal static Vector2 BeginVerticalScrollView(Vector2 scrollPosition, bool alwaysShowVertical, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options) - { - GUIScrollGroup g = (GUIScrollGroup)GUILayoutUtility.BeginLayoutGroup(background, null, typeof(GUIScrollGroup)); - if (Event.current.type == EventType.Layout) - { - g.resetCoords = true; - g.isVertical = true; - g.stretchWidth = 1; - g.stretchHeight = 1; - g.verticalScrollbar = verticalScrollbar; - g.horizontalScrollbar = GUIStyle.none; - g.allowHorizontalScroll = false; - g.ApplyOptions(options); - } - return EditorGUIInternal.DoBeginScrollViewForward(g.rect, scrollPosition, new Rect(0, 0, g.clientWidth, g.clientHeight), false, alwaysShowVertical, GUI.skin.horizontalScrollbar, verticalScrollbar, background); - } - - internal class HorizontalScrollViewScope : GUI.Scope - { - public Vector2 scrollPosition { get; protected set; } - public bool handleScrollWheel { get; set; } - - public HorizontalScrollViewScope(Vector2 scrollPosition, params GUILayoutOption[] options) - { - handleScrollWheel = true; - this.scrollPosition = BeginHorizontalScrollView(scrollPosition, options); - } - - public HorizontalScrollViewScope(Vector2 scrollPosition, bool alwaysShowHorizontal, GUIStyle horizontalScrollbar, GUIStyle background, params GUILayoutOption[] options) - { - handleScrollWheel = true; - this.scrollPosition = BeginHorizontalScrollView(scrollPosition, alwaysShowHorizontal, horizontalScrollbar, background, options); - } - - protected override void CloseScope() - { - EndScrollView(handleScrollWheel); - } - } - - internal static Vector2 BeginHorizontalScrollView(Vector2 scrollPosition, params GUILayoutOption[] options) - { - return BeginHorizontalScrollView(scrollPosition, false, GUI.skin.horizontalScrollbar, GUI.skin.scrollView, options); - } - - // Begin an automatically layouted scrollview. - - internal static Vector2 BeginHorizontalScrollView(Vector2 scrollPosition, bool alwaysShowHorizontal, GUIStyle horizontalScrollbar, GUIStyle background, params GUILayoutOption[] options) - { - GUIScrollGroup g = (GUIScrollGroup)GUILayoutUtility.BeginLayoutGroup(background, null, typeof(GUIScrollGroup)); - if (Event.current.type == EventType.Layout) - { - g.resetCoords = true; - g.isVertical = true; - g.stretchWidth = 1; - g.stretchHeight = 1; - g.verticalScrollbar = GUIStyle.none; - g.horizontalScrollbar = horizontalScrollbar; - g.allowHorizontalScroll = true; - g.allowVerticalScroll = false; - g.ApplyOptions(options); - } - return EditorGUIInternal.DoBeginScrollViewForward(g.rect, scrollPosition, new Rect(0, 0, g.clientWidth, g.clientHeight), alwaysShowHorizontal, false, horizontalScrollbar, GUI.skin.verticalScrollbar, background); - } - - // Ends a scrollview started with a call to BeginScrollView. - public static void EndScrollView() - { - GUILayout.EndScrollView(true); - } - - internal static void EndScrollView(bool handleScrollWheel) - { - GUILayout.EndScrollView(handleScrollWheel); - } - - public static bool PropertyField(SerializedProperty property, params GUILayoutOption[] options) - { - return PropertyField(property, null, IsChildrenIncluded(property), options); - } - - public static bool PropertyField(SerializedProperty property, GUIContent label, params GUILayoutOption[] options) - { - return PropertyField(property, label, IsChildrenIncluded(property), options); - } - - public static bool PropertyField(SerializedProperty property, bool includeChildren, params GUILayoutOption[] options) - { - return PropertyField(property, null, includeChildren, options); - } - - // Make a field for [[SerializedProperty]]. - public static bool PropertyField(SerializedProperty property, GUIContent label, bool includeChildren, params GUILayoutOption[] options) - { - return ScriptAttributeUtility.GetHandler(property).OnGUILayout(property, label, includeChildren, options); - } - - private static bool IsChildrenIncluded(SerializedProperty prop) - { - switch (prop.propertyType) - { - case SerializedPropertyType.Generic: - case SerializedPropertyType.Vector4: - return true; - default: - return false; - } - } - - public static Rect GetControlRect(params GUILayoutOption[] options) - { - return GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.layerMaskField, options); - } - - public static Rect GetControlRect(bool hasLabel, params GUILayoutOption[] options) - { - return GetControlRect(hasLabel, EditorGUI.kSingleLineHeight, EditorStyles.layerMaskField, options); - } - - public static Rect GetControlRect(bool hasLabel, float height, params GUILayoutOption[] options) - { - return GetControlRect(hasLabel, height, EditorStyles.layerMaskField, options); - } - - public static Rect GetControlRect(bool hasLabel, float height, GUIStyle style, params GUILayoutOption[] options) - { - return GUILayoutUtility.GetRect( - hasLabel ? kLabelFloatMinW : EditorGUIUtility.fieldWidth, - kLabelFloatMaxW, - height, height, style, options); - } - - internal static Rect GetSliderRect(bool hasLabel, params GUILayoutOption[] options) - { - return GetSliderRect(hasLabel, GUI.skin.horizontalSlider, options); - } - - internal static Rect GetSliderRect(bool hasLabel, GUIStyle sliderStyle, params GUILayoutOption[] options) - { - return GUILayoutUtility.GetRect( - hasLabel ? kLabelFloatMinW : EditorGUIUtility.fieldWidth, - kLabelFloatMaxW + EditorGUI.kSpacing + EditorGUI.kSliderMaxW, - EditorGUI.kSingleLineHeight, EditorGUI.kSingleLineHeight, sliderStyle, options); - } - - internal static Rect GetToggleRect(bool hasLabel, params GUILayoutOption[] options) - { - // Toggle is 14 pixels wide while float fields are EditorGUIUtility.fieldWidth pixels wide. - // Store difference in variable and add to min and max width values used for float fields. - float toggleAdjust = (14 - EditorGUIUtility.fieldWidth); - return GUILayoutUtility.GetRect( - hasLabel ? kLabelFloatMinW + toggleAdjust : EditorGUIUtility.fieldWidth + toggleAdjust, - kLabelFloatMaxW + toggleAdjust, - EditorGUI.kSingleLineHeight, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); - } - - public class FadeGroupScope : GUI.Scope - { - // when using the FadeGroupScope, make sure to only show the content when 'visible' is set to true, - // otherwise only the hide animation will run, and then the content will be visible again. - public bool visible { get; protected set; } - - public FadeGroupScope(float value) - { - visible = BeginFadeGroup(value); - } - - protected override void CloseScope() - { - EndFadeGroup(); - } - } - - public static bool BeginFadeGroup(float value) - { - GUILayoutFadeGroup g = (GUILayoutFadeGroup)GUILayoutUtility.BeginLayoutGroup(GUIStyle.none, null, typeof(GUILayoutFadeGroup)); - g.isVertical = true; - g.resetCoords = false; - g.fadeValue = value; - g.wasGUIEnabled = GUI.enabled; - g.guiColor = GUI.color; - g.consideredForMargin = value > 0; - - // We don't want the fade group gui clip to be used for calculating the label width of controls in this fade group, so we lock the context width. - EditorGUIUtility.LockContextWidth(); - - if (value != 0.0f && value != 1.0f) - { - g.resetCoords = true; - GUI.BeginGroup(g.rect); - - if (Event.current.type == EventType.MouseDown) - { - Event.current.Use(); - } - } - - return value != 0; - } - - public static void EndFadeGroup() - { - // If we're inside a fade group, end it here. - GUILayoutFadeGroup g = EditorGUILayoutUtilityInternal.topLevel as GUILayoutFadeGroup; - - // If there are no more FadeGroups to end, display a warning. - if (g == null) - { - Debug.LogWarning("Unexpected call to EndFadeGroup! Make sure to call EndFadeGroup the same number of times as BeginFadeGroup."); - return; - } - - if (g.fadeValue != 0.0f && g.fadeValue != 1.0f) - { - GUI.EndGroup(); - } - - EditorGUIUtility.UnlockContextWidth(); - GUI.enabled = g.wasGUIEnabled; - GUI.color = g.guiColor; - GUILayoutUtility.EndLayoutGroup(); - } - - public static BuildTargetGroup BeginBuildTargetSelectionGrouping() - { - BuildPlatform[] validPlatforms = BuildPlatforms.instance.GetValidPlatforms().ToArray(); - int selected = BeginPlatformGrouping(validPlatforms, null); - return validPlatforms[selected].namedBuildTarget.ToBuildTargetGroup(); - } - - public static void EndBuildTargetSelectionGrouping() - { - EndPlatformGrouping(); - } - - internal static int BeginPlatformGrouping(BuildPlatform[] platforms, GUIContent defaultTab) - { - return BeginPlatformGrouping(platforms, defaultTab, EditorStyles.frameBox); - } - - static Rect GetTabRect(Rect rect, int tabIndex, int tabCount, out GUIStyle tabStyle) - { - if (s_TabOnlyOne == null) - { - // Keep in sync with Tests/EditModeAndPlayModeTests/PlayerSettings/Assets/Editor/PlayerSettingsApplicationIdentifierTests.cs. - s_TabOnlyOne = "Tab onlyOne"; - s_TabFirst = "Tab first"; - s_TabMiddle = "Tab middle"; - s_TabLast = "Tab last"; - } - - tabStyle = s_TabMiddle; - - if (tabCount == 1) - { - tabStyle = s_TabOnlyOne; - } - else if (tabIndex == 0) - { - tabStyle = s_TabFirst; - } - else if (tabIndex == (tabCount - 1)) - { - tabStyle = s_TabLast; - } - - float tabWidth = rect.width / tabCount; - int left = Mathf.RoundToInt(tabIndex * tabWidth); - int right = Mathf.RoundToInt((tabIndex + 1) * tabWidth); - return new Rect(rect.x + left, rect.y, right - left, EditorGUI.kTabButtonHeight); - } - - internal static int BeginPlatformGrouping(BuildPlatform[] platforms, GUIContent defaultTab, GUIStyle style) - { - return BeginPlatformGrouping(platforms, defaultTab, style, null); - } - - internal static int BeginPlatformGrouping(BuildPlatform[] platforms, GUIContent defaultTab, GUIStyle style, Func showOverrideForPlatform) - { - int selectedPlatform = -1; - for (int i = 0; i < platforms.Length; i++) - { - if (platforms[i].IsSelected()) - { - selectedPlatform = i; - break; - } - } - if (selectedPlatform == -1) - { - s_SelectedDefault.value = true; - selectedPlatform = 0; - } - - int selected = defaultTab == null ? selectedPlatform : (s_SelectedDefault.value ? -1 : selectedPlatform); - - bool tempEnabled = GUI.enabled; - GUI.enabled = true; - EditorGUI.BeginChangeCheck(); - Rect r = BeginVertical(style); - int platformCount = platforms.Length; - int buttonCount = platformCount; - int startIndex = 0; - - if (defaultTab != null) - { - buttonCount++; - startIndex = -1; - } - - int buttonIndex = 0; - for (int i = startIndex; i < platformCount; i++, buttonIndex++) - { - GUIContent content = GUIContent.none; - - if (i == -1) - { - content = defaultTab; - } - else - { - content = new GUIContent(platforms[i].smallIcon, platforms[i].tooltip); - } - - GUIStyle buttonStyle = null; - Rect buttonRect = GetTabRect(r, buttonIndex, buttonCount, out buttonStyle); - - if (GUI.Toggle(buttonRect, selected == i, content, buttonStyle)) - selected = i; - if (showOverrideForPlatform != null) - { - if (showOverrideForPlatform(i)) - { - var prevMargin = EditorGUIUtility.leftMarginCoord; - var overrideRect = buttonRect; - const int margin = 3; - overrideRect.y += margin; - overrideRect.height -= margin * 2; - EditorGUIUtility.leftMarginCoord = overrideRect.x + margin; - EditorGUI.DrawOverrideBackgroundApplicable(overrideRect); - EditorGUIUtility.leftMarginCoord = prevMargin; - } - } - } - - // GUILayout.Space doesn't expand to available width, so use GetRect instead - GUILayoutUtility.GetRect(10, EditorGUI.kTabButtonHeight); - - GUI.enabled = tempEnabled; - - // Important that we only actually set the selectedBuildTargetGroup if the user clicked the button. - // If the current selectedBuildTargetGroup is one that is not among the tabs (because the build target - // is not supported), then this should not be changed unless the user explicitly does so. - // Otherwise, if the build window is open at the same time, the unsupported build target groups will - // not be selectable in the build window. - if (EditorGUI.EndChangeCheck()) - { - if (defaultTab == null) - { - platforms[selected].Select(); - } - else - { - if (selected < 0) - { - s_SelectedDefault.value = true; - } - else - { - platforms[selected].Select(); - s_SelectedDefault.value = false; - } - } - - // Repaint build window, if open. - Object[] buildWindows = Resources.FindObjectsOfTypeAll(typeof(BuildPlayerWindow)); - foreach (Object t in buildWindows) - { - BuildPlayerWindow buildWindow = t as BuildPlayerWindow; - if (buildWindow != null) - buildWindow.Repaint(); - } - } - - return selected; - } - - internal static void EndPlatformGrouping() - { - EndVertical(); - } - - internal static void MultiSelectionObjectTitleBar(Object[] objects) - { - string text = objects[0].name + " (" + ObjectNames.NicifyVariableName(ObjectNames.GetTypeName(objects[0])) + ")"; - if (objects.Length > 1) - { - text += " and " + (objects.Length - 1) + " other" + (objects.Length > 2 ? "s" : ""); - } - GUILayoutOption[] options = { GUILayout.Height(16f) }; - GUILayout.Label(EditorGUIUtility.TempContent(text, AssetPreview.GetMiniThumbnail(objects[0])), EditorStyles.boldLabel, options); - } - - // Returns true if specified bit is true for all targets - internal static bool BitToggleField(string label, SerializedProperty bitFieldProperty, int flag) - { - bool toggle = (bitFieldProperty.intValue & flag) != 0; - bool different = (bitFieldProperty.hasMultipleDifferentValuesBitwise & flag) != 0; - EditorGUI.showMixedValue = different; - EditorGUI.BeginChangeCheck(); - toggle = Toggle(label, toggle); - if (EditorGUI.EndChangeCheck()) - { - // If toggle has mixed values, always set all to true when clicking it - if (different) - { - toggle = true; - } - different = false; - int bitIndex = -1; - for (int i = 0; i < 32; i++) - { - if (((1 << i) & flag) != 0) - { - bitIndex = i; - break; - } - } - bitFieldProperty.SetBitAtIndexForAllTargetsImmediate(bitIndex, toggle); - } - EditorGUI.showMixedValue = false; - return toggle && !different; - } - - internal static void SortingLayerField(GUIContent label, SerializedProperty layerID, GUIStyle style) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style); - EditorGUI.SortingLayerField(r, label, layerID, style, EditorStyles.label); - } - - internal static string TextFieldDropDown(string text, string[] dropDownElement) - { - return TextFieldDropDown(GUIContent.none, text, dropDownElement); - } - - internal static string TextFieldDropDown(GUIContent label, string text, string[] dropDownElement) - { - Rect rect = GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.textField); - return EditorGUI.TextFieldDropDown(rect, label, text, dropDownElement); - } - - internal static string DelayedTextFieldDropDown(string text, string[] dropDownElement) - { - return DelayedTextFieldDropDown(GUIContent.none, text, dropDownElement); - } - - internal static string DelayedTextFieldDropDown(GUIContent label, string text, string[] dropDownElement) - { - Rect rect = GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.textFieldDropDownText); - return EditorGUI.DelayedTextFieldDropDown(rect, label, text, dropDownElement); - } - - // A button that returns true on mouse down - like a popup button - public static bool DropdownButton(GUIContent content, FocusType focusType, params GUILayoutOption[] options) - { - return DropdownButton(content, focusType, "MiniPullDown", options); - } - - // A button that returns true on mouse down - like a popup button - public static bool DropdownButton(GUIContent content, FocusType focusType, GUIStyle style, params GUILayoutOption[] options) - { - s_LastRect = GUILayoutUtility.GetRect(content, style, options); - return EditorGUI.DropdownButton(s_LastRect, content, focusType, style); - } - - // A toggle that returns true on mouse down - like a popup button and returns true if checked - internal static bool DropDownToggle(ref bool toggled, GUIContent content, GUIStyle toggleStyle) - { - GUIStyle buttonStyle = GUIStyle.none; - - // This is to be compatible with existing code - if (toggleStyle == EditorStyles.toolbarDropDownToggle || toggleStyle == EditorStyles.toolbarDropDownToggleRight) - buttonStyle = EditorStyles.toolbarDropDownToggleButton; - - return DropDownToggle(ref toggled, content, toggleStyle, buttonStyle); - } - - internal static bool DropDownToggle(ref bool toggled, GUIContent content, GUIStyle toggleStyle, GUIStyle toggleDropdownButtonStyle) - { - Rect toggleRect = GUILayoutUtility.GetRect(content, toggleStyle); - Rect arrowRightRect = Rect.zero; - - if (toggleDropdownButtonStyle != null) - { - arrowRightRect = new Rect(toggleRect.xMax - toggleDropdownButtonStyle.fixedWidth - toggleDropdownButtonStyle.margin.right, toggleRect.y, toggleDropdownButtonStyle.fixedWidth, toggleRect.height); - } - else - { - arrowRightRect = new Rect(toggleRect.xMax - toggleStyle.padding.right, toggleRect.y, toggleStyle.padding.right, toggleRect.height); - } - - - int dropdownButtonId = GUIUtility.GetControlID(EditorGUI.s_DropdownButtonHash, FocusType.Passive, arrowRightRect); - bool clicked = EditorGUI.DropdownButton(dropdownButtonId, arrowRightRect, GUIContent.none, GUIStyle.none); - - if (!clicked) - { - toggled = GUI.Toggle(toggleRect, toggled, content, toggleStyle); - } - - // Ensure that the dropdown button is rendered on top of the toggle - if (Event.current.type == EventType.Repaint && toggleDropdownButtonStyle != null && toggleDropdownButtonStyle != GUIStyle.none) - { - EditorGUI.DropdownButton(dropdownButtonId, arrowRightRect, GUIContent.none, toggleDropdownButtonStyle); - } - - return clicked; - } - - internal static int AdvancedPopup(int selectedIndex, string[] displayedOptions, params GUILayoutOption[] options) - { - return AdvancedPopup(selectedIndex, displayedOptions, "MiniPullDown", options); - } - - internal static int AdvancedPopup(int selectedIndex, string[] displayedOptions, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.AdvancedPopup(r, selectedIndex, displayedOptions, style); - } - - internal static int AdvancedLazyPopup(string displayedOption, int selectedIndex, Func> displayedOptionsFunc, GUIStyle style, params GUILayoutOption[] options) - { - Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); - return EditorGUI.AdvancedLazyPopup(r, displayedOption, selectedIndex, displayedOptionsFunc, style); - } - - [Obsolete("(UnityUpgradable) -> UnityEditor.HyperLinkClickedEventArgs", true)] - internal class HyperLinkClickedEventArgs - { - [Obsolete("(UnityUpgradable) -> UnityEditor.HyperLinkClickedEventArgs.hyperLinkData", true)] - public Dictionary hyperlinkInfos { get; private set; } - internal HyperLinkClickedEventArgs(Dictionary hyperLinkData) {} - } - } } diff --git a/Editor/Mono/EditorGUILayout.cs b/Editor/Mono/EditorGUILayout.cs new file mode 100644 index 0000000000..af8bc5e18e --- /dev/null +++ b/Editor/Mono/EditorGUILayout.cs @@ -0,0 +1,2536 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using System.Collections.Generic; +using UnityEditor.Build; +using UnityEngine; +using UnityEngine.Internal; +using Object = UnityEngine.Object; + +namespace UnityEditor; + +// Auto-layouted version of [[EditorGUI]] +sealed partial class EditorGUILayout +{ + // @TODO: Make private (and rename to not claim it's a constant). Shouldn't really be used outside of EditorGUI. + // Places that use this directly should likely use GetControlRect instead. + internal static float kLabelFloatMinW => EditorGUIUtility.labelWidth + EditorGUIUtility.fieldWidth + EditorGUI.kSpacing; + + internal static float kLabelFloatMaxW => EditorGUIUtility.labelWidth + EditorGUIUtility.fieldWidth + EditorGUI.kSpacing; + + internal static Rect s_LastRect; + + internal const float kPlatformTabWidth = 30; + + internal static SavedBool s_SelectedDefault = new SavedBool("Platform.ShownDefaultTab", true); + + static GUIStyle s_TabOnlyOne; + static GUIStyle s_TabFirst; + static GUIStyle s_TabMiddle; + static GUIStyle s_TabLast; + + [ExcludeFromDocs] + public static bool Foldout(bool foldout, string content) + { + return Foldout(foldout, content, EditorStyles.foldout); + } + + public static bool Foldout(bool foldout, string content, [DefaultValue("EditorStyles.foldout")] GUIStyle style) + { + return Foldout(foldout, EditorGUIUtility.TempContent(content), false, style); + } + + [ExcludeFromDocs] + public static bool Foldout(bool foldout, GUIContent content) + { + return Foldout(foldout, content, EditorStyles.foldout); + } + + public static bool Foldout(bool foldout, GUIContent content, [DefaultValue("EditorStyles.foldout")] GUIStyle style) + { + return Foldout(foldout, content, false, style); + } + + [ExcludeFromDocs] + public static bool Foldout(bool foldout, string content, bool toggleOnLabelClick) + { + return Foldout(foldout, content, toggleOnLabelClick, EditorStyles.foldout); + } + + public static bool Foldout(bool foldout, string content, bool toggleOnLabelClick, [DefaultValue("EditorStyles.foldout")] GUIStyle style) + { + return Foldout(foldout, EditorGUIUtility.TempContent(content), toggleOnLabelClick, style); + } + + [ExcludeFromDocs] + public static bool Foldout(bool foldout, GUIContent content, bool toggleOnLabelClick) + { + return Foldout(foldout, content, toggleOnLabelClick, EditorStyles.foldout); + } + + public static bool Foldout(bool foldout, GUIContent content, bool toggleOnLabelClick, [DefaultValue("EditorStyles.foldout")] GUIStyle style) + { + return FoldoutInternal(foldout, content, toggleOnLabelClick, style); + } + + [ExcludeFromDocs] + public static void PrefixLabel(string label) + { + GUIStyle followingStyle = "Button"; + PrefixLabel(label, followingStyle); + } + + public static void PrefixLabel(string label, [DefaultValue("\"Button\"")] GUIStyle followingStyle) + { + PrefixLabel(EditorGUIUtility.TempContent(label), followingStyle, EditorStyles.label); + } + + public static void PrefixLabel(string label, GUIStyle followingStyle, GUIStyle labelStyle) + { + PrefixLabel(EditorGUIUtility.TempContent(label), followingStyle, labelStyle); + } + + [ExcludeFromDocs] + public static void PrefixLabel(GUIContent label) + { + GUIStyle followingStyle = "Button"; + PrefixLabel(label, followingStyle); + } + + public static void PrefixLabel(GUIContent label, [DefaultValue("\"Button\"")] GUIStyle followingStyle) + { + PrefixLabel(label, followingStyle, EditorStyles.label); + } + + // Make a label in front of some control. + public static void PrefixLabel(GUIContent label, GUIStyle followingStyle, GUIStyle labelStyle) + { + PrefixLabelInternal(label, followingStyle, labelStyle); + } + + public static void LabelField(string label, params GUILayoutOption[] options) + { + LabelField(GUIContent.none, EditorGUIUtility.TempContent(label), EditorStyles.label, options); + } + + public static void LabelField(string label, GUIStyle style, params GUILayoutOption[] options) + { + LabelField(GUIContent.none, EditorGUIUtility.TempContent(label), style, options); + } + + public static void LabelField(GUIContent label, params GUILayoutOption[] options) + { + LabelField(GUIContent.none, label, EditorStyles.label, options); + } + + public static void LabelField(GUIContent label, GUIStyle style, params GUILayoutOption[] options) + { + LabelField(GUIContent.none, label, style, options); + } + + public static void LabelField(string label, string label2, params GUILayoutOption[] options) + { + LabelField(new GUIContent(label), EditorGUIUtility.TempContent(label2), EditorStyles.label, options); + } + + public static void LabelField(string label, string label2, GUIStyle style, params GUILayoutOption[] options) + { + LabelField(new GUIContent(label), EditorGUIUtility.TempContent(label2), style, options); + } + + public static void LabelField(GUIContent label, GUIContent label2, params GUILayoutOption[] options) + { + LabelField(label, label2, EditorStyles.label, options); + } + + // Make a label field. (Useful for showing read-only info.) + public static void LabelField(GUIContent label, GUIContent label2, GUIStyle style, params GUILayoutOption[] options) + { + if (!style.wordWrap) + { + // If we don't need word wrapping, just allocate the standard space to avoid corner case layout issues + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, options); + EditorGUI.LabelField(r, label, label2, style); + } + else + { + BeginHorizontal(); + PrefixLabel(label, style); + Rect r = GUILayoutUtility.GetRect(label2, style, options); + int oldIndent = EditorGUI.indentLevel; + EditorGUI.indentLevel = 0; + EditorGUI.LabelField(r, label2, style); + EditorGUI.indentLevel = oldIndent; + EndHorizontal(); + } + } + + public static bool LinkButton(string label, params GUILayoutOption[] options) + { + return LinkButton(EditorGUIUtility.TempContent(label), options); + } + + public static bool LinkButton(GUIContent label, params GUILayoutOption[] options) + { + var position = s_LastRect = GUILayoutUtility.GetRect(label, EditorStyles.linkLabel, options); + + Handles.color = EditorStyles.linkLabel.normal.textColor; + Handles.DrawLine(new Vector3(position.xMin + EditorStyles.linkLabel.padding.left, position.yMax), new Vector3(position.xMax - EditorStyles.linkLabel.padding.right, position.yMax)); + Handles.color = Color.white; + + EditorGUIUtility.AddCursorRect(position, MouseCursor.Link); + + return GUI.Button(position, label, EditorStyles.linkLabel); + } + + public static bool Toggle(bool value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetToggleRect(false, options); + return EditorGUI.Toggle(r, value); + } + + public static bool Toggle(string label, bool value, params GUILayoutOption[] options) + { + return Toggle(EditorGUIUtility.TempContent(label), value, options); + } + + public static bool Toggle(GUIContent label, bool value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetToggleRect(true, options); + return EditorGUI.Toggle(r, label, value); + } + + public static bool Toggle(bool value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetToggleRect(false, options); + return EditorGUI.Toggle(r, value, style); + } + + public static bool Toggle(string label, bool value, GUIStyle style, params GUILayoutOption[] options) + { + return Toggle(EditorGUIUtility.TempContent(label), value, style, options); + } + + // Make a toggle. + public static bool Toggle(GUIContent label, bool value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetToggleRect(true, options); + return EditorGUI.Toggle(r, label, value, style); + } + + public static bool ToggleLeft(string label, bool value, params GUILayoutOption[] options) + { + return ToggleLeft(EditorGUIUtility.TempContent(label), value, options); + } + + public static bool ToggleLeft(GUIContent label, bool value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, options); + return EditorGUI.ToggleLeft(r, label, value); + } + + public static bool ToggleLeft(string label, bool value, GUIStyle labelStyle, params GUILayoutOption[] options) + { + return ToggleLeft(EditorGUIUtility.TempContent(label), value, labelStyle, options); + } + + // Make a toggle with the label on the right. + public static bool ToggleLeft(GUIContent label, bool value, GUIStyle labelStyle, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, options); + return EditorGUI.ToggleLeft(r, label, value, labelStyle); + } + + public static string TextField(string text, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.textField, options); + return EditorGUI.TextField(r, text); + } + + public static string TextField(string text, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.TextField(r, text, style); + } + + public static string TextField(string label, string text, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.textField, options); + return EditorGUI.TextField(r, label, text); + } + + public static string TextField(string label, string text, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.TextField(r, label, text, style); + } + + public static string TextField(GUIContent label, string text, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.textField, options); + return EditorGUI.TextField(r, label, text); + } + + // Make a text field. + public static string TextField(GUIContent label, string text, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.TextField(r, label, text, style); + } + + public static string DelayedTextField(string text, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.textField, options); + return EditorGUI.DelayedTextField(r, text); + } + + public static string DelayedTextField(string text, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.DelayedTextField(r, text, style); + } + + public static string DelayedTextField(string label, string text, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.textField, options); + return EditorGUI.DelayedTextField(r, label, text); + } + + public static string DelayedTextField(string label, string text, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.DelayedTextField(r, label, text, style); + } + + public static string DelayedTextField(GUIContent label, string text, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.textField, options); + return EditorGUI.DelayedTextField(r, label, text); + } + + // Make a delayed text field. + public static string DelayedTextField(GUIContent label, string text, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.DelayedTextField(r, label, text, style); + } + + public static void DelayedTextField(SerializedProperty property, params GUILayoutOption[] options) + { + DelayedTextField(property, null, options); + } + + // Make a delayed text field. + internal static void DelayedTextField(SerializedProperty property, GUIContent label, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(EditorGUI.LabelHasContent(label), EditorGUI.kSingleLineHeight, EditorStyles.textField, options); + EditorGUI.DelayedTextFieldHelper(r, property, label, style); + } + + public static void DelayedTextField(SerializedProperty property, GUIContent label, params GUILayoutOption[] options) + { + DelayedTextField(property, label, EditorStyles.textField, options); + } + + internal static string ToolbarSearchField(string text, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GUILayoutUtility.GetRect(0, kLabelFloatMaxW * 1.5f, EditorGUI.kSingleLineHeight, EditorGUI.kSingleLineHeight, EditorStyles.toolbarSearchField, options); + int i = 0; + return EditorGUI.ToolbarSearchField(r, null, ref i, text); + } + + internal static string ToolbarSearchField(string text, string[] searchModes, ref int searchMode, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GUILayoutUtility.GetRect(0, kLabelFloatMaxW * 1.5f, EditorGUI.kSingleLineHeight, EditorGUI.kSingleLineHeight, EditorStyles.toolbarSearchField, options); + return EditorGUI.ToolbarSearchField(r, searchModes, ref searchMode, text); + } + + public static string TextArea(string text, params GUILayoutOption[] options) + { return TextArea(text, EditorStyles.textField, options); } + // Make a text area. + public static string TextArea(string text, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GUILayoutUtility.GetRect(EditorGUIUtility.TempContent(text), style, options); + return EditorGUI.TextArea(r, text, style); + } + + public static void SelectableLabel(string text, params GUILayoutOption[] options) + { + SelectableLabel(text, EditorStyles.label, options); + } + + // Make a selectable label field. (Useful for showing read-only info that can be copy-pasted.) + public static void SelectableLabel(string text, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight * 2, style, options); + EditorGUI.SelectableLabel(r, text, style); + } + + internal static Event KeyEventField(Event e, params GUILayoutOption[] options) + { + Rect r = GUILayoutUtility.GetRect(EditorGUI.s_PleasePressAKey, GUI.skin.textField, options); + return EditorGUI.KeyEventField(r, e); + } + + public static string PasswordField(string password, params GUILayoutOption[] options) + { + return PasswordField(password, EditorStyles.textField, options); + } + + public static string PasswordField(string password, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.PasswordField(r, password, style); + } + + public static string PasswordField(string label, string password, params GUILayoutOption[] options) + { + return PasswordField(EditorGUIUtility.TempContent(label), password, EditorStyles.textField, options); + } + + public static string PasswordField(string label, string password, GUIStyle style, params GUILayoutOption[] options) + { + return PasswordField(EditorGUIUtility.TempContent(label), password, style, options); + } + + public static string PasswordField(GUIContent label, string password, params GUILayoutOption[] options) + { + return PasswordField(label, password, EditorStyles.textField, options); + } + + // Make a text field where the user can enter a password. + public static string PasswordField(GUIContent label, string password, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.PasswordField(r, label, password, style); + } + + // Peak smoothing should be handled by client. Input: value and peak is normalized values (0 - 1). + internal static void VUMeterHorizontal(float value, float peak, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); + EditorGUI.VUMeter.HorizontalMeter(r, value, peak, EditorGUI.VUMeter.horizontalVUTexture, Color.grey); + } + + // Auto-smoothing of peak + internal static void VUMeterHorizontal(float value, ref EditorGUI.VUMeter.SmoothingData data, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); + EditorGUI.VUMeter.HorizontalMeter(r, value, ref data, EditorGUI.VUMeter.horizontalVUTexture, Color.grey); + } + + public static float FloatField(float value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); + return EditorGUI.FloatField(r, value); + } + + public static float FloatField(float value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.FloatField(r, value, style); + } + + public static float FloatField(string label, float value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); + return EditorGUI.FloatField(r, label, value); + } + + public static float FloatField(string label, float value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.FloatField(r, label, value, style); + } + + public static float FloatField(GUIContent label, float value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); + return EditorGUI.FloatField(r, label, value); + } + + // Make a text field for entering float values. + public static float FloatField(GUIContent label, float value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.FloatField(r, label, value, style); + } + + public static float DelayedFloatField(float value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); + return EditorGUI.DelayedFloatField(r, value); + } + + public static float DelayedFloatField(float value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.DelayedFloatField(r, value, style); + } + + public static float DelayedFloatField(string label, float value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); + return EditorGUI.DelayedFloatField(r, label, value); + } + + public static float DelayedFloatField(string label, float value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.DelayedFloatField(r, label, value, style); + } + + public static float DelayedFloatField(GUIContent label, float value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); + return EditorGUI.DelayedFloatField(r, label, value); + } + + // Make a delayed text field for entering float values. + public static float DelayedFloatField(GUIContent label, float value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.DelayedFloatField(r, label, value, style); + } + + public static void DelayedFloatField(SerializedProperty property, params GUILayoutOption[] options) + { + DelayedFloatField(property, null, options); + } + + // Make a delayed text field for entering float values. + public static void DelayedFloatField(SerializedProperty property, GUIContent label, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(EditorGUI.LabelHasContent(label), EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); + EditorGUI.DelayedFloatField(r, property, label); + } + + public static double DoubleField(double value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); + return EditorGUI.DoubleField(r, value); + } + + public static double DoubleField(double value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.DoubleField(r, value, style); + } + + public static double DoubleField(string label, double value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); + return EditorGUI.DoubleField(r, label, value); + } + + public static double DoubleField(string label, double value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.DoubleField(r, label, value, style); + } + + public static double DoubleField(GUIContent label, double value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); + return EditorGUI.DoubleField(r, label, value); + } + + // Make a text field for entering double values. + public static double DoubleField(GUIContent label, double value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.DoubleField(r, label, value, style); + } + + public static double DelayedDoubleField(double value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); + return EditorGUI.DelayedDoubleField(r, value); + } + + public static double DelayedDoubleField(double value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.DelayedDoubleField(r, value, style); + } + + public static double DelayedDoubleField(string label, double value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); + return EditorGUI.DelayedDoubleField(r, label, value); + } + + public static double DelayedDoubleField(string label, double value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.DelayedDoubleField(r, label, value, style); + } + + public static double DelayedDoubleField(GUIContent label, double value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); + return EditorGUI.DelayedDoubleField(r, label, value); + } + + // Make a delayed text field for entering double values. + public static double DelayedDoubleField(GUIContent label, double value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.DelayedDoubleField(r, label, value, style); + } + + public static int IntField(int value, params GUILayoutOption[] options) + { + return IntField(value, EditorStyles.numberField, options); + } + + public static int IntField(int value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.IntField(r, value, style); + } + + public static int IntField(string label, int value, params GUILayoutOption[] options) + { + return IntField(label, value, EditorStyles.numberField, options); + } + + public static int IntField(string label, int value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.IntField(r, label, value, style); + } + + public static int IntField(GUIContent label, int value, params GUILayoutOption[] options) + { + return IntField(label, value, EditorStyles.numberField, options); + } + + // Make a text field for entering integers. + public static int IntField(GUIContent label, int value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.IntField(r, label, value, style); + } + + public static int DelayedIntField(int value, params GUILayoutOption[] options) + { + return DelayedIntField(value, EditorStyles.numberField, options); + } + + public static int DelayedIntField(int value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.DelayedIntField(r, value, style); + } + + public static int DelayedIntField(string label, int value, params GUILayoutOption[] options) + { + return DelayedIntField(label, value, EditorStyles.numberField, options); + } + + public static int DelayedIntField(string label, int value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.DelayedIntField(r, label, value, style); + } + + public static int DelayedIntField(GUIContent label, int value, params GUILayoutOption[] options) + { + return DelayedIntField(label, value, EditorStyles.numberField, options); + } + + // Make a text field for entering integers. + public static int DelayedIntField(GUIContent label, int value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.DelayedIntField(r, label, value, style); + } + + public static void DelayedIntField(SerializedProperty property, params GUILayoutOption[] options) + { + DelayedIntField(property, null, options); + } + + // Make a text field for entering integers. + public static void DelayedIntField(SerializedProperty property, GUIContent label, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(EditorGUI.LabelHasContent(label), EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); + EditorGUI.DelayedIntField(r, property, label); + } + + public static long LongField(long value, params GUILayoutOption[] options) + { + return LongField(value, EditorStyles.numberField, options); + } + + public static long LongField(long value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.LongField(r, value, style); + } + + public static long LongField(string label, long value, params GUILayoutOption[] options) + { + return LongField(label, value, EditorStyles.numberField, options); + } + + public static long LongField(string label, long value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.LongField(r, label, value, style); + } + + public static long LongField(GUIContent label, long value, params GUILayoutOption[] options) + { + return LongField(label, value, EditorStyles.numberField, options); + } + + // Make a text field for entering integers. + public static long LongField(GUIContent label, long value, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.LongField(r, label, value, style); + } + + public static float Slider(float value, float leftValue, float rightValue, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetSliderRect(false, options); + return EditorGUI.Slider(r, value, leftValue, rightValue); + } + + public static float Slider(string label, float value, float leftValue, float rightValue, params GUILayoutOption[] options) + { + return Slider(EditorGUIUtility.TempContent(label), value, leftValue, rightValue, options); + } + + // Make a slider the user can drag to change a value between a min and a max. + public static float Slider(GUIContent label, float value, float leftValue, float rightValue, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetSliderRect(true, options); + return EditorGUI.Slider(r, label, value, leftValue, rightValue); + } + + internal static float Slider(GUIContent label, float value, float sliderLeftValue, float sliderRightValue, float textLeftValue, float textRightValue, params GUILayoutOption[] options) + { + return Slider(label, value, sliderLeftValue, sliderRightValue, textLeftValue, textRightValue, EditorStyles.numberField, + GUI.skin.horizontalSlider, GUI.skin.horizontalSliderThumb, null, GUI.skin.horizontalSliderThumbExtent, options); + } + + static void GetSliderParts(GUIStyle baseStyle, ref GUIStyle textFieldStyle, ref GUIStyle thumbStyle, ref GUIStyle thumbExtentStyle) + { + string baseName = baseStyle.name; + thumbStyle = GUI.skin.FindStyle(baseName + "Thumb") ?? thumbStyle; + thumbExtentStyle = GUI.skin.FindStyle(baseName + "ThumbExtent") ?? thumbExtentStyle; + textFieldStyle = GUI.skin.FindStyle(baseName + "TextField") ?? textFieldStyle; + } + + static void GetHorizontalSliderParts(GUIStyle baseStyle, out GUIStyle textFieldStyle, out GUIStyle thumbStyle, out GUIStyle thumbExtentStyle) + { + thumbStyle = GUI.skin.horizontalSliderThumb; + thumbExtentStyle = GUI.skin.horizontalSliderThumbExtent; + textFieldStyle = EditorStyles.numberField; + + GetSliderParts(baseStyle, ref textFieldStyle, ref thumbStyle, ref thumbExtentStyle); + } + + static void GetVerticalSliderParts(GUIStyle baseStyle, out GUIStyle textFieldStyle, out GUIStyle thumbStyle, out GUIStyle thumbExtentStyle) + { + thumbStyle = GUI.skin.verticalSliderThumb; + thumbExtentStyle = GUI.skin.verticalSliderThumbExtent; + textFieldStyle = EditorStyles.numberField; + + GetSliderParts(baseStyle, ref textFieldStyle, ref thumbStyle, ref thumbExtentStyle); + } + + internal static float Slider(GUIContent label, float value, float sliderLeftValue, float sliderRightValue, float textLeftValue, float textRightValue, GUIStyle sliderStyle, params GUILayoutOption[] options) + { + GUIStyle sliderThumbStyle, sliderThumbStyleExtent, sliderTextFieldStyle; + + GetHorizontalSliderParts(sliderStyle, out sliderTextFieldStyle, out sliderThumbStyle, out sliderThumbStyleExtent); + + return Slider(label, value, sliderLeftValue, sliderRightValue, textLeftValue, textRightValue, sliderTextFieldStyle, sliderStyle + , sliderThumbStyle, null, sliderThumbStyleExtent); + } + + internal static float Slider(GUIContent label, float value, float sliderLeftValue, float sliderRightValue, float textLeftValue, float textRightValue + , GUIStyle sliderTextField, GUIStyle sliderStyle, GUIStyle sliderThumbStyle, Texture2D sliderBackground, GUIStyle sliderThumbStyleExtent, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetSliderRect(true, sliderStyle, options); + return EditorGUI.Slider(r, label, value, sliderLeftValue, sliderRightValue, textLeftValue, textRightValue, sliderTextField, sliderStyle, sliderThumbStyle, sliderBackground, sliderThumbStyleExtent); + } + + public static void Slider(SerializedProperty property, float leftValue, float rightValue, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetSliderRect(false, options); + EditorGUI.Slider(r, property, leftValue, rightValue); + } + + public static void Slider(SerializedProperty property, float leftValue, float rightValue, string label, params GUILayoutOption[] options) + { + Slider(property, leftValue, rightValue, EditorGUIUtility.TempContent(label), options); + } + + // Make a slider the user can drag to change a value between a min and a max. + public static void Slider(SerializedProperty property, float leftValue, float rightValue, GUIContent label, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetSliderRect(true, options); + EditorGUI.Slider(r, property, leftValue, rightValue, label); + } + + internal static void Slider(SerializedProperty property, float sliderLeftValue, float sliderRightValue, float textLeftValue, float textRightValue, GUIContent label, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetSliderRect(true, options); + EditorGUI.Slider(r, property, sliderLeftValue, sliderRightValue, textLeftValue, textRightValue, label); + } + + internal static float PowerSlider(string label, float value, float leftValue, float rightValue, float power, params GUILayoutOption[] options) + { + return PowerSlider(EditorGUIUtility.TempContent(label), value, leftValue, rightValue, power, options); + } + + // Make a power slider the user can drag to change a value between a min and a max. + internal static float PowerSlider(GUIContent label, float value, float leftValue, float rightValue, float power, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetSliderRect(true, options); + return EditorGUI.PowerSlider(r, label, value, leftValue, rightValue, power); + } + + internal static int LogarithmicIntSlider(string label, int value, int leftValue, int rightValue, int logbase, int textFieldMin, int textFieldMax, params GUILayoutOption[] options) + { + return LogarithmicIntSlider(EditorGUIUtility.TempContent(label), value, leftValue, rightValue, logbase, textFieldMin, textFieldMax, options); + } + + internal static int LogarithmicIntSlider(GUIContent label, int value, int leftValue, int rightValue, int logbase, int textFieldMin, int textFieldMax, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetSliderRect(true, options); + return EditorGUI.LogarithmicIntSlider(r, label, value, leftValue, rightValue, logbase, textFieldMin, textFieldMax); + } + + public static int IntSlider(int value, int leftValue, int rightValue, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetSliderRect(false, options); + return EditorGUI.IntSlider(r, value, leftValue, rightValue); + } + + internal static int IntSlider(int value, int leftValue, int rightValue, float power, GUIStyle sliderStyle, params GUILayoutOption[] options) + { + GUIStyle sliderThumbStyle, sliderThumbStyleExtent, sliderTextFieldStyle; + + GetHorizontalSliderParts(sliderStyle, out sliderTextFieldStyle, out sliderThumbStyle, out sliderThumbStyleExtent); + + return IntSlider(value, leftValue, rightValue, power, sliderTextFieldStyle, sliderStyle, sliderThumbStyle, null, sliderThumbStyleExtent, options); + } + + internal static int IntSlider(int value, int leftValue, int rightValue, float power, + GUIStyle textfieldStyle, GUIStyle sliderStyle, GUIStyle thumbStyle, Texture2D sliderBackground, GUIStyle thumbStyleExtent, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetSliderRect(false, sliderStyle, options); + return EditorGUI.IntSlider(r, value, leftValue, rightValue, power, logbase: 1, textfieldStyle, sliderStyle, thumbStyle, sliderBackground, thumbStyleExtent); + } + + public static int IntSlider(string label, int value, int leftValue, int rightValue, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetSliderRect(true, options); + return EditorGUI.IntSlider(r, label, value, leftValue, rightValue); + } + + // Make a slider the user can drag to change an integer value between a min and a max. + public static int IntSlider(GUIContent label, int value, int leftValue, int rightValue, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetSliderRect(true, options); + return EditorGUI.IntSlider(r, label, value, leftValue, rightValue); + } + + public static void IntSlider(SerializedProperty property, int leftValue, int rightValue, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetSliderRect(false, options); + EditorGUI.IntSlider(r, property, leftValue, rightValue, property.displayName); + } + + public static void IntSlider(SerializedProperty property, int leftValue, int rightValue, string label, params GUILayoutOption[] options) + { + IntSlider(property, leftValue, rightValue, EditorGUIUtility.TempContent(label), options); + } + + // Make a slider the user can drag to change an integer value between a min and a max. + public static void IntSlider(SerializedProperty property, int leftValue, int rightValue, GUIContent label, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetSliderRect(true, options); + EditorGUI.IntSlider(r, property, leftValue, rightValue, label); + } + + public static void MinMaxSlider(ref float minValue, ref float maxValue, float minLimit, float maxLimit, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetSliderRect(false, options); + EditorGUI.MinMaxSlider(r, ref minValue, ref maxValue, minLimit, maxLimit); + } + + public static void MinMaxSlider(string label, ref float minValue, ref float maxValue, float minLimit, float maxLimit, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetSliderRect(true, options); + EditorGUI.MinMaxSlider(r, label, ref minValue, ref maxValue, minLimit, maxLimit); + } + + // Make a special slider the user can use to specify a range between a min and a max. + public static void MinMaxSlider(GUIContent label, ref float minValue, ref float maxValue, float minLimit, float maxLimit, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetSliderRect(true, options); + EditorGUI.MinMaxSlider(r, label, ref minValue, ref maxValue, minLimit, maxLimit); + } + + public static int Popup(int selectedIndex, string[] displayedOptions, params GUILayoutOption[] options) + { + return Popup(selectedIndex, displayedOptions, EditorStyles.popup, options); + } + + public static int Popup(int selectedIndex, string[] displayedOptions, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.Popup(r, selectedIndex, displayedOptions, style); + } + + public static int Popup(int selectedIndex, GUIContent[] displayedOptions, params GUILayoutOption[] options) + { + return Popup(selectedIndex, displayedOptions, EditorStyles.popup, options); + } + + public static int Popup(int selectedIndex, GUIContent[] displayedOptions, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.Popup(r, selectedIndex, displayedOptions, style); + } + + public static int Popup(string label, int selectedIndex, string[] displayedOptions, params GUILayoutOption[] options) + { + return Popup(label, selectedIndex, displayedOptions, EditorStyles.popup, options); + } + + public static int Popup(GUIContent label, int selectedIndex, string[] displayedOptions, params GUILayoutOption[] options) + { + return Popup(label, selectedIndex, displayedOptions, EditorStyles.popup, options); + } + + public static int Popup(string label, int selectedIndex, string[] displayedOptions, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.Popup(r, label, selectedIndex, displayedOptions, style); + } + + public static int Popup(GUIContent label, int selectedIndex, GUIContent[] displayedOptions, params GUILayoutOption[] options) + { + return Popup(label, selectedIndex, displayedOptions, EditorStyles.popup, options); + } + + // Make a generic popup selection field. + public static int Popup(GUIContent label, int selectedIndex, GUIContent[] displayedOptions, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.Popup(r, label, selectedIndex, displayedOptions, style); + } + + internal static int Popup(GUIContent label, int selectedIndex, string[] displayedOptions, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.Popup(r, label, selectedIndex, displayedOptions, style); + } + + internal static void Popup(SerializedProperty property, GUIContent[] displayedOptions, params GUILayoutOption[] options) + { + Popup(property, displayedOptions, null, options); + } + + internal static void Popup(SerializedProperty property, GUIContent[] displayedOptions, GUIContent label, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.popup, options); + EditorGUI.Popup(r, property, displayedOptions, label); + } + + public static Enum EnumPopup(Enum selected, params GUILayoutOption[] options) + { + return EnumPopup(selected, EditorStyles.popup, options); + } + + public static Enum EnumPopup(Enum selected, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.EnumPopup(r, selected, style); + } + + public static Enum EnumPopup(string label, Enum selected, params GUILayoutOption[] options) + { + return EnumPopup(label, selected, EditorStyles.popup, options); + } + + public static Enum EnumPopup(string label, Enum selected, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.EnumPopup(r, GUIContent.Temp(label), selected, null, false, style); + } + + public static Enum EnumPopup(GUIContent label, Enum selected, params GUILayoutOption[] options) + { + return EnumPopup(label, selected, EditorStyles.popup, options); + } + + // Make an enum popup selection field. + public static Enum EnumPopup(GUIContent label, Enum selected, GUIStyle style, params GUILayoutOption[] options) + { + return EnumPopup(label, selected, null, false, style, options); + } + + public static Enum EnumPopup(GUIContent label, Enum selected, Func checkEnabled, bool includeObsolete, params GUILayoutOption[] options) + { + return EnumPopup(label, selected, checkEnabled, includeObsolete, EditorStyles.popup, options); + } + + public static Enum EnumPopup(GUIContent label, Enum selected, Func checkEnabled, bool includeObsolete, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.EnumPopup(r, label, selected, checkEnabled, includeObsolete, style); + } + + public static int IntPopup(int selectedValue, string[] displayedOptions, int[] optionValues, params GUILayoutOption[] options) + { + return IntPopup(selectedValue, displayedOptions, optionValues, EditorStyles.popup, options); + } + + public static int IntPopup(int selectedValue, string[] displayedOptions, int[] optionValues, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.IntPopup(r, selectedValue, displayedOptions, optionValues, style); + } + + public static int IntPopup(int selectedValue, GUIContent[] displayedOptions, int[] optionValues, params GUILayoutOption[] options) + { + return IntPopup(selectedValue, displayedOptions, optionValues, EditorStyles.popup, options); + } + + public static int IntPopup(int selectedValue, GUIContent[] displayedOptions, int[] optionValues, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.IntPopup(r, GUIContent.none, selectedValue, displayedOptions, optionValues, style); + } + + public static int IntPopup(string label, int selectedValue, string[] displayedOptions, int[] optionValues, params GUILayoutOption[] options) + { + return IntPopup(label, selectedValue, displayedOptions, optionValues, EditorStyles.popup, options); + } + + public static int IntPopup(string label, int selectedValue, string[] displayedOptions, int[] optionValues, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.IntPopup(r, label, selectedValue, displayedOptions, optionValues, style); + } + + public static int IntPopup(GUIContent label, int selectedValue, GUIContent[] displayedOptions, int[] optionValues, params GUILayoutOption[] options) + { + return IntPopup(label, selectedValue, displayedOptions, optionValues, EditorStyles.popup, options); + } + + // Make an integer popup selection field. + public static int IntPopup(GUIContent label, int selectedValue, GUIContent[] displayedOptions, int[] optionValues, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.IntPopup(r, label, selectedValue, displayedOptions, optionValues, style); + } + + public static void IntPopup(SerializedProperty property, GUIContent[] displayedOptions, int[] optionValues, params GUILayoutOption[] options) + { + IntPopup(property, displayedOptions, optionValues, null, options); + } + + // Make an integer popup selection field. + public static void IntPopup(SerializedProperty property, GUIContent[] displayedOptions, int[] optionValues, GUIContent label, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.popup, options); + EditorGUI.IntPopup(r, property, displayedOptions, optionValues, label); + } + + [Obsolete("This function is obsolete and the style is not used.")] + public static void IntPopup(SerializedProperty property, GUIContent[] displayedOptions, int[] optionValues, GUIContent label, GUIStyle style, params GUILayoutOption[] options) + { + IntPopup(property, displayedOptions, optionValues, label, options); + } + + public static string TagField(string tag, params GUILayoutOption[] options) + { + return TagField(tag, EditorStyles.popup, options); + } + + public static string TagField(string tag, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.TagField(r, tag, style); + } + + public static string TagField(string label, string tag, params GUILayoutOption[] options) + { + return TagField(EditorGUIUtility.TempContent(label), tag, EditorStyles.popup, options); + } + + public static string TagField(string label, string tag, GUIStyle style, params GUILayoutOption[] options) + { + return TagField(EditorGUIUtility.TempContent(label), tag, style, options); + } + + public static string TagField(GUIContent label, string tag, params GUILayoutOption[] options) + { + return TagField(label, tag, EditorStyles.popup, options); + } + + // Make a tag selection field. + public static string TagField(GUIContent label, string tag, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.TagField(r, label, tag, style); + } + + public static int LayerField(int layer, params GUILayoutOption[] options) + { + return LayerField(layer, EditorStyles.popup, options); + } + + public static int LayerField(int layer, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.LayerField(r, layer, style); + } + + public static int LayerField(string label, int layer, params GUILayoutOption[] options) + { + return LayerField(EditorGUIUtility.TempContent(label), layer, EditorStyles.popup, options); + } + + public static int LayerField(string label, int layer, GUIStyle style, params GUILayoutOption[] options) + { + return LayerField(EditorGUIUtility.TempContent(label), layer, style, options); + } + + public static int LayerField(GUIContent label, int layer, params GUILayoutOption[] options) + { + return LayerField(label, layer, EditorStyles.popup, options); + } + + // Make a layer selection field. + public static int LayerField(GUIContent label, int layer, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.LayerField(r, label, layer, style); + } + + public static int MaskField(GUIContent label, int mask, string[] displayedOptions, GUIStyle style, params GUILayoutOption[] options) + { + var r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.MaskField(r, label, mask, displayedOptions, style); + } + + public static int MaskField(string label, int mask, string[] displayedOptions, GUIStyle style, params GUILayoutOption[] options) + { + var r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.MaskField(r, label, mask, displayedOptions, style); + } + + public static int MaskField(GUIContent label, int mask, string[] displayedOptions, params GUILayoutOption[] options) + { + var r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.popup, options); + return EditorGUI.MaskField(r, label, mask, displayedOptions, EditorStyles.popup); + } + + public static int MaskField(string label, int mask, string[] displayedOptions, params GUILayoutOption[] options) + { + var r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.popup, options); + return EditorGUI.MaskField(r, label, mask, displayedOptions, EditorStyles.popup); + } + + public static int MaskField(int mask, string[] displayedOptions, GUIStyle style, params GUILayoutOption[] options) + { + var r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.MaskField(r, mask, displayedOptions, style); + } + + // Make a field for masks. + public static int MaskField(int mask, string[] displayedOptions, params GUILayoutOption[] options) + { + var r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.popup, options); + return EditorGUI.MaskField(r, mask, displayedOptions, EditorStyles.popup); + } + + public static Enum EnumFlagsField(Enum enumValue, params GUILayoutOption[] options) + { + return EnumFlagsField(enumValue, EditorStyles.popup, options); + } + + public static Enum EnumFlagsField(Enum enumValue, GUIStyle style, params GUILayoutOption[] options) + { + var position = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.EnumFlagsField(position, enumValue, style); + } + + public static Enum EnumFlagsField(string label, Enum enumValue, params GUILayoutOption[] options) + { + return EnumFlagsField(label, enumValue, EditorStyles.popup, options); + } + + public static Enum EnumFlagsField(string label, Enum enumValue, GUIStyle style, params GUILayoutOption[] options) + { + return EnumFlagsField(EditorGUIUtility.TempContent(label), enumValue, style, options); + } + + public static Enum EnumFlagsField(GUIContent label, Enum enumValue, params GUILayoutOption[] options) + { + return EnumFlagsField(label, enumValue, EditorStyles.popup, options); + } + + public static Enum EnumFlagsField(GUIContent label, Enum enumValue, GUIStyle style, params GUILayoutOption[] options) + { + var position = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.EnumFlagsField(position, label, enumValue, style); + } + + public static Enum EnumFlagsField(GUIContent label, Enum enumValue, bool includeObsolete, params GUILayoutOption[] options) + { + return EnumFlagsField(label, enumValue, includeObsolete, EditorStyles.popup, options); + } + + public static Enum EnumFlagsField(GUIContent label, Enum enumValue, bool includeObsolete, GUIStyle style, params GUILayoutOption[] options) + { + var position = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.EnumFlagsField(position, label, enumValue, includeObsolete, style); + } + + [Obsolete("Check the docs for the usage of the new parameter 'allowSceneObjects'.")] + public static Object ObjectField(Object obj, Type objType, params GUILayoutOption[] options) + { + return ObjectField(obj, objType, true, options); + } + + public static Object ObjectField(Object obj, Type objType, Object targetBeingEdited, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, options); + return EditorGUI.ObjectField(r, obj, objType, targetBeingEdited); + } + + public static Object ObjectField(Object obj, Type objType, bool allowSceneObjects, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, options); + return EditorGUI.ObjectField(r, obj, objType, allowSceneObjects); + } + + [Obsolete("Check the docs for the usage of the new parameter 'allowSceneObjects'.")] + public static Object ObjectField(string label, Object obj, Type objType, params GUILayoutOption[] options) + { + return ObjectField(label, obj, objType, true, options); + } + + public static Object ObjectField(string label, Object obj, Type objType, Object targetBeingEdited, params GUILayoutOption[] options) + { + return ObjectField(EditorGUIUtility.TempContent(label), obj, objType, targetBeingEdited, options); + } + + public static Object ObjectField(string label, Object obj, Type objType, bool allowSceneObjects, params GUILayoutOption[] options) + { + return ObjectField(EditorGUIUtility.TempContent(label), obj, objType, allowSceneObjects, options); + } + + [Obsolete("Check the docs for the usage of the new parameter 'allowSceneObjects'.")] + public static Object ObjectField(GUIContent label, Object obj, Type objType, params GUILayoutOption[] options) + { + return ObjectField(label, obj, objType, true, options); + } + + // Make an object field. You can assign objects either by drag'n drop objects or by selecting an object using the Object Picker. + public static Object ObjectField(GUIContent label, Object obj, Type objType, Object targetBeingEdited, params GUILayoutOption[] options) + { + var height = EditorGUIUtility.HasObjectThumbnail(objType) ? EditorGUI.kObjectFieldThumbnailHeight : EditorGUI.kSingleLineHeight; + Rect r = s_LastRect = GetControlRect(true, height, options); + return EditorGUI.ObjectField(r, label, obj, objType, targetBeingEdited); + } + + // Make an object field. You can assign objects either by drag'n drop objects or by selecting an object using the Object Picker. + public static Object ObjectField(GUIContent label, Object obj, Type objType, bool allowSceneObjects, params GUILayoutOption[] options) + { + var height = EditorGUIUtility.HasObjectThumbnail(objType) ? EditorGUI.kObjectFieldThumbnailHeight : EditorGUI.kSingleLineHeight; + Rect r = s_LastRect = GetControlRect(true, height, options); + return EditorGUI.ObjectField(r, label, obj, objType, allowSceneObjects); + } + + public static void ObjectField(SerializedProperty property, params GUILayoutOption[] options) + { + ObjectField(property, (GUIContent)null, options); + } + + public static void ObjectField(SerializedProperty property, GUIContent label, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.objectField, options); + EditorGUI.ObjectField(r, property, label); + } + + public static void ObjectField(SerializedProperty property, Type objType, params GUILayoutOption[] options) + { + ObjectField(property, objType, null, options); + } + + // Make an object field. You can assign objects either by drag'n drop objects or by selecting an object using the Object Picker. + public static void ObjectField(SerializedProperty property, Type objType, GUIContent label, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.objectField, options); + EditorGUI.ObjectField(r, property, objType, label); + } + + internal static void ObjectField(SerializedProperty property, Type objType, GUIContent label, EditorGUI.ObjectFieldValidator validator, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.objectField, options); + EditorGUI.ObjectField(r, property, objType, label, EditorStyles.objectField, validator); + } + + internal static Object MiniThumbnailObjectField(GUIContent label, Object obj, Type objType, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, options); + return EditorGUI.MiniThumbnailObjectField(r, label, obj, objType); + } + + public static Vector2 Vector2Field(string label, Vector2 value, params GUILayoutOption[] options) + { + return Vector2Field(EditorGUIUtility.TempContent(label), value, options); + } + + // Make an X & Y field for entering a [[Vector2]]. + public static Vector2 Vector2Field(GUIContent label, Vector2 value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.GetPropertyHeight(SerializedPropertyType.Vector2, label), EditorStyles.numberField, options); + return EditorGUI.Vector2Field(r, label, value); + } + + public static Vector3 Vector3Field(string label, Vector3 value, params GUILayoutOption[] options) + { + return Vector3Field(EditorGUIUtility.TempContent(label), value, options); + } + + // Make an X, Y & Z field for entering a [[Vector3]]. + public static Vector3 Vector3Field(GUIContent label, Vector3 value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.GetPropertyHeight(SerializedPropertyType.Vector3, label), EditorStyles.numberField, options); + return EditorGUI.Vector3Field(r, label, value); + } + + // Make an X, Y & Z field for entering a [[Vector3]], with a "lock" + internal static Vector3 LinkedVector3Field(GUIContent label, Vector3 value, ref bool proportionalScale, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.GetPropertyHeight(SerializedPropertyType.Vector3, label), EditorStyles.numberField, options); + return EditorGUI.LinkedVector3Field(r, label, value, ref proportionalScale); + } + + // Make an X, Y & Z field for entering a [[Vector3]], with a "lock" + internal static Vector3 LinkedVector3Field(GUIContent label, Vector3 value, Vector3 initialValue, ref bool proportionalScale, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.GetPropertyHeight(SerializedPropertyType.Vector3, label), EditorStyles.numberField, options); + int axisModified = 0;// Use X as default modified axis + return EditorGUI.LinkedVector3Field(r, label, GUIContent.none, value, ref proportionalScale, initialValue, 0, ref axisModified, null); + } + + // Make an X, Y, Z & W field for entering a [[Vector4]]. + public static Vector4 Vector4Field(string label, Vector4 value, params GUILayoutOption[] options) + { + return Vector4Field(EditorGUIUtility.TempContent(label), value, options); + } + + // Make an X, Y, Z & W field for entering a [[Vector4]]. + public static Vector4 Vector4Field(GUIContent label, Vector4 value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.GetPropertyHeight(SerializedPropertyType.Vector4, label), EditorStyles.numberField, options); + return EditorGUI.Vector4Field(r, label, value); + } + + public static Vector2Int Vector2IntField(string label, Vector2Int value, params GUILayoutOption[] options) + { + return Vector2IntField(EditorGUIUtility.TempContent(label), value, options); + } + + // Make an X & Y field for entering a [[Vector2Int]]. + public static Vector2Int Vector2IntField(GUIContent label, Vector2Int value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.GetPropertyHeight(SerializedPropertyType.Vector2Int, label), EditorStyles.numberField, options); + return EditorGUI.Vector2IntField(r, label, value); + } + + public static Vector3Int Vector3IntField(string label, Vector3Int value, params GUILayoutOption[] options) + { + return Vector3IntField(EditorGUIUtility.TempContent(label), value, options); + } + + // Make an X, Y & Z field for entering a [[Vector3Int]]. + public static Vector3Int Vector3IntField(GUIContent label, Vector3Int value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.GetPropertyHeight(SerializedPropertyType.Vector3Int, label), EditorStyles.numberField, options); + return EditorGUI.Vector3IntField(r, label, value); + } + + public static Rect RectField(Rect value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.GetPropertyHeight(SerializedPropertyType.Rect, GUIContent.none), EditorStyles.numberField, options); + return EditorGUI.RectField(r, value); + } + + public static Rect RectField(string label, Rect value, params GUILayoutOption[] options) + { + return RectField(EditorGUIUtility.TempContent(label), value, options); + } + + // Make an X, Y, W & H field for entering a [[Rect]]. + public static Rect RectField(GUIContent label, Rect value, params GUILayoutOption[] options) + { + bool hasLabel = EditorGUI.LabelHasContent(label); + float height = EditorGUI.GetPropertyHeight(SerializedPropertyType.Rect, label); + Rect r = s_LastRect = GetControlRect(hasLabel, height, EditorStyles.numberField, options); + return EditorGUI.RectField(r, label, value); + } + + public static RectInt RectIntField(RectInt value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.GetPropertyHeight(SerializedPropertyType.RectInt, GUIContent.none), EditorStyles.numberField, options); + return EditorGUI.RectIntField(r, value); + } + + public static RectInt RectIntField(string label, RectInt value, params GUILayoutOption[] options) + { + return RectIntField(EditorGUIUtility.TempContent(label), value, options); + } + + // Make an X, Y, W & H field for entering a [[RectInt]]. + public static RectInt RectIntField(GUIContent label, RectInt value, params GUILayoutOption[] options) + { + bool hasLabel = EditorGUI.LabelHasContent(label); + float height = EditorGUI.GetPropertyHeight(SerializedPropertyType.RectInt, label); + Rect r = s_LastRect = GetControlRect(hasLabel, height, EditorStyles.numberField, options); + return EditorGUI.RectIntField(r, label, value); + } + + public static Bounds BoundsField(Bounds value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.GetPropertyHeight(SerializedPropertyType.Bounds, GUIContent.none), EditorStyles.numberField, options); + return EditorGUI.BoundsField(r, value); + } + + public static Bounds BoundsField(string label, Bounds value, params GUILayoutOption[] options) + { + return BoundsField(EditorGUIUtility.TempContent(label), value, options); + } + + // Make Center & Extents field for entering a [[Bounds]]. + public static Bounds BoundsField(GUIContent label, Bounds value, params GUILayoutOption[] options) + { + bool hasLabel = EditorGUI.LabelHasContent(label); + float height = EditorGUI.GetPropertyHeight(SerializedPropertyType.Bounds, label); + Rect r = s_LastRect = GetControlRect(hasLabel, height, EditorStyles.numberField, options); + return EditorGUI.BoundsField(r, label, value); + } + + public static BoundsInt BoundsIntField(BoundsInt value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.GetPropertyHeight(SerializedPropertyType.BoundsInt, GUIContent.none), EditorStyles.numberField, options); + return EditorGUI.BoundsIntField(r, value); + } + + public static BoundsInt BoundsIntField(string label, BoundsInt value, params GUILayoutOption[] options) + { + return BoundsIntField(EditorGUIUtility.TempContent(label), value, options); + } + + // Make Center & Extents field for entering a [[BoundsInt]]. + public static BoundsInt BoundsIntField(GUIContent label, BoundsInt value, params GUILayoutOption[] options) + { + bool hasLabel = EditorGUI.LabelHasContent(label); + float height = EditorGUI.GetPropertyHeight(SerializedPropertyType.BoundsInt, label); + Rect r = s_LastRect = GetControlRect(hasLabel, height, EditorStyles.numberField, options); + return EditorGUI.BoundsIntField(r, label, value); + } + + // Make a property field that look like a multi property field (but is made up of individual properties) + internal static void PropertiesField(GUIContent label, SerializedProperty[] properties, GUIContent[] propertyLabels, float propertyLabelsWidth, params GUILayoutOption[] options) + { + bool hasLabel = EditorGUI.LabelHasContent(label); + float height = EditorGUI.kSingleLineHeight * properties.Length + EditorGUI.kVerticalSpacingMultiField * (properties.Length - 1); + Rect r = s_LastRect = GetControlRect(hasLabel, height, EditorStyles.numberField, options); + EditorGUI.PropertiesField(r, label, properties, propertyLabels, propertyLabelsWidth); + } + + internal static int CycleButton(int selected, GUIContent[] contents, GUIStyle style, params GUILayoutOption[] options) + { + if (GUILayout.Button(contents[selected], style, options)) + { + selected++; + if (selected >= contents.Length) + selected = 0; + } + return selected; + } + + public static Color ColorField(Color value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); + return EditorGUI.ColorField(r, value); + } + + public static Color ColorField(string label, Color value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); + return EditorGUI.ColorField(r, label, value); + } + + // Make a field for selecting a [[Color]]. + public static Color ColorField(GUIContent label, Color value, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); + return EditorGUI.ColorField(r, label, value); + } + +#pragma warning disable 612 + [Obsolete("Use EditorGUILayout.ColorField(GUIContent label, Color value, bool showEyedropper, bool showAlpha, bool hdr, params GUILayoutOption[] options)")] + public static Color ColorField( + GUIContent label, Color value, bool showEyedropper, bool showAlpha, bool hdr, ColorPickerHDRConfig hdrConfig, params GUILayoutOption[] options + ) + { + return ColorField(label, value, showEyedropper, showAlpha, hdr); + } + +#pragma warning restore 612 + + public static Color ColorField(GUIContent label, Color value, bool showEyedropper, bool showAlpha, bool hdr, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); + return EditorGUI.ColorField(r, label, value, showEyedropper, showAlpha, hdr); + } + + public static AnimationCurve CurveField(AnimationCurve value, params GUILayoutOption[] options) + { + // TODO Change style + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); + return EditorGUI.CurveField(r, value); + } + + public static AnimationCurve CurveField(string label, AnimationCurve value, params GUILayoutOption[] options) + { + // TODO Change style + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); + return EditorGUI.CurveField(r, label, value); + } + + public static AnimationCurve CurveField(GUIContent label, AnimationCurve value, params GUILayoutOption[] options) + { + // TODO Change style + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); + return EditorGUI.CurveField(r, label, value); + } + + // Variants with settings + public static AnimationCurve CurveField(AnimationCurve value, Color color, Rect ranges, params GUILayoutOption[] options) + { + // TODO Change style + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); + return EditorGUI.CurveField(r, value, color, ranges); + } + + public static AnimationCurve CurveField(string label, AnimationCurve value, Color color, Rect ranges, params GUILayoutOption[] options) + { + // TODO Change style + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); + return EditorGUI.CurveField(r, label, value, color, ranges); + } + + // Make a field for editing an [[AnimationCurve]]. + public static AnimationCurve CurveField(GUIContent label, AnimationCurve value, Color color, Rect ranges, params GUILayoutOption[] options) + { + // TODO Change style + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); + return EditorGUI.CurveField(r, label, value, color, ranges); + } + + public static void CurveField(SerializedProperty property, Color color, Rect ranges, params GUILayoutOption[] options) + { + CurveField(property, color, ranges, null, options); + } + + // Make a field for editing an [[AnimationCurve]]. + public static void CurveField(SerializedProperty property, Color color, Rect ranges, GUIContent label, params GUILayoutOption[] options) + { + // TODO Change style + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.colorField, options); + EditorGUI.CurveField(r, property, color, ranges, label); + } + + public static bool InspectorTitlebar(bool foldout, Object targetObj) + { + return InspectorTitlebar(foldout, targetObj, true); + } + + public static bool InspectorTitlebar(bool foldout, Object targetObj, bool expandable) + { + return EditorGUI.InspectorTitlebar(GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.inspectorTitlebar), foldout, + targetObj, expandable); + } + + // Make an inspector-window-like titlebar. + public static bool InspectorTitlebar(bool foldout, Object[] targetObjs) + { + return InspectorTitlebar(foldout, targetObjs, true); + } + + public static bool InspectorTitlebar(bool foldout, Object[] targetObjs, bool expandable) + { + return EditorGUI.InspectorTitlebar(GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.inspectorTitlebar), foldout, + targetObjs, expandable); + } + + public static bool InspectorTitlebar(bool foldout, Editor editor) + { + return EditorGUI.InspectorTitlebar(GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.inspectorTitlebar), foldout, + editor); + } + + public static void InspectorTitlebar(Object[] targetObjs) + { + EditorGUI.InspectorTitlebar(GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.inspectorTitlebar), targetObjs); + } + + // Make a foldout with a toggle and title + internal static bool ToggleTitlebar(bool foldout, GUIContent label, ref bool toggleValue) + { + return EditorGUI.ToggleTitlebar(GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.inspectorTitlebar), label, foldout, ref toggleValue); + } + + internal static bool ToggleTitlebar(bool foldout, GUIContent label, SerializedProperty property) + { + bool toggleValue = property.boolValue; + EditorGUI.BeginChangeCheck(); + foldout = EditorGUI.ToggleTitlebar(GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.inspectorTitlebar), label, foldout, ref toggleValue); + if (EditorGUI.EndChangeCheck()) + property.boolValue = toggleValue; + + return foldout; + } + + internal static bool FoldoutTitlebar(bool foldout, GUIContent label, bool skipIconSpacing) + { + return FoldoutTitlebar(foldout, label, skipIconSpacing, EditorStyles.inspectorTitlebar, EditorStyles.inspectorTitlebarText); + } + + internal static bool FoldoutTitlebar(bool foldout, GUIContent label, bool skipIconSpacing, GUIStyle baseStyle, GUIStyle textStyle) + { + return EditorGUI.FoldoutTitlebar(GUILayoutUtility.GetRect(GUIContent.none, baseStyle, GUILayout.ExpandWidth(true)), label, foldout, skipIconSpacing, baseStyle, textStyle); + } + + // Make a label with a foldout arrow to the left of it. + internal static bool FoldoutInternal(bool foldout, GUIContent content, bool toggleOnLabelClick, GUIStyle style) + { + Rect r = s_LastRect = GUILayoutUtility.GetRect(EditorGUIUtility.fieldWidth, EditorGUIUtility.fieldWidth, EditorGUI.kSingleLineHeight, EditorGUI.kSingleLineHeight, style); + return EditorGUI.Foldout(r, foldout, content, toggleOnLabelClick, style); + } + + internal static uint LayerMaskField(UInt32 layers, GUIContent label, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, options); + return EditorGUI.LayerMaskField(r, layers, label); + } + + internal static LayerMask LayerMaskField(LayerMask layers, GUIContent label, params GUILayoutOption[] options) + { + var rect = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, options); + return EditorGUI.LayerMaskField(rect, layers, label); + } + + internal static void LayerMaskField(SerializedProperty property, GUIContent label, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(true, EditorGUI.kSingleLineHeight, options); + EditorGUI.LayerMaskField(r, property, label); + } + + public static void HelpBox(string message, MessageType type) + { + LabelField(GUIContent.none, EditorGUIUtility.TempContent(message, EditorGUIUtility.GetHelpIcon(type)), EditorStyles.helpBox); + } + + // Make a help box with a message to the user. + public static void HelpBox(string message, MessageType type, bool wide) + { + LabelField(wide ? GUIContent.none : EditorGUIUtility.blankContent, + EditorGUIUtility.TempContent(message, EditorGUIUtility.GetHelpIcon(type)), + EditorStyles.helpBox); + } + + // Make a help box with a message to the user. + public static void HelpBox(GUIContent content, bool wide = true) + { + LabelField(wide ? GUIContent.none : EditorGUIUtility.blankContent, + content, + EditorStyles.helpBox); + } + + // Make a label in front of some control. + internal static void PrefixLabelInternal(GUIContent label, GUIStyle followingStyle, GUIStyle labelStyle) + { + float p = followingStyle.margin.left; + if (!EditorGUI.LabelHasContent(label)) + { + GUILayoutUtility.GetRect(EditorGUI.indent - p, EditorGUI.kSingleLineHeight, followingStyle, GUILayout.ExpandWidth(false)); + return; + } + + Rect r = GUILayoutUtility.GetRect(EditorGUIUtility.labelWidth - p, EditorGUI.kSingleLineHeight, followingStyle, GUILayout.ExpandWidth(false)); + r.xMin += EditorGUI.indent; + EditorGUI.HandlePrefixLabel(r, r, label, 0, labelStyle); + } + + // Make a small space between the previous control and the following. + public static void Space() + { + Space(EditorGUI.kDefaultSpacing, true); + } + + public static void Space(float width) + { + Space(width, true); + } + + public static void Space(float width, bool expand) + { + GUILayoutUtility.GetRect(width, width, GUILayout.ExpandWidth(expand)); + } + + //[System.Obsolete ("Use Space() instead")] + // Make this function Obsolete when someone has time to _rename_ all + // the Standard Packages to Space(), as currently it shows tons of + // warnings. + // Same for the graphic tests. + // *undoc* + public static void Separator() + { + Space(); + } + + public class ToggleGroupScope : GUI.Scope + { + public bool enabled { get; protected set; } + + public ToggleGroupScope(string label, bool toggle) + { + enabled = BeginToggleGroup(label, toggle); + } + + public ToggleGroupScope(GUIContent label, bool toggle) + { + enabled = BeginToggleGroup(label, toggle); + } + + protected override void CloseScope() + { + EndToggleGroup(); + } + } + + public static bool BeginToggleGroup(string label, bool toggle) + { + return BeginToggleGroup(EditorGUIUtility.TempContent(label), toggle); + } + + // Begin a vertical group with a toggle to enable or disable all the controls within at once. + public static bool BeginToggleGroup(GUIContent label, bool toggle) + { + toggle = ToggleLeft(label, toggle, EditorStyles.boldLabel); + EditorGUI.BeginDisabled(!toggle); + GUILayout.BeginVertical(); + + return toggle; + } + + // Close a group started with ::ref::BeginToggleGroup + public static void EndToggleGroup() + { + GUILayout.EndVertical(); + EditorGUI.EndDisabled(); + } + + public class HorizontalScope : GUI.Scope + { + public Rect rect { get; protected set; } + + public HorizontalScope(params GUILayoutOption[] options) + { + rect = BeginHorizontal(options); + } + + public HorizontalScope(GUIStyle style, params GUILayoutOption[] options) + { + rect = BeginHorizontal(style, options); + } + + internal HorizontalScope(GUIContent content, GUIStyle style, params GUILayoutOption[] options) + { + rect = BeginHorizontal(content, style, options); + } + + protected override void CloseScope() + { + EndHorizontal(); + } + } + + public static Rect BeginHorizontal(params GUILayoutOption[] options) + { + return BeginHorizontal(GUIContent.none, GUIStyle.none, options); + } + + // Begin a horizontal group and get its rect back. + public static Rect BeginHorizontal(GUIStyle style, params GUILayoutOption[] options) + { + return BeginHorizontal(GUIContent.none, style, options); + } + + // public static Rect BeginHorizontal (string text, params GUILayoutOption[] options) { return BeginHorizontal (EditorGUIUtility.TempContent (text), GUIStyle.none, options); } + // public static Rect BeginHorizontal (Texture image, params GUILayoutOption[] options) { return BeginHorizontal (EditorGUIUtility.TempContent (image), GUIStyle.none, options); } + // public static Rect BeginHorizontal (GUIContent content, params GUILayoutOption[] options) { return BeginHorizontal (content, GUIStyle.none, options); } + // public static Rect BeginHorizontal (string text, GUIStyle style, params GUILayoutOption[] options) { return BeginHorizontal (EditorGUIUtility.TempContent (text), style, options); } + // public static Rect BeginHorizontal (Texture image, GUIStyle style, params GUILayoutOption[] options) { return BeginHorizontal (EditorGUIUtility.TempContent (image), style, options); } + internal static Rect BeginHorizontal(GUIContent content, GUIStyle style, params GUILayoutOption[] options) + { + GUILayoutGroup g = GUILayoutUtility.BeginLayoutGroup(style, options, typeof(GUILayoutGroup)); + g.isVertical = false; + if (style != GUIStyle.none || content != GUIContent.none) + { + GUI.Box(g.rect, GUIContent.none, style); + } + return g.rect; + } + + // Close a group started with BeginHorizontal + public static void EndHorizontal() + { + GUILayout.EndHorizontal(); + } + + public class VerticalScope : GUI.Scope + { + public Rect rect { get; protected set; } + + public VerticalScope(params GUILayoutOption[] options) + { + rect = BeginVertical(options); + } + + public VerticalScope(GUIStyle style, params GUILayoutOption[] options) + { + rect = BeginVertical(style, options); + } + + internal VerticalScope(GUIContent content, GUIStyle style, params GUILayoutOption[] options) + { + rect = BeginVertical(content, style, options); + } + + protected override void CloseScope() + { + EndVertical(); + } + } + + public static Rect BeginVertical(params GUILayoutOption[] options) + { + return BeginVertical(GUIContent.none, GUIStyle.none, options); + } + + // Begin a vertical group and get its rect back. + public static Rect BeginVertical(GUIStyle style, params GUILayoutOption[] options) + { + return BeginVertical(GUIContent.none, style, options); + } + + // public static Rect BeginVertical (string text, params GUILayoutOption[] options) { return BeginVertical (EditorGUIUtility.TempContent (text), GUIStyle.none, options); } + // public static Rect BeginVertical (Texture image, params GUILayoutOption[] options) { return BeginVertical (EditorGUIUtility.TempContent (image), GUIStyle.none, options); } + // public static Rect BeginVertical (GUIContent content, params GUILayoutOption[] options) { return BeginVertical (content, GUIStyle.none, options); } + // public static Rect BeginVertical (string text, GUIStyle style, params GUILayoutOption[] options) { return BeginVertical (EditorGUIUtility.TempContent (text), style, options); } + // public static Rect BeginVertical (Texture image, GUIStyle style, params GUILayoutOption[] options) { return BeginVertical (EditorGUIUtility.TempContent (image), style, options); } + internal static Rect BeginVertical(GUIContent content, GUIStyle style, params GUILayoutOption[] options) + { + GUILayoutGroup g = GUILayoutUtility.BeginLayoutGroup(style, options, typeof(GUILayoutGroup)); + g.isVertical = true; + if (style != GUIStyle.none || content != GUIContent.none) + { + GUI.Box(g.rect, GUIContent.none, style); + } + return g.rect; + } + + // Close a group started with BeginVertical + public static void EndVertical() + { + GUILayout.EndVertical(); + } + + public class ScrollViewScope : GUI.Scope + { + public Vector2 scrollPosition { get; protected set; } + public bool handleScrollWheel { get; set; } + + public ScrollViewScope(Vector2 scrollPosition, params GUILayoutOption[] options) + { + handleScrollWheel = true; + this.scrollPosition = BeginScrollView(scrollPosition, options); + } + + public ScrollViewScope(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical, params GUILayoutOption[] options) + { + handleScrollWheel = true; + this.scrollPosition = BeginScrollView(scrollPosition, alwaysShowHorizontal, alwaysShowVertical, options); + } + + public ScrollViewScope(Vector2 scrollPosition, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, params GUILayoutOption[] options) + { + handleScrollWheel = true; + this.scrollPosition = BeginScrollView(scrollPosition, horizontalScrollbar, verticalScrollbar, options); + } + + public ScrollViewScope(Vector2 scrollPosition, GUIStyle style, params GUILayoutOption[] options) + { + handleScrollWheel = true; + this.scrollPosition = BeginScrollView(scrollPosition, style, options); + } + + public ScrollViewScope(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options) + { + handleScrollWheel = true; + this.scrollPosition = BeginScrollView(scrollPosition, alwaysShowHorizontal, alwaysShowVertical, horizontalScrollbar, verticalScrollbar, background, options); + } + + internal ScrollViewScope(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, params GUILayoutOption[] options) + { + handleScrollWheel = true; + this.scrollPosition = BeginScrollView(scrollPosition, alwaysShowHorizontal, alwaysShowVertical, horizontalScrollbar, verticalScrollbar, options); + } + + protected override void CloseScope() + { + EndScrollView(handleScrollWheel); + } + } + + public static Vector2 BeginScrollView(Vector2 scrollPosition, params GUILayoutOption[] options) + { + return BeginScrollView(scrollPosition, false, false, GUI.skin.horizontalScrollbar, GUI.skin.verticalScrollbar, GUI.skin.scrollView, options); + } + + public static Vector2 BeginScrollView(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical, params GUILayoutOption[] options) + { + return BeginScrollView(scrollPosition, alwaysShowHorizontal, alwaysShowVertical, GUI.skin.horizontalScrollbar, GUI.skin.verticalScrollbar, GUI.skin.scrollView, options); + } + + public static Vector2 BeginScrollView(Vector2 scrollPosition, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, params GUILayoutOption[] options) + { + return BeginScrollView(scrollPosition, false, false, horizontalScrollbar, verticalScrollbar, GUI.skin.scrollView, options); + } + + public static Vector2 BeginScrollView(Vector2 scrollPosition, GUIStyle style, params GUILayoutOption[] options) + { + string name = style.name; + + GUIStyle vertical = GUI.skin.FindStyle(name + "VerticalScrollbar") ?? GUI.skin.verticalScrollbar; + GUIStyle horizontal = GUI.skin.FindStyle(name + "HorizontalScrollbar") ?? GUI.skin.horizontalScrollbar; + return BeginScrollView(scrollPosition, false, false, horizontal, vertical, style, options); + } + + internal static Vector2 BeginScrollView(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, params GUILayoutOption[] options) + { + return BeginScrollView(scrollPosition, alwaysShowHorizontal, alwaysShowVertical, horizontalScrollbar, verticalScrollbar, GUI.skin.scrollView, options); + } + + // Begin an automatically layouted scrollview. + public static Vector2 BeginScrollView(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical, GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options) + { + GUIScrollGroup g = (GUIScrollGroup)GUILayoutUtility.BeginLayoutGroup(background, null, typeof(GUIScrollGroup)); + if (Event.current.type == EventType.Layout) + { + g.resetCoords = true; + g.isVertical = true; + g.stretchWidth = 1; + g.stretchHeight = 1; + g.verticalScrollbar = verticalScrollbar; + g.horizontalScrollbar = horizontalScrollbar; + g.ApplyOptions(options); + } + return EditorGUIInternal.DoBeginScrollViewForward(g.rect, scrollPosition, new Rect(0, 0, g.clientWidth, g.clientHeight), alwaysShowHorizontal, alwaysShowVertical, horizontalScrollbar, verticalScrollbar, background); + } + + internal class VerticalScrollViewScope : GUI.Scope + { + public Vector2 scrollPosition { get; protected set; } + public bool handleScrollWheel { get; set; } + + public VerticalScrollViewScope(Vector2 scrollPosition, params GUILayoutOption[] options) + { + handleScrollWheel = true; + this.scrollPosition = BeginVerticalScrollView(scrollPosition, options); + } + + public VerticalScrollViewScope(Vector2 scrollPosition, bool alwaysShowVertical, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options) + { + handleScrollWheel = true; + this.scrollPosition = BeginVerticalScrollView(scrollPosition, alwaysShowVertical, verticalScrollbar, background, options); + } + + protected override void CloseScope() + { + EndScrollView(handleScrollWheel); + } + } + + internal static Vector2 BeginVerticalScrollView(Vector2 scrollPosition, params GUILayoutOption[] options) + { + return BeginVerticalScrollView(scrollPosition, false, GUI.skin.verticalScrollbar, GUI.skin.scrollView, options); + } + + // Begin an automatically layouted scrollview. + internal static Vector2 BeginVerticalScrollView(Vector2 scrollPosition, bool alwaysShowVertical, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options) + { + GUIScrollGroup g = (GUIScrollGroup)GUILayoutUtility.BeginLayoutGroup(background, null, typeof(GUIScrollGroup)); + if (Event.current.type == EventType.Layout) + { + g.resetCoords = true; + g.isVertical = true; + g.stretchWidth = 1; + g.stretchHeight = 1; + g.verticalScrollbar = verticalScrollbar; + g.horizontalScrollbar = GUIStyle.none; + g.allowHorizontalScroll = false; + g.ApplyOptions(options); + } + return EditorGUIInternal.DoBeginScrollViewForward(g.rect, scrollPosition, new Rect(0, 0, g.clientWidth, g.clientHeight), false, alwaysShowVertical, GUI.skin.horizontalScrollbar, verticalScrollbar, background); + } + + internal class HorizontalScrollViewScope : GUI.Scope + { + public Vector2 scrollPosition { get; protected set; } + public bool handleScrollWheel { get; set; } + + public HorizontalScrollViewScope(Vector2 scrollPosition, params GUILayoutOption[] options) + { + handleScrollWheel = true; + this.scrollPosition = BeginHorizontalScrollView(scrollPosition, options); + } + + public HorizontalScrollViewScope(Vector2 scrollPosition, bool alwaysShowHorizontal, GUIStyle horizontalScrollbar, GUIStyle background, params GUILayoutOption[] options) + { + handleScrollWheel = true; + this.scrollPosition = BeginHorizontalScrollView(scrollPosition, alwaysShowHorizontal, horizontalScrollbar, background, options); + } + + protected override void CloseScope() + { + EndScrollView(handleScrollWheel); + } + } + + internal static Vector2 BeginHorizontalScrollView(Vector2 scrollPosition, params GUILayoutOption[] options) + { + return BeginHorizontalScrollView(scrollPosition, false, GUI.skin.horizontalScrollbar, GUI.skin.scrollView, options); + } + + // Begin an automatically layouted scrollview. + + internal static Vector2 BeginHorizontalScrollView(Vector2 scrollPosition, bool alwaysShowHorizontal, GUIStyle horizontalScrollbar, GUIStyle background, params GUILayoutOption[] options) + { + GUIScrollGroup g = (GUIScrollGroup)GUILayoutUtility.BeginLayoutGroup(background, null, typeof(GUIScrollGroup)); + if (Event.current.type == EventType.Layout) + { + g.resetCoords = true; + g.isVertical = true; + g.stretchWidth = 1; + g.stretchHeight = 1; + g.verticalScrollbar = GUIStyle.none; + g.horizontalScrollbar = horizontalScrollbar; + g.allowHorizontalScroll = true; + g.allowVerticalScroll = false; + g.ApplyOptions(options); + } + return EditorGUIInternal.DoBeginScrollViewForward(g.rect, scrollPosition, new Rect(0, 0, g.clientWidth, g.clientHeight), alwaysShowHorizontal, false, horizontalScrollbar, GUI.skin.verticalScrollbar, background); + } + + // Ends a scrollview started with a call to BeginScrollView. + public static void EndScrollView() + { + GUILayout.EndScrollView(true); + } + + internal static void EndScrollView(bool handleScrollWheel) + { + GUILayout.EndScrollView(handleScrollWheel); + } + + public static bool PropertyField(SerializedProperty property, params GUILayoutOption[] options) + { + return PropertyField(property, null, IsChildrenIncluded(property), options); + } + + public static bool PropertyField(SerializedProperty property, GUIContent label, params GUILayoutOption[] options) + { + return PropertyField(property, label, IsChildrenIncluded(property), options); + } + + public static bool PropertyField(SerializedProperty property, bool includeChildren, params GUILayoutOption[] options) + { + return PropertyField(property, null, includeChildren, options); + } + + // Make a field for [[SerializedProperty]]. + public static bool PropertyField(SerializedProperty property, GUIContent label, bool includeChildren, params GUILayoutOption[] options) + { + return ScriptAttributeUtility.GetHandler(property).OnGUILayout(property, label, includeChildren, options); + } + + private static bool IsChildrenIncluded(SerializedProperty prop) + { + switch (prop.propertyType) + { + case SerializedPropertyType.Generic: + case SerializedPropertyType.Vector4: + return true; + default: + return false; + } + } + + public static Rect GetControlRect(params GUILayoutOption[] options) + { + return GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.layerMaskField, options); + } + + public static Rect GetControlRect(bool hasLabel, params GUILayoutOption[] options) + { + return GetControlRect(hasLabel, EditorGUI.kSingleLineHeight, EditorStyles.layerMaskField, options); + } + + public static Rect GetControlRect(bool hasLabel, float height, params GUILayoutOption[] options) + { + return GetControlRect(hasLabel, height, EditorStyles.layerMaskField, options); + } + + public static Rect GetControlRect(bool hasLabel, float height, GUIStyle style, params GUILayoutOption[] options) + { + return GUILayoutUtility.GetRect( + hasLabel ? kLabelFloatMinW : EditorGUIUtility.fieldWidth, + kLabelFloatMaxW, + height, height, style, options); + } + + internal static Rect GetSliderRect(bool hasLabel, params GUILayoutOption[] options) + { + return GetSliderRect(hasLabel, GUI.skin.horizontalSlider, options); + } + + internal static Rect GetSliderRect(bool hasLabel, GUIStyle sliderStyle, params GUILayoutOption[] options) + { + return GUILayoutUtility.GetRect( + hasLabel ? kLabelFloatMinW : EditorGUIUtility.fieldWidth, + kLabelFloatMaxW + EditorGUI.kSpacing + EditorGUI.kSliderMaxW, + EditorGUI.kSingleLineHeight, EditorGUI.kSingleLineHeight, sliderStyle, options); + } + + internal static Rect GetToggleRect(bool hasLabel, params GUILayoutOption[] options) + { + // Toggle is 14 pixels wide while float fields are EditorGUIUtility.fieldWidth pixels wide. + // Store difference in variable and add to min and max width values used for float fields. + float toggleAdjust = (14 - EditorGUIUtility.fieldWidth); + return GUILayoutUtility.GetRect( + hasLabel ? kLabelFloatMinW + toggleAdjust : EditorGUIUtility.fieldWidth + toggleAdjust, + kLabelFloatMaxW + toggleAdjust, + EditorGUI.kSingleLineHeight, EditorGUI.kSingleLineHeight, EditorStyles.numberField, options); + } + + public class FadeGroupScope : GUI.Scope + { + // when using the FadeGroupScope, make sure to only show the content when 'visible' is set to true, + // otherwise only the hide animation will run, and then the content will be visible again. + public bool visible { get; protected set; } + + public FadeGroupScope(float value) + { + visible = BeginFadeGroup(value); + } + + protected override void CloseScope() + { + EndFadeGroup(); + } + } + + public static bool BeginFadeGroup(float value) + { + GUILayoutFadeGroup g = (GUILayoutFadeGroup)GUILayoutUtility.BeginLayoutGroup(GUIStyle.none, null, typeof(GUILayoutFadeGroup)); + g.isVertical = true; + g.resetCoords = false; + g.fadeValue = value; + g.wasGUIEnabled = GUI.enabled; + g.guiColor = GUI.color; + g.consideredForMargin = value > 0; + + // We don't want the fade group gui clip to be used for calculating the label width of controls in this fade group, so we lock the context width. + EditorGUIUtility.LockContextWidth(); + + if (value != 0.0f && value != 1.0f) + { + g.resetCoords = true; + GUI.BeginGroup(g.rect); + + if (Event.current.type == EventType.MouseDown) + { + Event.current.Use(); + } + } + + return value != 0; + } + + public static void EndFadeGroup() + { + // If we're inside a fade group, end it here. + GUILayoutFadeGroup g = EditorGUILayoutUtilityInternal.topLevel as GUILayoutFadeGroup; + + // If there are no more FadeGroups to end, display a warning. + if (g == null) + { + Debug.LogWarning("Unexpected call to EndFadeGroup! Make sure to call EndFadeGroup the same number of times as BeginFadeGroup."); + return; + } + + if (g.fadeValue != 0.0f && g.fadeValue != 1.0f) + { + GUI.EndGroup(); + } + + EditorGUIUtility.UnlockContextWidth(); + GUI.enabled = g.wasGUIEnabled; + GUI.color = g.guiColor; + GUILayoutUtility.EndLayoutGroup(); + } + + public static BuildTargetGroup BeginBuildTargetSelectionGrouping() + { + BuildPlatform[] validPlatforms = BuildPlatforms.instance.GetValidPlatforms().ToArray(); + int selected = BeginPlatformGrouping(validPlatforms, null); + return validPlatforms[selected].namedBuildTarget.ToBuildTargetGroup(); + } + + public static void EndBuildTargetSelectionGrouping() + { + EndPlatformGrouping(); + } + + internal static int BeginPlatformGrouping(BuildPlatform[] platforms, GUIContent defaultTab) + { + return BeginPlatformGrouping(platforms, defaultTab, EditorStyles.frameBox); + } + + static Rect GetTabRect(Rect rect, int tabIndex, int tabCount, out GUIStyle tabStyle) + { + if (s_TabOnlyOne == null) + { + // Keep in sync with Tests/EditModeAndPlayModeTests/PlayerSettings/Assets/Editor/PlayerSettingsApplicationIdentifierTests.cs. + s_TabOnlyOne = "Tab onlyOne"; + s_TabFirst = "Tab first"; + s_TabMiddle = "Tab middle"; + s_TabLast = "Tab last"; + } + + tabStyle = s_TabMiddle; + + if (tabCount == 1) + { + tabStyle = s_TabOnlyOne; + } + else if (tabIndex == 0) + { + tabStyle = s_TabFirst; + } + else if (tabIndex == (tabCount - 1)) + { + tabStyle = s_TabLast; + } + + float tabWidth = rect.width / tabCount; + int left = Mathf.RoundToInt(tabIndex * tabWidth); + int right = Mathf.RoundToInt((tabIndex + 1) * tabWidth); + return new Rect(rect.x + left, rect.y, right - left, EditorGUI.kTabButtonHeight); + } + + internal static int BeginPlatformGrouping(BuildPlatform[] platforms, GUIContent defaultTab, GUIStyle style) + { + return BeginPlatformGrouping(platforms, defaultTab, style, null); + } + + internal static int BeginPlatformGrouping(BuildPlatform[] platforms, GUIContent defaultTab, GUIStyle style, Func showOverrideForPlatform) + { + int selectedPlatform = -1; + for (int i = 0; i < platforms.Length; i++) + { + if (platforms[i].IsSelected()) + { + selectedPlatform = i; + break; + } + } + if (selectedPlatform == -1) + { + s_SelectedDefault.value = true; + selectedPlatform = 0; + } + + int selected = defaultTab == null ? selectedPlatform : (s_SelectedDefault.value ? -1 : selectedPlatform); + + bool tempEnabled = GUI.enabled; + GUI.enabled = true; + EditorGUI.BeginChangeCheck(); + Rect r = BeginVertical(style); + int platformCount = platforms.Length; + int buttonCount = platformCount; + int startIndex = 0; + + if (defaultTab != null) + { + buttonCount++; + startIndex = -1; + } + + int buttonIndex = 0; + for (int i = startIndex; i < platformCount; i++, buttonIndex++) + { + GUIContent content = GUIContent.none; + + if (i == -1) + { + content = defaultTab; + } + else + { + content = new GUIContent(platforms[i].smallIcon, platforms[i].tooltip); + } + + GUIStyle buttonStyle = null; + Rect buttonRect = GetTabRect(r, buttonIndex, buttonCount, out buttonStyle); + + if (GUI.Toggle(buttonRect, selected == i, content, buttonStyle)) + selected = i; + if (showOverrideForPlatform != null) + { + if (showOverrideForPlatform(i)) + { + var prevMargin = EditorGUIUtility.leftMarginCoord; + var overrideRect = buttonRect; + const int margin = 3; + overrideRect.y += margin; + overrideRect.height -= margin * 2; + EditorGUIUtility.leftMarginCoord = overrideRect.x + margin; + EditorGUI.DrawOverrideBackgroundApplicable(overrideRect); + EditorGUIUtility.leftMarginCoord = prevMargin; + } + } + } + + // GUILayout.Space doesn't expand to available width, so use GetRect instead + GUILayoutUtility.GetRect(10, EditorGUI.kTabButtonHeight); + + GUI.enabled = tempEnabled; + + // Important that we only actually set the selectedBuildTargetGroup if the user clicked the button. + // If the current selectedBuildTargetGroup is one that is not among the tabs (because the build target + // is not supported), then this should not be changed unless the user explicitly does so. + // Otherwise, if the build window is open at the same time, the unsupported build target groups will + // not be selectable in the build window. + if (EditorGUI.EndChangeCheck()) + { + if (defaultTab == null) + { + platforms[selected].Select(); + } + else + { + if (selected < 0) + { + s_SelectedDefault.value = true; + } + else + { + platforms[selected].Select(); + s_SelectedDefault.value = false; + } + } + + // Repaint build window, if open. + Object[] buildWindows = Resources.FindObjectsOfTypeAll(typeof(BuildPlayerWindow)); + foreach (Object t in buildWindows) + { + BuildPlayerWindow buildWindow = t as BuildPlayerWindow; + if (buildWindow != null) + buildWindow.Repaint(); + } + } + + return selected; + } + + internal static void EndPlatformGrouping() + { + EndVertical(); + } + + internal static void MultiSelectionObjectTitleBar(Object[] objects) + { + string text = objects[0].name + " (" + ObjectNames.NicifyVariableName(ObjectNames.GetTypeName(objects[0])) + ")"; + if (objects.Length > 1) + { + text += " and " + (objects.Length - 1) + " other" + (objects.Length > 2 ? "s" : ""); + } + GUILayoutOption[] options = { GUILayout.Height(16f) }; + GUILayout.Label(EditorGUIUtility.TempContent(text, AssetPreview.GetMiniThumbnail(objects[0])), EditorStyles.boldLabel, options); + } + + // Returns true if specified bit is true for all targets + internal static bool BitToggleField(string label, SerializedProperty bitFieldProperty, int flag) + { + bool toggle = (bitFieldProperty.intValue & flag) != 0; + bool different = (bitFieldProperty.hasMultipleDifferentValuesBitwise & flag) != 0; + EditorGUI.showMixedValue = different; + EditorGUI.BeginChangeCheck(); + toggle = Toggle(label, toggle); + if (EditorGUI.EndChangeCheck()) + { + // If toggle has mixed values, always set all to true when clicking it + if (different) + { + toggle = true; + } + different = false; + int bitIndex = -1; + for (int i = 0; i < 32; i++) + { + if (((1 << i) & flag) != 0) + { + bitIndex = i; + break; + } + } + bitFieldProperty.SetBitAtIndexForAllTargetsImmediate(bitIndex, toggle); + } + EditorGUI.showMixedValue = false; + return toggle && !different; + } + + internal static void SortingLayerField(GUIContent label, SerializedProperty layerID, GUIStyle style) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style); + EditorGUI.SortingLayerField(r, label, layerID, style, EditorStyles.label); + } + + internal static string TextFieldDropDown(string text, string[] dropDownElement) + { + return TextFieldDropDown(GUIContent.none, text, dropDownElement); + } + + internal static string TextFieldDropDown(GUIContent label, string text, string[] dropDownElement) + { + Rect rect = GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.textField); + return EditorGUI.TextFieldDropDown(rect, label, text, dropDownElement); + } + + internal static string DelayedTextFieldDropDown(string text, string[] dropDownElement) + { + return DelayedTextFieldDropDown(GUIContent.none, text, dropDownElement); + } + + internal static string DelayedTextFieldDropDown(GUIContent label, string text, string[] dropDownElement) + { + Rect rect = GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.textFieldDropDownText); + return EditorGUI.DelayedTextFieldDropDown(rect, label, text, dropDownElement); + } + + // A button that returns true on mouse down - like a popup button + public static bool DropdownButton(GUIContent content, FocusType focusType, params GUILayoutOption[] options) + { + return DropdownButton(content, focusType, "MiniPullDown", options); + } + + // A button that returns true on mouse down - like a popup button + public static bool DropdownButton(GUIContent content, FocusType focusType, GUIStyle style, params GUILayoutOption[] options) + { + s_LastRect = GUILayoutUtility.GetRect(content, style, options); + return EditorGUI.DropdownButton(s_LastRect, content, focusType, style); + } + + // A toggle that returns true on mouse down - like a popup button and returns true if checked + internal static bool DropDownToggle(ref bool toggled, GUIContent content, GUIStyle toggleStyle) + { + GUIStyle buttonStyle = GUIStyle.none; + + // This is to be compatible with existing code + if (toggleStyle == EditorStyles.toolbarDropDownToggle || toggleStyle == EditorStyles.toolbarDropDownToggleRight) + buttonStyle = EditorStyles.toolbarDropDownToggleButton; + + return DropDownToggle(ref toggled, content, toggleStyle, buttonStyle); + } + + internal static bool DropDownToggle(ref bool toggled, GUIContent content, GUIStyle toggleStyle, GUIStyle toggleDropdownButtonStyle) + { + Rect toggleRect = GUILayoutUtility.GetRect(content, toggleStyle); + Rect arrowRightRect = Rect.zero; + + if (toggleDropdownButtonStyle != null) + { + arrowRightRect = new Rect(toggleRect.xMax - toggleDropdownButtonStyle.fixedWidth - toggleDropdownButtonStyle.margin.right, toggleRect.y, toggleDropdownButtonStyle.fixedWidth, toggleRect.height); + } + else + { + arrowRightRect = new Rect(toggleRect.xMax - toggleStyle.padding.right, toggleRect.y, toggleStyle.padding.right, toggleRect.height); + } + + + int dropdownButtonId = GUIUtility.GetControlID(EditorGUI.s_DropdownButtonHash, FocusType.Passive, arrowRightRect); + bool clicked = EditorGUI.DropdownButton(dropdownButtonId, arrowRightRect, GUIContent.none, GUIStyle.none); + + if (!clicked) + { + toggled = GUI.Toggle(toggleRect, toggled, content, toggleStyle); + } + + // Ensure that the dropdown button is rendered on top of the toggle + if (Event.current.type == EventType.Repaint && toggleDropdownButtonStyle != null && toggleDropdownButtonStyle != GUIStyle.none) + { + EditorGUI.DropdownButton(dropdownButtonId, arrowRightRect, GUIContent.none, toggleDropdownButtonStyle); + } + + return clicked; + } + + internal static int AdvancedPopup(int selectedIndex, string[] displayedOptions, params GUILayoutOption[] options) + { + return AdvancedPopup(selectedIndex, displayedOptions, "MiniPullDown", options); + } + + internal static int AdvancedPopup(int selectedIndex, string[] displayedOptions, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.AdvancedPopup(r, selectedIndex, displayedOptions, style); + } + + internal static int AdvancedLazyPopup(string displayedOption, int selectedIndex, Func> displayedOptionsFunc, GUIStyle style, params GUILayoutOption[] options) + { + Rect r = s_LastRect = GetControlRect(false, EditorGUI.kSingleLineHeight, style, options); + return EditorGUI.AdvancedLazyPopup(r, displayedOption, selectedIndex, displayedOptionsFunc, style); + } + + [Obsolete("(UnityUpgradable) -> UnityEditor.HyperLinkClickedEventArgs", true)] + internal class HyperLinkClickedEventArgs + { + [Obsolete("(UnityUpgradable) -> UnityEditor.HyperLinkClickedEventArgs.hyperLinkData", true)] + public Dictionary hyperlinkInfos { get; private set; } + internal HyperLinkClickedEventArgs(Dictionary hyperLinkData) {} + } +} diff --git a/Editor/Mono/EditorGUIUtility.bindings.cs b/Editor/Mono/EditorGUIUtility.bindings.cs index b1ba7ef79e..3abf7aaba6 100644 --- a/Editor/Mono/EditorGUIUtility.bindings.cs +++ b/Editor/Mono/EditorGUIUtility.bindings.cs @@ -151,6 +151,10 @@ public static void RenderGameViewCameras(RenderTexture target, int targetDisplay internal static extern void RenderPlayModeViewCamerasInternal(RenderTexture target, int targetDisplay, Vector2 mousePosition, bool gizmos, bool renderIMGUI); internal static extern void SetupWindowSpaceAndVSyncInternal(Rect screenRect); + internal static extern void PerformTonemappingForGameView(); + internal static extern void DrawTextureHdrSupport(Rect screenRect, Texture texture, Rect sourceRect, int leftBorder, + int rightBorder, int topBorder, int bottomBorder, Color color, Material mat, int pass, bool resetLinearToSrgbIfHdrActive); + private static extern Texture2D FindTextureByName(string name); private static extern Texture2D FindTextureByType([NotNull] Type type); internal static extern string GetObjectNameWithInfo(Object obj); diff --git a/Editor/Mono/EditorGUIUtility.cs b/Editor/Mono/EditorGUIUtility.cs index 20f324158e..5e40cc2538 100644 --- a/Editor/Mono/EditorGUIUtility.cs +++ b/Editor/Mono/EditorGUIUtility.cs @@ -1162,7 +1162,7 @@ internal static void NotifyLanguageChanged(SystemLanguage newLanguage) L10n.ClearCache(); EditorUtility.Internal_UpdateMenuTitleForLanguage(newLanguage); LocalizationDatabase.currentEditorLanguage = newLanguage; - PanelTextSettings.UpdateLocalizationFontAsset(); + EditorTextSettings.UpdateLocalizationFontAsset(); EditorApplication.RequestRepaintAllViews(); } diff --git a/Editor/Mono/EditorMode/ModeService.cs b/Editor/Mono/EditorMode/ModeService.cs index 58e027ef86..6312982172 100644 --- a/Editor/Mono/EditorMode/ModeService.cs +++ b/Editor/Mono/EditorMode/ModeService.cs @@ -99,6 +99,7 @@ public struct ModeChangedArgs public int nextIndex; } + internal const string k_ModePathsCache = "mode-paths-cache"; internal const string k_DefaultModeId = "default"; internal const string k_ModeCurrentIdKeyName = "mode-current-id"; internal const string k_MenuKeyChecked = "checked"; @@ -139,6 +140,7 @@ static ModeService() modeChanged += OnModeChangeMenus; modeChanged += OnModeChangeLayouts; + UnityEditor.PackageManager.Events.registeredPackages += OnRegisteredPackages; ModeDescriptorImporter.allowExplicitModeRefresh = true; } @@ -351,6 +353,12 @@ internal static bool HasStartupMode() return Application.HasARGV("editor-mode"); } + private static void OnRegisteredPackages(PackageManager.PackageRegistrationEventArgs evt) + { + SessionState.EraseString(k_ModePathsCache); + LoadModes(); + } + private static void LoadModes(bool checkStartupMode = false) { Log("LoadModes"); @@ -420,21 +428,38 @@ internal static void ScanModes() var builtinModeFile = Path.Combine(EditorApplication.applicationContentsPath, "Resources/default.mode"); FillModeData(builtinModeFile, modesData); - var modeDescriptors = AssetDatabase.EnumerateAllAssets(new SearchFilter + var modeFilePathCache = SessionState.GetString(k_ModePathsCache, ""); + + if (modeFilePathCache == "") { - searchArea = SearchFilter.SearchArea.InPackagesOnly, - classNames = new[] { nameof(ModeDescriptor) }, - showAllHits = true - }); + var modeDescriptors = AssetDatabase.EnumerateAllAssets(new SearchFilter + { + searchArea = SearchFilter.SearchArea.InPackagesOnly, + classNames = new[] { nameof(ModeDescriptor) }, + showAllHits = true + }); - while (modeDescriptors.MoveNext()) + while (modeDescriptors.MoveNext()) + { + var md = modeDescriptors.Current.pptrValue as ModeDescriptor; + if (md == null) + continue; + FillModeData(md.path, modesData); + modeFilePathCache += md.path + ";"; + } + SessionState.SetString(k_ModePathsCache, modeFilePathCache); + } + else { - var md = modeDescriptors.Current.pptrValue as ModeDescriptor; - if (md == null) - continue; - FillModeData(md.path, modesData); + var paths = modeFilePathCache.Split(";"); + foreach(var path in paths) + { + if (!File.Exists(path)) + continue; + FillModeData(path, modesData); + } } - + modes = new ModeEntry[modesData.Keys.Count]; modes[0] = CreateEntry(k_DefaultModeId, (JSONObject)modesData[k_DefaultModeId]); var modeIndex = 1; diff --git a/Editor/Mono/EditorSceneManager.bindings.cs b/Editor/Mono/EditorSceneManager.bindings.cs index bed62ddfbc..a8757b60c9 100644 --- a/Editor/Mono/EditorSceneManager.bindings.cs +++ b/Editor/Mono/EditorSceneManager.bindings.cs @@ -141,6 +141,9 @@ internal static void RemapAssetReferencesInScene(UnityEngine.SceneManagement.Sce [NativeMethod("GetPreviewScenesVisibleInHierarchy")] internal extern static bool GetPreviewScenesVisibleInHierarchy(); + [StaticAccessor("EditorSceneManagerBindings", StaticAccessorType.DoubleColon)] + internal extern static Scene GetDontDestroyOnLoadScene(); + [StaticAccessor("EditorSceneManagerBindings", StaticAccessorType.DoubleColon)] [NativeMethod("IsPreviewSceneObject")] public extern static bool IsPreviewSceneObject(UnityEngine.Object obj); @@ -241,7 +244,7 @@ internal static void RemapAssetReferencesInScene(UnityEngine.SceneManagement.Sce [NativeThrows] [StaticAccessor("EditorSceneManagerBindings", StaticAccessorType.DoubleColon)] [NativeMethod("RestoreSceneManagerSetup")] - public extern static void RestoreSceneManagerSetup(SceneSetup[] value); + public extern static void RestoreSceneManagerSetup([Unmarshalled] SceneSetup[] value); [StaticAccessor("EditorSceneManagerBindings", StaticAccessorType.DoubleColon)] [NativeMethod("LoadSceneManagerSetup")] diff --git a/Editor/Mono/EditorSettings.bindings.cs b/Editor/Mono/EditorSettings.bindings.cs index 2140f5e5f0..e5ba1d9a2e 100644 --- a/Editor/Mono/EditorSettings.bindings.cs +++ b/Editor/Mono/EditorSettings.bindings.cs @@ -69,6 +69,7 @@ public enum EnterPlayModeOptions None = 0, DisableDomainReload = 1 << 0, DisableSceneReload = 1 << 1, + [Obsolete("Option has no effect and is deprecated.")] DisableSceneBackupUnlessDirty = 1 << 2 } @@ -238,6 +239,7 @@ internal static extern string Internal_ProjectGenerationUserExtensions [StaticAccessor("GetEditorSettings()", StaticAccessorType.Dot)] internal static extern void SetEtcTextureCompressorDefaultBehavior(); + [Obsolete("EditorSettings.useLegacyProbeSampleCount is deprecated. The legacy method of selecting probe sample counts will be removed in a future release.", false)] [StaticAccessor("GetEditorSettings()", StaticAccessorType.Dot)] public static extern bool useLegacyProbeSampleCount { get; set; } diff --git a/Editor/Mono/EditorTextSettings.cs b/Editor/Mono/EditorTextSettings.cs new file mode 100644 index 0000000000..3b613a4f5e --- /dev/null +++ b/Editor/Mono/EditorTextSettings.cs @@ -0,0 +1,95 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.TextCore.Text; +using UnityEditor.Experimental; + +namespace UnityEditor +{ + + /// + /// Represents text rendering settings for the editor + /// + [InitializeOnLoad] + internal class EditorTextSettings : TextSettings + { + private static EditorTextSettings s_DefaultTextSettings; + private static float s_CurrentEditorSharpness; + private static bool s_CurrentEditorSharpnessLoadedOrSet = false; + + const string k_Platform = + " - Linux"; + + static EditorTextSettings() + { + IMGUITextHandle.GetEditorTextSettings = () => defaultTextSettings; + } + + internal static void SetCurrentEditorSharpness(float sharpness) + { + s_CurrentEditorSharpness = sharpness; + s_CurrentEditorSharpnessLoadedOrSet = true; + } + + internal override float GetEditorTextSharpness() + { + if (!s_CurrentEditorSharpnessLoadedOrSet) + { + SetCurrentEditorSharpness(EditorPrefs.GetFloat($"EditorTextSharpness_{EditorResources.GetFont(FontDef.Style.Normal).name}", 0.0f)); + } + return s_CurrentEditorSharpness; + } + + internal override Font GetEditorFont() + { + return EditorResources.GetFont(FontDef.Style.Normal); + } + + internal static EditorTextSettings defaultTextSettings + { + get + { + if (s_DefaultTextSettings == null) + { + s_DefaultTextSettings = EditorGUIUtility.Load(s_DefaultEditorTextSettingPath) as EditorTextSettings; + if (s_DefaultTextSettings) + UpdateLocalizationFontAsset(); + } + + return s_DefaultTextSettings; + } + } + + internal static void UpdateLocalizationFontAsset() + { + var localizationAssetPathPerSystemLanguage = new Dictionary() + { + { SystemLanguage.English, $"UIPackageResources/FontAssets/DynamicOSFontAssets/Localization/English{k_Platform}.asset" }, + { SystemLanguage.Japanese, $"UIPackageResources/FontAssets/DynamicOSFontAssets/Localization/Japanese{k_Platform}.asset" }, + { SystemLanguage.ChineseSimplified, $"UIPackageResources/FontAssets/DynamicOSFontAssets/Localization/ChineseSimplified{k_Platform}.asset" }, + { SystemLanguage.ChineseTraditional, $"UIPackageResources/FontAssets/DynamicOSFontAssets/Localization/ChineseTraditional{k_Platform}.asset" }, + { SystemLanguage.Korean, $"UIPackageResources/FontAssets/DynamicOSFontAssets/Localization/Korean{k_Platform}.asset" } + }; + + var globalFallbackAssetPath = $"UIPackageResources/FontAssets/DynamicOSFontAssets/GlobalFallback/GlobalFallback{k_Platform}.asset"; + + FontAsset localizationAsset = null; + + if (localizationAssetPathPerSystemLanguage.ContainsKey(LocalizationDatabase.currentEditorLanguage)) + { + localizationAsset = EditorGUIUtility.Load(localizationAssetPathPerSystemLanguage[LocalizationDatabase.currentEditorLanguage]) as FontAsset; + } + + var globalFallbackAsset = EditorGUIUtility.Load(globalFallbackAssetPath) as FontAsset; + + defaultTextSettings.fallbackFontAssets[0] = localizationAsset; + defaultTextSettings.fallbackFontAssets[defaultTextSettings.fallbackFontAssets.Count - 1] = globalFallbackAsset; + } + + internal static readonly string s_DefaultEditorTextSettingPath = "UIPackageResources/Editor Text Settings.asset"; + } +} diff --git a/Editor/Mono/EditorUserBuildSettings.bindings.cs b/Editor/Mono/EditorUserBuildSettings.bindings.cs index b7188b4de2..85e628724a 100644 --- a/Editor/Mono/EditorUserBuildSettings.bindings.cs +++ b/Editor/Mono/EditorUserBuildSettings.bindings.cs @@ -231,6 +231,13 @@ public enum WSABuildAndRunDeployTarget DevicePortal = 2 } + [NativeType(Header = "Editor/Src/EditorUserBuildSettings.h")] + public enum WindowsBuildAndRunDeployTarget + { + LocalMachine = 0, + DevicePortal = 2 + } + [NativeType(Header = "Editor/Src/EditorUserBuildSettings.h")] public enum WSABuildType { @@ -386,9 +393,6 @@ private EditorUserBuildSettings() {} [NativeMethod("GetActiveSubTargetFor")] internal static extern int GetActiveSubtargetFor(BuildTarget target); - [NativeMethod("SetActiveSubTargetFor")] - internal static extern void SetActiveSubtargetFor(BuildTarget target, int subtarget); - // QNX OS Version public static extern QNXOsVersion selectedQnxOsVersion { @@ -423,8 +427,30 @@ public static extern EmbeddedLinuxArchitecture selectedEmbeddedLinuxArchitecture public static extern string remoteDeviceExports { get; set; } public static extern string pathOnRemoteDevice { get; set; } - // The currently selected target for a standalone build. - public static extern BuildTarget selectedStandaloneTarget + public static BuildTarget selectedStandaloneTarget + { + get { return internal_SelectedStandaloneTarget; } + set + { + string platformName = BuildPipeline.GetBuildTargetName(value); + var architecture = GetPlatformSettings(platformName, kSettingArchitecture).ToLower(); + switch (value) + { + case BuildTarget.StandaloneWindows: + if (architecture != "x86") + SetPlatformSettings(platformName, kSettingArchitecture, OSArchitecture.x86.ToString()); + break; + case BuildTarget.StandaloneWindows64: + if (architecture != "x64" && architecture != "arm64") + SetPlatformSettings(platformName, kSettingArchitecture, OSArchitecture.x64.ToString()); + break; + } + + internal_SelectedStandaloneTarget = value; + } + } + + private static extern BuildTarget internal_SelectedStandaloneTarget { [NativeMethod("GetSelectedStandaloneTarget")] get; @@ -709,6 +735,13 @@ public static extern WSABuildAndRunDeployTarget wsaBuildAndRunDeployTarget set; } + public static extern WindowsBuildAndRunDeployTarget windowsBuildAndRunDeployTarget + { + [NativeMethod("GetSelectedWindowsBuildAndRunDeployTarget")] + get; + [NativeMethod("SetSelectedWindowsBuildAndRunDeployTarget")] + set; + } public static extern int overrideMaxTextureSize { get; set; } public static extern Build.OverrideTextureCompression overrideTextureCompression { get; set; } @@ -720,9 +753,14 @@ public static extern WSABuildAndRunDeployTarget wsaBuildAndRunDeployTarget internal static extern BuildTargetGroup activeBuildTargetGroup { get; } [NativeMethod("SwitchActiveBuildTargetSync")] - public static extern bool SwitchActiveBuildTarget(BuildTargetGroup targetGroup, BuildTarget target); + internal static extern bool SwitchActiveBuildTargetAndSubtarget(BuildTargetGroup targetGroup, BuildTarget target, int subtarget); + public static bool SwitchActiveBuildTarget(BuildTargetGroup targetGroup, BuildTarget target) + => SwitchActiveBuildTargetAndSubtarget(targetGroup, target, EditorUserBuildSettings.GetActiveSubtargetFor(target)); + [NativeMethod("SwitchActiveBuildTargetAsync")] - public static extern bool SwitchActiveBuildTargetAsync(BuildTargetGroup targetGroup, BuildTarget target); + internal static extern bool SwitchActiveBuildTargetAndSubtargetAsync(BuildTargetGroup targetGroup, BuildTarget target, int subtarget); + public static bool SwitchActiveBuildTargetAsync(BuildTargetGroup targetGroup, BuildTarget target) + => SwitchActiveBuildTargetAndSubtargetAsync(targetGroup, target, EditorUserBuildSettings.GetActiveSubtargetFor(target)); public static bool SwitchActiveBuildTarget(NamedBuildTarget namedBuildTarget, BuildTarget target) => BuildPlatforms.instance.BuildPlatformFromNamedBuildTarget(namedBuildTarget).SetActive(target); @@ -731,7 +769,9 @@ public static bool SwitchActiveBuildTarget(NamedBuildTarget namedBuildTarget, Bu // validating if support for it is installed. However it does not do things like script recompile // or domain reload -- generally only useful for asset import testing. [NativeMethod("SwitchActiveBuildTargetSyncNoCheck")] - internal static extern bool SwitchActiveBuildTargetNoCheck(BuildTargetGroup targetGroup, BuildTarget target); + internal static extern bool SwitchActiveBuildTargetAndSubtargetNoCheck(BuildTargetGroup targetGroup, BuildTarget target, int subtarget); + internal static bool SwitchActiveBuildTargetNoCheck(BuildTargetGroup targetGroup, BuildTarget target) + => SwitchActiveBuildTargetAndSubtargetNoCheck(targetGroup, target, EditorUserBuildSettings.GetActiveSubtargetFor(target)); // DEFINE directives for the compiler. public static extern string[] activeScriptCompilationDefines @@ -931,12 +971,21 @@ public static extern bool switchNVNDrawValidation_Heavy set; } - // Enable linkage of the Heap inspector tool for Nintendo Switch. - public static extern bool switchEnableHeapInspector + // Enable linkage of the Memory Tracker tool for Nintendo Switch. + public static extern bool switchEnableMemoryTracker + { + [NativeMethod("GetEnableMemoryTrackerForSwitch")] + get; + [NativeMethod("SetEnableMemoryTrackerForSwitch")] + set; + } + + // On startup the application waits for Memory Tracker to connect. + public static extern bool switchWaitForMemoryTrackerOnStartup { - [NativeMethod("GetEnableHeapInspectorForSwitch")] + [NativeMethod("GetWaitForSwitchMemoryTrackerOnStartup")] get; - [NativeMethod("SetEnableHeapInspectorForSwitch")] + [NativeMethod("SetWaitForSwitchMemoryTrackerOnStartup")] set; } diff --git a/Editor/Mono/EditorUserBuildSettingsUtils.cs b/Editor/Mono/EditorUserBuildSettingsUtils.cs index 16f66b64e6..3c493bc56b 100644 --- a/Editor/Mono/EditorUserBuildSettingsUtils.cs +++ b/Editor/Mono/EditorUserBuildSettingsUtils.cs @@ -108,7 +108,7 @@ public static bool SetActive(this BuildPlatform buildPlatform, BuildTarget targe } if (buildPlatform is BuildPlatformWithSubtarget) - EditorUserBuildSettings.SetActiveSubtargetFor(target, ((BuildPlatformWithSubtarget)buildPlatform).subtarget); + return EditorUserBuildSettings.SwitchActiveBuildTargetAndSubtarget(buildPlatform.namedBuildTarget.ToBuildTargetGroup(), target, ((BuildPlatformWithSubtarget)buildPlatform).subtarget); return EditorUserBuildSettings.SwitchActiveBuildTarget(buildPlatform.namedBuildTarget.ToBuildTargetGroup(), target); } diff --git a/Editor/Mono/EditorUtility.bindings.cs b/Editor/Mono/EditorUtility.bindings.cs index 600db06697..875df1d6c0 100644 --- a/Editor/Mono/EditorUtility.bindings.cs +++ b/Editor/Mono/EditorUtility.bindings.cs @@ -34,19 +34,26 @@ public static string OpenFilePanelWithFilters(string title, string directory, st public static extern void RevealInFinder(string path); [FreeFunction("DisplayDialog")] - static extern bool DoDisplayDialog(string title, string message, string ok, [DefaultValue("\"\"")] string cancel); + static extern bool DisplayDialogImpl(string title, string message, string ok, string cancel); public static bool DisplayDialog(string title, string message, string ok, [DefaultValue("\"\"")] string cancel) { + // i am not sure how picky should we be about the params + // for example, for the buttons we have code that ignores empty strings, + // to allow having "ok"-only dialogs (information panels, so to say) + // same with title+message: it sounds like we should allow skipping one of those + // hence we make sure that at least one button is present, and some message + // we can go more picky if we want in the future + if(string.IsNullOrEmpty(ok) && string.IsNullOrEmpty(cancel)) + throw new ArgumentException("Both 'ok' and 'cancel' strings are null or empty"); + if(string.IsNullOrEmpty(title) && string.IsNullOrEmpty(message)) + throw new ArgumentException("Both 'title' and 'message' strings are null or empty"); + using (new DisabledGuiViewInputScope(GUIView.current, true)) { - return DoDisplayDialog(title, message, ok, cancel); + return DisplayDialogImpl(title, message, ok, cancel); } } - - [FreeFunction("GetDialogResponse")] - internal static extern bool GetDialogResponse(InteractionContext interactionContext, string title, string message, string ok, [DefaultValue("\"\"")] string cancel); - [ExcludeFromDocs] public static bool DisplayDialog(string title, string message, string ok) { @@ -54,9 +61,22 @@ public static bool DisplayDialog(string title, string message, string ok) } [FreeFunction("DisplayDialogComplex")] - public static extern int DisplayDialogComplex(string title, string message, string ok, string cancel, string alt); - [FreeFunction("GetDialogResponseComplex")] - internal static extern int GetDialogResponseComplex(InteractionContext interactionContext, string title, string message, string ok, string cancel, string alt); + static extern int DisplayDialogComplexImpl(string title, string message, string ok, string cancel, string alt); + + public static int DisplayDialogComplex(string title, string message, string ok, string cancel, string alt) + { + // see the comment above in DisplayDialog + // our implementation allows setting some strings empty (the button will be skipped then) + // but we should totally ensure some buttons are set + + if(string.IsNullOrEmpty(ok) && string.IsNullOrEmpty(cancel) && string.IsNullOrEmpty(alt)) + throw new ArgumentException("All three 'ok', 'cancel' and 'alt' strings are null or empty"); + if(string.IsNullOrEmpty(title) && string.IsNullOrEmpty(message)) + throw new ArgumentException("Both 'title' and 'message' strings are null or empty"); + + return DisplayDialogComplexImpl(title, message, ok, cancel, alt); + } + [FreeFunction("RunOpenFolderPanel")] @@ -75,6 +95,7 @@ public static bool DisplayDialog(string title, string message, string ok) public static extern bool IsPersistent(Object target); public static extern bool IsValidUnityYAML(string yaml); public static extern string SaveFilePanel(string title, string directory, string defaultName, string extension); + [ThreadSafe] public static extern int NaturalCompare(string a, string b); public static extern Object InstanceIDToObject(int instanceID); public static extern void CompressTexture([NotNull] Texture2D texture, TextureFormat format, int quality); @@ -115,8 +136,8 @@ internal static void RemapAssetReferences(UnityEngine.Object[] objects, Dictiona private static extern void InternalCopySerializedIfDifferent([NotNull("NullExceptionObject")] Object source, [NotNull("NullExceptionObject")] Object dest); [NativeThrows] - public static extern Object[] CollectDependencies(Object[] roots); - public static extern Object[] CollectDeepHierarchy(Object[] roots); + public static extern Object[] CollectDependencies([Unmarshalled] Object[] roots); + public static extern Object[] CollectDeepHierarchy([Unmarshalled] Object[] roots); [FreeFunction("InstantiateObjectRemoveAllNonAnimationComponents")] private static extern Object Internal_InstantiateRemoveAllNonAnimationComponentsSingle([NotNull("NullExceptionObject")] Object data, Vector3 pos, Quaternion rot); diff --git a/Editor/Mono/EditorUtility.cs b/Editor/Mono/EditorUtility.cs index e95e677ccf..1d51348070 100644 --- a/Editor/Mono/EditorUtility.cs +++ b/Editor/Mono/EditorUtility.cs @@ -80,7 +80,7 @@ public static string GetDialogOptOutMessage(DialogOptOutDecisionType dialogOptOu public static bool LoadWindowLayout(string path) { - return WindowLayout.LoadWindowLayout(path, false); + return WindowLayout.TryLoadWindowLayout(path, false); } public static void CompressTexture(Texture2D texture, TextureFormat format, TextureCompressionQuality quality) @@ -506,10 +506,17 @@ internal static void DisplayObjectContextMenu(Rect position, Object[] context, i info.instanceObject = targetComponent; info.assetPath = AssetDatabase.GetAssetPath(sourceGo); GameObject rootObject = PrefabUtility.GetRootGameObject(sourceGo); - if (!PrefabUtility.IsPartOfPrefabThatCanBeAppliedTo(rootObject) || EditorUtility.IsPersistent(instanceGo)) + + if (targetComponent.hideFlags.HasFlag(HideFlags.DontSaveInEditor) + || !PrefabUtility.IsPartOfPrefabThatCanBeAppliedTo(rootObject) + || EditorUtility.IsPersistent(instanceGo)) + { pm.AddDisabledItem(menuItemContent); + } else + { pm.AddItem(menuItemContent, false, TargetChoiceHandler.ApplyPrefabAddedComponent, info); + } }, (menuItemContent) => { @@ -567,7 +574,7 @@ internal static void DisplayObjectContextMenu(Rect position, Object[] context, i GameObject rootObject = PrefabUtility.GetRootGameObject(sourceObject); bool isPersistent = EditorUtility.IsPersistent(instanceOrAssetObject); - if (!PrefabUtility.IsPartOfPrefabThatCanBeAppliedTo(rootObject) || (!isPersistent && !PrefabUtility.HasApplicableObjectOverridesForTarget(instanceOrAssetObject, rootObject, false))) + if (isPersistent || !PrefabUtility.IsPartOfPrefabThatCanBeAppliedTo(rootObject) || !PrefabUtility.HasApplicableObjectOverridesForTarget(instanceOrAssetObject, rootObject, false)) pm.AddDisabledItem(menuItemContent); else pm.AddItem(menuItemContent, false, TargetChoiceHandler.ApplyPrefabObjectOverride, info); diff --git a/Editor/Mono/EditorWindow.cs b/Editor/Mono/EditorWindow.cs index 4abbd65027..a51a57896a 100644 --- a/Editor/Mono/EditorWindow.cs +++ b/Editor/Mono/EditorWindow.cs @@ -44,6 +44,12 @@ public partial class EditorWindow : ScriptableObject [HideInInspector] internal Rect m_Pos = new Rect(0, 0, 320, 550); + [SerializeField] + internal DataModeController m_SerializedDataModeController; + public IDataModeController dataModeController => GetDataModeController_Internal(); // For each editor window. + internal DataModeController GetDataModeController_Internal() // For HostView to use internally. + => m_SerializedDataModeController ??= new DataModeController(); + private VisualElement m_UIRootElement; internal VisualElement baseRootVisualElement => m_UIRootElement == null @@ -106,6 +112,8 @@ internal SerializableJsonDictionary viewDataDictionary } } + internal static List activeEditorWindows { get; } = new List(); + internal void SaveViewData() { m_RequestedViewDataSave = true; @@ -249,7 +257,7 @@ internal static GUIContent GetLocalizedTitleContentFromType(Type t) else if (attr.useTypeNameAsIconName) iconName = t.ToString(); - if (!string.IsNullOrEmpty(iconName)) + if (!string.IsNullOrEmpty(iconName) && EditorGUIUtility.LoadIcon(iconName)) { // This should error msg if icon is not found since icon has been explicitly requested by the user return EditorGUIUtility.TrTextContentWithIcon(attr.title, iconName); @@ -697,6 +705,26 @@ public void ShowModal() } } + static void AssignTitle(EditorWindow win, string title) + { + if (title != null) + { + win.titleContent = new GUIContent(title); + return; + } + + // Do not assign anything new if the user has defined its own title. + var titleContent = GetLocalizedTitleContentFromType(win.GetType()); + if (win.titleContent.text == win.GetType().ToString()) + { + win.titleContent.text = titleContent.text; + } + if (win.titleContent.image == null) + { + win.titleContent.image = titleContent.image; + } + } + // Returns the first EditorWindow of type /t/ which is currently on the screen. static EditorWindow GetWindowPrivate(System.Type t, bool utility, string title, bool focus) { @@ -708,8 +736,7 @@ static EditorWindow GetWindowPrivate(System.Type t, bool utility, string title, try { win = ScriptableObject.CreateInstance(t) as EditorWindow; - if (title != null) - win.titleContent = new GUIContent(title); + AssignTitle(win, title); if (utility) win.ShowUtility(); else @@ -731,44 +758,44 @@ static EditorWindow GetWindowPrivate(System.Type t, bool utility, string title, return win; } - public static EditorWindow GetWindow(System.Type t, [DefaultValue("false")] bool utility, [DefaultValue("null")] string title, [DefaultValue("true")] bool focus) + public static EditorWindow GetWindow(System.Type windowType, [DefaultValue("false")] bool utility, [DefaultValue("null")] string title, [DefaultValue("true")] bool focus) { - return GetWindowPrivate(t, utility, title, focus); + return GetWindowPrivate(windowType, utility, title, focus); } [ExcludeFromDocs] - public static EditorWindow GetWindow(System.Type t, bool utility, string title) + public static EditorWindow GetWindow(System.Type windowType, bool utility, string title) { - return GetWindowPrivate(t, utility, title, true); + return GetWindowPrivate(windowType, utility, title, true); } [ExcludeFromDocs] - public static EditorWindow GetWindow(System.Type t, bool utility) + public static EditorWindow GetWindow(System.Type windowType, bool utility) { - return GetWindowPrivate(t, utility, null, true); + return GetWindowPrivate(windowType, utility, null, true); } [ExcludeFromDocs] - public static EditorWindow GetWindow(System.Type t) + public static EditorWindow GetWindow(System.Type windowType) { - return GetWindowPrivate(t, false, null, true); + return GetWindowPrivate(windowType, false, null, true); } - public static EditorWindow GetWindowWithRect(System.Type t, Rect rect, [DefaultValue("false")] bool utility, [DefaultValue("null")] string title) + public static EditorWindow GetWindowWithRect(System.Type windowType, Rect rect, [DefaultValue("false")] bool utility, [DefaultValue("null")] string title) { - return GetWindowWithRectPrivate(t, rect, utility, title); + return GetWindowWithRectPrivate(windowType, rect, utility, title); } [ExcludeFromDocs] - public static EditorWindow GetWindowWithRect(System.Type t, Rect rect, bool utility) + public static EditorWindow GetWindowWithRect(System.Type windowType, Rect rect, bool utility) { - return GetWindowWithRectPrivate(t, rect, utility, null); + return GetWindowWithRectPrivate(windowType, rect, utility, null); } [ExcludeFromDocs] - public static EditorWindow GetWindowWithRect(System.Type t, Rect rect) + public static EditorWindow GetWindowWithRect(System.Type windowType, Rect rect) { - return GetWindowWithRectPrivate(t, rect, false, null); + return GetWindowWithRectPrivate(windowType, rect, false, null); } public static T GetWindow() where T : EditorWindow @@ -838,8 +865,7 @@ public static T CreateWindow(string title, params System.Type[] desiredDockNe { T win = CreateInstance(); - if (title != null) - win.titleContent = new GUIContent(title); + AssignTitle(win, title); //Iterate the desired dock next to types... foreach (var desired in desiredDockNextTo) @@ -906,8 +932,7 @@ static EditorWindow GetWindowWithRectPrivate(System.Type t, Rect rect, bool util win.minSize = new Vector2(rect.width, rect.height); win.maxSize = new Vector2(rect.width, rect.height); win.position = rect; - if (title != null) - win.titleContent = new GUIContent(title); + AssignTitle(win, title); if (utility) win.ShowUtility(); else @@ -952,8 +977,7 @@ public static T GetWindowWithRect(Rect rect, bool utility, string title, bool window.minSize = new Vector2(rect.width, rect.height); window.maxSize = new Vector2(rect.width, rect.height); window.position = rect; - if (title != null) - window.titleContent = new GUIContent(title); + AssignTitle(window, title); if (utility) window.ShowUtility(); else @@ -1220,10 +1244,16 @@ static void Initialize() EditorApplication.delayCall += () => ShortcutIntegration.instance.contextManager.RegisterToolContext(s_ShortcutContext); } - private void OnDisableINTERNAL() + void OnEnableINTERNAL() + { + activeEditorWindows.Add(this); + } + + void OnDisableINTERNAL() { m_OverlayCanvas.OnContainerWindowDisabled(); SaveViewDataToDisk(); + activeEditorWindows.Remove(this); } internal void ReleaseViewData() diff --git a/Editor/Mono/EnumDataUtility.cs b/Editor/Mono/EnumDataUtility.cs index 1f0f5aa6a8..54864a2b07 100644 --- a/Editor/Mono/EnumDataUtility.cs +++ b/Editor/Mono/EnumDataUtility.cs @@ -4,6 +4,7 @@ using System; using UnityEngine; +using static UnityEngine.EnumDataUtility; namespace UnityEditor { @@ -11,7 +12,12 @@ internal static class EnumDataUtility { internal static EnumData GetCachedEnumData(Type enumType, bool excludeObsolete = true) { - return UnityEngine.EnumDataUtility.GetCachedEnumData(enumType, excludeObsolete, ObjectNames.NicifyVariableName); + return UnityEngine.EnumDataUtility.GetCachedEnumData(enumType, excludeObsolete ? CachedType.ExcludeObsolete : CachedType.IncludeObsoleteExceptErrors, ObjectNames.NicifyVariableName); + } + + internal static EnumData GetCachedEnumData(Type enumType, CachedType cachedType) + { + return UnityEngine.EnumDataUtility.GetCachedEnumData(enumType, cachedType, ObjectNames.NicifyVariableName); } internal static int EnumFlagsToInt(EnumData enumData, Enum enumValue) diff --git a/Editor/Mono/GI/InputExtraction.bindings.cs b/Editor/Mono/GI/InputExtraction.bindings.cs new file mode 100644 index 0000000000..4292ab37af --- /dev/null +++ b/Editor/Mono/GI/InputExtraction.bindings.cs @@ -0,0 +1,260 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using System.Runtime.InteropServices; +using UnityEngine.Bindings; + +namespace UnityEditor.LightBaking +{ + [NativeHeader("Editor/Src/GI/InputExtraction/InputExtraction.Bindings.h")] + [StaticAccessor("InputExtractionBindings", StaticAccessorType.DoubleColon)] + internal static class InputExtraction + { + [StructLayout(LayoutKind.Sequential)] + public class SourceMap : IDisposable + { + internal IntPtr m_Ptr; + internal bool m_OwnsPtr; + + public SourceMap() + { + m_Ptr = Internal_Create(); + m_OwnsPtr = true; + } + public SourceMap(IntPtr ptr) + { + m_Ptr = ptr; + m_OwnsPtr = false; + } + ~SourceMap() + { + Destroy(); + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); + } + + void Destroy() + { + if (m_OwnsPtr && m_Ptr != IntPtr.Zero) + { + Internal_Destroy(m_Ptr); + m_Ptr = IntPtr.Zero; + } + } + + public extern int GetInstanceIndex(int instanceID); + public extern int GetInstanceInstanceID(int instanceIndex); + + public extern int GetTreeCount(); + public extern int GetTreeInstanceIndex(int treeIndex); + + public extern int GetLightIndex(int instanceID); + public extern int GetLightInstanceID(int lightIndex); + + static extern IntPtr Internal_Create(); + static extern void Internal_Destroy(IntPtr ptr); + } + + public static extern bool ExtractFromScene(string outputFolderPath, LightBaker.BakeInput input, SourceMap map); + + private static string LookupGameObjectName(SourceMap map, int instanceIndex) + { + if (map == null) + return ""; + int instanceID = map.GetInstanceInstanceID(instanceIndex); + if (instanceID == 0) + return ""; + Object obj = EditorUtility.InstanceIDToObject(instanceID); + if (obj == null) + return ""; + else if (obj is UnityEngine.GameObject go) + { + return go.name; + } + else if (obj is UnityEngine.Component component) + { + return component.gameObject.name; + } + return ""; + } + + public static string LogInstances(LightBaker.BakeInput bakeInput, SourceMap map) + { + if (bakeInput is null) + return string.Empty; + string message = string.Empty; + message += $" instance count\t: {bakeInput.instanceCount}\n"; + for (int i = 0; i < bakeInput.instanceCount; ++i) + { + message += $" Instance [{i}] [{LookupGameObjectName(map, i)}]:\n"; + LightBaker.Instance instance = bakeInput.instance((uint)i); + message += $" mesh type\t\t: {instance.meshType}\n"; + if (instance.meshType == LightBaker.MeshType.MeshRenderer) + message += $" mesh index\t: {instance.meshIndex}\n"; + else if (instance.meshType == LightBaker.MeshType.Terrain) + message += $" terrain index\t: {instance.terrainIndex}\n"; + message += $" transform\t\t: {instance.transform.GetRow(0)}\n"; + message += $" \t\t: {instance.transform.GetRow(1)}\n"; + message += $" \t\t: {instance.transform.GetRow(2)}\n"; + message += $" \t\t: {instance.transform.GetRow(3)}\n"; + message += $" cast shadows\t: {instance.castShadows}\n"; + message += $" receive shadows\t: {instance.receiveShadows}\n"; + if (instance.meshType == LightBaker.MeshType.MeshRenderer) + { + message += $" odd negative scale\t: {instance.oddNegativeScale}\n"; + message += $" lod group\t\t: {instance.lodGroup}\n"; + message += $" lod mask\t\t: {instance.lodMask}\n"; + } + message += $" submesh count\t: {instance.submeshMaterialIndices.Length}\n"; + string indices = string.Empty; + for (int j = 0; j < instance.submeshMaterialIndices.Length; ++j) + indices += instance.submeshMaterialIndices[j] + (j < (instance.submeshMaterialIndices.Length - 1) ? ", " : ""); + message += $" submesh mat idxs\t: {{{indices}}}\n"; + } + return message; + } + + public static string LogSceneMaterials(LightBaker.BakeInput bakeInput) + { + if (bakeInput is null) + return string.Empty; + string message = string.Empty; + message += $" material count\t: {bakeInput.materialCount}\n"; + for (int i = 0; i < bakeInput.materialCount; ++i) + { + message += $" Material [{i}]:\n"; + message += $" doubleSidedGI\t: {bakeInput.GetMaterial((uint)i).doubleSidedGI}\n"; + message += $" transmissionChannels\t: {bakeInput.GetMaterial((uint)i).transmissionChannels}\n"; + message += $" transmissionType\t: {bakeInput.GetMaterial((uint)i).transmissionType}\n"; + } + return message; + } + + public static string LogSceneCookies(LightBaker.BakeInput bakeInput) + { + if (bakeInput is null) + return string.Empty; + string message = string.Empty; + + message += $" cookie tex count\t: {bakeInput.GetCookieCount()}\n"; + for (int i = 0; i < bakeInput.GetCookieCount(); ++i) + { + LightBaker.CookieData cookie = bakeInput.GetCookieData((uint)i); + message += $" CookieTexture [{i}]:\n"; + message += $" resolution\t\t: {cookie.resolution.width} x {cookie.resolution.height}\n"; + message += $" pixelStride\t\t: {cookie.pixelStride}\n"; + message += $" slices\t\t: {cookie.slices}\n"; + message += $" repeat\t\t: {cookie.repeat}\n"; + } + return message; + } + + public static string LogSceneLights(LightBaker.BakeInput bakeInput) + { + if (bakeInput is null) + return string.Empty; + string message = string.Empty; + message += $" light count\t\t: {bakeInput.GetLightCount()}\n"; + for (int i = 0; i < bakeInput.GetLightCount(); ++i) + { + LightBaker.Light light = bakeInput.GetLight((uint)i); + message += $" Light [{i}]:\n"; + message += $" color\t\t: {light.color}\n"; + message += $" indirect color\t: {light.indirectColor}\n"; + message += $" orientation\t: {light.orientation}\n"; + message += $" position\t\t: {light.position}\n"; + message += $" range\t\t: {light.range}\n"; + message += $" cookie index\t: {light.cookieTextureIndex}\n"; + message += $" cookie scale\t: {light.cookieScale}\n"; + message += $" cone angle\t: {light.coneAngle}\n"; + message += $" inner cone angle\t: {light.innerConeAngle}\n"; + message += $" shape0\t\t: {light.shape0}\n"; + message += $" shape1\t\t: {light.shape1}\n"; + message += $" type\t\t: {light.type}\n"; + message += $" mode\t\t: {light.mode}\n"; + message += $" falloff\t\t: {light.falloff}\n"; + message += $" angular falloff\t: {light.angularFalloff}\n"; + message += $" casts shadows\t: {light.castsShadows}\n"; + message += $" shadowmask chnl\t: {light.shadowMaskChannel}\n"; + } + return message; + } + + public static string LogSampleCounts(LightBaker.SampleCount sampleCount) + { + string message = string.Empty; + message += $" direct\t\t: {sampleCount.directSampleCount}\n"; + message += $" indirect\t\t: {sampleCount.indirectSampleCount}\n"; + message += $" environment\t: {sampleCount.environmentSampleCount}\n"; + return message; + } + + public static string LogSceneSettings(LightBaker.BakeInput bakeInput) + { + if (bakeInput is null) + return string.Empty; + var lightingSettings = bakeInput.GetLightingSettings(); + string message = string.Empty; + message += $" lighting settings\t:\n"; + message += $" lightmap sample counts:\n"; + message += LogSampleCounts(lightingSettings.lightmapSampleCounts); + message += $" lightprobe sample counts:\n"; + message += LogSampleCounts(lightingSettings.probeSampleCounts); + message += $" min bounces\t: {lightingSettings.minBounces}\n"; + message += $" max bounces\t: {lightingSettings.maxBounces}\n"; + message += $" lightmap bake mode\t: {lightingSettings.lightmapBakeMode}\n"; + message += $" mixed lighting mode\t: {lightingSettings.mixedLightingMode}\n"; + message += $" ao enabled\t\t: {lightingSettings.aoEnabled}\n"; + message += $" ao distance\t\t: {lightingSettings.aoDistance}\n"; + return message; + } + + public static string LogScene(LightBaker.BakeInput bakeInput, SourceMap map) + { + if (bakeInput is null) + return string.Empty; + string message = string.Empty; + message += LogSceneSettings(bakeInput); + message += LogInstances(bakeInput, map); + message += $" mesh count\t\t: {bakeInput.meshCount}\n"; + message += $" terrain count\t\t: {bakeInput.terrainCount}\n"; + message += $" heightmap count\t: {bakeInput.heightmapCount}\n"; + message += $" holemap count\t: {bakeInput.holemapCount}\n"; + message += LogSceneMaterials(bakeInput); + message += $" albedo tex count\t: {bakeInput.albedoTextureCount}\n"; + for (int i = 0; i < bakeInput.albedoTextureCount; ++i) + { + message += $" AlbedoTexture [{i}]:\n"; + message += $" resolution\t\t: {bakeInput.GetAlbedoTextureData((uint)i).resolution.width} x {bakeInput.GetAlbedoTextureData((uint)i).resolution.height}\n"; + } + message += $" emissive tex count\t: {bakeInput.emissiveTextureCount}\n"; + for (int i = 0; i < bakeInput.emissiveTextureCount; ++i) + { + message += $" EmissiveTexture [{i}]:\n"; + message += $" resolution\t\t: {bakeInput.GetEmissiveTextureData((uint)i).resolution.width} x {bakeInput.GetEmissiveTextureData((uint)i).resolution.height}\n"; + } + message += $" transmissive tex count\t: {bakeInput.transmissiveTextureCount}\n"; + for (int i = 0; i < bakeInput.transmissiveTextureCount; ++i) + { + message += $" TransmissiveTexture [{i}]:\n"; + message += $" resolution\t\t: {bakeInput.GetTransmissiveTextureData((uint)i).resolution.width} x {bakeInput.GetTransmissiveTextureData((uint)i).resolution.height}\n"; + } + message += LogSceneCookies(bakeInput); + message += LogSceneLights(bakeInput); + message += $" lightmap count\t: {bakeInput.lightmapCount}\n"; + for (int i = 0; i < bakeInput.lightmapCount; ++i) + { + message += $" Lightmap [{i}]:\n"; + message += $" resolution\t\t: {bakeInput.lightmapResolution((uint)i).width} x {bakeInput.lightmapResolution((uint)i).height}\n"; + message += $" instance count\t: {bakeInput.lightmapInstanceCount((uint)i)}\n"; + } + return message; + } + } +} diff --git a/Editor/Mono/GI/LightBaker.bindings.cs b/Editor/Mono/GI/LightBaker.bindings.cs new file mode 100644 index 0000000000..d5f19601a6 --- /dev/null +++ b/Editor/Mono/GI/LightBaker.bindings.cs @@ -0,0 +1,599 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using System.Runtime.InteropServices; +using UnityEngine; +using UnityEngine.Bindings; + +namespace UnityEditor.LightBaking +{ + [NativeHeader("Editor/Src/GI/LightBaker/LightBaker.Bindings.h")] + [StaticAccessor("LightBakerBindings", StaticAccessorType.DoubleColon)] + internal static class LightBaker + { + public enum ResultType : UInt32 + { + Success = 0, + Cancelled, + JobFailed, + OutOfMemory, + InvalidInput, + LowLevelAPIFailure, + IOFailed, + Undefined + } + + public struct Result + { + public ResultType type; + public String message; + public override string ToString() + { + if (message.Length == 0) + return $"Result type: '{type}'"; + else + return $"Result type: '{type}', message: '{message}'"; + + } + } + + + public enum Backend + { + CPU = 0, + GPU = 1 + } + public enum TransmissionChannels + { + Red = 0, + Alpha = 1, + AlphaCutout = 2, + RGB = 3, + None = 4 + } + public enum TransmissionType + { + Opacity = 0, + Transparency = 1, + None = 2 + } + public enum MeshType + { + Terrain = 0, + MeshRenderer = 1 + } + public enum MixedLightingMode + { + IndirectOnly = 0, + Subtractive = 1, + Shadowmask = 2, + }; + public enum LightmapBakeMode + { + NonDirectional = 0, + CombinedDirectional = 1 + }; + [Flags] + public enum ProbeRequestOutputType : UInt32 + { + RadianceDirect = 1 << 0, + RadianceIndirect = 1 << 1, + Validity = 1 << 2, + MixedLightOcclusion = 1 << 3, + LightProbeOcclusion = 1 << 4, + EnvironmentOcclusion = 1 << 5, + Depth = 1 << 6, + All = 0xFFFFFFFF + }; + public struct ProbeRequest + { + public ProbeRequestOutputType outputTypeMask; + public UInt64 positionOffset; + public UInt64 positionLength; + public float pushoff; + public string outputFolderPath; + }; + [Flags] + public enum LightmapRequestOutputType : UInt32 + { + IrradianceIndirect = 1 << 0, + IrradianceDirect = 1 << 1, + IrradianceEnvironment = 1 << 2, + Occupancy = 1 << 3, + Validity = 1 << 4, + DirectionalityIndirect = 1 << 5, + DirectionalityDirect = 1 << 6, + AmbientOcclusion = 1 << 7, + Shadowmask = 1 << 8, + Normal = 1 << 9, + ChartIndex = 1 << 10, + OverlapPixelIndex = 1 << 11, + All = 0xFFFFFFFF + }; + public enum TilingMode + { // Assuming a 4k lightmap (16M texels), the tiling will yield the following chunk sizes: + None = 0, // 4k * 4k = 16M texels + Quarter = 1, // 2k * 2k = 4M texels + Sixteenth = 2, // 1k * 1k = 1M texels + Sixtyfourth = 3, // 512 * 512 = 262k texels + TwoHundredFiftySixth = 4, // 256 * 256 = 65k texels + Max = TwoHundredFiftySixth, + Error = 5 // Error. We don't want to go lower (GPU occupancy will start to be a problem for smaller atlas sizes). + }; + public struct LightmapRequest + { + public LightmapRequestOutputType outputTypeMask; + public UInt32 lightmapOffset; + public UInt32 lightmapCount; + public TilingMode tilingMode; + public string outputFolderPath; + }; + public struct Resolution + { + public Resolution(UInt32 widthIn, UInt32 heightIn) + { + width = widthIn; + height = heightIn; + } + public UInt32 width; + public UInt32 height; + } + + public struct TextureData + { + public TextureData(Resolution resolutionIn) + { + resolution = resolutionIn; + data = new Vector4[resolution.width * resolution.height]; + } + public Resolution resolution; + public Vector4[] data; + } + + public struct CookieData + { + public CookieData(Resolution resolutionIn, UInt32 pixelStrideIn, UInt32 slicesIn, bool repeatIn) + { + resolution = resolutionIn; + pixelStride = pixelStrideIn; + slices = slicesIn; + repeat = repeatIn; + data = new byte[resolution.width * resolution.height * slices* pixelStride]; + } + public Resolution resolution; + public UInt32 pixelStride; + public UInt32 slices; + public bool repeat; + public byte[] data; + } + + public struct Instance + { + public MeshType meshType; + public int meshIndex; // index into BakeInput::m_MeshData, -1 for Terrain + public int terrainIndex; // index into BakeInput::m_TerrainData, -1 for MeshRenderer + public Matrix4x4 transform; + public bool castShadows; + public bool receiveShadows; + public bool oddNegativeScale; + public int lodGroup; + public byte lodMask; + public int[] submeshMaterialIndices; + } + + public struct Terrain + { + public UInt32 heightMapIndex; // index into BakeInput::m_HeightmapData + public int terrainHoleIndex; // index into BakeInput::m_TerrainHoleData -1 means no hole data + public float outputResolution; + public Vector3 heightmapScale; + public Vector4 uvBounds; + } + + public struct Material + { + public bool doubleSidedGI; + public TransmissionChannels transmissionChannels; + public TransmissionType transmissionType; + } + + public enum LightType : byte + { + Directional = 0, + Point = 1, + Spot = 2, + Rectangle = 3, + Disc = 4, + SpotPyramidShape = 5, + SpotBoxShape = 6 + }; + + public enum FalloffType : byte + { + InverseSquared = 0, + InverseSquaredNoRangeAttenuation = 1, + Linear = 2, + Legacy = 3 + }; + + public enum AngularFalloffType : byte + { + LUT = 0, + AnalyticAndInnerAngle = 1 + }; + + public enum LightMode : byte + { + Realtime = 0, + Mixed = 1, + Baked = 2 + }; + + public struct Light + { + public Vector3 color; + public Vector3 indirectColor; + public Quaternion orientation; + public Vector3 position; + public float range; + public Int32 cookieTextureIndex; + public float cookieScale; + public float coneAngle; + public float innerConeAngle; + public float shape0; + public float shape1; + public LightType type; + public LightMode mode; + public FalloffType falloff; + public AngularFalloffType angularFalloff; + public bool castsShadows; + public Int32 shadowMaskChannel; + } + + public struct SampleCount + { + public UInt32 directSampleCount; + public UInt32 indirectSampleCount; + public UInt32 environmentSampleCount; + }; + + public struct LightingSettings + { + public SampleCount lightmapSampleCounts; + public SampleCount probeSampleCounts; + public UInt32 minBounces; + public UInt32 maxBounces; + public LightmapBakeMode lightmapBakeMode; + public MixedLightingMode mixedLightingMode; + public bool aoEnabled; + public float aoDistance; + }; + + [StructLayout(LayoutKind.Sequential)] + public class BakeInput : IDisposable + { + internal IntPtr m_Ptr; + internal bool m_OwnsPtr; + + public BakeInput() + { + m_Ptr = Internal_Create(); + m_OwnsPtr = true; + } + public BakeInput(IntPtr ptr) + { + m_Ptr = ptr; + m_OwnsPtr = false; + } + ~BakeInput() + { + Destroy(); + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); + } + + void Destroy() + { + if (m_OwnsPtr && m_Ptr != IntPtr.Zero) + { + Internal_Destroy(m_Ptr); + m_Ptr = IntPtr.Zero; + } + } + + extern public UInt64 GetByteSize(); + + public Texture2D GetAlbedoTexture(UInt32 index) + { + if (index >= albedoTextureCount) + throw new ArgumentException(string.Format("index must be between 0 and " + (albedoTextureCount - 1) + ", but was {0}", index)); + TextureData textureData = GetAlbedoTextureData(index); + var tex = new Texture2D((int)textureData.resolution.width, (int)textureData.resolution.height, TextureFormat.RGBAFloat, false); + tex.SetPixelData(textureData.data, 0); + tex.filterMode = FilterMode.Point; + tex.Apply(); + return tex; + } + + public Texture2D GetEmissiveTexture(UInt32 index) + { + if (index >= emissiveTextureCount) + throw new ArgumentException(string.Format("index must be between 0 and " + (emissiveTextureCount - 1) + ", but was {0}", index)); + TextureData textureData = GetEmissiveTextureData(index); + var tex = new Texture2D((int)textureData.resolution.width, (int)textureData.resolution.height, TextureFormat.RGBAFloat, false); + tex.SetPixelData(textureData.data, 0); + tex.filterMode = FilterMode.Point; + tex.Apply(); + return tex; + } + + static extern IntPtr Internal_Create(); + [NativeMethod(IsThreadSafe = true)] + static extern void Internal_Destroy(IntPtr ptr); + + extern LightingSettings Internal_GetLightingSettings(); + public LightingSettings GetLightingSettings() + { + return Internal_GetLightingSettings(); + } + extern void Internal_SetLightingSettings(LightingSettings lightingSettings); + public void SetLightingSettings(LightingSettings lightingSettings) + { + Internal_SetLightingSettings(lightingSettings); + } + + extern UInt32 Internal_LightmapWidth(UInt32 index); + extern UInt32 Internal_LightmapHeight(UInt32 index); + extern UInt32 Internal_InstanceCount(UInt32 lightmapIndex); + + extern public UInt32 instanceCount { get; } + extern Instance Internal_Instance(UInt32 index); + public Instance instance(UInt32 index) + { + if (index >= instanceCount) + throw new ArgumentException(string.Format("index must be between 0 and " + (instanceCount - 1) + ", but was {0}", index)); + Instance instance = Internal_Instance(index); + return instance; + } + + extern public UInt32 terrainCount { get; } + extern Terrain Internal_GetTerrain(UInt32 index); + public Terrain GetTerrain(UInt32 index) + { + if (index >= terrainCount) + throw new ArgumentException(string.Format("index must be between 0 and " + (terrainCount - 1) + ", but was {0}", index)); + Terrain terrain = Internal_GetTerrain(index); + return terrain; + } + + public UInt32 lightmapInstanceCount(UInt32 index) + { + if (index >= lightmapCount) + throw new ArgumentException(string.Format("index must be between 0 and " + (lightmapCount - 1) + ", but was {0}", index)); + return Internal_InstanceCount(index); + } + + extern public UInt32 meshCount { get; } + extern public UInt32 heightmapCount { get; } + extern public UInt32 holemapCount { get; } + extern public UInt32 materialCount { get; } + extern Material Internal_GetMaterial(UInt32 index); + extern void Internal_SetMaterial(UInt32 index, Material material); + public Material GetMaterial(UInt32 index) + { + if (index >= materialCount) + throw new ArgumentException(string.Format("index must be between 0 and " + (materialCount - 1) + ", but was {0}", index)); + Material material = Internal_GetMaterial(index); + return material; + } + public void SetMaterial(UInt32 index, Material material) + { + if (index >= materialCount) + throw new ArgumentException(string.Format("index must be between 0 and " + (materialCount - 1) + ", but was {0}", index)); + Internal_SetMaterial(index, material); + } + public int GetMaterialIndex(UInt32 instanceIndex, UInt32 submeshIndex) + { + if (instanceIndex >= instanceCount) + throw new ArgumentException(string.Format("instanceIndex must be between 0 and " + (instanceCount - 1) + ", but was {0}", instanceIndex)); + Instance theInstance = instance(instanceIndex); + if (theInstance.submeshMaterialIndices.Length == 0) + throw new ArgumentException(string.Format("instance {0} has not materials", instanceIndex)); + if (submeshIndex >= theInstance.submeshMaterialIndices.Length) + throw new ArgumentException(string.Format("submeshIndex must be between 0 and " + (theInstance.submeshMaterialIndices.Length - 1) + ", but was {0}", submeshIndex)); + return theInstance.submeshMaterialIndices[submeshIndex]; + } + extern UInt32 Internal_GetLightCount(); + public UInt32 GetLightCount() + { + return Internal_GetLightCount(); + } + extern Light Internal_GetLight(UInt32 index); + extern void Internal_SetLight(UInt32 index, Light light); + public Light GetLight(UInt32 index) + { + if (index >= GetLightCount()) + throw new ArgumentException(string.Format("index must be between 0 and " + (GetLightCount() - 1) + ", but was {0}", index)); + return Internal_GetLight(index); + } + public void SetLight(UInt32 index, Light light) + { + if (index >= GetLightCount()) + throw new ArgumentException(string.Format("index must be between 0 and " + (GetLightCount() - 1) + ", but was {0}", index)); + Internal_SetLight(index, light); + } + + extern Int32 Internal_instanceAlbedoEmissiveIndex(UInt32 instanceIndex); + extern Int32 Internal_instanceTransmissiveIndex(UInt32 instanceIndex, UInt32 submeshIndex); + public Int32 instanceToAlbedoIndex(UInt32 instanceIndex) + { + if (instanceIndex >= instanceCount) + throw new ArgumentException(string.Format("index must be between 0 and " + (instanceCount - 1) + ", but was {0}", instanceIndex)); + return Internal_instanceAlbedoEmissiveIndex(instanceIndex); + } + public Int32 instanceToEmissiveIndex(UInt32 instanceIndex) + { + if (instanceIndex >= instanceCount) + throw new ArgumentException(string.Format("index must be between 0 and " + (instanceCount - 1) + ", but was {0}", instanceIndex)); + return Internal_instanceAlbedoEmissiveIndex(instanceIndex); + } + public Int32 instanceToTransmissiveIndex(UInt32 instanceIndex, UInt32 submeshIndex) + { + if (instanceIndex >= instanceCount) + throw new ArgumentException(string.Format("index must be between 0 and " + (instanceCount - 1) + ", but was {0}", instanceIndex)); + Instance inst = instance(instanceIndex); + int submeshCount = inst.submeshMaterialIndices.Length; + if (submeshIndex >= submeshCount) + throw new ArgumentException(string.Format("submeshIndex must be between 0 and " + (submeshCount - 1) + ", but was {0}", submeshIndex)); + int materialIndex = inst.submeshMaterialIndices[submeshIndex]; + if (materialIndex == -1) + throw new ArgumentException(string.Format("material for submesh {0} did not exist.", submeshIndex)); + + return Internal_instanceTransmissiveIndex(instanceIndex, submeshIndex); + } + + extern public UInt32 albedoTextureCount { get; } + extern TextureData Internal_GetAlbedoTextureData(UInt32 index); + extern void Internal_SetAlbedoTextureData(UInt32 index, TextureData textureData); + + public TextureData GetAlbedoTextureData(UInt32 index) + { + if (index >= albedoTextureCount) + throw new ArgumentException(string.Format("index must be between 0 and " + (albedoTextureCount - 1) + ", but was {0}", index)); + return Internal_GetAlbedoTextureData(index); + } + public void SetAlbedoTextureData(UInt32 index, TextureData textureData) + { + if (index >= albedoTextureCount) + throw new ArgumentException(string.Format("index must be between 0 and " + (albedoTextureCount - 1) + ", but was {0}", index)); + Internal_SetAlbedoTextureData(index, textureData); + } + + extern public UInt32 emissiveTextureCount { get; } + extern public UInt32 transmissiveTextureCount { get; } + extern TextureData Internal_GetEmissiveTextureData(UInt32 index); + extern void Internal_SetEmissiveTextureData(UInt32 index, TextureData textureData); + public TextureData GetEmissiveTextureData(UInt32 index) + { + if (index >= emissiveTextureCount) + throw new ArgumentException(string.Format("index must be between 0 and " + (emissiveTextureCount - 1) + ", but was {0}", index)); + return Internal_GetEmissiveTextureData(index); + } + public void SetEmissiveTextureData(UInt32 index, TextureData textureData) + { + if (index >= emissiveTextureCount) + throw new ArgumentException(string.Format("index must be between 0 and " + (emissiveTextureCount - 1) + ", but was {0}", index)); + Internal_SetEmissiveTextureData(index, textureData); + } + + extern TextureData Internal_GetTransmissiveTextureData(UInt32 index); + extern void Internal_SetTransmissiveTextureData(UInt32 index, TextureData textureData); + public TextureData GetTransmissiveTextureData(UInt32 index) + { + if (index >= transmissiveTextureCount) + throw new ArgumentException(string.Format("index must be between 0 and " + (transmissiveTextureCount - 1) + ", but was {0}", index)); + return Internal_GetTransmissiveTextureData(index); + } + public void SetTransmissiveTextureData(UInt32 index, TextureData textureData) + { + if (index >= emissiveTextureCount) + throw new ArgumentException(string.Format("index must be between 0 and " + (transmissiveTextureCount - 1) + ", but was {0}", index)); + Internal_SetTransmissiveTextureData(index, textureData); + } + + extern public UInt32 GetCookieCount(); + extern CookieData Internal_GetCookieData(UInt32 index); + extern void Internal_SetCookieData(UInt32 index, CookieData cookieData); + public CookieData GetCookieData(UInt32 index) + { + if (index >= GetCookieCount()) + throw new ArgumentException(string.Format("index must be between 0 and " + (GetCookieCount() - 1) + ", but was {0}", index)); + return Internal_GetCookieData(index); + } + public void SetCookieData(UInt32 index, CookieData cookieData) + { + if (index >= GetCookieCount()) + throw new ArgumentException(string.Format("index must be between 0 and " + (GetCookieCount() - 1) + ", but was {0}", index)); + Internal_SetCookieData(index, cookieData); + } + + extern public UInt32 lightmapCount { get; } + public Resolution lightmapResolution(UInt32 index) + { + if (index >= lightmapCount) + throw new ArgumentException(string.Format("index must be between 0 and " + (lightmapCount - 1) + ", but was {0}", index)); + Resolution resolution; + resolution.width = Internal_LightmapWidth(index); + resolution.height = Internal_LightmapHeight(index); + return resolution; + } + extern public UInt32 lightProbeCount { get; } + + extern public void SetLightmapResolution(Resolution resolution); + extern public void SetEnvironment(Vector4 color); + + public extern ProbeRequest[] GetProbeRequests(); + public extern void SetLightProbeRequests(ProbeRequest[] requests); + public extern LightmapRequest[] GetLightmapRequests(); + public extern void SetLightmapRequests(LightmapRequest[] requests); + + public void SetProbePositions(Vector3[] positions) + { + SetProbePositions(positions.AsSpan()); + } + public extern void SetProbePositions(ReadOnlySpan positions); + + public extern Vector3[] GetProbePositions(); + } + + [StructLayout(LayoutKind.Sequential)] + public class DeviceSettings : IDisposable + { + static extern IntPtr Internal_Create(); + [NativeMethod(IsThreadSafe = true)] + + public extern bool Initialize(Backend backend); + static extern void Internal_Destroy(IntPtr ptr); + + internal IntPtr m_Ptr; + internal bool m_OwnsPtr; + + public DeviceSettings() + { + m_Ptr = Internal_Create(); + m_OwnsPtr = true; + } + public DeviceSettings(IntPtr ptr) + { + m_Ptr = ptr; + m_OwnsPtr = false; + } + ~DeviceSettings() + { + Destroy(); + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); + } + + void Destroy() + { + if (m_OwnsPtr && m_Ptr != IntPtr.Zero) + { + Internal_Destroy(m_Ptr); + m_Ptr = IntPtr.Zero; + } + } + } + extern static public Result Bake(BakeInput input, DeviceSettings deviceSettings); + } +} diff --git a/Editor/Mono/GI/LightmapParameters.bindings.cs b/Editor/Mono/GI/LightmapParameters.bindings.cs index 70638fec6f..5a2abfe8db 100644 --- a/Editor/Mono/GI/LightmapParameters.bindings.cs +++ b/Editor/Mono/GI/LightmapParameters.bindings.cs @@ -61,5 +61,13 @@ public float edgeStitching get { return stitchEdges ? 1.0f : 0.0f; } set { stitchEdges = (value != 0.0f); } } + + [NativeHeader("Runtime/Graphics/LightmapEnums.h")] + public enum AntiAliasingSamples + { + SSAA1 = 1, + SSAA4 = 2, + SSAA16 = 4, + } } } diff --git a/Editor/Mono/GI/Lightmapping.bindings.cs b/Editor/Mono/GI/Lightmapping.bindings.cs index 2cd59f54c3..55621c0296 100644 --- a/Editor/Mono/GI/Lightmapping.bindings.cs +++ b/Editor/Mono/GI/Lightmapping.bindings.cs @@ -12,6 +12,7 @@ using Scene = UnityEngine.SceneManagement.Scene; using NativeArrayUnsafeUtility = Unity.Collections.LowLevel.Unsafe.NativeArrayUnsafeUtility; using Unity.Collections; +using UnityEditor.LightBaking; using UnityEngine.Rendering; namespace UnityEditor @@ -68,6 +69,22 @@ internal struct LightProbesConvergence [NativeName("m_ConvergedProbeSetCount")] public int convergedProbeSetCount; } + [UsedByNativeCode] + [NativeHeader("Editor/Src/GI/Progressive/PVRData.h")] + internal struct LightmapSize + { + [NativeName("m_Width")] public int width; + [NativeName("m_Height")] public int height; + } + + [UsedByNativeCode] + [NativeHeader("Editor/Src/GI/Progressive/PVRData.h")] + internal struct RunningBakeInfo + { + [NativeName("m_LightmapSizes")] public LightmapSize[] lightmapSizes; + [NativeName("m_ProbePositions")] public UInt64 probePositions; + } + [UsedByNativeCode] [NativeHeader("Editor/Src/GI/Progressive/PVRData.h")] internal struct LightmapMemory @@ -217,40 +234,26 @@ internal static FilterMode filterMode set { GetOrCreateLightingsSettings().lightmapFilterMode = value; } } - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] - internal static extern bool isProgressiveLightmapperDone {[NativeName("IsDone")] get; } - - // Returns true when the bake job is baking, false otherwise. - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] - internal static extern bool isBaking { [NativeName("IsBaking")] get; } + [StaticAccessor("BakedGISceneManager::Get()", StaticAccessorType.Arrow)] + internal static extern bool isProgressiveLightmapperDone {[NativeName("IsBakedGIDone")] get; } - // Returns true when the bake job is preparing, false otherwise. - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] - internal static extern bool isPreparing { [NativeName("IsPreparing")] get; } - - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] + [StaticAccessor("BakedGISceneManager::Get()", StaticAccessorType.Arrow)] internal static extern ulong occupiedTexelCount { get; } - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] + [StaticAccessor("BakedGISceneManager::Get()", StaticAccessorType.Arrow)] internal static extern ulong GetVisibleTexelCount(int lightmapIndex); - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] + [StaticAccessor("BakedGISceneManager::Get()", StaticAccessorType.Arrow)] internal static extern int atlasCount { [NativeName("GetAtlasCount")] get; } - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] + [StaticAccessor("BakedGISceneManager::Get()", StaticAccessorType.Arrow)] internal static extern LightmapConvergence GetLightmapConvergence(int lightmapIndex); - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] - internal static extern LightProbesConvergence GetLightProbesConvergence(); - - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] - internal static extern LightmapMemory GetLightmapMemory(int lightmapIndex); + [StaticAccessor("BakedGISceneManager::Get()", StaticAccessorType.Arrow)] + internal static extern RunningBakeInfo GetRunningBakeInfo(); - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] - internal static extern bool GetGBufferHash(int lightmapIndex, out Hash128 gbufferHash); - - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] - internal static extern float GetGBufferMemory(ref Hash128 gbufferHash); + [StaticAccessor("BakedGISceneManager::Get()", StaticAccessorType.Arrow)] + internal static extern LightProbesConvergence GetLightProbesConvergence(); [FreeFunction] internal static extern MemLabels GetLightProbeMemLabels(); @@ -261,9 +264,6 @@ internal static FilterMode filterMode [FreeFunction] internal static extern MemLabels GetMaterialTexturesMemLabels(); - [FreeFunction] - internal static extern MemLabels GetNotShownMemLabels(); - [StaticAccessor("PVRMemoryLabelTracker::Get()", StaticAccessorType.Arrow)] internal static extern void ResetExplicitlyShownMemLabels(); @@ -271,36 +271,30 @@ internal static FilterMode filterMode [NativeName("GetGeometryMemory")] internal static extern GeoMemLabels GetGeometryMemory(); - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] + [StaticAccessor("BakedGISceneManager::Get()", StaticAccessorType.Arrow)] internal static extern float ComputeTotalCPUMemoryUsageInBytes(); - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] + [StaticAccessor("BakedGISceneManager::Get()", StaticAccessorType.Arrow)] internal static extern float ComputeTotalGPUMemoryUsageInBytes(); - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] + [StaticAccessor("BakedGISceneManager::Get()", StaticAccessorType.Arrow)] internal static extern void LogGPUMemoryStatistics(); - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] - internal static extern float GetLightmapBakeTimeRaw(); - - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] + [StaticAccessor("BakedGISceneManager::Get()", StaticAccessorType.Arrow)] internal static extern float GetLightmapBakeTimeTotal(); - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] + [StaticAccessor("BakedGISceneManager::Get()", StaticAccessorType.Arrow)] [NativeName("GetLightmapBakePerformance")] internal static extern float GetLightmapBakePerformanceTotal(); - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] - internal static extern float GetLightmapBakePerformance(int lightmapIndex); - - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] + [StaticAccessor("BakedGISceneManager::Get()", StaticAccessorType.Arrow)] internal static extern string GetLightmapBakeGPUDeviceName(); - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] + [StaticAccessor("BakedGISceneManager::Get()", StaticAccessorType.Arrow)] internal static extern int GetLightmapBakeGPUDeviceIndex(); - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] - extern internal static DeviceAndPlatform[] GetLightmappingGpuDevices(); + [StaticAccessor("BakedGISceneManager::Get()", StaticAccessorType.Arrow)] + internal static extern DeviceAndPlatform[] GetLightmappingGpuDevices(); // Exports the current state of the scene to the dynamic GI workflow. [FreeFunction] @@ -320,6 +314,7 @@ internal static FilterMode filterMode // Stops the current bake at the state it has reached so far. [FreeFunction] + [System.Obsolete("ForceStop is no longer available, use Cancel instead to stop a bake.", false)] public static extern void ForceStop(); // Returns true when the bake job is running, false otherwise (RO). @@ -411,6 +406,20 @@ internal static void Internal_CallOnWroteLightingDataAsset() wroteLightingDataAsset(); } + // This event is fired when BakeInput has been populated, but before passing it to Bake(). + // Do not store and access BakeInput beyond the call-back. + internal static event Action createdBakeInput; + + internal static void Internal_CallOnCreatedBakeInput(IntPtr p_BakeInput, IntPtr p_SourceMap) + { + if (createdBakeInput != null) + { + using var bakeInput = new LightBaker.BakeInput(p_BakeInput); + using var sourceMap = new InputExtraction.SourceMap(p_SourceMap); + createdBakeInput(bakeInput, sourceMap); + } + } + [System.Obsolete("OnCompletedFunction.completed is obsolete, please use event bakeCompleted instead. ", false)] public static OnCompletedFunction completed; @@ -458,17 +467,6 @@ public static void Tetrahedralize(Vector3[] positions, out int[] outIndices, out [FreeFunction] private static extern TetrahedralizationData TetrahedralizeInternal(Vector3[] positions); - internal static void GetEnvironmentSamples(out Vector4[] outDirections, out Vector4[] outIntensities) - { - EnvironmentSamplesData data = GetEnvironmentSamplesInternal(); - outDirections = data.directions; - outIntensities = data.intensities; - } - - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] - [NativeName("GetEnvironmentSamples")] - private static extern EnvironmentSamplesData GetEnvironmentSamplesInternal(); - [FreeFunction] public static extern bool BakeReflectionProbe(ReflectionProbe probe, string path); @@ -680,18 +678,6 @@ public static void BakeMultipleScenes(string[] paths) [NativeHeader("Editor/Src/GI/EditorHelpers.h")] extern static internal bool GetInstanceHash([NotNull("NullExceptionObject")] Renderer renderer, out Hash128 instanceHash); - [FreeFunction] - [NativeHeader("Editor/Src/GI/EditorHelpers.h")] - extern static internal bool GetPVRInstanceHash(int instanceID, out Hash128 instanceHash); - - [FreeFunction] - [NativeHeader("Editor/Src/GI/EditorHelpers.h")] - extern static internal bool GetPVRAtlasHash(int instanceID, out Hash128 atlasHash); - - [FreeFunction] - [NativeHeader("Editor/Src/GI/EditorHelpers.h")] - extern static internal bool GetPVRAtlasInstanceOffset(int instanceID, out int atlasInstanceOffset); - [FreeFunction] [NativeHeader("Editor/Src/GI/EditorHelpers.h")] extern static internal bool GetGeometryHash([NotNull("NullExceptionObject")] Renderer renderer, out Hash128 geometryHash); @@ -706,39 +692,28 @@ namespace UnityEditor.Experimental { public sealed partial class Lightmapping { - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] + [StaticAccessor("BakedGISceneManager::Get()", StaticAccessorType.Arrow)] public static extern bool probesIgnoreDirectEnvironment { get; set; } - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] - private unsafe static extern void SetCustomBakeInputs([Span("inputDataLength", isReadOnly:true)]Vector4* inputData, int inputDataLength, int sampleCount); - public static void SetCustomBakeInputs(Vector4[] inputData, int sampleCount) { SetCustomBakeInputs(inputData.AsSpan(), sampleCount); } - public static unsafe void SetCustomBakeInputs(ReadOnlySpan inputData, int sampleCount) - { - fixed(Vector4* inputDataPtr = inputData) - { - SetCustomBakeInputs(inputDataPtr, inputData.Length, sampleCount); - } - } + [StaticAccessor("BakedGISceneManager::Get()", StaticAccessorType.Arrow)] + public static extern void SetCustomBakeInputs(ReadOnlySpan inputData, int sampleCount); - - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] - private static unsafe extern bool GetCustomBakeResultsCopy([Span("resultsLength")]Vector4* results, int resultsLength); - public static unsafe bool GetCustomBakeResults(Span results) + [StaticAccessor("BakedGISceneManager::Get()", StaticAccessorType.Arrow)] + private static extern unsafe bool GetCustomBakeResultsCopy(Span results); + public static bool GetCustomBakeResults(Span results) { - fixed (Vector4* resultsPtr = results) { - return GetCustomBakeResultsCopy(resultsPtr, results.Length); - } + return GetCustomBakeResultsCopy(results); } public static bool GetCustomBakeResults([Out] Vector4[] results) { return GetCustomBakeResults(results.AsSpan()); } - [StaticAccessor("ProgressiveRuntimeManager::Get()", StaticAccessorType.Arrow)] + [StaticAccessor("BakedGISceneManager::Get()", StaticAccessorType.Arrow)] public static extern ReadOnlySpan GetCustomBakeResultsNoCopy(); [Obsolete("UnityEditor.Experimental.Lightmapping.extractAmbientOcclusion is obsolete, use LightingSettings.extractAO instead. ", false)] @@ -833,9 +808,6 @@ public unsafe static bool GetAdditionalBakedProbes(int id, NativeArray positi { SetAdditionalBakedProbes(id, positions, true); } - public static unsafe void SetAdditionalBakedProbes(int id, ReadOnlySpan positions, bool dering) - { - fixed(Vector3* positionsPtr = positions) - { - SetAdditionalBakedProbes(id, positionsPtr, positions.Length, dering); - } - } + [FreeFunction] + public static extern void SetAdditionalBakedProbes(int id, ReadOnlySpan positions, bool dering); [FreeFunction] public static extern void SetLightDirty(Light light); diff --git a/Editor/Mono/GUI/AppStatusBar.cs b/Editor/Mono/GUI/AppStatusBar.cs index 2e425affc3..e2cee3898e 100644 --- a/Editor/Mono/GUI/AppStatusBar.cs +++ b/Editor/Mono/GUI/AppStatusBar.cs @@ -61,11 +61,15 @@ static Styles() const double k_CheckUnresponsiveFrequencyInSecond = 0.5; const float k_ShowProgressThreshold = 0.5f; private double m_LastUpdate; + private bool m_IsQuitting; private bool showBakeMode { get { + if (m_IsQuitting) + return false; + var settings = Lightmapping.GetLightingSettingsOrDefaultsFallback(); return settings.bakedGI || settings.realtimeGI; } @@ -93,6 +97,8 @@ protected override void OnEnable() Progress.added += RefreshProgressBar; Progress.removed += RefreshProgressBar; Progress.updated += RefreshProgressBar; + + EditorApplication.editorApplicationQuit += OnQuit; } protected override void OnDisable() @@ -100,6 +106,7 @@ protected override void OnDisable() Progress.added -= RefreshProgressBar; Progress.removed -= RefreshProgressBar; Progress.updated -= RefreshProgressBar; + EditorApplication.editorApplicationQuit -= OnQuit; EditorApplication.delayCall -= DelayRepaint; base.OnDisable(); } @@ -185,7 +192,6 @@ protected override void OldOnGUI() } GUILayout.EndHorizontal(); - DoWindowDecorationEnd(); EditorGUI.ShowRepaints(); } @@ -475,6 +481,8 @@ private GUIContent GetBakeModeIcon(bool? bakeMode) return Lightmapping.GetLightingSettingsOrDefaultsFallback().autoGenerate; } + private void OnQuit() + => m_IsQuitting = true; [RequiredByNativeCode] public static void StatusChanged() diff --git a/Editor/Mono/GUI/CacheServerWindow.cs b/Editor/Mono/GUI/CacheServerWindow.cs index ccde9e28b2..c9f0063a30 100644 --- a/Editor/Mono/GUI/CacheServerWindow.cs +++ b/Editor/Mono/GUI/CacheServerWindow.cs @@ -15,9 +15,16 @@ internal class CacheServerWindow : PopupWindowContent private readonly GUIContent m_StatusMessageConnected; private readonly GUIContent m_StatusMessageError; private readonly GUIContent m_OpenProjectSettings; - + private readonly GUIContent m_UploadArtifacts; + private readonly GUIContent m_UploadShaderCache; + private readonly GUIContent m_UploadAllRevisions; private readonly GUIContent m_RefreshIcon; + readonly string m_UploadArtifactsDefaultToolip = "Queues the upload of the current revision of all Artifacts present in the project. Only revisions not found on the Accelerator are uploaded."; + readonly string m_UploadShaderCacheDefaultToolip = "Queues the upload of all Shaders, and their variants, from the Unity Shader Cache. Only shaders and/or variants not found on the Accelerator are uploaded."; + readonly string m_UploadAllRevisionsDefaultToolip = "Queues upload of all revisions of every Artifact in the project. Only revisions not found on the Accelerator are uploaded."; + readonly string m_DisabledSettingPrefix = "Uploading is currently disabled in Project Settings."; + private readonly GUIStyle m_WindowStyle; public CacheServerWindow() @@ -26,7 +33,9 @@ public CacheServerWindow() m_StatusMessageConnected = EditorGUIUtility.TrTextContent("Connected"); m_StatusMessageError = EditorGUIUtility.TrTextContent("Attempting to reconnect"); m_OpenProjectSettings = EditorGUIUtility.TrTextContent("Open Project Settings..."); - + m_UploadArtifacts = EditorGUIUtility.TrTextContent("Upload Artifacts", m_UploadArtifactsDefaultToolip); + m_UploadShaderCache = EditorGUIUtility.TrTextContent("Upload Shader Cache", m_UploadShaderCacheDefaultToolip); + m_UploadAllRevisions = EditorGUIUtility.TrTextContent("Upload All Revisions", m_UploadAllRevisionsDefaultToolip); m_RefreshIcon = EditorGUIUtility.TrIconContent("Refresh", "Refresh connection"); m_WindowStyle = new GUIStyle { padding = new RectOffset(6, 6, 6, 6) }; @@ -36,9 +45,14 @@ public override void OnGUI(Rect rect) { var exit = false; + bool isCacheConnected = AssetDatabase.IsConnectedToCacheServer(); + + bool isCacheEnabled = AssetDatabase.IsCacheServerEnabled(); + bool isUploadEnabled = AssetDatabase.GetCacheServerEnableUpload(); + GUILayout.BeginArea(rect, m_WindowStyle); // Cache server connection url - if (AssetDatabase.IsCacheServerEnabled()) + if (isCacheEnabled) { var iconPosition = new Rect(); iconPosition.x = rect.width - (m_RefreshIcon.image.width / (Screen.dpi > 160 ? 2 : 1)) - m_WindowStyle.padding.right; @@ -55,7 +69,7 @@ public override void OnGUI(Rect rect) var style = new GUIStyle(); style.fontStyle = FontStyle.Bold; style.normal.textColor = EditorStyles.boldLabel.normal.textColor; - if (!AssetDatabase.IsConnectedToCacheServer()) + if (!isCacheConnected) { style.normal.textColor = new Color(0.97f, 0.32f, 0.31f); } @@ -78,6 +92,40 @@ public override void OnGUI(Rect rect) EditorGUILayout.LabelField(ConnectionStatusText(), statusTextStyle); GUILayout.EndHorizontal(); + if(isCacheConnected && isCacheEnabled) + { + // Divider line + var actionButtonLine = EditorGUILayout.GetControlRect(GUILayout.Height(1)); + actionButtonLine.x -= 6; + actionButtonLine.width += 12; + EditorGUI.DrawRect(actionButtonLine, new Color(0.387f, 0.387f, 0.387f)); + + GUILayout.BeginHorizontal(); + using (new EditorGUI.DisabledScope(!isUploadEnabled || !isCacheEnabled)) + { + // change tooltip based on Project Settings + m_UploadArtifacts.tooltip = isUploadEnabled ? m_UploadArtifactsDefaultToolip : $"{m_DisabledSettingPrefix} {m_UploadArtifactsDefaultToolip}"; + m_UploadShaderCache.tooltip = isUploadEnabled ? m_UploadShaderCacheDefaultToolip : $"{m_DisabledSettingPrefix} {m_UploadShaderCacheDefaultToolip}"; + m_UploadAllRevisions.tooltip = isUploadEnabled ? m_UploadAllRevisionsDefaultToolip : $"{m_DisabledSettingPrefix} {m_UploadAllRevisionsDefaultToolip}"; + + if (GUILayout.Button(m_UploadArtifacts, GUILayout.Width(110))) + { + CacheServer.UploadArtifacts(); + } + + if (GUILayout.Button(m_UploadShaderCache, GUILayout.Width(140))) + { + CacheServer.UploadShaderCache(); + } + + if (GUILayout.Button(m_UploadAllRevisions, GUILayout.Width(130))) + { + CacheServer.UploadArtifacts(uploadAllRevisions:true); + } + } + GUILayout.EndHorizontal(); + } + // Divider line var lineRect = EditorGUILayout.GetControlRect(GUILayout.Height(1)); lineRect.x -= 6; @@ -136,10 +184,22 @@ private void OpenPreferences() public override Vector2 GetWindowSize() { int lines = AssetDatabase.IsCacheServerEnabled() ? 3 : 2; + bool isConnected = AssetDatabase.IsConnectedToCacheServer(); + if (isConnected) + lines++; + int heightOfLines = (int)Math.Ceiling(EditorGUI.kSingleLineHeight * lines); int heightOfWindowPadding = m_WindowStyle.padding.top + m_WindowStyle.padding.bottom; int dividerLine = 2; - return new Vector2(250, heightOfLines + heightOfWindowPadding + dividerLine); + + if (isConnected) + dividerLine += 2; + + int width = 250; + if (isConnected) + width = 390; + + return new Vector2(width, heightOfLines + heightOfWindowPadding + dividerLine); } } } diff --git a/Editor/Mono/GUI/ColorMutator.cs b/Editor/Mono/GUI/ColorMutator.cs index 8ddf00633e..c335e38cd4 100644 --- a/Editor/Mono/GUI/ColorMutator.cs +++ b/Editor/Mono/GUI/ColorMutator.cs @@ -18,7 +18,7 @@ internal class ColorMutator { // specifies the max byte value to use when decomposing a float color into bytes with exposure // this is the value used by Photoshop - private const byte k_MaxByteForOverexposedColor = 191; + const byte k_MaxByteForOverexposedColor = 191; internal static void DecomposeHdrColor(Color linearColorHdr, out Color32 baseLinearColor, out float exposure) { @@ -46,23 +46,33 @@ internal static void DecomposeHdrColor(Color linearColorHdr, out Color32 baseLin } } - public Color originalColor => m_OriginalColor; [SerializeField] private Color m_OriginalColor; [SerializeField] private Color m_HDRBaseColor; + [SerializeField] private byte[] m_Color = new byte[4]; + [SerializeField] private float[] m_ColorHdr = new float[4]; + [SerializeField] private float[] m_Hsv = new float[3]; + [SerializeField] private float m_ExposureValue; + [SerializeField] private float m_BaseExposureValue; + + public Color originalColor => m_OriginalColor; + public Color32 color => new(m_Color[(int)RgbaChannel.R], m_Color[(int)RgbaChannel.G], m_Color[(int)RgbaChannel.B], m_Color[(int)RgbaChannel.A]); + public Vector3 colorHsv => new(m_Hsv[(int)HsvChannel.H], m_Hsv[(int)HsvChannel.S], m_Hsv[(int)HsvChannel.V]); + public Color exposureAdjustedColor => new(m_ColorHdr[(int)RgbaChannel.R], m_ColorHdr[(int)RgbaChannel.G], m_ColorHdr[(int)RgbaChannel.B], m_ColorHdr[(int)RgbaChannel.A]); - public Color32 color + public float exposureValue { - get + get => m_ExposureValue; + set { - return new Color32( - m_Color[(int)RgbaChannel.R], - m_Color[(int)RgbaChannel.G], - m_Color[(int)RgbaChannel.B], - m_Color[(int)RgbaChannel.A] - ); + if (Mathf.Approximately(m_ExposureValue, value)) + return; + m_ExposureValue = value; + var newRgbFloat = m_HDRBaseColor * Mathf.Pow(2f, m_ExposureValue - m_BaseExposureValue); + m_ColorHdr[(int)RgbaChannel.R] = newRgbFloat.r; + m_ColorHdr[(int)RgbaChannel.G] = newRgbFloat.g; + m_ColorHdr[(int)RgbaChannel.B] = newRgbFloat.b; } } - [SerializeField] private byte[] m_Color = new byte[4]; public byte GetColorChannel(RgbaChannel channel) { @@ -76,18 +86,14 @@ public float GetColorChannelNormalized(RgbaChannel channel) public void SetColorChannel(RgbaChannel channel, byte value) { - if (m_Color[(int)channel] == value) + var channelIndex = (int)channel; + if (m_Color[channelIndex] == value) return; - m_Color[(int)channel] = value; - m_ColorHdr[(int)channel] = (value / 255f); + m_Color[channelIndex] = value; + m_ColorHdr[channelIndex] = value / 255f; if (channel != RgbaChannel.A) - m_ColorHdr[(int)channel] *= Mathf.Pow(2f, m_ExposureValue); - Color.RGBToHSV( - color, - out m_Hsv[(int)HsvChannel.H], - out m_Hsv[(int)HsvChannel.S], - out m_Hsv[(int)HsvChannel.V] - ); + m_ColorHdr[channelIndex] *= Mathf.Pow(2f, m_ExposureValue); + Color.RGBToHSV(color, out m_Hsv[(int)HsvChannel.H], out m_Hsv[(int)HsvChannel.S], out m_Hsv[(int)HsvChannel.V]); } public void SetColorChannel(RgbaChannel channel, float normalizedValue) @@ -95,20 +101,6 @@ public void SetColorChannel(RgbaChannel channel, float normalizedValue) SetColorChannel(channel, (byte)Mathf.RoundToInt(Mathf.Clamp01(normalizedValue) * 255f)); } - public Color exposureAdjustedColor - { - get - { - return new Color( - m_ColorHdr[(int)RgbaChannel.R], - m_ColorHdr[(int)RgbaChannel.G], - m_ColorHdr[(int)RgbaChannel.B], - m_ColorHdr[(int)RgbaChannel.A] - ); - } - } - [SerializeField] private float[] m_ColorHdr = new float[4]; - public float GetColorChannelHdr(RgbaChannel channel) { return m_ColorHdr[(int)channel]; @@ -116,38 +108,21 @@ public float GetColorChannelHdr(RgbaChannel channel) public void SetColorChannelHdr(RgbaChannel channel, float value) { - if (m_ColorHdr[(int)channel] == value) + if (Mathf.Approximately(m_ColorHdr[(int)channel], value)) return; m_ColorHdr[(int)channel] = value; m_HDRBaseColor = new Color(m_ColorHdr[0], m_ColorHdr[1], m_ColorHdr[2], m_ColorHdr[3]); OnRgbaHdrChannelChanged((int)channel); + m_BaseExposureValue = m_ExposureValue; } - public Vector3 colorHsv - { - get - { - return new Vector3( - m_Hsv[(int)HsvChannel.H], - m_Hsv[(int)HsvChannel.S], - m_Hsv[(int)HsvChannel.V] - ); - } - } - [SerializeField] private float[] m_Hsv = new float[3]; - - public float GetColorChannel(HsvChannel channel) - { - return m_Hsv[(int)channel]; - } + public float GetColorChannel(HsvChannel channel) => m_Hsv[(int)channel]; public void SetColorChannel(HsvChannel channel, float value) { m_Hsv[(int)channel] = Mathf.Clamp01(value); - var newColor = Color.HSVToRGB( - m_Hsv[(int)HsvChannel.H], m_Hsv[(int)HsvChannel.S], m_Hsv[(int)HsvChannel.V] - ); + var newColor = Color.HSVToRGB(m_Hsv[(int)HsvChannel.H], m_Hsv[(int)HsvChannel.S], m_Hsv[(int)HsvChannel.V]); m_Color[(int)RgbaChannel.R] = (byte)Mathf.CeilToInt(newColor.r * 255f); m_Color[(int)RgbaChannel.G] = (byte)Mathf.CeilToInt(newColor.g * 255f); m_Color[(int)RgbaChannel.B] = (byte)Mathf.CeilToInt(newColor.b * 255f); @@ -159,33 +134,18 @@ public void SetColorChannel(HsvChannel channel, float value) m_HDRBaseColor = new Color(m_ColorHdr[0], m_ColorHdr[1], m_ColorHdr[2], m_ColorHdr[3]); } - public float exposureValue - { - get { return m_ExposureValue; } - set - { - if (m_ExposureValue == value) - return; - m_ExposureValue = value; - var newRgbFloat = m_HDRBaseColor * Mathf.Pow(2f, m_ExposureValue); - m_ColorHdr[(int)RgbaChannel.R] = newRgbFloat.r; - m_ColorHdr[(int)RgbaChannel.G] = newRgbFloat.g; - m_ColorHdr[(int)RgbaChannel.B] = newRgbFloat.b; - } - } - [SerializeField] private float m_ExposureValue; - public ColorMutator(Color originalColor) { m_OriginalColor = originalColor; - m_HDRBaseColor = originalColor; Reset(); } public void Reset() { - if (m_ColorHdr == null || m_ColorHdr.Length != 4) + if (m_ColorHdr is not { Length: 4 }) m_ColorHdr = new float[4]; + if (m_Color is not { Length: 4 }) + m_Color = new byte[4]; m_ColorHdr[(int)RgbaChannel.R] = m_OriginalColor.r; m_ColorHdr[(int)RgbaChannel.G] = m_OriginalColor.g; @@ -193,10 +153,8 @@ public void Reset() m_ColorHdr[(int)RgbaChannel.A] = m_OriginalColor.a; m_HDRBaseColor = new Color(m_ColorHdr[0], m_ColorHdr[1], m_ColorHdr[2], m_ColorHdr[3]); - if (m_Color == null || m_Color.Length != 4) - m_Color = new byte[4]; - OnRgbaHdrChannelChanged(-1); + m_BaseExposureValue = m_ExposureValue; } void OnRgbaHdrChannelChanged(int channel) @@ -205,17 +163,12 @@ void OnRgbaHdrChannelChanged(int channel) if (channel == (int)RgbaChannel.A) return; - Color32 baseColor; - DecomposeHdrColor(exposureAdjustedColor, out baseColor, out m_ExposureValue); + DecomposeHdrColor(exposureAdjustedColor, out var baseColor, out m_ExposureValue); + m_Color[(int)RgbaChannel.R] = baseColor.r; m_Color[(int)RgbaChannel.G] = baseColor.g; m_Color[(int)RgbaChannel.B] = baseColor.b; - Color.RGBToHSV( - color, - out m_Hsv[(int)HsvChannel.H], - out m_Hsv[(int)HsvChannel.S], - out m_Hsv[(int)HsvChannel.V] - ); + Color.RGBToHSV(color, out m_Hsv[(int)HsvChannel.H], out m_Hsv[(int)HsvChannel.S], out m_Hsv[(int)HsvChannel.V]); } } } diff --git a/Editor/Mono/GUI/ColorPicker.cs b/Editor/Mono/GUI/ColorPicker.cs index 350cc124bf..006b6724a4 100644 --- a/Editor/Mono/GUI/ColorPicker.cs +++ b/Editor/Mono/GUI/ColorPicker.cs @@ -479,7 +479,7 @@ static class Styles public static readonly GUIStyle selectedExposureSwatchStroke = "ColorPickerCurrentExposureSwatchBorder"; public static readonly GUIContent eyeDropper = EditorGUIUtility.TrIconContent("EyeDropper.Large", "Pick a color from the screen."); - public static readonly GUIContent exposureValue = EditorGUIUtility.TrTextContent("Intensity", "Number of stops to over- or under-expose the color."); + public static readonly GUIContent exposureValue = EditorGUIUtility.TrTextContent("Intensity", "Number of stops to over- or under-expose the color. The intensity calculates each time based on the predefined max color component of 191 (0.749) when Color Picker opens."); public static readonly GUIContent hexLabel = EditorGUIUtility.TrTextContent("Hexadecimal"); public static readonly GUIContent presetsToggle = EditorGUIUtility.TrTextContent("Swatches"); @@ -493,11 +493,23 @@ static class Styles public static readonly Texture2D alphaSliderCheckerBackground = EditorGUIUtility.LoadRequired("Previews/Textures/textureChecker.png") as Texture2D; - public static readonly GUIContent[] sliderModeLabels = new[] + public static readonly GUIContent RGB0_255Mode = EditorGUIUtility.TrTextContent("RGB 0-255"); + public static readonly GUIContent RGB0_1Mode = EditorGUIUtility.TrTextContent("RGB 0-1.0"); + public static readonly GUIContent RGB0_InfMode = EditorGUIUtility.TrTextContent("RGB 0-Inf"); + public static readonly GUIContent RGBHSVMode = EditorGUIUtility.TrTextContent("HSV"); + + public static readonly GUIContent[] sliderLDRModeLabels = new[] + { + RGB0_255Mode, + RGB0_1Mode, + RGBHSVMode + }; + + public static readonly GUIContent[] sliderHDRModeLabels = new[] { - EditorGUIUtility.TrTextContent("RGB 0-255"), - EditorGUIUtility.TrTextContent("RGB 0-1.0"), - EditorGUIUtility.TrTextContent("HSV") + RGB0_255Mode, + RGB0_InfMode, + RGBHSVMode }; public static readonly int[] sliderModeValues = new[] { 0, 1, 2 }; @@ -699,9 +711,10 @@ void DoColorSliders(float availableWidth) float oldFieldWidth = EditorGUIUtility.fieldWidth; EditorGUIUtility.labelWidth = availableWidth - Styles.sliderModeFieldWidth; EditorGUIUtility.fieldWidth = Styles.sliderModeFieldWidth; - m_SliderMode = (SliderMode)EditorGUILayout.IntPopup( - GUIContent.Temp(" "), (int)m_SliderMode, Styles.sliderModeLabels, Styles.sliderModeValues - ); + if(m_HDR && m_Color.exposureAdjustedColor.maxColorComponent > 1f) + m_SliderMode = (SliderMode)EditorGUILayout.IntPopup(GUIContent.Temp(" "), (int)m_SliderMode, Styles.sliderHDRModeLabels, Styles.sliderModeValues); + else + m_SliderMode = (SliderMode)EditorGUILayout.IntPopup(GUIContent.Temp(" "), (int)m_SliderMode, Styles.sliderLDRModeLabels, Styles.sliderModeValues); GUILayout.Space(Styles.extraVerticalSpacing); @@ -1236,6 +1249,9 @@ public static void Start(Action colorPickedCallback) static void Start(GUIView viewToUpdate, Action colorPickedCallback) { + if(!InternalEditorUtility.IsAllowedToReadPixelOutsideUnity(out var errorMessage)) + Debug.LogWarning(errorMessage); + instance.m_DelegateView = viewToUpdate; instance.m_ColorPickedCallback = colorPickedCallback; ContainerWindow win = CreateInstance(); @@ -1250,6 +1266,7 @@ static void Start(GUIView viewToUpdate, Action colorPickedCallback) Vector2 p = GUIUtility.GUIToScreenPoint(Event.current != null ? Event.current.mousePosition : Vector2.zero); win.position = new Rect(p.x - kDummyWindowSize / 2, p.y - kDummyWindowSize / 2, kDummyWindowSize, kDummyWindowSize); instance.wantsMouseMove = true; + instance.Focus(); instance.SetEyeDropperOpen(true); instance.StealMouseCapture(); instance.m_IsOpened = true; diff --git a/Editor/Mono/GUI/DockArea.cs b/Editor/Mono/GUI/DockArea.cs index 65bea38bfc..df12f81bfd 100644 --- a/Editor/Mono/GUI/DockArea.cs +++ b/Editor/Mono/GUI/DockArea.cs @@ -74,6 +74,22 @@ private static class Styles private static DropInfo s_DropInfo = null; private static Dictionary s_GUIContents = new Dictionary(); + private static bool s_IsCapabilitySet; + private static bool s_HasCapabilityCached; + private static bool s_HasCapability + { + get + { + if (s_IsCapabilitySet) + return s_HasCapabilityCached; + + s_IsCapabilitySet = true; + s_HasCapabilityCached = ModeService.HasCapability(ModeCapability.StaticTabs, false); + ModeService.modeChanged += (_) => s_HasCapabilityCached = ModeService.HasCapability(ModeCapability.StaticTabs, false); + return s_HasCapabilityCached; + } + } + [SerializeField] internal List m_Panes = new List(); [SerializeField] internal int m_Selected; [SerializeField] internal int m_LastSelected; @@ -118,27 +134,13 @@ private void RemoveNullWindows() s_GUIContents.Clear(); } - internal override void DoWindowDecorationStart() - { - // On windows, we want both close window and side resizes. - // Titlebar dragging is done at the end, so we can drag next to tabs. - if (window != null) - window.HandleWindowDecorationStart(windowPosition); - } - - internal override void DoWindowDecorationEnd() - { - if (window != null) - window.HandleWindowDecorationEnd(windowPosition); - } - protected override void OnDestroy() { // Prevents double-destroy that may be indirectly caused if Close() is called by OnLostFocus() m_IsBeingDestroyed = true; if (hasFocus) - m_OnLostFocus?.Invoke(); + OnLostFocus(); actualView = null; @@ -152,7 +154,6 @@ protected override void OnDestroy() continue; UnityEngine.Object.DestroyImmediate(w, true); - EditorWindow.UpdateWindowMenuListing(); } m_Panes.Clear(); @@ -248,8 +249,14 @@ public void RemoveTab(EditorWindow pane, bool killIfEmpty, bool sendEvents = tru private static void UpdateWindowTitle(EditorWindow w) { - if (w && w.m_Parent && w.m_Parent.window && w.titleContent != null) + if (w && + w.m_Parent && + w.m_Parent.window && + !w.m_Parent.window.m_IsMppmCloneWindow && + w.titleContent != null) + { w.m_Parent.window.title = w.titleContent.text; + } } private static void UpdateWindowHasUnsavedChanges(EditorWindow w) @@ -408,7 +415,7 @@ private void DrawTabs(Rect tabAreaRect) tabStyle = Styles.dragTab; var firstTabStyle = Styles.dragTabFirst; - if (ModeService.HasCapability(ModeCapability.StaticTabs, false)) + if (s_HasCapability) { firstTabStyle = Styles.tabLabel; } @@ -804,7 +811,7 @@ private float DragTab(Rect tabAreaRect, float scrollOffset, GUIStyle tabStyle, G { case EventType.TouchDown: case EventType.MouseDown: - if (ModeService.HasCapability(ModeCapability.StaticTabs, false)) + if (s_HasCapability) { break; } @@ -836,7 +843,7 @@ private float DragTab(Rect tabAreaRect, float scrollOffset, GUIStyle tabStyle, G } break; case EventType.ContextClick: - if (ModeService.HasCapability(ModeCapability.StaticTabs, false)) + if (s_HasCapability) { break; } @@ -853,10 +860,10 @@ private float DragTab(Rect tabAreaRect, float scrollOffset, GUIStyle tabStyle, G case EventType.TouchMove: case EventType.MouseDrag: - if (ModeService.HasCapability(ModeCapability.StaticTabs, false)) + if (s_HasCapability) { - break; - } + break; + } if (evt.pointerType == PointerType.Pen && !evt.penStatus.HasFlag(PenStatus.Contact)) break; @@ -961,7 +968,7 @@ private float DragTab(Rect tabAreaRect, float scrollOffset, GUIStyle tabStyle, G break; case EventType.TouchUp: case EventType.MouseUp: - if (ModeService.HasCapability(ModeCapability.StaticTabs, false)) + if (s_HasCapability) { break; } diff --git a/Editor/Mono/GUI/EditorApplicationLayout.cs b/Editor/Mono/GUI/EditorApplicationLayout.cs index d893abcd75..f125a3fc99 100644 --- a/Editor/Mono/GUI/EditorApplicationLayout.cs +++ b/Editor/Mono/GUI/EditorApplicationLayout.cs @@ -23,12 +23,13 @@ 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_PlayModeViewList != null && m_PlayModeViewList.Count > 0; + return m_PlayModeView != null; } static internal void SetPlaymodeLayout() @@ -39,22 +40,6 @@ 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(); } @@ -65,101 +50,53 @@ 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() { - InitializePlaymodeViewList(); - WindowLayout.ShowAppropriateViewOnEnterExitPlaymodeList(true, out m_PlayModeViewList); + m_PlayModeView = WindowLayout.ShowAppropriateViewOnEnterExitPlaymode(true) as PlayModeView; + if (m_PlayModeView == null) + return; - var fullscreenDetected = false; - var monitorNames = EditorFullscreenController.GetConnectedDisplayNames(); - - foreach (var playModeView in m_PlayModeViewList) + if (m_PlayModeView.enterPlayModeBehavior == PlayModeView.EnterPlayModeBehavior.PlayMaximized) { - if (playModeView == null) - continue; - - 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) + if (m_PlayModeView.m_Parent is DockArea dockArea) { - EditorFullscreenController.isFullscreenOnPlay = false; - } + m_MaximizePending = WindowLayout.MaximizePrepare(dockArea.actualView); - 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); - } - } + var gameView = dockArea.actualView as GameView; + if (gameView != null) + m_PlayModeView.m_Parent.EnableVSync(gameView.vSyncEnabled); } + } - EditorFullscreenController.OnEnterPlaymode(); + // 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 (!EditorFullscreenController.isFullscreenOnPlay) - { - playModeView.m_Parent.SetAsStartView(); - playModeView.m_Parent.SetAsLastPlayModeView(); - - if (playModeView is IGameViewOnPlayMenuUser) - { - if (((IGameViewOnPlayMenuUser)playModeView).playFocused) - { - playModeView.Focus(); - } - } - } - Toolbar.RepaintToolbar(); - } + //GameView should be actively focussed If Playmode is entered in maximized state - case 1252097 + if (m_PlayModeView.maximized) + m_PlayModeView.m_Parent.Focus(); + + Toolbar.RepaintToolbar(); } static internal void FinalizePlaymodeLayout() { - foreach (var playModeView in m_PlayModeViewList) + if (m_PlayModeView != null) { - if (playModeView != null) - { - if (m_MaximizePending) - WindowLayout.MaximizePresent(playModeView); + if (m_MaximizePending) + WindowLayout.MaximizePresent(m_PlayModeView); - // All StartView references on all play mode views must be cleared before play mode starts. Otherwise it may cause issues - // with input being routed to the correct game window. See case 1381985 - playModeView.m_Parent.ClearStartView(); - } + m_PlayModeView.m_Parent.ClearStartView(); } + + Clear(); + } + + static private void Clear() + { + m_MaximizePending = false; + m_PlayModeView = null; } } } // namespace diff --git a/Editor/Mono/GUI/EditorCache.cs b/Editor/Mono/GUI/EditorCache.cs index 03ffb9404a..7a94acc92e 100644 --- a/Editor/Mono/GUI/EditorCache.cs +++ b/Editor/Mono/GUI/EditorCache.cs @@ -67,10 +67,17 @@ private bool Init(Object obj, EditorFeatures requirements) // Are we dealing with a Root Editor? if (editor.GetType() != nonRootEditor.GetType()) { - // Try again, with a normal editor + // Destroy previous editor + UnityEngine.Object.DestroyImmediate(editor); + // Try again, with a non Root editor editor = nonRootEditor; onSceneDragMethodInfo = editor.GetType().GetMethod("OnSceneDrag", sceneDragReflectionFlags); } + else + { + // Destroy unused non Root editor + UnityEngine.Object.DestroyImmediate(nonRootEditor); + } } if (onSceneDragMethodInfo != null) diff --git a/Editor/Mono/GUI/EditorStyles.cs b/Editor/Mono/GUI/EditorStyles.cs index a07eb373b5..b2cce459ba 100644 --- a/Editor/Mono/GUI/EditorStyles.cs +++ b/Editor/Mono/GUI/EditorStyles.cs @@ -475,13 +475,13 @@ private void InitSharedStyles() m_ToolbarCreateAddNewDropDown = GetStyle("ToolbarCreateAddNewDropDown"); m_ToolbarTextField = GetStyle("toolbarTextField"); m_ToolbarLabel = GetStyle("ToolbarLabel"); - m_ToolbarSearchField = GetStyle("ToolbarSeachTextField"); - m_ToolbarSearchFieldPopup = GetStyle("ToolbarSeachTextFieldPopup"); + m_ToolbarSearchField = GetStyle("ToolbarSearchTextField"); + m_ToolbarSearchFieldPopup = GetStyle("ToolbarSearchTextFieldPopup"); m_ToolbarSearchFieldWithJump = GetStyle("ToolbarSearchTextFieldWithJump"); m_ToolbarSearchFieldWithJumpPopup = GetStyle("ToolbarSearchTextFieldWithJumpPopup"); m_ToolbarSearchFieldJumpButton = GetStyle("ToolbarSearchTextFieldJumpButton"); - m_ToolbarSearchFieldCancelButton = GetStyle("ToolbarSeachCancelButton"); - m_ToolbarSearchFieldCancelButtonEmpty = GetStyle("ToolbarSeachCancelButtonEmpty"); + m_ToolbarSearchFieldCancelButton = GetStyle("ToolbarSearchCancelButton"); + m_ToolbarSearchFieldCancelButtonEmpty = GetStyle("ToolbarSearchCancelButtonEmpty"); m_ToolbarSearchFieldCancelButtonWithJump = GetStyle("ToolbarSearchCancelButtonWithJump"); m_ToolbarSearchFieldCancelButtonWithJumpEmpty = GetStyle("ToolbarSearchCancelButtonWithJumpEmpty"); m_ToolbarSearchFieldWithJumpSynced = GetStyle("ToolbarSearchTextFieldWithJumpSynced"); diff --git a/Editor/Mono/GUI/InternalEditorGUI.cs b/Editor/Mono/GUI/InternalEditorGUI.cs index 1dfc7f46b3..c495c3210f 100644 --- a/Editor/Mono/GUI/InternalEditorGUI.cs +++ b/Editor/Mono/GUI/InternalEditorGUI.cs @@ -243,18 +243,6 @@ 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 db0e20fb68..dba74acbd3 100644 --- a/Editor/Mono/GUI/InternalEditorGUILayout.cs +++ b/Editor/Mono/GUI/InternalEditorGUILayout.cs @@ -25,12 +25,6 @@ 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 31fed6affb..abb202fea9 100644 --- a/Editor/Mono/GUI/LazyLoadReferenceField.cs +++ b/Editor/Mono/GUI/LazyLoadReferenceField.cs @@ -2,6 +2,7 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using UnityEditor.UIElements; using UnityEngine; using UnityEngine.UIElements; @@ -35,12 +36,14 @@ public override VisualElement CreatePropertyGUI(SerializedProperty property) { ScriptAttributeUtility.GetFieldInfoFromProperty(property, out var fieldType); - var objectField = new UnityEditor.UIElements.ObjectField(property.displayName); + var objectField = new ObjectField(preferredLabel); var genericType = fieldType.GetGenericArguments()[0]; objectField.objectType = genericType; objectField.value = property.objectReferenceValue; objectField.bindingPath = property.propertyPath; + PropertyField.ConfigureFieldStyles(objectField); + return objectField; } } diff --git a/Editor/Mono/GUI/MaskFieldGUI.cs b/Editor/Mono/GUI/MaskFieldGUI.cs index 0c4ca9c1e8..7570a3c20b 100644 --- a/Editor/Mono/GUI/MaskFieldGUI.cs +++ b/Editor/Mono/GUI/MaskFieldGUI.cs @@ -33,6 +33,12 @@ private class MaskCallbackInfo // Which view should we send it to. private readonly GUIView m_SourceView; + // Current drop-down reference + public MaskFieldDropDown m_DropDown; + + // validation flag for masks that are changed externally + private bool m_Validate = false; + public MaskCallbackInfo(int controlID) { m_ControlID = controlID; @@ -77,6 +83,31 @@ internal void SetMaskValueDelegate(object userData, string[] options, int select if (m_SourceView) m_SourceView.SendEvent(EditorGUIUtility.CommandEvent(kMaskMenuChangedMessage)); } + + public void UpdateFlagChanges(int mask, int[] optionMaskValues) + { + var evt = Event.current; + + if (evt.type == EventType.ExecuteCommand) + { + m_Validate = true; + } + // This code is responsible for verifying whether the incoming mask value differs from the one that is currently selected in the dropdown menu. + // When these values do not match, it serves as confirmation that the incoming value has been modified. + // Subsequently, we proceed to update the dropdown menu to reflect these changes. + else if (evt.type == EventType.Repaint && m_Validate) + { + if (m_DropDown == null) + { + return; + } + + if (mask != m_NewMask) + m_DropDown.UpdateMaskValues(mask, optionMaskValues); + + m_Validate = false; + } + } } /// Make a field for a generic mask. @@ -117,6 +148,10 @@ internal static int DoMaskField(Rect position, int controlID, int mask, string[] GetMenuOptions(mask, flagNames, flagValues, out var buttonText, out var buttonTextMixed, out var optionNames, out var optionMaskValues, out _, enumType); + // This checks and update flags changes that are modified out of dropdown menu + if (MaskCallbackInfo.m_Instance != null) + MaskCallbackInfo.m_Instance.UpdateFlagChanges(mask, optionMaskValues); + Event evt = Event.current; if (evt.type == EventType.Repaint) { @@ -126,7 +161,8 @@ internal static int DoMaskField(Rect position, int controlID, int mask, string[] else if ((evt.type == EventType.MouseDown && position.Contains(evt.mousePosition)) || evt.MainActionKeyForControl(controlID)) { MaskCallbackInfo.m_Instance = new MaskCallbackInfo(controlID); - PopupWindowWithoutFocus.Show(position, new MaskFieldDropDown(optionNames, flagValues, optionMaskValues, mask, MaskCallbackInfo.m_Instance.SetMaskValueDelegate)); + MaskCallbackInfo.m_Instance.m_DropDown = new MaskFieldDropDown(optionNames, flagValues, optionMaskValues, mask, MaskCallbackInfo.m_Instance.SetMaskValueDelegate); + PopupWindowWithoutFocus.Show(position, MaskCallbackInfo.m_Instance.m_DropDown); } return mask; @@ -234,6 +270,38 @@ internal static GUIContent DoMixedLabel(string label, string mixedLabel, Rect re return content; } + internal static void CalculateMaskValues(int mask, int[] flagValues, ref int[] optionMaskValues) + { + uint selectedValue = (uint)mask; + + var flagStartIndex = 0; + if (flagValues[0] == 0) + flagStartIndex++; + if (flagValues.Length > 1 && flagValues[1] == -1) + flagStartIndex++; + + if (mask == ~0) + { + uint allLayersMask = 0; + for (var flagIndex = flagStartIndex; flagIndex < flagValues.Length; flagIndex++) + { + allLayersMask |= (uint)flagValues[flagIndex]; + } + + selectedValue = allLayersMask; + } + + var flagEndIndex = flagStartIndex + optionMaskValues.Length - 2; + + for (var flagIndex = flagStartIndex; flagIndex < flagEndIndex; flagIndex++) + { + uint flagValue = (uint)flagValues[flagIndex]; + + bool flagSet = ((selectedValue & flagValue) == flagValue); + + optionMaskValues[flagIndex-flagStartIndex+2] = (int)(flagSet ? selectedValue & ~flagValue : selectedValue | flagValue); + } + } internal static void GetMenuOptions(int mask, string[] flagNames, int[] flagValues, out string buttonText, out string buttonMixedValuesText, out string[] optionNames, out int[] optionMaskValues, out int[] selectedOptions, Type enumType = null) @@ -342,15 +410,8 @@ internal static void GetMenuOptions(int mask, string[] flagNames, int[] flagValu optionMaskValues[1] = everythingValue; if (EditorGUI.showMixedValue) intermediateMask = 0; - for (var flagIndex = flagStartIndex; flagIndex < flagEndIndex; flagIndex++) - { - var optionIndex = flagIndex - flagStartIndex + 2; - var flagValue = flagValues[flagIndex]; - var flagSet = ((intermediateMask & flagValue) == flagValue); - var newMask = (flagSet ? intermediateMask & ~flagValue : intermediateMask | flagValue); - optionMaskValues[optionIndex] = newMask; - } + CalculateMaskValues(intermediateMask, flagValues, ref optionMaskValues); } } } diff --git a/Editor/Mono/GUI/PackageImport.cs b/Editor/Mono/GUI/PackageImport.cs index b37824d595..22c79558a4 100644 --- a/Editor/Mono/GUI/PackageImport.cs +++ b/Editor/Mono/GUI/PackageImport.cs @@ -244,12 +244,14 @@ void BottomArea() { PackageImportWizard.instance.CancelImport(); } - if (PackageImportWizard.instance.IsProjectSettingStep && GUILayout.Button(EditorGUIUtility.TrTextContent("Back"))) + + var isSecondStep = PackageImportWizard.instance.IsMultiStepWizard && + PackageImportWizard.instance.IsProjectSettingStep; + if (isSecondStep && GUILayout.Button(EditorGUIUtility.TrTextContent("Back"))) { PackageImportWizard.instance.DoPreviousStep(m_ImportPackageItems); } - var buttonText = PackageImportWizard.instance.IsMultiStepWizard - && !PackageImportWizard.instance.IsProjectSettingStep ? "Next" : "Import"; + var buttonText = isSecondStep || !PackageImportWizard.instance.IsMultiStepWizard ? "Import" : "Next"; if (GUILayout.Button(EditorGUIUtility.TrTextContent(buttonText))) { if (m_ImportPackageItems != null) @@ -440,8 +442,14 @@ public void StartImport(string packagePath, ImportPackageItem[] items, string pa m_AssetContentItems.Add(item); } - m_IsMultiStepWizard = m_ProjectSettingItems.Any(); - ShowImportWindow(m_AssetContentItems.ToArray()); + m_IsMultiStepWizard = m_AssetContentItems.Any() && m_ProjectSettingItems.Any(); + if (m_AssetContentItems.Any()) + ShowImportWindow(m_AssetContentItems.ToArray()); + else if (m_ProjectSettingItems.Any()) + { + m_IsProjectSettingStep = true; + ShowImportWindow(m_ProjectSettingItems.ToArray()); + } } public void DoNextStep(ImportPackageItem[] importPackageItems) @@ -463,12 +471,12 @@ public void DoNextStep(ImportPackageItem[] importPackageItems) public void DoPreviousStep(ImportPackageItem[] importPackageItems) { - if (IsProjectSettingStep) - { - m_ProjectSettingItems = new List(importPackageItems); - m_IsProjectSettingStep = false; - ShowImportWindow(m_AssetContentItems.ToArray()); - } + if (!IsProjectSettingStep || !IsMultiStepWizard) + return; + + m_ProjectSettingItems = new List(importPackageItems); + m_IsProjectSettingStep = false; + ShowImportWindow(m_AssetContentItems.ToArray()); } public void CancelImport() @@ -495,8 +503,7 @@ private void ShowImportWindow(ImportPackageItem[] items) private void FinishImport() { - var completeItemList = IsMultiStepWizard ? m_AssetContentItems.Concat(m_ProjectSettingItems) : m_AssetContentItems; - PackageUtility.ImportPackageAssetsWithOrigin(m_AssetOrigin, completeItemList.ToArray()); + PackageUtility.ImportPackageAssetsWithOrigin(m_AssetOrigin, m_AssetContentItems.Concat(m_ProjectSettingItems).ToArray()); CloseImportWindow(); } diff --git a/Editor/Mono/GUI/PingData.cs b/Editor/Mono/GUI/PingData.cs index 0a584af0b2..22a2e297f9 100644 --- a/Editor/Mono/GUI/PingData.cs +++ b/Editor/Mono/GUI/PingData.cs @@ -68,8 +68,8 @@ public void HandlePing() if (m_ContentDraw != null && Event.current.type == EventType.Repaint) { Rect backRect = m_ContentRect; - backRect.x -= m_PingStyle.padding.left; - backRect.y -= m_PingStyle.padding.top; + backRect.x -= m_PingStyle.padding.left / 2f; + backRect.y -= m_PingStyle.padding.top / 2f; m_PingStyle.Draw(backRect, GUIContent.none, false, false, false, false); m_ContentDraw(m_ContentRect); } diff --git a/Editor/Mono/GUI/ReorderableList.cs b/Editor/Mono/GUI/ReorderableList.cs index f5fe9ac05a..ae01c675cd 100644 --- a/Editor/Mono/GUI/ReorderableList.cs +++ b/Editor/Mono/GUI/ReorderableList.cs @@ -538,6 +538,8 @@ public int index private float listElementTopPadding => headerHeight > 5 ? 4 : 1; // headerHeight is usually set to 3px when there is no header content. Therefore, we add a 1px top margin to match the 4px bottom margin private const float kListElementBottomPadding = 4; + bool useCulling => GUI.matrix.rotation == Quaternion.identity && GUI.matrix.lossyScale == Vector3.one; + // draggable accessor public bool draggable { @@ -874,8 +876,11 @@ private void DoListElements(Rect listRect, Rect visibleRect) var next = Mathf.Min(i + 1, m_Count - 1); var previous = Mathf.Max(i - 1, 0); - if (visibleRect.y > GetElementYOffset(next) + GetElementHeight(next)) continue; - if (visibleRect.y + visibleRect.height < GetElementYOffset(previous)) break; + if (useCulling) + { + if (visibleRect.y > GetElementYOffset(next) + GetElementHeight(next)) continue; + if (visibleRect.y + visibleRect.height < GetElementYOffset(previous)) break; + } var nonDragTargetIndex = m_NonDragTargetIndices[i]; if (nonDragTargetIndex != -1) @@ -953,8 +958,11 @@ private void DoListElements(Rect listRect, Rect visibleRect) // if we aren't dragging, we just draw all of the elements in order for (int i = 0; i < m_Count; i++) { - if (visibleRect.y > GetElementYOffset(i) + GetElementHeight(i)) continue; - if (visibleRect.y + visibleRect.height < GetElementYOffset(i > 0 ? i - 1 : i)) break; + if (useCulling) + { + if (visibleRect.y > GetElementYOffset(i) + GetElementHeight(i)) continue; + if (visibleRect.y + visibleRect.height < GetElementYOffset(i > 0 ? i - 1 : i)) break; + } bool activeElement = m_Selection.Any(id => id == i); bool focusedElement = (activeElement && HasKeyboardControl()); diff --git a/Editor/Mono/GUI/SliderWithTexture.cs b/Editor/Mono/GUI/SliderWithTexture.cs index 129ccdf2e5..e5e754040e 100644 --- a/Editor/Mono/GUI/SliderWithTexture.cs +++ b/Editor/Mono/GUI/SliderWithTexture.cs @@ -106,7 +106,7 @@ Texture2D sliderBackground ? EditorGUIUtility.DragZoneRect(position) : default(Rect); // Ensure dragzone is empty when we have no label return DoSlider( - controlRect, dragZone, id, sliderValue, sliderMin, sliderMax, formatString, textFieldMin, textFieldMax, power, + controlRect, dragZone, id, sliderValue, sliderMin, sliderMax, formatString, textFieldMin, textFieldMax, power, 1f, EditorStyles.numberField, "ColorPickerSliderBackground", "ColorPickerHorizThumb", sliderBackground, null ); } diff --git a/Editor/Mono/GUI/Toolbar.cs b/Editor/Mono/GUI/Toolbar.cs index 1df88833c6..f960fddecd 100644 --- a/Editor/Mono/GUI/Toolbar.cs +++ b/Editor/Mono/GUI/Toolbar.cs @@ -100,6 +100,7 @@ void CreateContents() protected override void OnDisable() { + m_Root?.RemoveFromHierarchy(); base.OnDisable(); EditorApplication.modifierKeysChanged -= Repaint; } @@ -113,9 +114,6 @@ protected override void OldOnGUI() { if (Event.current.type == EventType.Repaint) Styles.appToolbar.Draw(new Rect(0, 0, position.width, position.height), false, false, false, false); - - BeginOffsetArea(GetToolbarPosition(), GUIContent.none, GUIStyle.none); - EndOffsetArea(); } static VisualElement CreateRoot() diff --git a/Editor/Mono/GUI/Toolbars/Core/EditorToolbar.cs b/Editor/Mono/GUI/Toolbars/Core/EditorToolbar.cs index 88b56addcc..adb1ff5976 100644 --- a/Editor/Mono/GUI/Toolbars/Core/EditorToolbar.cs +++ b/Editor/Mono/GUI/Toolbars/Core/EditorToolbar.cs @@ -16,6 +16,7 @@ sealed class EditorToolbar public const string elementClassName = "unity-editor-toolbar-element"; public const string elementIconClassName = elementClassName + "__icon"; public const string elementLabelClassName = elementClassName + "__label"; + public const string elementTextIconClassName = elementClassName + "__text-icon"; string[] m_ToolbarElements; readonly EditorWindow m_Context; diff --git a/Editor/Mono/GUI/Tools/BuiltinTools.cs b/Editor/Mono/GUI/Tools/BuiltinTools.cs index 8b29d4ff5f..f4ee42bc3b 100644 --- a/Editor/Mono/GUI/Tools/BuiltinTools.cs +++ b/Editor/Mono/GUI/Tools/BuiltinTools.cs @@ -44,7 +44,7 @@ public override void OnToolGUI(EditorWindow window) if (!view || !Selection.activeTransform || Tools.s_Hidden) return; - if (!StageUtility.IsGameObjectRenderedByCameraAndPartOfEditableScene(Selection.activeTransform.gameObject, Camera.current)) + if (StageUtility.IsGizmoCulledBySceneCullingMasksOrFocusedScene(Selection.activeTransform.gameObject, Camera.current)) return; GUIContent disabledLabel; @@ -591,7 +591,7 @@ protected override void ToolGUI(SceneView view, Vector3 handlePosition, bool isS TransformManipulator.BeginManipulationHandling(false); // Move handle EditorGUI.BeginChangeCheck(); - Vector3 newPos = MoveHandlesGUI(rect, handlePosition, rectRotation); + Vector3 newPos = MoveHandlesGUI(rect, rectRotation * rect.center + handlePosition, rectRotation); if (EditorGUI.EndChangeCheck() && !isStatic) { if (GridSnapping.active) diff --git a/Editor/Mono/GUI/Tools/EditorToolCache.cs b/Editor/Mono/GUI/Tools/EditorToolCache.cs index c185bcaf86..a455381dd8 100644 --- a/Editor/Mono/GUI/Tools/EditorToolCache.cs +++ b/Editor/Mono/GUI/Tools/EditorToolCache.cs @@ -303,15 +303,22 @@ public void InstantiateEditors(EditorToolContext ctx, List edit // represented. Addresses case where a single locked inspector is open. var shared = ActiveEditorTracker.sharedTracker; var activeTracker = shared.isLocked ? sharedTracker : shared; - var inspectors = InspectorWindow.GetInspectors(); + var propertyEditors = PropertyEditor.GetPropertyEditors(); - // Collect editor tools for the shared tracker first, then any locked inspectors + // Collect editor tools for the shared tracker first, then any locked inspectors or open properties editors CollectEditorsForTracker(ctx, activeTracker, editors); - foreach (var inspector in inspectors) + foreach (var propertyEditor in propertyEditors) { - if (inspector.isLocked) - CollectEditorsForTracker(ctx, inspector.tracker, editors); + if (propertyEditor is InspectorWindow) + { + if ((propertyEditor as InspectorWindow).isLocked) + CollectEditorsForTracker(ctx, propertyEditor.tracker, editors); + } + else + { + CollectEditorsForTracker(ctx, propertyEditor.tracker, editors); + } } foreach (var editor in editors) diff --git a/Editor/Mono/GUI/Tools/EditorToolGUI.cs b/Editor/Mono/GUI/Tools/EditorToolGUI.cs index 9907f9315e..4a64680cdf 100644 --- a/Editor/Mono/GUI/Tools/EditorToolGUI.cs +++ b/Editor/Mono/GUI/Tools/EditorToolGUI.cs @@ -24,6 +24,40 @@ public sealed partial class EditorGUILayout static class Styles { public static GUIStyle command = "AppCommand"; + public static GUIStyle commandLeft; + public static GUIStyle commandMid; + public static GUIStyle commandRight; + + static Styles() + { + GUI.FindStyles(ref command, out commandLeft, out commandMid, out commandRight, "left", "mid", "right"); + + var normalColor = command.normal.textColor; + + command.imagePosition = ImagePosition.ImageAbove; + command.active.textColor = normalColor; + command.onActive.textColor = normalColor; + command.onNormal.textColor = normalColor; + command.fontStyle = FontStyle.Bold; + + commandLeft.imagePosition = ImagePosition.ImageAbove; + commandLeft.active.textColor = normalColor; + commandLeft.onActive.textColor = normalColor; + commandLeft.onNormal.textColor = normalColor; + commandLeft.fontStyle = FontStyle.Bold; + + commandMid.imagePosition = ImagePosition.ImageAbove; + commandMid.active.textColor = normalColor; + commandMid.onActive.textColor = normalColor; + commandMid.onNormal.textColor = normalColor; + commandMid.fontStyle = FontStyle.Bold; + + commandRight.imagePosition = ImagePosition.ImageAbove; + commandRight.active.textColor = normalColor; + commandRight.onActive.textColor = normalColor; + commandRight.onNormal.textColor = normalColor; + commandRight.fontStyle = FontStyle.Bold; + } } public static void EditorToolbarForTarget(UObject target) @@ -138,7 +172,7 @@ internal static bool EditorToolbar(GUIContent content, T selected, IList t EditorGUI.BeginChangeCheck(); - index = GUILayout.Toolbar(index, buttons, enabled, Styles.command); + index = GUILayout.Toolbar(index, buttons, enabled, Styles.command, Styles.commandLeft, Styles.commandMid, Styles.commandRight); if (EditorGUI.EndChangeCheck()) { diff --git a/Editor/Mono/GUI/Tools/EditorToolManager.cs b/Editor/Mono/GUI/Tools/EditorToolManager.cs index 96c732591e..e2f8125831 100644 --- a/Editor/Mono/GUI/Tools/EditorToolManager.cs +++ b/Editor/Mono/GUI/Tools/EditorToolManager.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; +using UnityEditor.SceneManagement; using UnityEngine; using UnityObject = UnityEngine.Object; @@ -497,14 +498,26 @@ internal static bool TryPopToolState() return false; } + static bool IsGizmoCulledBySceneCullingMasksOrFocusedScene(UnityObject uobject) + { + var cmp = uobject as UnityEngine.Component; + if (cmp == null) + return false; + + return StageUtility.IsGizmoCulledBySceneCullingMasksOrFocusedScene(cmp.gameObject, Camera.current); + } + internal static void OnToolGUI(EditorWindow window) { - activeToolContext.OnToolGUI(window); + if (!IsGizmoCulledBySceneCullingMasksOrFocusedScene(activeToolContext.target)) + activeToolContext.OnToolGUI(window); if (Tools.s_Hidden || instance.m_ActiveTool == null) return; var current = instance.m_ActiveTool; + if (IsGizmoCulledBySceneCullingMasksOrFocusedScene(current.target)) + return; using (new EditorGUI.DisabledScope(!current.IsAvailable())) { @@ -699,6 +712,9 @@ internal static void InvokeOnSceneGUICustomEditorTools() { foreach (var context in instance.m_ComponentContexts) { + if (IsGizmoCulledBySceneCullingMasksOrFocusedScene(context.target)) + continue; + // ReSharper disable once SuspiciousTypeConversion.Global if (context.editor is IDrawSelectedHandles handle) handle.OnDrawHandles(); @@ -706,6 +722,9 @@ internal static void InvokeOnSceneGUICustomEditorTools() foreach (var tool in instance.m_ComponentTools) { + if (IsGizmoCulledBySceneCullingMasksOrFocusedScene(tool.target)) + continue; + // ReSharper disable once SuspiciousTypeConversion.Global if (tool.editor is IDrawSelectedHandles handle) handle.OnDrawHandles(); diff --git a/Editor/Mono/GUI/Tools/EditorToolUtility.cs b/Editor/Mono/GUI/Tools/EditorToolUtility.cs index b9b1a131e6..a925faac8c 100644 --- a/Editor/Mono/GUI/Tools/EditorToolUtility.cs +++ b/Editor/Mono/GUI/Tools/EditorToolUtility.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; +using UnityEditor.Overlays; using UnityEngine; using UObject = UnityEngine.Object; @@ -212,8 +213,10 @@ internal static bool IsGlobalTool(EditorTool tool) if(GetEnumWithEditorTool(tool) == Tool.Custom) { var type = tool.GetType(); - return !IsComponentTool(type) - && EditorToolManager.additionalContextToolTypesCache.All(t => t != type); + return !IsComponentTool(type) // Component tool? + && !IsManipulationTool(GetEnumWithEditorTool(tool, EditorToolManager.GetSingleton())) // Built-in tool? + && !IsBuiltinOverride(tool) // Built-in tool override? + && EditorToolManager.additionalContextToolTypesCache.Any(t => t == type); // Additional/Extra tool? } return false; @@ -249,13 +252,8 @@ internal static GUIContent GetIcon(Type editorToolType, bool forceReload = false if(( res.image = EditorGUIUtility.FindTexture(editorToolType) ) != null) goto ReturnToolbarIcon; - // If it's a custom editor tool, try to get an icon for the tool's target type - var attrib = GetEditorToolAttribute(editorToolType); - if(attrib?.targetType != null && ( res.image = AssetPreview.GetMiniTypeThumbnailFromType(attrib.targetType) ) != null) - goto ReturnToolbarIcon; - - // And finally fall back to the default Custom Tool icon - res.image = EditorGUIUtility.IconContent("CustomTool").image; + // And finally fall back to the significant letters of the tool's typename + res.text = OverlayUtilities.GetSignificantLettersForIcon(editorToolType.Name); ReturnToolbarIcon: if (string.IsNullOrEmpty(res.tooltip)) diff --git a/Editor/Mono/GUI/Tools/GameObjectToolContext.cs b/Editor/Mono/GUI/Tools/GameObjectToolContext.cs index e30582f40a..d9b9822896 100644 --- a/Editor/Mono/GUI/Tools/GameObjectToolContext.cs +++ b/Editor/Mono/GUI/Tools/GameObjectToolContext.cs @@ -9,7 +9,7 @@ namespace UnityEditor.EditorTools [EditorToolContext, Icon(k_IconPath)] public sealed class GameObjectToolContext : EditorToolContext { - const string k_IconPath = "Toolbars/ObjectMode"; + const string k_IconPath = "GameObject Icon"; GameObjectToolContext() {} } } diff --git a/Editor/Mono/GUI/TreeView/AssetOrGameObjectTreeViewDragging.cs b/Editor/Mono/GUI/TreeView/AssetOrGameObjectTreeViewDragging.cs index 8c7c1bdafe..040e596a46 100644 --- a/Editor/Mono/GUI/TreeView/AssetOrGameObjectTreeViewDragging.cs +++ b/Editor/Mono/GUI/TreeView/AssetOrGameObjectTreeViewDragging.cs @@ -30,7 +30,7 @@ public override bool CanStartDrag(TreeViewItem targetItem, List draggedItem foreach (var draggedItemID in draggedItemIDs) { var path = AssetDatabase.GetAssetPath(draggedItemID); - if (AssetDatabase.IsValidFolder(path) && !AssetDatabase.GetAssetFolderInfo(path, out _, out _)) + if (AssetDatabase.IsValidFolder(path) && !AssetDatabase.TryGetAssetFolderInfo(path, out _, out _)) return false; } diff --git a/Editor/Mono/GUI/TreeView/GameObjectTreeViewGUI.cs b/Editor/Mono/GUI/TreeView/GameObjectTreeViewGUI.cs index 194e82dac3..c84038c285 100644 --- a/Editor/Mono/GUI/TreeView/GameObjectTreeViewGUI.cs +++ b/Editor/Mono/GUI/TreeView/GameObjectTreeViewGUI.cs @@ -413,7 +413,7 @@ override protected void DoItemGUI(Rect rect, int row, TreeViewItem item, bool se if (goItem == null) return; - EnsureLazyInitialization(goItem); + EnsureLazyInitialization(goItem); // Needed to ensure item is ready for all ui controls if (goItem.isSceneHeader) { @@ -679,13 +679,15 @@ void SetPrefabModeButtonVisibility(GameObjectTreeViewItem item) protected override void OnContentGUI(Rect rect, int row, TreeViewItem item, string label, bool selected, bool focused, bool useBoldFont, bool isPinging) { - if (Event.current.type != EventType.Repaint) - return; - GameObjectTreeViewItem goItem = item as GameObjectTreeViewItem; if (goItem == null) return; + if (Event.current.type != EventType.Repaint) + return; + + EnsureLazyInitialization(goItem); // Needed to ensure icon is initialized if reload happens during DoItemGUI + rect.xMax = m_ContentRectRight; if (goItem.isSceneHeader) diff --git a/Editor/Mono/GUI/TypeSelectionList.cs b/Editor/Mono/GUI/TypeSelectionList.cs index d02cec9b74..6971ac0480 100644 --- a/Editor/Mono/GUI/TypeSelectionList.cs +++ b/Editor/Mono/GUI/TypeSelectionList.cs @@ -3,8 +3,6 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; -using UnityEditor; -using UnityEditorInternal; using System; using System.Collections.Generic; using Object = UnityEngine.Object; @@ -19,12 +17,20 @@ class TypeSelectionList public TypeSelectionList(Object[] objects) { // Create dictionary of lists of objects indexed by their type. - Dictionary> types = new Dictionary>(); + var types = new Dictionary>(); foreach (Object o in objects) { - string typeName = ObjectNames.GetTypeName(o); - if (o is GameObject && EditorUtility.IsPersistent(o)) - typeName = "Prefab"; + var typeName = ObjectNames.GetTypeName(o) + "{0}"; + + if (EditorUtility.IsPersistent(o)) + { + if (o is GameObject) + typeName = "Prefab{0}"; + + var importerType = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(o))?.GetType(); + if (importerType is not null && importerType != typeof(AssetImporter)) + typeName = $"{typeName} ({importerType.Name})"; + } if (!types.ContainsKey(typeName)) types[typeName] = new List(); @@ -33,7 +39,7 @@ public TypeSelectionList(Object[] objects) // Create and store a TypeSelection per type. m_TypeSelections = new List(); - foreach (KeyValuePair> kvp in types) + foreach (var kvp in types) m_TypeSelections.Add(new TypeSelection(kvp.Key, kvp.Value.ToArray())); // Sort the TypeSelections @@ -50,7 +56,11 @@ public TypeSelection(string typeName, Object[] objects) { System.Diagnostics.Debug.Assert(objects != null && objects.Length >= 1); this.objects = objects; - label = new GUIContent(objects.Length + " " + ObjectNames.NicifyVariableName(typeName) + (objects.Length > 1 ? "s" : "")); + + label = new GUIContent( + $"{objects.Length} " + + $"{ObjectNames.NicifyVariableName(string.Format(typeName, objects.Length > 1 ? "s" : ""))}"); + if (objects[0] is GameObject) label.image = EditorUtility.IsPersistent(objects[0]) ? PrefabUtility.GameObjectStyles.prefabIcon : PrefabUtility.GameObjectStyles.gameObjectIcon; else diff --git a/Editor/Mono/GUI/WindowLayout.cs b/Editor/Mono/GUI/WindowLayout.cs index 9e1967aa73..3d4abc50bd 100644 --- a/Editor/Mono/GUI/WindowLayout.cs +++ b/Editor/Mono/GUI/WindowLayout.cs @@ -82,8 +82,10 @@ public float size internal static string layoutsPreferencesPath => FileUtil.CombinePaths(InternalEditorUtility.unityPreferencesFolder, "Layouts"); internal static string layoutsModePreferencesPath => FileUtil.CombinePaths(layoutsPreferencesPath, ModeService.currentId); internal static string layoutsDefaultModePreferencesPath => FileUtil.CombinePaths(layoutsPreferencesPath, "default"); + internal static string layoutsCurrentModePreferencesPath => FileUtil.CombinePaths(layoutsPreferencesPath, "current"); internal static string layoutsProjectPath => FileUtil.CombinePaths("UserSettings", "Layouts"); internal static string ProjectLayoutPath => GetProjectLayoutPerMode(ModeService.currentId); + internal static string currentLayoutName => GetLayoutFileName(ModeService.currentId, Application.unityVersionVer); [UsedImplicitly, RequiredByNativeCode] public static void LoadDefaultWindowPreferences() @@ -97,14 +99,13 @@ public static void LoadCurrentModeLayout(bool keepMainWindow) InitializeLayoutPreferencesFolder(); var dynamicLayout = ModeService.GetDynamicLayout(); if (dynamicLayout == null) - LoadProjectLayout(keepMainWindow); + LoadLastUsedLayoutForCurrentMode(keepMainWindow); else { - var projectLayoutExists = File.Exists(ProjectLayoutPath); if ((projectLayoutExists && Convert.ToBoolean(dynamicLayout["restore_saved_layout"])) || !LoadModeDynamicLayout(keepMainWindow, dynamicLayout)) - LoadProjectLayout(keepMainWindow); + LoadLastUsedLayoutForCurrentMode(keepMainWindow); } } @@ -210,7 +211,7 @@ internal static void InitContainerWindow(ref ContainerWindow window, string wind { if (window == null) { - + window = ScriptableObject.CreateInstance(); var windowMinSize = new Vector2(120, 80); @@ -283,7 +284,7 @@ internal static ContainerWindow ShowWindowWithDynamicLayout(string windowId, str var window = Resources.FindObjectsOfTypeAll().FirstOrDefault(w => w.windowID == windowId); InitContainerWindow(ref window, windowId, layoutData); - + window.m_IsMppmCloneWindow = true; GenerateLayout(window, ShowMode.Utility, availableEditorWindowTypes, centerViewInfo, topViewInfo, bottomViewInfo, layoutData); return window; } @@ -413,23 +414,73 @@ private static bool ParseViewData(Type[] availableEditorWindowTypes, object view return true; } - private static void LoadProjectLayout(bool keepMainWindow) + // Used by tests + internal static string GetLayoutFileName(string mode, int version) => $"{mode}-{version}.dwlt"; + + static IEnumerable GetCurrentModeLayouts() { - var projectLayoutExists = File.Exists(ProjectLayoutPath); - if (!projectLayoutExists) + var layouts = ModeService.GetModeDataSection(ModeService.currentIndex, ModeDescriptor.LayoutsKey); + + if (layouts is IList modeLayoutPaths) { - var currentLayoutPath = GetCurrentLayoutPath(); - if (EnsureDirectoryCreated(ProjectLayoutPath)) + foreach (var layoutPath in modeLayoutPaths.Cast()) { - Console.WriteLine($"[LAYOUT] LoadProjectLayout: Copying Project Current Layout: {ProjectLayoutPath} from {currentLayoutPath}"); - FileUtil.CopyFileOrDirectory(currentLayoutPath, ProjectLayoutPath); + if (!File.Exists(layoutPath)) + continue; + yield return layoutPath; } } + } - Debug.Assert(File.Exists(ProjectLayoutPath)); + // Iterate through potential layouts in descending order of precedence. + // 1. Last loaded layout in project for matching Unity version + // 2. Last loaded layout in project for any Unity version, in descending alphabetical order + // 3. Last loaded layout in global preferences for matching Unity version + // 4. Last loaded layout in global preferences for any Unity version, in descending alphabetical order + // 5. Any available layouts specified by the EditorMode, if EditorMode supplies layouts + // 6. The factory default layout + private static void LoadLastUsedLayoutForCurrentMode(bool keepMainWindow) + { + // steps 1-4 + foreach (var layout in GetLastLayout()) + if (LoadWindowLayout(layout, layout != ProjectLayoutPath, false, keepMainWindow, false)) + return; + + // step 5 + foreach (var layout in GetCurrentModeLayouts()) + if (LoadWindowLayout(layout, layout != ProjectLayoutPath, false, keepMainWindow, false)) + return; + + // step 6 + var defaultLayout = Path.Combine(layoutsDefaultModePreferencesPath, kDefaultLayoutName); + + // If all else fails, load the default layout that ships with the editor. If that fails, prompt the user to + // restore the default layouts. + if (!LoadWindowLayout(defaultLayout, true, false, keepMainWindow, false)) + { + int option = 0; - // Load the current project layout - LoadWindowLayout(ProjectLayoutPath, !projectLayoutExists, false, keepMainWindow); + if (!Application.isTestRun && Application.isHumanControllingUs) + { + option = EditorUtility.DisplayDialogComplex("Missing Default Layout", "No valid user created or " + + "default window layout found. Please revert factory settings to restore the default layouts.", + "Quit", "Revert Factory Settings", ""); + } + else + { + ResetUserLayouts(); + } + + switch (option) + { + case 0: + EditorApplication.Exit(0); + break; + case 1: + ResetFactorySettings(); + break; + } + } } [UsedImplicitly, RequiredByNativeCode] @@ -444,8 +495,49 @@ public static void SaveDefaultWindowPreferences() internal static void SaveCurrentLayoutPerMode(string modeId) { - // Save Project Current Layout + // Save the layout in two places. Once in the Project/UserSettings directory, then again the global + // preferences. The latter is used when opening a new project (or any case where UserSettings/Layouts/ does + // not exist). SaveWindowLayout(FileUtil.CombinePaths(Directory.GetCurrentDirectory(), GetProjectLayoutPerMode(modeId))); + SaveWindowLayout(Path.Combine(layoutsCurrentModePreferencesPath, GetLayoutFileName(modeId, Application.unityVersionVer))); + } + + // Iterate through potential layout files, prioritizing exact match followed by descending unity version. + // IMPORTANT: This function is "dumb" in that it does not do any kind of sophisticated version comparison. If the + // naming scheme for current layouts is changed, or this function is called on to sort user saved layouts, you will + // need to add more sophisticated filtering. + public static IEnumerable GetLastLayout(string directory, string mode, int version) + { + var currentModeAndVersionLayout = GetLayoutFileName(mode, version); + string layoutSearchPattern = $"{mode}-*.*wlt"; + + // first try the exact match + var preferred = Path.Combine(directory, currentModeAndVersionLayout); + + if(File.Exists(preferred)) + yield return preferred; + + // if that fails, fall back to layouts for this mode from other unity versions in descending order + if (Directory.Exists(directory)) + { + var paths = Directory.GetFiles(directory, layoutSearchPattern) + .Where(p => string.Compare(p, preferred, StringComparison.OrdinalIgnoreCase) != 0) + .OrderByDescending(p => p, StringComparer.OrdinalIgnoreCase); + + foreach (var path in paths) + yield return path; + } + } + + // used by Tests/EditModeAndPlayModeTests/EditorModes + internal static IEnumerable GetLastLayout() + { + var mode = ModeService.currentId; + var version = Application.unityVersionVer; + foreach (var layout in GetLastLayout(layoutsProjectPath, mode, version)) + yield return layout; + foreach (var layout in GetLastLayout(layoutsCurrentModePreferencesPath, mode, version)) + yield return layout; } internal static string GetCurrentLayoutPath() @@ -466,7 +558,7 @@ internal static string GetDefaultLayoutPath() internal static string GetProjectLayoutPerMode(string modeId) { - return FileUtil.CombinePaths(layoutsProjectPath, $"{modeId}-{Application.unityVersionVer}.dwlt"); + return FileUtil.CombinePaths(layoutsProjectPath, GetLayoutFileName(modeId, Application.unityVersionVer)); } private static void InitializeLayoutPreferencesFolder() @@ -545,7 +637,7 @@ internal static void ReloadWindowLayoutMenu() foreach (var layoutPath in layoutPaths) { var name = Path.GetFileNameWithoutExtension(layoutPath); - Menu.AddMenuItem("Window/Layouts/" + name, "", false, layoutMenuItemPriority++, () => LoadWindowLayout(layoutPath, false, true, true), null); + Menu.AddMenuItem("Window/Layouts/" + name, "", false, layoutMenuItemPriority++, () => LoadWindowLayout(layoutPath, false, true, true, true), null); } layoutMenuItemPriority += 500; @@ -563,7 +655,7 @@ internal static void ReloadWindowLayoutMenu() if (!File.Exists(layoutPath)) continue; var name = Path.GetFileNameWithoutExtension(layoutPath); - Menu.AddMenuItem("Window/Layouts/" + name, "", Toolbar.lastLoadedLayoutName == name, layoutMenuItemPriority++, () => LoadWindowLayout(layoutPath, false), null); + Menu.AddMenuItem("Window/Layouts/" + name, "", Toolbar.lastLoadedLayoutName == name, layoutMenuItemPriority++, () => TryLoadWindowLayout(layoutPath, false), null); } } @@ -586,7 +678,7 @@ private static void AddLegacyLayoutMenuItems(ref int layoutMenuItemPriority) const string legacyRootMenu = "Window/Layouts/Other Versions"; const string legacyCurrentLayoutPath = "Library/CurrentLayout-default.dwlt"; if (File.Exists(legacyCurrentLayoutPath)) - Menu.AddMenuItem($"{legacyRootMenu}/Default (2020)", "", false, layoutMenuItemPriority++, () => LoadWindowLayout(legacyCurrentLayoutPath, false, true, false), null); + Menu.AddMenuItem($"{legacyRootMenu}/Default (2020)", "", false, layoutMenuItemPriority++, () => LoadWindowLayout(legacyCurrentLayoutPath, false, true, false, true), null); if (!Directory.Exists(layoutsProjectPath)) return; @@ -603,7 +695,7 @@ private static void AddLegacyLayoutMenuItems(ref int layoutMenuItemPriority) name = ObjectNames.NicifyVariableName(names[0]); menuName = $"{legacyRootMenu}/{name} ({names[1]})"; } - Menu.AddMenuItem(menuName, "", false, layoutMenuItemPriority++, () => LoadWindowLayout(layoutPath, false, true, false), null); + Menu.AddMenuItem(menuName, "", false, layoutMenuItemPriority++, () => LoadWindowLayout(layoutPath, false, true, false, true), null); } } @@ -731,97 +823,6 @@ 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 @@ -1208,12 +1209,17 @@ static void DeleteWindowLayoutImpl(string name, string path) ShortcutIntegration.instance.RebuildShortcuts(); } - public static bool LoadWindowLayout(string path, bool newProjectLayoutWasCreated) + // Attempts to load a layout. If unsuccessful, restores the previous layout. + public static bool TryLoadWindowLayout(string path, bool newProjectLayoutWasCreated) { - return LoadWindowLayout(path, newProjectLayoutWasCreated, true, false); + SaveDefaultWindowPreferences(); + if (LoadWindowLayout(path, newProjectLayoutWasCreated, true, false, true)) + return true; + LoadCurrentModeLayout(FindMainWindow()); + return false; } - public static bool LoadWindowLayout(string path, bool newProjectLayoutWasCreated, bool setLastLoadedLayoutName, bool keepMainWindow) + public static bool LoadWindowLayout(string path, bool newProjectLayoutWasCreated, bool setLastLoadedLayoutName, bool keepMainWindow, bool logErrorsToConsole) { Console.WriteLine($"[LAYOUT] About to load {path}, keepMainWindow={keepMainWindow}"); @@ -1223,8 +1229,6 @@ public static bool LoadWindowLayout(string path, bool newProjectLayoutWasCreated bool mainWindowMaximized = ContainerWindow.mainWindow?.maximized ?? false; Rect mainWindowPosition = ContainerWindow.mainWindow?.position ?? new Rect(); - bool layoutLoadingIssue = false; - // Load new windows and show them try { @@ -1248,7 +1252,7 @@ public static bool LoadWindowLayout(string path, bool newProjectLayoutWasCreated UnityObject[] loadedWindows = InternalEditorUtility.LoadSerializedFileAndForget(path); if (loadedWindows == null || loadedWindows.Length == 0) - throw new LayoutException($"Window layout at {path} could not be loaded."); + throw new LayoutException("No windows found in layout."); List newWindows = new List(); @@ -1264,10 +1268,9 @@ public static bool LoadWindowLayout(string path, bool newProjectLayoutWasCreated { if (!editorWin || !editorWin.m_Parent || !editorWin.m_Parent.window) { - Console.WriteLine("[LAYOUT] Removed unparented EditorWindow while reading window layout: window #" + i + ", type=" + - o.GetType() + ", instanceID=" + o.GetInstanceID()); + Console.WriteLine($"[LAYOUT] Removed un-parented EditorWindow while reading window layout" + + $" window #{i}, type={o.GetType()} instanceID={o.GetInstanceID()}"); UnityObject.DestroyImmediate(editorWin, true); - layoutLoadingIssue = true; continue; } } @@ -1315,6 +1318,8 @@ public static bool LoadWindowLayout(string path, bool newProjectLayoutWasCreated if (mainWindowPosition.width != 0.0) { mainWindowToSetSize = cur; + // This is the same reference as the mainwindow, so need to freeze it too on for Linux during reload. + mainWindowToSetSize.SetFreeze(true); mainWindowToSetSize.position = mainWindowPosition; } @@ -1336,16 +1341,23 @@ public static bool LoadWindowLayout(string path, bool newProjectLayoutWasCreated } if (mainWindowToSetSize) + { + mainWindowToSetSize.SetFreeze(true); mainWindowToSetSize.position = mainWindowPosition; + } // Always show main window before other windows. So that other windows can // get their parent/owner. if (!mainWindow) throw new LayoutException("Error while reading window layout: no main window found"); + // Don't adjust height to prevent main window shrink during layout on Linux. + mainWindow.SetFreeze(true); mainWindow.Show(mainWindow.showMode, loadPosition: true, displayImmediately: true, setFocus: true); if (mainWindowToSetSize && mainWindow.maximized != mainWindowMaximized) mainWindow.ToggleMaximize(); + // Unfreeze to make sure resize work properly. + mainWindow.SetFreeze(false); // Make sure to restore the save to layout flag when loading a layout from a file. if (keepMainWindow) @@ -1366,40 +1378,23 @@ public static bool LoadWindowLayout(string path, bool newProjectLayoutWasCreated containerWindow.Show(containerWindow.showMode, loadPosition: false, displayImmediately: true, setFocus: true); } - // Unmaximize maximized PlayModeView window if maximize on play is enabled + // Un-maximize maximized PlayModeView window if maximize on play is enabled PlayModeView playModeView = GetMaximizedWindow() as PlayModeView; if (playModeView != null && playModeView.enterPlayModeBehavior == PlayModeView.EnterPlayModeBehavior.PlayMaximized) Unmaximize(playModeView); } catch (Exception ex) { - Debug.LogError("Failed to load window layout: " + ex); - - int option = 0; - if (!Application.isTestRun && Application.isHumanControllingUs) - { - option = EditorUtility.DisplayDialogComplex("Failed to load window layout", - $"This can happen if layout contains custom windows and there are compile errors in the project.\r\n\r\n{ex.Message}", - "Load Default Layout", "Quit", "Revert Factory Settings"); - } + // When loading a new project we don't want to log an error if one of the last saved layouts throws. + // There isn't anything useful a user can do about it, and we can gracefully recover. However when a + // layout is loaded from the menu or a mode change, we do want to let the user know about layout loading + // problems because they can act on it by either deleting the layout or importing whatever asset is + // missing. + var error = $"Failed to load window layout \"{path}\": {ex}"; + if(logErrorsToConsole) + Debug.LogError(error); else - { - ResetUserLayouts(); - } - - switch (option) - { - case 0: - LoadDefaultLayout(); - break; - case 1: - EditorApplication.Exit(0); - break; - case 2: - ResetFactorySettings(); - break; - } - + Console.WriteLine($"[LAYOUT] {error}"); return false; } finally @@ -1412,9 +1407,6 @@ public static bool LoadWindowLayout(string path, bool newProjectLayoutWasCreated Toolbar.lastLoadedLayoutName = null; } - if (layoutLoadingIssue) - Debug.Log("The editor layout could not be fully loaded, this can happen when the layout contains EditorWindows not available in this project"); - return true; } @@ -1431,7 +1423,7 @@ internal static void LoadDefaultLayout() } Debug.Assert(File.Exists(ProjectLayoutPath)); - LoadWindowLayout(ProjectLayoutPath, true); + LoadWindowLayout(ProjectLayoutPath, true, true, false, false); } public static void CloseWindows() @@ -1553,11 +1545,9 @@ internal static void SaveGUI() public static void LoadFromFile() { var layoutFilePath = EditorUtility.OpenFilePanelWithFilters("Load layout from disk...", "", new[] {"Layout", "wlt"}); - if (String.IsNullOrEmpty(layoutFilePath)) + if (string.IsNullOrEmpty(layoutFilePath)) return; - - if (LoadWindowLayout(layoutFilePath, false)) - Debug.LogFormat(LogType.Log, LogOption.NoStacktrace, null, "Loaded layout from " + layoutFilePath); + TryLoadWindowLayout(layoutFilePath, false); } public static void SaveToFile() @@ -1622,6 +1612,7 @@ internal class SaveWindowLayout : EditorWindow const int k_Width = 200; const int k_Height = 48; const int k_HelpBoxHeight = 40; + const int k_MaxLayoutNameLength = 128; static readonly string k_InvalidChars = EditorUtility.GetInvalidFilenameChars(); static readonly string s_InvalidCharsFormatString = L10n.Tr("Invalid characters: {0}"); @@ -1632,6 +1623,7 @@ internal static SaveWindowLayout ShowWindow() { SaveWindowLayout w = GetWindowDontShow(); w.minSize = w.maxSize = new Vector2(k_Width, k_Height); + w.m_Pos = new Rect(0, 0,k_Width, k_Height); w.ShowAuxWindow(); return w; } @@ -1659,10 +1651,14 @@ void OnGUI() } GUI.SetNextControlName("m_PreferencesName"); EditorGUI.BeginChangeCheck(); - m_LayoutName = EditorGUILayout.TextField(m_LayoutName); - m_LayoutName = m_LayoutName.TrimEnd(); + m_LayoutName = EditorGUILayout.TextField(m_LayoutName); if (EditorGUI.EndChangeCheck()) { + if (m_LayoutName.Length > k_MaxLayoutNameLength) + { + m_LayoutName = m_LayoutName.Substring(0, k_MaxLayoutNameLength); + } + m_LayoutName = m_LayoutName.TrimEnd(); UpdateCurrentInvalidChars(); } @@ -1788,7 +1784,7 @@ internal static WindowFocusState instance get { if (m_Instance == null) - m_Instance = FindObjectOfType(typeof(WindowFocusState)) as WindowFocusState; + m_Instance = FindFirstObjectByType(typeof(WindowFocusState)) as WindowFocusState; if (m_Instance == null) m_Instance = CreateInstance(); return m_Instance; diff --git a/Editor/Mono/GUIView.cs b/Editor/Mono/GUIView.cs index a1c000a816..e4f8dde6c7 100644 --- a/Editor/Mono/GUIView.cs +++ b/Editor/Mono/GUIView.cs @@ -222,15 +222,6 @@ protected override void OnDestroy() base.OnDestroy(); } - // Draw resize handles, etc. - internal virtual void DoWindowDecorationStart() - { - } - - internal virtual void DoWindowDecorationEnd() - { - } - [RequiredByNativeCode] internal string GetViewName() { @@ -272,8 +263,6 @@ public static void BeginOffsetArea(Rect screenRect, GUIContent content, GUIStyle public static void EndOffsetArea() { - if (Event.current.type == EventType.Used) - return; GUILayoutUtility.EndLayoutGroup(); GUI.EndGroup(); } @@ -302,5 +291,17 @@ internal void CaptureMetalScene() System.Diagnostics.Process.Start(System.IO.Path.GetDirectoryName(path)); } } + + static readonly Action k_QueryMarkDirty = MarkDirtyRepaint; + + static void MarkDirtyRepaint(TextElement v) + { + v.MarkDirtyRepaint(); + } + + internal void RepaintUITKText() + { + visualTree.Query().ForEach(k_QueryMarkDirty); + } } } //namespace diff --git a/Editor/Mono/GameView/GameView.cs b/Editor/Mono/GameView/GameView.cs index 6472f31aa3..1114656c02 100644 --- a/Editor/Mono/GameView/GameView.cs +++ b/Editor/Mono/GameView/GameView.cs @@ -38,7 +38,7 @@ Floating GameView in separate window namespace UnityEditor { [EditorWindowTitle(title = "Game", useTypeNameAsIconName = true)] - internal class GameView : PlayModeView, IHasCustomMenu, IGameViewSizeMenuUser, IGameViewOnPlayMenuUser + internal class GameView : PlayModeView, IHasCustomMenu, IGameViewSizeMenuUser { const int kScaleSliderMinWidth = 30; const int kScaleSliderMaxWidth = 150; @@ -68,7 +68,6 @@ 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) @@ -84,8 +83,6 @@ 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; @@ -110,8 +107,6 @@ internal static class Styles 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 frameDebuggerContent = EditorGUIUtility.TrIconContent("Debug_Frame_d", "Opens the 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 const string k_StatsShortcutID = "Game View/Toggle Stats"; public const string k_StatsTooltip = "View general rendering information"; @@ -127,35 +122,10 @@ static Styles() gameViewBackgroundStyle = "GameViewBackground"; renderdocContent = EditorGUIUtility.TrIconContent("FrameCapture", RenderDocUtil.openInRenderDocTooltip); } - - 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; - [FreeFunction] - extern private static bool NeedToPerformRendering(); - public GameView() { autoRepaintOnSceneChange = true; @@ -169,81 +139,6 @@ 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 @@ -277,12 +172,6 @@ public bool vSyncEnabled } } - public bool playFocused - { - get { return m_PlayFocused; } - set { m_PlayFocused = value; } - } - public int selectedSizeIndex { get @@ -312,9 +201,7 @@ internal void SetTargetDisplay(int id) Rect GetViewInWindow(Rect pos) { - if (showToolbar) - return new Rect(0, EditorGUI.kWindowToolbarHeight, pos.width, pos.height - EditorGUI.kWindowToolbarHeight); - return new Rect(0, 0, pos.width, pos.height); + return new Rect(0, EditorGUI.kWindowToolbarHeight, pos.width, pos.height - EditorGUI.kWindowToolbarHeight); } Rect GetViewPixelRect(Rect viewRectInWindow) @@ -410,12 +297,6 @@ 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 => isFullscreen ? m_showToolbarOnFullscreen : m_showToolbar; - set => m_showToolbar = value; - } - internal bool drawGizmos { get => m_Gizmos; @@ -438,15 +319,12 @@ public void OnEnable() prevSizeGroupType = (int)currentSizeGroupType; titleContent = GetLocalizedTitleContent(); UpdateZoomAreaAndParent(); - showToolbar = ModeService.HasCapability(ModeCapability.GameViewToolbar, true); ModeService.modeChanged += OnEditorModeChanged; EditorApplication.playModeStateChanged += OnPlayModeStateChanged; Undo.undoRedoEvent += UndoRedoPerformed; - if (!suppressRenderingForFullscreen) - targetSize = targetRenderSize; - + targetSize = targetRenderSize; PlayModeAnalytics.GameViewEnableEvent(); } @@ -472,9 +350,6 @@ internal static Vector2 GetSizeOfMainGameView() private void UpdateZoomAreaAndParent() { - if (suppressRenderingForFullscreen) - return; - // Configure ZoomableArea for new resolution so that old resolution doesn't restrict scale bool oldScaleWasDefault = Mathf.Approximately(m_ZoomArea.scale.y, m_defaultScale); ConfigureZoomArea(); @@ -501,8 +376,7 @@ protected void AllowCursorLockAndHide(bool enable) private void OnFocus() { SetFocus(true); - if (!suppressRenderingForFullscreen) - targetSize = targetRenderSize; + targetSize = targetRenderSize; } private void OnLostFocus() @@ -520,18 +394,21 @@ private void OnLostFocus() internal override void OnResized() { - if (!suppressRenderingForFullscreen) - targetSize = targetRenderSize; + targetSize = targetRenderSize; } internal override void OnBackgroundViewResized(Rect pos) { - // Should only update the game view size if it's in Aspect Ratio mode, otherwise - // we keep the static size - - if (suppressRenderingForFullscreen) + // If we are switching from GameView to Simulator, this call will overwrite the value already written + // by the SimulatorView since both tabs exist for a brief period of time. Don't do anything here + // if this view is the one being switched out. + if (m_SwitchingPlayModeViewType) + { return; + } + // Should only update the game view size if it's in Aspect Ratio mode, otherwise + // we keep the static size Rect viewInWindow = GetViewInWindow(pos); Rect viewPixelRect = GetViewPixelRect(viewInWindow); var newTargetSize = @@ -671,31 +548,6 @@ 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) @@ -705,28 +557,22 @@ private void DoToolbarGUI() GUILayout.BeginHorizontal(EditorStyles.toolbar); { - if (!isFullscreen) + var availableTypes = GetAvailableWindowTypes(); + if (availableTypes.Count > 1) { - EditorGUI.BeginDisabled(enterPlayModeBehavior == EnterPlayModeBehavior.PlayFullscreen); - var availableTypes = GetAvailableWindowTypes(); - if (availableTypes.Count > 1) + 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)); + if (viewIndex == -1) + viewIndex = 0; + + EditorGUILayout.Space(); + if (types[viewIndex] != typeof(GameView)) { - var typeNames = availableTypes.Values.ToList(); - var types = availableTypes.Keys.ToList(); - var viewIndex = types.IndexOf(typeof(GameView)); - if (viewIndex == -1) - viewIndex = 0; - - viewIndex = EditorGUILayout.Popup(viewIndex, typeNames.ToArray(), - EditorStyles.toolbarPopup, - GUILayout.Width(90)); - EditorGUILayout.Space(); - if (types[viewIndex] != typeof(GameView)) - { - SwapMainWindow(types[viewIndex]); - } + SwapMainWindow(types[viewIndex]); } - EditorGUI.EndDisabled(); } if (ModuleManager.ShouldShowMultiDisplayOption()) @@ -766,6 +612,10 @@ private void DoToolbarGUI() } GUILayout.FlexibleSpace(); + using (new EditorGUI.DisabledScope(EditorApplication.isPlaying && !EditorApplication.isPaused)) + { + enterPlayModeBehavior = (EnterPlayModeBehavior)EditorGUILayout.EnumPopup(enterPlayModeBehavior, EditorStyles.toolbarDropDown, GUILayout.Width(110)); + } if (ShouldShowMetalFrameCaptureGUI()) { @@ -781,7 +631,10 @@ private void DoToolbarGUI() using (new EditorGUI.DisabledScope(!RenderDoc.IsSupported())) { if (GUILayout.Button(Styles.renderdocContent, EditorStyles.toolbarButton)) + { RenderDoc.CaptureRenderDoc(); + GUIUtility.ExitGUI(); + } } } @@ -816,14 +669,6 @@ private void DoToolbarGUI() } GUILayout.FlexibleSpace(); - // Don't display the fullscreen selection dropdown on gameview toolbars that are already fullscreen. - if (!isFullscreen) - { - EditorGUI.BeginDisabled(EditorApplication.isPlaying && !EditorApplication.isPaused); - EditorGUILayout.GameViewOnPlayPopup(playModeBehaviorIdx, this, EditorStyles.toolbarDropDown); - EditorGUI.EndDisabled(); - } - EditorUtility.audioMasterMute = GUILayout.Toggle(EditorUtility.audioMasterMute, EditorUtility.audioMasterMute ? Styles.muteOnContent : Styles.muteOffContent, EditorStyles.toolbarButton); @@ -847,7 +692,6 @@ private void DoToolbarGUI() } } GUILayout.EndHorizontal(); - MirrorFullscreenToolbarSettings(); } [GameViewShortcut(Styles.k_StatsShortcutID)] @@ -1025,8 +869,6 @@ void RepaintIfNeeded() private void OnEditorModeChanged(ModeService.ModeChangedArgs args) { - showToolbar = ModeService.HasCapability(ModeCapability.GameViewToolbar, true); - Repaint(); } @@ -1060,19 +902,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(); } - if (showToolbar) - DoToolbarGUI(); + DoToolbarGUI(); if (type == EventType.MouseDown || type == EventType.MouseUp) EditorApplication.globalEventHandler?.Invoke(); @@ -1114,7 +949,7 @@ private void OnGUI() m_ZoomArea.BeginViewGUI(); // Window size might change on Layout event - if (type == EventType.Layout && !suppressRenderingForFullscreen) + if (type == EventType.Layout) targetSize = targetRenderSize; // Setup game view dimensions, so that player loop can use it for input @@ -1122,23 +957,19 @@ private void OnGUI() if (m_Parent) { var zoomedTarget = new Rect(targetInView.position + gameViewTarget.position, targetInView.size); - if (!isFullscreen) - { - SetParentGameViewDimensions(zoomedTarget, gameViewTarget, targetRenderSize); - } - else - { - SetParentGameViewDimensions(zoomedTarget, gameViewTarget, EditorFullscreenController.fullscreenDisplayRenderSize); - } + SetParentGameViewDimensions(zoomedTarget, gameViewTarget, targetRenderSize); } var editorMousePosition = Event.current.mousePosition; var gameMousePosition = (editorMousePosition + gameMouseOffset) * gameMouseScale; - if (type == EventType.Repaint && !suppressRenderingForFullscreen) + if (type == EventType.Repaint) { GUI.Box(m_ZoomArea.drawRect, GUIContent.none, Styles.gameViewBackgroundStyle); + // Tonemapping for HDR targets + EditorGUIUtility.PerformTonemappingForGameView(); + Vector2 oldOffset = GUIUtility.s_EditorScreenPointOffset; GUIUtility.s_EditorScreenPointOffset = Vector2.zero; SavedGUIState oldState = SavedGUIState.Create(); @@ -1161,8 +992,7 @@ private void OnGUI() viewPadding = targetInParent.position; viewMouseScale = gameMouseScale; - if (!EditorApplication.isPlaying || - (EditorApplication.isPlaying && NeedToPerformRendering() && !Unsupported.IsEditorPlayerLoopWaiting())) + if (renderViewCallNeededInOnGUI) m_RenderTexture = RenderView(gameMousePosition, clearTexture); if (m_TargetClamped) @@ -1179,7 +1009,7 @@ private void OnGUI() Rect drawRect = deviceFlippedTargetInView; drawRect.x = Mathf.Round(drawRect.x); drawRect.y = Mathf.Round(drawRect.y); - Graphics.DrawTexture(drawRect, m_RenderTexture, new Rect(0, 0, 1, 1), 0, 0, 0, 0, GUI.color, GUI.blitMaterial); + EditorGUIUtility.DrawTextureHdrSupport(drawRect, m_RenderTexture, new Rect(0, 0, 1, 1), 0, 0, 0, 0, GUI.color, GUI.blitMaterial, -1, true); GUI.EndGroup(); } } @@ -1188,13 +1018,6 @@ 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) @@ -1262,15 +1085,6 @@ private void OnGUI() 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); - } - internal void SetCustomResolution(Vector2 res, string baseName) { GameViewSize customSize = null; @@ -1303,34 +1117,5 @@ internal void SetCustomResolution(Vector2 res, string baseName) targetSize = targetRenderSize; SceneView.RepaintAll(); } - - 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; - GameViewOnPlayMenu.SetFocusedToggle(this, true); - } - 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 deleted file mode 100644 index 281ae52fa1..0000000000 --- a/Editor/Mono/GameView/GameViewOnPlayMenu.cs +++ /dev/null @@ -1,164 +0,0 @@ -// 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("Play in Window", "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", "Forcibly focus the game view when entering play mode."); - public static readonly GUIContent playFocusedToggleDisabledContent = EditorGUIUtility.TrTextContent("Focused", "Focus toggle is currently disabled because a view will play maximized."); - 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; - private IFlexibleMenuItemProvider m_ItemProvider; - - 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; - m_ItemProvider = itemProvider; - } - - 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 + EditorGUI.kSingleLineHeight; - 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()) - { - m_GameView.vSyncEnabled = GUI.Toggle(rect, m_GameView.vSyncEnabled, Styles.vSyncToggleContent); - } - else - { - m_GameView.vSyncEnabled = false; - GUI.Label(rect, Styles.vSyncUnsupportedContent, EditorStyles.miniLabel); - } - } - - private static void UncheckFocusToggleOnAllViews() - { - List playViewList; - WindowLayout.ShowAppropriateViewOnEnterExitPlaymodeList(true, out playViewList); - foreach (PlayModeView playView in playViewList) - { - if (playView is IGameViewOnPlayMenuUser) - { - ((IGameViewOnPlayMenuUser)playView).playFocused = false; - } - } - } - - public static void SetFocusedToggle(IGameViewOnPlayMenuUser view, bool newValue) - { - UncheckFocusToggleOnAllViews(); - view.playFocused = newValue; - } - - private bool IsAnyViewInMaximizeMode() - { - List playViewList; - WindowLayout.ShowAppropriateViewOnEnterExitPlaymodeList(true, out playViewList); - foreach (PlayModeView playView in playViewList) - { - if (playView.enterPlayModeBehavior == PlayModeView.EnterPlayModeBehavior.PlayMaximized) - { - SetFocusedToggle(playView as IGameViewOnPlayMenuUser, true); - return true; - } - } - return false; - } - - public override void OnGUI(Rect rect) - { - var frameRect = new Rect(rect.x, rect.y, rect.width, rect.height); - GUI.Label(frameRect, "", EditorStyles.viewBackground); - - var focusTextSize = EditorStyles.label.CalcSize(Styles.playFocusedToggleContent); - GUI.enabled = !IsAnyViewInMaximizeMode(); - var focusToggleRect = new Rect(Styles.kMargin, Styles.kTopMargin, focusTextSize.x, EditorGUI.kSingleLineHeight); - bool playFocusedToggle = GUI.Toggle(focusToggleRect, m_GameView.playFocused, GUI.enabled ? Styles.playFocusedToggleContent : Styles.playFocusedToggleDisabledContent); - GUI.enabled = true; - if (playFocusedToggle != m_GameView.playFocused) - { - SetFocusedToggle(m_GameView, playFocusedToggle); - } - var vsyncToggleRect = new Rect(focusTextSize.x + Styles.kMargin*2, Styles.kTopMargin, rect.width, EditorGUI.kSingleLineHeight); - DoVSyncToggle(vsyncToggleRect); - - var labelSize = EditorStyles.boldLabel.CalcSize(Styles.gamePlayModeBehaviorLabelContent); - var labelRect = new Rect(Styles.kMargin, Styles.kTopMargin + EditorGUI.kSingleLineHeight, labelSize.x, labelSize.y + EditorGUI.kSingleLineHeight); - GUI.Label(labelRect, Styles.gamePlayModeBehaviorLabelContent, EditorStyles.boldLabel); - - rect.y = rect.y + Styles.contentOffset + EditorGUI.kSingleLineHeight; - - 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 deleted file mode 100644 index 40102b6cac..0000000000 --- a/Editor/Mono/GameView/GameViewOnPlayMenuItemProvider.cs +++ /dev/null @@ -1,122 +0,0 @@ -// 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 b518ce473d..588b92fdae 100644 --- a/Editor/Mono/GameView/GameViewSizeMenu.cs +++ b/Editor/Mono/GameView/GameViewSizeMenu.cs @@ -10,11 +10,15 @@ 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; } } + float frameHeight { get { return kTopMargin * 2 + EditorGUI.kSingleLineHeight * (IsVSyncToggleVisible() ? 2 : 1);}} float contentOffset { get { return frameHeight + EditorGUI.kControlVerticalSpacing; } } public GameViewSizeMenu(IFlexibleMenuItemProvider itemProvider, int selectionIndex, FlexibleMenuModifyItemUI modifyItemUi, IGameViewSizeMenuUser gameView) @@ -32,6 +36,26 @@ 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); @@ -43,7 +67,7 @@ public override void OnGUI(Rect rect) m_GameView.lowResolutionForAspectRatios = GUI.Toggle(toggleRect, m_GameView.forceLowResolutionAspectRatios || m_GameView.lowResolutionForAspectRatios, GameView.Styles.lowResAspectRatiosContextMenuContent); GUI.enabled = true; - + DoVSyncToggle(toggleRect); rect.height = rect.height - contentOffset; rect.y = rect.y + contentOffset; diff --git a/Editor/Mono/GameView/GameViewSizesMenuModifyItemUI.cs b/Editor/Mono/GameView/GameViewSizesMenuModifyItemUI.cs index 2ac7b2877a..8ffdf97ae7 100644 --- a/Editor/Mono/GameView/GameViewSizesMenuModifyItemUI.cs +++ b/Editor/Mono/GameView/GameViewSizesMenuModifyItemUI.cs @@ -99,9 +99,11 @@ override public void OnGUI(Rect rect) { if (string.IsNullOrEmpty(displayText)) displayText = "Result"; - else - displayText = GetCroppedText(displayText, cropWidth, EditorStyles.label); - GUILayout.Label(GUIContent.Temp(displayText), EditorStyles.label); + + var clipping = EditorStyles.label.clipping; + EditorStyles.label.clipping = TextClipping.Ellipsis; + GUILayout.Label(GUIContent.Temp(displayText), EditorStyles.label, GUILayout.MaxWidth(cropWidth)); + EditorStyles.label.clipping = clipping; } GUILayout.FlexibleSpace(); GUILayout.Space(margin); @@ -133,21 +135,6 @@ override public void OnGUI(Rect rect) GUILayout.Space(Styles.windowBottomPadding); } - - string GetCroppedText(string fullText, float cropWidth, GUIStyle style) - { - // Check if we need to crop - int characterCountVisible = style.GetNumCharactersThatFitWithinWidth(fullText, cropWidth); - if (characterCountVisible == -1) - { - return fullText; - } - - if (characterCountVisible > 1 && characterCountVisible != fullText.Length) - return fullText.Substring(0, characterCountVisible - 1) + ("\u2026"); // 'horizontal ellipsis' (U+2026) is: ... - else - return fullText; - } } } diff --git a/Editor/Mono/GameView/IGameViewSizeMenuUser.cs b/Editor/Mono/GameView/IGameViewSizeMenuUser.cs index d2ed188a87..ff43698b8a 100644 --- a/Editor/Mono/GameView/IGameViewSizeMenuUser.cs +++ b/Editor/Mono/GameView/IGameViewSizeMenuUser.cs @@ -9,5 +9,6 @@ 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/Graphics/ShaderCompilerData.cs b/Editor/Mono/Graphics/ShaderCompilerData.cs index d31085e31e..29ff1a1873 100644 --- a/Editor/Mono/Graphics/ShaderCompilerData.cs +++ b/Editor/Mono/Graphics/ShaderCompilerData.cs @@ -69,6 +69,7 @@ public enum ShaderCompilerPlatform { None = 0, // For non initialized variable. D3D = 4, // Direct3D 11 (FL10.0 and up) and Direct3D 12, compiled with MS D3DCompiler + [System.Obsolete(@"OpenGL ES 2.0 is no longer supported since Unity 2023.1")] GLES20 = 5, // OpenGL ES 2.0 / WebGL 1.0, compiled with MS D3DCompiler + HLSLcc GLES3x = 9, // OpenGL ES 3.0+ / WebGL 2.0, compiled with MS D3DCompiler + HLSLcc PS4 = 11, // Sony PS4 diff --git a/Editor/Mono/Handles.cs b/Editor/Mono/Handles.cs index 1fdd7bf8ea..ca6ee0c7e7 100644 --- a/Editor/Mono/Handles.cs +++ b/Editor/Mono/Handles.cs @@ -1146,15 +1146,6 @@ static void DoDrawAAPolyLine(Color[] colors, Vector3[] points, int actualNumberO HandleUtility.ApplyWireMaterial(zTest); Color defaultColor = new Color(1, 1, 1, alpha); - - if (colors != null) - { - for (int i = 0; i < colors.Length; i++) - colors[i] *= defaultColor; - } - else - defaultColor *= color; - Internal_DrawAAPolyLine(colors, points, defaultColor, actualNumberOfPoints, lineTex, width, matrix); } diff --git a/Editor/Mono/Help.cs b/Editor/Mono/Help.cs index 93036121ed..8490eb6cc9 100644 --- a/Editor/Mono/Help.cs +++ b/Editor/Mono/Help.cs @@ -211,7 +211,11 @@ internal static string GetHelpURLForObject(Object obj, bool defaultToMonoBehavio } } - return url; + if (IsURL(url)) + return url; + + // Assume url is a topic that needs to be properly formatted: + return FindHelpNamed(url); } var topicForObject = HelpFileNameForObject(obj); @@ -355,6 +359,11 @@ internal static string HelpFileNameForObject(Object obj) return "LightmapSnapshot"; } + if (obj is PrefabImporter) + { + return "-Prefab Asset"; + } + if (obj is DefaultAsset) return ""; @@ -429,6 +438,11 @@ private static void InitDocumentation() } } + private static bool IsURL(string path) + { + return path.StartsWith("http://") || path.StartsWith("https://") || path.StartsWith(k_AbsoluteFileRef); + } + private static bool IsLocalPath(string path) { return !path.StartsWith("http://") && !path.StartsWith("https://"); diff --git a/Editor/Mono/HierarchyProperty.bindings.cs b/Editor/Mono/HierarchyProperty.bindings.cs index 8f1295023f..0efef4a481 100644 --- a/Editor/Mono/HierarchyProperty.bindings.cs +++ b/Editor/Mono/HierarchyProperty.bindings.cs @@ -210,7 +210,7 @@ public void SetSearchFilter(string searchString, int mode) // 4.0 interface (made internal for now) internal void SetSearchFilter(SearchFilter filter) { - SetSearchFilterImpl(SearchFilter.Split(filter.nameFilter), filter.classNames, filter.assetLabels, filter.assetBundleNames, filter.versionControlStates, filter.softLockControlStates, filter.referencingInstanceIDs, filter.sceneHandles, filter.GlobToRegex().ToArray(), filter.productIds, filter.anyWithAssetOrigin, filter.showAllHits, filter.importLogFlags); + SetSearchFilterImpl(SearchFilter.Split(filter.nameFilter), filter.classNames, filter.assetLabels, filter.assetBundleNames, new string[0], new string[0], filter.referencingInstanceIDs, filter.sceneHandles, filter.GlobToRegex().ToArray(), filter.productIds, filter.anyWithAssetOrigin, filter.showAllHits, filter.importLogFlags, filter.filterByTypeIntersection); } internal void CopySearchFilterFrom(HierarchyProperty other) @@ -221,7 +221,7 @@ internal void CopySearchFilterFrom(HierarchyProperty other) } [FreeFunction("HierarchyPropertyBindings::SetSearchFilterImpl", HasExplicitThis = true)] - extern void SetSearchFilterImpl(string[] nameFilters, string[] classNames, string[] assetLabels, string[] assetBundleNames, string[] versionControlStates, string[] softLockControlStates, int[] referencingInstanceIDs, int[] sceneHandles, string[] regex, int[] productIds, bool anyWithAssetOrigin, bool showAllHits, ImportLogFlags importLogFlags); + extern void SetSearchFilterImpl(string[] nameFilters, string[] classNames, string[] assetLabels, string[] assetBundleNames, string[] versionControlStates, string[] softLockControlStates, int[] referencingInstanceIDs, int[] sceneHandles, string[] regex, int[] productIds, bool anyWithAssetOrigin, bool showAllHits, ImportLogFlags importLogFlags, bool filterByTypeIntersection); [FreeFunction("HierarchyPropertyBindings::CopySearchFilterImpl", HasExplicitThis = true)] extern void CopySearchFilterImpl(HierarchyProperty other); diff --git a/Editor/Mono/HostView.cs b/Editor/Mono/HostView.cs index 88baaf4e69..63ad4b4397 100644 --- a/Editor/Mono/HostView.cs +++ b/Editor/Mono/HostView.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Data.Common; using System.IO; using System.Linq; using System.Reflection; @@ -31,13 +32,31 @@ public static class DataModes { public const float switchButtonWidth = 16.0f; + static readonly string k_AutomaticAuthoringString = L10n.Tr("Automatic (Authoring)"); + static readonly string k_AutomaticMixedString = L10n.Tr("Automatic (Mixed)"); + static readonly string k_AutomaticRuntimeString = L10n.Tr("Automatic (Runtime)"); + static readonly string k_AuthoringString = L10n.Tr("Authoring"); + static readonly string k_MixedString = L10n.Tr("Mixed"); + static readonly string k_RuntimeString = L10n.Tr("Runtime"); + static readonly string k_DisabledString = L10n.Tr("Disabled"); + + // Auto data mode icons static readonly Texture2D k_AuthoringModeIcon = EditorGUIUtility.LoadIcon("DataMode.Authoring"); static readonly Texture2D k_MixedModeIcon = EditorGUIUtility.LoadIcon("DataMode.Mixed"); static readonly Texture2D k_RuntimeModeIcon = EditorGUIUtility.LoadIcon("DataMode.Runtime"); - public static readonly GUIContent authoringModeContent = EditorGUIUtility.TrIconContent(k_AuthoringModeIcon, "Data Mode: Authoring"); - public static readonly GUIContent mixedModeContent = EditorGUIUtility.TrIconContent(k_MixedModeIcon, "Data Mode: Mixed"); - public static readonly GUIContent runtimeModeContent = EditorGUIUtility.TrIconContent(k_RuntimeModeIcon, "Data Mode: Runtime"); + public static readonly GUIContent authoringModeContent = EditorGUIUtility.TrIconContent(k_AuthoringModeIcon, k_AutomaticAuthoringString); + public static readonly GUIContent mixedModeContent = EditorGUIUtility.TrIconContent(k_MixedModeIcon, k_AutomaticMixedString); + public static readonly GUIContent runtimeModeContent = EditorGUIUtility.TrIconContent(k_RuntimeModeIcon, k_AutomaticRuntimeString); + + // Sticky data mode icons + static readonly Texture2D k_StickyAuthoringModeIcon = EditorGUIUtility.LoadIcon("DataMode.Authoring.Sticky"); + static readonly Texture2D k_StickyMixedModeIcon = EditorGUIUtility.LoadIcon("DataMode.Mixed.Sticky"); + static readonly Texture2D k_StickyRuntimeModeIcon = EditorGUIUtility.LoadIcon("DataMode.Runtime.Sticky"); + + public static readonly GUIContent stickyAuthoringModeContent = EditorGUIUtility.TrIconContent(k_StickyAuthoringModeIcon, k_AuthoringString); + public static readonly GUIContent stickyMixedModeContent = EditorGUIUtility.TrIconContent(k_StickyMixedModeIcon, k_MixedString); + public static readonly GUIContent stickyRuntimeModeContent = EditorGUIUtility.TrIconContent(k_StickyRuntimeModeIcon, k_RuntimeString); // Use an empty style to avoid the hover effect of normal buttons public static readonly GUIStyle switchStyle = new GUIStyle(); @@ -45,10 +64,10 @@ public static class DataModes public static readonly Dictionary dataModeNameLabels = new Dictionary { - { DataMode.Disabled, EditorGUIUtility.TrTextContent("Disabled") }, - { DataMode.Authoring, EditorGUIUtility.TrTextContent("Authoring Mode") }, - { DataMode.Mixed, EditorGUIUtility.TrTextContent("Mixed Mode") }, - { DataMode.Runtime, EditorGUIUtility.TrTextContent("Runtime Mode") } + { DataMode.Disabled, EditorGUIUtility.TextContent(k_DisabledString) }, + { DataMode.Authoring, EditorGUIUtility.TextContent(k_AuthoringString) }, + { DataMode.Mixed, EditorGUIUtility.TextContent(k_MixedString) }, + { DataMode.Runtime, EditorGUIUtility.TextContent(k_RuntimeString) } }; } @@ -85,6 +104,8 @@ static Styles() protected EditorWindowDelegate m_ModifierKeysChanged; protected EditorWindowShowButtonDelegate m_ShowButton; + private bool m_IsLosingFocus = false; + internal EditorWindow actualView { get { return m_ActualView; } @@ -282,7 +303,6 @@ protected override void OldOnGUI() { // Call reset GUI state as first thing so GUI.color is correct when drawing window decoration. EditorGUIUtility.ResetGUIState(); - DoWindowDecorationStart(); using (new GUILayout.VerticalScope(Styles.background)) { @@ -298,7 +318,6 @@ protected override void OldOnGUI() { CheckNotificationStatus(); - DoWindowDecorationEnd(); EditorGUI.ShowRepaints(); } } @@ -321,8 +340,22 @@ protected override bool OnFocus() internal void OnLostFocus() { + // Avoid calling OnLostFocus script callback if we are already processing the focus lost. + // This can happen when user scripts call Close() in OnLostFocus() callback. + if (m_IsLosingFocus) + return; + EditorGUI.EndEditingActiveTextField(); - m_OnLostFocus?.Invoke(); + + try + { + m_IsLosingFocus = true; + m_OnLostFocus?.Invoke(); + } + finally + { + m_IsLosingFocus = false; + } // Callback could have killed us if (!this) @@ -466,7 +499,10 @@ public void InvokeOnGUI(Rect onGUIPosition) if (!this) return; - DoWindowDecorationStart(); + //We backup and restore the group count to properly identify the ongui method where the unbalance occurs. + int backup = GUILayoutUtility.unbalancedgroupscount; + + GUILayoutUtility.unbalancedgroupscount = 0; BeginOffsetArea(m_ActualView.rootVisualElement.worldBound, GUIContent.none, Styles.tabWindowBackground); EditorGUIUtility.ResetGUIState(); @@ -474,36 +510,43 @@ public void InvokeOnGUI(Rect onGUIPosition) bool isExitGUIException = false; try { - GUILayoutUtility.unbalancedgroupscount = 0; m_OnGUI?.Invoke(); - if (GUILayoutUtility.unbalancedgroupscount > 0) - { - Debug.LogError("GUI Error: Invalid GUILayout state in " + GetActualViewName() + " view. Verify that all layout Begin/End calls match"); - GUILayoutUtility.unbalancedgroupscount = 0; - } } catch (TargetInvocationException e) { - if (e.InnerException is ExitGUIException) - isExitGUIException = true; + Exception exception = e; + while (exception is TargetInvocationException && exception.InnerException != null) + exception = exception.InnerException; + + isExitGUIException = exception is ExitGUIException; + + throw; + } + catch(ExitGUIException) + { + isExitGUIException = true; throw; } finally { // We can't reset gui state after ExitGUI we just want to bail completely - if (!isExitGUIException) + if (!(isExitGUIException || GUIUtility.guiIsExiting)) { CheckNotificationStatus(); EndOffsetArea(); + if (GUILayoutUtility.unbalancedgroupscount != 0) + { + Debug.LogError("GUI Error: Invalid GUILayout state in " + GetActualViewName() + " view. Verify that all layout Begin/End calls match"); + } EditorGUIUtility.ResetGUIState(); - DoWindowDecorationEnd(); - if (Event.current != null && Event.current.type == EventType.Repaint) Styles.overlay.Draw(onGUIPosition, GUIContent.none, 0); } + + GUILayoutUtility.unbalancedgroupscount = backup; } } @@ -527,9 +570,6 @@ public IEditorWindowBackend editorWindowBackend set { windowBackend = value; } } - DataMode m_CachedDataMode; - bool m_ShouldDrawDataModeSwitch; - protected void RegisterSelectedPane(bool sendEvents) { if (!m_ActualView) @@ -561,19 +601,6 @@ protected void RegisterSelectedPane(bool sendEvents) EditorApplication.update += m_ActualView.CheckForWindowRepaint; } - if (m_ActualView is IDataModeHandler dataModeHandler) - { - UpdateDataMode(dataModeHandler.dataMode, false); - - if (m_ActualView is IDataModeHandlerAndDispatcher dataModesDispatcher) - dataModesDispatcher.dataModeChanged += OnViewDataModeChanged; - } - else - { - m_CachedDataMode = DataMode.Disabled; - m_ShouldDrawDataModeSwitch = false; - } - if (sendEvents) { try @@ -611,19 +638,15 @@ protected void DeregisterSelectedPane(bool clearActualView, bool sendEvents) EditorApplication.update -= m_ActualView.CheckForWindowRepaint; } - if (m_ActualView is IDataModeHandlerAndDispatcher dataModesDispatcher) - dataModesDispatcher.dataModeChanged -= OnViewDataModeChanged; - if (clearActualView) { - var onLostFocus = m_OnLostFocus; var onBecameInvisible = m_OnBecameInvisible; m_ActualView = null; if (sendEvents) { - onLostFocus?.Invoke(); + OnLostFocus(); onBecameInvisible?.Invoke(); } ClearDelegates(); @@ -696,7 +719,7 @@ internal float GetExtraButtonsWidth() if (m_ShowButton != null) extraWidth += ContainerWindow.kButtonWidth; - if (m_ShouldDrawDataModeSwitch) + if (m_ActualView.GetDataModeController_Internal().ShouldDrawDataModesSwitch()) extraWidth += Styles.DataModes.switchButtonWidth + Styles.iconMargin; foreach (var item in windowActions) @@ -723,13 +746,15 @@ protected void ShowGenericMenu(float leftOffset, float topOffset) if (m_ShowButton != null) m_ShowButton.Invoke(new Rect(leftOffset, topOffset, ContainerWindow.kButtonWidth, ContainerWindow.kButtonHeight)); - if (m_ShouldDrawDataModeSwitch) + var dataModeControllerInternal = m_ActualView.GetDataModeController_Internal(); + if (dataModeControllerInternal.ShouldDrawDataModesSwitch()) { - var switchContent = m_CachedDataMode switch + var isClientInAutomaticDataMode = dataModeControllerInternal.isAutomatic; + var switchContent = dataModeControllerInternal.dataMode switch { - DataMode.Authoring => Styles.DataModes.authoringModeContent, - DataMode.Mixed => Styles.DataModes.mixedModeContent, - DataMode.Runtime => Styles.DataModes.runtimeModeContent, + DataMode.Authoring => isClientInAutomaticDataMode? Styles.DataModes.authoringModeContent : Styles.DataModes.stickyAuthoringModeContent, + DataMode.Mixed => isClientInAutomaticDataMode? Styles.DataModes.mixedModeContent : Styles.DataModes.stickyMixedModeContent, + DataMode.Runtime => isClientInAutomaticDataMode? Styles.DataModes.runtimeModeContent : Styles.DataModes.stickyRuntimeModeContent, _ => default }; @@ -741,11 +766,14 @@ protected void ShowGenericMenu(float leftOffset, float topOffset) if (EditorGUI.Button(switchRect, switchContent, Styles.DataModes.switchStyle)) { - // This cast is guaranteed to work by m_ShouldDrawDataModeSwitch - var dataModesClient = (IDataModeHandler) m_ActualView; + var evt = Event.current; - dataModesClient.SwitchToNextDataMode(); - UpdateDataMode(dataModesClient.dataMode, true); + if (evt.button == 0 || evt.button == 1) // left click or right click + { + var menu = new GenericMenu(); + PopulateDataModeDropdown(dataModeControllerInternal, menu); + menu.DropDown(switchRect); + } } } } @@ -770,39 +798,23 @@ protected void ShowGenericMenu(float leftOffset, float topOffset) } } - bool ShouldDrawDataModesSwitch() - { - return m_ActualView is IDataModeHandler dataModesHandler - && dataModesHandler.dataMode != DataMode.Disabled - // We don't want to show this switch if there are not - // at least 2 modes supported at the current moment. - && dataModesHandler.supportedDataModes.Count > 1; - } - - void SelectDataMode(object dataMode) + void PopulateDataModeDropdown(DataModeController clientDataModeController, GenericMenu menu) { - if (m_ActualView is not IDataModeHandler dataModeHandler) - return; // Something very weird has happened... + var autoLabel = !clientDataModeController.isAutomatic + ? L10n.Tr($"Automatic ({clientDataModeController.preferredDataMode})") + : L10n.Tr($"Automatic ({clientDataModeController.dataMode})"); - if (dataMode is DataMode mode && dataModeHandler.IsDataModeSupported(mode)) - dataModeHandler.SwitchToDataMode(mode); - else - dataModeHandler.SwitchToDefaultDataMode(); - - UpdateDataMode(dataModeHandler.dataMode, true); - } - - void OnViewDataModeChanged(DataMode newDataMode) => UpdateDataMode(newDataMode, true); + menu.AddItem(new GUIContent(autoLabel), + clientDataModeController.isAutomatic, + () => clientDataModeController.SwitchToAutomatic()); - void UpdateDataMode(DataMode newDataMode, bool needsRepaint) - { - m_CachedDataMode = newDataMode; - m_ShouldDrawDataModeSwitch = ShouldDrawDataModesSwitch(); + menu.AddSeparator(""); - if (needsRepaint) + foreach (var mode in clientDataModeController.supportedDataModes) { - m_ActualView.Repaint(); - RepaintImmediately(); + menu.AddItem(Styles.DataModes.dataModeNameLabels[mode], + !clientDataModeController.isAutomatic && clientDataModeController.dataMode == mode, + () => clientDataModeController.SwitchToStickyDataMode(mode)); } } @@ -920,54 +932,12 @@ internal void Reload(object userData) File.Delete(saveWindowPath); } - readonly List m_DataModeSanitizationCache = new List(3); // Number of modes, minus `Disabled` - - static void SanitizeSupportedDataModesList(IReadOnlyList originalList, List sanitizedList) - { - sanitizedList.Clear(); - - foreach (var mode in originalList) - { - if (mode == DataMode.Disabled) - continue; // Never list `DataMode.Disabled` - - if (sanitizedList.Contains(mode)) - continue; // Prevent duplicate entries - - sanitizedList.Add(mode); - } - - // Ensure we are displaying the data modes in a predefined order, regardless of - // the order in which the user defined their list. - sanitizedList.Sort(); - } protected virtual void AddDefaultItemsToMenu(GenericMenu menu, EditorWindow window) { if (menu.GetItemCount() != 0) menu.AddSeparator(""); - if (m_ShouldDrawDataModeSwitch) - { - // This cast is guaranteed to work by m_ShouldDrawDataModeSwitch - var dataModesHandler = (IDataModeHandler) window; - SanitizeSupportedDataModesList(dataModesHandler.supportedDataModes, m_DataModeSanitizationCache); - - // Don't show anything if only one mode is supported - if (m_DataModeSanitizationCache.Count > 1) - { - foreach (var mode in m_DataModeSanitizationCache) - { - menu.AddItem(Styles.DataModes.dataModeNameLabels[mode], - m_CachedDataMode == mode, - SelectDataMode, - mode); - } - - menu.AddSeparator(""); - } - } - if(window is ISupportsOverlays) { var binding = ShortcutManager.instance.GetShortcutBinding("Overlays/Show Overlay Menu"); diff --git a/Editor/Mono/ImportSettings/DesktopPluginImporterExtension.cs b/Editor/Mono/ImportSettings/DesktopPluginImporterExtension.cs index d8fe397fbb..6f7e8e71e5 100644 --- a/Editor/Mono/ImportSettings/DesktopPluginImporterExtension.cs +++ b/Editor/Mono/ImportSettings/DesktopPluginImporterExtension.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Text.RegularExpressions; using System.IO; using System.Linq; using UnityEditor.Modules; @@ -25,7 +26,12 @@ internal enum DesktopPluginCPUArchitecture internal class DesktopSingleCPUProperty : Property { public DesktopSingleCPUProperty(BuildTarget buildTarget, DesktopPluginCPUArchitecture architecture) - : base(EditorGUIUtility.TrTextContent(GetArchitectureNameInGUI(buildTarget, architecture)), "CPU", architecture, BuildPipeline.GetBuildTargetName(buildTarget)) + : base(EditorGUIUtility.TrTextContent(GetArchitectureNameInGUI(buildTarget, architecture)), cpuKey, architecture, BuildPipeline.GetBuildTargetName(buildTarget)) + { + } + + public DesktopSingleCPUProperty(GUIContent name, BuildTarget buildTarget) + : base(name, cpuKey, DesktopPluginCPUArchitecture.AnyCPU, BuildPipeline.GetBuildTargetName(buildTarget)) { } @@ -66,7 +72,7 @@ class DesktopMultiCPUProperty : Property private readonly GUIContent[] m_SupportedArchitectureNames; public DesktopMultiCPUProperty(BuildTarget buildTarget, params DesktopPluginCPUArchitecture[] supportedArchitectures) : - base("CPU", "CPU", DesktopPluginCPUArchitecture.None, BuildPipeline.GetBuildTargetName(buildTarget)) + base(cpuKey, cpuKey, DesktopPluginCPUArchitecture.None, BuildPipeline.GetBuildTargetName(buildTarget)) { // Add "None" and "AnyCPU" architectures to the supported architecture list m_SupportedArchitectures = new DesktopPluginCPUArchitecture[supportedArchitectures.Length + 2]; @@ -131,9 +137,9 @@ internal override void OnGUI(PluginImporterInspector inspector) } } - // Windows has 1 build target per CPU architecture - private readonly DesktopSingleCPUProperty m_WindowsX86; - private readonly DesktopSingleCPUProperty m_WindowsX86_X64; + // Windows has 2 build targets. One build target for 32bit that supports 1 CPU architectue. The other for 64 bit that supports multiple 64 bit architectures (x64 and ARM64). + private readonly DesktopSingleCPUProperty m_Windows32; + private readonly DesktopMultiCPUProperty m_Windows64; // Linux has 1 build target and 1 supported CPU architecture private readonly DesktopSingleCPUProperty m_Linux; @@ -141,21 +147,38 @@ internal override void OnGUI(PluginImporterInspector inspector) // macOS has multiple architectures, but one target. private readonly DesktopMultiCPUProperty m_MacOS; + // For Managed plugins the only options for CPU architecture should be "None" or "AnyCPU", so it can be DesktopSingleCPUProperty + private readonly DesktopSingleCPUProperty m_Windows32Managed; + private readonly DesktopSingleCPUProperty m_Windows64Managed; + private readonly DesktopSingleCPUProperty m_LinuxManaged; + private readonly DesktopSingleCPUProperty m_MacOSManaged; public DesktopPluginImporterExtension() : base(null) { - m_WindowsX86 = new DesktopSingleCPUProperty(BuildTarget.StandaloneWindows, DesktopPluginCPUArchitecture.x86); - m_WindowsX86_X64 = new DesktopSingleCPUProperty(BuildTarget.StandaloneWindows64, DesktopPluginCPUArchitecture.x86_64); + m_Windows32 = new DesktopSingleCPUProperty(BuildTarget.StandaloneWindows, DesktopPluginCPUArchitecture.x86); + m_Windows64 = new DesktopMultiCPUProperty(BuildTarget.StandaloneWindows64, DesktopPluginCPUArchitecture.x86_64, DesktopPluginCPUArchitecture.ARM64); + m_Linux = new DesktopSingleCPUProperty(BuildTarget.StandaloneLinux64, DesktopPluginCPUArchitecture.x86_64); m_MacOS = new DesktopMultiCPUProperty(BuildTarget.StandaloneOSX, DesktopPluginCPUArchitecture.x86_64, DesktopPluginCPUArchitecture.ARM64); + // Windows 32-bit (x86) and Windows 64-bit (ARM64/x86_64) are separate targets, so they have separate checkboxes + // Linux only has x64 architecture and Mac has a single target for both 64-bit architectures (ARM64/Intel-64bit) + m_Windows32Managed = new DesktopSingleCPUProperty(EditorGUIUtility.TrTextContent("Windows x86"), BuildTarget.StandaloneWindows); + m_Windows64Managed = new DesktopSingleCPUProperty(EditorGUIUtility.TrTextContent("Windows 64-bit"), BuildTarget.StandaloneWindows64); + m_LinuxManaged = new DesktopSingleCPUProperty(EditorGUIUtility.TrTextContent("Linux x64"),BuildTarget.StandaloneLinux64); + m_MacOSManaged = new DesktopSingleCPUProperty(EditorGUIUtility.TrTextContent("macOS 64-bit"),BuildTarget.StandaloneOSX); + properties = new Property[] { - m_WindowsX86, - m_WindowsX86_X64, + m_Windows32, + m_Windows64, m_Linux, - m_MacOS + m_MacOS, + m_Windows32Managed, + m_Windows64Managed, + m_LinuxManaged, + m_MacOSManaged }; } @@ -174,7 +197,7 @@ private bool IsUsableOnOSX(PluginImporter imp) return true; string ext = FileUtil.GetPathExtension(imp.assetPath).ToLower(); - return ext == "so" || ext == "bundle" || ext == "dylib" || IsCppPluginFile(imp.assetPath); + return ext == "bundle" || ext == "dylib" || IsLinuxLibrary(imp.assetPath) || IsCppPluginFile(imp.assetPath); } private bool IsUsableOnLinux(PluginImporter imp) @@ -182,33 +205,49 @@ private bool IsUsableOnLinux(PluginImporter imp) if (!imp.isNativePlugin) return true; - string ext = FileUtil.GetPathExtension(imp.assetPath).ToLower(); - return ext == "so" || IsCppPluginFile(imp.assetPath); + return IsLinuxLibrary(imp.assetPath) || IsCppPluginFile(imp.assetPath); } public override void OnPlatformSettingsGUI(PluginImporterInspector inspector) { PluginImporter imp = inspector.importer; EditorGUI.BeginChangeCheck(); - if (IsUsableOnWindows(imp)) + // skip CPU property for things that aren't native libs + if (imp.isNativePlugin) { - EditorGUILayout.LabelField(EditorGUIUtility.TrTextContent("Windows"), EditorStyles.boldLabel); - m_WindowsX86.OnGUI(inspector); - m_WindowsX86_X64.OnGUI(inspector); - EditorGUILayout.Space(); - } + if (IsUsableOnWindows(imp)) + { + EditorGUILayout.LabelField(EditorGUIUtility.TrTextContent("Windows"), EditorStyles.boldLabel); + m_Windows32.OnGUI(inspector); + m_Windows64.OnGUI(inspector); + EditorGUILayout.Space(); + } + + if (IsUsableOnLinux(imp)) + { + EditorGUILayout.LabelField(EditorGUIUtility.TrTextContent("Linux"), EditorStyles.boldLabel); + m_Linux.OnGUI(inspector); + EditorGUILayout.Space(); + } - if (IsUsableOnLinux(imp)) + if (IsUsableOnOSX(imp)) + { + EditorGUILayout.LabelField(EditorGUIUtility.TrTextContent("macOS"), EditorStyles.boldLabel); + m_MacOS.OnGUI(inspector); + EditorGUILayout.Space(); + } + } + else { - EditorGUILayout.LabelField(EditorGUIUtility.TrTextContent("Linux"), EditorStyles.boldLabel); - m_Linux.OnGUI(inspector); + // Managed plugins are usable on all platforms + m_Windows32Managed.OnGUI(inspector); + m_Windows64Managed.OnGUI(inspector); EditorGUILayout.Space(); - } - if (IsUsableOnOSX(imp)) - { - EditorGUILayout.LabelField(EditorGUIUtility.TrTextContent("macOS"), EditorStyles.boldLabel); - m_MacOS.OnGUI(inspector); + m_LinuxManaged.OnGUI(inspector); + EditorGUILayout.Space(); + + m_MacOSManaged.OnGUI(inspector); EditorGUILayout.Space(); } @@ -226,7 +265,7 @@ public void ValidateSingleCPUTargets(PluginImporterInspector inspector) foreach (var importer in inspector.importers) { - importer.SetPlatformData(target.platformName, "CPU", target.value.ToString()); + importer.SetPlatformData(target.platformName, cpuKey, target.value.ToString()); } } } @@ -251,13 +290,18 @@ public override string CalculateFinalPluginPath(string platformName, PluginImpor if (pluginForLinux && !IsUsableOnLinux(imp)) return string.Empty; - string cpu = imp.GetPlatformData(platformName, "CPU"); + string cpu = imp.GetPlatformData(platformName, cpuKey); if (string.Compare(cpu, "None", true) == 0) return string.Empty; if (pluginForWindows) { + if (string.Compare(cpu, nameof(DesktopPluginCPUArchitecture.ARM64), true) == 0) + { + return Path.Combine(cpu, Path.GetFileName(imp.assetPath)); + } + // Fix case 1185926: plugins for x86_64 are supposed to be copied to Plugins/x86_64 // Plugins for x86 are supposed to be copied to Plugins/x86 var cpuName = target == BuildTarget.StandaloneWindows ? nameof(DesktopPluginCPUArchitecture.x86) : nameof(DesktopPluginCPUArchitecture.x86_64); @@ -274,6 +318,13 @@ public override string CalculateFinalPluginPath(string platformName, PluginImpor return Path.GetFileName(imp.assetPath); } + // Regex that matchers strings ending in ".so" or ".so.12" or ".so.4.7" and so on. + private static Regex LinuxLibraryRegex = new Regex(@"\.so(\.[0-9]+)*$", RegexOptions.Compiled | RegexOptions.IgnoreCase); + internal static bool IsLinuxLibrary(string assetPath) + { + return LinuxLibraryRegex.IsMatch(assetPath); + } + internal static bool IsCppPluginFile(string assetPath) { var extension = Path.GetExtension(assetPath).ToLower(); diff --git a/Editor/Mono/ImportSettings/SpeedTreeImporterInspector.cs b/Editor/Mono/ImportSettings/SpeedTreeImporterInspector.cs index ad1aa221cb..624383e3f0 100644 --- a/Editor/Mono/ImportSettings/SpeedTreeImporterInspector.cs +++ b/Editor/Mono/ImportSettings/SpeedTreeImporterInspector.cs @@ -97,6 +97,14 @@ protected override bool OnApplyRevertGUI() { bool applied = base.OnApplyRevertGUI(); + bool hasModified = HasModified(); + if (tabs == null) // Hitting apply, we lose the tabs object within base.OnApplyRevertGUI() + { + if(hasModified) + Apply(); + return applied; + } + bool doMatsHaveDifferentShaders = (tabs[0] as SpeedTreeImporterModelEditor).doMaterialsHaveDifferentShader; if (HasRemappedMaterials() || doMatsHaveDifferentShaders) @@ -104,7 +112,6 @@ protected override bool OnApplyRevertGUI() // Force material upgrade when a custom render pipeline is active so that render pipeline-specific material // modifications may be applied. bool upgrade = upgradeMaterials || (UnityEngine.Rendering.GraphicsSettings.currentRenderPipeline != null); - bool hasModified = HasModified(); if (GUILayout.Button(GetGenButtonText(hasModified, upgrade))) { // Apply the changes and generate the materials before importing so that asset previews are up-to-date. diff --git a/Editor/Mono/ImportSettings/SpeedTreeImporterModelEditor.cs b/Editor/Mono/ImportSettings/SpeedTreeImporterModelEditor.cs index 498a23c31e..420614ee83 100644 --- a/Editor/Mono/ImportSettings/SpeedTreeImporterModelEditor.cs +++ b/Editor/Mono/ImportSettings/SpeedTreeImporterModelEditor.cs @@ -35,7 +35,7 @@ private class Styles public static GUIContent UseReflectionProbes = EditorGUIUtility.TrTextContent("Reflection Probes", "The tree uses reflection probe for rendering"); // TODO: update help text public static GUIContent AdditionalSettingsHeader = EditorGUIUtility.TrTextContent("Additional Settings"); - // TODO: motion vector settings? + public static GUIContent MotionVectorMode = EditorGUIUtility.TrTextContent("Motion Vectors", "Motion vector mode to set for the mesh renderer of each LOD object"); public static GUIContent WindHeader = EditorGUIUtility.TrTextContent("Wind"); public static GUIContent WindQuality = EditorGUIUtility.TrTextContent("Wind Quality", "Controls the wind effect's quality."); @@ -63,6 +63,12 @@ private class Styles , new GUIContent("inch to m") , new GUIContent("Custom") }; + public static GUIContent[] MotionVectorModeNames = // Match SharedRendererDataTypes.h / enum MotionVectorGenerationMode + { + new GUIContent("Camera Motion Only") // kMotionVectorCamera = 0, // Use camera motion for motion vectors + , new GUIContent("Per Object Motion") // kMotionVectorObject, // Use a per object motion vector pass for this object + , new GUIContent("Force No Motion") // kMotionVectorForceNoMotion, // Force no motion for this object (0 into motion buffer) + }; } //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -72,6 +78,7 @@ private class Styles // mesh private SerializedProperty m_ScaleFactor; private SerializedProperty m_UnitConversionEnumValue; + private SerializedProperty m_MotionVectorModeEnumValue; // material private SerializedProperty m_MainColor; @@ -123,6 +130,7 @@ internal override void OnEnable() { m_ScaleFactor = serializedObject.FindProperty("m_ScaleFactor"); m_UnitConversionEnumValue = serializedObject.FindProperty("m_UnitConversionEnumValue"); + m_MotionVectorModeEnumValue = serializedObject.FindProperty("m_MotionVectorModeEnumValue"); m_MainColor = serializedObject.FindProperty("m_MainColor"); m_EnableHueVariation = serializedObject.FindProperty("m_EnableHueVariation"); @@ -256,7 +264,7 @@ private bool DoMaterialsHaveDifferentShader() { foreach (var mat in mr.sharedMaterials) { - if (mat.shader != defaultShader) + if (mat?.shader != defaultShader) return true; } } @@ -265,7 +273,7 @@ private bool DoMaterialsHaveDifferentShader() { foreach (var br in prefabs[i].transform.GetComponentsInChildren()) { - if (br.billboard.material.shader != defaultBillboardShader) + if (br.billboard.material?.shader != defaultBillboardShader) return true; } } @@ -274,6 +282,14 @@ private bool DoMaterialsHaveDifferentShader() return false; } + private void ShowAdditionalSettingsGUI() + { + // motion vectors + GUILayout.Label(Styles.AdditionalSettingsHeader, EditorStyles.boldLabel); + EditorGUILayout.Popup(m_MotionVectorModeEnumValue, Styles.MotionVectorModeNames, Styles.MotionVectorMode); + + EditorGUILayout.Space(); + } public override void OnInspectorGUI() { @@ -281,6 +297,7 @@ public override void OnInspectorGUI() ShowMeshGUI(); ShowMaterialGUI(); ShowLightingGUI(); + ShowAdditionalSettingsGUI(); ShowWindGUI(); ShowLODGUI(); @@ -323,13 +340,36 @@ private void ShowMeshGUI() EditorGUILayout.Space(); } + + bool ShouldRenderHueVariationDropdown() + { + SpeedTreeImporter importer = importers.First(); + + if (importer.enableHueByDefault) + { + return true; + } + + for (int i = 0; i < importer.enableHue.Length; ++i) + { + if (importer.enableSettingOverride[i] && importer.enableHue[i]) + { + return true; + } + } + + return false; + } + public void ShowMaterialGUI() { EditorGUILayout.LabelField("Material", EditorStyles.boldLabel); EditorGUILayout.PropertyField(m_MainColor, Styles.MainColor); EditorGUILayout.PropertyField(m_EnableHueVariation, Styles.EnableColorVariation); - if (importers.First().enableHueByDefault) + + + if (ShouldRenderHueVariationDropdown()) { EditorGUILayout.PropertyField(m_HueVariation, Styles.HueVariation); } diff --git a/Editor/Mono/ImportSettings/TextureImportPlatformSettings.cs b/Editor/Mono/ImportSettings/TextureImportPlatformSettings.cs index c04ac0b6a4..647382bae2 100644 --- a/Editor/Mono/ImportSettings/TextureImportPlatformSettings.cs +++ b/Editor/Mono/ImportSettings/TextureImportPlatformSettings.cs @@ -142,6 +142,15 @@ public void Apply() public static BuildPlatform[] GetBuildPlayerValidPlatforms() { List validPlatforms = BuildPlatforms.instance.GetValidPlatforms(); + + // The Dedicated Server is a subtarget of Standalone, textures are generally stripped from server builds, + // so overriding the texture settings for the Dedicated Server is a rare case that doesn't add much value. + // Supporting texture overrides for Dedicated Server is possible, but as importer dependencies cannot distinguish + // today between Standalone-Server and Standalone-Non-Server, such support means that textures would reimport on + // switching, for instance, between Standalone-Win and Standalon-OSX. That would represent a performance regression + // and so, we have opted to not support texture overrides for Dedicated Server. + validPlatforms.Remove(BuildPlatforms.instance.BuildPlatformFromNamedBuildTarget(NamedBuildTarget.Server)); + return validPlatforms.ToArray(); } @@ -163,7 +172,11 @@ internal static void ShowPlatformSpecificSettings(List 0 && !m_IsReadable.hasMultipleDifferentValues && ShouldShowWarningForReadWrite()) { - ToggleFromInt(m_IsReadable, s_Styles.readWrite); - if (!enabled && m_IsReadable.intValue > 0) - { - EditorGUILayout.HelpBox(s_Styles.readWriteWarning.text, MessageType.Warning, true); - } + EditorGUILayout.HelpBox(s_Styles.readWriteWarning.text, MessageType.Info, true); } } @@ -1443,6 +1444,7 @@ public override void OnInspectorGUI() // NB we do these weird things partly because ApplyTextureType has early out // NB hence we want settings to have *old* textureType when calling it TextureImporterSettings settings = GetSerializedPropertySettings(); + settings.textureType = (TextureImporterType)oldTextureType; settings.ApplyTextureType((TextureImporterType)newTextureType); settings.textureType = (TextureImporterType)newTextureType; @@ -1548,31 +1550,6 @@ public override void OnInspectorGUI() if (!string.IsNullOrEmpty(m_ImportWarning)) EditorGUILayout.HelpBox(m_ImportWarning, MessageType.Warning); - if (!m_TextureShape.hasMultipleDifferentValues && !m_EnableMipMap.hasMultipleDifferentValues && !m_NPOTScale.hasMultipleDifferentValues) - { - // To avoid complexity of combinations of settings (e.g., tex1 is rescaled to POT with mipmap enabled, tex2 is NPOT with mipmap disabled, then all is fine but we would still get warnings) - // we only show the warnings for a single texture (or a group with the same values) - - if (m_TextureShape.intValue == (int)TextureImporterShape.Texture2D && m_EnableMipMap.boolValue && m_NPOTScale.intValue == (int)TextureImporterNPOTScale.None && TargetsHaveNPOTTextures()) - { - BuildTarget buildTarget = EditorUserBuildSettings.activeBuildTarget; - UnityEngine.Rendering.GraphicsDeviceType[] activeTargetGraphicsAPIs = PlayerSettings.GetGraphicsAPIs(buildTarget); - if (Array.Exists(activeTargetGraphicsAPIs, api => api == UnityEngine.Rendering.GraphicsDeviceType.OpenGLES2)) - { - GUIContent c; - if (buildTarget == BuildTarget.WebGL) - c = EditorGUIUtility.TrTextContent("NPOT textures are not fully supported on WebGL1. On these devices, mipmapping will be disabled."); - else if (buildTarget == BuildTarget.Android) - c = EditorGUIUtility.TrTextContent("Some Android devices running on OpenGLES2 may not support NPOT textures. If this is the case, mipmapping will be disabled."); - else - c = EditorGUIUtility.TrTextContent("Some devices running on OpenGLES2 may not support NPOT textures. If this is the case, mipmapping will be disabled."); - - EditorGUILayout.HelpBox(c.text, MessageType.Warning, true); - } - } - } - - GUI.enabled = wasEnabled; } @@ -1678,14 +1655,24 @@ private static bool IsPowerOfTwo(int f) return ((f & (f - 1)) == 0); } - bool CanReadWrite() + bool ShouldShowWarningForReadWrite() { - foreach (TextureImportPlatformSettings ps in m_PlatformSettings) + const int maxRecommendedSize = 536870912; // 512 MB + + if (textureInspector && textureInspector.targets != null) { - if (ps.model.platformTextureSettings.maxTextureSize > TextureImporter.MaxTextureSizeAllowedForReadable) - return false; + foreach (Texture texture in textureInspector.targets) + { + if (texture) + { + if (TextureUtil.GetStorageMemorySizeLong(texture) > maxRecommendedSize) + { + return true; + } + } + } } - return true; + return false; } public virtual void BuildTargetList() diff --git a/Editor/Mono/Inspector/AdvancedDropdown/AdvancedDropdownDataSource.cs b/Editor/Mono/Inspector/AdvancedDropdown/AdvancedDropdownDataSource.cs index a0e5cf80d6..df35ec80ee 100644 --- a/Editor/Mono/Inspector/AdvancedDropdown/AdvancedDropdownDataSource.cs +++ b/Editor/Mono/Inspector/AdvancedDropdown/AdvancedDropdownDataSource.cs @@ -13,11 +13,13 @@ internal abstract class AdvancedDropdownDataSource private AdvancedDropdownItem m_MainTree; private AdvancedDropdownItem m_SearchTree; + private AdvancedDropdownItem m_CurrentContextTree; private List m_SelectedIDs = new List(); public AdvancedDropdownItem mainTree { get { return m_MainTree; }} public AdvancedDropdownItem searchTree { get { return m_SearchTree; }} public List selectedIDs { get { return m_SelectedIDs; }} + public bool CurrentFolderContextualSearch { get; set; } protected AdvancedDropdownItem root { get { return m_MainTree; }} protected List m_SearchableElements; @@ -34,8 +36,13 @@ public void ReloadData() protected abstract AdvancedDropdownItem FetchData(); - public void RebuildSearch(string search) + public void RebuildSearch(string search, AdvancedDropdownItem currentTree) { + if (CurrentFolderContextualSearch && m_CurrentContextTree != currentTree && currentTree != searchTree) + { + m_SearchableElements = null; + } + m_CurrentContextTree = currentTree; m_SearchTree = Search(search); } @@ -126,7 +133,7 @@ protected virtual AdvancedDropdownItem Search(string searchString) void BuildSearchableElements() { m_SearchableElements = new List(); - BuildSearchableElements(root); + BuildSearchableElements(CurrentFolderContextualSearch && m_CurrentContextTree != null ? m_CurrentContextTree : root); } void BuildSearchableElements(AdvancedDropdownItem item) diff --git a/Editor/Mono/Inspector/AdvancedDropdown/AdvancedDropdownGUI.cs b/Editor/Mono/Inspector/AdvancedDropdown/AdvancedDropdownGUI.cs index 860a7b0db3..ca26e20101 100644 --- a/Editor/Mono/Inspector/AdvancedDropdown/AdvancedDropdownGUI.cs +++ b/Editor/Mono/Inspector/AdvancedDropdown/AdvancedDropdownGUI.cs @@ -34,6 +34,8 @@ internal static void LoadStyles() Debug.Assert(Event.current.type == EventType.Repaint && Styles.itemStyle != null); } + public static string k_SearchFieldName = "ComponentSearch"; + //This should ideally match line height private Vector2 s_IconSize = new Vector2(13, 13); private AdvancedDropdownDataSource m_DataSource; @@ -153,7 +155,7 @@ internal void DrawSearchField(bool isSearchFieldDisabled, string searchString, A using (new EditorGUI.DisabledScope(isSearchFieldDisabled)) { - GUI.SetNextControlName("ComponentSearch"); + GUI.SetNextControlName(k_SearchFieldName); var newSearch = DrawSearchFieldControl(searchString); diff --git a/Editor/Mono/Inspector/AdvancedDropdown/AdvancedDropdownWindow.cs b/Editor/Mono/Inspector/AdvancedDropdown/AdvancedDropdownWindow.cs index 6fdf2c9ca8..69749d861d 100644 --- a/Editor/Mono/Inspector/AdvancedDropdown/AdvancedDropdownWindow.cs +++ b/Editor/Mono/Inspector/AdvancedDropdown/AdvancedDropdownWindow.cs @@ -56,7 +56,7 @@ protected internal string searchString set { m_Search = value; - m_DataSource.RebuildSearch(m_Search); + m_DataSource.RebuildSearch(m_Search, m_CurrentlyRenderedTree); m_CurrentlyRenderedTree = m_DataSource.mainTree; if (hasSearch) { @@ -273,7 +273,7 @@ private void OnDirtyList() m_DataSource.ReloadData(); if (hasSearch) { - m_DataSource.RebuildSearch(searchString); + m_DataSource.RebuildSearch(searchString, m_CurrentlyRenderedTree); if (state.GetSelectedIndex(m_CurrentlyRenderedTree) < 0) { state.SetSelectedIndex(m_CurrentlyRenderedTree, 0); @@ -338,14 +338,15 @@ private void HandleKeyboard() } // Do these if we're not in search mode - if (!hasSearch) + if (!hasSearch && + (string.IsNullOrEmpty(GUI.GetNameOfFocusedControl()) || GUI.GetNameOfFocusedControl() == AdvancedDropdownGUI.k_SearchFieldName)) { if (evt.keyCode == KeyCode.LeftArrow || evt.keyCode == KeyCode.Backspace) { GoToParent(); evt.Use(); } - if (evt.keyCode == KeyCode.RightArrow) + else if (evt.keyCode == KeyCode.RightArrow) { var idx = m_State.GetSelectedIndex(m_CurrentlyRenderedTree); if (idx > -1 && m_CurrentlyRenderedTree.children.ElementAt(idx).children.Any()) @@ -354,7 +355,7 @@ private void HandleKeyboard() } evt.Use(); } - if (evt.keyCode == KeyCode.Escape) + else if (evt.keyCode == KeyCode.Escape) { Close(); evt.Use(); @@ -494,6 +495,7 @@ private void DrawList(AdvancedDropdownItem item) protected void GoToParent() { + GUI.FocusControl(""); if (m_ViewsStack.Count == 0) return; m_LastTime = DateTime.Now.Ticks; @@ -507,6 +509,7 @@ protected void GoToParent() private void GoToChild() { + GUI.FocusControl(""); m_ViewsStack.Push(m_CurrentlyRenderedTree); m_LastTime = DateTime.Now.Ticks; if (m_NewAnimTarget < 0) diff --git a/Editor/Mono/Inspector/AdvancedDropdown/EditorGUI/StatelessAdvancedDropdown.cs b/Editor/Mono/Inspector/AdvancedDropdown/EditorGUI/StatelessAdvancedDropdown.cs index 25031500d4..e62c5d9312 100644 --- a/Editor/Mono/Inspector/AdvancedDropdown/EditorGUI/StatelessAdvancedDropdown.cs +++ b/Editor/Mono/Inspector/AdvancedDropdown/EditorGUI/StatelessAdvancedDropdown.cs @@ -181,7 +181,7 @@ public static Enum DoEnumMaskPopup(Rect rect, Enum options, GUIStyle style) { var enumData = EnumDataUtility.GetCachedEnumData(options.GetType()); var optionValue = EnumDataUtility.EnumFlagsToInt(enumData, options); - MaskFieldGUI.GetMenuOptions(optionValue, enumData.displayNames, enumData.flagValues, out var buttonText, out var buttonTextMixed, out var optionNames, out var optionMaskValues, out var selectedOptions); + MaskFieldGUI.GetMenuOptions(optionValue, enumData.displayNames, enumData.flagValues, out var buttonText, out var buttonTextMixed, out _, out _, out _); var id = EditorGUIUtility.GetControlID("AdvancedDropdown".GetHashCode(), FocusType.Keyboard, rect); @@ -244,7 +244,7 @@ static int DoMaskField(Rect rect, int mask, string[] displayedOptions, GUIStyle for (int i = 0; i < flagValues.Length; ++i) flagValues[i] = (1 << i); - MaskFieldGUI.GetMenuOptions(mask, displayedOptions, flagValues, out var buttonText, out var buttonTextMixed, out var optionNames, out var optionMaskValues, out var selectedOptions); + MaskFieldGUI.GetMenuOptions(mask, displayedOptions, flagValues, out var buttonText, out var buttonTextMixed, out _, out _, out _); var id = EditorGUIUtility.GetControlID("AdvancedDropdown".GetHashCode(), FocusType.Keyboard, rect); diff --git a/Editor/Mono/Inspector/AnimationClipEditor.cs b/Editor/Mono/Inspector/AnimationClipEditor.cs index 09dd5b3a6b..4f3a18fc81 100644 --- a/Editor/Mono/Inspector/AnimationClipEditor.cs +++ b/Editor/Mono/Inspector/AnimationClipEditor.cs @@ -1780,6 +1780,7 @@ internal class EventManipulationHandler private AnimationWindowEvent[] m_Events; private TimeArea m_Timeline; + private AnimationEventEditorState m_EventEditorState = new(); public EventManipulationHandler(TimeArea timeArea) { @@ -1828,11 +1829,17 @@ public bool HandleEventManipulation(Rect rect, ref AnimationEvent[] events, Anim sharedOffset = Mathf.FloorToInt(Mathf.Max(0, spread - (eventMarker.width - 1) * (sharedLeft))); } + // UUM-49717 + // Depending on the resolution and the scale of the display, the icon size could be greater than the visible height of the timeline. + // We divide it so that it fits. + float absRectHeight = Mathf.Abs(rect.height); + int divider = absRectHeight > 0 && absRectHeight < eventMarker.height ? Mathf.CeilToInt(eventMarker.height / absRectHeight) : 1; + Rect r = new Rect( - keypos + sharedOffset - eventMarker.width / 2, + keypos + sharedOffset - eventMarker.width / (2 * divider), (rect.height - 10) * (float)(sharedLeft - shared + 1) / Mathf.Max(1, shared - 1), - eventMarker.width, - eventMarker.height); + eventMarker.width / divider, + eventMarker.height / divider); hitRects[i] = r; drawRects[i] = r; @@ -2062,7 +2069,7 @@ public void Draw(Rect window) { EditorGUI.indentLevel++; if (m_Events != null && m_Events.Length > 0) - AnimationWindowEventInspector.OnEditAnimationEvents(m_Events); + AnimationWindowEventInspector.OnEditAnimationEvents(m_Events, m_EventEditorState); else AnimationWindowEventInspector.OnDisabledAnimationEvent(); diff --git a/Editor/Mono/Inspector/AssemblyDefinitionImporterInspector.cs b/Editor/Mono/Inspector/AssemblyDefinitionImporterInspector.cs index 27719993f8..157d384600 100644 --- a/Editor/Mono/Inspector/AssemblyDefinitionImporterInspector.cs +++ b/Editor/Mono/Inspector/AssemblyDefinitionImporterInspector.cs @@ -24,11 +24,15 @@ internal class Styles { public static readonly GUIContent name = EditorGUIUtility.TrTextContent("Name", "The assembly name is used to generate a .dll file on you disk."); public static readonly GUIContent rootNamespace = EditorGUIUtility.TrTextContent("Root Namespace", "Specify the root namespace of the assembly."); - public static readonly GUIContent defineConstraints = EditorGUIUtility.TrTextContent("Define Constraints", "Specify a constraint in the assembly definition. The assembly definition only builds if this constraint returns True."); - public static readonly GUIContent versionDefines = EditorGUIUtility.TrTextContent("Version Defines", "Specify which versions of a packages and modules to include in compilations."); + public static readonly GUIContent defineConstraints = EditorGUIUtility.TrTextContent("Define Constraints", "Specify constraints which determine if the assembly will be compiled or not. The assembly will compile whenever all constraints are met."); + public static readonly GUIContent versionDefines = EditorGUIUtility.TrTextContent("Version Defines", "Specify preprocessor symbols to define based on package, module or Unity versions."); + public static readonly GUIContent resource = EditorGUIUtility.TrTextContent("If resource", "Select 'Unity' or the package or module that you want to set a define for."); + public static readonly GUIContent version = EditorGUIUtility.TrTextContent("version is", "Specify the semantic version of your chosen module, package or Unity Version. You must use mathematical interval notation."); + public static readonly GUIContent define = EditorGUIUtility.TrTextContent("set define", "Specify the name you want this define to have. This define is only set if the expression's condition is satisfied."); + public static readonly GUIContent expressionOutcome = EditorGUIUtility.TrTextContent("Version expression outcome", "Shows the mathematical equation applied to the version number used determine if the symbol should be defined."); public static readonly GUIContent references = EditorGUIUtility.TrTextContent("Assembly Definition References", "The list of assembly files that this assembly definition should reference."); public static readonly GUIContent precompiledReferences = EditorGUIUtility.TrTextContent("Assembly References", "The list of Precompiled assemblies that this assembly definition should reference."); - public static readonly GUIContent generalOptions = EditorGUIUtility.TrTextContent("General"); + public static readonly GUIContent generalOptions = EditorGUIUtility.TrTextContent("General Options"); public static readonly GUIContent allowUnsafeCode = EditorGUIUtility.TrTextContent("Allow 'unsafe' Code", "When enabled, the C# compiler for this assembly includes types or members that have the `unsafe` keyword."); public static readonly GUIContent overrideReferences = EditorGUIUtility.TrTextContent("Override References", "When enabled, you can select which specific precompiled assemblies to refer to via a drop-down list that appears. When not enabled, this assembly definition refers to all auto-referenced precompiled assemblies."); public static readonly GUIContent autoReferenced = EditorGUIUtility.TrTextContent("Auto Referenced", "When enabled, this assembly definition is automatically referenced in predefined assemblies."); @@ -40,7 +44,6 @@ internal class Styles public static readonly GUIContent selectAll = EditorGUIUtility.TrTextContent("Select all"); public static readonly GUIContent deselectAll = EditorGUIUtility.TrTextContent("Deselect all"); public static readonly GUIContent loadError = EditorGUIUtility.TrTextContent("Load error"); - public static readonly GUIContent expressionOutcome = EditorGUIUtility.TrTextContent("Expression outcome", "Shows the mathematical equation that your Expression represents."); public static readonly GUIContent noEngineReferences = EditorGUIUtility.TrTextContent("No Engine References", "When enabled, references to UnityEngine/UnityEditor will not be added when compiling this assembly."); // This is used to make everything in reorderable list elements centered vertically. @@ -56,16 +59,16 @@ internal class Styles public static Texture2D validDefineConstraint => EditorGUIUtility.pixelsPerPoint > 1 ? kValidDefineConstraintHighDpi : kValidDefineConstraint; public static Texture2D invalidDefineConstraint => EditorGUIUtility.pixelsPerPoint > 1 ? kInvalidDefineConstraintHighDpi : kInvalidDefineConstraint; - static string kCompatibleTextTitle = L10n.Tr("Define constraints are compatible."); - static string kIncompatibleTextTitle = L10n.Tr("One or more define constraints are invalid or incompatible."); + static string kCompatibleTextTitle = L10n.Tr("Define constraints are met."); + static string kIncompatibleTextTitle = L10n.Tr("One or more define constraints are invalid or not met."); public static string GetTitleTooltipFromDefineConstraintCompatibility(bool compatible) { return compatible ? kCompatibleTextTitle : kIncompatibleTextTitle; } - static string kCompatibleTextIndividual = L10n.Tr("Define constraint is compatible."); - static string kIncompatibleTextIndividual = L10n.Tr("Define constraint is incompatible."); + static string kCompatibleTextIndividual = L10n.Tr("Define constraint is met."); + static string kIncompatibleTextIndividual = L10n.Tr("Define constraint is not met."); static string kInvalidTextIndividual = L10n.Tr("Define constraint is invalid."); public static string GetIndividualTooltipFromDefineConstraintStatus(DefineConstraintsHelper.DefineConstraintStatus status) @@ -195,7 +198,7 @@ public override void OnEnable() { try { - using (var fs = File.Open(assetPath, FileMode.Open, FileAccess.Write)) {} + using (var fs = File.Open(assetPath, FileMode.Open, FileAccess.Write)) { } } catch { @@ -255,54 +258,22 @@ public override void OnInspectorGUI() EditorGUILayout.PropertyField(m_AssemblyName, Styles.name); } + GUILayout.Space(6f); + + // General Options GUILayout.Label(Styles.generalOptions, EditorStyles.boldLabel); + EditorGUILayout.BeginVertical(GUI.skin.box); EditorGUILayout.PropertyField(m_AllowUnsafeCode, Styles.allowUnsafeCode); EditorGUILayout.PropertyField(m_AutoReferenced, Styles.autoReferenced); EditorGUILayout.PropertyField(m_NoEngineReferences, Styles.noEngineReferences); EditorGUILayout.PropertyField(m_OverrideReferences, Styles.overrideReferences); EditorGUILayout.PropertyField(m_RootNamespace, Styles.rootNamespace); - EditorGUILayout.EndVertical(); - GUILayout.Space(10f); - - EditorGUILayout.BeginHorizontal(); - GUILayout.Label(Styles.defineConstraints, EditorStyles.boldLabel); - GUILayout.FlexibleSpace(); - EditorGUILayout.EndHorizontal(); - - if (m_DefineConstraints.serializedProperty.arraySize > 0) - { - var defineConstraintsCompatible = true; - - var defines = GetDefines(); - - if (defines != null) - { - for (var i = 0; i < m_DefineConstraints.serializedProperty.arraySize && defineConstraintsCompatible; ++i) - { - var defineConstraint = m_DefineConstraints.serializedProperty.GetArrayElementAtIndex(i).FindPropertyRelative("name").stringValue; - - if (DefineConstraintsHelper.GetDefineConstraintCompatibility(defines, defineConstraint) != DefineConstraintsHelper.DefineConstraintStatus.Compatible) - { - defineConstraintsCompatible = false; - } - } - - var constraintValidityRect = new Rect(GUILayoutUtility.GetLastRect()); - constraintValidityRect.x = constraintValidityRect.width - Styles.kValidityIconWidth / 4; - var image = defineConstraintsCompatible ? Styles.validDefineConstraint : Styles.invalidDefineConstraint; - var tooltip = Styles.GetTitleTooltipFromDefineConstraintCompatibility(defineConstraintsCompatible); - var content = new GUIContent(image, tooltip); - - constraintValidityRect.width = Styles.kValidityIconWidth; - constraintValidityRect.height = Styles.kValidityIconHeight; - EditorGUI.LabelField(constraintValidityRect, content); - } - } - m_DefineConstraints.DoLayoutList(); + GUILayout.Space(6f); + // ASMDEF References GUILayout.Label(Styles.references, EditorStyles.boldLabel); EditorGUILayout.BeginVertical(GUI.skin.box); @@ -331,6 +302,9 @@ public override void OnInspectorGUI() } } + GUILayout.Space(6f); + + // Platforms GUILayout.Label(Styles.platforms, EditorStyles.boldLabel); EditorGUILayout.BeginVertical(GUI.skin.box); @@ -403,10 +377,51 @@ public override void OnInspectorGUI() GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } - EditorGUILayout.EndVertical(); - GUILayout.Space(10f); + GUILayout.Space(6f); + + // Define Constraints + EditorGUILayout.BeginHorizontal(); + GUILayout.Label(Styles.defineConstraints, EditorStyles.boldLabel); + GUILayout.FlexibleSpace(); + EditorGUILayout.EndHorizontal(); + + if (m_DefineConstraints.serializedProperty.arraySize > 0) + { + var defineConstraintsCompatible = true; + + var defines = GetDefines(); + + if (defines != null) + { + for (var i = 0; i < m_DefineConstraints.serializedProperty.arraySize && defineConstraintsCompatible; ++i) + { + var defineConstraint = m_DefineConstraints.serializedProperty.GetArrayElementAtIndex(i).FindPropertyRelative("name").stringValue; + + if (DefineConstraintsHelper.GetDefineConstraintCompatibility(defines, defineConstraint) != DefineConstraintsHelper.DefineConstraintStatus.Compatible) + { + defineConstraintsCompatible = false; + } + } + + var constraintValidityRect = new Rect(GUILayoutUtility.GetLastRect()); + constraintValidityRect.x = constraintValidityRect.width - Styles.kValidityIconWidth / 4; + var image = defineConstraintsCompatible ? Styles.validDefineConstraint : Styles.invalidDefineConstraint; + var tooltip = Styles.GetTitleTooltipFromDefineConstraintCompatibility(defineConstraintsCompatible); + var content = new GUIContent(image, tooltip); + + constraintValidityRect.width = Styles.kValidityIconWidth; + constraintValidityRect.height = Styles.kValidityIconHeight; + EditorGUI.LabelField(constraintValidityRect, content); + } + } + + m_DefineConstraints.DoLayoutList(); + + GUILayout.Space(6f); + + // Version Defines EditorGUILayout.BeginVertical(GUI.skin.box); GUILayout.Label(Styles.versionDefines, EditorStyles.boldLabel); m_VersionDefineList.DoLayoutList(); @@ -534,20 +549,28 @@ private string[] GetDefines() return defines.Distinct().ToArray(); } - private List BuildListOfVersionDefineResourceOptions(string preselectedResourceName) + private List BuildListOfVersionDefineResourceOptions(string preselectedResourceName) { - var versionDefineResourceOptions = EditorCompilationInterface.Instance.GetVersionMetaDatas().Keys.ToList(); + var versionDefineResourceOptions = EditorCompilationInterface.Instance.GetVersionMetaDatas().Values.ToList(); - if (!string.IsNullOrEmpty(preselectedResourceName) && !versionDefineResourceOptions.Contains(preselectedResourceName)) + if (!string.IsNullOrEmpty(preselectedResourceName) && !versionDefineResourceOptions.Where(x => x.Name == preselectedResourceName).Any()) { - versionDefineResourceOptions.Add(preselectedResourceName); + versionDefineResourceOptions.Add(new VersionMetaData(preselectedResourceName)); } - versionDefineResourceOptions.Insert(0, "Select..."); + versionDefineResourceOptions.Insert(0, new VersionMetaData("Select...")); - bool optionUnityIsInTheList = versionDefineResourceOptions.Remove(UnityVersionTypeName); - if (optionUnityIsInTheList) - versionDefineResourceOptions.Insert(1, UnityVersionTypeName); + // Make sure Unity is always second option (index 1). + for (int i = versionDefineResourceOptions.Count - 1; i >= 2; --i) + { + if (versionDefineResourceOptions[i].Name == UnityVersionTypeName) + { + var tmp = versionDefineResourceOptions[i]; + versionDefineResourceOptions.RemoveAt(i); + versionDefineResourceOptions.Insert(1, tmp); + break; + } + } return versionDefineResourceOptions; } @@ -567,22 +590,41 @@ private void DrawVersionDefineListElement(Rect rect, int index, bool isactive, b int indexOfSelected = 0; if (!string.IsNullOrEmpty(nameProp.stringValue)) { - indexOfSelected = versionedResourceOptions.IndexOf(nameProp.stringValue); + indexOfSelected = versionedResourceOptions.IndexOf(versionedResourceOptions.Where(x => x.Name == nameProp.stringValue).First()); } bool mixed = versionDefineProp.hasMultipleDifferentValues; EditorGUI.showMixedValue = mixed; + string[] versionedResourceOptionsStrings = new string[versionedResourceOptions.Count]; + + for (int i = 0; i < versionedResourceOptions.Count; ++i) + { + if (string.IsNullOrEmpty(versionedResourceOptions[i].Version)) + { + versionedResourceOptionsStrings[i] = versionedResourceOptions[i].Name; + } + else + { + versionedResourceOptionsStrings[i] = $"{versionedResourceOptions[i].Name} ({versionedResourceOptions[i].Version})"; + } + } + var elementRect = new Rect(rect); elementRect.height = EditorGUIUtility.singleLineHeight; - int popupIndex = EditorGUI.Popup(elementRect, GUIContent.Temp("Resource", "Select 'Unity' or the package or module that you want to set a define for."), indexOfSelected, versionedResourceOptions.ToArray()); - nameProp.stringValue = versionedResourceOptions[popupIndex]; + elementRect.y += 1; + + var prefixLabel = EditorGUI.PrefixLabel(elementRect, Styles.resource); + + int popupIndex = EditorGUI.AdvancedPopup(prefixLabel, indexOfSelected, versionedResourceOptionsStrings); + + nameProp.stringValue = versionedResourceOptions[popupIndex].Name; elementRect.y += EditorGUIUtility.singleLineHeight; - defineProp.stringValue = EditorGUI.TextField(elementRect, GUIContent.Temp("Define", "Specify the name you want this define to have. This define is only set if the expression below returns true."), defineProp.stringValue); + expressionProp.stringValue = EditorGUI.TextField(elementRect, Styles.version, expressionProp.stringValue); elementRect.y += EditorGUIUtility.singleLineHeight; - expressionProp.stringValue = EditorGUI.TextField(elementRect, GUIContent.Temp("Expression", "Specify the Unity version or the semantic version of your chosen module or package. You must use mathematical interval notation."), expressionProp.stringValue); + defineProp.stringValue = EditorGUI.TextField(elementRect, Styles.define, defineProp.stringValue); string expressionOutcome = null; if (!string.IsNullOrEmpty(expressionProp.stringValue)) diff --git a/Editor/Mono/Inspector/AudioClipInspector.cs b/Editor/Mono/Inspector/AudioClipInspector.cs index 9d469b68c6..530acbac69 100644 --- a/Editor/Mono/Inspector/AudioClipInspector.cs +++ b/Editor/Mono/Inspector/AudioClipInspector.cs @@ -28,6 +28,9 @@ internal class AudioClipInspector : Editor static GUIContent s_AutoPlayIcon; static GUIContent s_LoopIcon; + static private string s_PreviewDisabledMessage = "AudioClip preview not available when Unity Audio is disabled in Project Settings"; + static private string s_TrPreviewDisabledMessage = L10n.Tr(s_PreviewDisabledMessage); + static Texture2D s_DefaultIcon; private Material m_HandleLinesMaterial; @@ -48,9 +51,11 @@ static void Init() s_AutoPlay = EditorPrefs.GetBool("AutoPlayAudio", false); s_Loop = false; - s_AutoPlayIcon = EditorGUIUtility.TrIconContent("preAudioAutoPlayOff", "Turn Auto Play on/off"); - s_PlayIcon = EditorGUIUtility.TrIconContent("PlayButton", "Play"); - s_LoopIcon = EditorGUIUtility.TrIconContent("preAudioLoopOff", "Loop on/off"); + var unityAudioDisabled = AudioSettings.unityAudioDisabled; + + s_AutoPlayIcon = EditorGUIUtility.TrIconContent("preAudioAutoPlayOff", unityAudioDisabled ? s_PreviewDisabledMessage : "Turn Auto Play on/off"); + s_PlayIcon = EditorGUIUtility.TrIconContent("PlayButton", unityAudioDisabled ? s_PreviewDisabledMessage : "Play"); + s_LoopIcon = EditorGUIUtility.TrIconContent("preAudioLoopOff", unityAudioDisabled ? s_PreviewDisabledMessage : "Loop on/off"); s_DefaultIcon = EditorGUIUtility.LoadIcon("Profiler.Audio"); } @@ -118,6 +123,7 @@ public override void OnPreviewSettings() AudioClip clip = target as AudioClip; m_MultiEditing = targets.Length > 1; + using (new EditorGUI.DisabledScope(AudioSettings.unityAudioDisabled)) { using (new EditorGUI.DisabledScope(m_MultiEditing && !playing)) { @@ -297,6 +303,11 @@ public override void OnPreviewGUI(Rect r, GUIStyle background) s_PlayFirst = false; } + if (AudioSettings.unityAudioDisabled) + { + EditorGUILayout.HelpBox(s_TrPreviewDisabledMessage, MessageType.Info); + } + // force update GUI if (playing) GUIView.current.Repaint(); diff --git a/Editor/Mono/Inspector/AudioSourceInspector.cs b/Editor/Mono/Inspector/AudioSourceInspector.cs index 27514408e5..1679eebe99 100644 --- a/Editor/Mono/Inspector/AudioSourceInspector.cs +++ b/Editor/Mono/Inspector/AudioSourceInspector.cs @@ -494,7 +494,7 @@ private void Audio3DGUI() if (targets.Length == 1) { AudioSource t = (AudioSource)target; - AudioListener audioListener = (AudioListener)FindObjectOfType(typeof(AudioListener)); + AudioListener audioListener = (AudioListener)FindFirstObjectByType(typeof(AudioListener)); if (audioListener != null) { float distToListener = (AudioUtil.GetListenerPos() - t.transform.position).magnitude; diff --git a/Editor/Mono/Inspector/CameraEditor.cs b/Editor/Mono/Inspector/CameraEditor.cs index f019b0651f..b70d8cbde1 100644 --- a/Editor/Mono/Inspector/CameraEditor.cs +++ b/Editor/Mono/Inspector/CameraEditor.cs @@ -552,8 +552,6 @@ int targetEyeValue get { return settings.targetEye.intValue; } } - Overlay m_PreviewOverlay; - static List displayDescriptors = new List(); static void OnReloadSubsystemsComplete() @@ -578,7 +576,7 @@ public void OnEnable() SubsystemManager.afterReloadSubsystems += OnReloadSubsystemsComplete; if(!SceneViewCameraOverlay.forceDisable) - SceneView.AddOverlayToActiveView(m_PreviewOverlay = CreatePreviewOverlay(c)); + CreatePreviewOverlay(c); } public void OnDestroy() @@ -589,7 +587,7 @@ public void OnDestroy() public void OnDisable() { - SceneView.RemoveOverlayFromActiveView(m_PreviewOverlay); + SceneViewCameraOverlay.DisableCameraOverlay((Camera)target); m_ShowBGColorOptions.valueChanged.RemoveListener(Repaint); m_ShowOrthoOptions.valueChanged.RemoveListener(Repaint); m_ShowTargetEyeOption.valueChanged.RemoveListener(Repaint); @@ -774,7 +772,7 @@ public virtual void OnOverlayGUI(Object target, SceneView sceneView) { } - public virtual Overlay CreatePreviewOverlay(Camera cam) => new SceneViewCameraOverlay(cam); + public virtual Overlay CreatePreviewOverlay(Camera cam) => SceneViewCameraOverlay.GetOrCreateCameraOverlay(cam); [RequiredByNativeCode] internal static float GetGameViewAspectRatio() diff --git a/Editor/Mono/Inspector/CameraEditorUtils.cs b/Editor/Mono/Inspector/CameraEditorUtils.cs index d1430a24b5..bf8b8b7768 100644 --- a/Editor/Mono/Inspector/CameraEditorUtils.cs +++ b/Editor/Mono/Inspector/CameraEditorUtils.cs @@ -183,7 +183,7 @@ public static bool TryGetSensorGateFrustum(Camera camera, Vector3[] near, Vector if (near != null) { Vector2 planeSize; - planeSize.y = 2.0f * camera.nearClipPlane * Mathf.Tan(Mathf.Deg2Rad * camera.fieldOfView * 0.5f); + planeSize.y = camera.nearClipPlane * Mathf.Tan(Mathf.Deg2Rad * camera.fieldOfView * 0.5f); planeSize.x = planeSize.y * camera.sensorSize.x / camera.sensorSize.y; Vector3 rightOffset = camera.gameObject.transform.right * planeSize.x; diff --git a/Editor/Mono/Inspector/CameraOverlay.cs b/Editor/Mono/Inspector/CameraOverlay.cs index 6b546205fa..bc8732d195 100644 --- a/Editor/Mono/Inspector/CameraOverlay.cs +++ b/Editor/Mono/Inspector/CameraOverlay.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.Collections.Generic; using UnityEditor.Overlays; using UnityEngine; using UnityEngine.Experimental.Rendering; @@ -10,7 +11,7 @@ namespace UnityEditor { - [Overlay(id = k_OverlayID, displayName = k_DisplayName)] + [Overlay(id = k_OverlayID, displayName = k_DisplayName, defaultDisplay = true)] [Icon("Icons/Overlays/CameraPreview.png")] class SceneViewCameraOverlay : IMGUIOverlay { @@ -28,9 +29,10 @@ class SceneViewCameraOverlay : IMGUIOverlay Camera previewCamera => m_PreviewCamera; RenderTexture m_PreviewTexture; - int m_QualitySettingsAntiAliasing = -1; - public SceneViewCameraOverlay(Camera camera) + static Dictionary s_CameraOverlays = new Dictionary(); + + SceneViewCameraOverlay(Camera camera) { minSize = new Vector2(40, 40); maxSize = new Vector2(4000, 4000); @@ -39,6 +41,8 @@ public SceneViewCameraOverlay(Camera camera) displayName = selectedCamera == null || string.IsNullOrEmpty(selectedCamera.name) ? "Camera Preview" : selectedCamera.name; + + s_CameraOverlays.Add(camera, (this ,1)); } public override void OnCreated() @@ -55,6 +59,37 @@ public override void OnWillBeDestroyed() UnityObject.DestroyImmediate(m_PreviewCamera.gameObject, true); } + public static SceneViewCameraOverlay GetOrCreateCameraOverlay(Camera camera) + { + if (s_CameraOverlays.ContainsKey(camera)) + { + var value = s_CameraOverlays[camera]; + value.count += 1; + s_CameraOverlays[camera] = value; + return value.overlay; + } + + var overlay = new SceneViewCameraOverlay(camera); + SceneView.AddOverlayToActiveView(overlay); + return overlay; + } + + public static void DisableCameraOverlay(Camera cam) + { + if (s_CameraOverlays.ContainsKey(cam)) + { + var value = s_CameraOverlays[cam]; + value.count -= 1; + if (value.count == 0) + { + s_CameraOverlays.Remove(cam); + SceneView.RemoveOverlayFromActiveView(value.overlay); + } + else + s_CameraOverlays[cam] = value; + } + } + RenderTexture GetPreviewTextureWithSizeAndAA(int width, int height) { int antiAliasing = Mathf.Max(1, QualitySettings.antiAliasing); @@ -64,7 +99,6 @@ RenderTexture GetPreviewTextureWithSizeAndAA(int width, int height) m_PreviewTexture.Release(); m_PreviewTexture = new RenderTexture(width, height, 24, SystemInfo.GetGraphicsFormat(DefaultFormat.LDR)); - m_QualitySettingsAntiAliasing = QualitySettings.antiAliasing; m_PreviewTexture.antiAliasing = antiAliasing; } return m_PreviewTexture; @@ -82,8 +116,9 @@ public override void OnGUI() imguiContainer.style.minWidth = collapsed ? new StyleLength(240) : new StyleLength(StyleKeyword.Auto); imguiContainer.style.minHeight = collapsed ? new StyleLength(135) : new StyleLength(StyleKeyword.Auto); - imguiContainer.parent.style.flexGrow = 1; - imguiContainer.style.flexGrow = 1; + + imguiContainer.style.flexGrow = collapsed ? 0 : 1; + imguiContainer.parent.style.flexGrow = collapsed ? 0 : 1; if (selectedCamera == null) { @@ -104,7 +139,7 @@ public override void OnGUI() var cameraRect = imguiContainer.rect; cameraRect.width = Mathf.Floor(cameraRect.width); - if (cameraRect.width < 1 || cameraRect.height < 1) + if (cameraRect.width < 1 || cameraRect.height < 1 || float.IsNaN(cameraRect.width) || float.IsNaN(cameraRect.height)) return; if (Event.current.type == EventType.Repaint) @@ -176,6 +211,7 @@ public override void OnGUI() RenderTexture.active = rt; } + previewCamera.cameraType = CameraType.Preview; previewCamera.Render(); Graphics.DrawTexture(previewRect, previewTexture, new Rect(0, 0, 1, 1), 0, 0, 0, 0, GUI.color, EditorGUIUtility.GUITextureBlit2SRGBMaterial); diff --git a/Editor/Mono/Inspector/CanvasEditor.cs b/Editor/Mono/Inspector/CanvasEditor.cs index 796d8602cc..a61867dcbf 100644 --- a/Editor/Mono/Inspector/CanvasEditor.cs +++ b/Editor/Mono/Inspector/CanvasEditor.cs @@ -28,6 +28,7 @@ internal class CanvasEditor : Editor SerializedProperty m_OverrideSorting; SerializedProperty m_ShaderChannels; SerializedProperty m_UpdateRectTransformForStandalone; + SerializedProperty m_VertexColorAlwaysGammaSpace; AnimBool m_OverlayMode; AnimBool m_CameraMode; @@ -47,6 +48,7 @@ private static class Styles public static GUIContent m_ShaderChannel = EditorGUIUtility.TrTextContent("Additional Shader Channels"); public static GUIContent pixelPerfectContent = EditorGUIUtility.TrTextContent("Pixel Perfect"); public static GUIContent standaloneRenderResize = EditorGUIUtility.TrTextContent("Resize Canvas", "For manual Camera.Render calls should the canvas resize to match the destination target."); + public static GUIContent vertexColorAlwaysGammaSpace = EditorGUIUtility.TrTextContent("Vertex Color Always In Gamma Color Space", "UI vertex colors are always in gamma color space disregard of the player settings"); } private bool m_AllNested = false; @@ -81,6 +83,7 @@ void OnEnable() m_PixelPerfectOverride = serializedObject.FindProperty("m_OverridePixelPerfect"); m_ShaderChannels = serializedObject.FindProperty("m_AdditionalShaderChannelsFlag"); m_UpdateRectTransformForStandalone = serializedObject.FindProperty("m_UpdateRectTransformForStandalone"); + m_VertexColorAlwaysGammaSpace = serializedObject.FindProperty("m_VertexColorAlwaysGammaSpace"); m_OverlayMode = new AnimBool(m_RenderMode.intValue == 0); m_OverlayMode.valueChanged.AddListener(Repaint); @@ -311,7 +314,19 @@ public override void OnInspectorGUI() if (m_RenderMode.intValue == 0) // Overlay canvas { if (((newShaderChannelValue & (int)AdditionalCanvasShaderChannels.Normal) | (newShaderChannelValue & (int)AdditionalCanvasShaderChannels.Tangent)) != 0) - EditorGUILayout.HelpBox("Shader channels Normal and Tangent are most often used with lighting, which an Overlay canvas does not support. Its likely these channels are not needed.", MessageType.Warning); + { + var helpMessage = "Shader channels Normal and Tangent are most often used with lighting, which an Overlay canvas does not support. Its likely these channels are not needed."; + rect = GUILayoutUtility.GetRect(EditorGUIUtility.TempContent(helpMessage, EditorGUIUtility.GetHelpIcon(MessageType.Warning)), EditorStyles.helpBox); + EditorGUI.HelpBox(rect, helpMessage, MessageType.Warning); + } + } + + EditorGUILayout.PropertyField(m_VertexColorAlwaysGammaSpace, Styles.vertexColorAlwaysGammaSpace); + + if (PlayerSettings.colorSpace == ColorSpace.Linear) + { + if (!m_VertexColorAlwaysGammaSpace.boolValue) + EditorGUILayout.HelpBox( "Keep vertex color in Gamma space to allow gamma to linear color space conversion to happen in UI shaders. This will enhance UI color precision in linear color space.", MessageType.Warning); } } else diff --git a/Editor/Mono/Inspector/CubemapInspector.cs b/Editor/Mono/Inspector/CubemapInspector.cs index bedd050c54..ec70c78f7e 100644 --- a/Editor/Mono/Inspector/CubemapInspector.cs +++ b/Editor/Mono/Inspector/CubemapInspector.cs @@ -4,7 +4,8 @@ using System.Globalization; using UnityEngine; - +using UnityEngine.Rendering; +using UnityEngine.Experimental.Rendering; namespace UnityEditor { @@ -15,13 +16,9 @@ internal class CubemapInspector : TextureInspector static private readonly int[] kSizesValues = { 16, 32, 64, 128, 256, 512, 1024, 2048 }; const int kTextureSize = 64; - private Texture2D[] m_Images; + private static readonly string kNativeTextureNotice = L10n.Tr("External texture: Unity cannot make changes to this Cubemap."); - protected override void OnEnable() - { - base.OnEnable(); - InitTexturesFromCubemap(); - } + private Texture2D[] m_Images; protected override void OnDisable() { @@ -41,18 +38,34 @@ protected override void OnDisable() private void InitTexturesFromCubemap() { var c = target as Cubemap; - if (c != null) + if (c is null || c.isNativeTexture) { - if (m_Images == null) - m_Images = new Texture2D[6]; - for (int i = 0; i < m_Images.Length; ++i) - { - if (m_Images[i] && !EditorUtility.IsPersistent(m_Images[i])) - DestroyImmediate(m_Images[i]); + return; + } - if (TextureUtil.GetSourceTexture(c, (CubemapFace)i)) + if (m_Images == null) + m_Images = new Texture2D[6]; + for (int i = 0; i < m_Images.Length; ++i) + { + if (m_Images[i] && !EditorUtility.IsPersistent(m_Images[i])) + DestroyImmediate(m_Images[i]); + + if (TextureUtil.GetSourceTexture(c, (CubemapFace)i)) + { + m_Images[i] = TextureUtil.GetSourceTexture(c, (CubemapFace)i); + } + else + { + // When the Cubemap is compressed, avoid "CopyCubemapFaceIntoTexture" due to potentially very high decompression cost. (example: Cubemap with no mipmaps) + // Note: the CopyTexture approach may produce results that look slightly different if "CopyCubemapFaceIntoTexture" would have downscaled to kTextureSize. + if (GraphicsFormatUtility.IsCompressedFormat(c.format) && SystemInfo.copyTextureSupport.HasFlag(CopyTextureSupport.DifferentTypes)) { - m_Images[i] = TextureUtil.GetSourceTexture(c, (CubemapFace)i); + int previewSize = System.Math.Clamp(kTextureSize, c.width >> (c.mipmapCount - 1), c.width); + m_Images[i] = new Texture2D(previewSize, previewSize, c.format, false); + m_Images[i].hideFlags = HideFlags.HideAndDontSave; + + int mipToCopy = (int)(System.Math.Log(c.width, 2) - System.Math.Log(previewSize, 2)); + Graphics.CopyTexture(c, i, mipToCopy, m_Images[i], 0, 0); } else { @@ -66,13 +79,20 @@ private void InitTexturesFromCubemap() public override void OnInspectorGUI() { + var c = target as Cubemap; + if (c == null) + return; + + if (c.isNativeTexture) + { + EditorGUILayout.HelpBox(kNativeTextureNotice, MessageType.Info); + return; + } + if (m_Images == null) InitTexturesFromCubemap(); EditorGUIUtility.labelWidth = 50; - var c = target as Cubemap; - if (c == null) - return; GUILayout.BeginVertical(); diff --git a/Editor/Mono/Inspector/Editor.cs b/Editor/Mono/Inspector/Editor.cs index 6c02192e2a..57ca2a5fbf 100644 --- a/Editor/Mono/Inspector/Editor.cs +++ b/Editor/Mono/Inspector/Editor.cs @@ -306,6 +306,7 @@ internal interface IToolModeOwner bool ModeSurvivesSelectionChange(int toolMode); } + [Obsolete(@"CustomEditorForRenderPipelineAttribute is deprecated. Use CustomEditor with SupportedOnCurrentPipeline instead. #from(2023.1)", false)] [AttributeUsage(AttributeTargets.Class)] public class CustomEditorForRenderPipelineAttribute : CustomEditor { @@ -390,6 +391,11 @@ internal InspectorMode inspectorMode } } + internal DataMode dataMode => + propertyViewer is EditorWindow editorWindow + ? editorWindow.dataModeController.dataMode + : DataMode.Disabled; + internal static float kLineHeight = EditorGUI.kSingleLineHeight; internal bool hideInspector = false; @@ -445,7 +451,33 @@ public virtual void DiscardChanges() // used internally to know if this the first editor in the inspector window internal bool firstInspectedEditor { get; set; } - internal IPropertyView propertyViewer { get; set; } + IPropertyView m_PropertyViewer; + + internal IPropertyView propertyViewer + { + get + { + return m_PropertyViewer; + } + set + { + if (m_PropertyViewer != value) + { + m_PropertyViewer = value; + + // We are being assigned a new property view with different inspector mode to what our serialized object was built with. + if (null != m_PropertyViewer && m_PropertyViewer.inspectorMode != m_InspectorMode) + { + // Keep the local inspector mode in sync. + m_InspectorMode = m_PropertyViewer.inspectorMode; + + // Trash the local serialized object and property cache to force a rebuild. + m_SerializedObject = null; + m_EnabledProperty = null; + } + } + } + } internal virtual bool HasLargeHeader() { @@ -659,6 +691,9 @@ private void CreateSerializedObject() { m_SerializedObject = new SerializedObject(targets, m_Context); m_SerializedObject.inspectorMode = inspectorMode; + if (m_SerializedObject.inspectorDataMode != dataMode) + m_SerializedObject.inspectorDataMode = dataMode; + AssignCachedProperties(this, m_SerializedObject.GetIterator()); m_EnabledProperty = m_SerializedObject.FindProperty("m_Enabled"); } @@ -917,6 +952,9 @@ public void DrawHeader() EditorGUILayout.BeginVertical(UseDefaultMargins() ? EditorStyles.inspectorDefaultMargins : GUIStyle.none); } } + + // Restore previous hierarchy mode + EditorGUIUtility.hierarchyMode = hierarchyMode; } // This is the method to override to create custom header GUI. @@ -1325,10 +1363,14 @@ internal bool CanBeExpandedViaAFoldout() return true; if (m_SerializedObject == null) + { CreateSerializedObject(); + } else m_SerializedObject.Update(); m_SerializedObject.inspectorMode = inspectorMode; + if (m_SerializedObject.inspectorDataMode != dataMode) + m_SerializedObject.inspectorDataMode = dataMode; return CanBeExpandedViaAFoldoutWithoutUpdate(); } @@ -1339,7 +1381,9 @@ internal bool CanBeExpandedViaAFoldoutWithoutUpdate() return true; if (m_SerializedObject == null) + { CreateSerializedObject(); + } SerializedProperty property = m_SerializedObject.GetIterator(); bool analyzePropertyChildren = true; diff --git a/Editor/Mono/Inspector/EditorDragging.cs b/Editor/Mono/Inspector/EditorDragging.cs index 31a7b8d442..7cc50be9ce 100644 --- a/Editor/Mono/Inspector/EditorDragging.cs +++ b/Editor/Mono/Inspector/EditorDragging.cs @@ -101,9 +101,16 @@ void HandleNativeDragDropInBottomArea(Editor[] editors, Rect rect) if (Event.current.type == EventType.DragPerform) { - DragAndDrop.AcceptDrag(); - m_TargetIndex = -1; - GUIUtility.ExitGUI(); + // Determine if any object references are component, which would mean we are reordering components in the inspector + var anyComponent = DragAndDrop.objectReferences != null && DragAndDrop.objectReferences.Any(o => o is Component); + + // None of the object references are component, cancel further processing to avoid adding component twice + if (!anyComponent) + { + DragAndDrop.AcceptDrag(); + m_TargetIndex = -1; + GUIUtility.ExitGUI(); + } } } diff --git a/Editor/Mono/Inspector/EditorElementUpdater.cs b/Editor/Mono/Inspector/EditorElementUpdater.cs index 488371110c..950857780b 100644 --- a/Editor/Mono/Inspector/EditorElementUpdater.cs +++ b/Editor/Mono/Inspector/EditorElementUpdater.cs @@ -34,12 +34,27 @@ public EditorElementUpdater(PropertyEditor propertyEditor) /// /// Adds the specified to the updater. /// - /// + /// The editor element to add. public void Add(IEditorElement element) { m_EditorElements.Add(element); } + /// + /// Removes the specified from the updater. + /// + /// The editor element to remove. + public void Remove(IEditorElement element) + { + var index = m_EditorElements.IndexOf(element); + + if (index == -1) + return; + + if (m_Index > index) + m_Index--; + } + /// /// Clears the internal state and resets the enumerator. /// diff --git a/Editor/Mono/Inspector/EditorSettingsInspector.cs b/Editor/Mono/Inspector/EditorSettingsInspector.cs index 70cb76e10a..2676fbc4b7 100644 --- a/Editor/Mono/Inspector/EditorSettingsInspector.cs +++ b/Editor/Mono/Inspector/EditorSettingsInspector.cs @@ -7,7 +7,6 @@ using UnityEditorInternal; using UnityEngine; using UnityEditor.Hardware; -using UnityEditor.Collaboration; using UnityEngine.Assertions; namespace UnityEditor @@ -93,7 +92,6 @@ class Content public static readonly GUIContent enterPlayModeOptionsEnabled = EditorGUIUtility.TrTextContent("Enter Play Mode Options", "Enables options when Entering Play Mode"); public static readonly GUIContent enterPlayModeOptionsEnableDomainReload = EditorGUIUtility.TrTextContent("Reload Domain", "Enables Domain Reload when Entering Play Mode. Domain reload reinitializes game completely making loading behavior very close to the Player"); public static readonly GUIContent enterPlayModeOptionsEnableSceneReload = EditorGUIUtility.TrTextContent("Reload Scene", "Enables Scene Reload when Entering Play Mode. Scene reload makes loading behavior and performance characteristics very close to the Player"); - public static readonly GUIContent enterPlayModeOptionsDisableSceneBackup = EditorGUIUtility.TrTextContent("Disable Scene Backup", "Conditionally skips writing a backup of the open scenes to disk. Only scenes that are modified in-memory need to be backed up, but making modifications from script may change the scene without setting the scene's dirty flag."); public static readonly GUIContent numberingScheme = EditorGUIUtility.TrTextContent("Numbering Scheme"); @@ -427,21 +425,14 @@ public override void OnInspectorGUI() if (m_IsGlobalSettings) ShowUnityRemoteGUI(editorEnabled); - bool collabEnabled = Collab.instance.IsCollabEnabledForCurrentProject(); GUILayout.Space(10); + GUI.enabled = true; + GUILayout.Label(Content.assetSerialization, EditorStyles.boldLabel); + GUI.enabled = editorEnabled; + int index = m_SerializationMode.intValue; - using (new EditorGUI.DisabledScope(!collabEnabled)) - { - GUI.enabled = !collabEnabled; - GUILayout.Label(Content.assetSerialization, EditorStyles.boldLabel); - GUI.enabled = editorEnabled && !collabEnabled; - CreatePopupMenu("Mode", serializationPopupList, index, SetAssetSerializationMode); - } - if (collabEnabled) - { - EditorGUILayout.HelpBox("Asset Serialization is forced to Text when using Collaboration feature.", MessageType.Warning); - } + CreatePopupMenu("Mode", serializationPopupList, index, SetAssetSerializationMode); if (m_SerializationMode.intValue != (int)SerializationMode.ForceBinary) { @@ -461,7 +452,7 @@ public override void OnInspectorGUI() GUI.enabled = editorEnabled; if (GUILayout.Button(Content.ucbpLearnMore, EditorStyles.linkLabel)) { - var help = Help.FindHelpNamed("AssetBundles-Building"); + var help = Help.FindHelpNamed("Build-MultiProcess"); Application.OpenURL(help); } GUILayout.EndHorizontal(); @@ -561,8 +552,11 @@ public override void OnInspectorGUI() EditorGUILayout.PropertyField(m_UseLegacyProbeSampleCount, Content.useLegacyProbeSampleCount); if (EditorGUI.EndChangeCheck()) { + if (m_IsGlobalSettings) +#pragma warning disable 618 EditorSettings.useLegacyProbeSampleCount = m_UseLegacyProbeSampleCount.boolValue; +#pragma warning restore 618 EditorApplication.RequestRepaintAllViews(); } @@ -821,10 +815,21 @@ private void DoCacheServerSettings() if (address.Length == 2) port = Convert.ToUInt16(address[1]); - if (AssetDatabase.CanConnectToCacheServer(ip, port)) + bool canConnect = AssetDatabase.CanConnectToCacheServer(ip, port); + bool isConnected = AssetDatabase.IsConnectedToCacheServer(); + if (canConnect) m_CacheServerConnectionState = CacheServerConnectionState.Success; else m_CacheServerConnectionState = CacheServerConnectionState.Failure; + + //We have to check if we're out of sync. here. + //If we can connect, but we're not connected, we need to update some UI + //If we CANNOT connect, but we are connected, we are out of sync. too and + //need to update some UI. + //Calling RefreshSettings here fixes that, and this check encapsulates the + //above 2 conditions. + if (canConnect != isConnected) + AssetDatabase.RefreshSettings(); } GUILayout.Space(25); @@ -991,7 +996,6 @@ private void DoEnterPlayModeSettings() EnterPlayModeOptions options = (EnterPlayModeOptions)m_EnterPlayModeOptions.intValue; options = ToggleEnterPlayModeOptions(options, EnterPlayModeOptions.DisableDomainReload, Content.enterPlayModeOptionsEnableDomainReload); options = ToggleEnterPlayModeOptions(options, EnterPlayModeOptions.DisableSceneReload, Content.enterPlayModeOptionsEnableSceneReload); - options = ToggleEnterPlayModeOptions(options, EnterPlayModeOptions.DisableSceneBackupUnlessDirty, Content.enterPlayModeOptionsDisableSceneBackup); if (m_EnterPlayModeOptions.intValue != (int)options) { @@ -1013,23 +1017,10 @@ private void DoEnterInspectorSettings() if (EditorGUI.EndChangeCheck() && m_IsGlobalSettings) { EditorSettings.inspectorUseIMGUIDefaultInspector = m_InspectorUseIMGUIDefaultInspector.boolValue; - - // Needs to be delayCall because it forces redrawing of UI which messes with the current IMGUI context of the Settings window. - EditorApplication.delayCall += ClearEditorsAndRebuildInspectors; + PropertyEditor.ClearAndRebuildAll(); } } - static void ClearEditorsAndRebuildInspectors() - { - // Cannot use something like EditorUtility.ForceRebuildInspectors() because this only refreshes - // the inspector's values and IMGUI state, but otherwise, if the target did not change we - // re-use the Editors. We need a special clear function to properly recreate the UI using - // the new setting. - var propertyEditors = Resources.FindObjectsOfTypeAll(); - foreach (var propertyEditor in propertyEditors) - propertyEditor.ClearEditorsAndRebuild(); - } - static int GetIndexById(DevDevice[] elements, string id, int defaultIndex) { for (int i = 0; i < elements.Length; i++) diff --git a/Editor/Mono/Inspector/Enlighten/LightmapParameters.cs b/Editor/Mono/Inspector/Enlighten/LightmapParameters.cs index 731ff30454..4a9308bde5 100644 --- a/Editor/Mono/Inspector/Enlighten/LightmapParameters.cs +++ b/Editor/Mono/Inspector/Enlighten/LightmapParameters.cs @@ -5,6 +5,7 @@ using UnityEngine; using UnityEditor; using UnityEngine.Rendering; +using System; namespace UnityEditor { @@ -72,14 +73,22 @@ public override void OnInspectorGUI() { ++EditorGUI.indentLevel; - EditorGUILayout.LabelField(Styles.enlightenLabel, EditorStyles.boldLabel); - - ++EditorGUI.indentLevel; - EditorGUILayout.PropertyField(m_Resolution, Styles.resolutionContent); EditorGUILayout.Slider(m_ClusterResolution, 0.1F, 1.0F, Styles.clusterResolutionContent); EditorGUILayout.IntSlider(m_IrradianceBudget, 32, 2048, Styles.irradianceBudgetContent); - EditorGUILayout.IntSlider(m_IrradianceQuality, 512, 131072, Styles.irradianceQualityContent); + if (m_IrradianceQuality.hasMultipleDifferentValues) + { + EditorGUI.BeginChangeCheck(); + EditorGUI.showMixedValue = true; + + int newValue = (int) EditorGUILayout.LogarithmicIntSlider(Styles.irradianceQualityContent, value: m_IrradianceQuality.intValue, leftValue: 512, rightValue: 131072, logbase: 2, 512, 131072); + if (EditorGUI.EndChangeCheck()) + m_IrradianceQuality.intValue = newValue; + + EditorGUI.showMixedValue = false; + } + else + m_IrradianceQuality.intValue = EditorGUILayout.LogarithmicIntSlider(Styles.irradianceQualityContent, value: m_IrradianceQuality.intValue, leftValue: 512, rightValue: 131072, logbase: 2, 512, 131072); EditorGUILayout.Slider(m_ModellingTolerance, 0.0f, 1.0f, Styles.modellingToleranceContent); EditorGUILayout.PropertyField(m_EdgeStitching, Styles.edgeStitchingContent); EditorGUILayout.PropertyField(m_IsTransparent, Styles.isTransparent); @@ -87,27 +96,53 @@ public override void OnInspectorGUI() EditorGUILayout.Space(); --EditorGUI.indentLevel; - --EditorGUI.indentLevel; } } - GUILayout.Label(Styles.bakedGIContent, EditorStyles.boldLabel); + m_BakedGISettings.value = EditorGUILayout.FoldoutTitlebar(m_BakedGISettings.value, Styles.bakedGIContent, true); - EditorGUILayout.PropertyField(m_AntiAliasingSamples, Styles.antiAliasingSamplesContent); - const float minPushOff = 0.0001f; // Keep in sync with PLM_MIN_PUSHOFF - EditorGUILayout.Slider(m_Pushoff, minPushOff, 1.0f, Styles.pushoffContent); - EditorGUILayout.PropertyField(m_BakedLightmapTag, Styles.bakedLightmapTagContent); - m_LimitLightmapCount.boolValue = EditorGUILayout.Toggle(Styles.limitLightmapCount, m_LimitLightmapCount.boolValue); - if (m_LimitLightmapCount.boolValue) + if (m_BakedGISettings.value) { EditorGUI.indentLevel++; - EditorGUILayout.PropertyField(m_LightmapMaxCount, Styles.lightmapMaxCount); + if (m_AntiAliasingSamples.hasMultipleDifferentValues) + { + EditorGUI.BeginChangeCheck(); + EditorGUI.showMixedValue = true; + + int fieldValue = EditorGUILayout.IntPopup(Styles.antiAliasingSamplesContent, m_AntiAliasingSamples.intValue, Styles.antiAliasingSamplesStrings, Styles.antiAliasingSampleValues); + + if (EditorGUI.EndChangeCheck()) + m_AntiAliasingSamples.intValue = fieldValue; + + EditorGUI.showMixedValue = false; + } + + else + { + int fieldValue = EditorGUILayout.IntPopup(Styles.antiAliasingSamplesContent, m_AntiAliasingSamples.intValue, Styles.antiAliasingSamplesStrings, Styles.antiAliasingSampleValues); + m_AntiAliasingSamples.intValue = fieldValue; + } + + const float minPushOff = 0.0001f; // Keep in sync with PLM_MIN_PUSHOFF + EditorGUILayout.Slider(m_Pushoff, minPushOff, 1.0f, Styles.pushoffContent); + EditorGUILayout.PropertyField(m_BakedLightmapTag, Styles.bakedLightmapTagContent); + m_LimitLightmapCount.boolValue = EditorGUILayout.Toggle(Styles.limitLightmapCount, m_LimitLightmapCount.boolValue); + if (m_LimitLightmapCount.boolValue) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(m_LightmapMaxCount, Styles.lightmapMaxCount); + EditorGUI.indentLevel--; + } EditorGUI.indentLevel--; } - EditorGUILayout.Space(); - - GUILayout.Label(Styles.generalGIContent, EditorStyles.boldLabel); - EditorGUILayout.Slider(m_BackFaceTolerance, 0.0f, 1.0f, Styles.backFaceToleranceContent); + + m_GeneralParametersSettings.value = EditorGUILayout.FoldoutTitlebar(m_GeneralParametersSettings.value, Styles.generalGIContent, true); + if (m_GeneralParametersSettings.value) + { + ++EditorGUI.indentLevel; + EditorGUILayout.Slider(m_BackFaceTolerance, 0.0f, 1.0f, Styles.backFaceToleranceContent); + --EditorGUI.indentLevel; + } serializedObject.ApplyModifiedProperties(); } @@ -120,27 +155,31 @@ internal override void OnHeaderControlsGUI() private class Styles { + public static readonly int[] antiAliasingSampleValues = (int[]) Enum.GetValues(typeof(LightmapParameters.AntiAliasingSamples)); + public static readonly GUIContent[] antiAliasingSamplesStrings = + { + EditorGUIUtility.TrTextContent("1"), + EditorGUIUtility.TrTextContent("4"), + EditorGUIUtility.TrTextContent("16") + }; + public static readonly GUIContent generalGIContent = EditorGUIUtility.TrTextContent("General Parameters", "Settings used in both Precomputed Realtime Global Illumination and Baked Global Illumination."); public static readonly GUIContent precomputedRealtimeGIContent = EditorGUIUtility.TrTextContent("Realtime Global Illumination", "Settings used in Precomputed Realtime Global Illumination where it is precomputed how indirect light can bounce between static objects, but the final lighting is done at runtime. Lights, ambient lighting in addition to the materials and emission of static objects can still be changed at runtime. Only static objects can affect GI by blocking and bouncing light, but non-static objects can receive bounced light via light probes."); // Reuse the label from the Lighting window public static readonly GUIContent resolutionContent = EditorGUIUtility.TrTextContent("Resolution", "Realtime lightmap resolution in texels per world unit. This value is multiplied by the realtime resolution in the Lighting window to give the output lightmap resolution. This should generally be an order of magnitude less than what is common for baked lightmaps to keep the precompute time manageable and the performance at runtime acceptable. Note that if this is made more fine-grained, then the Irradiance Budget will often need to be increased too, to fully take advantage of this increased detail."); public static readonly GUIContent clusterResolutionContent = EditorGUIUtility.TrTextContent("Cluster Resolution", "The ratio between the resolution of the clusters with which light bounce is calculated and the resolution of the output lightmaps that sample from these."); public static readonly GUIContent irradianceBudgetContent = EditorGUIUtility.TrTextContent("Irradiance Budget", "The amount of data used by each texel in the output lightmap. Specifies how fine-grained a view of the scene an output texel has. Small values mean more averaged out lighting, since the light contributions from more clusters are treated as one. Affects runtime memory usage and to a lesser degree runtime CPU usage."); public static readonly GUIContent irradianceQualityContent = EditorGUIUtility.TrTextContent("Irradiance Quality", "The number of rays to cast to compute which clusters affect a given output lightmap texel - the granularity of how this is saved is defined by the Irradiance Budget. Affects the speed of the precomputation but has no influence on runtime performance."); - public static readonly GUIContent backFaceToleranceContent = EditorGUIUtility.TrTextContent("Backface Tolerance", "The percentage of rays shot from an output texel that must hit front faces to be considered usable. Allows a texel to be invalidated if too many of the rays cast from it hit back faces (the texel is inside some geometry). In that case artefacts are avoided by cloning valid values from surrounding texels. For example, if backface tolerance is 0.0, the texel is rejected only if it sees nothing but backfaces. If it is 1.0, the ray origin is rejected if it has even one ray that hits a backface."); + public static readonly GUIContent backFaceToleranceContent = EditorGUIUtility.TrTextContent("Backface Tolerance", "Defines the percentage of rays which must hit front-facing geometry for the lightmapper to consider a texel valid. Increasing this number increases the likelihood that texels will be invalidated when backfaces can be seen. Invalid texels will then receive dilation."); public static readonly GUIContent modellingToleranceContent = EditorGUIUtility.TrTextContent("Modelling Tolerance", "Maximum size of gaps that can be ignored for GI."); public static readonly GUIContent edgeStitchingContent = EditorGUIUtility.TrTextContent("Edge Stitching", "If enabled, ensures that UV charts (aka UV islands) in the generated lightmaps blend together where they meet so there is no visible seam between them."); public static readonly GUIContent systemTagContent = EditorGUIUtility.TrTextContent("System Tag", "Systems are groups of objects whose lightmaps are in the same atlas. It is also the granularity at which dependencies are calculated. Multiple systems are created automatically if the scene is big enough, but it can be helpful to be able to split them up manually for e.g. streaming in sections of a level. The system tag lets you force an object into a different realtime system even though all the other parameters are the same."); public static readonly GUIContent bakedGIContent = EditorGUIUtility.TrTextContent("Baked Global Illumination", "Settings used in Baked Global Illumination where direct and indirect lighting for static objects is precalculated and saved (baked) into lightmaps for use at runtime. This is useful when lights are known to be static, for mobile, for low end devices and other situations where there is not enough processing power to use Precomputed Realtime GI. You can toggle on each light whether it should be included in the bake."); // Reuse the label from the Lighting window - public static readonly GUIContent antiAliasingSamplesContent = EditorGUIUtility.TrTextContent("Anti-aliasing Samples", "The maximum number of times to supersample a texel to reduce aliasing. Progressive lightmapper supersamples the positions and normals buffers (part of the G-buffer) and hence the sample count is a multiplier on the amount of memory used for those buffers. Progressive lightmapper clamps the value to the [1;16] range."); + public static readonly GUIContent antiAliasingSamplesContent = EditorGUIUtility.TrTextContent("Anti-aliasing Samples", "How many samples to use when antialiasing lightmap texels. Ray positions and normals buffers are also increased in size by this value. Higher values improve lightmap quality but also multiply memory usage when baking. A value of 1 disables supersampling."); public static readonly GUIContent isTransparent = EditorGUIUtility.TrTextContent("Is Transparent", "If enabled, the object appears transparent during GlobalIllumination lighting calculations. Backfaces are not contributing to and light travels through the surface. This is useful for emissive invisible surfaces."); public static readonly GUIContent pushoffContent = EditorGUIUtility.TrTextContent("Pushoff", "The amount to push off geometry for ray tracing, in modelling units. It is applied to all baked light maps, so it will affect direct light, indirect light and AO. Useful for getting rid of unwanted AO or shadowing."); public static readonly GUIContent bakedLightmapTagContent = EditorGUIUtility.TrTextContent("Baked Tag", "An integer that lets you force an object into a different baked lightmap even though all the other parameters are the same. This can be useful e.g. when streaming in sections of a level."); public static readonly GUIContent limitLightmapCount = EditorGUIUtility.TrTextContent("Limit Lightmap Count", "If enabled, objects with the same baked GI settings will be packed into a specified number of lightmaps. This may reduce the objects' lightmap resolution."); public static readonly GUIContent lightmapMaxCount = EditorGUIUtility.TrTextContent("Max Lightmaps", "The maximum number of lightmaps into which objects will be packed."); - - public static readonly GUIContent generalLabel = EditorGUIUtility.TrTextContent("General"); - public static readonly GUIContent progressiveLabel = EditorGUIUtility.TrTextContent("Progressive Lightmapper"); - public static readonly GUIContent enlightenLabel = EditorGUIUtility.TrTextContent("Enlighten"); } } } diff --git a/Editor/Mono/Inspector/GameObjectInspector.cs b/Editor/Mono/Inspector/GameObjectInspector.cs index ebc604cda1..d812786e48 100644 --- a/Editor/Mono/Inspector/GameObjectInspector.cs +++ b/Editor/Mono/Inspector/GameObjectInspector.cs @@ -35,7 +35,7 @@ static class Styles public static GUIContent layerContent = EditorGUIUtility.TrTextContent("Layer", "The layer that this GameObject is in.\n\nChoose Add Layer... to edit the list of available layers."); public static GUIContent tagContent = EditorGUIUtility.TrTextContent("Tag", "The tag that this GameObject has.\n\nChoose Untagged to remove the current tag.\n\nChoose Add Tag... to edit the list of available tags."); public static GUIContent staticPreviewContent = EditorGUIUtility.TrTextContent("Static Preview", "This asset is greater than 8MB so, by default, the Asset Preview displays a static preview.\nTo view the asset interactively, click the Asset Preview."); - + public static float tagFieldWidth = EditorGUI.CalcPrefixLabelWidth(Styles.tagContent, EditorStyles.boldLabel); public static float layerFieldWidth = EditorGUI.CalcPrefixLabelWidth(Styles.layerContent, EditorStyles.boldLabel); @@ -1400,15 +1400,24 @@ internal void OnSceneDragInternal(SceneView sceneView, int index, EventType type internal static void DragPerform(SceneView sceneView, GameObject draggedObject, GameObject go) { - var defaultParentObject = SceneView.GetDefaultParentObjectIfSet(); + Transform defaultParentObject = SceneView.GetDefaultParentObjectIfSet(); var parent = defaultParentObject != null ? defaultParentObject : sceneView.customParentForDraggedObjects; - string uniqueName = GameObjectUtility.GetUniqueNameForSibling(parent, draggedObject.name); + if (parent != null) - draggedObject.transform.parent = parent; + { + draggedObject.transform.SetParent(parent, true); + } + + if (defaultParentObject == null && sceneView.customParentForDraggedObjects == null) + { + draggedObject.transform.SetAsLastSibling(); + } + draggedObject.hideFlags = 0; Undo.RegisterCreatedObjectUndo(draggedObject, "Place " + draggedObject.name); DragAndDrop.AcceptDrag(); + if (s_ShouldClearSelection) { Selection.objects = new[] { draggedObject }; @@ -1420,11 +1429,15 @@ internal static void DragPerform(SceneView sceneView, GameObject draggedObject, // selection to all of them by joining them to the previous selection list Selection.objects = Selection.gameObjects.Union(new[] { draggedObject }).ToArray(); } + HandleUtility.ignoreRaySnapObjects = null; + if (SceneView.mouseOverWindow != null) SceneView.mouseOverWindow.Focus(); + if (!Application.IsPlaying(draggedObject)) draggedObject.name = uniqueName; + s_CyclicNestingDetected = false; } diff --git a/Editor/Mono/Inspector/GenericInspector.cs b/Editor/Mono/Inspector/GenericInspector.cs index 1f3dac789e..e7a3b19e7f 100644 --- a/Editor/Mono/Inspector/GenericInspector.cs +++ b/Editor/Mono/Inspector/GenericInspector.cs @@ -4,6 +4,7 @@ using System.Linq; using UnityEngine; +using UnityEngine.UIElements; namespace UnityEditor { @@ -68,11 +69,19 @@ internal override bool GetOptimizedGUIBlock(bool isDirty, bool isVisible, out fl // Update serialized object representation if (m_SerializedObject == null) - m_SerializedObject = new SerializedObject(targets, m_Context) { inspectorMode = inspectorMode }; + { + m_SerializedObject = new SerializedObject(targets, m_Context) + { + inspectorMode = inspectorMode, + inspectorDataMode = dataMode + }; + } else { m_SerializedObject.Update(); m_SerializedObject.inspectorMode = inspectorMode; + if (m_SerializedObject.inspectorDataMode != dataMode) + m_SerializedObject.inspectorDataMode = dataMode; } height = 0; @@ -373,5 +382,22 @@ public override void OnInspectorGUI() base.OnInspectorGUI(); } + + public override VisualElement CreateInspectorGUI() + { + if (serializedObject == null) + return null; + + var root = new VisualElement(); + + if (MissingSerializeReference(target)) + { + root.Add(new HelpBox(GetMissingSerializeRefererenceMessageContainer(), HelpBoxMessageType.Warning)); + } + + UIElements.InspectorElement.FillDefaultInspector(root, serializedObject, this); + + return root; + } } } diff --git a/Editor/Mono/Inspector/GraphicsSettingsInspector.cs b/Editor/Mono/Inspector/GraphicsSettingsInspector.cs index a00d445e82..7ae5a88729 100644 --- a/Editor/Mono/Inspector/GraphicsSettingsInspector.cs +++ b/Editor/Mono/Inspector/GraphicsSettingsInspector.cs @@ -2,231 +2,209 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using System; +using System.Collections.Generic; using System.Linq; +using System.Reflection; using UnityEditorInternal; -using UnityEngine; -using UnityEngine.Events; using UnityEngine.Rendering; -using AlwaysIncludedShadersEditor = UnityEditor.GraphicsSettingsWindow.AlwaysIncludedShadersEditor; -using BuiltinShadersEditor = UnityEditor.GraphicsSettingsWindow.BuiltinShadersEditor; -using Object = UnityEngine.Object; -using ShaderPreloadEditor = UnityEditor.GraphicsSettingsWindow.ShaderPreloadEditor; -using ShaderStrippingEditor = UnityEditor.GraphicsSettingsWindow.ShaderStrippingEditor; -using TierSettingsEditor = UnityEditor.GraphicsSettingsWindow.TierSettingsEditor; -using VideoShadersEditor = UnityEditor.GraphicsSettingsWindow.VideoShadersEditor; +using UnityEngine.UIElements; namespace UnityEditor { - [CustomEditor(typeof(UnityEngine.Rendering.GraphicsSettings))] + [CustomEditor(typeof(GraphicsSettings))] internal class GraphicsSettingsInspector : ProjectSettingsBaseEditor { - internal class Styles - { - public static readonly GUIContent showEditorWindow = EditorGUIUtility.TrTextContent("Open Editor..."); - public static readonly GUIContent closeEditorWindow = EditorGUIUtility.TrTextContent("Close Editor"); - public static readonly GUIContent tierSettings = EditorGUIUtility.TrTextContent("Tier Settings"); - public static readonly GUIContent builtinSettings = EditorGUIUtility.TrTextContent("Built-in Shader Settings"); - public static readonly GUIContent shaderStrippingSettings = EditorGUIUtility.TrTextContent("Shader Stripping"); - public static readonly GUIContent shaderPreloadSettings = EditorGUIUtility.TrTextContent("Shader Loading"); - public static readonly GUIContent logWhenShaderIsCompiled = EditorGUIUtility.TrTextContent("Log Shader Compilation", "When enabled, the player will print shader information each time a shader is being compiled (development and debug mode only)."); - public static readonly GUIContent lightProbeOutsideHullStrategy = EditorGUIUtility.TrTextContent("Renderer Light Probe Selection", "Finding the Light Probes closest to a Renderer positioned outside of the tetrahedral Light Probe hull can be very expensive in terms of CPU cycles. Use this option to configure if Unity should spend time searching the hull to find the closest probe, or if it should use the global Ambient Probe instead."); - public static readonly int[] lightProbeOutsideHullStrategyValues = - { - (int)LightProbeOutsideHullStrategy.kLightProbeSearchTetrahedralHull, - (int)LightProbeOutsideHullStrategy.kLightProbeUseAmbientProbe - }; - public static readonly GUIContent[] lightProbeOutsideHullStrategyStrings = - { - EditorGUIUtility.TrTextContent("Find closest Light Probe"), - EditorGUIUtility.TrTextContent("Use Ambient Probe"), - }; - public static readonly GUIContent cameraSettings = EditorGUIUtility.TrTextContent("Camera Settings"); - public static readonly GUIContent renderPipeSettings = EditorGUIUtility.TrTextContent("Scriptable Render Pipeline Settings", "This defines the default render pipeline, which Unity uses when there is no override for a given quality level."); - public static readonly GUIContent renderPipeLabel = EditorGUIUtility.TrTextContent("Scriptable Render Pipeline"); + internal class ResourcesPaths + { + internal const string graphicsSettings = "StyleSheets/ProjectSettings/GraphicsSettings.uss"; + internal const string bodyTemplate = "UXML/ProjectSettings/GraphicsSettingsEditor.uxml"; } - Editor m_TierSettingsEditor; - Editor m_BuiltinShadersEditor; - Editor m_VideoShadersEditor; - Editor m_AlwaysIncludedShadersEditor; - Editor m_ShaderStrippingEditor; - Editor m_ShaderPreloadEditor; - SerializedProperty m_TransparencySortMode; - SerializedProperty m_TransparencySortAxis; - SerializedProperty m_ScriptableRenderLoop; - SerializedProperty m_LogWhenShaderIsCompiled; - SerializedProperty m_LightProbeOutsideHullStrategy; + readonly VisibilityControllerBasedOnRenderPipeline m_VisibilityController = new(); - Object graphicsSettings + internal void CreateInspectorUI(VisualElement root) { - get { return UnityEngine.Rendering.GraphicsSettings.GetGraphicsSettings(); } + root.Add(ObjectUpdater()); + root.Add(CreateGUI()); } - Editor tierSettingsEditor + // As we use multiple IMGUI container while porting everything to UITK we will call serializedObject.Update in first separate IMGUI container. + // This way we don't need to do it in each following containers. + VisualElement ObjectUpdater() { - get + return new IMGUIContainer(() => { - Editor.CreateCachedEditor(graphicsSettings, typeof(TierSettingsEditor), ref m_TierSettingsEditor); - ((TierSettingsEditor)m_TierSettingsEditor).verticalLayout = true; - return m_TierSettingsEditor; - } - } - Editor builtinShadersEditor - { - get { Editor.CreateCachedEditor(graphicsSettings, typeof(BuiltinShadersEditor), ref m_BuiltinShadersEditor); return m_BuiltinShadersEditor; } - } - Editor videoShadersEditor - { - get { Editor.CreateCachedEditor(graphicsSettings, typeof(VideoShadersEditor), ref m_VideoShadersEditor); return m_VideoShadersEditor; } + m_VisibilityController.Update(); + serializedObject.Update(); + }); } - Editor alwaysIncludedShadersEditor - { - get { Editor.CreateCachedEditor(graphicsSettings, typeof(AlwaysIncludedShadersEditor), ref m_AlwaysIncludedShadersEditor); return m_AlwaysIncludedShadersEditor; } - } - Editor shaderStrippingEditor + + VisualElement CreateGUI() { - get { Editor.CreateCachedEditor(graphicsSettings, typeof(ShaderStrippingEditor), ref m_ShaderStrippingEditor); return m_ShaderStrippingEditor; } + var visualTreeAsset = EditorGUIUtility.Load(ResourcesPaths.bodyTemplate) as VisualTreeAsset; + var template = visualTreeAsset.Instantiate(); + template + .Query() + .ForEach(d => d.Initialize(serializedObject)); + + m_VisibilityController.RegisterVisualElementTree(template); + return template; } - Editor shaderPreloadEditor + + void OnEnable() { - get { Editor.CreateCachedEditor(graphicsSettings, typeof(ShaderPreloadEditor), ref m_ShaderPreloadEditor); return m_ShaderPreloadEditor; } + m_VisibilityController.Initialize(); } - public void OnEnable() + void OnDisable() { - m_TransparencySortMode = serializedObject.FindProperty("m_TransparencySortMode"); - m_TransparencySortAxis = serializedObject.FindProperty("m_TransparencySortAxis"); - m_ScriptableRenderLoop = serializedObject.FindProperty("m_CustomRenderPipeline"); - m_LogWhenShaderIsCompiled = serializedObject.FindProperty("m_LogWhenShaderIsCompiled"); - m_LightProbeOutsideHullStrategy = serializedObject.FindProperty("m_LightProbeOutsideHullStrategy"); - tierSettingsAnimator = new AnimatedValues.AnimBool(showTierSettingsUI, Repaint); + m_VisibilityController.Clear(); + m_VisibilityController.Dispose(); } + } + + /// + /// Control visibility of UI elements depends on active Render Pipeline. + /// For one it stays specific for GraphicsSettings as it requires a way to determine BuiltinOnly elements and there is no generic way to do it. + /// + internal class VisibilityControllerBasedOnRenderPipeline : IDisposable + { + readonly List> m_TrackedElements = new(); + + RenderPipelineAsset m_PreviousAsset; - private void HandleEditorWindowButton() + public void Initialize() { - TierSettingsWindow window = TierSettingsWindow.GetInstance(); - GUIContent text = window == null ? Styles.showEditorWindow : Styles.closeEditorWindow; - if (GUILayout.Button(text, EditorStyles.miniButton, GUILayout.Width(110))) - { - if (window) - { - window.Close(); - } - else - { - TierSettingsWindow.CreateWindow(); - TierSettingsWindow.GetInstance().Show(); - } - } + RenderPipelineManager.activeRenderPipelineAssetChanged += RenderPipelineAssetChanged; } - // this is category animation is blatantly copied from PlayerSettingsEditor.cs - private bool showTierSettingsUI = true; // show by default, as otherwise users are confused - private UnityEditor.AnimatedValues.AnimBool tierSettingsAnimator = null; - - private void TierSettingsGUI() + public bool RegisterVisualElement(GraphicsSettingsElement element) { - bool enabled = GUI.enabled; - GUI.enabled = true; // we don't want to disable the expand behavior - EditorGUILayout.BeginVertical(EditorStyles.helpBox, GUILayout.Height(20)); + if (m_TrackedElements.Any(t => t.Item1 == element)) + return false; - EditorGUILayout.BeginHorizontal(); - Rect r = GUILayoutUtility.GetRect(20, 21); r.x += 3; r.width += 6; - showTierSettingsUI = EditorGUI.FoldoutTitlebar(r, Styles.tierSettings, showTierSettingsUI, true, EditorStyles.inspectorTitlebarFlat, EditorStyles.inspectorTitlebarText); - HandleEditorWindowButton(); - EditorGUILayout.EndHorizontal(); + var type = element.GetType(); + var attribute = type.GetCustomAttribute(false); - tierSettingsAnimator.target = showTierSettingsUI; - GUI.enabled = enabled; + UpdateElementVisibility (element, attribute); + m_TrackedElements.Add(new ValueTuple(element, attribute)); - if (EditorGUILayout.BeginFadeGroup(tierSettingsAnimator.faded) && TierSettingsWindow.GetInstance() == null) - tierSettingsEditor.OnInspectorGUI(); - EditorGUILayout.EndFadeGroup(); - EditorGUILayout.EndVertical(); + return true; } - public override void OnInspectorGUI() + public void RegisterVisualElementTree(VisualElement element) { - serializedObject.Update(); - - GUILayout.Label(Styles.renderPipeSettings, EditorStyles.boldLabel); - EditorGUI.RenderPipelineAssetField(serializedObject, m_ScriptableRenderLoop); - EditorGUILayout.Space(); + element.Query().ForEach(RegisterVisualElement); + } - bool usingSRP = GraphicsSettings.currentRenderPipeline != null; - if (usingSRP) - EditorGUILayout.HelpBox("A Scriptable Render Pipeline is in use, some settings will not be used and are hidden", MessageType.Info); + public bool UnregisterVisualElement(GraphicsSettingsElement element) + { + var type = element.GetType(); + var attribute = type.GetCustomAttribute(false); + if (attribute == null) + return false; - if (!usingSRP) - { - GUILayout.Label(Styles.cameraSettings, EditorStyles.boldLabel); - EditorGUILayout.PropertyField(m_TransparencySortMode); - if ((TransparencySortMode) m_TransparencySortMode.intValue == TransparencySortMode.CustomAxis) - EditorGUILayout.PropertyField(m_TransparencySortAxis); + var index = m_TrackedElements.FindIndex(t => element == t.Item1); + if (index < 0) + return false; - EditorGUILayout.Space(); - } + m_TrackedElements.RemoveAt(index); + return true; + } - float labelWidth = EditorGUIUtility.labelWidth; + public void Update() + { + if (GraphicsSettings.currentRenderPipeline == m_PreviousAsset) + return; - // Hide tier settings for SRPs and close tier settings window if open - if (usingSRP) - { - TierSettingsWindow window = TierSettingsWindow.GetInstance(); - if (window != null) - window.Close(); - } - else + for (var i = 0; i < m_TrackedElements.Count; i++) { - TierSettingsGUI(); + var trackedElement = m_TrackedElements[i]; + UpdateElementVisibility(trackedElement.Item1, trackedElement.Item2); } - EditorGUIUtility.labelWidth = labelWidth; + m_PreviousAsset = GraphicsSettings.currentRenderPipeline; + } - GUILayout.Label(Styles.builtinSettings, EditorStyles.boldLabel); - if (!usingSRP) - builtinShadersEditor.OnInspectorGUI(); - videoShadersEditor.OnInspectorGUI(); - alwaysIncludedShadersEditor.OnInspectorGUI(); + void RenderPipelineAssetChanged(RenderPipelineAsset previous, RenderPipelineAsset next) + { + Update(); + } - EditorGUILayout.Space(); - GUILayout.Label(Styles.shaderStrippingSettings, EditorStyles.boldLabel); - shaderStrippingEditor.OnInspectorGUI(); + bool ShouldDisplayElement(GraphicsSettingsElement element, SupportedOnRenderPipelineAttribute attribute) + { + if (attribute is { isSupportedOnCurrentPipeline: true }) + return true; + return element.BuiltinOnly && !GraphicsSettings.isScriptableRenderPipelineEnabled || !element.BuiltinOnly; + } + + void UpdateElementVisibility(GraphicsSettingsElement element, SupportedOnRenderPipelineAttribute attribute) + { + if (ShouldDisplayElement(element, attribute)) + Show(element); + else + Hide(element); + } - EditorGUILayout.Space(); - GUILayout.Label(Styles.shaderPreloadSettings, EditorStyles.boldLabel); - EditorGUILayout.PropertyField(m_LogWhenShaderIsCompiled, Styles.logWhenShaderIsCompiled); - EditorGUILayout.IntPopup(m_LightProbeOutsideHullStrategy, Styles.lightProbeOutsideHullStrategyStrings, Styles.lightProbeOutsideHullStrategyValues, Styles.lightProbeOutsideHullStrategy); + void Show(GraphicsSettingsElement element) + { + element.style.display = DisplayStyle.Flex; + } - shaderPreloadEditor.OnInspectorGUI(); + void Hide(GraphicsSettingsElement element) + { + element.style.display = DisplayStyle.None; + } - serializedObject.ApplyModifiedProperties(); + public void Clear() + { + m_TrackedElements.Clear(); } - public void SetSectionOpenListener(UnityAction action) + public void Dispose() { - tierSettingsAnimator.valueChanged.RemoveAllListeners(); - tierSettingsAnimator.valueChanged.AddListener(action); + RenderPipelineManager.activeRenderPipelineAssetChanged -= RenderPipelineAssetChanged; } + } + internal class GraphicsSettingsProvider : SettingsProvider + { [SettingsProvider] - static SettingsProvider CreateProjectSettingsProvider() + public static SettingsProvider CreateUserSettingsProvider() { - var provider = AssetSettingsProvider.CreateProviderFromAssetPath("Project/Graphics", "ProjectSettings/GraphicsSettings.asset"); - provider.keywords = SettingsProvider.GetSearchKeywordsFromGUIContentProperties() - .Concat(SettingsProvider.GetSearchKeywordsFromGUIContentProperties()) - .Concat(SettingsProvider.GetSearchKeywordsFromGUIContentProperties()) - .Concat(SettingsProvider.GetSearchKeywordsFromGUIContentProperties()) - .Concat(SettingsProvider.GetSearchKeywordsFromGUIContentProperties()) - .Concat(SettingsProvider.GetSearchKeywordsFromPath("ProjectSettings/GraphicsSettings.asset")); - - provider.activateHandler = (searchContext, rootElement) => + var graphicsSettingsProvider = new GraphicsSettingsProvider("Project/Graphics", SettingsScope.Project) { - (provider.settingsEditor as GraphicsSettingsInspector)?.SetSectionOpenListener(provider.Repaint); + keywords = GetSearchKeywordsFromGUIContentProperties() + .Concat(GetSearchKeywordsFromGUIContentProperties()) + .Concat(GetSearchKeywordsFromGUIContentProperties()) + .Concat(GetSearchKeywordsFromGUIContentProperties()) + .Concat(GetSearchKeywordsFromGUIContentProperties()) + .Concat(GetSearchKeywordsFromGUIContentProperties()) + .Concat(GetSearchKeywordsFromGUIContentProperties()) + .Concat(GetSearchKeywordsFromGUIContentProperties()) + .Concat(GetSearchKeywordsFromGUIContentProperties()) + .Concat(GetSearchKeywordsFromGUIContentProperties()) + .Concat(GetSearchKeywordsFromGUIContentProperties()) + .Concat(GetSearchKeywordsFromGUIContentProperties()) + .Concat(GetSearchKeywordsFromPath("ProjectSettings/GraphicsSettings.asset")), + icon = EditorGUIUtility.FindTexture("UnityEngine/UI/GraphicRaycaster Icon") }; + return graphicsSettingsProvider; + } + + internal GraphicsSettingsProvider(string path, SettingsScope scopes, IEnumerable keywords = null) : base(path, scopes, keywords) + { + activateHandler = (text, element) => + { + var settingsObj = AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/GraphicsSettings.asset"); + if (settingsObj == null) + return; - provider.icon = EditorGUIUtility.FindTexture("UnityEngine/UI/GraphicRaycaster Icon"); - return provider; + var editor = Editor.CreateEditor(settingsObj) as GraphicsSettingsInspector; + element.styleSheets.Add(EditorGUIUtility.Load(GraphicsSettingsInspector.ResourcesPaths.graphicsSettings) as StyleSheet); + editor.CreateInspectorUI(element); + }; } } } diff --git a/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsElement.cs b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsElement.cs new file mode 100644 index 0000000000..221322c722 --- /dev/null +++ b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsElement.cs @@ -0,0 +1,35 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using UnityEditor.Experimental; +using UnityEngine; +using UnityEngine.UIElements; + +namespace UnityEditor +{ + internal abstract class GraphicsSettingsElement : VisualElement + { + protected SerializedObject m_SerializedObject; + protected SettingsWindow m_SettingsWindow; + + protected Color HighlightSelectionColor = EditorResources.GetStyle("sb-settings-panel-client-area").GetColor("-unity-search-highlight-selection-color"); + protected Color HighlightColor = EditorResources.GetStyle("sb-settings-panel-client-area").GetColor("-unity-search-highlight-color"); + + //We rely on SupportedOn attribute for the cases when we need to show element for SRP. + //Here is a way to specify when we want to have element visible for BuiltinOnly. + //Important notice: we check first for SupportedOn first, then for this backup field. + public virtual bool BuiltinOnly => false; + + internal void Initialize(SerializedObject serializedObject) + { + m_SerializedObject = serializedObject; + + m_SettingsWindow = EditorWindow.GetWindow(); + + Initialize(); + } + + protected abstract void Initialize(); + } +} diff --git a/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorAlwaysIncludedShader.cs b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorAlwaysIncludedShader.cs new file mode 100644 index 0000000000..fb79baf390 --- /dev/null +++ b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorAlwaysIncludedShader.cs @@ -0,0 +1,34 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using UnityEngine.UIElements; + +namespace UnityEditor +{ + internal class GraphicsSettingsInspectorAlwaysIncludedShader : GraphicsSettingsElement + { + public new class UxmlFactory : UxmlFactory { } + + SerializedProperty m_AlwaysIncludedShaders; + + protected override void Initialize() + { + m_AlwaysIncludedShaders = m_SerializedObject.FindProperty("m_AlwaysIncludedShaders"); + m_AlwaysIncludedShaders.isExpanded = true; + + Add(new IMGUIContainer(Draw)); + } + + void Draw() + { + using var highlightScope = new EditorGUI.LabelHighlightScope(m_SettingsWindow.GetSearchText(), HighlightSelectionColor, HighlightColor); + using var check = new EditorGUI.ChangeCheckScope(); + using var settingsScope = new LabelWidthScope(); + using var wideScreenScope = new WideScreenScope(this); + EditorGUILayout.PropertyField(m_AlwaysIncludedShaders, true); + if (check.changed) + m_SerializedObject.ApplyModifiedProperties(); + } + } +} diff --git a/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorBuiltinShaderLabel.cs b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorBuiltinShaderLabel.cs new file mode 100644 index 0000000000..e106e8b1a6 --- /dev/null +++ b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorBuiltinShaderLabel.cs @@ -0,0 +1,23 @@ +// 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.UIElements; + +namespace UnityEditor; + +internal class GraphicsSettingsInspectorBuiltinShaderLabel : GraphicsSettingsElement +{ + public new class UxmlFactory : UxmlFactory { } + + internal class Styles + { + public static readonly GUIContent builtinSettings = EditorGUIUtility.TrTextContent("Built-in Shader Settings"); + } + + protected override void Initialize() + { + Add(new Label(Styles.builtinSettings.text)); + } +} diff --git a/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorBuiltinShaders.cs b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorBuiltinShaders.cs new file mode 100644 index 0000000000..16130f2300 --- /dev/null +++ b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorBuiltinShaders.cs @@ -0,0 +1,106 @@ +// 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.Rendering; +using UnityEngine.UIElements; + +namespace UnityEditor +{ + internal class GraphicsSettingsInspectorBuiltinShaders : GraphicsSettingsElement + { + public new class UxmlFactory : UxmlFactory { } + internal class Styles + { + public static readonly GUIContent deferredString = EditorGUIUtility.TrTextContent("Deferred", "Shader used for Deferred Shading."); + public static readonly GUIContent deferredReflString = EditorGUIUtility.TrTextContent("Deferred Reflections", "Shader used for Deferred reflection probes."); + public static readonly GUIContent screenShadowsString = EditorGUIUtility.TrTextContent("Screen Space Shadows", "Shader used for screen-space cascaded shadows."); + public static readonly GUIContent depthNormalsString = EditorGUIUtility.TrTextContent("Depth Normals", "Shader used for depth and normals texture when enabled on a Camera."); + + public static readonly GUIContent motionVectorsString = + EditorGUIUtility.TrTextContent("Motion Vectors", "Shader for generation of Motion Vectors when the rendering camera has renderMotionVectors set to true."); + + public static readonly GUIContent lightHaloString = EditorGUIUtility.TrTextContent("Light Halo", "Default Shader used for light halos."); + public static readonly GUIContent lensFlareString = EditorGUIUtility.TrTextContent("Lens Flare", "Default Shader used for lens flares."); + } + + public override bool BuiltinOnly => true; + + BuiltinShaderSettings m_Deferred; + BuiltinShaderSettings m_DeferredReflections; + BuiltinShaderSettings m_ScreenSpaceShadows; + BuiltinShaderSettings m_DepthNormals; + BuiltinShaderSettings m_MotionVectors; + BuiltinShaderSettings m_LightHalo; + BuiltinShaderSettings m_LensFlare; + + protected override void Initialize() + { + m_Deferred = new BuiltinShaderSettings(Styles.deferredString, "m_Deferred", m_SerializedObject); + m_DeferredReflections = new BuiltinShaderSettings(Styles.deferredReflString, "m_DeferredReflections", m_SerializedObject); + m_ScreenSpaceShadows = new BuiltinShaderSettings(Styles.screenShadowsString, "m_ScreenSpaceShadows", m_SerializedObject); + m_DepthNormals = new BuiltinShaderSettings(Styles.depthNormalsString, "m_DepthNormals", m_SerializedObject); + m_MotionVectors = new BuiltinShaderSettings(Styles.motionVectorsString, "m_MotionVectors", m_SerializedObject); + m_LightHalo = new BuiltinShaderSettings(Styles.lightHaloString, "m_LightHalo", m_SerializedObject); + m_LensFlare = new BuiltinShaderSettings(Styles.lensFlareString, "m_LensFlare", m_SerializedObject); + + Add(new IMGUIContainer(Draw)); + } + + void Draw() + { + using var highlightScope = new EditorGUI.LabelHighlightScope(m_SettingsWindow.GetSearchText(), HighlightSelectionColor, HighlightColor); + using var check = new EditorGUI.ChangeCheckScope(); + using var settingsScope = new LabelWidthScope(); + using var wideScreenScope = new WideScreenScope(this); + m_Deferred.DoGUI(); + + // deferred reflections being off affects forward vs deferred style probe rendering; + // need to reload shaders for new platform macro to live update + using (var internalCheck = new EditorGUI.ChangeCheckScope()) + { + m_DeferredReflections.DoGUI(); + if (internalCheck.changed) + ShaderUtil.ReloadAllShaders(); + } + + m_ScreenSpaceShadows.DoGUI(); + m_DepthNormals.DoGUI(); + m_MotionVectors.DoGUI(); + m_LightHalo.DoGUI(); + m_LensFlare.DoGUI(); + + if (check.changed) + m_SerializedObject.ApplyModifiedProperties(); + } + + internal readonly struct BuiltinShaderSettings + { + internal enum BuiltinShaderMode + { + None = 0, + Builtin, + Custom + } + + readonly SerializedProperty m_Mode; + readonly SerializedProperty m_Shader; + readonly GUIContent m_Label; + + internal BuiltinShaderSettings(GUIContent label, string name, SerializedObject serializedObject) + { + m_Mode = serializedObject.FindProperty(name + ".m_Mode"); + m_Shader = serializedObject.FindProperty(name + ".m_Shader"); + m_Label = label; + } + + internal void DoGUI() + { + EditorGUILayout.PropertyField(m_Mode, m_Label); + if (m_Mode.intValue == (int)BuiltinShaderMode.Custom) + EditorGUILayout.PropertyField(m_Shader); + } + } + } +} diff --git a/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorCameraSettings.cs b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorCameraSettings.cs new file mode 100644 index 0000000000..01d0e38c89 --- /dev/null +++ b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorCameraSettings.cs @@ -0,0 +1,48 @@ +// 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.Rendering; +using UnityEngine.UIElements; + +namespace UnityEditor +{ + internal class GraphicsSettingsInspectorCameraSettings : GraphicsSettingsElement + { + public new class UxmlFactory : UxmlFactory { } + + internal class Styles + { + public static readonly GUIContent cameraSettings = EditorGUIUtility.TrTextContent("Camera Settings"); + } + + public override bool BuiltinOnly => true; + + SerializedProperty m_TransparencySortMode; + SerializedProperty m_TransparencySortAxis; + + protected override void Initialize() + { + m_TransparencySortMode = m_SerializedObject.FindProperty("m_TransparencySortMode"); + m_TransparencySortAxis = m_SerializedObject.FindProperty("m_TransparencySortAxis"); + + Add(new IMGUIContainer(Draw)); + } + + void Draw() + { + using var highlightScope = new EditorGUI.LabelHighlightScope(m_SettingsWindow.GetSearchText(), HighlightSelectionColor, HighlightColor); + using var settingsScope = new LabelWidthScope(); + using var wideScreenScope = new WideScreenScope(this); + GUILayout.Label(Styles.cameraSettings, EditorStyles.boldLabel); + + using var changeScope = new EditorGUI.ChangeCheckScope(); + EditorGUILayout.PropertyField(m_TransparencySortMode); + if ((TransparencySortMode)m_TransparencySortMode.intValue == TransparencySortMode.CustomAxis) + EditorGUILayout.PropertyField(m_TransparencySortAxis); + if (changeScope.changed) + m_SerializedObject.ApplyModifiedProperties(); + } + } +} diff --git a/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorCullingSettings.cs b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorCullingSettings.cs new file mode 100644 index 0000000000..640507d05a --- /dev/null +++ b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorCullingSettings.cs @@ -0,0 +1,56 @@ +// 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.UIElements; + +namespace UnityEditor +{ + internal class GraphicsSettingsInspectorCullingSettings : GraphicsSettingsElement + { + public new class UxmlFactory : UxmlFactory + { + } + + internal class Styles + { + public static readonly GUIContent cullingSettings = EditorGUIUtility.TrTextContent("Culling Settings"); + public static readonly GUIContent cameraRelativeSettings = EditorGUIUtility.TrTextContent("Camera-Relative Culling"); + + public static readonly GUIContent cameraRelativeLightCulling = EditorGUIUtility.TrTextContent("Lights", + "When enabled, Unity uses the camera position as the reference point for culling lights instead of the world space origin."); + + public static readonly GUIContent cameraRelativeShadowCulling = EditorGUIUtility.TrTextContent("Shadows", + "When enabled, Unity uses the camera position as the reference point for culling shadows instead of the world space origin."); + + } + + SerializedProperty m_CameraRelativeLightCulling; + SerializedProperty m_CameraRelativeShadowCulling; + + protected override void Initialize() + { + m_CameraRelativeLightCulling = m_SerializedObject.FindProperty("m_CameraRelativeLightCulling"); + m_CameraRelativeShadowCulling = m_SerializedObject.FindProperty("m_CameraRelativeShadowCulling"); + + Add(new IMGUIContainer(Draw)); + } + + void Draw() + { + using var highlightScope = new EditorGUI.LabelHighlightScope(m_SettingsWindow.GetSearchText(), HighlightSelectionColor, HighlightColor); + using var check = new EditorGUI.ChangeCheckScope(); + using var settingsScope = new LabelWidthScope(); + using var wideScreenScope = new WideScreenScope(this); + GUILayout.Label(Styles.cullingSettings, EditorStyles.boldLabel); + EditorGUILayout.LabelField(Styles.cameraRelativeSettings, EditorStyles.label); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(m_CameraRelativeLightCulling, Styles.cameraRelativeLightCulling); + EditorGUILayout.PropertyField(m_CameraRelativeShadowCulling, Styles.cameraRelativeShadowCulling); + EditorGUI.indentLevel--; + if (check.changed) + m_SerializedObject.ApplyModifiedProperties(); + } + } +} diff --git a/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorLightProbe.cs b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorLightProbe.cs new file mode 100644 index 0000000000..d684fad906 --- /dev/null +++ b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorLightProbe.cs @@ -0,0 +1,53 @@ +// 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.Rendering; +using UnityEngine.UIElements; + +namespace UnityEditor +{ + internal class GraphicsSettingsInspectorLightProbe : GraphicsSettingsElement + { + public new class UxmlFactory : UxmlFactory { } + + internal class Styles + { + public static readonly GUIContent lightProbeOutsideHullStrategy = EditorGUIUtility.TrTextContent("Renderer Light Probe Selection", + "Finding the Light Probes closest to a Renderer positioned outside of the tetrahedral Light Probe hull can be very expensive in terms of CPU cycles. Use this option to configure if Unity should spend time searching the hull to find the closest probe, or if it should use the global Ambient Probe instead."); + + public static readonly int[] lightProbeOutsideHullStrategyValues = + { + (int)LightProbeOutsideHullStrategy.kLightProbeSearchTetrahedralHull, + (int)LightProbeOutsideHullStrategy.kLightProbeUseAmbientProbe + }; + + public static readonly GUIContent[] lightProbeOutsideHullStrategyStrings = + { + EditorGUIUtility.TrTextContent("Find closest Light Probe"), + EditorGUIUtility.TrTextContent("Use Ambient Probe"), + }; + } + + SerializedProperty m_LightProbeOutsideHullStrategy; + + protected override void Initialize() + { + m_LightProbeOutsideHullStrategy = m_SerializedObject.FindProperty("m_LightProbeOutsideHullStrategy"); + + Add(new IMGUIContainer(Draw)); + } + + void Draw() + { + using var highlightScope = new EditorGUI.LabelHighlightScope(m_SettingsWindow.GetSearchText(), HighlightSelectionColor, HighlightColor); + using var changeScope = new EditorGUI.ChangeCheckScope(); + using var settingsScope = new LabelWidthScope(); + using var wideScreenScope = new WideScreenScope(this); + EditorGUILayout.IntPopup(m_LightProbeOutsideHullStrategy, Styles.lightProbeOutsideHullStrategyStrings, Styles.lightProbeOutsideHullStrategyValues, Styles.lightProbeOutsideHullStrategy); + if (changeScope.changed) + m_SerializedObject.ApplyModifiedProperties(); + } + } +} diff --git a/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorRenderPipelineAsset.cs b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorRenderPipelineAsset.cs new file mode 100644 index 0000000000..9e8c6a2365 --- /dev/null +++ b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorRenderPipelineAsset.cs @@ -0,0 +1,43 @@ +// 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.Rendering; +using UnityEngine.UIElements; + +namespace UnityEditor +{ + internal class GraphicsSettingsInspectorRenderPipelineAsset : GraphicsSettingsElement + { + public new class UxmlFactory : UxmlFactory { } + + internal class Styles + { + public static readonly GUIContent renderPipeSettings = EditorGUIUtility.TrTextContent("Scriptable Render Pipeline Settings", + "This defines the default render pipeline, which Unity uses when there is no override for a given quality level."); + + public static readonly GUIContent renderPipelineMessage = EditorGUIUtility.TrTextContent("A Scriptable Render Pipeline is in use, some settings will not be used and are hidden"); + } + + SerializedProperty m_ScriptableRenderLoop; + + protected override void Initialize() + { + m_ScriptableRenderLoop = m_SerializedObject.FindProperty("m_CustomRenderPipeline"); + + Add(new IMGUIContainer(Draw)); + } + + void Draw() + { + using var settingsScope = new LabelWidthScope(); + using var wideScreenScope = new WideScreenScope(this); + GUILayout.Label(Styles.renderPipeSettings, EditorStyles.boldLabel); + EditorGUI.RenderPipelineAssetField(m_SerializedObject, m_ScriptableRenderLoop); + + if (GraphicsSettings.isScriptableRenderPipelineEnabled) + EditorGUILayout.HelpBox(Styles.renderPipelineMessage.text, MessageType.Info); + } + } +} diff --git a/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorShaderLog.cs b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorShaderLog.cs new file mode 100644 index 0000000000..801bcaabf4 --- /dev/null +++ b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorShaderLog.cs @@ -0,0 +1,43 @@ +// 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.UIElements; + +namespace UnityEditor +{ + internal class GraphicsSettingsInspectorShaderLog : GraphicsSettingsElement + { + public new class UxmlFactory : UxmlFactory { } + + internal class Styles + { + public static readonly GUIContent shaderPreloadSettings = EditorGUIUtility.TrTextContent("Shader Loading"); + public static readonly GUIContent logWhenShaderIsCompiled = EditorGUIUtility.TrTextContent("Log Shader Compilation", + "When enabled, the player will print shader information each time a shader is being compiled (development and debug mode only)."); + } + + SerializedProperty m_LogWhenShaderIsCompiled; + + protected override void Initialize() + { + m_LogWhenShaderIsCompiled = m_SerializedObject.FindProperty("m_LogWhenShaderIsCompiled"); + + Add(new IMGUIContainer(Draw)); + } + + void Draw() + { + using var highlightScope = new EditorGUI.LabelHighlightScope(m_SettingsWindow.GetSearchText(), HighlightSelectionColor, HighlightColor); + using var settingsScope = new LabelWidthScope(); + using var wideScreenScope = new WideScreenScope(this); + GUILayout.Label(Styles.shaderPreloadSettings, EditorStyles.boldLabel); + + using var changeScope = new EditorGUI.ChangeCheckScope(); + EditorGUILayout.PropertyField(m_LogWhenShaderIsCompiled, Styles.logWhenShaderIsCompiled); + if (changeScope.changed) + m_SerializedObject.ApplyModifiedProperties(); + } + } +} diff --git a/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorShaderPreload.cs b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorShaderPreload.cs new file mode 100644 index 0000000000..04eefa5e4f --- /dev/null +++ b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorShaderPreload.cs @@ -0,0 +1,92 @@ +// 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.UIElements; + +namespace UnityEditor +{ + internal class GraphicsSettingsInspectorShaderPreload : GraphicsSettingsElement + { + public new class UxmlFactory : UxmlFactory { } + internal class Styles + { + public static readonly GUIContent shaderPreloadSave = EditorGUIUtility.TrTextContent("Save to asset...", "Save currently tracked shaders into a Shader Variant Manifest asset."); + public static readonly GUIContent shaderPreloadClear = EditorGUIUtility.TrTextContent("Clear", "Clear currently tracked shader variant information."); + public static readonly GUIContent delayShaderPreload = EditorGUIUtility.TrTextContent("Preload shaders after showing first scene"); + public static readonly GUIContent preloadShadersTimeLimit = EditorGUIUtility.TrTextContent("Preload time limit per frame (ms)"); + public static readonly GUIContent saveShaderVariantCollectionMessage = EditorGUIUtility.TrTextContent("Save shader variant collection"); + public static readonly GUIContent saveShaderVariantCollectionTitle = EditorGUIUtility.TrTextContent("Save Shader Variant Collection"); + } + + SerializedProperty m_PreloadedShaders; + SerializedProperty m_PreloadShadersBatchTimeLimit; + bool m_DelayShaderPreload; + int m_PreloadShadersTimeLimit; + + protected override void Initialize() + { + m_PreloadedShaders = m_SerializedObject.FindProperty("m_PreloadedShaders"); + m_PreloadedShaders.isExpanded = true; + m_PreloadShadersBatchTimeLimit = m_SerializedObject.FindProperty("m_PreloadShadersBatchTimeLimit"); + LoadShaderPreloadingDelay(); + + Add(new IMGUIContainer(Draw)); + } + + void Draw() + { + using var highlightScope = new EditorGUI.LabelHighlightScope(m_SettingsWindow.GetSearchText(), HighlightSelectionColor, HighlightColor); + using var check = new EditorGUI.ChangeCheckScope(); + using var settingsScope = new LabelWidthScope(); + using var wideScreenScope = new WideScreenScope(this); + EditorGUILayout.PropertyField(m_PreloadedShaders, true); + + m_DelayShaderPreload = EditorGUILayout.Toggle(Styles.delayShaderPreload, m_DelayShaderPreload); + if (m_DelayShaderPreload) + m_PreloadShadersTimeLimit = EditorGUILayout.IntField(Styles.preloadShadersTimeLimit, m_PreloadShadersTimeLimit); + SaveShaderPreloadingDelay(); + + EditorGUILayout.Space(); + GUILayout.Label($"Currently tracked: {ShaderUtil.GetCurrentShaderVariantCollectionShaderCount()} shaders {ShaderUtil.GetCurrentShaderVariantCollectionVariantCount()} total variants"); + + EditorGUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + if (GUILayout.Button(Styles.shaderPreloadSave, EditorStyles.miniButton)) + { + var assetPath = EditorUtility.SaveFilePanelInProject( + Styles.saveShaderVariantCollectionTitle.text, + "NewShaderVariants", + "shadervariants", + Styles.saveShaderVariantCollectionMessage.text, + ProjectWindowUtil.GetActiveFolderPath()); + if (!string.IsNullOrEmpty(assetPath)) + ShaderUtil.SaveCurrentShaderVariantCollection(assetPath); + GUIUtility.ExitGUI(); + } + + if (GUILayout.Button(Styles.shaderPreloadClear, EditorStyles.miniButton)) + ShaderUtil.ClearCurrentShaderVariantCollection(); + EditorGUILayout.EndHorizontal(); + + if (check.changed) + m_SerializedObject.ApplyModifiedProperties(); + } + + void LoadShaderPreloadingDelay() + { + m_PreloadShadersTimeLimit = m_PreloadShadersBatchTimeLimit.intValue; + m_DelayShaderPreload = m_PreloadShadersTimeLimit >= 0; + if (!m_DelayShaderPreload) + m_PreloadShadersTimeLimit = 0; + } + + void SaveShaderPreloadingDelay() + { + var newVal = m_DelayShaderPreload ? m_PreloadShadersTimeLimit : -1; + if (m_PreloadShadersBatchTimeLimit.intValue != newVal) + m_PreloadShadersBatchTimeLimit.intValue = newVal; + } + } +} diff --git a/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorShaderStripping.cs b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorShaderStripping.cs new file mode 100644 index 0000000000..5eecc851fd --- /dev/null +++ b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorShaderStripping.cs @@ -0,0 +1,138 @@ +// 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.UIElements; + +namespace UnityEditor +{ + internal class GraphicsSettingsInspectorShaderStripping : GraphicsSettingsElement + { + public new class UxmlFactory : UxmlFactory { } + + internal class Styles + { + public static readonly GUIContent shaderStrippingSettings = EditorGUIUtility.TrTextContent("Shader Stripping"); + + public static readonly GUIContent lightmapModes = EditorGUIUtility.TrTextContent("Lightmap Modes"); + public static readonly GUIContent lightmapPlain = EditorGUIUtility.TrTextContent("Baked Non-Directional", "Include support for baked non-directional lightmaps."); + public static readonly GUIContent lightmapDirCombined = EditorGUIUtility.TrTextContent("Baked Directional", "Include support for baked directional lightmaps."); + public static readonly GUIContent lightmapKeepShadowMask = EditorGUIUtility.TrTextContent("Baked Shadowmask", "Include support for baked shadow occlusion."); + public static readonly GUIContent lightmapKeepSubtractive = EditorGUIUtility.TrTextContent("Baked Subtractive", "Include support for baked subtractive lightmaps."); + public static readonly GUIContent lightmapDynamicPlain = EditorGUIUtility.TrTextContent("Realtime Non-Directional", "Include support for realtime non-directional lightmaps."); + public static readonly GUIContent lightmapDynamicDirCombined = EditorGUIUtility.TrTextContent("Realtime Directional", "Include support for realtime directional lightmaps."); + public static readonly GUIContent lightmapFromScene = EditorGUIUtility.TrTextContent("Import From Current Scene", "Calculate lightmap modes used by the current scene."); + + public static readonly GUIContent fogModes = EditorGUIUtility.TrTextContent("Fog Modes"); + public static readonly GUIContent fogLinear = EditorGUIUtility.TrTextContent("Linear", "Include support for Linear fog."); + public static readonly GUIContent fogExp = EditorGUIUtility.TrTextContent("Exponential", "Include support for Exponential fog."); + public static readonly GUIContent fogExp2 = EditorGUIUtility.TrTextContent("Exponential Squared", "Include support for Exponential Squared fog."); + public static readonly GUIContent fogFromScene = EditorGUIUtility.TrTextContent("Import From Current Scene", "Calculate fog modes used by the current scene."); + + public static readonly GUIContent instancingVariants = EditorGUIUtility.TrTextContent("Instancing Variants"); + public static readonly GUIContent brgVariants = EditorGUIUtility.TrTextContent("BatchRendererGroup Variants"); + } + + SerializedProperty m_LightmapStripping; + SerializedProperty m_LightmapKeepPlain; + SerializedProperty m_LightmapKeepDirCombined; + SerializedProperty m_LightmapKeepDynamicPlain; + SerializedProperty m_LightmapKeepDynamicDirCombined; + SerializedProperty m_LightmapKeepShadowMask; + SerializedProperty m_LightmapKeepSubtractive; + SerializedProperty m_FogStripping; + SerializedProperty m_FogKeepLinear; + SerializedProperty m_FogKeepExp; + SerializedProperty m_FogKeepExp2; + SerializedProperty m_InstancingStripping; + SerializedProperty m_BrgStripping; + + protected override void Initialize() + { + m_LightmapStripping = m_SerializedObject.FindProperty("m_LightmapStripping"); + m_LightmapKeepPlain = m_SerializedObject.FindProperty("m_LightmapKeepPlain"); + m_LightmapKeepDirCombined = m_SerializedObject.FindProperty("m_LightmapKeepDirCombined"); + m_LightmapKeepDynamicPlain = m_SerializedObject.FindProperty("m_LightmapKeepDynamicPlain"); + m_LightmapKeepDynamicDirCombined = m_SerializedObject.FindProperty("m_LightmapKeepDynamicDirCombined"); + m_LightmapKeepShadowMask = m_SerializedObject.FindProperty("m_LightmapKeepShadowMask"); + m_LightmapKeepSubtractive = m_SerializedObject.FindProperty("m_LightmapKeepSubtractive"); + m_FogStripping = m_SerializedObject.FindProperty("m_FogStripping"); + m_FogKeepLinear = m_SerializedObject.FindProperty("m_FogKeepLinear"); + m_FogKeepExp = m_SerializedObject.FindProperty("m_FogKeepExp"); + m_FogKeepExp2 = m_SerializedObject.FindProperty("m_FogKeepExp2"); + m_InstancingStripping = m_SerializedObject.FindProperty("m_InstancingStripping"); + m_BrgStripping = m_SerializedObject.FindProperty("m_BrgStripping"); + + Add(new IMGUIContainer(Draw)); + } + + void Draw() + { + using var highlightScope = new EditorGUI.LabelHighlightScope(m_SettingsWindow.GetSearchText(), HighlightSelectionColor, HighlightColor); + using var check = new EditorGUI.ChangeCheckScope(); + using var settingsScope = new LabelWidthScope(); + using var wideScreenScope = new WideScreenScope(this); + + EditorGUILayout.LabelField(Styles.shaderStrippingSettings, EditorStyles.boldLabel); + + bool calcLightmapStripping = false, calcFogStripping = false; + + EditorGUILayout.PropertyField(m_LightmapStripping, Styles.lightmapModes); + + if (m_LightmapStripping.intValue != 0) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(m_LightmapKeepPlain, Styles.lightmapPlain); + EditorGUILayout.PropertyField(m_LightmapKeepDirCombined, Styles.lightmapDirCombined); + EditorGUILayout.PropertyField(m_LightmapKeepDynamicPlain, Styles.lightmapDynamicPlain); + EditorGUILayout.PropertyField(m_LightmapKeepDynamicDirCombined, Styles.lightmapDynamicDirCombined); + EditorGUILayout.PropertyField(m_LightmapKeepShadowMask, Styles.lightmapKeepShadowMask); + EditorGUILayout.PropertyField(m_LightmapKeepSubtractive, Styles.lightmapKeepSubtractive); + EditorGUILayout.Space(); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.PrefixLabel(GUIContent.Temp(" "), EditorStyles.miniButton); + + if (GUILayout.Button(Styles.lightmapFromScene, EditorStyles.miniButton, GUILayout.ExpandWidth(false))) + calcLightmapStripping = true; + + EditorGUILayout.EndHorizontal(); + EditorGUI.indentLevel--; + EditorGUILayout.Space(); + } + + EditorGUILayout.PropertyField(m_FogStripping, Styles.fogModes); + if (m_FogStripping.intValue != 0) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(m_FogKeepLinear, Styles.fogLinear); + EditorGUILayout.PropertyField(m_FogKeepExp, Styles.fogExp); + EditorGUILayout.PropertyField(m_FogKeepExp2, Styles.fogExp2); + EditorGUILayout.Space(); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.PrefixLabel(GUIContent.Temp(" "), EditorStyles.miniButton); + + if (GUILayout.Button(Styles.fogFromScene, EditorStyles.miniButton, GUILayout.ExpandWidth(false))) + calcFogStripping = true; + + EditorGUILayout.EndHorizontal(); + EditorGUI.indentLevel--; + EditorGUILayout.Space(); + } + + EditorGUILayout.PropertyField(m_InstancingStripping, Styles.instancingVariants); + EditorGUILayout.PropertyField(m_BrgStripping, Styles.brgVariants); + + if (check.changed) + m_SerializedObject.ApplyModifiedProperties(); + + // need to do these after ApplyModifiedProperties, since it changes their values from native code + if (calcLightmapStripping) + ShaderUtil.CalculateLightmapStrippingFromCurrentScene(); + if (calcFogStripping) + ShaderUtil.CalculateFogStrippingFromCurrentScene(); + } + } +} diff --git a/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorTierSettings.cs b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorTierSettings.cs new file mode 100644 index 0000000000..d6be01ae0f --- /dev/null +++ b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorTierSettings.cs @@ -0,0 +1,427 @@ +// 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 UnityEditor.AnimatedValues; +using UnityEditor.Build; +using UnityEditor.Rendering; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.UIElements; + +namespace UnityEditor +{ + internal class TierSettingsWindow : EditorWindow + { + static TierSettingsWindow s_Instance; + + public static void CreateWindow() + { + s_Instance = GetWindow(); + s_Instance.minSize = new Vector2(600, 300); + s_Instance.titleContent = GraphicsSettingsInspectorTierSettings.Styles.tierSettings; + } + + internal static TierSettingsWindow GetInstance() + { + return s_Instance; + } + + SerializedObject m_SerializedObject; + + void OnEnable() + { + s_Instance = this; + m_SerializedObject = new SerializedObject(GraphicsSettings.GetGraphicsSettings()); + var graphicsSettingsInspectorTierSettings = new GraphicsSettingsInspectorTierSettings() + { + style = + { + marginTop = 5, + marginBottom = 5, + marginLeft = 5, + marginRight = 5 + }, + UseAnimation = false + }; + graphicsSettingsInspectorTierSettings.Initialize(m_SerializedObject); + rootVisualElement.Add(graphicsSettingsInspectorTierSettings); + RenderPipelineManager.activeRenderPipelineAssetChanged += RenderPipelineAssetChanged; + } + + void RenderPipelineAssetChanged(RenderPipelineAsset previous, RenderPipelineAsset next) + { + if (next != null) + Close(); + } + + void OnDisable() + { + RenderPipelineManager.activeRenderPipelineAssetChanged -= RenderPipelineAssetChanged; + rootVisualElement.Clear(); + + if (s_Instance == this) + s_Instance = null; + } + } + + internal class GraphicsSettingsInspectorTierSettings : GraphicsSettingsElement + { + public new class UxmlFactory : UxmlFactory + { + } + + internal class Styles + { + public static readonly GUIContent[] shaderQualityName = + { EditorGUIUtility.TrTextContent("Low"), EditorGUIUtility.TrTextContent("Medium"), EditorGUIUtility.TrTextContent("High") }; + + public static readonly int[] shaderQualityValue = + { (int)ShaderQuality.Low, (int)ShaderQuality.Medium, (int)ShaderQuality.High }; + + public static readonly GUIContent[] renderingPathName = + { EditorGUIUtility.TrTextContent("Forward"), EditorGUIUtility.TrTextContent("Deferred"), EditorGUIUtility.TrTextContent("Legacy Vertex Lit") }; + + public static readonly int[] renderingPathValue = + { (int)RenderingPath.Forward, (int)RenderingPath.DeferredShading, (int)RenderingPath.VertexLit }; + + public static readonly GUIContent[] hdrModeName = + { EditorGUIUtility.TrTextContent("FP16"), EditorGUIUtility.TrTextContent("R11G11B10") }; + + public static readonly int[] hdrModeValue = + { (int)CameraHDRMode.FP16, (int)CameraHDRMode.R11G11B10 }; + + public static readonly GUIContent[] realtimeGICPUUsageName = + { EditorGUIUtility.TrTextContent("Low"), EditorGUIUtility.TrTextContent("Medium"), EditorGUIUtility.TrTextContent("High"), EditorGUIUtility.TrTextContent("Unlimited") }; + + public static readonly int[] realtimeGICPUUsageValue = + { (int)RealtimeGICPUUsage.Low, (int)RealtimeGICPUUsage.Medium, (int)RealtimeGICPUUsage.High, (int)RealtimeGICPUUsage.Unlimited }; + + public static readonly GUIContent showEditorWindow = EditorGUIUtility.TrTextContent("Open Editor..."); + public static readonly GUIContent closeEditorWindow = EditorGUIUtility.TrTextContent("Close Editor"); + public static readonly GUIContent tierSettings = EditorGUIUtility.TrTextContent("Tier Settings"); + + public static readonly GUIContent[] tierName = + { + EditorGUIUtility.TrTextContent("Low (Tier 1)"), EditorGUIUtility.TrTextContent("Medium (Tier 2)"), EditorGUIUtility.TrTextContent("High (Tier 3)") + }; + + public static readonly GUIContent empty = EditorGUIUtility.TextContent(""); + public static readonly GUIContent autoSettingsLabel = EditorGUIUtility.TrTextContent("Use Defaults"); + + public static readonly GUIContent standardShaderSettings = EditorGUIUtility.TrTextContent("Standard Shader"); + public static readonly GUIContent renderingSettings = EditorGUIUtility.TrTextContent("Rendering"); + + public static readonly GUIContent standardShaderQuality = EditorGUIUtility.TrTextContent("Standard Shader Quality"); + + public static readonly GUIContent reflectionProbeBoxProjection = + EditorGUIUtility.TrTextContent("Reflection Probes Box Projection", "Enable projection for reflection UV mappings on Reflection Probes."); + + public static readonly GUIContent reflectionProbeBlending = EditorGUIUtility.TrTextContent("Reflection Probes Blending", + "Gradually fade out one probe's cubemap while fading in the other's as the reflective object passes from one zone to the other."); + + public static readonly GUIContent detailNormalMap = + EditorGUIUtility.TrTextContent("Detail Normal Map", "Enable Detail (secondary) Normal Map sampling for up-close viewing, if assigned."); + + public static readonly GUIContent cascadedShadowMaps = EditorGUIUtility.TrTextContent("Cascaded Shadows"); + + public static readonly GUIContent prefer32BitShadowMaps = + EditorGUIUtility.TrTextContent("Prefer 32-bit shadow maps", "Enable 32-bit float shadow map when you are targeting PS4 or platforms using DX11 or DX12."); + + public static readonly GUIContent semitransparentShadows = EditorGUIUtility.TrTextContent("Enable Semitransparent Shadows"); + + public static readonly GUIContent enableLPPV = + EditorGUIUtility.TrTextContent("Enable Light Probe Proxy Volume", "Enable rendering a 3D grid of interpolated Light Probes inside a Bounding Volume."); + + public static readonly GUIContent renderingPath = EditorGUIUtility.TrTextContent("Rendering Path", + "Choose how Unity should render graphics. Different rendering paths affect the performance of your game, and how lighting and shading are calculated."); + + public static readonly GUIContent useHDR = EditorGUIUtility.TrTextContent("Use HDR", "Enable High Dynamic Range rendering for this tier."); + public static readonly GUIContent hdrMode = EditorGUIUtility.TrTextContent("HDR Mode", "Color render texture format for the HDR buffer to use when HDR is enabled."); + + public static readonly GUIContent realtimeGICPUUsage = EditorGUIUtility.TrTextContent("Realtime Global Illumination CPU Usage", + "How many CPU worker threads to create for Realtime Global Illumination lighting calculations in the Player. Increasing this makes the system react faster to changes in lighting at a cost of using more CPU time. The higher the CPU Usage value, the more worker threads are created for solving Realtime GI."); + } + + public override bool BuiltinOnly => true; + public bool UseAnimation { get; set; } = true; + + bool verticalLayout = true; + + // this is category animation is blatantly copied from PlayerSettingsEditor.cs + bool m_ShowTierSettingsUI = true; // show by default, as otherwise users are confused + AnimBool m_TierSettingsAnimator; + + protected override void Initialize() + { + var container = new IMGUIContainer(Draw); + Add(container); + + if (UseAnimation) + m_TierSettingsAnimator = new AnimBool(m_ShowTierSettingsUI, container.MarkDirtyRepaint); + } + + void Draw() + { + if (GraphicsSettings.isScriptableRenderPipelineEnabled) + { + var window = TierSettingsWindow.GetInstance(); + if (window != null) + window.Close(); + } + else + { + using var settingsScope = new LabelWidthScope(); + using var wideScreenScope = new WideScreenScope(this); + if (m_TierSettingsAnimator == null) + OnInspectorGUI(); + else + TierSettingsGUI(); + } + } + + void HandleEditorWindowButton() + { + var window = TierSettingsWindow.GetInstance(); + var text = window == null ? Styles.showEditorWindow : Styles.closeEditorWindow; + if (!GUILayout.Button(text, EditorStyles.miniButton, GUILayout.Width(110))) + return; + + if (window) + { + window.Close(); + } + else + { + TierSettingsWindow.CreateWindow(); + TierSettingsWindow.GetInstance().Show(); + } + } + + void TierSettingsGUI() + { + var enabled = GUI.enabled; + GUI.enabled = true; // we don't want to disable the expand behavior + EditorGUILayout.BeginVertical(EditorStyles.helpBox, GUILayout.Height(20)); + + EditorGUILayout.BeginHorizontal(); + var r = GUILayoutUtility.GetRect(20, 21); + r.x += 3; + r.width += 6; + m_ShowTierSettingsUI = EditorGUI.FoldoutTitlebar(r, Styles.tierSettings, m_ShowTierSettingsUI, true, EditorStyles.inspectorTitlebarFlat, EditorStyles.inspectorTitlebarText); + HandleEditorWindowButton(); + EditorGUILayout.EndHorizontal(); + + m_TierSettingsAnimator.target = m_ShowTierSettingsUI; + GUI.enabled = enabled; + + if (EditorGUILayout.BeginFadeGroup(m_TierSettingsAnimator.faded) && TierSettingsWindow.GetInstance() == null) + OnInspectorGUI(); + EditorGUILayout.EndFadeGroup(); + EditorGUILayout.EndVertical(); + } + + void OnInspectorGUI() + { + using var highlightScope = new EditorGUI.LabelHighlightScope(m_SettingsWindow.GetSearchText(), HighlightSelectionColor, HighlightColor); + var validPlatforms = BuildPlatforms.instance.GetValidPlatforms().ToArray(); + var platform = validPlatforms[EditorGUILayout.BeginPlatformGrouping(validPlatforms, null, EditorStyles.frameBox)]; + + if (verticalLayout) OnGuiVertical(platform); + else OnGuiHorizontal(platform); + + EditorGUILayout.EndPlatformGrouping(); + } + + void OnFieldLabelsGUI(bool vertical) + { + var usingSRP = GraphicsSettings.currentRenderPipeline != null; + if (!vertical) + EditorGUILayout.LabelField(Styles.standardShaderSettings, EditorStyles.boldLabel); + + if (!usingSRP) + { + EditorGUILayout.LabelField(Styles.standardShaderQuality); + EditorGUILayout.LabelField(Styles.reflectionProbeBoxProjection); + EditorGUILayout.LabelField(Styles.reflectionProbeBlending); + EditorGUILayout.LabelField(Styles.detailNormalMap); + EditorGUILayout.LabelField(Styles.semitransparentShadows); + } + + if (SupportedRenderingFeatures.active.lightProbeProxyVolumes) + EditorGUILayout.LabelField(Styles.enableLPPV); + + if (!vertical) + { + EditorGUILayout.LabelField(Styles.empty, EditorStyles.boldLabel); + EditorGUILayout.LabelField(Styles.renderingSettings, EditorStyles.boldLabel); + } + + if (!usingSRP) + { + EditorGUILayout.LabelField(Styles.cascadedShadowMaps); + EditorGUILayout.LabelField(Styles.prefer32BitShadowMaps); + EditorGUILayout.LabelField(Styles.useHDR); + EditorGUILayout.LabelField(Styles.hdrMode); + EditorGUILayout.LabelField(Styles.renderingPath); + } + + if (SupportedRenderingFeatures.IsLightmapBakeTypeSupported(LightmapBakeType.Realtime)) + EditorGUILayout.LabelField(Styles.realtimeGICPUUsage); + } + + + // custom enum handling + ShaderQuality ShaderQualityPopup(ShaderQuality sq) => + (ShaderQuality)EditorGUILayout.IntPopup((int)sq, Styles.shaderQualityName, Styles.shaderQualityValue); + + RenderingPath RenderingPathPopup(RenderingPath rp) => + (RenderingPath)EditorGUILayout.IntPopup((int)rp, Styles.renderingPathName, Styles.renderingPathValue); + + CameraHDRMode HDRModePopup(CameraHDRMode mode) => + (CameraHDRMode)EditorGUILayout.IntPopup((int)mode, Styles.hdrModeName, Styles.hdrModeValue); + + RealtimeGICPUUsage RealtimeGICPUUsagePopup(RealtimeGICPUUsage usage) => + (RealtimeGICPUUsage)EditorGUILayout.IntPopup((int)usage, Styles.realtimeGICPUUsageName, Styles.realtimeGICPUUsageValue); + + + void OnTierGUI(BuildPlatform platform, GraphicsTier tier, bool vertical) + { + var ts = EditorGraphicsSettings.GetTierSettings(platform.namedBuildTarget, tier); + + EditorGUI.BeginChangeCheck(); + + + if (!vertical) + EditorGUILayout.LabelField(Styles.empty, EditorStyles.boldLabel); + + var usingSRP = GraphicsSettings.currentRenderPipeline != null; + if (!usingSRP) + { + ts.standardShaderQuality = ShaderQualityPopup(ts.standardShaderQuality); + ts.reflectionProbeBoxProjection = EditorGUILayout.Toggle(ts.reflectionProbeBoxProjection); + ts.reflectionProbeBlending = EditorGUILayout.Toggle(ts.reflectionProbeBlending); + ts.detailNormalMap = EditorGUILayout.Toggle(ts.detailNormalMap); + ts.semitransparentShadows = EditorGUILayout.Toggle(ts.semitransparentShadows); + } + + if (SupportedRenderingFeatures.active.lightProbeProxyVolumes) + ts.enableLPPV = EditorGUILayout.Toggle(ts.enableLPPV); + + if (!vertical) + { + EditorGUILayout.LabelField(Styles.empty, EditorStyles.boldLabel); + EditorGUILayout.LabelField(Styles.empty, EditorStyles.boldLabel); + } + + if (!usingSRP) + { + ts.cascadedShadowMaps = EditorGUILayout.Toggle(ts.cascadedShadowMaps); + ts.prefer32BitShadowMaps = EditorGUILayout.Toggle(ts.prefer32BitShadowMaps); + ts.hdr = EditorGUILayout.Toggle(ts.hdr); + ts.hdrMode = HDRModePopup(ts.hdrMode); + ts.renderingPath = RenderingPathPopup(ts.renderingPath); + } + + if (!usingSRP) + if (SupportedRenderingFeatures.IsLightmapBakeTypeSupported(LightmapBakeType.Realtime)) + ts.realtimeGICPUUsage = RealtimeGICPUUsagePopup(ts.realtimeGICPUUsage); + + if (EditorGUI.EndChangeCheck()) + { + // TODO: it should be doable in c# now as we "expose" GraphicsSettings anyway + EditorGraphicsSettings.RegisterUndo(); + EditorGraphicsSettings.SetTierSettings(platform.namedBuildTarget, tier, ts); + } + } + + void OnGuiHorizontal(BuildPlatform platform) + { + EditorGUILayout.BeginHorizontal(); + + EditorGUILayout.BeginVertical(); + EditorGUIUtility.labelWidth = 140; + EditorGUILayout.LabelField(Styles.empty, EditorStyles.boldLabel); + OnFieldLabelsGUI(false); + EditorGUILayout.LabelField(Styles.empty, EditorStyles.boldLabel); + EditorGUILayout.LabelField(Styles.autoSettingsLabel, EditorStyles.boldLabel); + EditorGUILayout.EndVertical(); + + EditorGUIUtility.labelWidth = 50; + foreach (GraphicsTier tier in Enum.GetValues(typeof(GraphicsTier))) + { + bool autoSettings = EditorGraphicsSettings.AreTierSettingsAutomatic(platform.namedBuildTarget.ToBuildTargetGroup(), tier); + + EditorGUILayout.BeginVertical(); + EditorGUILayout.LabelField(Styles.tierName[(int)tier], EditorStyles.boldLabel); + using (new EditorGUI.DisabledScope(autoSettings)) + OnTierGUI(platform, tier, false); + + EditorGUILayout.LabelField(Styles.empty, EditorStyles.boldLabel); + EditorGUI.BeginChangeCheck(); + autoSettings = EditorGUILayout.Toggle(autoSettings); + if (EditorGUI.EndChangeCheck()) + { + EditorGraphicsSettings.RegisterUndo(); + EditorGraphicsSettings.MakeTierSettingsAutomatic(platform.namedBuildTarget.ToBuildTargetGroup(), tier, autoSettings); + EditorGraphicsSettings.OnUpdateTierSettings(platform.namedBuildTarget.ToBuildTargetGroup(), true); + } + + EditorGUILayout.EndVertical(); + } + + EditorGUIUtility.labelWidth = 0; + + EditorGUILayout.EndHorizontal(); + } + + void OnGuiVertical(BuildPlatform platform) + { + EditorGUILayout.BeginVertical(); + foreach (GraphicsTier tier in Enum.GetValues(typeof(GraphicsTier))) + { + var autoSettings = EditorGraphicsSettings.AreTierSettingsAutomatic(platform.namedBuildTarget.ToBuildTargetGroup(), tier); + EditorGUI.BeginChangeCheck(); + { + GUILayout.BeginHorizontal(); + EditorGUIUtility.labelWidth = 80; + EditorGUILayout.LabelField(Styles.tierName[(int)tier], EditorStyles.boldLabel); + GUILayout.FlexibleSpace(); + EditorGUIUtility.labelWidth = 80; + autoSettings = EditorGUILayout.Toggle(Styles.autoSettingsLabel, autoSettings); + GUILayout.EndHorizontal(); + } + + if (EditorGUI.EndChangeCheck()) + { + EditorGraphicsSettings.RegisterUndo(); + EditorGraphicsSettings.MakeTierSettingsAutomatic(platform.namedBuildTarget.ToBuildTargetGroup(), tier, autoSettings); + EditorGraphicsSettings.OnUpdateTierSettings(platform.namedBuildTarget.ToBuildTargetGroup(), true); + } + + using (new EditorGUI.DisabledScope(autoSettings)) + { + EditorGUI.indentLevel++; + EditorGUILayout.BeginHorizontal(); + + EditorGUILayout.BeginVertical(); + EditorGUIUtility.labelWidth = 140; + OnFieldLabelsGUI(true); + EditorGUILayout.EndVertical(); + + EditorGUILayout.BeginVertical(); + EditorGUIUtility.labelWidth = 50; + OnTierGUI(platform, tier, true); + EditorGUILayout.EndVertical(); + + GUILayout.EndHorizontal(); + EditorGUI.indentLevel--; + } + } + + GUILayout.EndVertical(); + EditorGUIUtility.labelWidth = 0; + } + } +} diff --git a/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorTitleBar.cs b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorTitleBar.cs new file mode 100644 index 0000000000..473f2f085f --- /dev/null +++ b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorTitleBar.cs @@ -0,0 +1,67 @@ +// 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 UnityEditor.Experimental; +using UnityEditor.StyleSheets; +using UnityEngine; +using UnityEngine.UIElements; +using Object = UnityEngine.Object; + +namespace UnityEditor +{ + internal class GraphicsSettingsInspectorTitleBar : GraphicsSettingsElement + { + public new class UxmlFactory : UxmlFactory { } + + internal class Styles + { + public static Lazy ImguiStylesHeader = new(() => "SettingsHeader"); + public static StyleBlock settingsPanel { get; } = EditorResources.GetStyle("sb-settings-panel-client-area"); + + public static StyleBlock header { get; } = EditorResources.GetStyle("sb-settings-header"); + + public static StyleBlock settingsBtn { get; } = EditorResources.GetStyle("sb-settings-icon-btn"); + + public static readonly GUIContent mainHeader = EditorGUIUtility.TrTextContent("Graphics"); + } + + Object[] m_TargetObjects; + + protected override void Initialize() + { + m_TargetObjects = m_SerializedObject.targetObjects; + Add(new IMGUIContainer(Draw)); + } + + void Draw() + { + using var highlightScope = new EditorGUI.LabelHighlightScope(m_SettingsWindow.GetSearchText(), HighlightSelectionColor, HighlightColor); + GUILayout.BeginHorizontal(); + GUILayout.Space(Styles.settingsPanel.GetFloat(StyleCatalogKeyword.marginLeft)); + GUILayout.Label(Styles.mainHeader, Styles.ImguiStylesHeader.Value, GUILayout.MaxHeight(Styles.header.GetFloat("max-height")), + GUILayout.MinWidth(160)); + GUILayout.FlexibleSpace(); + + var btnWidth = Styles.settingsBtn.GetFloat(StyleCatalogKeyword.width); + var btnHeight = Styles.settingsBtn.GetFloat(StyleCatalogKeyword.height); + var btnMargin = Styles.settingsBtn.GetFloat(StyleCatalogKeyword.marginTop); + + var currentRect = GUILayoutUtility.GetRect(btnWidth, btnHeight); + currentRect.y = btnMargin; + EditorGUIUtility.DrawEditorHeaderItems(currentRect, m_TargetObjects); + var settingsRect = GUILayoutUtility.GetRect(btnWidth, btnHeight); + settingsRect.y = currentRect.y; + + // Settings; process event even for disabled UI + var wasEnabled = GUI.enabled; + GUI.enabled = true; + var showMenu = EditorGUI.DropdownButton(settingsRect, GUIContent.none, FocusType.Passive, EditorStyles.optionsButtonStyle); + GUI.enabled = wasEnabled; + if (showMenu) + EditorUtility.DisplayObjectContextMenu(settingsRect, m_TargetObjects, 0); + GUILayout.EndHorizontal(); + } + } +} diff --git a/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorVideoShader.cs b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorVideoShader.cs new file mode 100644 index 0000000000..1ea9985c95 --- /dev/null +++ b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsInspectorVideoShader.cs @@ -0,0 +1,38 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using UnityEngine; +using UnityEngine.UIElements; + +namespace UnityEditor +{ + internal class GraphicsSettingsInspectorVideoShader : GraphicsSettingsElement + { + public new class UxmlFactory : UxmlFactory { } + internal class Styles + { + public static readonly GUIContent modeString = EditorGUIUtility.TrTextContent("Video", "Shaders used by the VideoPlayer decoding and compositing."); + } + + SerializedProperty m_VideoShadersIncludeMode; + + protected override void Initialize() + { + m_VideoShadersIncludeMode = m_SerializedObject.FindProperty("m_VideoShadersIncludeMode"); + + Add(new IMGUIContainer(Draw)); + } + + void Draw() + { + using var highlightScope = new EditorGUI.LabelHighlightScope(m_SettingsWindow.GetSearchText(), HighlightSelectionColor, HighlightColor); + using var check = new EditorGUI.ChangeCheckScope(); + using var settingsScope = new LabelWidthScope(); + using var wideScreenScope = new WideScreenScope(this); + EditorGUILayout.PropertyField(m_VideoShadersIncludeMode, Styles.modeString); + if(check.changed) + m_SerializedObject.ApplyModifiedProperties(); + } + } +} diff --git a/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsScopes.cs b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsScopes.cs new file mode 100644 index 0000000000..6f91317bca --- /dev/null +++ b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsScopes.cs @@ -0,0 +1,53 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using UnityEditor.Experimental; +using UnityEditor.StyleSheets; +using UnityEngine; +using UnityEngine.UIElements; + +namespace UnityEditor +{ + internal class LabelWidthScope : GUI.Scope + { + static StyleBlock window => EditorResources.GetStyle("sb-settings-window"); + static float s_DefaultLabelWidth => window.GetFloat("-unity-label-width"); + + readonly float m_LabelWidth; + public LabelWidthScope(float layoutMaxWidth) + { + m_LabelWidth = EditorGUIUtility.labelWidth; + EditorGUIUtility.labelWidth = s_DefaultLabelWidth; + } + + public LabelWidthScope() : this(s_DefaultLabelWidth) + { + } + + protected override void CloseScope() + { + EditorGUIUtility.labelWidth = m_LabelWidth; + } + } + + internal class WideScreenScope : GUI.Scope + { + readonly bool m_CurrentWideMode; + + public WideScreenScope(VisualElement currentElement) + { + m_CurrentWideMode = EditorGUIUtility.wideMode; + + // the inspector's width can be NaN if this is our first layout check. + // If that's the case we'll set wideMode to true to avoid computing too tall an inspector on the first layout calculation + var inspectorWidth = currentElement.layout.width; + EditorGUIUtility.wideMode = float.IsNaN(inspectorWidth) || inspectorWidth > Editor.k_WideModeMinWidth; + } + + protected override void CloseScope() + { + EditorGUIUtility.wideMode = m_CurrentWideMode; + } + } +} diff --git a/Editor/Mono/Inspector/InspectorWindow.cs b/Editor/Mono/Inspector/InspectorWindow.cs index 79ac72112a..23a2ea09bc 100644 --- a/Editor/Mono/Inspector/InspectorWindow.cs +++ b/Editor/Mono/Inspector/InspectorWindow.cs @@ -92,12 +92,11 @@ protected override void OnEnable() m_LockTracker.tracker = tracker; m_LockTracker.lockStateChanged.AddListener(LockStateChanged); + m_Tracker.dataMode = GetDataModeController_Internal().dataMode; EditorApplication.projectWasLoaded += OnProjectWasLoaded; Selection.selectionChanged += OnSelectionChanged; AssemblyReloadEvents.afterAssemblyReload += OnAfterAssemblyReload; - - UpdateDataMode(); } private void OnAfterAssemblyReload() @@ -158,32 +157,22 @@ private void OnSelectionChanged() m_MultiEditLabel.RemoveFromHierarchy(); } - UpdateDataMode(); - } - - private void UpdateDataMode() - { - UpdateSupportedDataModes(); + if (isLocked) + return; - // Try to respect the DataMode hint provided by the selection. - // If impossible, try to retain the current DataMode if supported. - if (IsDataModeSupported(Selection.dataModeHint)) - SwitchToDataMode(Selection.dataModeHint); - else if (!IsDataModeSupported(dataMode)) - SwitchToDefaultDataMode(); + UpdateSupportedDataModesList(); } // Note: supportedModes is cleared before and sorted after this method is called protected override void OnUpdateSupportedDataModes(List supportedModes) { - m_UserSupportedDataModes.Clear(); - // Not showing data modes in debug - if (m_InspectorMode == InspectorMode.Normal) - { - DataModeSupportUtils.GetDataModeSupport(Selection.activeObject, Selection.activeContext, m_UserSupportedDataModes); - supportedModes.AddRange(m_UserSupportedDataModes); - } + if (m_InspectorMode != InspectorMode.Normal) + return; + + m_UserSupportedDataModes.Clear(); + DataModeSupportUtils.GetDataModeSupport(Selection.activeObject, Selection.activeContext, m_UserSupportedDataModes); + supportedModes.AddRange(m_UserSupportedDataModes); } protected override void OnDisable() @@ -490,6 +479,9 @@ internal static void RefreshInspectors() internal override Object GetInspectedObject() { + if (tracker.hasComponentsWhichCannotBeMultiEdited && !tracker.isLocked) + return Selection.activeObject; + Editor editor = InspectorWindowUtils.GetFirstNonImportInspectorEditor(tracker.activeEditors); if (editor == null) return null; @@ -498,6 +490,9 @@ internal override Object GetInspectedObject() internal Object[] GetInspectedObjects() { + if (tracker.hasComponentsWhichCannotBeMultiEdited && !tracker.isLocked) + return Selection.objects; + Editor editor = InspectorWindowUtils.GetFirstNonImportInspectorEditor(tracker.activeEditors); if (editor == null) return null; diff --git a/Editor/Mono/Inspector/InspectorWindowUtils.cs b/Editor/Mono/Inspector/InspectorWindowUtils.cs index 375159071c..5c7e1d405c 100644 --- a/Editor/Mono/Inspector/InspectorWindowUtils.cs +++ b/Editor/Mono/Inspector/InspectorWindowUtils.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Linq; using UnityEngine; using Object = UnityEngine.Object; @@ -76,15 +77,15 @@ public static void GetPreviewableTypes(out Dictionary> previewa continue; } - List types; - - if (!previewableTypes.TryGetValue(previewAttr.m_Type, out types)) + foreach (var customPreviewType in new[] { previewAttr.m_Type }.Concat(TypeCache.GetTypesDerivedFrom(previewAttr.m_Type))) { - types = new List(); - previewableTypes.Add(previewAttr.m_Type, types); + if (!previewableTypes.TryGetValue(customPreviewType, out var types)) + { + types = new List(); + previewableTypes.Add(customPreviewType, types); + } + types.Add(type); } - - types.Add(type); } } } diff --git a/Editor/Mono/Inspector/LightProbeGroupInspector.cs b/Editor/Mono/Inspector/LightProbeGroupInspector.cs index 6cf61c3cd9..fe911a3255 100644 --- a/Editor/Mono/Inspector/LightProbeGroupInspector.cs +++ b/Editor/Mono/Inspector/LightProbeGroupInspector.cs @@ -255,7 +255,7 @@ public void HandleEditMenuHotKeyCommands() public static void TetrahedralizeSceneProbes(out Vector3[] positions, out int[] indices) { - var probeGroups = Object.FindObjectsOfType(typeof(LightProbeGroup)) as LightProbeGroup[]; + var probeGroups = Object.FindObjectsByType(FindObjectsSortMode.None); if (probeGroups == null) { @@ -621,7 +621,7 @@ public override void OnInspectorGUI() bool srpHasAlternativeToLegacyProbes = UnityEngine.Rendering.SupportedRenderingFeatures.active.overridesLightProbeSystem; if (srpHasAlternativeToLegacyProbes) { - EditorGUILayout.HelpBox(UnityEngine.Rendering.SupportedRenderingFeatures.active.overridesLightProbeSystemWarningMessage, MessageType.Warning, wide: true); + EditorGUILayout.HelpBox(SupportedRenderingFeatures.active.overridesLightProbeSystemWarningMessage, MessageType.Warning, true); } using (new EditorGUI.DisabledScope(srpHasAlternativeToLegacyProbes)) diff --git a/Editor/Mono/Inspector/LightingSettingsEditor.cs b/Editor/Mono/Inspector/LightingSettingsEditor.cs index fcb0f2a06b..1cf2800c3a 100644 --- a/Editor/Mono/Inspector/LightingSettingsEditor.cs +++ b/Editor/Mono/Inspector/LightingSettingsEditor.cs @@ -22,6 +22,7 @@ public void OnEnable() m_Editor = new SharedLightingSettingsEditor(); m_Editor.OnEnable(); m_Editor.UpdateSettings(serializedObject); + m_Editor.ClampMaxRanges(); } internal override void OnHeaderControlsGUI() @@ -38,6 +39,11 @@ public override void OnInspectorGUI() serializedObject.ApplyModifiedProperties(); } + + private void OnFocus() + { + m_Editor.ClampMaxRanges(); + } } internal class SharedLightingSettingsEditor @@ -69,6 +75,7 @@ internal class SharedLightingSettingsEditor SerializedProperty m_CompAOExponentDirect; SerializedProperty m_LightmapCompression; SerializedProperty m_LightmapMaxSize; + SerializedProperty m_LightmapSizeFixed; SerializedProperty m_BakeBackend; // pvr SerializedProperty m_PVRSampleCount; @@ -99,8 +106,6 @@ internal class SharedLightingSettingsEditor SerializedProperty m_ForceWhiteAlbedo; SerializedProperty m_ForceUpdates; SerializedProperty m_FilterMode; - SerializedProperty m_TiledBaking; - SerializedProperty m_NumRaysToShootPerTexel; SerializedProperty m_RespectSceneVisibilityWhenBakingGI; enum DenoiserTarget @@ -195,7 +200,6 @@ static class Styles public static readonly GUIContent mixedLightsLabel = EditorGUIUtility.TrTextContent("Mixed Lighting", "Bake Global Illumination for mixed lights and static objects. May bake both direct and/or indirect lighting based on settings. Only static objects are blocking and bouncing light, dynamic objects receive baked lighting via light probes."); public static readonly GUIContent generalLightmapLabel = EditorGUIUtility.TrTextContent("Lightmapping Settings", "Settings that apply to both Global Illumination modes (Precomputed Realtime and Baked)."); public static readonly GUIContent internalLabel = EditorGUIUtility.TrTextContent("Internal Settings", "Internal only settings. "); - public static readonly GUIContent noRealtimeGIInSM2AndGLES2 = EditorGUIUtility.TrTextContent("Realtime Global Illumination is not supported on SM2.0 hardware nor when using GLES2.0."); public static readonly GUIContent forceWhiteAlbedo = EditorGUIUtility.TrTextContent("Force White Albedo", "Force white albedo during lighting calculations."); public static readonly GUIContent forceUpdates = EditorGUIUtility.TrTextContent("Force Updates", "Force continuous updates of runtime indirect lighting calculations."); public static readonly GUIContent filterMode = EditorGUIUtility.TrTextContent("Filter Mode"); @@ -205,6 +209,7 @@ static class Styles public static readonly GUIContent lightmapResolution = EditorGUIUtility.TrTextContent("Lightmap Resolution", "Sets the resolution in texels used per unit for objects lit by baked global illumination. The higher this value is, the more time the Editor needs to bake lighting."); public static readonly GUIContent padding = EditorGUIUtility.TrTextContent("Lightmap Padding", "Sets the separation in texels between shapes in the baked lightmap."); public static readonly GUIContent lightmapMaxSize = EditorGUIUtility.TrTextContent("Max Lightmap Size", "Sets the max size of the full lightmap Texture in pixels. Values are squared, so a setting of 1024 can produce a 1024x1024 pixel sized lightmap."); + public static readonly GUIContent lightmapSizeFixed = EditorGUIUtility.TrTextContent("Fixed Lightmap Size", "Forces all lightmap textures to use the same size. These can be no larger than Max Lightmap Size."); public static readonly GUIContent lightmapCompression = EditorGUIUtility.TrTextContent("Lightmap Compression", "Compresses baked lightmaps created using this Lighting Settings Asset. Lower quality compression reduces memory and storage requirements, at the cost of more visual artifacts. Higher quality compression requires more memory and storage, but provides better visual results."); public static readonly GUIContent tiledBaking = EditorGUIUtility.TrTextContent("Tiled baking", "Determines the tiled baking mode. Auto: Memory status triggers tiling. If Auto is not enabled, bakes may fail. Disabled: Never use tiling."); public static readonly GUIContent ambientOcclusion = EditorGUIUtility.TrTextContent("Ambient Occlusion", "Specifies whether to include ambient occlusion or not in the baked lightmap result. Enabling this results in simulating the soft shadows that occur in cracks and crevices of objects when light is reflected onto them."); @@ -245,6 +250,11 @@ static class Styles public static readonly GUIStyle labelStyle = EditorStyles.wordWrappedMiniLabel; } + private int maxDirectSamples = 1024; + private int maxIndirectSamples = 8192; + private int maxEnvironmentSamples = 2048; + private SerializedObject currentLSO; + public void OnEnable() { m_ShowRealtimeLightsSettings = new SavedBool("LightingSettings.ShowRealtimeLightsSettings", false); @@ -274,73 +284,78 @@ public void OnGUI(bool compact, bool drawAutoGenerate) EditorGUILayout.Space(); } + if (currentLSO == null || currentLSO != m_GIWorkflowMode.serializedObject) + { + currentLSO = m_GIWorkflowMode.serializedObject; + ClampMaxRanges(); + } + RealtimeLightingGUI(compact); MixedLightingGUI(compact); GeneralLightmapSettingsGUI(compact); InternalSettingsGUI(compact); } - public void UpdateSettings(SerializedObject lso) + public void UpdateSettings(SerializedObject lightingSettingsObject) { - if (lso != null) - { - m_GIWorkflowMode = lso.FindProperty("m_GIWorkflowMode"); - - //realtime GI - m_RealtimeResolution = lso.FindProperty("m_RealtimeResolution"); - m_EnableRealtimeGI = lso.FindProperty("m_EnableRealtimeLightmaps"); - m_RealtimeEnvironmentLighting = lso.FindProperty("m_RealtimeEnvironmentLighting"); - - //baked - m_EnabledBakedGI = lso.FindProperty("m_EnableBakedLightmaps"); - m_BakeBackend = lso.FindProperty("m_BakeBackend"); - m_MixedBakeMode = lso.FindProperty("m_MixedBakeMode"); - m_AlbedoBoost = lso.FindProperty("m_AlbedoBoost"); - m_IndirectOutputScale = lso.FindProperty("m_IndirectOutputScale"); - m_LightmapMaxSize = lso.FindProperty("m_LightmapMaxSize"); - m_LightmapParameters = lso.FindProperty("m_LightmapParameters"); - m_LightmapDirectionalMode = lso.FindProperty("m_LightmapsBakeMode"); - m_BakeResolution = lso.FindProperty("m_BakeResolution"); - m_Padding = lso.FindProperty("m_Padding"); - m_AmbientOcclusion = lso.FindProperty("m_AO"); - m_AOMaxDistance = lso.FindProperty("m_AOMaxDistance"); - m_CompAOExponent = lso.FindProperty("m_CompAOExponent"); - m_CompAOExponentDirect = lso.FindProperty("m_CompAOExponentDirect"); - m_LightmapCompression = lso.FindProperty("m_LightmapCompression"); - - // pvr - m_PVRSampleCount = lso.FindProperty("m_PVRSampleCount"); - m_PVRDirectSampleCount = lso.FindProperty("m_PVRDirectSampleCount"); - m_PVRBounces = lso.FindProperty("m_PVRBounces"); - m_PVRCulling = lso.FindProperty("m_PVRCulling"); - m_PVRFilteringMode = lso.FindProperty("m_PVRFilteringMode"); - m_PVRFilterTypeDirect = lso.FindProperty("m_PVRFilterTypeDirect"); - m_PVRFilterTypeIndirect = lso.FindProperty("m_PVRFilterTypeIndirect"); - m_PVRFilterTypeAO = lso.FindProperty("m_PVRFilterTypeAO"); - m_PVRDenoiserTypeDirect = lso.FindProperty("m_PVRDenoiserTypeDirect"); - m_PVRDenoiserTypeIndirect = lso.FindProperty("m_PVRDenoiserTypeIndirect"); - m_PVRDenoiserTypeAO = lso.FindProperty("m_PVRDenoiserTypeAO"); - m_PVRFilteringGaussRadiusDirect = lso.FindProperty("m_PVRFilteringGaussRadiusDirect"); - m_PVRFilteringGaussRadiusIndirect = lso.FindProperty("m_PVRFilteringGaussRadiusIndirect"); - m_PVRFilteringGaussRadiusAO = lso.FindProperty("m_PVRFilteringGaussRadiusAO"); - m_PVRFilteringAtrousPositionSigmaDirect = lso.FindProperty("m_PVRFilteringAtrousPositionSigmaDirect"); - m_PVRFilteringAtrousPositionSigmaIndirect = lso.FindProperty("m_PVRFilteringAtrousPositionSigmaIndirect"); - m_PVRFilteringAtrousPositionSigmaAO = lso.FindProperty("m_PVRFilteringAtrousPositionSigmaAO"); - m_PVREnvironmentIS = lso.FindProperty("m_PVREnvironmentImportanceSampling"); - m_PVREnvironmentSampleCount = lso.FindProperty("m_PVREnvironmentSampleCount"); - m_LightProbeSampleCountMultiplier = lso.FindProperty("m_LightProbeSampleCountMultiplier"); - - //dev debug properties - m_ExportTrainingData = lso.FindProperty("m_ExportTrainingData"); - m_TrainingDataDestination = lso.FindProperty("m_TrainingDataDestination"); - m_ForceWhiteAlbedo = lso.FindProperty("m_ForceWhiteAlbedo"); - m_ForceUpdates = lso.FindProperty("m_ForceUpdates"); - m_FilterMode = lso.FindProperty("m_FilterMode"); - m_BounceScale = lso.FindProperty("m_BounceScale"); - m_TiledBaking = lso.FindProperty("m_PVRTiledBaking"); - m_NumRaysToShootPerTexel = lso.FindProperty("m_NumRaysToShootPerTexel"); - m_RespectSceneVisibilityWhenBakingGI = lso.FindProperty("m_RespectSceneVisibilityWhenBakingGI"); - } + if (lightingSettingsObject == null) + return; + + m_GIWorkflowMode = lightingSettingsObject.FindProperty("m_GIWorkflowMode"); + + //realtime GI + m_RealtimeResolution = lightingSettingsObject.FindProperty("m_RealtimeResolution"); + m_EnableRealtimeGI = lightingSettingsObject.FindProperty("m_EnableRealtimeLightmaps"); + m_RealtimeEnvironmentLighting = lightingSettingsObject.FindProperty("m_RealtimeEnvironmentLighting"); + + //baked + m_EnabledBakedGI = lightingSettingsObject.FindProperty("m_EnableBakedLightmaps"); + m_BakeBackend = lightingSettingsObject.FindProperty("m_BakeBackend"); + m_MixedBakeMode = lightingSettingsObject.FindProperty("m_MixedBakeMode"); + m_AlbedoBoost = lightingSettingsObject.FindProperty("m_AlbedoBoost"); + m_IndirectOutputScale = lightingSettingsObject.FindProperty("m_IndirectOutputScale"); + m_LightmapMaxSize = lightingSettingsObject.FindProperty("m_LightmapMaxSize"); + m_LightmapSizeFixed = lightingSettingsObject.FindProperty("m_LightmapSizeFixed"); + m_LightmapParameters = lightingSettingsObject.FindProperty("m_LightmapParameters"); + m_LightmapDirectionalMode = lightingSettingsObject.FindProperty("m_LightmapsBakeMode"); + m_BakeResolution = lightingSettingsObject.FindProperty("m_BakeResolution"); + m_Padding = lightingSettingsObject.FindProperty("m_Padding"); + m_AmbientOcclusion = lightingSettingsObject.FindProperty("m_AO"); + m_AOMaxDistance = lightingSettingsObject.FindProperty("m_AOMaxDistance"); + m_CompAOExponent = lightingSettingsObject.FindProperty("m_CompAOExponent"); + m_CompAOExponentDirect = lightingSettingsObject.FindProperty("m_CompAOExponentDirect"); + m_LightmapCompression = lightingSettingsObject.FindProperty("m_LightmapCompression"); + + // pvr + m_PVRSampleCount = lightingSettingsObject.FindProperty("m_PVRSampleCount"); + m_PVRDirectSampleCount = lightingSettingsObject.FindProperty("m_PVRDirectSampleCount"); + m_PVRBounces = lightingSettingsObject.FindProperty("m_PVRBounces"); + m_PVRCulling = lightingSettingsObject.FindProperty("m_PVRCulling"); + m_PVRFilteringMode = lightingSettingsObject.FindProperty("m_PVRFilteringMode"); + m_PVRFilterTypeDirect = lightingSettingsObject.FindProperty("m_PVRFilterTypeDirect"); + m_PVRFilterTypeIndirect = lightingSettingsObject.FindProperty("m_PVRFilterTypeIndirect"); + m_PVRFilterTypeAO = lightingSettingsObject.FindProperty("m_PVRFilterTypeAO"); + m_PVRDenoiserTypeDirect = lightingSettingsObject.FindProperty("m_PVRDenoiserTypeDirect"); + m_PVRDenoiserTypeIndirect = lightingSettingsObject.FindProperty("m_PVRDenoiserTypeIndirect"); + m_PVRDenoiserTypeAO = lightingSettingsObject.FindProperty("m_PVRDenoiserTypeAO"); + m_PVRFilteringGaussRadiusDirect = lightingSettingsObject.FindProperty("m_PVRFilteringGaussRadiusDirect"); + m_PVRFilteringGaussRadiusIndirect = lightingSettingsObject.FindProperty("m_PVRFilteringGaussRadiusIndirect"); + m_PVRFilteringGaussRadiusAO = lightingSettingsObject.FindProperty("m_PVRFilteringGaussRadiusAO"); + m_PVRFilteringAtrousPositionSigmaDirect = lightingSettingsObject.FindProperty("m_PVRFilteringAtrousPositionSigmaDirect"); + m_PVRFilteringAtrousPositionSigmaIndirect = lightingSettingsObject.FindProperty("m_PVRFilteringAtrousPositionSigmaIndirect"); + m_PVRFilteringAtrousPositionSigmaAO = lightingSettingsObject.FindProperty("m_PVRFilteringAtrousPositionSigmaAO"); + m_PVREnvironmentIS = lightingSettingsObject.FindProperty("m_PVREnvironmentImportanceSampling"); + m_PVREnvironmentSampleCount = lightingSettingsObject.FindProperty("m_PVREnvironmentSampleCount"); + m_LightProbeSampleCountMultiplier = lightingSettingsObject.FindProperty("m_LightProbeSampleCountMultiplier"); + + //dev debug properties + m_ExportTrainingData = lightingSettingsObject.FindProperty("m_ExportTrainingData"); + m_TrainingDataDestination = lightingSettingsObject.FindProperty("m_TrainingDataDestination"); + m_ForceWhiteAlbedo = lightingSettingsObject.FindProperty("m_ForceWhiteAlbedo"); + m_ForceUpdates = lightingSettingsObject.FindProperty("m_ForceUpdates"); + m_FilterMode = lightingSettingsObject.FindProperty("m_FilterMode"); + m_BounceScale = lightingSettingsObject.FindProperty("m_BounceScale"); + m_RespectSceneVisibilityWhenBakingGI = lightingSettingsObject.FindProperty("m_RespectSceneVisibilityWhenBakingGI"); } // Private methods @@ -364,11 +379,6 @@ void RealtimeLightingGUI(bool compact) EditorGUILayout.PropertyField(m_EnableRealtimeGI, Styles.useRealtimeGI); - if (m_EnableRealtimeGI.boolValue && PlayerHasSM20Support()) - { - EditorGUILayout.HelpBox(Styles.noRealtimeGIInSM2AndGLES2.text, MessageType.Warning); - } - EditorGUI.indentLevel++; bool bakedGISupported = SupportedRenderingFeatures.IsLightmapBakeTypeSupported(LightmapBakeType.Baked); @@ -507,21 +517,27 @@ void GeneralLightmapSettingsGUI(bool compact) { BakeBackendGUI(); - if (lightmapperSupported && !m_BakeBackend.hasMultipleDifferentValues) + if (lightmapperSupported) { EditorGUI.indentLevel++; - EditorGUILayout.PropertyField(m_PVRCulling, Styles.culling); + bool iterative = m_GIWorkflowMode.intValue == (int)Lightmapping.GIWorkflowMode.Iterative; + if (iterative) + { + EditorGUILayout.PropertyField(m_PVRCulling, Styles.culling); + } EditorGUILayout.PropertyField(m_PVREnvironmentIS, Styles.environmentImportanceSampling); - int sampleCount = EditorGUILayout.DelayedIntField(Styles.directSampleCount, m_PVRDirectSampleCount.intValue); - m_PVRDirectSampleCount.intValue = sampleCount; - sampleCount = EditorGUILayout.DelayedIntField(Styles.indirectSampleCount, m_PVRSampleCount.intValue); - m_PVRSampleCount.intValue = sampleCount; - sampleCount = EditorGUILayout.DelayedIntField(Styles.environmentSampleCount, m_PVREnvironmentSampleCount.intValue); - m_PVREnvironmentSampleCount.intValue = sampleCount; + MultiEditableLogarithmicIntSlider(m_PVRDirectSampleCount, Styles.directSampleCount, 1, maxDirectSamples, 1, 1 << 30); + MultiEditableLogarithmicIntSlider(m_PVRSampleCount, Styles.indirectSampleCount, 1, maxIndirectSamples, 1, 1 << 30); + MultiEditableLogarithmicIntSlider(m_PVREnvironmentSampleCount, Styles.environmentSampleCount, 1, maxEnvironmentSamples, 1, 1 << 30); + maxDirectSamples = (int)Mathf.ClosestPowerOfTwo(Math.Max(maxDirectSamples, m_PVRDirectSampleCount.intValue)); + maxIndirectSamples = (int)Mathf.ClosestPowerOfTwo(Math.Max(maxIndirectSamples, m_PVRSampleCount.intValue)); + +#pragma warning disable 618 using (new EditorGUI.DisabledScope(EditorSettings.useLegacyProbeSampleCount)) +#pragma warning restore 618 { EditorGUILayout.PropertyField(m_LightProbeSampleCountMultiplier, Styles.probeSampleCountMultiplier); } @@ -628,6 +644,8 @@ void GeneralLightmapSettingsGUI(bool compact) EditorGUILayout.IntPopup(m_LightmapMaxSize, Styles.lightmapMaxSizeStrings, Styles.lightmapMaxSizeValues, Styles.lightmapMaxSize); + EditorGUILayout.PropertyField(m_LightmapSizeFixed, Styles.lightmapSizeFixed); + EditorGUILayout.IntPopup(m_LightmapCompression, Styles.lightmapCompressionStrings, Styles.lightmapCompressionValues, Styles.lightmapCompression); EditorGUILayout.PropertyField(m_AmbientOcclusion, Styles.ambientOcclusion); @@ -689,10 +707,13 @@ void InternalSettingsGUI(bool compact) if (m_ShowInternalSettings.value) { + bool enableRealtimeGI = (m_EnableRealtimeGI.boolValue && !m_EnableRealtimeGI.hasMultipleDifferentValues); EditorGUI.indentLevel++; - - EditorGUILayout.PropertyField(m_ForceWhiteAlbedo, Styles.forceWhiteAlbedo); - EditorGUILayout.PropertyField(m_ForceUpdates, Styles.forceUpdates); + if (enableRealtimeGI) + { + EditorGUILayout.PropertyField(m_ForceWhiteAlbedo, Styles.forceWhiteAlbedo); + EditorGUILayout.PropertyField(m_ForceUpdates, Styles.forceUpdates); + } EditorGUILayout.PropertyField(m_ExportTrainingData, Styles.exportTrainingData); @@ -704,10 +725,10 @@ void InternalSettingsGUI(bool compact) } EditorGUILayout.PropertyField(m_FilterMode, Styles.filterMode); - EditorGUILayout.Slider(m_BounceScale, 0.0f, 10.0f, Styles.bounceScale); - if (m_BakeBackend.intValue == (int)LightingSettings.Lightmapper.ProgressiveGPU) - EditorGUILayout.IntPopup(m_TiledBaking, Styles.tiledBakingStrings, Styles.tiledBakingValues, Styles.tiledBaking); - + if (enableRealtimeGI) + { + EditorGUILayout.Slider(m_BounceScale, 0.0f, 10.0f, Styles.bounceScale); + } EditorGUI.indentLevel--; EditorGUILayout.Space(); } @@ -718,38 +739,17 @@ void InternalSettingsGUI(bool compact) // Helper methods - static bool PlayerHasSM20Support() - { - var apis = PlayerSettings.GetGraphicsAPIs(EditorUserBuildSettings.activeBuildTarget); - bool hasSM20Api = apis.Contains(UnityEngine.Rendering.GraphicsDeviceType.OpenGLES2); - return hasSM20Api; - } - static void DrawPropertyFieldWithPostfixLabel(SerializedProperty property, GUIContent label, GUIContent postfixLabel) { const float minimumWidth = 170.0f; const float postfixLabelWidth = 80.0f; - switch (property.propertyType) - { - case SerializedPropertyType.Float: - DrawFieldWithPostfixLabel( - (Rect propertyRect) => { property.floatValue = EditorGUI.FloatField(propertyRect, label, property.floatValue); }, - postfixLabel, - EditorStyles.numberField, - minimumWidth, - postfixLabelWidth); - break; - - case SerializedPropertyType.Integer: - DrawFieldWithPostfixLabel( - (Rect propertyRect) => { property.intValue = EditorGUI.IntField(propertyRect, label, property.intValue); }, - postfixLabel, - EditorStyles.numberField, - minimumWidth, - postfixLabelWidth); - break; - } + DrawFieldWithPostfixLabel( + (Rect propertyRect) => { EditorGUI.PropertyField(propertyRect, property, label); }, + postfixLabel, + EditorStyles.numberField, + minimumWidth, + postfixLabelWidth); } static void DrawFilterSettingField(SerializedProperty gaussSetting, @@ -965,5 +965,31 @@ void DrawDenoiserTypeDropdown(SerializedProperty prop, GUIContent label, Denoise } EditorGUI.EndProperty(); } + + int MultiEditableLogarithmicIntSlider(SerializedProperty property, GUIContent style, int min, int max, int textFieldMin, int textFieldMax) + { + if (property.hasMultipleDifferentValues) + { + EditorGUI.BeginChangeCheck(); + EditorGUI.showMixedValue = true; + + int newValue = EditorGUILayout.LogarithmicIntSlider(style, property.intValue, min, max, 2, textFieldMin, textFieldMax); + if (EditorGUI.EndChangeCheck()) + property.intValue = newValue; + + EditorGUI.showMixedValue = false; + } + else + property.intValue = EditorGUILayout.LogarithmicIntSlider(style, property.intValue, min, max, 2, textFieldMin, textFieldMax); + + return property.intValue; + } + + internal void ClampMaxRanges() + { + maxDirectSamples = Mathf.Max(m_PVRDirectSampleCount.intValue, 1024); + maxIndirectSamples = Mathf.Max(m_PVRSampleCount.intValue, 8192); + maxEnvironmentSamples = Mathf.Max(m_PVREnvironmentSampleCount.intValue, 2048); + } } } diff --git a/Editor/Mono/Inspector/MaskFieldDropdown.cs b/Editor/Mono/Inspector/MaskFieldDropdown.cs index 92abeb52f9..9806ae1361 100644 --- a/Editor/Mono/Inspector/MaskFieldDropdown.cs +++ b/Editor/Mono/Inspector/MaskFieldDropdown.cs @@ -21,8 +21,8 @@ internal class MaskFieldDropDown : PopupWindowContent SelectionModes[] m_SelectionMatch; string[] m_OptionNames; int[] m_flagValues; - uint[] m_OptionMaskValues; - uint[] m_SelectionMaskValues; + int[] m_OptionMaskValues; + int[] m_SelectionMaskValues; int m_AllLayersMask = 0; @@ -46,15 +46,12 @@ public MaskFieldDropDown(string[] optionNames, int[] flagValues, int[] optionMas // these are not flag values, i.e. 1, 2, 4... // but the mask & flagValue[0..n] for each possible flag value // this is to ensure backwards compatibility with everything that uses MaskFieldGUI.GetSelectedValueForControl - m_OptionMaskValues = Array.ConvertAll(optionMaskValues, x=>(uint)x); + m_OptionMaskValues = (int[])optionMaskValues.Clone(); m_OptionNames = (string[])optionNames.Clone(); m_flagValues = new int[optionNames.Length]; - if (flagValues == null || flagValues.Length == optionNames.Length - 2) - { - m_flagValues[0] = 0; - m_flagValues[1] = -1; - } + m_flagValues[0] = 0; + m_flagValues[1] = -1; if (flagValues == null) { @@ -63,11 +60,23 @@ public MaskFieldDropDown(string[] optionNames, int[] flagValues, int[] optionMas } else { - Array.Copy(flagValues, 0, m_flagValues, optionNames.Length - flagValues.Length, flagValues.Length); + int index = 0; + int length = flagValues.Length; + + if (flagValues[0] == 0) + { + index = 1; + length--; + } + + if (flagValues[flagValues.Length - 1] == ~0) + length--; + + Array.Copy(flagValues, index, m_flagValues, 2, length); } m_SelectionMatch = new SelectionModes[] { SelectionModes.All }; - m_SelectionMaskValues = new uint[] { (uint)mask }; + m_SelectionMaskValues = new int[] { mask }; m_SingleSelection = true; m_MaskChangeCallback = maskChangeCallback; @@ -78,6 +87,12 @@ public MaskFieldDropDown(string[] optionNames, int[] flagValues, int[] optionMas m_AllLayersMask |= val; } + public void UpdateMaskValues(int mask, int[] optionMaskValues) + { + m_OptionMaskValues = (int[])optionMaskValues.Clone(); + m_SelectionMaskValues = new int[] { mask }; + } + public override Vector2 GetWindowSize() { var rowCount = m_OptionNames[0] == "Nothing" ? m_OptionNames.Length : m_OptionNames.Length + 2; @@ -116,9 +131,10 @@ void DrawGUIForArrays() for (int i = 0; i < m_OptionNames.Length; i++) { bool toggleVal = (m_SelectionMaskValues[0] & m_OptionMaskValues[i]) == m_OptionMaskValues[i]; - if (m_SelectionMaskValues[0] != 0 && i == 0) + if ((m_SelectionMaskValues[0] != 0 && i == 0) || m_SelectionMaskValues[0] != -1 && i == 1) toggleVal = false; - if ((m_SelectionMaskValues[0] == int.MaxValue || m_SelectionMaskValues[0] == m_AllLayersMask) && i == 1) + + if((m_SelectionMaskValues[0] == m_AllLayersMask) && i == 1) toggleVal = true; var guiRect = EditorGUILayout.GetControlRect(false, EditorGUI.kSingleLineHeight); @@ -132,7 +148,7 @@ void DrawGUIForArrays() { m_SelectionMaskValues[0] = m_OptionMaskValues[i]; var oldMaskValues = (uint[])m_OptionMaskValues.Clone(); - RecalculateMasks(); + MaskFieldGUI.CalculateMaskValues(m_SelectionMaskValues[0], m_flagValues, ref m_OptionMaskValues); // If all flag options are selected the mask becomes everythingValue to be consistent with the "Everything" option if (oldMaskValues[i] == (uint)m_AllLayersMask) @@ -143,14 +159,6 @@ void DrawGUIForArrays() } } - void RecalculateMasks() - { - uint selectedValue = m_SelectionMaskValues[0] == ~0u ? (uint)m_AllLayersMask : m_SelectionMaskValues[0]; - - for (var flagIndex = 2; flagIndex < m_flagValues.Length; flagIndex++) - m_OptionMaskValues[flagIndex] = selectedValue ^ (uint)m_flagValues[flagIndex]; - } - public override void OnGUI(Rect rect) { if (Event.current.type == EventType.MouseMove) @@ -172,14 +180,14 @@ public override void OnGUI(Rect rect) return; var isNothing = m_SerializedProperty.intValue == 0; - var isEverything = m_SerializedProperty.intValue == int.MaxValue; + var isEverything = m_SerializedProperty.intValue == -1; GUILayout.Space(2); var toggleStyle = m_SerializedProperty.hasMultipleDifferentValues && isNothing ? Styles.menuItemMixed : Styles.menuItem; DrawEverythingOrNothingSelectedToggle(isNothing, "Nothing", toggleStyle, 0); toggleStyle = m_SerializedProperty.hasMultipleDifferentValues && isEverything ? Styles.menuItemMixed : Styles.menuItem; - DrawEverythingOrNothingSelectedToggle(isEverything, "Everything", toggleStyle, int.MaxValue); + DrawEverythingOrNothingSelectedToggle(isEverything, "Everything", toggleStyle, -1); for (int i = 0; i < m_OptionNames.Length; i++) { @@ -206,7 +214,7 @@ void ChangeMaskValues(int maskIndex, bool add) { var selectionCount = m_SerializedProperty.serializedObject.targetObjects.Length; - m_SelectionMaskValues = new uint[selectionCount]; + m_SelectionMaskValues = new int[selectionCount]; for (int i = 0; i < selectionCount; i++) { var serializedObject = new SerializedObject(m_SerializedProperty.serializedObject.targetObjects[i]); @@ -232,9 +240,9 @@ void ChangeMaskValues(int maskIndex, bool add) } if (property.intValue == m_AllLayersMask) - property.intValue = int.MaxValue; + property.intValue = -1; - m_SelectionMaskValues[i] = (uint)property.intValue; + m_SelectionMaskValues[i] = property.intValue; serializedObject.ApplyModifiedProperties(); } @@ -248,11 +256,10 @@ public override void OnOpen() { m_SelectionMatch = new SelectionModes[m_LayerCount]; GetMultiSelectionValues(m_SerializedProperty, out m_SelectionMaskValues, out m_SelectionMatch, m_LayerCount); - int[] definedLayers = new int[m_SelectionMaskValues.Length]; - TagManager.GetDefinedLayers(ref m_OptionNames, ref definedLayers); - m_OptionMaskValues = definedLayers.Select(v => (uint)v).ToArray(); + m_OptionMaskValues = new int[m_SelectionMaskValues.Length]; + TagManager.GetDefinedLayers(ref m_OptionNames, ref m_OptionMaskValues); for (int i = 0; i < m_OptionMaskValues.Length; i++) - m_AllLayersMask |= (int)m_OptionMaskValues[i]; + m_AllLayersMask |= m_OptionMaskValues[i]; } for (int i = 0; i < m_OptionNames.Length; i++) @@ -286,7 +293,7 @@ internal class StaticFieldDropdown : PopupWindowContent SelectionModes[] m_SelectionMatch; string[] m_OptionNames; - uint[] m_SelectionMaskValues; + int[] m_SelectionMaskValues; int m_OptionCount; int m_AllLayersMask; List m_FunctioningOptions = new List(); @@ -365,12 +372,12 @@ public override void OnGUI(Rect rect) void ChangeMaskValues(int maskIndex, bool add) { var selectionCount = m_SerializedProperty.serializedObject.targetObjects.Length; - m_SelectionMaskValues = new uint[selectionCount]; + m_SelectionMaskValues = new int[selectionCount]; SceneModeUtility.SetStaticFlags(m_SerializedProperty.serializedObject.targetObjects, maskIndex, add); for (int i = 0; i < selectionCount; i++) - m_SelectionMaskValues[i] = (uint)m_SerializedProperty.intValue; + m_SelectionMaskValues[i] = m_SerializedProperty.intValue; m_SerializedProperty.serializedObject.ApplyModifiedProperties(); m_SerializedProperty.serializedObject.SetIsDifferentCacheDirty(); @@ -490,32 +497,32 @@ internal static void GetSingleSelectionValues(int maskValue, out SelectionModes[ } } - internal static void GetMultiSelectionValues(SerializedProperty serializedProperty, out uint[] selectionMaskValues, out SelectionModes[] selectionMatch, int layerCount) + internal static void GetMultiSelectionValues(SerializedProperty serializedProperty, out int[] selectionMaskValues, out SelectionModes[] selectionMatch, int layerCount) { var selectionCount = serializedProperty.serializedObject.targetObjects.Length; - selectionMaskValues = new uint[selectionCount]; + selectionMaskValues = new int[selectionCount]; selectionMatch = new SelectionModes[layerCount]; for (int i = 0; i < selectionCount; i++) { var serializedObject = new SerializedObject(serializedProperty.serializedObject.targetObjects[i]); var property = serializedObject.FindProperty(serializedProperty.propertyPath); - selectionMaskValues[i] = (uint)property.intValue; + selectionMaskValues[i] = property.intValue; } if (selectionCount == 1) { - GetSingleSelectionValues((int)selectionMaskValues[0], out selectionMatch, layerCount); + GetSingleSelectionValues(selectionMaskValues[0], out selectionMatch, layerCount); return; } uint[] firstSelected; - GetSelected((int)selectionMaskValues[0], out firstSelected, layerCount); + GetSelected(selectionMaskValues[0], out firstSelected, layerCount); for (int i = 1; i < selectionCount; i++) { uint[] secondSelected; - GetSelected((int)selectionMaskValues[i], out secondSelected, layerCount); + GetSelected(selectionMaskValues[i], out secondSelected, layerCount); for (int j = 0; j < layerCount; j++) { diff --git a/Editor/Mono/Inspector/MaterialEditor.cs b/Editor/Mono/Inspector/MaterialEditor.cs index 3c5f7b4ea6..43056cd1b9 100644 --- a/Editor/Mono/Inspector/MaterialEditor.cs +++ b/Editor/Mono/Inspector/MaterialEditor.cs @@ -1385,17 +1385,27 @@ internal static Color ColorPropertyInternal(Rect position, MaterialProperty prop } public Vector4 VectorProperty(MaterialProperty prop, string label) + { + return VectorProperty(prop, new GUIContent(label)); + } + + public Vector4 VectorProperty(MaterialProperty prop, GUIContent label) { Rect r = GetPropertyRect(prop, label, true); return VectorProperty(r, prop, label); } - + public Vector4 VectorProperty(Rect position, MaterialProperty prop, string label) + { + return VectorPropertyInternal(position, prop, new GUIContent(label)); + } + + public Vector4 VectorProperty(Rect position, MaterialProperty prop, GUIContent label) { return VectorPropertyInternal(position, prop, label); } - internal static Vector4 VectorPropertyInternal(in Rect position, in MaterialProperty prop, in string label) + internal static Vector4 VectorPropertyInternal(in Rect position, in MaterialProperty prop, in GUIContent label) { BeginProperty(position, prop); @@ -1563,6 +1573,11 @@ public Rect GetTexturePropertyCustomArea(Rect position) } public Texture TextureProperty(Rect position, MaterialProperty prop, string label) + { + return TextureProperty(position, prop, new GUIContent(label, string.Empty)); + } + + public Texture TextureProperty(Rect position, MaterialProperty prop, GUIContent label) { bool scaleOffset = ((prop.flags & MaterialProperty.PropFlags.NoScaleOffset) == 0); return TextureProperty(position, prop, label, scaleOffset); @@ -1572,14 +1587,19 @@ public Texture TextureProperty(Rect position, MaterialProperty prop, string labe { return TextureProperty(position, prop, label, string.Empty, scaleOffset); } - + public Texture TextureProperty(Rect position, MaterialProperty prop, string label, string tooltip, bool scaleOffset) + { + return TextureProperty(position, prop, new GUIContent(label, tooltip), scaleOffset); + } + + public Texture TextureProperty(Rect position, MaterialProperty prop, GUIContent label, bool scaleOffset) { Rect scopeRect = new Rect(position.x, position.y, position.width, EditorGUI.lineHeight); BeginProperty(scopeRect, prop); // Label - EditorGUI.PrefixLabel(position, new GUIContent(label, tooltip)); + EditorGUI.PrefixLabel(position, label); // Texture slot position.height = GetTextureFieldHeight(); @@ -2030,10 +2050,10 @@ internal void DefaultShaderPropertyInternal(Rect position, MaterialProperty prop ColorPropertyInternal(position, prop, label); break; case MaterialProperty.PropType.Texture: // textures - TextureProperty(position, prop, label.text); + TextureProperty(position, prop, label); break; case MaterialProperty.PropType.Vector: // vectors - VectorProperty(position, prop, label.text); + VectorProperty(position, prop, label); break; default: GUI.Label(position, "Unknown property type: " + prop.name + ": " + (int)prop.type); @@ -2535,12 +2555,12 @@ private bool DoReflectionProbePicker(out Rect buttonRect) public void DefaultPreviewSettingsGUI() { - if (!ShaderUtil.hardwareSupportsRectRenderTexture) + var mat = target as Material; + if (!SupportsRenderingPreview(mat)) return; Init(); - var mat = target as Material; var viewType = GetPreviewType(mat); if (targets.Length > 1 || viewType == PreviewType.Mesh) { @@ -2561,12 +2581,12 @@ public void DefaultPreviewSettingsGUI() public sealed override Texture2D RenderStaticPreview(string assetPath, Object[] subAssets, int width, int height) { - if (!ShaderUtil.hardwareSupportsRectRenderTexture) + var mat = target as Material; + if (!SupportsRenderingPreview(mat)) return null; Init(); - var previewRenderUtility = GetPreviewRendererUtility(); EditorUtility.SetCameraAnimateMaterials(previewRenderUtility.camera, true); @@ -2743,9 +2763,25 @@ public override void OnPreviewGUI(Rect r, GUIStyle background) DefaultPreviewGUI(r, background); } - public void DefaultPreviewGUI(Rect r, GUIStyle background) + private static bool SupportsRenderingPreview(Material material) { if (!ShaderUtil.hardwareSupportsRectRenderTexture) + return false; + + if (material == null) + return false; + + var assetPath = AssetDatabase.GetAssetPath(material); + if (assetPath.EndsWith(".vfx", StringComparison.InvariantCultureIgnoreCase)) + return false; + + return true; + } + + public void DefaultPreviewGUI(Rect r, GUIStyle background) + { + var mat = target as Material; + if (!SupportsRenderingPreview(mat)) { if (Event.current.type == EventType.Repaint) EditorGUI.DropShadowLabel(new Rect(r.x, r.y, r.width, 40), "Material preview \nnot available"); @@ -2754,7 +2790,6 @@ public void DefaultPreviewGUI(Rect r, GUIStyle background) Init(); - var mat = target as Material; var viewType = GetPreviewType(mat); if (DoesPreviewAllowRotation(viewType)) @@ -2903,6 +2938,7 @@ internal void HandleSkybox(GameObject go, Event evt) evt.Use(); } else + { switch (evt.type) { case EventType.DragUpdated: @@ -2915,14 +2951,13 @@ internal void HandleSkybox(GameObject go, Event evt) applyAndConsumeEvent = true; break; } + } if (applyAndConsumeEvent) { - if (s_OriginalMaterial == null) - { - Undo.RecordObject(FindObjectOfType(), Styles.undoAssignSkyboxMaterial); - s_OriginalMaterial = RenderSettings.skybox; - } + Undo.RecordObject(RenderSettings.GetRenderSettings(), Styles.undoAssignSkyboxMaterial); + if (s_OriginalMaterial == null) s_OriginalMaterial = RenderSettings.skybox; + RenderSettings.skybox = target as Material; if (evt.type == EventType.DragPerform) s_OriginalMaterial = null; @@ -3045,6 +3080,7 @@ internal static void AddAdditionalMaterialMenuItems(GenericMenu menu) menu.AddItem(new GUIContent("Create Variant for Renderer"), false, () => { var directory = "Assets/Materials"; + Directory.CreateDirectory(directory); var assetPath = AssetDatabase.GetAssetPath(material); var assetName = Path.GetFileNameWithoutExtension(assetPath) + " Variant"; var package = PackageManager.PackageInfo.FindForAssetPath(assetPath); @@ -3053,7 +3089,6 @@ internal static void AddAdditionalMaterialMenuItems(GenericMenu menu) var path = AssetDatabase.GenerateUniqueAssetPath(Path.Combine(directory, assetName + ".mat")); var variant = new Material(material) { name = assetName, parent = material }; - Directory.CreateDirectory(directory); AssetDatabase.CreateAsset(variant, path); foreach (var renderer in renderers) diff --git a/Editor/Mono/Inspector/MaterialPropertyDrawer.cs b/Editor/Mono/Inspector/MaterialPropertyDrawer.cs index e0d336a28e..b2e8c8f3dc 100644 --- a/Editor/Mono/Inspector/MaterialPropertyDrawer.cs +++ b/Editor/Mono/Inspector/MaterialPropertyDrawer.cs @@ -84,7 +84,7 @@ internal static void InvalidatePropertyCache(Shader shader) var toDelete = new List(); foreach (string key in s_PropertyHandlers.Keys) { - if (key.StartsWith(keyStart)) + if (key.StartsWith(keyStart, StringComparison.Ordinal)) toDelete.Add(key); } foreach (string key in toDelete) diff --git a/Editor/Mono/Inspector/MeshRendererEditor.cs b/Editor/Mono/Inspector/MeshRendererEditor.cs index bbbdd77f84..a9af8cea0d 100644 --- a/Editor/Mono/Inspector/MeshRendererEditor.cs +++ b/Editor/Mono/Inspector/MeshRendererEditor.cs @@ -5,6 +5,7 @@ using UnityEngine; using System.Linq; using UnityEngine.Rendering; +using System.Collections.Generic; namespace UnityEditor { @@ -47,7 +48,6 @@ private void LightingDataUpdatedRepaint() Repaint(); } } - public override void OnInspectorGUI() { serializedObject.Update(); @@ -61,7 +61,11 @@ public override void OnInspectorGUI() displayMaterialWarning = mf != null && mf.sharedMesh != null && m_Materials.arraySize > mf.sharedMesh.subMeshCount; } - using (new EditorGUI.DisabledScope(((MeshRenderer)serializedObject.targetObject).GetComponent() != null)) + // Disable Materials menu for the legacy Tree objects, see Fogbugz case: 1283092 + Tree treeComponent = ((MeshRenderer)serializedObject.targetObject).GetComponent(); + bool hasTreeComponent = treeComponent != null; + bool isSpeedTree = hasTreeComponent && treeComponent.data == null; // SpeedTrees always have the Tree component, but have null 'data' property + using (new EditorGUI.DisabledScope(hasTreeComponent && !isSpeedTree)) { DrawMaterials(); } diff --git a/Editor/Mono/Inspector/MinMaxCurvePropertyDrawer.cs b/Editor/Mono/Inspector/MinMaxCurvePropertyDrawer.cs index be443ca486..f28402c69e 100644 --- a/Editor/Mono/Inspector/MinMaxCurvePropertyDrawer.cs +++ b/Editor/Mono/Inspector/MinMaxCurvePropertyDrawer.cs @@ -262,7 +262,7 @@ PropertyField PrepareProperty(SerializedProperty prop, string label, VisualEleme var region = new IMGUIContainer(() => { - var label = new GUIContent(property.name); + var label = new GUIContent(preferredLabel); var rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight); EditorGUI.LabelField(rect, label); rect = EditorGUI.PrefixLabel(rect, label); @@ -296,7 +296,7 @@ PropertyField PrepareProperty(SerializedProperty prop, string label, VisualEleme if (state == MinMaxCurveState.k_Scalar) { label.ResetPositionProperties(); - label.text = property.name; + label.text = preferredLabel; } else { diff --git a/Editor/Mono/Inspector/MinMaxGradientPropertyDrawer.cs b/Editor/Mono/Inspector/MinMaxGradientPropertyDrawer.cs index 91f46dade6..a1512e55e8 100644 --- a/Editor/Mono/Inspector/MinMaxGradientPropertyDrawer.cs +++ b/Editor/Mono/Inspector/MinMaxGradientPropertyDrawer.cs @@ -135,7 +135,9 @@ public override VisualElement CreatePropertyGUI(SerializedProperty property) { Init(property); - return new MinMaxGradientField(m_Property, property.localizedDisplayName); + var field = new MinMaxGradientField(m_Property, preferredLabel); + PropertyField.ConfigureFieldStyles(field); + return field; } } } diff --git a/Editor/Mono/Inspector/MonoScriptInspector.cs b/Editor/Mono/Inspector/MonoScriptInspector.cs index 557cde58be..d3709e92ac 100644 --- a/Editor/Mono/Inspector/MonoScriptInspector.cs +++ b/Editor/Mono/Inspector/MonoScriptInspector.cs @@ -223,7 +223,8 @@ public override void OnInspectorGUI() rect.x = 0; rect.y -= 3; rect.width = GUIClip.visibleRect.width + 1; - GUI.Box(rect, m_CachedPreview, m_TextStyle); + GUI.Box(rect, ""); + EditorGUI.SelectableLabel(rect, m_CachedPreview.text, m_TextStyle); } GUI.enabled = enabledTemp; } diff --git a/Editor/Mono/Inspector/NotSupportedOnRenderPipelineInspector.cs b/Editor/Mono/Inspector/NotSupportedOnRenderPipelineInspector.cs index 27bce1200f..6426e2f3cb 100644 --- a/Editor/Mono/Inspector/NotSupportedOnRenderPipelineInspector.cs +++ b/Editor/Mono/Inspector/NotSupportedOnRenderPipelineInspector.cs @@ -12,5 +12,10 @@ public override VisualElement CreateInspectorGUI() { return new HelpBox("This component is not supported on the currently active render pipeline.", HelpBoxMessageType.Warning); } + + public override void OnInspectorGUI() + { + EditorGUILayout.HelpBox("This component is not supported on the currently active render pipeline.", MessageType.Warning); + } } } diff --git a/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs b/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs index fe04dd224a..62215268f6 100644 --- a/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs +++ b/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs @@ -50,9 +50,6 @@ static Styles() class SettingsContent { - public static readonly GUIContent frameTimingStatsWebGLWarning = EditorGUIUtility.TrTextContent("Frame timing stats are supported in WebGL 2 only. Uncheck 'Automatic Graphics API' if it is set and remove the WebGL 1 API."); - public static readonly GUIContent lightmapEncodingWebGLWarning = EditorGUIUtility.TrTextContent("High quality lightmap encoding requires WebGL 2. Navigate to 'WebGL Player Settings', uncheck 'Automatic Graphics API' if it is set and remove the WebGL 1 API."); - public static readonly GUIContent hdrCubemapEncodingWebGLWarning = EditorGUIUtility.TrTextContent("High quality HDR cubemap encoding requires WebGL 2. Navigate to 'WebGL Player Settings', uncheck 'Automatic Graphics API' if it is set and remove the WebGL 1 API."); public static readonly GUIContent colorSpaceAndroidWarning = EditorGUIUtility.TrTextContent("Linear colorspace requires OpenGL ES 3.0 or Vulkan, remove OpenGL ES 2 API from the list. Blit Type for non-SRP projects must be Always Blit or Auto."); public static readonly GUIContent colorSpaceWebGLWarning = EditorGUIUtility.TrTextContent("Linear colorspace requires WebGL 2, uncheck 'Automatic Graphics API' to remove WebGL 1 API. WARNING: If DXT sRGB is not supported by the browser, texture will be decompressed"); public static readonly GUIContent colorSpaceIOSWarning = EditorGUIUtility.TrTextContent("Linear colorspace requires Metal API only. Uncheck 'Automatic Graphics API' and remove OpenGL ES 2/3 APIs."); @@ -96,6 +93,7 @@ class SettingsContent public static readonly GUIContent platformShaderChunkCount = EditorGUIUtility.TrTextContent("Chunk count", "Use this setting to control how much memory is used when loading shader variants."); public static readonly GUIContent bakeCollisionMeshes = EditorGUIUtility.TrTextContent("Prebake Collision Meshes*", "Bake collision data into the meshes on build time"); + public static readonly GUIContent dedicatedServerOptimizations = EditorGUIUtility.TrTextContent("Enable Dedicated Server optimizations", "Performs additional optimizations on Dedicated Server builds."); public static readonly GUIContent keepLoadedShadersAlive = EditorGUIUtility.TrTextContent("Keep Loaded Shaders Alive*", "Prevents shaders from being unloaded"); public static readonly GUIContent preloadedAssets = EditorGUIUtility.TrTextContent("Preloaded Assets*", "Assets to load at start up in the player and kept alive until the player terminates"); public static readonly GUIContent stripEngineCode = EditorGUIUtility.TrTextContent("Strip Engine Code*", "Strip Unused Engine Code - Note that byte code stripping of managed assemblies is always enabled for the IL2CPP scripting backend."); @@ -146,6 +144,7 @@ class SettingsContent public static readonly GUIContent logObjCUncaughtExceptions = EditorGUIUtility.TrTextContent("Log Obj-C Uncaught Exceptions*"); public static readonly GUIContent enableCrashReportAPI = EditorGUIUtility.TrTextContent("Enable CrashReport API*"); public static readonly GUIContent activeColorSpace = EditorGUIUtility.TrTextContent("Color Space*"); + public static readonly GUIContent unsupportedMSAAFallback = EditorGUIUtility.TrTextContent("MSAA Fallback"); public static readonly GUIContent colorGamut = EditorGUIUtility.TrTextContent("Color Gamut*"); public static readonly GUIContent colorGamutForMac = EditorGUIUtility.TrTextContent("Color Gamut For Mac*"); public static readonly GUIContent metalForceHardShadows = EditorGUIUtility.TrTextContent("Force hard shadows on Metal*"); @@ -189,12 +188,13 @@ class SettingsContent EditorGUIUtility.TrTextContent("Always allowed"), }; public static readonly GUIContent iOSURLSchemes = EditorGUIUtility.TrTextContent("Supported URL schemes*"); - public static readonly GUIContent iOSExternalAudioInputNotSupported = EditorGUIUtility.TrTextContent("Audio input from Bluetooth microphones is not supported when Mute Other Audio Sources is off."); + public static readonly GUIContent iOSExternalAudioInputNotSupported = EditorGUIUtility.TrTextContent("Audio input from Bluetooth microphones is not supported when Mute Other Audio Sources and Prepare iOS for Recording are both off."); public static readonly GUIContent aotOptions = EditorGUIUtility.TrTextContent("AOT Compilation Options*"); public static readonly GUIContent require31 = EditorGUIUtility.TrTextContent("Require ES3.1"); public static readonly GUIContent requireAEP = EditorGUIUtility.TrTextContent("Require ES3.1+AEP"); public static readonly GUIContent require32 = EditorGUIUtility.TrTextContent("Require ES3.2"); public static readonly GUIContent skinOnGPU = EditorGUIUtility.TrTextContent("GPU Skinning*", "Calculate mesh skinning and blend shapes on the GPU via shaders"); + public static readonly GUIContent[] meshDeformations = { EditorGUIUtility.TrTextContent("CPU"), EditorGUIUtility.TrTextContent("GPU"), EditorGUIUtility.TrTextContent("GPU (Batched)") }; public static readonly GUIContent scriptingDefineSymbols = EditorGUIUtility.TrTextContent("Scripting Define Symbols", "Preprocessor defines passed to the C# script compiler."); public static readonly GUIContent additionalCompilerArguments = EditorGUIUtility.TrTextContent("Additional Compiler Arguments", "Additional arguments passed to the C# script compiler."); public static readonly GUIContent scriptingDefineSymbolsApply = EditorGUIUtility.TrTextContent("Apply"); @@ -205,7 +205,8 @@ class SettingsContent public static readonly GUIContent managedStrippingLevel = EditorGUIUtility.TrTextContent("Managed Stripping Level", "If scripting backend is IL2CPP, managed stripping can't be disabled."); public static readonly GUIContent il2cppCompilerConfiguration = EditorGUIUtility.TrTextContent("C++ Compiler Configuration"); public static readonly GUIContent il2cppCodeGeneration = EditorGUIUtility.TrTextContent("IL2CPP Code Generation", "Determines whether IL2CPP should generate code optimized for runtime performance or build size/iteration."); - public static readonly GUIContent[] il2cppCodeGenerationNames = new GUIContent[] { EditorGUIUtility.TrTextContent("Faster runtime"), EditorGUIUtility.TrTextContent("Faster (smaller) builds") }; + public static readonly GUIContent[] il2cppCodeGenerationNames = new GUIContent[] { EditorGUIUtility.TrTextContent("Faster runtime"), EditorGUIUtility.TrTextContent("Faster (smaller) builds") }; + public static readonly GUIContent il2cppStacktraceInformation = EditorGUIUtility.TrTextContent("IL2CPP Stacktrace Information", "Which information to include in stack traces. Including the file name and line number may increase build size."); public static readonly GUIContent scriptingMono2x = EditorGUIUtility.TrTextContent("Mono"); public static readonly GUIContent scriptingIL2CPP = EditorGUIUtility.TrTextContent("IL2CPP"); public static readonly GUIContent scriptingCoreCLR = EditorGUIUtility.TrTextContent("CoreCLR"); @@ -239,8 +240,6 @@ class SettingsContent public static readonly GUIContent[] hdrCubemapEncodingNames = { EditorGUIUtility.TrTextContent("Low Quality"), EditorGUIUtility.TrTextContent("Normal Quality"), EditorGUIUtility.TrTextContent("High Quality") }; public static readonly GUIContent lightmapStreamingEnabled = EditorGUIUtility.TrTextContent("Lightmap Streaming", "Only load larger lightmap mipmaps as needed to render the current game cameras. Requires texture streaming to be enabled in quality settings. This value is applied to the light map textures as they are generated."); public static readonly GUIContent lightmapStreamingPriority = EditorGUIUtility.TrTextContent("Streaming Priority", "Lightmap mipmap streaming priority when there's contention for resources. Positive numbers represent higher priority. Valid range is -128 to 127. This value is applied to the light map textures as they are generated."); - public static readonly GUIContent lightmapQualityAndroidWarning = EditorGUIUtility.TrTextContent("The Lightmap Encoding scheme you have selected requires OpenGL ES 3.0 or Vulkan. Navigate to 'Android Player Settings' > 'Rendering' and disable the OpenGL ES 2 API."); - public static readonly GUIContent hdrCubemapQualityAndroidWarning = EditorGUIUtility.TrTextContent("The HDR Cubemap Encoding scheme you have selected requires OpenGL ES 3.0 or Vulkan. Navigate to 'Android Player Settings' > 'Rendering' and disable the OpenGL ES 2 API."); public static readonly GUIContent legacyClampBlendShapeWeights = EditorGUIUtility.TrTextContent("Clamp BlendShapes (Deprecated)*", "If set, the range of BlendShape weights in SkinnedMeshRenderers will be clamped."); public static readonly GUIContent virtualTexturingSupportEnabled = EditorGUIUtility.TrTextContent("Virtual Texturing (Experimental)*", "Enable Virtual Texturing. This feature is experimental and not ready for production use. Changing this value requires an Editor restart."); public static readonly GUIContent virtualTexturingUnsupportedPlatformWarning = EditorGUIUtility.TrTextContent("The current target platform does not support Virtual Texturing. To build for this platform, uncheck Enable Virtual Texturing."); @@ -255,29 +254,34 @@ class SettingsContent public static readonly GUIContent notApplicableInfo = EditorGUIUtility.TrTextContent("Not applicable for this platform."); public static readonly GUIContent loadStoreDebugModeCheckbox = EditorGUIUtility.TrTextContent("Load/Store Action Debug Mode", "Initializes Framebuffer such that errors in the load/store actions will be visually apparent. (Removed in Release Builds)"); public static readonly GUIContent loadStoreDebugModeEditorOnlyCheckbox = EditorGUIUtility.TrTextContent("Editor Only", "Load/Store Action Debug Mode will only affect the Editor"); - - public static string undoChangedBatchingString { get { return LocalizationDatabase.GetLocalizedString("Changed Batching Settings"); } } - public static string undoChangedGraphicsAPIString { get { return LocalizationDatabase.GetLocalizedString("Changed Graphics API Settings"); } } - public static string undoChangedScriptingDefineString { get { return LocalizationDatabase.GetLocalizedString("Changed Scripting Define Settings"); } } - public static string undoChangedGraphicsJobsString { get { return LocalizationDatabase.GetLocalizedString("Changed Graphics Jobs Setting"); } } - public static string undoChangedGraphicsJobModeString { get { return LocalizationDatabase.GetLocalizedString("Changed Graphics Job Mode Setting"); } } - public static string changeColorSpaceString { get { return LocalizationDatabase.GetLocalizedString("Changing the color space may take a significant amount of time."); } } - public static string undoChangedPlatformShaderChunkSizeString { get { return LocalizationDatabase.GetLocalizedString("Changed Shader Chunk Size Platform Setting"); } } - public static string undoChangedPlatformShaderChunkCountString { get { return LocalizationDatabase.GetLocalizedString("Changed Shader Chunk Count Platform Setting"); } } - public static string undoChangedDefaultShaderChunkSizeString { get { return LocalizationDatabase.GetLocalizedString("Changed Shader Chunk Size Default Setting"); } } - public static string undoChangedDefaultShaderChunkCountString { get { return LocalizationDatabase.GetLocalizedString("Changed Shader Chunk Count Default Setting"); } } + public static readonly GUIContent allowHDRDisplay = EditorGUIUtility.TrTextContent("Allow HDR Display Output*", "Enable the use of HDR displays and include all the resources required for them to function correctly."); + public static readonly GUIContent useHDRDisplay = EditorGUIUtility.TrTextContent("Use HDR Display Output*", "Checks if the main display supports HDR and if it does, switches to HDR output at the start of the application."); + public static readonly GUIContent hdrOutputRequireHDRRenderingWarning = EditorGUIUtility.TrTextContent("The active Render Pipeline does not have HDR enabled. Enable HDR in the Render Pipeline Asset to see the changes."); + + public static readonly string undoChangedBatchingString = L10n.Tr("Changed Batching Settings"); + public static readonly string undoChangedGraphicsAPIString = L10n.Tr("Changed Graphics API Settings"); + public static readonly string undoChangedScriptingDefineString = L10n.Tr("Changed Scripting Define Settings"); + public static readonly string undoChangedGraphicsJobsString = L10n.Tr("Changed Graphics Jobs Setting"); + public static readonly string undoChangedGraphicsJobModeString = L10n.Tr("Changed Graphics Job Mode Setting"); + public static readonly string changeColorSpaceString = L10n.Tr("Changing the color space may take a significant amount of time."); + public static readonly string undoChangedPlatformShaderChunkSizeString = L10n.Tr("Changed Shader Chunk Size Platform Setting"); + public static readonly string undoChangedPlatformShaderChunkCountString = L10n.Tr("Changed Shader Chunk Count Platform Setting"); + public static readonly string undoChangedDefaultShaderChunkSizeString = L10n.Tr("Changed Shader Chunk Size Default Setting"); + public static readonly string undoChangedDefaultShaderChunkCountString = L10n.Tr("Changed Shader Chunk Count Default Setting"); } class RecompileReason { - public static readonly string scriptingDefineSymbolsModified = "Scripting define symbols setting modified"; - public static readonly string suppressCommonWarningsModified = "Suppress common warnings setting modified"; - public static readonly string allowUnsafeCodeModified = "Allow 'unsafe' code setting modified"; - public static readonly string apiCompatibilityLevelModified = "API Compatibility level modified"; - public static readonly string editorAssembliesCompatibilityLevelModified = "Editor Assemblies Compatibility level modified"; - public static readonly string useDeterministicCompilationModified = "Use deterministic compilation modified"; - public static readonly string additionalCompilerArgumentsModified = "Additional compiler arguments modified"; - public static readonly string activeBuildTargetGroupModified = "Active build target group modified"; + public static readonly string scriptingDefineSymbolsModified = L10n.Tr("Scripting define symbols setting modified"); + public static readonly string suppressCommonWarningsModified = L10n.Tr("Suppress common warnings setting modified"); + public static readonly string allowUnsafeCodeModified = L10n.Tr("Allow 'unsafe' code setting modified"); + public static readonly string apiCompatibilityLevelModified = L10n.Tr("API Compatibility level modified"); + public static readonly string editorAssembliesCompatibilityLevelModified = L10n.Tr("Editor Assemblies Compatibility level modified"); + public static readonly string useDeterministicCompilationModified = L10n.Tr("Use deterministic compilation modified"); + public static readonly string additionalCompilerArgumentsModified = L10n.Tr("Additional compiler arguments modified"); + public static readonly string activeBuildTargetGroupModified = L10n.Tr("Active build target group modified"); + + public static readonly string presetChanged = L10n.Tr("Preset changed"); } PlayerSettingsSplashScreenEditor m_SplashScreenEditor; @@ -302,8 +306,9 @@ PlayerSettingsIconsEditor iconsEditor } } - private static GraphicsJobMode[] m_GfxJobModeValues = new GraphicsJobMode[] { GraphicsJobMode.Native, GraphicsJobMode.Legacy }; - private static GUIContent[] m_GfxJobModeNames = new GUIContent[] { EditorGUIUtility.TrTextContent("Native"), EditorGUIUtility.TrTextContent("Legacy") }; + private static GraphicsJobMode[] m_GfxJobModeValues = new GraphicsJobMode[] { GraphicsJobMode.Native, GraphicsJobMode.Legacy, GraphicsJobMode.Split }; + private static GUIContent[] m_GfxJobModeNames = new GUIContent[] { EditorGUIUtility.TrTextContent("Native"), EditorGUIUtility.TrTextContent("Legacy"), EditorGUIUtility.TrTextContent("Split") }; + private static MeshDeformation[] m_MeshDeformations = { MeshDeformation.CPU, MeshDeformation.GPU, MeshDeformation.GPUBatched }; // Section and tab selection state @@ -394,6 +399,7 @@ PlayerSettingsIconsEditor iconsEditor SerializedProperty m_DefaultScreenHeight; SerializedProperty m_ActiveColorSpace; + SerializedProperty m_UnsupportedMSAAFallback; SerializedProperty m_StripUnusedMeshComponents; SerializedProperty m_StrictShaderVariantMatching; SerializedProperty m_MipStripping; @@ -410,6 +416,7 @@ PlayerSettingsIconsEditor iconsEditor SerializedProperty m_KeepLoadedShadersAlive; SerializedProperty m_PreloadedAssets; SerializedProperty m_BakeCollisionMeshes; + SerializedProperty m_DedicatedServerOptimizations; SerializedProperty m_ResizableWindow; SerializedProperty m_FullscreenMode; SerializedProperty m_VisibleInBackground; @@ -421,6 +428,7 @@ PlayerSettingsIconsEditor iconsEditor SerializedProperty m_CaptureSingleScreen; SerializedProperty m_SkinOnGPU; + SerializedProperty m_MeshDeformation; SerializedProperty m_EnableLoadStoreDebugMode; @@ -452,12 +460,12 @@ PlayerSettingsIconsEditor iconsEditor SerializedProperty m_EditorAssembliesCompatibilityLevel; SerializedProperty m_Il2CppCompilerConfiguration; SerializedProperty m_Il2CppCodeGeneration; + SerializedProperty m_Il2CppStacktraceInformation; SerializedProperty m_ScriptingDefines; SerializedProperty m_AdditionalCompilerArguments; SerializedProperty m_StackTraceTypes; SerializedProperty m_ManagedStrippingLevel; SerializedProperty m_ActiveInputHandler; - SerializedProperty m_SelectedPlatform; // Embedded Linux specific SerializedProperty m_ForceSRGBBlit; @@ -515,6 +523,8 @@ public static void SyncColorGamuts() bool isPresetWindowOpen = false; bool hasPresetWindowClosed = false; + const string kSelectedPlatform = "PlayerSettings.SelectedPlatform"; + public SerializedProperty FindPropertyAssert(string name) { SerializedProperty property = serializedObject.FindProperty(name); @@ -545,6 +555,7 @@ void OnEnable() m_UIStatusBarHidden = FindPropertyAssert("uIStatusBarHidden"); m_UIStatusBarStyle = FindPropertyAssert("uIStatusBarStyle"); m_ActiveColorSpace = FindPropertyAssert("m_ActiveColorSpace"); + m_UnsupportedMSAAFallback = FindPropertyAssert("unsupportedMSAAFallback"); m_StripUnusedMeshComponents = FindPropertyAssert("StripUnusedMeshComponents"); m_StrictShaderVariantMatching = FindPropertyAssert("strictShaderVariantMatching"); m_MipStripping = FindPropertyAssert("mipStripping"); @@ -596,12 +607,12 @@ void OnEnable() m_EditorAssembliesCompatibilityLevel = FindPropertyAssert("editorAssembliesCompatibilityLevel"); m_Il2CppCompilerConfiguration = FindPropertyAssert("il2cppCompilerConfiguration"); m_Il2CppCodeGeneration = FindPropertyAssert("il2cppCodeGeneration"); + m_Il2CppStacktraceInformation = FindPropertyAssert("il2cppStacktraceInformation"); m_ScriptingDefines = FindPropertyAssert("scriptingDefineSymbols"); m_StackTraceTypes = FindPropertyAssert("m_StackTraceTypes"); m_ManagedStrippingLevel = FindPropertyAssert("managedStrippingLevel"); m_ActiveInputHandler = FindPropertyAssert("activeInputHandler"); m_AdditionalCompilerArguments = FindPropertyAssert("additionalCompilerArguments"); - m_SelectedPlatform = FindPropertyAssert("selectedPlatform"); m_DefaultScreenWidth = FindPropertyAssert("defaultScreenWidth"); m_DefaultScreenHeight = FindPropertyAssert("defaultScreenHeight"); @@ -627,6 +638,7 @@ void OnEnable() m_KeepLoadedShadersAlive = FindPropertyAssert("keepLoadedShadersAlive"); m_PreloadedAssets = FindPropertyAssert("preloadedAssets"); m_BakeCollisionMeshes = FindPropertyAssert("bakeCollisionMeshes"); + m_DedicatedServerOptimizations = FindPropertyAssert("dedicatedServerOptimizations"); m_ResizableWindow = FindPropertyAssert("resizableWindow"); m_UseMacAppStoreValidation = FindPropertyAssert("useMacAppStoreValidation"); m_MacAppStoreCategory = FindPropertyAssert("macAppStoreCategory"); @@ -638,6 +650,7 @@ void OnEnable() m_VisibleInBackground = FindPropertyAssert("visibleInBackground"); m_AllowFullscreenSwitch = FindPropertyAssert("allowFullscreenSwitch"); m_SkinOnGPU = FindPropertyAssert("gpuSkinning"); + m_MeshDeformation = FindPropertyAssert("meshDeformation"); m_ForceSingleInstance = FindPropertyAssert("forceSingleInstance"); m_UseFlipModelSwapchain = FindPropertyAssert("useFlipModelSwapchain"); @@ -659,12 +672,15 @@ void OnEnable() var validPlatformsLength = validPlatforms.Length; m_SettingsExtensions = new ISettingEditorExtension[validPlatformsLength]; + var currentPlatform = 0; for (int i = 0; i < validPlatformsLength; i++) { string module = ModuleManager.GetTargetStringFromBuildTargetGroup(validPlatforms[i].namedBuildTarget.ToBuildTargetGroup()); m_SettingsExtensions[i] = ModuleManager.GetEditorSettingsExtension(module); if (m_SettingsExtensions[i] != null) m_SettingsExtensions[i].OnEnable(this); + if (validPlatforms[i].IsActive()) + currentPlatform = i; } for (int i = 0; i < m_SectionAnimators.Length; i++) @@ -678,7 +694,7 @@ void OnEnable() // we access this cache both from player settings editor and script side when changing api s_GraphicsDeviceLists.Clear(); - var selectedPlatform = m_SelectedPlatform.intValue; + var selectedPlatform = SessionState.GetInt(kSelectedPlatform, currentPlatform); if (selectedPlatform < 0) selectedPlatform = 0; @@ -864,9 +880,12 @@ public override void OnInspectorGUI() EditorGUILayout.Space(); EditorGUI.BeginChangeCheck(); - int oldPlatform = m_SelectedPlatform.intValue; - m_SelectedPlatform.intValue = EditorGUILayout.BeginPlatformGrouping(validPlatforms, null); - int selectedPlatformValue = m_SelectedPlatform.intValue; + int oldPlatform = SessionState.GetInt(kSelectedPlatform, 0); + int selectedPlatformValue = EditorGUILayout.BeginPlatformGrouping(validPlatforms, null); + if (selectedPlatformValue != oldPlatform) + { + SessionState.SetInt(kSelectedPlatform, selectedPlatformValue); + } if (EditorGUI.EndChangeCheck()) { @@ -925,6 +944,11 @@ public override void OnInspectorGUI() if (hasPresetWindowClosed) { + // We recompile after the window is closed just to make sure all the values are set/shown correctly. + // There might be a smarter idea where you detect the values that have changed and only do it if it's required, + // but the way the Preset window applies those changes as well as the way IMGUI works makes it difficult to track. + SetReason(RecompileReason.presetChanged); + OnPresetSelectorClosed(); } else if (HasReasonToCompile()) @@ -1176,19 +1200,9 @@ static private string GraphicsDeviceTypeToString(BuildTarget target, GraphicsDev { if (target == BuildTarget.WebGL) { - if (graphicsDeviceType == GraphicsDeviceType.OpenGLES2) return "WebGL 1 (Deprecated)"; if (graphicsDeviceType == GraphicsDeviceType.OpenGLES3) return "WebGL 2"; } string name = graphicsDeviceType.ToString(); - if (target == BuildTarget.iOS || target == BuildTarget.tvOS) - { - if (name.Contains("OpenGLES")) - name += " (Deprecated)"; - } - else if (graphicsDeviceType == GraphicsDeviceType.OpenGLES2) - { - name += " (Deprecated)"; - } return name; } @@ -1197,7 +1211,6 @@ static private GraphicsDeviceType GraphicsDeviceTypeFromString(string graphicsDe { graphicsDeviceType = graphicsDeviceType.Replace(" (Deprecated)", ""); graphicsDeviceType = graphicsDeviceType.Replace(" (Experimental)", ""); - if (graphicsDeviceType == "WebGL 1") return GraphicsDeviceType.OpenGLES2; if (graphicsDeviceType == "WebGL 2") return GraphicsDeviceType.OpenGLES3; return (GraphicsDeviceType)Enum.Parse(typeof(GraphicsDeviceType), graphicsDeviceType, true); } @@ -1222,6 +1235,14 @@ private void AddGraphicsDeviceElement(BuildTarget target, Rect rect, Reorderable if (availableDevices == null || availableDevices.Length == 0) return; + //As part of OpenGL deprection from MacOS, hide the option of adding OpenGL + if (target == BuildTarget.StandaloneOSX) + { + var availableDeviceList = availableDevices.ToList(); + availableDeviceList.Remove(GraphicsDeviceType.OpenGLCore); + availableDevices = availableDeviceList.ToArray(); + } + var names = new string[availableDevices.Length]; var enabled = new bool[availableDevices.Length]; for (int i = 0; i < availableDevices.Length; ++i) @@ -1378,8 +1399,8 @@ void OpenGLES31OptionsGUI(BuildTargetGroup targetGroup, BuildTarget targetPlatfo return; var apis = PlayerSettings.GetGraphicsAPIs(targetPlatform); - // only available if we include ES3, and not ES2 - var hasMinES3 = apis.Contains(GraphicsDeviceType.OpenGLES3) && !apis.Contains(GraphicsDeviceType.OpenGLES2); + // only available if we include ES3 + var hasMinES3 = apis.Contains(GraphicsDeviceType.OpenGLES3); if (!hasMinES3) return; @@ -1714,6 +1735,19 @@ public static T BuildEnumPopup(GUIContent uiString, T selected, T[] options, return options[newIdx]; } + public static void EnumPropertyField(SerializedProperty property, GUIContent name) where T : Enum + { + using (var horizontal = new EditorGUILayout.HorizontalScope()) + { + using (new EditorGUI.PropertyScope(horizontal.rect, GUIContent.none, property)) + { + var values = (T[])Enum.GetValues(typeof(T)); + var valueNames = Enum.GetNames(typeof(T)).Select(e => new GUIContent(e)).ToArray(); + PlayerSettingsEditor.BuildEnumPopup(property, name, values, valueNames); + } + } + } + public void OtherSectionGUI(BuildPlatform platform, ISettingEditorExtension settingsExtension, int sectionIndex = 4) { if (BeginSettingsBox(sectionIndex, SettingsContent.otherSettingsTitle)) @@ -1828,39 +1862,33 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, ISettingEditorExte } } - // Display a warning for platforms that some devices don't support linear rendering if the settings are not fine for linear colorspace - if (PlayerSettings.colorSpace == ColorSpace.Linear) + using (new EditorGUI.DisabledScope(EditorApplication.isPlaying)) { - bool showWarning = false; - GUIContent warningMessage = null; - var apis = PlayerSettings.GetGraphicsAPIs(platform.defaultTarget); - - if (platform.namedBuildTarget == NamedBuildTarget.Android) - { - // SRP should handle blits internally - bool hasBlitDisabled = (PlayerSettings.Android.blitType == AndroidBlitType.Never) && (GraphicsSettings.currentRenderPipeline == null); - showWarning = hasBlitDisabled || apis.Contains(GraphicsDeviceType.OpenGLES2); - warningMessage = SettingsContent.colorSpaceAndroidWarning; - } - else if (platform.namedBuildTarget == NamedBuildTarget.iOS || platform.namedBuildTarget == NamedBuildTarget.tvOS) - { - showWarning = apis.Contains(GraphicsDeviceType.OpenGLES3) || apis.Contains(GraphicsDeviceType.OpenGLES2); - warningMessage = SettingsContent.colorSpaceIOSWarning; - } - else if ((platform.namedBuildTarget == NamedBuildTarget.WebGL)) + switch (platform.defaultTarget) { - showWarning = apis.Contains(GraphicsDeviceType.OpenGLES2); - warningMessage = SettingsContent.colorSpaceWebGLWarning; + case BuildTarget.StandaloneOSX: + case BuildTarget.StandaloneWindows: + case BuildTarget.iOS: + case BuildTarget.Android: + case BuildTarget.StandaloneWindows64: + case BuildTarget.WebGL: + case BuildTarget.WSAPlayer: + case BuildTarget.StandaloneLinux64: + case BuildTarget.tvOS: + EditorGUILayout.PropertyField(m_UnsupportedMSAAFallback, SettingsContent.unsupportedMSAAFallback); + break; + default: + break; } - else if ((platform.namedBuildTarget == NamedBuildTarget.EmbeddedLinux)) + } + + // Special cases for some platform with limitations regarding linear colorspace + if (PlayerSettings.colorSpace == ColorSpace.Linear) + { + if (platform.namedBuildTarget == NamedBuildTarget.EmbeddedLinux) { - showWarning = apis.Contains(GraphicsDeviceType.OpenGLES2); - warningMessage = SettingsContent.colorSpaceEmbeddedLinuxWarning; EditorGUILayout.PropertyField(m_ForceSRGBBlit, SettingsContent.forceSRGBBlit); } - - if (showWarning) - EditorGUILayout.HelpBox(warningMessage.text, MessageType.Warning); } // Graphics APIs @@ -1971,14 +1999,52 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, ISettingEditorExte } } + if (platform.namedBuildTarget.ToBuildTargetGroup() == BuildTargetGroup.Standalone) + { + GraphicsDeviceType[] gfxAPIs = PlayerSettings.GetGraphicsAPIs(platform.defaultTarget); + gfxJobModesSupported = (gfxAPIs[0] == GraphicsDeviceType.Direct3D12) || (gfxAPIs[0] == GraphicsDeviceType.Vulkan); + } + // GPU Skinning toggle (only show on relevant platforms) if (!BuildTargetDiscovery.PlatformHasFlag(platform.defaultTarget, TargetAttributes.GPUSkinningNotSupported)) { - EditorGUI.BeginChangeCheck(); - EditorGUILayout.PropertyField(m_SkinOnGPU, SettingsContent.skinOnGPU); - if (EditorGUI.EndChangeCheck()) + /// Adding support to other platforms in progress... + bool platformSupportsBatching = + platform.namedBuildTarget == NamedBuildTarget.NintendoSwitch || + platform.namedBuildTarget == NamedBuildTarget.PS5; + + if (platformSupportsBatching) { - ShaderUtil.RecreateSkinnedMeshResources(); + MeshDeformation currentDeformation = (MeshDeformation)m_MeshDeformation.intValue; + + EditorGUI.BeginChangeCheck(); + MeshDeformation newDeformation = BuildEnumPopup(SettingsContent.skinOnGPU, currentDeformation, m_MeshDeformations, SettingsContent.meshDeformations); + if (EditorGUI.EndChangeCheck()) + { + m_SkinOnGPU.boolValue = newDeformation != MeshDeformation.CPU; + m_MeshDeformation.intValue = (int)newDeformation; + serializedObject.ApplyModifiedProperties(); + ShaderUtil.RecreateSkinnedMeshResources(); + } + } + else + { + // Use the original checkbox UI but preserve underlying batching mode whenever possible. + // We need to do this because gpuSkinning/meshDeformation are properties which are shared between all platforms + // and if the user sets gpuSkinning mode to "enabled", we actually want to preserve "batchEnabled" if it was set for other platforms. + // Platforms that do not support batching but have meshDeformation == GPUBatched just silently use original non-batched code. + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(m_SkinOnGPU, SettingsContent.skinOnGPU); + if (EditorGUI.EndChangeCheck()) + { + // Preserve the value of m_MeshDeformation when possible. + if (!m_SkinOnGPU.boolValue) + m_MeshDeformation.intValue = (int)MeshDeformation.CPU; + else + m_MeshDeformation.intValue = m_MeshDeformation.intValue != (int)MeshDeformation.CPU ? m_MeshDeformation.intValue : (int)MeshDeformation.GPUBatched; + serializedObject.ApplyModifiedProperties(); + ShaderUtil.RecreateSkinnedMeshResources(); + } } } @@ -1996,8 +2062,7 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, ISettingEditorExte GraphicsJobMode newGfxJobMode = GraphicsJobMode.Legacy; if (gfxAPIs[0] == GraphicsDeviceType.XboxOneD3D12) { - newGfxJobMode = GraphicsJobMode.Native; - graphicsJobsOptionEnabled = false; + newGfxJobMode = GraphicsJobMode.Split; if (graphicsJobs == false) { PlayerSettings.SetGraphicsJobsForPlatform(platform.defaultTarget, true); @@ -2006,9 +2071,9 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, ISettingEditorExte } } PlayerSettings.SetGraphicsJobModeForPlatform(platform.defaultTarget, newGfxJobMode); - graphicsJobsModeOptionEnabled = false; + PlayerSettings.SetGraphicsThreadingModeForPlatform(platform.defaultTarget, GfxThreadingMode.SplitJobs); } - else if (platform.namedBuildTarget == new NamedBuildTarget("PS5")) + else if (platform.namedBuildTarget == NamedBuildTarget.PS5) { // On PS5NGGC, we only have kGfxJobModeNative so we disable the options in that case GraphicsDeviceType[] gfxAPIs = PlayerSettings.GetGraphicsAPIs(platform.defaultTarget); @@ -2023,6 +2088,7 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, ISettingEditorExte } PlayerSettings.SetGraphicsJobModeForPlatform(platform.defaultTarget, GraphicsJobMode.Native); + PlayerSettings.SetGraphicsThreadingModeForPlatform(platform.defaultTarget, GfxThreadingMode.ClientWorkerNativeJobs); graphicsJobsModeOptionEnabled = false; } } @@ -2071,6 +2137,13 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, ISettingEditorExte { Undo.RecordObject(target, SettingsContent.undoChangedGraphicsJobModeString); PlayerSettings.SetGraphicsJobModeForPlatform(platform.defaultTarget, newGfxJobMode); + + if(newGfxJobMode == GraphicsJobMode.Native) + PlayerSettings.SetGraphicsThreadingModeForPlatform(platform.defaultTarget, GfxThreadingMode.ClientWorkerNativeJobs); + else if (newGfxJobMode == GraphicsJobMode.Legacy) + PlayerSettings.SetGraphicsThreadingModeForPlatform(platform.defaultTarget, GfxThreadingMode.ClientWorkerJobs); + else if (newGfxJobMode == GraphicsJobMode.Split) + PlayerSettings.SetGraphicsThreadingModeForPlatform(platform.defaultTarget, GfxThreadingMode.SplitJobs); } } } @@ -2120,21 +2193,6 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, ISettingEditorExte GUIUtility.ExitGUI(); } - - if (encodingQuality == LightmapEncodingQuality.High && - platform.namedBuildTarget == NamedBuildTarget.WebGL && - PlayerSettings.GetGraphicsAPIs(BuildTarget.WebGL).Contains(GraphicsDeviceType.OpenGLES2)) - { - EditorGUILayout.HelpBox(SettingsContent.lightmapEncodingWebGLWarning.text, MessageType.Warning); - } - - if (encodingQuality != LightmapEncodingQuality.Low && platform.namedBuildTarget == NamedBuildTarget.Android) - { - var apis = PlayerSettings.GetGraphicsAPIs(BuildTarget.Android); - var hasMinAPI = (apis.Contains(GraphicsDeviceType.Vulkan) || apis.Contains(GraphicsDeviceType.OpenGLES3)) && !apis.Contains(GraphicsDeviceType.OpenGLES2); - if (!hasMinAPI) - EditorGUILayout.HelpBox(SettingsContent.lightmapQualityAndroidWarning.text, MessageType.Warning); - } } { @@ -2152,21 +2210,6 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, ISettingEditorExte GUIUtility.ExitGUI(); } - - if (encodingQuality == HDRCubemapEncodingQuality.High && - platform.namedBuildTarget == NamedBuildTarget.WebGL && - PlayerSettings.GetGraphicsAPIs(BuildTarget.WebGL).Contains(GraphicsDeviceType.OpenGLES2)) - { - EditorGUILayout.HelpBox(SettingsContent.hdrCubemapEncodingWebGLWarning.text, MessageType.Warning); - } - - if (encodingQuality != HDRCubemapEncodingQuality.Low && platform.namedBuildTarget == NamedBuildTarget.Android) - { - var apis = PlayerSettings.GetGraphicsAPIs(BuildTarget.Android); - var hasMinAPI = (apis.Contains(GraphicsDeviceType.Vulkan) || apis.Contains(GraphicsDeviceType.OpenGLES3)) && !apis.Contains(GraphicsDeviceType.OpenGLES2); - if (!hasMinAPI) - EditorGUILayout.HelpBox(SettingsContent.hdrCubemapQualityAndroidWarning.text, MessageType.Warning); - } } } } @@ -2205,14 +2248,6 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, ISettingEditorExte if (platform.namedBuildTarget.ToBuildTargetGroup() == BuildTargetGroup.Standalone || platform.namedBuildTarget == NamedBuildTarget.WindowsStoreApps || platform.namedBuildTarget == NamedBuildTarget.WebGL || (settingsExtension != null && settingsExtension.SupportsFrameTimingStatistics())) { PlayerSettings.enableFrameTimingStats = EditorGUILayout.Toggle(SettingsContent.enableFrameTimingStats, PlayerSettings.enableFrameTimingStats); - if (PlayerSettings.enableFrameTimingStats && platform.namedBuildTarget == NamedBuildTarget.WebGL) - { - var apis = PlayerSettings.GetGraphicsAPIs(BuildTarget.WebGL); - if (apis.Contains(GraphicsDeviceType.OpenGLES2)) - { - EditorGUILayout.HelpBox(SettingsContent.frameTimingStatsWebGLWarning.text, MessageType.Warning); - } - } if (PlayerSettings.enableFrameTimingStats) { EditorGUILayout.HelpBox(SettingsContent.openGLFrameTimingStatsOnGPURecordersOffInfo.text, MessageType.Info); @@ -2220,7 +2255,7 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, ISettingEditorExte } // Tickbox for OpenGL-only option to toggle Profiler GPU Recorders. - if (platform.namedBuildTarget == NamedBuildTarget.Standalone || platform.namedBuildTarget == NamedBuildTarget.Android) + if (platform.namedBuildTarget == NamedBuildTarget.Standalone || platform.namedBuildTarget == NamedBuildTarget.Android || platform.namedBuildTarget == NamedBuildTarget.EmbeddedLinux || platform.namedBuildTarget == NamedBuildTarget.QNX) { PlayerSettings.enableOpenGLProfilerGPURecorders = EditorGUILayout.Toggle(SettingsContent.enableOpenGLProfilerGPURecorders, PlayerSettings.enableOpenGLProfilerGPURecorders); @@ -2235,38 +2270,52 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, ISettingEditorExte if (hdrDisplaySupported) { - string label = "Use display in HDR mode"; - string tooltip = "Switch the display to HDR output (on supported displays)" + ((platform.namedBuildTarget == NamedBuildTarget.XboxOne) ? " at start of application." : "."); - bool oldUseHDRDisplay = PlayerSettings.useHDRDisplay; - PlayerSettings.useHDRDisplay = EditorGUILayout.Toggle(EditorGUIUtility.TrTextContent(label, tooltip), oldUseHDRDisplay); bool requestRepaint = false; - - if (oldUseHDRDisplay != PlayerSettings.useHDRDisplay) + bool oldAllowHDRDisplaySupport = PlayerSettings.allowHDRDisplaySupport; + PlayerSettings.allowHDRDisplaySupport = EditorGUILayout.Toggle(SettingsContent.allowHDRDisplay, oldAllowHDRDisplaySupport); + if (oldAllowHDRDisplaySupport != PlayerSettings.allowHDRDisplaySupport) requestRepaint = true; - if (platform.namedBuildTarget.ToBuildTargetGroup() == BuildTargetGroup.Standalone || platform.namedBuildTarget == NamedBuildTarget.WindowsStoreApps) + using (new EditorGUI.DisabledScope(!PlayerSettings.allowHDRDisplaySupport)) { - using (new EditorGUI.DisabledScope(!PlayerSettings.useHDRDisplay)) + using (new EditorGUI.IndentLevelScope()) { - using (new EditorGUI.IndentLevelScope()) - { - EditorGUI.BeginChangeCheck(); - D3DHDRDisplayBitDepth oldBitDepth = PlayerSettings.D3DHDRBitDepth; - D3DHDRDisplayBitDepth[] bitDepthValues = { D3DHDRDisplayBitDepth.D3DHDRDisplayBitDepth10, D3DHDRDisplayBitDepth.D3DHDRDisplayBitDepth16 }; - GUIContent HDRBitDepthLabel = EditorGUIUtility.TrTextContent("Swap Chain Bit Depth", "Affects the bit depth of the final swap chain format and color space."); - GUIContent[] HDRBitDepthNames = { EditorGUIUtility.TrTextContent("Bit Depth 10"), EditorGUIUtility.TrTextContent("Bit Depth 16") }; + bool oldUseHDRDisplay = PlayerSettings.useHDRDisplay; + PlayerSettings.useHDRDisplay = EditorGUILayout.Toggle(SettingsContent.useHDRDisplay, oldUseHDRDisplay); + + if (oldUseHDRDisplay != PlayerSettings.useHDRDisplay) + requestRepaint = true; - D3DHDRDisplayBitDepth bitDepth = BuildEnumPopup(HDRBitDepthLabel, oldBitDepth, bitDepthValues, HDRBitDepthNames); - if (EditorGUI.EndChangeCheck()) + if (platform.namedBuildTarget.ToBuildTargetGroup() == BuildTargetGroup.Standalone || platform.namedBuildTarget == NamedBuildTarget.WindowsStoreApps) + { + using (new EditorGUI.DisabledScope(!PlayerSettings.useHDRDisplay)) { - PlayerSettings.D3DHDRBitDepth = bitDepth; - if (oldBitDepth != bitDepth) - requestRepaint = true; + using (new EditorGUI.IndentLevelScope()) + { + EditorGUI.BeginChangeCheck(); + HDRDisplayBitDepth oldBitDepth = PlayerSettings.hdrBitDepth; + HDRDisplayBitDepth[] bitDepthValues = { HDRDisplayBitDepth.BitDepth10, HDRDisplayBitDepth.BitDepth16 }; + GUIContent hdrBitDepthLabel = EditorGUIUtility.TrTextContent("Swap Chain Bit Depth", "Affects the bit depth of the final swap chain format and color space."); + GUIContent[] hdrBitDepthNames = { EditorGUIUtility.TrTextContent("Bit Depth 10"), EditorGUIUtility.TrTextContent("Bit Depth 16") }; + + HDRDisplayBitDepth bitDepth = BuildEnumPopup(hdrBitDepthLabel, oldBitDepth, bitDepthValues, hdrBitDepthNames); + if (EditorGUI.EndChangeCheck()) + { + PlayerSettings.hdrBitDepth = bitDepth; + if (oldBitDepth != bitDepth) + requestRepaint = true; + } + } } } } } + if (PlayerSettings.allowHDRDisplaySupport && GraphicsSettings.currentRenderPipeline != null && !SupportedRenderingFeatures.active.supportsHDR) + { + EditorGUILayout.HelpBox(SettingsContent.hdrOutputRequireHDRRenderingWarning.text, MessageType.Info); + } + if (requestRepaint) EditorApplication.RequestRepaintAllViews(); } @@ -2556,6 +2605,14 @@ private Il2CppCodeGeneration GetCurrentIl2CppCodeGenerationForTarget(NamedBuildT return Il2CppCodeGeneration.OptimizeSpeed; } + private Il2CppStacktraceInformation GetCurrentIl2CppStacktraceInformationOptionForTarget(NamedBuildTarget namedBuildTarget) + { + if (m_Il2CppStacktraceInformation.TryGetMapEntry(namedBuildTarget.TargetName, out var entry)) + return (Il2CppStacktraceInformation)entry.FindPropertyRelative("second").intValue; + else + return Il2CppStacktraceInformation.MethodOnly; + } + private ManagedStrippingLevel GetCurrentManagedStrippingLevelForTarget(NamedBuildTarget namedBuildTarget, ScriptingImplementation backend) { if (m_ManagedStrippingLevel.TryGetMapEntry(namedBuildTarget.TargetName, out var entry)) @@ -2761,6 +2818,18 @@ private void OtherSectionConfigurationGUI(BuildPlatform platform, ISettingEditor } } + // Il2Cpp Stacktrace Configuration + using (new EditorGUI.DisabledScope(currentBackend != ScriptingImplementation.IL2CPP)) + { + Il2CppStacktraceInformation config = GetCurrentIl2CppStacktraceInformationOptionForTarget(platform.namedBuildTarget); + + var newConfiguration = BuildEnumPopup(SettingsContent.il2cppStacktraceInformation, config, + GetIl2CppStacktraceOptions(), GetIl2CppStacktraceOptionNames()); + + if (config != newConfiguration) + m_Il2CppStacktraceInformation.SetMapValue(platform.namedBuildTarget.TargetName, (int)newConfiguration); + } + bool gcIncrementalEnabled = BuildPipeline.IsFeatureSupported("ENABLE_SCRIPTING_GC_WBARRIERS", platform.defaultTarget); if (platform.namedBuildTarget == NamedBuildTarget.iOS) gcIncrementalEnabled = gcIncrementalEnabled && currentBackend == ScriptingImplementation.IL2CPP; @@ -2820,19 +2889,18 @@ private void OtherSectionConfigurationGUI(BuildPlatform platform, ISettingEditor EditorGUILayout.PropertyField(m_AccelerometerFrequency, SettingsContent.accelerometerFrequency); if (platform.namedBuildTarget == NamedBuildTarget.iOS || platform.namedBuildTarget == NamedBuildTarget.tvOS || platform.namedBuildTarget == NamedBuildTarget.Android) - { EditorGUILayout.PropertyField(m_MuteOtherAudioSources, SettingsContent.muteOtherAudioSources); - if (m_MuteOtherAudioSources.boolValue == false && platform.namedBuildTarget == NamedBuildTarget.iOS) - EditorGUILayout.HelpBox(SettingsContent.iOSExternalAudioInputNotSupported.text, MessageType.Warning); - } - // TVOS TODO: check what should stay or go if (platform.namedBuildTarget == NamedBuildTarget.iOS || platform.namedBuildTarget == NamedBuildTarget.tvOS) { if (platform.namedBuildTarget == NamedBuildTarget.iOS) { EditorGUILayout.PropertyField(m_PrepareIOSForRecording, SettingsContent.prepareIOSForRecording); + + if (m_MuteOtherAudioSources.boolValue == false && m_PrepareIOSForRecording.boolValue == false) + EditorGUILayout.HelpBox(SettingsContent.iOSExternalAudioInputNotSupported.text, MessageType.Warning); + EditorGUILayout.PropertyField(m_ForceIOSSpeakersWhenRecording, SettingsContent.forceIOSSpeakersWhenRecording); } EditorGUILayout.PropertyField(m_UIRequiresPersistentWiFi, SettingsContent.UIRequiresPersistentWiFi); @@ -3150,6 +3218,9 @@ private void OtherSectionOptimizationGUI(BuildPlatform platform) // Optimization GUILayout.Label(SettingsContent.optimizationTitle, EditorStyles.boldLabel); + if (platform.namedBuildTarget == NamedBuildTarget.Server) + EditorGUILayout.PropertyField(m_DedicatedServerOptimizations, SettingsContent.dedicatedServerOptimizations); + EditorGUILayout.PropertyField(m_BakeCollisionMeshes, SettingsContent.bakeCollisionMeshes); if (isPreset) @@ -3261,6 +3332,31 @@ private GUIContent[] GetIl2CppCompilerConfigurationNames() return m_Il2cppCompilerConfigurationNames; } + static Il2CppStacktraceInformation[] m_Il2cppStacktraceOptions; + static GUIContent[] m_Il2cppStacktraceOptionNames; + + private Il2CppStacktraceInformation[] GetIl2CppStacktraceOptions() + { + if (m_Il2cppStacktraceOptions == null) + m_Il2cppStacktraceOptions = (Il2CppStacktraceInformation[])Enum.GetValues(typeof(Il2CppStacktraceInformation)); + + return m_Il2cppStacktraceOptions; + } + + private GUIContent[] GetIl2CppStacktraceOptionNames() + { + if (m_Il2cppStacktraceOptionNames == null) + { + m_Il2cppStacktraceOptionNames = new GUIContent[] + { + EditorGUIUtility.TextContent("Method Name"), + EditorGUIUtility.TextContent("Method Name, File Name, and Line Number"), + }; + } + + return m_Il2cppStacktraceOptionNames; + } + public static bool IsLatestApiCompatibility(ApiCompatibilityLevel level) { return (level == ApiCompatibilityLevel.NET_4_6 || level == ApiCompatibilityLevel.NET_Standard_2_0); diff --git a/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsSplashScreenEditor.cs b/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsSplashScreenEditor.cs index db95a28427..eac6ee47b8 100644 --- a/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsSplashScreenEditor.cs +++ b/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsSplashScreenEditor.cs @@ -81,7 +81,7 @@ static AssetDeleteResult OnWillDeleteAsset(string asset, RemoveAssetOptions opti class Texts { public GUIContent animate = EditorGUIUtility.TrTextContent("Animation"); - public GUIContent backgroundColor = EditorGUIUtility.TrTextContent("Background Color", "Background color when no background image is used."); + public GUIContent backgroundColor = EditorGUIUtility.TrTextContent("Background Color", "Background color when no background image is used. On Android, use this property to set static splash image background color."); public GUIContent backgroundImage = EditorGUIUtility.TrTextContent("Background Image", "Image to be used in landscape and portrait (when portrait image is not set)."); public GUIContent backgroundPortraitImage = EditorGUIUtility.TrTextContent("Alternate Portrait Image*", "Optional image to be used in portrait mode."); public GUIContent backgroundTitle = EditorGUIUtility.TrTextContent("Background*"); @@ -319,13 +319,20 @@ public void SplashSectionGUI(BuildPlatform platform, ISettingEditorExtension set } else { - ObjectReferencePropertyField(m_VirtualRealitySplashScreen, k_Texts.vrSplashScreen); + bool VREnabled = BuildPipeline.IsFeatureSupported("ENABLE_VR", platform.defaultTarget); + + if (VREnabled) + ObjectReferencePropertyField(m_VirtualRealitySplashScreen, k_Texts.vrSplashScreen); if (TargetSupportsOptionalBuiltinSplashScreen(platform.namedBuildTarget.ToBuildTargetGroup(), settingsExtension)) BuiltinCustomSplashScreenGUI(platform.namedBuildTarget.ToBuildTargetGroup(), settingsExtension); if (settingsExtension != null) + { settingsExtension.SplashSectionGUI(); + if (!m_ShowUnitySplashScreen.boolValue && settingsExtension.SupportsStaticSplashScreenBackgroundColor()) + EditorGUILayout.PropertyField(m_SplashScreenBackgroundColor, k_Texts.backgroundColor); + } if (m_ShowUnitySplashScreen.boolValue) m_Owner.ShowSharedNote(); diff --git a/Editor/Mono/Inspector/PreviewRenderUtility.cs b/Editor/Mono/Inspector/PreviewRenderUtility.cs index 4ddda64d1a..d3807a21ff 100644 --- a/Editor/Mono/Inspector/PreviewRenderUtility.cs +++ b/Editor/Mono/Inspector/PreviewRenderUtility.cs @@ -311,7 +311,7 @@ private void InitPreview(Rect r) } m_TargetRect = r; - float scaleFac = GetScaleFactor(r.width, r.height); + float scaleFac = EditorGUIUtility.pixelsPerPoint; int rtWidth = (int)(r.width * scaleFac); int rtHeight = (int)(r.height * scaleFac); diff --git a/Editor/Mono/Inspector/PreviewWindow.cs b/Editor/Mono/Inspector/PreviewWindow.cs index 32dbf2ced1..ee74887ac9 100644 --- a/Editor/Mono/Inspector/PreviewWindow.cs +++ b/Editor/Mono/Inspector/PreviewWindow.cs @@ -52,7 +52,7 @@ protected override void OnEnable() protected override void OnDisable() { base.OnDisable(); - if (m_ParentInspectorWindow != null) + if (m_ParentInspectorWindow != null && GetInspectors().Contains(m_ParentInspectorWindow)) m_ParentInspectorWindow.RebuildContentsContainers(); } diff --git a/Editor/Mono/Inspector/PropertyEditor.cs b/Editor/Mono/Inspector/PropertyEditor.cs index 9bf3f126b3..0e0336ffe4 100644 --- a/Editor/Mono/Inspector/PropertyEditor.cs +++ b/Editor/Mono/Inspector/PropertyEditor.cs @@ -47,7 +47,7 @@ interface IPropertySourceOpener Object hoveredObject { get; } } - class PropertyEditor : EditorWindow, IPropertyView, IHasCustomMenu, IDataModeHandlerAndDispatcher + class PropertyEditor : EditorWindow, IPropertyView, IHasCustomMenu { internal const string k_AssetPropertiesMenuItemName = "Assets/Properties... _&P"; protected const string s_MultiEditClassName = "unity-inspector-no-multi-edit-warning"; @@ -89,6 +89,7 @@ class PropertyEditor : EditorWindow, IPropertyView, IHasCustomMenu, IDataModeHan [SerializeField] protected string m_GlobalObjectId = ""; [SerializeField] protected InspectorMode m_InspectorMode = InspectorMode.Normal; + private static readonly List m_AllPropertyEditors = new List(); private Object m_InspectedObject; private static PropertyEditor s_LastPropertyEditor; protected int m_LastInitialEditorInstanceID; @@ -126,10 +127,14 @@ class PropertyEditor : EditorWindow, IPropertyView, IHasCustomMenu, IDataModeHan protected bool m_HasPreview; protected HashSet m_DrawnSelection = new HashSet(); + readonly List m_EditorTargetTypes = new List(); + + List m_SupportedDataModes = new(4); + static readonly List k_DisabledDataModes = new() {DataMode.Disabled}; + public GUIView parent => m_Parent; public HashSet editorsWithImportedObjectLabel { get; } = new HashSet(); public EditorDragging editorDragging { get; } - internal int inspectorElementModeOverride { get; set; } = 0; public Editor lastInteractedEditor { get; set; } internal static PropertyEditor HoveredPropertyEditor { get; private set; } internal static PropertyEditor FocusedPropertyEditor { get; private set; } @@ -323,10 +328,10 @@ protected virtual void OnEnable() m_PreviewResizer.Init("InspectorPreview"); m_LabelGUI.OnEnable(); m_FirstInitialize = true; + var shouldUpdateSupportedDataModes = m_SerializedDataModeController == null; CreateTracker(); EditorApplication.focusChanged += OnFocusChanged; - EditorApplication.playModeStateChanged += OnPlayModeStateChanged; Undo.undoRedoEvent += OnUndoRedoPerformed; PrefabUtility.prefabInstanceUnpacked += OnPrefabInstanceUnpacked; ObjectChangeEvents.changesPublished += OnObjectChanged; @@ -337,6 +342,14 @@ protected virtual void OnEnable() rootVisualElement.RegisterCallback(OnMouseLeave); rootVisualElement.RegisterCallback(OnFocusIn); rootVisualElement.RegisterCallback(OnFocusOut); + + dataModeController.dataModeChanged += OnDataModeChanged; + EditorApplication.playModeStateChanged += OnPlayModeStateChanged; + + if (shouldUpdateSupportedDataModes) + EditorApplication.CallDelayed(UpdateSupportedDataModesList); + + if (!m_AllPropertyEditors.Contains(this)) m_AllPropertyEditors.Add(this); } [UsedImplicitly] @@ -349,7 +362,6 @@ protected virtual void OnDisable() m_LastVerticalScrollValue = m_ScrollView?.verticalScroller.value ?? 0; EditorApplication.focusChanged -= OnFocusChanged; - EditorApplication.playModeStateChanged -= OnPlayModeStateChanged; Undo.undoRedoEvent -= OnUndoRedoPerformed; PrefabUtility.prefabInstanceUnpacked -= OnPrefabInstanceUnpacked; ObjectChangeEvents.changesPublished -= OnObjectChanged; @@ -360,6 +372,11 @@ protected virtual void OnDisable() rootVisualElement.UnregisterCallback(OnMouseLeave); rootVisualElement.UnregisterCallback(OnFocusIn); rootVisualElement.UnregisterCallback(OnFocusOut); + + dataModeController.dataModeChanged -= OnDataModeChanged; + EditorApplication.playModeStateChanged -= OnPlayModeStateChanged; + + m_AllPropertyEditors.Remove(this); } private void OnMouseEnter(MouseEnterEvent e) => HoveredPropertyEditor = this; @@ -440,6 +457,12 @@ protected virtual void Update() UpdateWindowObjectNameTitle(); } + internal static IEnumerable GetPropertyEditors() + { + return m_AllPropertyEditors.AsEnumerable(); + } + + protected void SetMode(InspectorMode mode) { if (m_InspectorMode != mode) @@ -535,6 +558,23 @@ private void OnGeometryChanged(GeometryChangedEvent e) RestoreVerticalScrollIfNeeded(); } + internal static void ClearAndRebuildAll() + { + // Needs to be delayCall because it forces redrawing of UI which messes with the current IMGUI context of the Settings window. + EditorApplication.delayCall += ClearAndRebuildAllDelayed; + } + + static void ClearAndRebuildAllDelayed() + { + // Cannot use something like EditorUtility.ForceRebuildInspectors() because this only refreshes + // the inspector's values and IMGUI state, but otherwise, if the target did not change we + // re-use the Editors. We need a special clear function to properly recreate the UI using + // the new setting. + var propertyEditors = Resources.FindObjectsOfTypeAll(); + foreach (var propertyEditor in propertyEditors) + propertyEditor.ClearEditorsAndRebuild(); + } + internal void ClearEditorsAndRebuild() { // Clear the editors Element so that a real rebuild is done @@ -687,7 +727,6 @@ protected virtual void CreateTracker() m_Tracker = new ActiveEditorTracker { inspectorMode = InspectorMode.Normal }; if (LoadPersistedObject()) { - dataMode = GetLastKnownDataMode(); m_Tracker.ForceRebuild(); } } @@ -728,8 +767,23 @@ protected virtual void UpdateWindowObjectNameTitle() Repaint(); } - private void OnUndoRedoPerformed(in UndoRedoInfo info) + void OnUndoRedoPerformed(in UndoRedoInfo info) { + // Fix for a swapping an `m_Script` field in debug mode followed by an undo/redo. This causes the serialized object + // to be reset back to it's previous type. The editor instance remains the same, the serialized object remains the same and property iterators return + // the previous type properties. This breaks most assumptions in the inspectors and property drawers and causes numerous errors. As a patch we do a nuclear + // rebuild of everything if this state is detected. + var editors = tracker.activeEditors; + for (var i = 0; i < m_EditorTargetTypes.Count && i < editors.Length; i++) + { + var targetType = editors[i].target ? editors[i].target.GetType() : null; + + if (targetType == m_EditorTargetTypes[i]) + continue; + + ActiveEditorTracker.sharedTracker.ForceRebuild(); + } + // We need to detect and rebuild removed or suppressed component titlebars if the // backend changes. Situations where this detection is needed is when: // 1) Undo could cause a removed component to become a suppressed component and vice versa @@ -1005,6 +1059,13 @@ internal virtual void RebuildContentsContainers() Editor[] editors = tracker.activeEditors; + // Fix for a swapping an `m_Script` field in debug mode followed by an undo/redo. This causes the serialized object to be reset back to it's previous type. + // The editor instance remains the same, the serialized object remains the same and property iterators return the previous type properties. + // Here we track the last built editor types which is compared during the next undo-redo operation. + m_EditorTargetTypes.Clear(); + foreach (var editor in editors) + m_EditorTargetTypes.Add(editor.target ? editor.target.GetType() : null); + if (editors.Any() && versionControlElement != null) { versionControlElement.Add(CreateIMGUIContainer( @@ -1075,10 +1136,6 @@ internal virtual void RebuildContentsContainers() ScriptAttributeUtility.ClearGlobalCache(); EndRebuildContentContainers(); - - if (dataMode == DataMode.Disabled) - SwitchToDefaultDataMode(); - Repaint(); RefreshTitle(); } @@ -1297,7 +1354,7 @@ private Object[] GetInspectedAssets() // This is used if more than one asset is selected // Ideally the tracker should be refactored to track not just editors but also the selection that caused them, so we wouldn't need this - return Selection.objects.Where(IsOpenForEdit).ToArray(); + return Selection.objects.Where(EditorUtility.IsPersistent).ToArray(); } protected virtual bool BeginDrawPreviewAndLabels() { return true; } @@ -1467,7 +1524,7 @@ private void DrawPreviewAndLabels() GUILayout.BeginVertical(Styles.footer); if (hasLabels) { - using (new EditorGUI.DisabledScope(assets.Any(a => EditorUtility.IsPersistent(a) && !Editor.IsAppropriateFileOpenForEdit(a)))) + using (new EditorGUI.DisabledScope(assets.Any(a => !IsOpenForEdit(a) || !Editor.IsAppropriateFileOpenForEdit(a)))) { m_LabelGUI.OnLabelGUI(assets); } @@ -1475,7 +1532,10 @@ private void DrawPreviewAndLabels() if (hasBundleName) { - m_AssetBundleNameGUI.OnAssetBundleNameGUI(assets); + using (new EditorGUI.DisabledScope(assets.Any(a => !IsOpenForEdit(a)))) + { + m_AssetBundleNameGUI.OnAssetBundleNameGUI(assets); + } } GUILayout.EndVertical(); } @@ -1852,11 +1912,8 @@ private void DrawEditors(Editor[] editors) if (!(editor.target is ParticleSystemRenderer) && (mapping == null || !mapping.TryGetValue(editor.target.GetInstanceID(), out var culledEditorContainer))) { - string editorTitle = editorTarget == null - ? "Nothing Selected" - : ObjectNames.GetInspectorTitle(editorTarget); culledEditorContainer = - new UIElements.EditorElement(editorIndex, this, editors, true) { name = editorTitle }; + new UIElements.EditorElement(editorIndex, this, editors, true); editorsElement.Add(culledEditorContainer as VisualElement); if (!InspectorElement.disabledThrottling) @@ -1868,10 +1925,7 @@ private void DrawEditors(Editor[] editors) if (mapping == null || !mapping.TryGetValue(editors[editorIndex].target.GetInstanceID(), out var editorContainer)) { - string editorTitle = editorTarget == null ? - "Nothing Selected" : - $"{editor.GetType().Name}_{editorTarget.GetType().Name}_{editorTarget.GetInstanceID()}"; - editorContainer = new UIElements.EditorElement(editorIndex, this, editors) { name = editorTitle }; + editorContainer = new UIElements.EditorElement(editorIndex, this, editors); editorsElement.Add(editorContainer as VisualElement); if (!InspectorElement.disabledThrottling) m_EditorElementUpdater.Add(editorContainer); @@ -2278,6 +2332,7 @@ private Dictionary ProcessEditorElementsToRebuild(Editor[] return null; } + editors[newEditorsIndex].propertyViewer = this; currentElement.Reinit(newEditorsIndex, editors); editorToElementMap[ed.target.GetInstanceID()] = currentElement; ++newEditorsIndex; @@ -2288,6 +2343,7 @@ private Dictionary ProcessEditorElementsToRebuild(Editor[] for (int j = previousEditorsIndex; j < currentElements.Count; ++j) { currentElements[j].RemoveFromHierarchy(); + m_EditorElementUpdater.Remove(currentElements[j]); } return editorToElementMap; @@ -2372,123 +2428,52 @@ static void OpenHoveredItemPropertyEditor(ShortcutManagement.ShortcutArguments a } } - static readonly List k_DisabledDataModes = new() {DataMode.Disabled}; + internal static DataMode GetPreferredDataMode() + => EditorApplication.isPlaying + ? DataMode.Mixed + : DataMode.Authoring; - readonly List m_SupportedDataModes = new(4); + List GetSupportedDataModes() => m_SupportedDataModes; - bool areDataModesEnabled => - m_InspectorMode == InspectorMode.Normal && - m_SupportedDataModes.Count > 0; + bool m_IsEnteringPlaymode; - List GetSupportedDataModesForContext() => - areDataModesEnabled && m_SupportedDataModes.Count > 0 - ? m_SupportedDataModes - : k_DisabledDataModes; - - DataMode GetLastKnownDataMode() + void OnPlayModeStateChanged(PlayModeStateChange stateChange) { - if (areDataModesEnabled) - return EditorApplication.isPlaying - ? m_LastKnownPlayModeDataMode - : m_LastKnownEditModeDataMode; - - return DataMode.Disabled; - } - - [SerializeField] DataMode m_LastKnownEditModeDataMode = DataMode.Authoring; - [SerializeField] DataMode m_LastKnownPlayModeDataMode = DataMode.Runtime; - - public event Action dataModeChanged; - - // Caching the DataMode to avoid round-trips to native multiple times per frame. - // Note: DataMode.Disabled is the same default as in ActiveEditorTracker.cpp - DataMode m_DataMode = DataMode.Disabled; - - public DataMode dataMode - { - get => m_DataMode; - private set - { - if (m_DataMode == value) - return; - - tracker.dataMode = value; - m_DataMode = value; - } - } - - public IReadOnlyList supportedDataModes => GetSupportedDataModesForContext(); - - public bool IsDataModeSupported(DataMode mode) => GetSupportedDataModesForContext().Contains(mode); - - public void SwitchToNextDataMode() - { - var possibleDataModes = GetSupportedDataModesForContext(); - var nextIndex = possibleDataModes.IndexOf(dataMode) + 1; - if (nextIndex >= possibleDataModes.Count) - nextIndex = 0; + if (stateChange is not (PlayModeStateChange.EnteredEditMode or PlayModeStateChange.EnteredPlayMode)) + return; - SwitchToDataMode(possibleDataModes[nextIndex]); + m_IsEnteringPlaymode = true; + UpdateSupportedDataModesList(); } - public void SwitchToDefaultDataMode() + void OnDataModeChanged(DataModeChangeEventArgs evt) { - var selectedDataMode = GetLastKnownDataMode(); - - if (!IsDataModeSupported(selectedDataMode)) - { - // Fallback - selectedDataMode = supportedDataModes[0]; - } - - SwitchToDataMode(selectedDataMode); + tracker.dataMode = evt.nextDataMode; + tracker.ForceRebuild(); } - public void SwitchToDataMode(DataMode mode) + protected void UpdateSupportedDataModesList() { - if (!IsDataModeSupported(mode)) - SwitchToDefaultDataMode(); - - if (dataMode == mode) - return; + m_SupportedDataModes.Clear(); + OnUpdateSupportedDataModes(m_SupportedDataModes); + m_SupportedDataModes.Sort(); - dataMode = mode; - tracker.ForceRebuild(); + if (m_InspectorMode != InspectorMode.Normal || m_SupportedDataModes.Count == 0) + m_SupportedDataModes = k_DisabledDataModes; - dataModeChanged?.Invoke(dataMode); - } + var dataMode = Selection.dataModeHint == DataMode.Disabled ? GetPreferredDataMode() : Selection.dataModeHint; - void OnPlayModeStateChanged(PlayModeStateChange playModeStateChange) - { - switch (playModeStateChange) + // When entering playmode, Inspector relies on getting selection hint + // from Hierarchy window to switch to the proper data mode. + // However when inspector is locked, selection is locked too. + // Here we force the preferred data mode to inspector for this special case. + if (m_IsEnteringPlaymode && m_Tracker.isLocked) { - case PlayModeStateChange.ExitingEditMode: - { - // We're about to exit edit mode, save the last known DataMode - m_LastKnownEditModeDataMode = dataMode; - break; - } - case PlayModeStateChange.ExitingPlayMode: - { - // We're about to exit play mode, save the last known DataMode - m_LastKnownPlayModeDataMode = dataMode; - break; - } - case PlayModeStateChange.EnteredEditMode: - case PlayModeStateChange.EnteredPlayMode: - { - UpdateSupportedDataModes(); - SwitchToDefaultDataMode(); - break; - } + dataMode = GetPreferredDataMode(); + m_IsEnteringPlaymode = false; } - } - protected void UpdateSupportedDataModes() - { - m_SupportedDataModes.Clear(); - OnUpdateSupportedDataModes(m_SupportedDataModes); - m_SupportedDataModes.Sort(); + dataModeController.UpdateSupportedDataModes(m_SupportedDataModes, dataMode); } protected virtual void OnUpdateSupportedDataModes(List supportedModes) diff --git a/Editor/Mono/Inspector/QualitySettingsEditor.cs b/Editor/Mono/Inspector/QualitySettingsEditor.cs index c96b3cbbf6..b2f0c0fa11 100644 --- a/Editor/Mono/Inspector/QualitySettingsEditor.cs +++ b/Editor/Mono/Inspector/QualitySettingsEditor.cs @@ -31,13 +31,13 @@ private class Content }; public static readonly GUIContent[] kTextureMipmapLimitGroupsOffsetModeItems = { - EditorGUIUtility.TrTextContent("Offset Global Mipmap Limit: -3", "Upload 3 mips less compared to the Global Mipmap Limit."), - EditorGUIUtility.TrTextContent("Offset Global Mipmap Limit: -2", "Upload 2 mips less compared to the Global Mipmap Limit."), - EditorGUIUtility.TrTextContent("Offset Global Mipmap Limit: -1", "Upload 1 mip less compared to the Global Mipmap Limit."), + EditorGUIUtility.TrTextContent("Offset Global Mipmap Limit: -3", "Upload 3 mips extra compared to the Global Mipmap Limit."), + EditorGUIUtility.TrTextContent("Offset Global Mipmap Limit: -2", "Upload 2 mips extra compared to the Global Mipmap Limit."), + EditorGUIUtility.TrTextContent("Offset Global Mipmap Limit: -1", "Upload 1 mip extra compared to the Global Mipmap Limit."), EditorGUIUtility.TrTextContent("Use Global Mipmap Limit", "No offset or override occurs, simply use the Global Mipmap Limit. (Default)"), - EditorGUIUtility.TrTextContent("Offset Global Mipmap Limit: +1", "Upload 1 mip extra compared to the Global Mipmap Limit."), - EditorGUIUtility.TrTextContent("Offset Global Mipmap Limit: +2", "Upload 2 mips extra compared to the Global Mipmap Limit."), - EditorGUIUtility.TrTextContent("Offset Global Mipmap Limit: +3", "Upload 3 mips extra compared to the Global Mipmap Limit.") + EditorGUIUtility.TrTextContent("Offset Global Mipmap Limit: +1", "Upload 1 mip less compared to the Global Mipmap Limit."), + EditorGUIUtility.TrTextContent("Offset Global Mipmap Limit: +2", "Upload 2 mips less compared to the Global Mipmap Limit."), + EditorGUIUtility.TrTextContent("Offset Global Mipmap Limit: +3", "Upload 3 mips less compared to the Global Mipmap Limit.") }; public static readonly GUIContent kTextureMipmapLimitGroupsOptions = EditorGUIUtility.TrIconContent("_Menu", "Show additional options"); public static readonly GUIContent kTextureMipmapLimitGroupsOptionsIdentify = EditorGUIUtility.TrTextContent("Identify textures"); @@ -46,12 +46,13 @@ private class Content public static readonly GUIContent kTextureMipmapLimitGroupsAddButton = EditorGUIUtility.TrIconContent("Toolbar Plus", "Create a new mipmap limit group. Note that this adds a group to all quality levels, not only the active one!"); public static readonly GUIContent kTextureMipmapLimitGroupsRemoveButton = EditorGUIUtility.TrIconContent("Toolbar Minus", "Remove mipmap limit group. Note that this removes the group from all quality levels, not only the active one!"); - public static readonly string kTextureMipmapLimitGroupsDialogTitle = L10n.Tr("Mipmap Limit Groups"); - public static readonly string kTextureMipmapLimitGroupsDialogMessageOnRemove = L10n.Tr("Textures in your project may still be using '{0}'.\n\nSelect 'No' to remove '{0}' without modifying its associated textures. Relevant textures stay bound to '{0}' and fall back automatically to the global mipmap limit.\n\nSelect 'Yes' to remove '{0}' and reset the group property of associated textures to 'None'. This triggers a re-import and may take some time.\n\nSelect 'Cancel' if you do not wish to remove '{0}' anymore."); - public static readonly string kTextureMipmapLimitGroupsDialogMessageOnRename = L10n.Tr("Textures in your project may still be using '{0}'.\n\nSelect 'No' to rename '{0}' without modifying its associated textures. Relevant textures stay bound to '{0}' and fall back automatically to the global mipmap limit.\n\nSelect 'Yes' to rename '{0}' and update the group property of associated textures to '{1}'. This triggers a re-import and may take some time.\n\nSelect 'Cancel' if you do not wish to rename '{0}' anymore."); - public static readonly string kTextureMipmapLimitGroupsDialogMessageOnRenameFail = L10n.Tr("The mipmap limit group '{0}' already exists!\n'{1}' was not renamed."); + public static readonly string kTextureMipmapLimitGroupsDialogTitleOnUpdate = L10n.Tr("Mipmap Limit Groups: Update textures?"); + public static readonly string kTextureMipmapLimitGroupsDialogMessageOnRemove = L10n.Tr("Textures in your project may still be using '{0}'.\n\nSelect 'No' to remove the group without modifying its associated textures. Relevant textures stay bound to the group and fall back automatically to the global mipmap limit.\n\nSelect 'Yes' to remove the group and reset the group property of associated textures to 'None'. This triggers a re-import and may take some time. An undo cannot revert the importer changes."); + public static readonly string kTextureMipmapLimitGroupsDialogMessageOnRename = L10n.Tr("Textures in your project may still be using '{0}'.\n\nSelect 'No' to rename the group without modifying its associated textures. Relevant textures stay bound to the group and fall back automatically to the global mipmap limit.\n\nSelect 'Yes' to rename the group and update the group property of associated textures to '{1}'. This triggers a re-import and may take some time. An undo cannot revert the importer changes."); + public static readonly string kTextureMipmapLimitGroupsDialogTitleOnFailure = L10n.Tr("Mipmap Limit Groups: Operation failed"); + public static readonly string kTextureMipmapLimitGroupsDialogMessageOnRenameFail = L10n.Tr("The mipmap limit group '{0}' already exists.\n'{1}' was not renamed."); public static readonly string kTextureMipmapLimitGroupsDialogMessageOnUpdateAssetsError = L10n.Tr("An error occured while updating texture assets: {0}"); - public static readonly string kTextureMipmapLimitGroupsDialogMessageOnIdentifyFail = L10n.Tr("No textures are linked to the mipmap limit group '{0}'!"); + public static readonly string kTextureMipmapLimitGroupsDialogMessageOnIdentifyFail = L10n.Tr("No textures are linked to the mipmap limit group '{0}'."); public static readonly GUIContent kStreamingMipmapsActive = EditorGUIUtility.TrTextContent("Texture Streaming", "When enabled, Unity only streams texture mipmaps relevant to the current Camera's position in a Scene. This reduces the total amount of memory Unity needs for textures. Individual textures must also have 'Streaming Mip Maps' enabled in their Import Settings."); public static readonly GUIContent kStreamingMipmapsMemoryBudget = EditorGUIUtility.TrTextContent("Memory Budget", "The amount of memory (in megabytes) to allocate for all loaded textures."); @@ -65,6 +66,7 @@ private class Content public static readonly GUIContent kBillboardsFaceCameraPos = EditorGUIUtility.TrTextContent("Billboards Face Camera Position", "When enabled, terrain billboards face towards the camera position. Otherwise, they face towards the camera plane. This makes billboards look nicer when the camera rotates but it is more resource intensive to process."); public static readonly GUIContent kUseLegacyDistribution = EditorGUIUtility.TrTextContent("Use Legacy Details Distribution", "When enabled, terrain details will be scattered using the old scattering algorithm that often resulted in overlapping details. Included for backwards compatibility with terrain authored in Unity 2022.1 and earlier."); public static readonly GUIContent kVSyncCountLabel = EditorGUIUtility.TrTextContent("VSync Count", "Specifies how Unity synchronizes rendering with the refresh rate of the display device."); + public static readonly GUIContent kRealtimeLGiCpuUsageLabel = EditorGUIUtility.TrTextContent("Realtime GI CPU Usage", "How many CPU worker threads to create for Realtime Global Illumination lighting calculations in the Player. Increasing this makes the system react faster to changes in lighting at a cost of using more CPU time. The higher the CPU Usage value, the more worker threads are created for solving Realtime GI."); public static readonly GUIContent kLODBiasLabel = EditorGUIUtility.TrTextContent("LOD Bias", "The bias Unity uses to determine which model to render when a GameObject’s on-screen size is between two LOD levels. Values between 0 and 1 favor the less detailed model. Values greater than 1 favor the more detailed model."); public static readonly GUIContent kMaximumLODLevelLabel = EditorGUIUtility.TrTextContent("Maximum LOD Level", "The highest LOD to use in the application."); public static readonly GUIContent kEnableLODCrossFadeLabel = EditorGUIUtility.TrTextContent("LOD Cross Fade", "Enables or disables LOD Cross Fade."); @@ -73,6 +75,7 @@ private class Content public static readonly GUIContent kAsyncUploadTimeSlice = EditorGUIUtility.TrTextContent("Time Slice", "The amount of time (in milliseconds) Unity spends uploading Texture and Mesh data to the GPU per frame."); public static readonly GUIContent kAsyncUploadBufferSize = EditorGUIUtility.TrTextContent("Buffer Size", "The size (in megabytes) of the upload buffer Unity uses to stream Texture and Mesh data to GPU."); public static readonly GUIContent kAsyncUploadPersistentBuffer = EditorGUIUtility.TrTextContent("Persistent Buffer", "When enabled, the upload buffer persists even when there is nothing left to upload."); + public static readonly GUIContent kAsyncUploadBufferSizeWarning = EditorGUIUtility.TrTextContent("Unity has detected that you are using an upload buffer size of {0} MB with the '{1}' setting enabled. If you have issues with excessive memory usage, you may need to reduce the upload buffer size or disable the '{1}' setting. Memory fragmentation can occur if you choose the latter option."); public static readonly GUIContent kOverrideTerrainPixelError = EditorGUIUtility.TrTextContent("", "Whether to override pixel error in active Terrains."); public static readonly GUIContent kOverrideTerrainBasemapDist = EditorGUIUtility.TrTextContent("", "Whether to override base map distance in active Terrains."); @@ -119,7 +122,8 @@ private class Styles } public const int kMinAsyncRingBufferSize = 2; - public const int kMaxAsyncRingBufferSize = 512; + public const int kMaxAsyncRingBufferSize = 2047; + public const int kAsyncRingBufferSizeWarningThreshold = 513; public const int kMinAsyncUploadTimeSlice = 1; public const int kMaxAsyncUploadTimeSlice = 33; @@ -647,6 +651,7 @@ public override void OnInspectorGUI() var terrainFadeLengthProperty = currentSettings.FindPropertyRelative("terrainFadeLength"); var terrainMaxTreesProperty = currentSettings.FindPropertyRelative("terrainMaxTrees"); var vSyncCountProperty = currentSettings.FindPropertyRelative("vSyncCount"); + var realtimeGICPUUsageProperty = currentSettings.FindPropertyRelative("realtimeGICPUUsage"); var lodBiasProperty = currentSettings.FindPropertyRelative("lodBias"); var maximumLODLevelProperty = currentSettings.FindPropertyRelative("maximumLODLevel"); var enableLODCrossFadeProperty = currentSettings.FindPropertyRelative("enableLODCrossFade"); @@ -702,6 +707,9 @@ public override void OnInspectorGUI() EditorGUILayout.HelpBox(EditorGUIUtility.TrTextContent($"VSync Count '{vSyncCountProperty.enumLocalizedDisplayNames[vSyncCountProperty.enumValueIndex]}' is ignored on Android, iOS and tvOS.", EditorGUIUtility.GetHelpIcon(MessageType.Warning))); } + if (usingSRP) + EditorGUILayout.PropertyField(realtimeGICPUUsageProperty, Content.kRealtimeLGiCpuUsageLabel); + bool shadowMaskSupported = SupportedRenderingFeatures.IsMixedLightingModeSupported(MixedLightingMode.Shadowmask); bool showShadowMaskUsage = shadowMaskSupported && !SupportedRenderingFeatures.active.overridesShadowmask; @@ -853,6 +861,12 @@ public override void OnInspectorGUI() asyncUploadTimeSliceProperty.intValue = Mathf.Clamp(asyncUploadTimeSliceProperty.intValue, kMinAsyncUploadTimeSlice, kMaxAsyncUploadTimeSlice); asyncUploadBufferSizeProperty.intValue = Mathf.Clamp(asyncUploadBufferSizeProperty.intValue, kMinAsyncRingBufferSize, kMaxAsyncRingBufferSize); + if (asyncUploadBufferSizeProperty.intValue >= kAsyncRingBufferSizeWarningThreshold && asyncUploadPersistentBufferProperty.boolValue) + { + string messageToDisplay = string.Format(Content.kAsyncUploadBufferSizeWarning.text, asyncUploadBufferSizeProperty.intValue, Content.kAsyncUploadPersistentBuffer.text); + EditorGUILayout.HelpBox(messageToDisplay, MessageType.Warning, false); + } + GUILayout.Space(10); GUILayout.Label(EditorGUIUtility.TempContent("Level of Detail"), EditorStyles.boldLabel); @@ -1026,37 +1040,40 @@ void DoTextureMipmapLimitGroupNameTextField(Rect rect, GUIContent label) { const string controlName = "TextFieldTextureMipmapLimitGroup"; - using (var changed = new EditorGUI.ChangeCheckScope()) - { - GUI.SetNextControlName(controlName); - string newName = EditorGUI.DelayedTextField(rect, label.text, EditorStyles.textField); + GUI.SetNextControlName(controlName); + EditorGUI.DelayedTextField(rect, label.text, EditorStyles.textField); - Event e = Event.current; - if (m_TextureMipmapLimitGroupsTextFieldNeedsFocus) + Event e = Event.current; + if (m_TextureMipmapLimitGroupsTextFieldNeedsFocus) + { + if (e.type == EventType.Repaint) // Wait until all other events are out of the way { - if (e.type == EventType.Repaint) // Wait until all other events are out of the way - { - EditorGUI.s_DelayedTextEditor.text = label.text; // Should already be the case, but just to make sure. - EditorGUI.s_DelayedTextEditor.SelectAll(); - EditorGUI.FocusTextInControl(controlName); - m_TextureMipmapLimitGroupsTextFieldNeedsFocus = false; - } + EditorGUI.s_DelayedTextEditor.text = label.text; // Should already be the case, but just to make sure. + EditorGUI.s_DelayedTextEditor.SelectAll(); + EditorGUI.FocusTextInControl(controlName); + m_TextureMipmapLimitGroupsTextFieldNeedsFocus = false; } - else + } + else + { + // If clicking out, the rename is NOT cancelled. + if (e.isMouse && !rect.Contains(e.mousePosition)) { - if (changed.changed) - { - EndRenamingTextureMipmapLimitGroup(newName); - } - // If clicking out, the rename is NOT cancelled. - else if (e.isMouse && !rect.Contains(e.mousePosition)) + EndRenamingTextureMipmapLimitGroup(EditorGUI.s_DelayedTextEditor.text); + } + // If editing stops or focus is lost, submit the current content of the text editor. + // Pressing ESC effectively cancels the rename. Pressing Enter, Tab or clicking away + // submits the user's new group name. (renaming is cancelled if the name didn't change) + else if (!EditorGUIUtility.editingTextField || GUI.GetNameOfFocusedControl() != controlName) + { + // Cannot open a dialog box until a Repaint event if the user clicked away from the QualitySettingsEditor. + if (e.type == EventType.Repaint) { EndRenamingTextureMipmapLimitGroup(EditorGUI.s_DelayedTextEditor.text); + GUIUtility.ExitGUI(); // Prevents layout errors when clicking on certain other windows. (Hierarchy) } - else if (!EditorGUIUtility.editingTextField) - { - EndRenamingTextureMipmapLimitGroup(); - } + else + GUI.InternalRepaintEditorWindow(); // Prevents missing controls when window doesn't repaint on its own. } } } @@ -1141,8 +1158,8 @@ void RemoveTextureMipmapLimitGroup(ReorderableList list) if (!Presets.Preset.IsEditorTargetAPreset(target)) { - int selection = EditorUtility.DisplayDialogComplex(Content.kTextureMipmapLimitGroupsDialogTitle, - string.Format(Content.kTextureMipmapLimitGroupsDialogMessageOnRemove, nameOfGroupToRemove), + int selection = EditorUtility.DisplayDialogComplex(Content.kTextureMipmapLimitGroupsDialogTitleOnUpdate, + string.Format(Content.kTextureMipmapLimitGroupsDialogMessageOnRemove, GetShortTextureMipmapLimitGroupName(nameOfGroupToRemove)), L10n.Tr("No"), L10n.Tr("Cancel"), L10n.Tr("Yes")); switch (selection) @@ -1203,14 +1220,23 @@ void DuplicateTextureMipmapLimitGroup(int indexOfGroupToDuplicate) void UpdateTextureAssetsLinkedToOldTextureMipmapLimitGroup(string oldName, string newName) { + // If we operate on importers while they are selected, the user will still be prompted to confirm changes + // that they already agreed to. To avoid this: reset the selection, and restore it after we're done. + Object[] originalSelection = Selection.objects; + try { - string[] guids = AssetDatabase.FindAssets("t:texture"); + Selection.objects = null; + + string[] guids = AssetDatabase.FindAssets("t:texture t:preset"); AssetDatabase.StartAssetEditing(); for (int i = 0; i < guids.Length; ++i) { - AssetImporter importer = AssetImporter.GetAtPath(AssetDatabase.GUIDToAssetPath(guids[i])); + string assetPath = AssetDatabase.GUIDToAssetPath(guids[i]); + Presets.Preset preset = AssetDatabase.LoadMainAssetAtPath(assetPath) as Presets.Preset; + bool isPreset = preset is not null; + AssetImporter importer = isPreset ? preset.GetReferenceObject() as AssetImporter : AssetImporter.GetAtPath(assetPath); if (importer is TextureImporter) { @@ -1218,7 +1244,13 @@ void UpdateTextureAssetsLinkedToOldTextureMipmapLimitGroup(string oldName, strin if (texImporter.textureShape == TextureImporterShape.Texture2D && texImporter.mipmapLimitGroupName == oldName) { texImporter.mipmapLimitGroupName = newName; - importer.SaveAndReimport(); + if (!isPreset) + importer.SaveAndReimport(); + else + { + preset.UpdateProperties(importer); + AssetDatabase.ImportAsset(assetPath); + } } } else if (importer is IHVImageFormatImporter) @@ -1227,20 +1259,27 @@ void UpdateTextureAssetsLinkedToOldTextureMipmapLimitGroup(string oldName, strin if (ihvImporter.mipmapLimitGroupName == oldName) { ihvImporter.mipmapLimitGroupName = newName; - importer.SaveAndReimport(); + if (!isPreset) + importer.SaveAndReimport(); + else + { + preset.UpdateProperties(importer); + AssetDatabase.ImportAsset(assetPath); + } } } } } catch (System.Exception e) { - EditorUtility.DisplayDialog(Content.kTextureMipmapLimitGroupsDialogTitle, + EditorUtility.DisplayDialog(Content.kTextureMipmapLimitGroupsDialogTitleOnFailure, string.Format(Content.kTextureMipmapLimitGroupsDialogMessageOnUpdateAssetsError, e.Message), L10n.Tr("OK")); } finally { AssetDatabase.StopAssetEditing(); + Selection.objects = originalSelection; } } @@ -1275,8 +1314,8 @@ void IdentifyAssetsUsingTextureMipmapLimitGroup(string groupNameToIdentify) } else { - EditorUtility.DisplayDialog(Content.kTextureMipmapLimitGroupsDialogTitle, - string.Format(Content.kTextureMipmapLimitGroupsDialogMessageOnIdentifyFail, groupNameToIdentify), + EditorUtility.DisplayDialog(Content.kTextureMipmapLimitGroupsDialogTitleOnFailure, + string.Format(Content.kTextureMipmapLimitGroupsDialogMessageOnIdentifyFail, GetShortTextureMipmapLimitGroupName(groupNameToIdentify)), L10n.Tr("OK")); } } @@ -1311,12 +1350,15 @@ void EndRenamingTextureMipmapLimitGroup(string newName = "") return; } + string shortNewName = GetShortTextureMipmapLimitGroupName(newName); + string shortToRename = GetShortTextureMipmapLimitGroupName(toRename); + for (int i = 0; i < m_TextureMipmapLimitGroupNamesProperty.arraySize; ++i) { if (m_TextureMipmapLimitGroupNamesProperty.GetArrayElementAtIndex(i).stringValue == newName) { - EditorUtility.DisplayDialog(Content.kTextureMipmapLimitGroupsDialogTitle, - string.Format(Content.kTextureMipmapLimitGroupsDialogMessageOnRenameFail, newName, toRename), + EditorUtility.DisplayDialog(Content.kTextureMipmapLimitGroupsDialogTitleOnFailure, + string.Format(Content.kTextureMipmapLimitGroupsDialogMessageOnRenameFail, shortNewName, shortToRename), L10n.Tr("OK")); return; } @@ -1325,8 +1367,8 @@ void EndRenamingTextureMipmapLimitGroup(string newName = "") bool applyModifiedProperties = true; if (m_TextureMipmapLimitGroupsRenameShowUpdatePrompt) { - int selection = EditorUtility.DisplayDialogComplex(Content.kTextureMipmapLimitGroupsDialogTitle, - string.Format(Content.kTextureMipmapLimitGroupsDialogMessageOnRename, toRename, newName), + int selection = EditorUtility.DisplayDialogComplex(Content.kTextureMipmapLimitGroupsDialogTitleOnUpdate, + string.Format(Content.kTextureMipmapLimitGroupsDialogMessageOnRename, shortToRename, shortNewName), L10n.Tr("No"), L10n.Tr("Cancel"), L10n.Tr("Yes")); switch (selection) @@ -1380,6 +1422,20 @@ string[] GetAllKnownTextureMipmapLimitGroupNames() return existingNames; } + // Only meant to limit the length of various messages addressed to the user that concern mipmap limit groups. + // Don't use elsewhere. + string GetShortTextureMipmapLimitGroupName(string groupName) + { + const int maxGroupNameLength = 41; + // ^ Cannot fit more than this many characters on 1 line. + if (groupName.Length > maxGroupNameLength) + { + const string suffix = " (...)"; + groupName = groupName.Substring(0, maxGroupNameLength - suffix.Length) + suffix; + } + return groupName; + } + [SettingsProvider] internal static SettingsProvider CreateProjectSettingsProvider() { diff --git a/Editor/Mono/Inspector/ReflectionProbeEditor.cs b/Editor/Mono/Inspector/ReflectionProbeEditor.cs index a9eb5df846..5da525110e 100644 --- a/Editor/Mono/Inspector/ReflectionProbeEditor.cs +++ b/Editor/Mono/Inspector/ReflectionProbeEditor.cs @@ -239,7 +239,7 @@ public void OnDisable() private bool IsCollidingWithOtherProbes(string targetPath, ReflectionProbe targetProbe, out ReflectionProbe collidingProbe) { - ReflectionProbe[] probes = FindObjectsOfType().ToArray(); + ReflectionProbe[] probes = FindObjectsByType(FindObjectsSortMode.InstanceID).ToArray(); collidingProbe = null; foreach (var probe in probes) { @@ -692,7 +692,7 @@ private void OnPreSceneGUICallback(SceneView sceneView) if (!reflectiveMaterial) return; - if (!StageUtility.IsGameObjectRenderedByCameraAndPartOfEditableScene(p.gameObject, Camera.current)) + if (StageUtility.IsGizmoCulledBySceneCullingMasksOrFocusedScene(p.gameObject, Camera.current)) return; Matrix4x4 m = new Matrix4x4(); diff --git a/Editor/Mono/Inspector/RenderPipelineEditorUtility.cs b/Editor/Mono/Inspector/RenderPipelineEditorUtility.cs index 61edf8d303..690db4bc23 100644 --- a/Editor/Mono/Inspector/RenderPipelineEditorUtility.cs +++ b/Editor/Mono/Inspector/RenderPipelineEditorUtility.cs @@ -9,42 +9,27 @@ namespace UnityEditor.Rendering { - [AttributeUsage(AttributeTargets.Class)] - public class ScriptableRenderPipelineExtensionAttribute : Attribute - { - internal Type renderPipelineType; - - public ScriptableRenderPipelineExtensionAttribute(Type renderPipelineAsset) - { - if (!(renderPipelineAsset?.IsSubclassOf(typeof(RenderPipelineAsset)) ?? false)) - throw new ArgumentException("Given renderPipelineAsset must derive from RenderPipelineAsset"); - renderPipelineType = renderPipelineAsset; - } - - public bool inUse - => GraphicsSettings.currentRenderPipeline?.GetType() == renderPipelineType; - } - public static class RenderPipelineEditorUtility { public static Type[] GetDerivedTypesSupportedOnCurrentPipeline() { - return TypeCache.GetTypesDerivedFrom().Where(t => - { - var attribute = t.GetCustomAttribute(); - return attribute != null && attribute.isSupportedOnCurrentPipeline; - - }).ToArray(); + return TypeCache.GetTypesDerivedFrom() + .Where(t => t.GetCustomAttribute() is { isSupportedOnCurrentPipeline: true }) + .ToArray(); } + [Obsolete($"{nameof(FetchFirstCompatibleTypeUsingScriptableRenderPipelineExtension)} is deprecated. Use {nameof(GetDerivedTypesSupportedOnCurrentPipeline)} instead. #from(2023.1)", false)] public static Type FetchFirstCompatibleTypeUsingScriptableRenderPipelineExtension() { var extensionTypes = TypeCache.GetTypesDerivedFrom(); foreach (Type extensionType in extensionTypes) { +#pragma warning disable CS0618 if (Attribute.GetCustomAttribute(extensionType, typeof(ScriptableRenderPipelineExtensionAttribute)) is ScriptableRenderPipelineExtensionAttribute { inUse: true }) return extensionType; +#pragma warning restore CS0618 + } return null; diff --git a/Editor/Mono/Inspector/RenderTextureEditor.cs b/Editor/Mono/Inspector/RenderTextureEditor.cs index db07e6b22d..d539d32dbf 100644 --- a/Editor/Mono/Inspector/RenderTextureEditor.cs +++ b/Editor/Mono/Inspector/RenderTextureEditor.cs @@ -27,6 +27,7 @@ private class Styles public readonly GUIContent enableMipmaps = EditorGUIUtility.TrTextContent("Enable Mip Maps", "This render texture will have Mip Maps."); public readonly GUIContent autoGeneratesMipmaps = EditorGUIUtility.TrTextContent("Auto generate Mip Maps", "This render texture automatically generates its Mip Maps."); public readonly GUIContent useDynamicScale = EditorGUIUtility.TrTextContent("Dynamic Scaling", "Allow the texture to be automatically resized by ScalableBufferManager, to support dynamic resolution."); + public readonly GUIContent enableRandomWrite = EditorGUIUtility.TrTextContent("Random Write", "Enable/disable random access write into the color buffer of this render texture."); public readonly GUIContent shadowSamplingMode = EditorGUIUtility.TrTextContent("Shadow Sampling Mode", "Enable/disable shadow depth-compare sampling and percentage closer filtering."); public readonly GUIContent[] renderTextureAntiAliasing = @@ -68,6 +69,7 @@ protected enum GUIElements SerializedProperty m_Dimension; SerializedProperty m_sRGB; SerializedProperty m_UseDynamicScale; + SerializedProperty m_EnableRandomWrite; SerializedProperty m_ShadowSamplingMode; protected override void OnEnable() @@ -85,6 +87,7 @@ protected override void OnEnable() m_Dimension = serializedObject.FindProperty("m_Dimension"); m_sRGB = serializedObject.FindProperty("m_SRGB"); m_UseDynamicScale = serializedObject.FindProperty("m_UseDynamicScale"); + m_EnableRandomWrite = serializedObject.FindProperty("m_EnableRandomWrite"); m_ShadowSamplingMode = serializedObject.FindProperty("m_ShadowSamplingMode"); Undo.undoRedoEvent += OnUndoRedoPerformed; @@ -220,6 +223,7 @@ protected void OnRenderTextureGUI(GUIElements guiElements) } EditorGUILayout.PropertyField(m_UseDynamicScale, styles.useDynamicScale); + EditorGUILayout.PropertyField(m_EnableRandomWrite, styles.enableRandomWrite); var rt = target as RenderTexture; if (GUI.changed && rt != null) diff --git a/Editor/Mono/Inspector/RendererLightingSettings.cs b/Editor/Mono/Inspector/RendererLightingSettings.cs index 818fba4046..091251a7a5 100644 --- a/Editor/Mono/Inspector/RendererLightingSettings.cs +++ b/Editor/Mono/Inspector/RendererLightingSettings.cs @@ -375,6 +375,7 @@ public void RenderTerrainSettings() { EditorGUILayout.HelpBox(Styles.giNotEnabledInfo.text, MessageType.Info); EditorGUI.indentLevel -= 1; + EditorGUILayout.EndFoldoutHeaderGroup(); return; } @@ -594,24 +595,6 @@ void ShowAtlasGUI(int instanceID, bool isMeshRenderer) GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); - bool showProgressiveInfo = !isPreset && settings.bakedGI; - - if (showProgressiveInfo && Unsupported.IsDeveloperMode()) - { - Hash128 instanceHash; - Lightmapping.GetPVRInstanceHash(instanceID, out instanceHash); - EditorGUILayout.LabelField(Styles.pvrInstanceHash, GUIContent.Temp(instanceHash.ToString())); - - Hash128 atlasHash; - Lightmapping.GetPVRAtlasHash(instanceID, out atlasHash); - EditorGUILayout.LabelField(Styles.pvrAtlasHash, GUIContent.Temp(atlasHash.ToString())); - - int atlasInstanceOffset; - Lightmapping.GetPVRAtlasInstanceOffset(instanceID, out atlasInstanceOffset); - EditorGUILayout.LabelField(Styles.pvrAtlasInstanceOffset, GUIContent.Temp(atlasInstanceOffset.ToString())); - } - EditorGUI.indentLevel -= 1; - GUILayout.Space(5); } diff --git a/Editor/Mono/Inspector/ReorderableListWrapper.cs b/Editor/Mono/Inspector/ReorderableListWrapper.cs index b2a5dd1139..66e085a3df 100644 --- a/Editor/Mono/Inspector/ReorderableListWrapper.cs +++ b/Editor/Mono/Inspector/ReorderableListWrapper.cs @@ -24,10 +24,13 @@ public static class Constants internal ReorderableList m_ReorderableList; float m_HeaderHeight; bool m_Reorderable = false; - bool m_IsNotInPrefabContextModeWithOverrides = false; + bool m_ListIsPatchedInPrefabModeInContext = false; + bool m_DisableListElements = false; SerializedProperty m_OriginalProperty; SerializedProperty m_ArraySize; + string m_PropertyPath = string.Empty; + string m_PropertyPathArraySize = string.Empty; internal static Rect s_ToolTipRect; @@ -41,14 +44,23 @@ internal SerializedProperty Property set { m_OriginalProperty = value; - if (!m_OriginalProperty.isValid) return; + if (!m_OriginalProperty.isValid) + { + m_ArraySize = null; + m_PropertyPath = string.Empty; + m_PropertyPathArraySize = string.Empty; + return; + } m_ArraySize = m_OriginalProperty.FindPropertyRelative("Array.size"); + m_PropertyPath = m_OriginalProperty.propertyPath; + m_PropertyPathArraySize = m_OriginalProperty + ".Array.size"; if (m_ReorderableList != null) { bool versionChanged = !SerializedProperty.VersionEquals(m_ReorderableList.serializedProperty, m_OriginalProperty); m_ReorderableList.serializedProperty = m_OriginalProperty; + UpdatePrefabPatchState(m_OriginalProperty.serializedObject.targetObject); if (versionChanged || m_ArraySize != null && m_LastArraySize != m_ArraySize.intValue) { @@ -78,18 +90,16 @@ public static string GetPropertyIdentifier(SerializedProperty serializedProperty public ReorderableListWrapper(SerializedProperty property, GUIContent label, bool reorderable = true) { - Property = property; - m_HeaderHeight = Constants.kDefaultFoldoutHeaderHeight; - Init(reorderable); + Init(reorderable, property); } - void Init(bool reorderable) + void Init(bool reorderable, SerializedProperty property) { m_Reorderable = reorderable; - SerializedProperty childProperty = Property.Copy(); + SerializedProperty childProperty = property.Copy(); childProperty.Next(true); - m_ReorderableList = new ReorderableList(Property.serializedObject, Property.Copy(), m_Reorderable, false, true, true); + m_ReorderableList = new ReorderableList(property.serializedObject, property.Copy(), m_Reorderable, false, true, true); m_ReorderableList.headerHeight = ReorderableList.Defaults.minHeaderHeight; m_ReorderableList.m_IsEditable = true; m_ReorderableList.multiSelect = true; @@ -98,13 +108,16 @@ void Init(bool reorderable) m_ReorderableList.onCanAddCallback += (list) => { - return m_IsNotInPrefabContextModeWithOverrides; + return !m_ListIsPatchedInPrefabModeInContext; }; m_ReorderableList.onCanRemoveCallback += (list) => { - return m_IsNotInPrefabContextModeWithOverrides; + return !m_ListIsPatchedInPrefabModeInContext; }; + + Property = property; + m_HeaderHeight = Constants.kDefaultFoldoutHeaderHeight; } internal void InvalidateCache() => m_ReorderableList.InvalidateCache(); @@ -114,13 +127,28 @@ public float GetHeight() return m_HeaderHeight + (Property.isExpanded && m_ReorderableList != null ? Constants.kHeaderPadding + m_ReorderableList.GetHeight() : 0.0f); } + void UpdatePrefabPatchState(Object serializedObjectTarget) + { + m_DisableListElements = false; + m_ListIsPatchedInPrefabModeInContext = false; + + var prefabStage = PrefabStageUtility.GetCurrentPrefabStage(); + if (prefabStage != null) + { + m_ListIsPatchedInPrefabModeInContext = prefabStage.HasPatchedPropertyModificationsFor(serializedObjectTarget, m_PropertyPath); + if (m_ListIsPatchedInPrefabModeInContext) + { + m_DisableListElements = prefabStage.HasPatchedPropertyModificationsFor(serializedObjectTarget, m_PropertyPathArraySize); + } + } + + if (m_ReorderableList != null) + m_ReorderableList.draggable = m_Reorderable && !m_ListIsPatchedInPrefabModeInContext; + } + public void Draw(GUIContent label, Rect r, Rect visibleArea, string tooltip, bool includeChildren) { r.xMin += EditorGUI.indent; - var prefabStage = PrefabStageUtility.GetCurrentPrefabStage(); - m_IsNotInPrefabContextModeWithOverrides = prefabStage == null || prefabStage.mode != PrefabStage.Mode.InContext || !PrefabStage.s_PatchAllOverriddenProperties - || Selection.objects.All(obj => PrefabUtility.IsPartOfAnyPrefab(obj) && !AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(obj)).Equals(AssetDatabase.AssetPathToGUID(prefabStage.assetPath))); - m_ReorderableList.draggable = m_Reorderable && m_IsNotInPrefabContextModeWithOverrides; Rect headerRect = new Rect(r.x, r.y, r.width, m_HeaderHeight); Rect sizeRect = new Rect(headerRect.xMax - Constants.kArraySizeWidth - EditorGUI.indent * EditorGUI.indentLevel, headerRect.y, @@ -165,7 +193,11 @@ public void Draw(GUIContent label, Rect r, Rect visibleArea, string tooltip, boo m_ReorderableList.InvalidateCacheRecursive(); } + if (m_DisableListElements) + GUI.enabled = false; + DrawChildren(r, headerRect, sizeRect, visibleArea, prevType); + GUI.enabled = prevEnabled; } void DrawChildren(Rect listRect, Rect headerRect, Rect sizeRect, Rect visibleRect, EventType previousEvent) diff --git a/Editor/Mono/Inspector/ScriptExecutionOrderInspector.cs b/Editor/Mono/Inspector/ScriptExecutionOrderInspector.cs index 51a99df4f3..130810206c 100644 --- a/Editor/Mono/Inspector/ScriptExecutionOrderInspector.cs +++ b/Editor/Mono/Inspector/ScriptExecutionOrderInspector.cs @@ -207,7 +207,7 @@ static bool IsValidScript(MonoScript script) // The user can only define the order of scripts in the assets folder. bool isRootFolder, isReadOnly; - if (!AssetDatabase.GetAssetFolderInfo(AssetDatabase.GetAssetPath(script), out isRootFolder, out isReadOnly)) + if (!AssetDatabase.TryGetAssetFolderInfo(AssetDatabase.GetAssetPath(script), out isRootFolder, out isReadOnly)) { return false; } diff --git a/Editor/Mono/Inspector/ScriptableRenderPipelineExtensionAttribute.deprecated.cs b/Editor/Mono/Inspector/ScriptableRenderPipelineExtensionAttribute.deprecated.cs new file mode 100644 index 0000000000..0f6a6bb85a --- /dev/null +++ b/Editor/Mono/Inspector/ScriptableRenderPipelineExtensionAttribute.deprecated.cs @@ -0,0 +1,26 @@ +// 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.Rendering; + +namespace UnityEditor.Rendering; + +[Obsolete($"{nameof(ScriptableRenderPipelineExtensionAttribute)} is deprecated. Use {nameof(SupportedOnRenderPipelineAttribute)} instead. #from(23.1) (UnityUpgradable) -> UnityEngine.Rendering.SupportedOnRenderPipelineAttribute", false)] +[AttributeUsage(AttributeTargets.Class)] +public class ScriptableRenderPipelineExtensionAttribute : Attribute +{ + internal Type renderPipelineType; + + public ScriptableRenderPipelineExtensionAttribute(Type rpAssetType) + { + if (!(rpAssetType?.IsSubclassOf(typeof(RenderPipelineAsset)) ?? false)) + throw new ArgumentException($"Given {nameof(rpAssetType)} must derive from {nameof(RenderPipelineAsset)}"); + renderPipelineType = rpAssetType; + } + + [Obsolete($"ScriptableRenderPipelineExtensionAttribute.inUse is deprecated. Use SupportedOnRenderPipelineAttribute.isSupportedOnCurrentPipeline instead. #from(23.1) (UnityUpgradable) -> UnityEngine.Rendering.SupportedOnRenderPipelineAttribute.isSupportedOnCurrentPipeline", false)] + public bool inUse + => GraphicsSettings.currentRenderPipelineAssetType == renderPipelineType; +} diff --git a/Editor/Mono/Inspector/SpriteFrameInspector.cs b/Editor/Mono/Inspector/SpriteFrameInspector.cs index 76a5c416f7..07ae7133f3 100644 --- a/Editor/Mono/Inspector/SpriteFrameInspector.cs +++ b/Editor/Mono/Inspector/SpriteFrameInspector.cs @@ -185,14 +185,18 @@ public static Texture2D BuildPreviewTexture(Sprite sprite, Material spriteRender if (!isPolygon) { // Try to have a minimum of 64 pixels for width and height, unless requested width and height is smaller - var minWidth = Mathf.Min(64, width); - var minHeight = Mathf.Min(64, height); + var minWidth = Mathf.Min(64f, width); + var minHeight = Mathf.Min(64f, height); PreviewHelpers.AdjustWidthAndHeightForStaticPreview((int) spriteWidth, (int) spriteHeight, ref width, ref height); - // Set minimum size for width and height to prevent small previews for small sprites - width = Mathf.Max(minWidth, width); - height = Mathf.Max(minHeight, height); + // Set minimum size for width/height to prevent small previews for small sprites + if (width < minWidth && height < minHeight) + { + var ratio = Mathf.Min( minWidth / width, minHeight / height); + width = Mathf.FloorToInt(width * ratio); + height = Mathf.FloorToInt(height * ratio); + } } SavedRenderTargetState savedRTState = new SavedRenderTargetState(); diff --git a/Editor/Mono/Inspector/TagManagerInspector.cs b/Editor/Mono/Inspector/TagManagerInspector.cs index 1ac0eb1f36..f028a3a3f6 100644 --- a/Editor/Mono/Inspector/TagManagerInspector.cs +++ b/Editor/Mono/Inspector/TagManagerInspector.cs @@ -151,7 +151,8 @@ public override void OnGUI(Rect windowRect) } GUI.enabled = m_NewTagName.Length != 0; - if (GUILayout.Button("Save") || hitEnter) + var savePressed = GUILayout.Button("Save"); + if (!string.IsNullOrWhiteSpace(m_NewTagName) && (savePressed || hitEnter)) { EnterCB(m_NewTagName); editorWindow.Close(); @@ -240,6 +241,9 @@ void AddToSortLayerList(ReorderableList list) list.serializedProperty.GetArrayElementAtIndex(list.index).FindPropertyRelative("name").stringValue = "New Layer"; list.serializedProperty.serializedObject.ApplyModifiedProperties(); } + + if (SortingLayer.onLayerAdded != null) + SortingLayer.onLayerAdded(SortingLayer.layers[list.index]); } public void ReorderSortLayerList(ReorderableList list) @@ -250,6 +254,9 @@ public void ReorderSortLayerList(ReorderableList list) private void RemoveFromSortLayerList(ReorderableList list) { + if (SortingLayer.onLayerRemoved != null) + SortingLayer.onLayerRemoved(SortingLayer.layers[list.index]); + ReorderableList.defaultBehaviours.DoRemoveButton(list); serializedObject.ApplyModifiedProperties(); serializedObject.Update(); diff --git a/Editor/Mono/Inspector/Texture2DArrayInspector.cs b/Editor/Mono/Inspector/Texture2DArrayInspector.cs index eb45552ba9..becde6411c 100644 --- a/Editor/Mono/Inspector/Texture2DArrayInspector.cs +++ b/Editor/Mono/Inspector/Texture2DArrayInspector.cs @@ -15,7 +15,7 @@ class Texture2DArrayInspector : TextureInspector public override string GetInfoString() { var tex = (Texture2DArray)target; - var info = $"{tex.width}x{tex.height} {tex.depth} slice{(tex.depth != 1 ? "s" : "")} {GraphicsFormatUtility.GetFormatString(tex.format)} {EditorUtility.FormatBytes(TextureUtil.GetRuntimeMemorySizeLong(tex))}"; + var info = $"{tex.width}x{tex.height} {tex.depth} slice{(tex.depth != 1 ? "s" : "")} {GraphicsFormatUtility.GetFormatString(tex.format)} {EditorUtility.FormatBytes(TextureUtil.GetStorageMemorySizeLong(tex))}"; return info; } diff --git a/Editor/Mono/Inspector/Texture3DPreview.cs b/Editor/Mono/Inspector/Texture3DPreview.cs index 0f3edc1678..e36eb05462 100644 --- a/Editor/Mono/Inspector/Texture3DPreview.cs +++ b/Editor/Mono/Inspector/Texture3DPreview.cs @@ -98,6 +98,7 @@ static class Styles const float s_MinViewDistance = 0f; const float s_MaxViewDistance = 5.0f; static readonly Vector2 s_InitialRotation = new Vector2(15, 30); + const float s_MaxPreviewPixelCount = 512 * 512 * 512; PreviewRenderUtility m_PreviewUtility; Preview3DMode m_Preview3DMode; @@ -188,7 +189,7 @@ public string GetInfoString() Vector3 resolution = GetTextureResolution(Texture); var format = GraphicsFormatUtility.GetFormatString(Texture.graphicsFormat); - var size = EditorUtility.FormatBytes(TextureUtil.GetRuntimeMemorySizeLong(Texture)); + var size = EditorUtility.FormatBytes(TextureUtil.GetStorageMemorySizeLong(Texture)); string info = $"{resolution.x}x{resolution.y}x{resolution.z} {format} {size}"; return info; } @@ -440,6 +441,16 @@ void DrawPreview() m_PreviewUtility.Render(); } + bool IsPreviewExpensiveToDisplay() + { + if (m_Preview3DMode == Preview3DMode.Volume || m_Preview3DMode == Preview3DMode.SDF) + { + Vector3 res = GetTextureResolution(Texture); + return res.x * res.y * res.z > s_MaxPreviewPixelCount; + } + return false; + } + public void OnPreviewGUI(Rect r, GUIStyle background) { if (!ShaderUtil.hardwareSupportsRectRenderTexture || !SystemInfo.supports3DTextures) @@ -455,6 +466,12 @@ public void OnPreviewGUI(Rect r, GUIStyle background) EditorGUI.DropShadowLabel(new Rect(r.x, r.y, r.width, 40), "Compressed 3D texture preview is not supported"); return; } + if (IsPreviewExpensiveToDisplay()) + { + if (Event.current.type == EventType.Repaint) + EditorGUI.DropShadowLabel(new Rect(r.x, r.y, r.width, 40), "Preview disabled (3D texture too large to display)"); + return; + } InitPreviewUtility(); Event e = Event.current; @@ -487,6 +504,9 @@ public Texture2D RenderStaticPreview(Texture texture, int width, int height) OnEnable(); m_QualityModifier *= 2; + if (IsPreviewExpensiveToDisplay()) + return null; + Rect r = new Rect(0, 0, width, height); m_PreviewUtility.BeginStaticPreview(r); DrawPreview(); diff --git a/Editor/Mono/Inspector/TextureInspector.cs b/Editor/Mono/Inspector/TextureInspector.cs index 27be8f1962..cda9a7406a 100644 --- a/Editor/Mono/Inspector/TextureInspector.cs +++ b/Editor/Mono/Inspector/TextureInspector.cs @@ -30,8 +30,8 @@ internal static void AdjustWidthAndHeightForStaticPreview(int textureWidth, int { // For textures larger than our wanted width and height we ensure to // keep aspect ratio of the texture and fit it to best match our wanted width and height. - float relWidth = height / (float)textureWidth; - float relHeight = width / (float)textureHeight; + float relWidth = width / (float)textureWidth; + float relHeight = height / (float)textureHeight; float scale = Mathf.Min(relHeight, relWidth); @@ -69,7 +69,7 @@ internal class TextureInspector : Editor class Styles { public GUIContent smallZoom, largeZoom; - public GUIStyle toolbarButton, previewSlider, previewSliderThumb, previewLabel; + public GUIStyle toolbarButton, previewSlider, previewSliderThumb, previewLabel, mipLevelLabel; public readonly GUIContent[] previewButtonContents = { @@ -111,6 +111,10 @@ public Styles() previewSlider = "preSlider"; previewSliderThumb = "preSliderThumb"; previewLabel = "toolbarLabel"; + + mipLevelLabel = "PreOverlayLabel"; + mipLevelLabel.alignment = TextAnchor.UpperCenter; + mipLevelLabel.padding.top = 5; } } static Styles s_Styles; @@ -233,7 +237,10 @@ protected virtual void OnDisable() { RestoreLastTextureMipLevels(); + m_TextureMipLevels.Clear(); + m_CubemapPreview.OnDisable(); + m_Texture3DPreview.OnDisable(); DestroyImmediate(m_Texture3DPreview); } @@ -283,6 +290,15 @@ public float GetMipLevelForRendering() return Mathf.Min(m_MipLevel, TextureUtil.GetMipmapCount(target as Texture) - 1); } + public int GetMipmapLimit(Texture t) + { + if (t is Texture2D) + { + return (t as Texture2D).activeMipmapLimit; + } + return 0; + } + public float mipLevel { get @@ -680,9 +696,17 @@ public override void OnPreviewSettings() if (mipCount > 1) { + int mipmapLimit = GetMipmapLimit(target as Texture); GUILayout.Box(s_Styles.smallZoom, s_Styles.previewLabel); GUI.changed = false; - m_MipLevel = Mathf.Round(GUILayout.HorizontalSlider(m_MipLevel, mipCount - 1, 0, s_Styles.previewSlider, s_Styles.previewSliderThumb, GUILayout.MaxWidth(64))); + + int leftValue = mipCount - mipmapLimit - 1; + if (m_MipLevel > leftValue) + { + // Left value can change depending on the mipmap limit. Cap slider value appropriately. + m_MipLevel = leftValue; + } + m_MipLevel = Mathf.Round(GUILayout.HorizontalSlider(m_MipLevel, leftValue, 0, s_Styles.previewSlider, s_Styles.previewSliderThumb, GUILayout.MaxWidth(64))); //For now, we don't have mipmaps smaller than the tile size when using VT. if (EditorGUI.UseVTMaterial(tex)) @@ -847,9 +871,20 @@ public override void OnPreviewGUI(Rect r, GUIStyle background) TextureUtil.SetFilterModeNoDirty(t, oldFilter); + int mipmapLimit = GetMipmapLimit(target as Texture); + int cpuMipLevel = Mathf.Min(TextureUtil.GetMipmapCount(target as Texture) - 1, (int)mipLevel + mipmapLimit); m_Pos = PreviewGUI.EndScrollView(); - if (mipLevel != 0) - EditorGUI.DropShadowLabel(new Rect(r.x, r.y, r.width, 20), "Mip " + mipLevel); + if (cpuMipLevel != 0) + { + GUIContent mipLevelTextContent = new GUIContent((cpuMipLevel != mipLevel) + ? string.Format("Mip {0}\nMip {1} on GPU (Texture Limit)", cpuMipLevel, mipLevel) + : string.Format("Mip {0}", mipLevel)); + Vector2 size = s_Styles.mipLevelLabel.CalcSize(mipLevelTextContent); + if (size.x <= r.width) + { + EditorGUI.DropShadowLabel(new Rect(r.x, r.y, r.width, size.y), mipLevelTextContent, s_Styles.mipLevelLabel); + } + } } private void DrawRect(Rect rect) diff --git a/Editor/Mono/Inspector/TimeManagerInspector.cs b/Editor/Mono/Inspector/TimeManagerInspector.cs index d9ec0bf632..bec8951261 100644 --- a/Editor/Mono/Inspector/TimeManagerInspector.cs +++ b/Editor/Mono/Inspector/TimeManagerInspector.cs @@ -2,6 +2,8 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using System; +using Unity.IntegerTime; using UnityEngine; namespace UnityEditor @@ -17,24 +19,36 @@ class Content public static readonly GUIContent maxParticleTimestepLabel = EditorGUIUtility.TrTextContent("Maximum Particle Timestep", "The maximum frame delta time Unity permits for a single iteration of the Particle System update. If the delta time is larger than this value, Unity will run the Particle System update multiple times during the frame with smaller timesteps. This preserves the quality of the simulation."); } - SerializedProperty m_FixedTimestepProperty; + SerializedProperty m_FixedTimestepCountProperty; SerializedProperty m_MaxAllowedTimestepProperty; SerializedProperty m_TimeScaleProperty; SerializedProperty m_MaxParticleTimestepProperty; + static RationalTime.TicksPerSecond m_FixedTimeTicksPerSecond; + const float MinFixedTimeStep = 0.0001f; + public void OnEnable() { - m_FixedTimestepProperty = serializedObject.FindProperty("Fixed Timestep"); + var fixedTimestepProperty = serializedObject.FindProperty("Fixed Timestep"); + m_FixedTimestepCountProperty = fixedTimestepProperty.FindPropertyRelative("m_Count"); m_MaxAllowedTimestepProperty = serializedObject.FindProperty("Maximum Allowed Timestep"); m_TimeScaleProperty = serializedObject.FindProperty("m_TimeScale"); m_MaxParticleTimestepProperty = serializedObject.FindProperty("Maximum Particle Timestep"); + + if (!m_FixedTimeTicksPerSecond.Valid) // FixedTime has a constant ticks per second value + { + var numerator = fixedTimestepProperty.FindPropertyRelative("m_Rate.m_Numerator"); + var denominator = fixedTimestepProperty.FindPropertyRelative("m_Rate.m_Denominator"); + m_FixedTimeTicksPerSecond = + new RationalTime.TicksPerSecond(numerator.uintValue, denominator.uintValue); + } } public override void OnInspectorGUI() { serializedObject.Update(); - EditorGUILayout.PropertyField(m_FixedTimestepProperty, Content.fixedTimestepLabel); + DrawFixedTimeAsFloat(m_FixedTimestepCountProperty); EditorGUILayout.PropertyField(m_MaxAllowedTimestepProperty, Content.maxAllowedTimestepLabel); EditorGUILayout.PropertyField(m_TimeScaleProperty, Content.timeScaleLabel); EditorGUILayout.PropertyField(m_MaxParticleTimestepProperty, Content.maxParticleTimestepLabel); @@ -42,6 +56,26 @@ public override void OnInspectorGUI() serializedObject.ApplyModifiedProperties(); } + static void DrawFixedTimeAsFloat(SerializedProperty prop) + { + var fixedTime = (float)new RationalTime(prop.longValue, m_FixedTimeTicksPerSecond).ToDouble(); // Convert a tick count to a float + using (var c = new EditorGUI.ChangeCheckScope()) + { + var maxFixedTime = MathF.Max(fixedTime, MinFixedTimeStep); + var roundedTime = Math.Round(maxFixedTime, 4, MidpointRounding.AwayFromZero); + fixedTime = EditorGUILayout.FloatField(Content.fixedTimestepLabel, (float)roundedTime); + + if (c.changed) + { + var newCount = RationalTime + .FromDouble(fixedTime, m_FixedTimeTicksPerSecond) + .Count; // convert it back to a count to store in the property + prop.longValue = newCount; + } + } + } + + [SettingsProvider] internal static SettingsProvider CreateProjectSettingsProvider() { diff --git a/Editor/Mono/Inspector/TrailRendererEditor.cs b/Editor/Mono/Inspector/TrailRendererEditor.cs index ff3d7b5ffc..9023f644a4 100644 --- a/Editor/Mono/Inspector/TrailRendererEditor.cs +++ b/Editor/Mono/Inspector/TrailRendererEditor.cs @@ -309,9 +309,9 @@ private void OnSceneViewGUI(SceneView sceneView) foreach (var obj in targets) { - if (obj is TrailRenderer tr) + if (obj is TrailRenderer trail) { - var worldBounds = tr.bounds; + var worldBounds = trail.bounds; Handles.DrawWireCube(worldBounds.center, worldBounds.size); } } @@ -320,9 +320,9 @@ private void OnSceneViewGUI(SceneView sceneView) } // Move trail using shape - if (m_PreviewBackupPosition.HasValue) + if (target is TrailRenderer tr) { - if (target is TrailRenderer tr) + if (m_PreviewBackupPosition.HasValue) { if (s_PreviewIsPlaying && !s_PreviewIsPaused) { @@ -388,6 +388,10 @@ private void OnSceneViewGUI(SceneView sceneView) tr.previewTimeScale = 0.0f; } } + else + { + tr.previewTimeScale = s_PreviewTimeScale; // Not playing or paused, but might be dragging the Trail around the scene view using the Transform gizmo + } } } diff --git a/Editor/Mono/Inspector/TransformRotationGUI.cs b/Editor/Mono/Inspector/TransformRotationGUI.cs index 41d1a9e243..dfa51ba56d 100644 --- a/Editor/Mono/Inspector/TransformRotationGUI.cs +++ b/Editor/Mono/Inspector/TransformRotationGUI.cs @@ -100,8 +100,13 @@ public void RotationField(bool disabled) var prevWidth = EditorGUIUtility.labelWidth; var prevIndent = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; + SerializedProperty rotation = m_Rotation.Copy(); + for (int i = 0; i < m_EulerFloats.Length; i++) { + rotation.Next(true); + EditorGUI.BeginProperty(nr, s_XYZLabels[i], rotation); + EditorGUIUtility.labelWidth = EditorGUI.GetLabelWidth(s_XYZLabels[i]); EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = (differentRotationMask & (1 << i)) != 0; @@ -125,6 +130,8 @@ public void RotationField(bool disabled) } nr.x += w + EditorGUI.kSpacingSubLabel; + + EditorGUI.EndProperty(); } EditorGUIUtility.labelWidth = prevWidth; EditorGUI.indentLevel = prevIndent; diff --git a/Editor/Mono/Inspector/UnityEventDrawer.cs b/Editor/Mono/Inspector/UnityEventDrawer.cs index 44ad0876e9..e4981d9bb7 100644 --- a/Editor/Mono/Inspector/UnityEventDrawer.cs +++ b/Editor/Mono/Inspector/UnityEventDrawer.cs @@ -212,6 +212,20 @@ private ListView CreateListView(SerializedProperty property) eventItem.BindFields(propertyData, createMenuCallback, formatSelectedValueCallback, getArgumentCallback); }; + listView.itemsAdded += indices => + { + foreach (var i in indices) + { + var pListener = propertyRelative.GetArrayElementAtIndex(i); + var callState = pListener.FindPropertyRelative(kCallStatePath); + // SERIAL-124 + // Objects added to an array via SerializedObject do not have their default values set. + // Therefore we need to set the initial values here to fix UUM-27561 + callState.enumValueIndex = (int)UnityEventCallState.RuntimeOnly; + callState.serializedObject.ApplyModifiedPropertiesWithoutUndo(); + } + }; + return listView; } @@ -256,7 +270,7 @@ private SerializedProperty GetArgument(SerializedProperty pListener) public override VisualElement CreatePropertyGUI(SerializedProperty property) { m_Prop = property; - m_Text = property.displayName; + m_Text = preferredLabel; m_DummyEvent = GetDummyEvent(m_Prop); var listViewContainer = new VisualElement(); @@ -264,6 +278,7 @@ public override VisualElement CreatePropertyGUI(SerializedProperty property) var header = new Label(); header.text = GetHeaderText(); + header.tooltip = property.tooltip; header.AddToClassList(kHeaderClassName); var listView = CreateListView(property); diff --git a/Editor/Mono/Inspector/VersionControlSettingsInspector.cs b/Editor/Mono/Inspector/VersionControlSettingsInspector.cs index 1fae6146be..1dd04ccf43 100644 --- a/Editor/Mono/Inspector/VersionControlSettingsInspector.cs +++ b/Editor/Mono/Inspector/VersionControlSettingsInspector.cs @@ -11,7 +11,6 @@ using UnityEditorInternal; using UnityEngine; using UnityEngine.UIElements; -using UnityEditor.Collaboration; namespace UnityEditor { @@ -25,6 +24,7 @@ class Styles public static GUIContent automaticAdd = new GUIContent("Automatic Add", "Automatically add newly created assets to version control."); public static GUIContent smartMerge = new GUIContent("Smart merge"); + public static GUIContent trackPackagesOutsideProject = new GUIContent("Version Packages Outside Project", "Tracks changes to packages that reside on disk outside of the project's root folder."); public static GUIContent vcsConnect = new GUIContent("Connect"); public static GUIContent vcsReconnect = new GUIContent("Reconnect"); public static GUIContent workOffline = new GUIContent("Work Offline", @@ -156,9 +156,6 @@ private void SetVersionControlSystem(object data) private bool VersionControlSystemHasGUI() { - bool collabEnabled = Collab.instance.IsCollabEnabledForCurrentProject(); - if (!collabEnabled) - { ExternalVersionControl system = VersionControlSettings.mode; return system != ExternalVersionControl.Disabled && @@ -166,9 +163,6 @@ private bool VersionControlSystemHasGUI() system != ExternalVersionControl.Generic; } - return false; - } - string[] GetVCConfigFieldRecentValues(string fieldName) { if (m_VCConfigFieldsRecentValues.ContainsKey(fieldName)) @@ -243,20 +237,13 @@ public override void OnInspectorGUI() GUILayout.Space(10); GUILayout.BeginVertical(EditorStyles.inspectorDefaultMargins); - bool collabEnabled = Collab.instance.IsCollabEnabledForCurrentProject(); - using (new EditorGUI.DisabledScope(!collabEnabled)) + using (new EditorGUI.DisabledScope(true)) { - GUI.enabled = !collabEnabled; + GUI.enabled = true; ExternalVersionControl selvc = VersionControlSettings.mode; CreatePopupMenuVersionControl(Styles.mode.text, vcPopupList, selvc, SetVersionControlSystem); - GUI.enabled = !collabEnabled; - } - - if (collabEnabled) - { - EditorGUILayout.HelpBox("Version Control not available when using Collaboration feature.", - MessageType.Warning); + GUI.enabled = true; } GUI.enabled = true; @@ -410,6 +397,9 @@ public override void OnInspectorGUI() EditorUserSettings.semanticMergeMode = (SemanticMergeMode)EditorGUILayout.Popup(Styles.smartMerge, (int)EditorUserSettings.semanticMergeMode, semanticMergePopupList); + VersionControlSettings.trackPackagesOutsideProject = + EditorGUILayout.Toggle(Styles.trackPackagesOutsideProject, VersionControlSettings.trackPackagesOutsideProject); + GUILayout.Space(10); GUILayout.Label(Styles.overlayIcons); diff --git a/Editor/Mono/InternalEditorUtility.bindings.cs b/Editor/Mono/InternalEditorUtility.bindings.cs index 88f469ce01..424b3fedb4 100644 --- a/Editor/Mono/InternalEditorUtility.bindings.cs +++ b/Editor/Mono/InternalEditorUtility.bindings.cs @@ -529,6 +529,9 @@ public static Version GetUnityVersion() [FreeFunction("InternalEditorUtilityBindings::ReadScreenPixelUnderCursor")] extern public static Color[] ReadScreenPixelUnderCursor(Vector2 cursorPosHint, int sizex, int sizey); + [FreeFunction("InternalEditorUtilityBindings::IsAllowedToReadPixelOutsideUnity")] + extern internal static bool IsAllowedToReadPixelOutsideUnity(out string errorMessage); + [StaticAccessor("GetGpuDeviceManager()", StaticAccessorType.Dot)] [NativeMethod("SetDevice")] extern public static void SetGpuDeviceAndRecreateGraphics(int index, string name); @@ -672,7 +675,7 @@ extern public static float remoteScreenHeight [StaticAccessor("CustomLighting::Get()", StaticAccessorType.Dot)] [NativeMethod("SetCustomLighting")] - extern public static void SetCustomLightingInternal(Light[] lights, Color ambient); + extern public static void SetCustomLightingInternal([Unmarshalled] Light[] lights, Color ambient); public static void SetCustomLighting(Light[] lights, Color ambient) { @@ -913,7 +916,7 @@ internal static PrecompiledAssembly[] GetUnityAssemblies(bool buildingForEditor, extern internal static bool SaveCursorToInMemoryResource(Texture2D image, Vector2 hotSpot, ushort cursorDataResourceId, IntPtr cursorDirectoryBuffer, uint cursorDirectoryBufferSize, IntPtr cursorDataBuffer, uint cursorDataBufferSize); [FreeFunction("GetScriptCompilationDefines")] - extern internal static string[] GetCompilationDefines(EditorScriptCompilationOptions options, BuildTargetGroup targetGroup, BuildTarget target, int subtarget, ApiCompatibilityLevel apiCompatibilityLevel, string[] extraDefines = null); + extern internal static string[] GetCompilationDefines(EditorScriptCompilationOptions options, BuildTargetGroup targetGroup, BuildTarget target, int subtarget, ApiCompatibilityLevel apiCompatibilityLevel, string[] extraDefines = null); //Launches an application that is kept alive, even during a domain reload [FreeFunction("LaunchApplication")] diff --git a/Editor/Mono/Modules/BeeBuildPostprocessor.cs b/Editor/Mono/Modules/BeeBuildPostprocessor.cs index 954f5f6411..83a81b617b 100644 --- a/Editor/Mono/Modules/BeeBuildPostprocessor.cs +++ b/Editor/Mono/Modules/BeeBuildPostprocessor.cs @@ -168,6 +168,13 @@ IEnumerable GetPluginsFor(BuildTarget target) } } + static IEnumerable GetFilesWithRoleFromBuildReport(BuildReport report, params string[] roles) => + report.GetFiles() + .Where(file => roles.Contains(file.role)) + .Select(file => file.path.ToNPath()) + .GroupBy(file => file.FileName) + .Select(group => group.First()); + LinkerConfig LinkerConfigFor(BuildPostProcessArgs args) { var namedBuildTarget = GetNamedBuildTarget(args); @@ -178,39 +185,45 @@ LinkerConfig LinkerConfigFor(BuildPostProcessArgs args) if (GetScriptingBackend(args) == ScriptingBackend.IL2CPP && strippingLevel == ManagedStrippingLevel.Disabled) strippingLevel = ManagedStrippingLevel.Minimal; - if (strippingLevel > ManagedStrippingLevel.Disabled) - { - var additionalArgs = new List(); + var additionalArgs = new List(); - var diagArgs = Debug.GetDiagnosticSwitch("VMUnityLinkerAdditionalArgs").value as string; - if (!string.IsNullOrEmpty(diagArgs)) - additionalArgs.Add(diagArgs.Trim('\'')); - - var linkerInputDirectory = DagDirectory.Combine($"artifacts/UnityLinkerInputs").CreateDirectory(); - return new LinkerConfig + var diagArgs = Debug.GetDiagnosticSwitch("VMUnityLinkerAdditionalArgs").value as string; + if (!string.IsNullOrEmpty(diagArgs)) + additionalArgs.Add(diagArgs.Trim('\'')); + + var linkerInputDirectory = DagDirectory.Combine($"artifacts/UnityLinkerInputs").CreateDirectory(); + + // In Disabled mode, we pass all generated and engine assemblies to the linker as roots, as the linker + // will only perform a simple assembly reference traversal, ignoring link.xml files and attributes which + // would otherwise find dependent assemblies to preserve. + // In other modes (when stripping is desired), we pass only a smaller set of user assemblies (assemblies from + // packages if used in any scenes, as well as any assembly from the Assets folder) as roots. + var assembliesToProcess = strippingLevel == ManagedStrippingLevel.Disabled + ? GetFilesWithRoleFromBuildReport(args.report, "ManagedLibrary", "ManagedEngineAPI").Select(f => f.FileName) + : args.usedClassRegistry.GetUserAssemblies(); + + return new LinkerConfig + { + LinkXmlFiles = AssemblyStripper.GetLinkXmlFiles(args, linkerInputDirectory), + EditorToLinkerData = AssemblyStripper.WriteEditorData(args, linkerInputDirectory), + AssembliesToProcess = assembliesToProcess.ToArray(), + Runtime = GetScriptingBackend(args).ToString().ToLowerInvariant(), + Profile = IL2CPPUtils.ApiCompatibilityLevelToDotNetProfileArgument( + PlayerSettings.GetApiCompatibilityLevel(namedBuildTarget), args.target), + Ruleset = strippingLevel switch { - LinkXmlFiles = AssemblyStripper.GetLinkXmlFiles(args, linkerInputDirectory), - EditorToLinkerData = AssemblyStripper.WriteEditorData(args, linkerInputDirectory), - AssembliesToProcess = args.usedClassRegistry.GetUserAssemblies(), - Runtime = GetScriptingBackend(args).ToString().ToLower(), - Profile = IL2CPPUtils.ApiCompatibilityLevelToDotNetProfileArgument( - PlayerSettings.GetApiCompatibilityLevel(namedBuildTarget), args.target), - Ruleset = strippingLevel switch - { - ManagedStrippingLevel.Minimal => "Minimal", - ManagedStrippingLevel.Low => "Conservative", - ManagedStrippingLevel.Medium => "Aggressive", - ManagedStrippingLevel.High => "Experimental", - _ => throw new ArgumentException($"Unhandled {nameof(ManagedStrippingLevel)} value") - }, - AdditionalArgs = additionalArgs.ToArray(), - ModulesAssetPath = $"{BuildPipeline.GetPlaybackEngineDirectory(args.target, 0)}/modules.asset", - AllowDebugging = GetAllowDebugging(args), - PerformEngineStripping = PlayerSettings.stripEngineCode, - }; - } - - return null; + ManagedStrippingLevel.Disabled => "Copy", + ManagedStrippingLevel.Minimal => "Minimal", + ManagedStrippingLevel.Low => "Conservative", + ManagedStrippingLevel.Medium => "Aggressive", + ManagedStrippingLevel.High => "Experimental", + _ => throw new ArgumentException($"Unhandled {nameof(ManagedStrippingLevel)} value") + }, + AdditionalArgs = additionalArgs.ToArray(), + ModulesAssetPath = $"{BuildPipeline.GetPlaybackEngineDirectory(args.target, 0)}/modules.asset", + AllowDebugging = GetAllowDebugging(args), + PerformEngineStripping = PlayerSettings.stripEngineCode, + }; } static bool IsBuildOptionSet(BuildOptions options, BuildOptions flag) => (options & flag) != 0; @@ -332,9 +345,21 @@ Il2CppConfig Il2CppConfigFor(BuildPostProcessArgs args) ToolChainPath = toolchainPath, RelativeDataPath = relativeDataPath, ExtraTypes = extraTypesFile?.ToString(), + GenerateUsymFile = PlayerSettings.GetIl2CppStacktraceInformation(NamedBuildTarget.FromBuildTargetGroup(BuildPipeline.GetBuildTargetGroup(args.target))) == Il2CppStacktraceInformation.MethodFileLineNumber, + UsymtoolPath = GetUsymtoolPath(), }; } + static string GetUsymtoolPath() + { + if (Application.platform == RuntimePlatform.OSXEditor) + return Paths.Combine(EditorApplication.applicationContentsPath, "Tools", "macosx", "usymtool"); + if (Application.platform == RuntimePlatform.LinuxEditor) + return Paths.Combine(EditorApplication.applicationContentsPath, "Tools", "usymtool"); + + return Paths.Combine(EditorApplication.applicationContentsPath, "Tools", "usymtool.exe"); + } + static bool IsNewInputSystemEnabled() { var propName = "activeInputHandler"; @@ -375,7 +400,8 @@ static GenerateNativePluginsForAssembliesSettings GetGenerateNativePluginsForAss ApplicationIdentifier = PlayerSettings.GetApplicationIdentifier(GetNamedBuildTarget(args)), InstallIntoBuildsFolder = GetInstallingIntoBuildsFolder(args), GenerateIdeProject = GetCreateSolution(args), - Development = (args.report.summary.options & BuildOptions.Development) == BuildOptions.Development, + Development = (args.options & BuildOptions.Development) == BuildOptions.Development, + NoGUID = (args.options & BuildOptions.NoUniqueIdentifier) == BuildOptions.NoUniqueIdentifier, ScriptingBackend = GetScriptingBackend(args), Architecture = GetArchitecture(args), DataFolder = GetDataFolderFor(args), @@ -391,11 +417,8 @@ static GenerateNativePluginsForAssembliesSettings GetGenerateNativePluginsForAss .Select(e => new StreamingAssetsFile { File = e.src.ToString(), RelativePath = e.dst.ToString() }) .ToArray(), UseNewInputSystem = IsNewInputSystemEnabled(), - ManagedAssemblies = args.report.GetFiles() - .Where(file => file.role == "ManagedLibrary" || file.role == "DependentManagedLibrary" || file.role == "ManagedEngineAPI") - .Select(file => file.path.ToNPath()) - .GroupBy(file => file.FileName) - .Select(group => group.First().ToString()) + ManagedAssemblies = GetFilesWithRoleFromBuildReport(args.report, "ManagedLibrary", "DependentManagedLibrary", "ManagedEngineAPI") + .Select(p => p.ToString()) .ToArray() }; @@ -443,8 +466,7 @@ RunnableProgram MakePlayerBuildProgram(BuildPostProcessArgs args) var searchPaths = $"{beePlatformFolder}{Path.PathSeparator}"; if (IL2CPPUtils.UsingDevelopmentBuild()) { - NPath il2cppPath = IL2CPPUtils.GetExePath("il2cpp").ToNPath().Parent; - searchPaths = $"{il2cppPath}{Path.PathSeparator}"; + searchPaths = $"{IL2CPPUtils.ConstructBeeLibrarySearchPath()}{Path.PathSeparator}"; } return new SystemProcessRunnableProgram(NetCoreRunProgram.NetCoreRunPath, @@ -501,12 +523,20 @@ void UnityLinkerResultProcessor(NodeFinishedMessage node) DefaultResultProcessor(node); } + void UsymtoolResultProcessor(NodeFinishedMessage node) + { + // Usymtool might print a message like "error: " to stdout, even when + // it succeeds. So only process error messages when it fails. + if (node.ExitCode != 0) + DefaultResultProcessor(node); + } + public BeeBuildPostprocessor() { ResultProcessors["IL2CPP_CodeGen"] = PrintStdoutOnErrorProcessor; ResultProcessors["UnityLinker"] = UnityLinkerResultProcessor; ResultProcessors["ExtractUsedFeatures"] = PrintStdoutOnErrorProcessor; - + ResultProcessors["Usym"] = UsymtoolResultProcessor; } protected void DefaultResultProcessor(NodeFinishedMessage node, bool printErrors = true, bool printWarnings = true) @@ -569,12 +599,25 @@ void ReportBuildOutputFiles(BuildPostProcessArgs args) var filesOutput = BeeDriverResult.DataFromBuildProgram.Get(); foreach (var outputfile in filesOutput.Files.ToNPaths().Where(f => f.FileExists() && !f.IsSymbolicLink)) args.report.RecordFileAdded(outputfile.ToString(), outputfile.Extension); + + var config = filesOutput.BootConfigArtifact.ToNPath().ReadAllLines(); + var guidKey = "build-guid="; + var guidLine = config.FirstOrDefault(l => l.StartsWith(guidKey)); + if (guidLine != null) + { + var guid = guidLine.Substring(guidKey.Length); + args.report.SetBuildGUID(new GUID(guid)); + } + else + { + args.report.SetBuildGUID(new GUID("00000000000000000000000000000000")); + } } - public virtual string PrepareForBuild(BuildOptions options, BuildTarget target) + public virtual string PrepareForBuild(BuildPlayerOptions buildOptions) { // Clean the Bee folder in PrepareForBuild, so that it is also clean for script compilation. - if ((options & BuildOptions.CleanBuildCache) == BuildOptions.CleanBuildCache) + if ((buildOptions.options & BuildOptions.CleanBuildCache) == BuildOptions.CleanBuildCache) EditorCompilation.ClearBeeBuildArtifacts(); return null; @@ -665,7 +708,6 @@ public virtual void PostProcess(BuildPostProcessArgs args) { EditorUtility.DisplayCancelableProgressBar("Incremental Player Build", "Canceling build", 1.0f); cancellationTokenSource.Cancel(); - throw new OperationCanceledException(); } } args.report.EndBuildStep(buildStep); @@ -699,6 +741,13 @@ public virtual void PostProcess(BuildPostProcessArgs args) { throw; } + catch (AggregateException e) + { + if (e.InnerException is OperationCanceledException or BuildFailedException) + throw e.InnerException; + + throw new BuildFailedException(e); + } catch (Exception e) { throw new BuildFailedException(e); diff --git a/Editor/Mono/Modules/DefaultPlayerSettingsEditorExtension.cs b/Editor/Mono/Modules/DefaultPlayerSettingsEditorExtension.cs index 74aab2fa5b..c0ec91b87d 100644 --- a/Editor/Mono/Modules/DefaultPlayerSettingsEditorExtension.cs +++ b/Editor/Mono/Modules/DefaultPlayerSettingsEditorExtension.cs @@ -146,5 +146,10 @@ public virtual bool ShouldShowVulkanSettings() } public virtual void VulkanSectionGUI() {} + + public virtual bool SupportsStaticSplashScreenBackgroundColor() + { + return false; + } } } diff --git a/Editor/Mono/Modules/DefaultPluginImporterExtension.cs b/Editor/Mono/Modules/DefaultPluginImporterExtension.cs index 1a85779c90..59e9a1c917 100644 --- a/Editor/Mono/Modules/DefaultPluginImporterExtension.cs +++ b/Editor/Mono/Modules/DefaultPluginImporterExtension.cs @@ -17,6 +17,7 @@ internal class DefaultPluginImporterExtension : IPluginImporterExtension { protected bool hasModified = false; protected Property[] properties = null; + protected const string cpuKey = "CPU"; internal class Property { @@ -127,6 +128,9 @@ public virtual void OnPlatformSettingsGUI(PluginImporterInspector inspector) EditorGUI.BeginChangeCheck(); foreach (var p in properties) { + // skip CPU property for things that aren't native libs + if (p.key == cpuKey && !inspector.importer.isNativePlugin) + continue; p.OnGUI(inspector); } if (EditorGUI.EndChangeCheck()) hasModified = true; @@ -143,7 +147,7 @@ protected virtual void RefreshProperties(PluginImporterInspector inspector) public virtual string CalculateFinalPluginPath(string platformName, PluginImporter imp) { - string cpu = imp.GetPlatformData(platformName, "CPU"); + string cpu = imp.GetPlatformData(platformName, cpuKey); if (!string.IsNullOrEmpty(cpu) && (string.Compare(cpu, "AnyCPU", true) != 0) && (string.Compare(cpu, "None", true) != 0)) return Path.Combine(cpu, Path.GetFileName(imp.assetPath)); diff --git a/Editor/Mono/Modules/DefaultTextureImportSettingsExtension.cs b/Editor/Mono/Modules/DefaultTextureImportSettingsExtension.cs index b7ffd898ba..bbf3a8cbfd 100644 --- a/Editor/Mono/Modules/DefaultTextureImportSettingsExtension.cs +++ b/Editor/Mono/Modules/DefaultTextureImportSettingsExtension.cs @@ -59,7 +59,11 @@ public virtual void ShowImportSettings(BaseTextureImportPlatformSettings editor) { // Max texture size Rect controlRect = EditorGUILayout.GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.popup); - GUIContent label = EditorGUI.BeginProperty(controlRect, maxSize, editor.model.maxTextureSizeProperty); + GUIContent label = maxSize; + if (editor.model.maxTextureSizeProperty != null) + { + label = EditorGUI.BeginProperty(controlRect, label, editor.model.maxTextureSizeProperty); + } EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = editor.model.maxTextureSizeIsDifferent; int maxTextureSize = EditorGUI.IntPopup(controlRect, label, editor.model.platformTextureSettings.maxTextureSize, GUIContent.Temp(kMaxTextureSizeStrings), kMaxTextureSizeValues); @@ -67,7 +71,10 @@ public virtual void ShowImportSettings(BaseTextureImportPlatformSettings editor) { editor.model.SetMaxTextureSizeForAll(maxTextureSize); } - EditorGUI.EndProperty(); + if (editor.model.maxTextureSizeProperty != null) + { + EditorGUI.EndProperty(); + } // Show a note if max size is overriden globally by the user var userMaxSizeOverride = EditorUserBuildSettings.overrideMaxTextureSize; @@ -76,7 +83,11 @@ public virtual void ShowImportSettings(BaseTextureImportPlatformSettings editor) // Resize Algorithm controlRect = EditorGUILayout.GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.popup); - label = EditorGUI.BeginProperty(controlRect, kResizeAlgorithm, editor.model.resizeAlgorithmProperty); + label = kResizeAlgorithm; + if (editor.model.resizeAlgorithmProperty != null) + { + label = EditorGUI.BeginProperty(controlRect, label, editor.model.resizeAlgorithmProperty); + } EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = editor.model.resizeAlgorithmIsDifferent; int resizeAlgorithmVal = EditorGUI.IntPopup(controlRect, label, (int)editor.model.platformTextureSettings.resizeAlgorithm, GUIContent.Temp(kResizeAlgorithmStrings), kResizeAlgorithmValues); @@ -84,7 +95,10 @@ public virtual void ShowImportSettings(BaseTextureImportPlatformSettings editor) { editor.model.SetResizeAlgorithmForAll((TextureResizeAlgorithm)resizeAlgorithmVal); } - EditorGUI.EndProperty(); + if (editor.model.resizeAlgorithmProperty != null) + { + EditorGUI.EndProperty(); + } // Texture format int[] formatValuesForAll = {}; @@ -149,7 +163,11 @@ public virtual void ShowImportSettings(BaseTextureImportPlatformSettings editor) using (new EditorGUI.DisabledScope(formatOptionsAreDifferent || formatStringsForAll.Length == 1)) { controlRect = EditorGUILayout.GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.popup); - label = EditorGUI.BeginProperty(controlRect, kTextureFormat, editor.model.textureFormatProperty); + label = kTextureFormat; + if (editor.model.textureFormatProperty != null) + { + label = EditorGUI.BeginProperty(controlRect, label, editor.model.textureFormatProperty); + } EditorGUI.BeginChangeCheck(); bool mixedValues = formatOptionsAreDifferent || editor.model.textureFormatIsDifferent; EditorGUI.showMixedValue = mixedValues; @@ -159,7 +177,10 @@ public virtual void ShowImportSettings(BaseTextureImportPlatformSettings editor) editor.model.SetTextureFormatForAll((TextureImporterFormat)selectionResult); formatForAll = selectionResult; } - EditorGUI.EndProperty(); + if (editor.model.textureFormatProperty != null) + { + EditorGUI.EndProperty(); + } if (!mixedValues && !Array.Exists(formatValuesForAll, i => i == formatForAll)) { @@ -171,7 +192,11 @@ public virtual void ShowImportSettings(BaseTextureImportPlatformSettings editor) if (editor.model.isDefault && editor.model.platformTextureSettings.format == TextureImporterFormat.Automatic) { controlRect = EditorGUILayout.GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.popup); - label = EditorGUI.BeginProperty(controlRect, kTextureCompression, editor.model.textureCompressionProperty); + label = kTextureCompression; + if (editor.model.textureCompressionProperty != null) + { + label = EditorGUI.BeginProperty(controlRect, label, editor.model.textureCompressionProperty); + } EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = editor.model.overriddenIsDifferent || editor.model.textureCompressionIsDifferent; @@ -183,7 +208,10 @@ public virtual void ShowImportSettings(BaseTextureImportPlatformSettings editor) { editor.model.SetTextureCompressionForAll(textureCompression); } - EditorGUI.EndProperty(); + if (editor.model.textureCompressionProperty != null) + { + EditorGUI.EndProperty(); + } } // Use Crunch Compression @@ -193,7 +221,11 @@ public virtual void ShowImportSettings(BaseTextureImportPlatformSettings editor) (textureShape == TextureImporterShape.Texture2D || textureShape == TextureImporterShape.TextureCube)) // 2DArray & 3D don't support Crunch { controlRect = EditorGUILayout.GetToggleRect(true); - label = EditorGUI.BeginProperty(controlRect, kCrunchedCompression, editor.model.crunchedCompressionProperty); + label = kCrunchedCompression; + if (editor.model.crunchedCompressionProperty != null) + { + label = EditorGUI.BeginProperty(controlRect, label, editor.model.crunchedCompressionProperty); + } EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = editor.model.overriddenIsDifferent || editor.model.crunchedCompressionIsDifferent; @@ -203,7 +235,10 @@ public virtual void ShowImportSettings(BaseTextureImportPlatformSettings editor) { editor.model.SetCrunchedCompressionForAll(crunchedCompression); } - EditorGUI.EndProperty(); + if (editor.model.crunchedCompressionProperty != null) + { + EditorGUI.EndProperty(); + } } // compression quality @@ -233,7 +268,11 @@ public virtual void ShowImportSettings(BaseTextureImportPlatformSettings editor) if (isETCPlatform && isDealingWithSprite && isETCFormatSelected) { controlRect = EditorGUILayout.GetToggleRect(true); - label = EditorGUI.BeginProperty(controlRect, kUseAlphaSplitLabel, editor.model.alphaSplitProperty); + label = kUseAlphaSplitLabel; + if (editor.model.alphaSplitProperty != null) + { + label = EditorGUI.BeginProperty(controlRect, label, editor.model.alphaSplitProperty); + } EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = editor.model.overriddenIsDifferent || editor.model.allowsAlphaSplitIsDifferent; bool allowsAlphaSplit = EditorGUI.Toggle(controlRect, label, editor.model.platformTextureSettings.allowsAlphaSplitting); @@ -241,7 +280,10 @@ public virtual void ShowImportSettings(BaseTextureImportPlatformSettings editor) { editor.model.SetAllowsAlphaSplitForAll(allowsAlphaSplit); } - EditorGUI.EndProperty(); + if (editor.model.alphaSplitProperty != null) + { + EditorGUI.EndProperty(); + } } } @@ -250,7 +292,11 @@ private void EditCompressionQuality(BaseTextureImportPlatformSettings editor, bo bool showAsEnum = !isCrunchedFormat && (BuildTargetDiscovery.PlatformHasFlag(editor.model.buildTarget, TargetAttributes.HasIntegratedGPU) || (textureFormat == TextureImporterFormat.BC6H) || (textureFormat == TextureImporterFormat.BC7)); Rect controlRect = showAsEnum ? EditorGUILayout.GetControlRect(true, EditorGUI.kSingleLineHeight, EditorStyles.popup) : EditorGUILayout.GetSliderRect(true); - GUIContent label = EditorGUI.BeginProperty(controlRect, showAsEnum ? kCompressionQuality : kCompressionQualitySlider, editor.model.compressionQualityProperty); + GUIContent label = showAsEnum ? kCompressionQuality : kCompressionQualitySlider; + if (editor.model.compressionQualityProperty != null) + { + label = EditorGUI.BeginProperty(controlRect, label, editor.model.compressionQualityProperty); + } EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = editor.model.overriddenIsDifferent || @@ -298,7 +344,10 @@ private void EditCompressionQuality(BaseTextureImportPlatformSettings editor, bo editor.model.SetCompressionQualityForAll(compression); //SyncPlatformSettings (); } - EditorGUI.EndProperty(); + if (editor.model.compressionQualityProperty != null) + { + EditorGUI.EndProperty(); + } } } } diff --git a/Editor/Mono/Modules/ModuleManager.cs b/Editor/Mono/Modules/ModuleManager.cs index 29e61d5c1e..f8af789703 100644 --- a/Editor/Mono/Modules/ModuleManager.cs +++ b/Editor/Mono/Modules/ModuleManager.cs @@ -6,6 +6,7 @@ using System.IO; using System.Collections.Generic; using System.Text.RegularExpressions; +using Unity.Profiling; using UnityEditor.Hardware; using UnityEditorInternal; using UnityEngine; @@ -17,6 +18,8 @@ namespace UnityEditor.Modules { internal static class ModuleManager { + private static readonly ProfilerMarkerWithStringData s_InitializePlatformSupportModule = ProfilerMarkerWithStringData.Create("InitializePlatformSupportModule", "Name"); + [NonSerialized] static Dictionary s_PlatformModules; @@ -113,14 +116,17 @@ internal static void InitializePlatformSupportModules() foreach (var module in platformSupportModules.Values) { - foreach (var library in module.NativeLibraries) - EditorUtility.LoadPlatformSupportNativeLibrary(library); - foreach (var fullPath in module.AssemblyReferencesForUserScripts) - InternalEditorUtility.RegisterPlatformModuleAssembly(Path.GetFileName(fullPath), fullPath); + using (s_InitializePlatformSupportModule.Auto(module.TargetName)) + { + foreach (var library in module.NativeLibraries) + EditorUtility.LoadPlatformSupportNativeLibrary(library); + foreach (var fullPath in module.AssemblyReferencesForUserScripts) + InternalEditorUtility.RegisterPlatformModuleAssembly(Path.GetFileName(fullPath), fullPath); - EditorUtility.LoadPlatformSupportModuleNativeDllInternal(module.TargetName); + EditorUtility.LoadPlatformSupportModuleNativeDllInternal(module.TargetName); - module.OnLoad(); + module.OnLoad(); + } } // Setup active build target and call OnActivate() for current platform module diff --git a/Editor/Mono/Modules/PlatformSupportModule.cs b/Editor/Mono/Modules/PlatformSupportModule.cs index e4b8721c45..bdb5790a98 100644 --- a/Editor/Mono/Modules/PlatformSupportModule.cs +++ b/Editor/Mono/Modules/PlatformSupportModule.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using UnityEditor.DeploymentTargets; using UnityEditor.Build; +using UnityEditor.Build.Reporting; using UnityEngine; namespace UnityEditor.Modules @@ -146,7 +147,8 @@ internal interface IBuildPostprocessor // This is the place to make sure platform has everything it needs for the build. // Use EditorUtility.Display(Cancelable)ProgressBar when running long tasks (e.g. downloading SDK from internet). // Return non-empty string indicating error message to stop the build. - string PrepareForBuild(BuildOptions options, BuildTarget target); + /// Build details. Useful to get the location of the player to build, for instance. + string PrepareForBuild(BuildPlayerOptions buildOptions); void PostProcessCompletedBuild(BuildPostProcessArgs args); @@ -256,6 +258,8 @@ internal interface ISettingEditorExtension bool SupportsFrameTimingStatistics(); void SerializedObjectUpdated(); + + bool SupportsStaticSplashScreenBackgroundColor(); } diff --git a/Editor/Mono/Networking/PlayerConnection/ConnectionDropDown.cs b/Editor/Mono/Networking/PlayerConnection/ConnectionDropDown.cs index 3b5bd0cda3..aba28e0286 100644 --- a/Editor/Mono/Networking/PlayerConnection/ConnectionDropDown.cs +++ b/Editor/Mono/Networking/PlayerConnection/ConnectionDropDown.cs @@ -142,7 +142,6 @@ public static GUIContent GetIcon(string name) "WSAPlayerX64" => EditorGUIUtility.IconContent("BuildSettings.Metro.Small"), "WSAPlayerARM" => EditorGUIUtility.IconContent("BuildSettings.Metro.Small"), "Switch" => EditorGUIUtility.IconContent("BuildSettings.Switch.Small"), - "Stadia" => EditorGUIUtility.IconContent("BuildSettings.Stadia.small"), "EmbeddedLinuxArm64" => EditorGUIUtility.IconContent("BuildSettings.EmbeddedLinux.Small"), "EmbeddedLinuxArm32" => EditorGUIUtility.IconContent("BuildSettings.EmbeddedLinux.Small"), "EmbeddedLinuxX64" => EditorGUIUtility.IconContent("BuildSettings.EmbeddedLinux.Small"), diff --git a/Editor/Mono/ObjectListArea.cs b/Editor/Mono/ObjectListArea.cs index bc50f31383..8357301ad2 100644 --- a/Editor/Mono/ObjectListArea.cs +++ b/Editor/Mono/ObjectListArea.cs @@ -103,8 +103,6 @@ static class Styles public bool foldersFirst { get; set; } int m_KeyboardControlID; - Dictionary m_AssetReferenceToCroppedNameMap = new Dictionary(new AssetReference.GuidThenInstanceIDEqualityComparer()); - int m_WidthUsedForCroppingName; bool m_AllowRenameOnMouseUp = true; @@ -212,9 +210,6 @@ public void Init(Rect rect, HierarchyType hierarchyType, SearchFilter searchFilt Repaint(); - // Clear instanceID to cropped name cache on init - ClearCroppedLabelCache(); - // Prepare data SetupData(true); } @@ -419,6 +414,8 @@ void HandleUnusedEvents() SetSelection(new int[0], false); } + internal Vector2 sizeUsedForCroppingName; + public bool CanShowThumbnails() { // @@ -1280,43 +1277,6 @@ public void OnInspectorUpdate() } } - void ClearCroppedLabelCache() - { - m_AssetReferenceToCroppedNameMap.Clear(); - } - - protected string GetCroppedLabelText(AssetReference assetReference, string fullText, float cropWidth) - { - // Clear when width changes - if (m_WidthUsedForCroppingName != (int)cropWidth) - ClearCroppedLabelCache(); - - string croppedText; - if (!m_AssetReferenceToCroppedNameMap.TryGetValue(assetReference, out croppedText)) - { - // Ensure to clean up once in a while - if (m_AssetReferenceToCroppedNameMap.Count > GetMaxNumVisibleItems() * 2 + 30) - ClearCroppedLabelCache(); - - // Check if we need to crop - int characterCountVisible = Styles.resultsGridLabel.GetNumCharactersThatFitWithinWidth(fullText, cropWidth); - if (characterCountVisible == -1) - { - Repaint(); - return fullText; // failed: do not cache result - } - - if (characterCountVisible > 1 && characterCountVisible != fullText.Length) - croppedText = fullText.Substring(0, characterCountVisible - 1) + ("\u2026"); // 'horizontal ellipsis' (U+2026) is: ... - else - croppedText = fullText; - - m_AssetReferenceToCroppedNameMap[assetReference] = croppedText; - m_WidthUsedForCroppingName = (int)cropWidth; - } - return croppedText; - } - public bool IsShowing(int instanceID) { return m_LocalAssets.IndexOf(instanceID) >= 0; @@ -1386,7 +1346,8 @@ public void BeginPing(int instanceID) float vcPadding = s_VCEnabled ? k_ListModeVersionControlOverlayPadding : 0f; var assetReference = new AssetReference() { instanceID = instanceID }; - GUIContent cont = new GUIContent(m_LocalAssets.ListMode ? name : GetCroppedLabelText(assetReference, name, m_WidthUsedForCroppingName)); + var textClipping = m_LocalAssets.ListMode ? TextClipping.Overflow : TextClipping.Ellipsis; + GUIContent cont = new GUIContent(name); string label = cont.text; if (m_LocalAssets.ListMode) @@ -1410,17 +1371,25 @@ public void BeginPing(int instanceID) else { m_Ping.m_PingStyle = Styles.miniPing; - Vector2 pingLabelSize = m_Ping.m_PingStyle.CalcSize(cont); - m_Ping.m_ContentRect.width = pingLabelSize.x; + var oldClipping = m_Ping.m_PingStyle.clipping; + m_Ping.m_PingStyle.clipping = textClipping; + Vector2 pingLabelSize = Styles.resultsGridLabel.CalcSizeWithConstraints(cont, sizeUsedForCroppingName); + m_Ping.m_ContentRect.width = pingLabelSize.x + Styles.resultsGridLabel.padding.horizontal; m_Ping.m_ContentRect.height = pingLabelSize.y; m_Ping.m_ContentDraw = (Rect r) => { // We need to temporary adjust style to render into content rect (org anchor is middle-centered) - TextAnchor orgAnchor = Styles.resultsGridLabel.alignment; - Styles.resultsGridLabel.alignment = TextAnchor.UpperLeft; + var orgAnchor = Styles.resultsGridLabel.alignment; + var orgClipping = Styles.resultsGridLabel.clipping; + // Shift the rect to match the original text position + r.position -= new Vector2(5, 1); + Styles.resultsGridLabel.alignment = TextAnchor.MiddleCenter; + Styles.resultsGridLabel.clipping = TextClipping.Ellipsis; Styles.resultsGridLabel.Draw(r, label, false, false, false, false); Styles.resultsGridLabel.alignment = orgAnchor; + Styles.resultsGridLabel.clipping = orgClipping; }; + m_Ping.m_PingStyle.clipping = oldClipping; } Vector2 pos = CalculatePingPosition(); m_Ping.m_ContentRect.x = pos.x; diff --git a/Editor/Mono/ObjectListLocalGroup.cs b/Editor/Mono/ObjectListLocalGroup.cs index 3b8c3468ba..e3f9e548c8 100644 --- a/Editor/Mono/ObjectListLocalGroup.cs +++ b/Editor/Mono/ObjectListLocalGroup.cs @@ -7,11 +7,8 @@ using UnityEditor.VersionControl; using UnityEditorInternal; using UnityEditorInternal.VersionControl; -using System.Collections; using System.Collections.Generic; -using System.Linq; using Math = System.Math; -using IndexOutOfRangeException = System.IndexOutOfRangeException; using AssetReference = UnityEditorInternal.InternalEditorUtility.AssetReference; namespace UnityEditor @@ -117,6 +114,7 @@ private void InitAssetPreviewIgnoreList() m_AssetPreviewIgnoreList.Add(typeof(LightingSettings)); m_AssetExtensionsPreviewIgnoreList.Add(".index"); + m_AssetExtensionsPreviewIgnoreList.Add(".vfx"); } //Use this to add the specific types that needs to ignored for AssetPreview image generation. @@ -835,6 +833,8 @@ void DrawItem(Rect position, FilteredHierarchy.FilterResult filterItem, BuiltinR } else // Icon grid { + Texture previewImage = null; + // Get icon bool drawDropShadow = false; if (string.IsNullOrEmpty(assetReference.guid) && m_Owner.GetCreateAssetUtility().instanceID == assetReference.instanceID && m_Owner.GetCreateAssetUtility().icon != null) @@ -849,17 +849,16 @@ void DrawItem(Rect position, FilteredHierarchy.FilterResult filterItem, BuiltinR else { // Check for asset preview - Texture image = null; bool shouldGetAssetPreview = ShouldGetAssetPreview(assetReference.instanceID); if (shouldGetAssetPreview) { if (assetReference.instanceID != 0) - image = AssetPreview.GetAssetPreview(assetReference.instanceID, m_Owner.GetAssetPreviewManagerID()); + previewImage = AssetPreview.GetAssetPreview(assetReference.instanceID, m_Owner.GetAssetPreviewManagerID()); else if (!string.IsNullOrEmpty(assetReference.guid)) - image = AssetPreview.GetAssetPreviewFromGUID(assetReference.guid, m_Owner.GetAssetPreviewManagerID()); + previewImage = AssetPreview.GetAssetPreviewFromGUID(assetReference.guid, m_Owner.GetAssetPreviewManagerID()); } - m_Content.image = image; + m_Content.image = previewImage; if (m_Content.image != null) drawDropShadow = true; @@ -942,11 +941,66 @@ void DrawItem(Rect position, FilteredHierarchy.FilterResult filterItem, BuiltinR if (isDropTarget) Styles.resultsLabel.Draw(new Rect(labelRect.x - 10, labelRect.y, labelRect.width + 20, labelRect.height), GUIContent.none, true, true, false, false); - labeltext = m_Owner.GetCroppedLabelText(assetReference, labeltext, orgPosition.width); - var labelNewRect = Styles.resultsGridLabel.CalcSizeWithConstraints(GUIContent.Temp(labeltext), orgPosition.size); - labelRect.x = orgPosition.x + (orgPosition.width - labelNewRect.x) / 2.0f; - labelRect.width = labelNewRect.x; - Styles.resultsGridLabel.Draw(labelRect, labeltext, false, false, selected, m_Owner.HasFocus()); + + Texture2D typeIcon = null; + if (filterItem != null && previewImage != null) + { + Type type = InternalEditorUtility.GetTypeWithoutLoadingObject(filterItem.instanceID); + + if (type != typeof(Texture2D)) + { + typeIcon = filterItem.icon; + } + } + + if (builtinResource != null) + { + Type type = InternalEditorUtility.GetTypeWithoutLoadingObject(builtinResource.m_InstanceID); + + if (type != typeof(Texture2D)) + { + typeIcon = AssetPreview.GetMiniTypeThumbnail(type); + } + } + + var orgClipping = Styles.resultsGridLabel.clipping; + var orgAlignment = Styles.resultsLabel.alignment; + var size = Styles.resultsGridLabel.CalcSizeWithConstraints(GUIContent.Temp(labeltext, typeIcon), orgPosition.size); + size.x += Styles.resultsGridLabel.padding.horizontal; + labelRect.x = orgPosition.x + (orgPosition.width - size.x) / 2.0f; + labelRect.width = size.x; + labelRect.height = size.y; + m_Owner.sizeUsedForCroppingName = orgPosition.size; + + Styles.resultsGridLabel.clipping = TextClipping.Ellipsis; + Styles.resultsGridLabel.alignment = TextAnchor.MiddleCenter; + Styles.resultsGridLabel.Draw(labelRect, GUIContent.Temp(labeltext, typeIcon), false, false, selected, m_Owner.HasFocus()); + Styles.resultsGridLabel.clipping = orgClipping; + Styles.resultsLabel.alignment = orgAlignment; + + // We only need to set the tooltip once, and not for every item. + if (labelRect.Contains(Event.current.mousePosition)) + { + string tooltip = null; + + if (filterItem != null) + { + //We use GetAssetPath to have the file extension as well + string path = AssetDatabase.GetAssetPath(filterItem.instanceID); + tooltip = path.Substring(path.LastIndexOf('/') + 1); + } + else if (builtinResource != null) + { + //We have a "None" item in the ObjectSelector that has a 0 instanceID + if (builtinResource.m_InstanceID != 0) + tooltip = builtinResource.m_Name + "\n" + "(Built-in Resource)"; + } + + if (tooltip != null) + { + GUI.Label(labelRect, GUIContent.Temp("", tooltip)); + } + } } } diff --git a/Editor/Mono/ObjectNames.cs b/Editor/Mono/ObjectNames.cs index b40fbc23fb..f90bfb6a70 100644 --- a/Editor/Mono/ObjectNames.cs +++ b/Editor/Mono/ObjectNames.cs @@ -49,7 +49,7 @@ public static bool TryGet(Type objectType, out string title) } } - private static string GetObjectTypeName([NotNull] Object o) + private static string GetObjectTypeName([NotNull] Object o, bool multiObjectEditing = false) { if (o is GameObject) return o.name; @@ -72,6 +72,9 @@ private static string GetObjectTypeName([NotNull] Object o) var meshfilter = o as MeshFilter; if (meshfilter) { + if (multiObjectEditing) + return "MeshFilter"; + var mesh = meshfilter.sharedMesh; return (mesh ? mesh.name : L10n.Tr("[none]")) + " (MeshFilter)"; } @@ -102,8 +105,7 @@ private static string GetObjectTypeName([NotNull] Object o) return o.name + " (" + o.GetType().Name + ")"; } - // Inspector title for an object. - public static string GetInspectorTitle(Object obj) + public static string GetInspectorTitle(Object obj, bool multiObjectEditing) { if (obj == null && (object)obj != null && (obj is MonoBehaviour || obj is ScriptableObject)) return L10n.Tr(" (Script)"); @@ -113,7 +115,7 @@ public static string GetInspectorTitle(Object obj) string title; if (!InspectorTitles.TryGet(obj.GetType(), out title)) - title = NicifyVariableName(GetObjectTypeName(obj)); + title = NicifyVariableName(GetObjectTypeName(obj, multiObjectEditing)); if (Attribute.IsDefined(obj.GetType(), typeof(ObsoleteAttribute))) title += L10n.Tr(" (Deprecated)"); @@ -121,6 +123,12 @@ public static string GetInspectorTitle(Object obj) return title; } + // Inspector title for an object. + public static string GetInspectorTitle(Object obj) + { + return GetInspectorTitle(obj, false); + } + // Like GetClassName but handles folders, scenes, GUISkins, and other default assets as separate types. internal static string GetTypeName(Object obj) { @@ -137,7 +145,7 @@ internal static string GetTypeName(Object obj) return "Folder"; else if (obj.GetType() == typeof(Object)) return System.IO.Path.GetExtension(pathLower) + " File"; - return ObjectNames.GetClassName(obj); + return obj.GetType().Name; } [Obsolete("Please use NicifyVariableName instead")] diff --git a/Editor/Mono/ObjectSelector.cs b/Editor/Mono/ObjectSelector.cs index 790990a32d..bf8680172d 100644 --- a/Editor/Mono/ObjectSelector.cs +++ b/Editor/Mono/ObjectSelector.cs @@ -545,10 +545,8 @@ internal void Show(UnityObject obj, Type[] requiredTypes, UnityObject objectBein var shouldRepositionWindow = m_Parent != null; ShowWithMode(ShowMode.AuxWindow); - string text = "Select " + (requiredTypes[0] == null ? m_RequiredTypes[0] : requiredTypes[0].Name); - for (int i = 1; i < requiredTypes.Length; i++) - text += (i == requiredTypes.Length - 1 ? " or " : ", ") + (requiredTypes[i] == null ? m_RequiredTypes[i] : requiredTypes[i].Name); - titleContent = EditorGUIUtility.TrTextContent(text); + + titleContent = EditorGUIUtility.TrTextContent(GenerateTitleContent(requiredTypes, m_RequiredTypes)); // Deal with window size if (shouldRepositionWindow) @@ -598,6 +596,20 @@ internal void Show(UnityObject obj, Type[] requiredTypes, UnityObject objectBein } } + internal static string GenerateTitleContent(Type[] requiredTypes, string[] requiredTypeStrings) + { + var typeName = requiredTypes[0] == null ? requiredTypeStrings[0] : requiredTypes[0].Name; + var text = "Select " + ObjectNames.NicifyVariableName(typeName); + + for (int i = 1; i < requiredTypes.Length; i++) + { + typeName = requiredTypes[i] == null ? requiredTypeStrings[i] : requiredTypes[i].Name; + text += (i == requiredTypes.Length - 1 ? " or " : ", ") + ObjectNames.NicifyVariableName(typeName); + } + + return text; + } + void ItemWasDoubleClicked() { SendEvent(ObjectSelectorSelectionDoneCommand, false); @@ -855,10 +867,13 @@ void WidePreview(float actualSize, string s, UnityObject o, EditorWrapper p) else if (o != null) DrawObjectIcon(previewRect, m_ListArea.m_SelectedObjectIcon); + var prevClipping = Styles.smallStatus.clipping; + Styles.smallStatus.clipping = TextClipping.Overflow; if (EditorGUIUtility.isProSkin) EditorGUI.DropShadowLabel(labelRect, s, Styles.smallStatus); else GUI.Label(labelRect, s, Styles.smallStatus); + Styles.smallStatus.clipping = prevClipping; } void OverlapPreview(float actualSize, string s, UnityObject o, EditorWrapper p) diff --git a/Editor/Mono/Overlays/OverlayAttribute.cs b/Editor/Mono/Overlays/OverlayAttribute.cs index 472241b0fe..50209d7ca0 100644 --- a/Editor/Mono/Overlays/OverlayAttribute.cs +++ b/Editor/Mono/Overlays/OverlayAttribute.cs @@ -90,7 +90,7 @@ public float defaultHeight public OverlayAttribute() { m_EditorWindowType = null; - m_DefaultDisplay = false; + m_DefaultDisplay = true; m_Id = null; m_DisplayName = null; m_UssName = null; diff --git a/Editor/Mono/Overlays/OverlayCanvas.cs b/Editor/Mono/Overlays/OverlayCanvas.cs index cabdd1df31..fd2eeca98d 100644 --- a/Editor/Mono/Overlays/OverlayCanvas.cs +++ b/Editor/Mono/Overlays/OverlayCanvas.cs @@ -141,6 +141,13 @@ public override string ToString() } } + [Serializable] + sealed class ContainerData + { + public string containerId; + public float scrollOffset; + } + //Dock position within container //for a horizontal container, Top is left, Bottom is right public enum DockPosition @@ -231,6 +238,7 @@ internal OverlayContainer GetDockZoneContainer(DockZone zone) return null; } + bool m_MouseInCurrentCanvas = false; OverlayMenu m_Menu; internal string lastAppliedPresetName => m_LastAppliedPresetName; List m_Overlays = new List(); @@ -242,6 +250,12 @@ internal OverlayContainer GetDockZoneContainer(DockZone zone) [SerializeField] List m_SaveData = new List(); + [SerializeField] + List m_ContainerData = new List(); + + [SerializeField] + bool m_OverlaysVisible = true; + VisualElement m_RootVisualElement; internal EditorWindow containerWindow { get; set; } @@ -281,6 +295,8 @@ internal OverlayCanvas() { } internal void SetOverlaysEnabled(bool visible) { + m_OverlaysVisible = visible; + if (visible == overlaysEnabled) return; @@ -335,6 +351,10 @@ VisualElement CreateRoot() else defaultContainer = container; } + + var data = GetContainerData(container.name); + if (container is ToolbarOverlayContainer toolbar) + toolbar.scrollOffset = data.scrollOffset; } m_OriginGhost = new VisualElement { name = "origin-ghost"}; @@ -350,6 +370,9 @@ VisualElement CreateRoot() ve.RegisterCallback(OnDetachedFromPanel); m_WindowRoot = ve.Q("overlay-window-root"); + + SetOverlaysEnabled(m_OverlaysVisible); + return ve; } @@ -370,11 +393,15 @@ void OnAttachedToPanel(AttachToPanelEvent evt) { //this is used to clamp overlays to floating container bounds. floatingContainer.RegisterCallback(GeometryChanged); + rootVisualElement.RegisterCallback(OnMouseEnter); + rootVisualElement.RegisterCallback(OnMouseLeave); } void OnDetachedFromPanel(DetachFromPanelEvent evt) { floatingContainer.UnregisterCallback(GeometryChanged); + rootVisualElement.UnregisterCallback(OnMouseEnter); + rootVisualElement.UnregisterCallback(OnMouseLeave); } internal void OnContainerWindowDisabled() @@ -383,6 +410,16 @@ internal void OnContainerWindowDisabled() overlay.OnWillBeDestroyed(); } + void OnMouseEnter(MouseEnterEvent evt) + { + m_MouseInCurrentCanvas = true; + } + + void OnMouseLeave(MouseLeaveEvent evt) + { + m_MouseInCurrentCanvas = false; + } + internal Rect ClampToOverlayWindow(Rect rect) { return ClampRectToBounds(rootVisualElement.localBound, rect); @@ -451,7 +488,7 @@ internal void HideHoveredOverlay() internal void ShowMenu(bool show, bool atMousePosition = true) { if (show && !menuVisible) - menu.Show(atMousePosition); + menu.Show(atMousePosition && m_MouseInCurrentCanvas); else if (!show) menu.Hide(); } @@ -505,7 +542,10 @@ internal void ShowOriginGhost(Overlay overlay) { m_OriginGhost.style.width = overlay.rootVisualElement.layout.width; m_OriginGhost.style.height = overlay.rootVisualElement.layout.height; - overlay.container?.Insert(overlay.container.IndexOf(overlay.rootVisualElement), m_OriginGhost); + if (overlay.container.GetOverlayIndex(overlay, out var section, out var index)) + { + overlay.container.GetSectionElement(section)?.Insert(index, m_OriginGhost); + } } internal void UpdateGhostHover(bool hovered) @@ -551,6 +591,10 @@ public void OnBeforeSerialize() if (!after[i].dontSaveInLayout) WriteOrReplaceSaveData(after[i], i); } + + var data = GetContainerData(container.name); + if (container is ToolbarOverlayContainer toolbar) + data.scrollOffset = toolbar.scrollOffset; } } } @@ -780,5 +824,21 @@ void RestoreOverlays() afterOverlaysInitialized?.Invoke(); } + + ContainerData GetContainerData(string containerId) + { + foreach (var data in m_ContainerData) + if (data.containerId == containerId) + return data; + + var newData = new ContainerData + { + containerId = containerId, + scrollOffset = 0 + }; + + m_ContainerData.Add(newData); + return newData; + } } } diff --git a/Editor/Mono/Overlays/OverlayContainer.cs b/Editor/Mono/Overlays/OverlayContainer.cs index a31d7c8b43..7f1b47c79e 100644 --- a/Editor/Mono/Overlays/OverlayContainer.cs +++ b/Editor/Mono/Overlays/OverlayContainer.cs @@ -53,14 +53,18 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext } public const string className = "unity-overlay-container"; - public const string spacerClassName = "overlay-container__spacer"; const string k_HorizontalClassName = className + "-horizontal"; const string k_VerticalClassName = className + "-vertical"; + const string k_ContentClassName = className + "__content"; + const string k_BeforeClassName = className + "__before-spacer-container"; + const string k_AfterClassName = className + "__after-spacer-container"; + const string k_SpacingContainerClassName = className + "__spacing-container"; public static readonly Overlay spacerMarker = null; readonly List m_BeforeOverlays = new List(); readonly List m_AfterOverlays = new List(); - readonly VisualElement m_Spacer; + readonly VisualElement m_BeforeSectionContent; + readonly VisualElement m_AfterSectionContent; // This is set by querying the stylesheet for 'vertical' and 'horizontal' Layout m_SupportedOverlayLayouts = 0; //Used as a flag in this case @@ -68,6 +72,9 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext public OverlayCanvas canvas { get; internal set; } + protected readonly VisualElement beforeSectionContainer; + protected readonly VisualElement afterSectionContainer; + public int overlayCount => m_BeforeOverlays.Count + m_AfterOverlays.Count; public virtual Layout preferredLayout => Layout.Panel; @@ -87,21 +94,32 @@ public bool isHorizontal } } - protected VisualElement spacer => m_Spacer; - public bool isSpacerVisible => !Mathf.Approximately(spacer.rect.width, 0) && !Mathf.Approximately(spacer.rect.height, 0); + public float spacerSize => isHorizontal + ? layout.width - (beforeSectionContainer.layout.width + afterSectionContainer.layout.width) + : layout.height - (beforeSectionContainer.layout.height + afterSectionContainer.layout.height); + + public bool isSpacerVisible => !Mathf.Approximately(spacerSize, 0); public OverlayContainer() { AddToClassList(className); name = className; - Add(m_Spacer = new VisualElement()); - m_Spacer.AddToClassList(spacerClassName); - - m_Spacer.Add(new OverlayContainerDropZone(this, OverlayContainerDropZone.Placement.Start)); - var dropZoneSpacer = new VisualElement { name = "DropZonesSpacer" }; - m_Spacer.Add(dropZoneSpacer); - m_Spacer.Add(new OverlayContainerDropZone(this, OverlayContainerDropZone.Placement.End)); + beforeSectionContainer = new VisualElement(); + Add(beforeSectionContainer); + beforeSectionContainer.Add(m_BeforeSectionContent = new VisualElement()); + beforeSectionContainer.Add(new OverlayContainerDropZone(this, OverlayContainerDropZone.Placement.Start)); + beforeSectionContainer.AddToClassList(k_BeforeClassName); + beforeSectionContainer.AddToClassList(k_SpacingContainerClassName); + m_BeforeSectionContent.AddToClassList(k_ContentClassName); + + afterSectionContainer = new VisualElement(); + Add(afterSectionContainer); + afterSectionContainer.Add(new OverlayContainerDropZone(this, OverlayContainerDropZone.Placement.End)); + afterSectionContainer.Add(m_AfterSectionContent = new VisualElement()); + afterSectionContainer.AddToClassList(k_AfterClassName); + afterSectionContainer.AddToClassList(k_SpacingContainerClassName); + m_AfterSectionContent.AddToClassList(k_ContentClassName); SetVertical(); } @@ -135,32 +153,22 @@ public void InsertOverlay(Overlay overlay, OverlayContainerSection section, int return; var list = GetSectionInternal(section); - + var element = GetSectionElement(section); int realIndex = -1; //Insert relative to another element in case other visual elements are added to hierarchy if (index < list.Count) { - realIndex = IndexOf(list[index].rootVisualElement); - - // Section after spacer is listed from bottom to spacer instead of spacer to bottom. - // So before an overlay after the spacer is actually after the overlay element in the container hierarchy. - if (section == OverlayContainerSection.AfterSpacer) - ++realIndex; - + realIndex = element.IndexOf(list[index].rootVisualElement); } - - if (realIndex < 0) + else if (index == list.Count) { - switch (section) - { - case OverlayContainerSection.BeforeSpacer: realIndex = IndexOf(m_Spacer); break; - case OverlayContainerSection.AfterSpacer: realIndex = IndexOf(m_Spacer) + 1; break; - } + realIndex = element.childCount; } + realIndex = Mathf.Max(realIndex, 0); - Insert(realIndex, overlay.rootVisualElement); + element.Insert(realIndex, overlay.rootVisualElement); list.Insert(index, overlay); } @@ -219,6 +227,17 @@ public ReadOnlyCollection GetSection(OverlayContainerSection section) return GetSectionInternal(section).AsReadOnly(); } + public VisualElement GetSectionElement(OverlayContainerSection section) + { + switch (section) + { + case OverlayContainerSection.BeforeSpacer: return m_BeforeSectionContent; + case OverlayContainerSection.AfterSpacer: return m_AfterSectionContent; + default: + throw new InvalidEnumArgumentException(); + } + } + List GetSectionInternal(OverlayContainerSection section) { switch (section) @@ -283,38 +302,82 @@ class ToolbarOverlayContainer : OverlayContainer readonly OverlayDropZoneBase m_NoElementDropZone; readonly VisualElement m_ContentContainer; + readonly ScrollView m_ScrollView; + + float m_ScrollOffsetRequestedValue; public override VisualElement contentContainer => m_ContentContainer ?? base.contentContainer; public override Layout preferredLayout => isHorizontal ? Layout.HorizontalToolbar : Layout.VerticalToolbar; + internal bool canAssignScrollOffset => isHorizontal ? HasValidScrollerValues(m_ScrollView.horizontalScroller) : HasValidScrollerValues(m_ScrollView.verticalScroller); + + public float scrollOffset + { + get => isHorizontal ? m_ScrollView.scrollOffset.x : m_ScrollView.scrollOffset.y; + set + { + if (canAssignScrollOffset) + m_ScrollView.scrollOffset = isHorizontal ? new Vector2(value, 0) : new Vector2(0, value); + else + m_ScrollOffsetRequestedValue = value; + } + } + + + public ToolbarOverlayContainer() { + m_ScrollView = new ScrollView(ScrollViewMode.Horizontal); + m_ScrollView.style.width = new StyleLength(new Length(100, LengthUnit.Percent)); + hierarchy.Add(m_ScrollView); + m_ScrollView.RegisterCallback(DelayScrollViewInit); + AddToClassList(k_ToolbarClassName); m_NoElementDropZone = new HiddenToolbarDropZone(this) { name = "NoElementToolbarDropZone" }; hierarchy.Add(m_NoElementDropZone); - hierarchy.Add(m_ContentContainer = new VisualElement()); - m_ContentContainer.style.flexGrow = 1; - m_ContentContainer.style.flexDirection = isHorizontal ? FlexDirection.Row : FlexDirection.Column; - m_ContentContainer.pickingMode = PickingMode.Ignore; - m_ContentContainer.Add(spacer); + m_ContentContainer = m_ScrollView.contentContainer; + Add(beforeSectionContainer); + Add(afterSectionContainer); + + //Force the current direction because scroll view was just created + if (isHorizontal) + SetHorizontal(); + else + SetVertical(); + } + + void DelayScrollViewInit(GeometryChangedEvent evt) + { + m_ScrollView.UnregisterCallback(DelayScrollViewInit); + m_ScrollView.horizontalScrollerVisibility = m_ScrollView.verticalScrollerVisibility = ScrollerVisibility.Hidden; + if (!Mathf.Approximately(m_ScrollOffsetRequestedValue, 0)) + scrollOffset = m_ScrollOffsetRequestedValue; } protected override void SetHorizontal() { base.SetHorizontal(); - if (contentContainer != null) - contentContainer.style.flexDirection = FlexDirection.Row; + if (m_ScrollView != null) + { + m_ScrollView.mode = ScrollViewMode.Horizontal; + m_ScrollView.style.width = new StyleLength(new Length(100, LengthUnit.Percent)); + m_ScrollView.style.height = new StyleLength(StyleKeyword.Auto); + } } protected override void SetVertical() { base.SetVertical(); - if (contentContainer != null) - contentContainer.style.flexDirection = FlexDirection.Column; + if (m_ScrollView != null) + { + m_ScrollView.mode = ScrollViewMode.Vertical; + m_ScrollView.style.height = new StyleLength(new Length(100, LengthUnit.Percent)); + m_ScrollView.style.width = new StyleLength(StyleKeyword.Auto); + } } public override bool IsOverlayLayoutSupported(Layout requested) @@ -323,5 +386,10 @@ public override bool IsOverlayLayoutSupported(Layout requested) return (requested & Layout.HorizontalToolbar) > 0; return (requested & Layout.VerticalToolbar) > 0; } + + bool HasValidScrollerValues(Scroller scroller) + { + return !float.IsNaN(scroller.lowValue) && !float.IsNaN(scroller.highValue); + } } } diff --git a/Editor/Mono/Overlays/OverlayDropZone.cs b/Editor/Mono/Overlays/OverlayDropZone.cs index 87f5db1458..f3c32e19b8 100644 --- a/Editor/Mono/Overlays/OverlayDropZone.cs +++ b/Editor/Mono/Overlays/OverlayDropZone.cs @@ -107,6 +107,15 @@ protected override void OnDropZoneActivated(Overlay draggedOverlay) } else { + if (m_Container is ToolbarOverlayContainer) + { + var targetSize = m_Container.spacerSize * .5f; + if (m_Container.isHorizontal) + dropArea.style.width = targetSize; + else + dropArea.style.height = targetSize; + } + if (m_Container is ToolbarOverlayContainer && !m_Container.HasVisibleOverlays()) { SetVisualMode(VisualMode.Disabled); @@ -271,8 +280,6 @@ public override void PopulateDestMarkerClassList(IList classes) protected override void OnDropZoneActivated(Overlay draggedOverlay) { - base.OnDropZoneActivated(draggedOverlay); - SetVisualMode(m_OverlayContainer.HasVisibleOverlays() ? VisualMode.Disabled : VisualMode.Custom); } @@ -315,6 +322,8 @@ protected enum VisualMode public int priority { get; set; } public abstract void DropOverlay(Overlay overlay); + protected VisualElement dropArea => m_DropArea; + public virtual void PopulateDestMarkerClassList(IList classes) { switch (visualMode) @@ -364,6 +373,7 @@ protected OverlayDropZoneBase() pickingMode = PickingMode.Ignore; m_DropArea.pickingMode = PickingMode.Ignore; m_VisualBounds.pickingMode = PickingMode.Ignore; + m_DropArea.style.display = DisplayStyle.None; RegisterCallback(OnAttachToPanel); RegisterCallback(OnDetachFromPanel); @@ -385,12 +395,14 @@ void OnDragStarted(Overlay overlay) { //TODO check if in same canvas? or we support dragging to different canvas m_DropArea.pickingMode = PickingMode.Position; + m_DropArea.style.display = DisplayStyle.Flex; OnDropZoneActivated(overlay); } void OnDragEnded(Overlay overlay) { m_DropArea.pickingMode = PickingMode.Ignore; + m_DropArea.style.display = DisplayStyle.None; OnDropZoneDeactivated(overlay); } diff --git a/Editor/Mono/Overlays/OverlayPopup.cs b/Editor/Mono/Overlays/OverlayPopup.cs index 8e03328486..26078d57ed 100644 --- a/Editor/Mono/Overlays/OverlayPopup.cs +++ b/Editor/Mono/Overlays/OverlayPopup.cs @@ -41,6 +41,7 @@ public OverlayPopup(Overlay overlay) { var proposed = overlay.collapsedButtonRect; proposed.size = evt.newRect.size; + var placement = OverlayCanvas.ClampRectToBounds(overlay.canvas.windowRoot.worldBound, proposed); if (!Mathf.Approximately(proposed.position.x, placement.position.x)) @@ -54,34 +55,63 @@ public OverlayPopup(Overlay overlay) this.EnableInClassList(k_FromVertical, true); if (!overlay.isInToolbar) - { this.EnableInClassList(k_OutsideToolbar, true); - var overlayWorldBound = overlay.rootVisualElement.worldBound; - var rightPlacement = overlayWorldBound.x + overlayWorldBound.width; - var rightSideSpace = canvasWorld.xMax - rightPlacement; + var overlayWorldBound = overlay.rootVisualElement.worldBound; + + var rightPlacement = overlayWorldBound.x + overlayWorldBound.width; + var rightSideSpace = canvasWorld.xMax - rightPlacement; - var xAdjusted = placement.position.x; - if (rightSideSpace >= placement.width) - xAdjusted = rightPlacement; - else + var xAdjusted = placement.position.x; + var maxWidth = placement.width; + if (rightSideSpace >= placement.width) + { + xAdjusted = rightPlacement; + } + else + { + var leftSideSpace = placement.x - overlay.canvas.rootVisualElement.worldBound.x; + if (leftSideSpace >= placement.width) + { + xAdjusted = overlayWorldBound.x - placement.width; + } + else // If neither side has enough space, show the popup on the widest one { - var leftSideSpace = placement.x - overlay.canvas.rootVisualElement.worldBound.x; - if (leftSideSpace >= placement.width) + if (rightSideSpace > leftSideSpace) + xAdjusted = overlayWorldBound.x + overlayWorldBound.width; + else xAdjusted = overlayWorldBound.x - placement.width; - else // If neither side has enough space, show the popup on the widest one - { - if (rightSideSpace > leftSideSpace) - xAdjusted = overlayWorldBound.x + overlayWorldBound.width; - else - xAdjusted = overlayWorldBound.x - placement.width; - } + + maxWidth = canvasWorld.xMax - xAdjusted; + } + } + + var yAdjusted = placement.position.y; + var bottomSpace = canvasWorld.yMax - yAdjusted; + + var maxHeight = placement.height; + if (bottomSpace < placement.height) + { + var upPlacement = overlayWorldBound.y + overlayWorldBound.height; + var upSpace = upPlacement - canvasWorld.y; + if (upSpace >= placement.height) + { + yAdjusted = upPlacement - placement.height; + } + else // If neither side has enough space, show the popup on the widest one + { + if (bottomSpace <= upSpace) + yAdjusted = upPlacement - placement.height; + + maxHeight = canvasWorld.yMax - yAdjusted; } - placement.position = new Vector2(xAdjusted, placement.position.y); } - style.maxWidth = canvasWorld.xMax - placement.position.x; - style.maxHeight = canvasWorld.yMax - placement.position.y; + placement.position = new Vector2(xAdjusted, yAdjusted); + + style.maxHeight = maxHeight; + style.maxWidth = maxWidth; + transform.position = placement.position - canvasWorld.position; }); } diff --git a/Editor/Mono/Overlays/OverlayResizer.cs b/Editor/Mono/Overlays/OverlayResizer.cs index 9bb742c3a2..5abd03bbe6 100644 --- a/Editor/Mono/Overlays/OverlayResizer.cs +++ b/Editor/Mono/Overlays/OverlayResizer.cs @@ -13,6 +13,7 @@ class OverlayResizerGroup : VisualElement { const float k_CornerSize = 8; const float k_SideSize = 6; + const float k_MinDistanceFromEdge = 10; [Flags] enum Direction @@ -75,11 +76,18 @@ public OverlayResizer(Overlay overlay, Direction direction) void OnMouseDown(MouseDownEvent evt) { + var container = m_Overlay.rootVisualElement.GetFirstAncestorOfType(); + + + var overlayPosition = m_Overlay.rootVisualElement.layout.position; + if (container != null) + overlayPosition = m_Overlay.rootVisualElement.parent.ChangeCoordinatesTo(container, overlayPosition); + m_OriginalRect = new Rect( - m_Overlay.floating ? m_Overlay.floatingPosition : m_Overlay.rootVisualElement.layout.position, + m_Overlay.floating ? m_Overlay.floatingPosition : overlayPosition, m_Overlay.size); - m_ContainerRect = m_Overlay.rootVisualElement.parent.rect; + m_ContainerRect = container?.rect ?? new Rect(float.NegativeInfinity,float.NegativeInfinity,float.PositiveInfinity,float.PositiveInfinity); m_OriginalMousePosition = evt.mousePosition; m_MaxSize = m_Overlay.maxSize; m_MinSize = m_Overlay.minSize; @@ -210,6 +218,9 @@ public OverlayResizerGroup(Overlay overlay) overlay.containerChanged += OnOverlayContainerChanged; overlay.layoutChanged += OnOverlayLayoutChanged; + overlay.floatingPositionChanged += OnOverlayPositionChanged; + overlay.collapsedChanged += OnOverlayCollaspedChanged; + m_Overlay.rootVisualElement.RegisterCallback(OnOverlayGeometryChanged); UpdateResizerVisibility(); } @@ -223,6 +234,21 @@ void OnOverlayContainerChanged(OverlayContainer container) UpdateResizerVisibility(); } + void OnOverlayGeometryChanged(GeometryChangedEvent evt) + { + UpdateResizerVisibility(); + } + + void OnOverlayPositionChanged(Vector3 position) + { + UpdateResizerVisibility(); + } + + void OnOverlayCollaspedChanged(bool collapsed) + { + UpdateResizerVisibility(); + } + bool ContainerCanShowResizer(OverlayResizer resizer) { var container = m_Overlay.container; @@ -255,14 +281,15 @@ bool ContainerCanShowResizer(OverlayResizer resizer) void UpdateResizerVisibility() { - bool globalHide = m_Overlay.layout != Layout.Panel; + bool globalHide = m_Overlay.layout != Layout.Panel && !m_Overlay.collapsed; foreach (var resizer in m_Resizers) { bool hide = globalHide || !ContainerCanShowResizer(resizer); if (resizer.HasMultipleDirections()) { - hide |= m_Overlay.minSize == m_Overlay.maxSize; + hide |= Mathf.Approximately(m_Overlay.minSize.x, m_Overlay.maxSize.x); + hide |= Mathf.Approximately(m_Overlay.minSize.y, m_Overlay.maxSize.y); } else { @@ -273,6 +300,26 @@ void UpdateResizerVisibility() hide |= Mathf.Approximately(m_Overlay.minSize.y, m_Overlay.maxSize.y); } + if (m_Overlay.canvas != null) + { + var canvas = m_Overlay.canvas.rootVisualElement; + var canvasRect = canvas.rect; + var overlayRect = canvas.WorldToLocal(m_Overlay.rootVisualElement.worldBound); + + if (resizer.HasDirection(Direction.Left)) + hide |= overlayRect.xMin <= k_MinDistanceFromEdge; + + if (resizer.HasDirection(Direction.Right)) + hide |= overlayRect.xMax >= canvasRect.xMax - k_MinDistanceFromEdge; + + if (resizer.HasDirection(Direction.Top)) + hide |= overlayRect.yMin <= k_MinDistanceFromEdge; + + if (resizer.HasDirection(Direction.Bottom)) + hide |= overlayRect.yMax >= canvasRect.yMax - k_MinDistanceFromEdge; + } + + resizer.style.display = hide ? DisplayStyle.None : DisplayStyle.Flex; } } diff --git a/Editor/Mono/Overlays/OverlayUtilities.cs b/Editor/Mono/Overlays/OverlayUtilities.cs index 92f9a4e5b8..fe4c09d0ab 100644 --- a/Editor/Mono/Overlays/OverlayUtilities.cs +++ b/Editor/Mono/Overlays/OverlayUtilities.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Text.RegularExpressions; using UnityEditor.EditorTools; using UnityEditor.Toolbars; using UnityEngine; @@ -142,11 +143,21 @@ internal static string GetSignificantLettersForIcon(string s) var folders = s.Split('/'); var last = folders[folders.Length - 1]; - var words = last.Trim().Split(' '); + if (string.IsNullOrEmpty(last)) + return string.Empty; + var words = last.Trim().Split(' '); if (words.Length == 1) { - return words[0].Length > 1 ? words[0].Substring(0, 2) : words[0][0].ToString(); + var regex = new Regex(@"[A-Z][^A-Z]*", RegexOptions.Compiled); + var matches = regex.Matches(words[0]); + if (matches == null || matches.Count == 0) + return words[0].Length > 1 ? words[0].Substring(0, 2) : words[0][0].ToString(); + + if (matches.Count == 1) + return matches[0].Length > 1 ? matches[0].Value.Substring(0, 2) : matches[0].Value[0].ToString(); + + return matches[0].Value.Substring(0, 1) + matches[1].Value.Substring(0, 1); } return words[0].Substring(0, 1) + words[1].Substring(0, 1); diff --git a/Editor/Mono/PerformanceTools/FrameDebugger.cs b/Editor/Mono/PerformanceTools/FrameDebugger.cs index 1e0445443d..b4439cd664 100644 --- a/Editor/Mono/PerformanceTools/FrameDebugger.cs +++ b/Editor/Mono/PerformanceTools/FrameDebugger.cs @@ -27,6 +27,8 @@ internal class FrameDebuggerWindow : EditorWindow private int m_EnablingWaitCounter = 0; private int m_RepaintFrames = k_NeedToRepaintFrames; private int m_FrameEventsHash; + private bool m_ShowTabbedErrorBox; + private bool m_HasOpenedPlaymodeView; private Rect m_SearchRect; private string m_SearchString = String.Empty; private IConnectionState m_AttachToPlayerState; @@ -103,6 +105,12 @@ internal void ChangeFrameEventLimit(int newLimit, FrameDebuggerTreeView.FrameDeb m_TreeView?.SelectFrameEventIndex(newLimit); } + internal void OnConnectedProfilerChange() + { + DisableFrameDebugger(); + EnableFrameDebugger(); + } + internal static void RepaintAll() { foreach (var fd in s_FrameDebuggers) @@ -186,10 +194,17 @@ private void DrawDisabledFrameDebugger() } EditorGUILayout.HelpBox(FrameDebuggerStyles.EventDetails.k_DescriptionString, MessageType.Info, true); + + if (m_ShowTabbedErrorBox) + EditorGUILayout.HelpBox(FrameDebuggerStyles.EventDetails.k_TabbedWithPlaymodeErrorString, MessageType.Error, true); } private void HandleEnablingFrameDebugger() { + // Make sure the PlayMode window is enabled and shown... + if (!OpenPlayModeView()) + return; + if (Event.current.type != EventType.Repaint) return; @@ -203,19 +218,74 @@ private void HandleEnablingFrameDebugger() } } - private void DrawEnabledFrameDebugger(bool repaint) + private bool CheckIfFDIsDockedWithGameWindow(DockArea da, PlayModeView gameWindow) { - int oldLimit = FrameDebuggerUtility.limit; - FrameDebuggerEvent[] descs = FrameDebuggerUtility.GetFrameEvents(); + for (int i = 0; i < da.m_Panes.Count; i++) + if (gameWindow == da.m_Panes[i]) + return true; + return false; + } - // Make sure the PlayMode window is enabled and shown... - if (FrameDebugger.IsLocalEnabled()) + private bool OpenPlayModeView() + { + if (m_HasOpenedPlaymodeView) + return true; + + // When debugging remote players, we can ignore this check as it doesn't render to the Game Window. + if (!FrameDebugger.IsLocalEnabled() && m_AttachToPlayerState.connectedToTarget != ConnectionTarget.Editor) + return true; + + PlayModeView mainGameWindow = PlayModeView.GetMainPlayModeView(); + List allGameWindows = PlayModeView.GetAllPlayModeViewWindows(); + if (mainGameWindow || allGameWindows.Count > 0) { - PlayModeView playModeView = PlayModeView.GetMainPlayModeView(); - if (playModeView) - playModeView.ShowTab(); + PlayModeView gameWindowToUse = mainGameWindow; + + // The Frame Debugger and Game Window can not be docked together in + // the panes list (tabs) as both need to be shown in the Editor. + bool isFDInTheSamePaneAsGameWindow = false; + DockArea da = m_Parent as DockArea; + if (da) + isFDInTheSamePaneAsGameWindow |= CheckIfFDIsDockedWithGameWindow(da, mainGameWindow); + + // If it's docked, check if there are other game windows available to use + if (isFDInTheSamePaneAsGameWindow && allGameWindows.Count > 1) + { + for (int i = 0; i < allGameWindows.Count; i++) + { + if (CheckIfFDIsDockedWithGameWindow(da, allGameWindows[i])) + continue; + + isFDInTheSamePaneAsGameWindow = false; + gameWindowToUse = allGameWindows[i]; + break; + } + } + + // When we can't enable the FD debugger, we display an error box informing the + // user to undock the Frame Debugger Window so it's not tabbed with the Game Window. + if (isFDInTheSamePaneAsGameWindow) + { + m_ShowTabbedErrorBox = true; + return false; + } + // Otherwise we show the Game Window + else + { + gameWindowToUse.ShowTab(); + m_HasOpenedPlaymodeView = true; + return true; + } } + return false; + } + + private void DrawEnabledFrameDebugger(bool repaint) + { + int oldLimit = FrameDebuggerUtility.limit; + FrameDebuggerEvent[] descs = FrameDebuggerUtility.GetFrameEvents(); + // captured frame event contents have changed, rebuild the tree data if (HasEventHashChanged) { @@ -319,13 +389,10 @@ private void EnableFrameDebugger() if (enablingLocally && !FrameDebuggerUtility.locallySupported) return; - // Make sure game view is visible when enabling frame debugger locally - if (FrameDebugger.IsLocalEnabled()) - { - PlayModeView playModeView = PlayModeView.GetMainPlayModeView(); - if (playModeView) - playModeView.ShowTab(); - } + m_ShowTabbedErrorBox = false; + m_HasOpenedPlaymodeView = false; + if (!OpenPlayModeView()) + return; // pause play mode if needed if (enablingLocally) @@ -342,7 +409,6 @@ private void EnableFrameDebugger() m_EnablingWaitCounter = 0; m_EventDetailsView.Reset(); - RepaintOnLimitChange(); } @@ -363,12 +429,13 @@ private void DisableFrameDebugger() m_EventDetailsView = null; } + m_HasOpenedPlaymodeView = false; FrameDebuggerStyles.OnDisable(); m_TreeViewState = null; m_TreeView = null; } - private void RepaintOnLimitChange() + internal void RepaintOnLimitChange() { m_RepaintFrames = k_NeedToRepaintFrames; RepaintAllNeededThings(); diff --git a/Editor/Mono/PerformanceTools/FrameDebuggerEventDetailsView.cs b/Editor/Mono/PerformanceTools/FrameDebuggerEventDetailsView.cs index 6a8cc31401..988bc13305 100644 --- a/Editor/Mono/PerformanceTools/FrameDebuggerEventDetailsView.cs +++ b/Editor/Mono/PerformanceTools/FrameDebuggerEventDetailsView.cs @@ -548,7 +548,11 @@ private void DrawEventMesh(float viewportWidth, float viewportHeight, float texW } EditorGUILayout.EndHorizontal(); - m_Preview?.OnPreviewGUI(previewRect, EditorStyles.helpBox); + var evt = Event.current; + if (FrameDebuggerHelper.IsHoveringRect(previewRect) || evt.type != EventType.ScrollWheel) + { + m_Preview?.OnPreviewGUI(previewRect, EditorStyles.helpBox); + } } private void DrawEventMeshBackground(float viewportWidth, float viewportHeight) diff --git a/Editor/Mono/PerformanceTools/FrameDebuggerEventDisplayData.cs b/Editor/Mono/PerformanceTools/FrameDebuggerEventDisplayData.cs index 345e746967..fb9441f8ba 100644 --- a/Editor/Mono/PerformanceTools/FrameDebuggerEventDisplayData.cs +++ b/Editor/Mono/PerformanceTools/FrameDebuggerEventDisplayData.cs @@ -408,6 +408,12 @@ private bool CreateShaderPropertyDisplayInfo(ShaderPropertyType dataType, int ar return true; } + private string GetArrayIndexString(int nameLength, int numOfValues, int currentIndex) + { + int numOfSpaces = nameLength + (FrameDebuggerHelper.CountDigits(numOfValues) - FrameDebuggerHelper.CountDigits(currentIndex)); + return $"{new string(' ', numOfSpaces)}[{currentIndex}]"; + } + private bool GetShaderPropertyData(ShaderPropertyType dataType, int arrayIndex, string name, ref ShaderPropertyCollection propertyTypeDisplayData, ref ShaderInfo shaderInfo, ref ShaderPropertyDisplayInfo data) { m_StringBuilder.Clear(); @@ -501,7 +507,13 @@ private bool GetShaderPropertyData(ShaderPropertyType dataType, int arrayIndex, m_StringBuilder.Clear(); data.m_FoldoutString = String.Format(propertyTypeDisplayData.m_Format, $"{name}[{numOfValues}]", stage, string.Empty, string.Empty, string.Empty, string.Empty); for (int k = arrayIndex; k < arrayIndex + numOfValues; k++) - m_StringBuilder.Append(String.Format(propertyTypeDisplayData.m_Format, $"[{k - arrayIndex}]", string.Empty, shaderInfo.m_Vectors[k].m_Value)); + { + int value = shaderInfo.m_Ints[k].m_Value; + m_StringBuilder.AppendFormat(propertyTypeDisplayData.m_Format, + GetArrayIndexString(name.Length, numOfValues, k - arrayIndex), + string.Empty, + value).AppendLine(); + } // Remove last linebreak if (m_StringBuilder.Length > 2) @@ -529,7 +541,13 @@ private bool GetShaderPropertyData(ShaderPropertyType dataType, int arrayIndex, data.m_FoldoutString = m_StringBuilder.AppendFormat(propertyTypeDisplayData.m_Format, $"{name}[{numOfValues}]", stage, string.Empty, string.Empty, string.Empty, string.Empty).ToString(); m_StringBuilder.Clear(); for (int k = arrayIndex; k < arrayIndex + numOfValues; k++) - m_StringBuilder.AppendFormat(propertyTypeDisplayData.m_Format, $"[{k - arrayIndex}]", string.Empty, shaderInfo.m_Floats[k].m_Value); + { + float value = shaderInfo.m_Floats[k].m_Value; + m_StringBuilder.AppendFormat(propertyTypeDisplayData.m_Format, + GetArrayIndexString(name.Length, numOfValues, k - arrayIndex), + string.Empty, + value).AppendLine(); + } // Remove last linebreak if (numOfValues > 1 && m_StringBuilder.Length > 2) @@ -557,9 +575,13 @@ private bool GetShaderPropertyData(ShaderPropertyType dataType, int arrayIndex, for (int k = arrayIndex; k < arrayIndex + numOfValues; k++) { Vector4 value = shaderInfo.m_Vectors[k].m_Value; - int numOfSpaces = name.Length + (FrameDebuggerHelper.CountDigits(numOfValues) - FrameDebuggerHelper.CountDigits(k - arrayIndex)); - m_StringBuilder.AppendFormat(propertyTypeDisplayData.m_Format, $"{new string(' ', numOfSpaces)}[{k - arrayIndex}]", string.Empty, value.x, value.y, value.z, value.w); - m_StringBuilder.AppendLine(); + m_StringBuilder.AppendFormat(propertyTypeDisplayData.m_Format, + GetArrayIndexString(name.Length, numOfValues, k - arrayIndex), + string.Empty, + value.x, + value.y, + value.z, + value.w).AppendLine(); } // Remove last linebreak @@ -597,13 +619,12 @@ private bool GetShaderPropertyData(ShaderPropertyType dataType, int arrayIndex, for (int k = arrayIndex; k < arrayIndex + numOfValues; k++) { Matrix4x4 value = shaderInfo.m_Matrices[k].m_Value; - int numOfSpaces = name.Length + (FrameDebuggerHelper.CountDigits(numOfValues) - FrameDebuggerHelper.CountDigits(k - arrayIndex)); m_StringBuilder.AppendFormat(propertyTypeDisplayData.m_Format, - $"{new string(' ', numOfSpaces)}[{k - arrayIndex}]", string.Empty, value.m00, value.m01, value.m02, value.m03, - string.Empty, string.Empty, value.m10, value.m11, value.m12, value.m13, - string.Empty, string.Empty, value.m20, value.m21, value.m22, value.m23, - string.Empty, string.Empty, value.m30, value.m31, value.m32, value.m33); - m_StringBuilder.AppendLine(); + GetArrayIndexString(name.Length, numOfValues, k - arrayIndex), + string.Empty, value.m00, value.m01, value.m02, value.m03, + string.Empty, string.Empty, value.m10, value.m11, value.m12, value.m13, + string.Empty, string.Empty, value.m20, value.m21, value.m22, value.m23, + string.Empty, string.Empty, value.m30, value.m31, value.m32, value.m33).AppendLine(); } // Remove last linebreak diff --git a/Editor/Mono/PerformanceTools/FrameDebuggerHelper.cs b/Editor/Mono/PerformanceTools/FrameDebuggerHelper.cs index 84ba201ea1..e724455b33 100644 --- a/Editor/Mono/PerformanceTools/FrameDebuggerHelper.cs +++ b/Editor/Mono/PerformanceTools/FrameDebuggerHelper.cs @@ -41,6 +41,7 @@ internal static Material frameDebuggerMaterial internal static bool IsAHierarchyLevelBreakEvent(FrameEventType eventType) => eventType == FrameEventType.HierarchyLevelBreak; internal static bool IsCurrentEventMouseDown() => Event.current.type == EventType.MouseDown; internal static bool IsClickingRect(Rect rect) => rect.Contains(Event.current.mousePosition) && Event.current.type == EventType.MouseDown; + internal static bool IsHoveringRect(Rect rect) => rect.Contains(Event.current.mousePosition); internal static bool IsARenderTexture(ref Texture t) => t != null && (t as RenderTexture) != null; internal static bool IsADepthTexture(ref Texture t) => IsARenderTexture(ref t) && ((t as RenderTexture).graphicsFormat == GraphicsFormat.None); @@ -129,7 +130,7 @@ private static void SetMaterialProperties( frameDebuggerMaterial.EnableKeyword(ShaderPropertyIDs._TEX2DARRAY); else if (samplerType == TextureDimension.CubeArray) frameDebuggerMaterial.EnableKeyword(ShaderPropertyIDs._CUBEMAP); - + mat.DisableKeyword(ShaderPropertyIDs._MSAA_2); mat.DisableKeyword(ShaderPropertyIDs._MSAA_4); mat.DisableKeyword(ShaderPropertyIDs._MSAA_8); diff --git a/Editor/Mono/PerformanceTools/FrameDebuggerStyles.cs b/Editor/Mono/PerformanceTools/FrameDebuggerStyles.cs index c8ae23f7de..fd63a8f6c2 100644 --- a/Editor/Mono/PerformanceTools/FrameDebuggerStyles.cs +++ b/Editor/Mono/PerformanceTools/FrameDebuggerStyles.cs @@ -58,8 +58,13 @@ internal struct Window // Tree internal struct Tree { - internal static readonly GUIStyle s_RowText = "OL Label"; - internal static readonly GUIStyle s_RowTextRight = "OL RightLabel"; + internal static readonly GUIStyle s_RowText = new GUIStyle(EditorStyles.label); + internal static readonly GUIStyle s_RowTextBold = new GUIStyle(EditorStyles.boldLabel); + internal static readonly GUIStyle s_RowTextRight = new GUIStyle(EditorStyles.boldLabel) + { + alignment = TextAnchor.MiddleRight + }; + internal const string k_UnknownScopeString = ""; } @@ -162,7 +167,7 @@ internal struct EventDetails internal static readonly GUIStyle s_ArrayFoldoutStyle = new GUIStyle(EditorStyles.foldout) { - margin = new RectOffset(-30, 0, 0, 0), + margin = new RectOffset(-29, 0, 0, 0), }; internal static readonly GUIStyle s_TitleHorizontalStyle = new GUIStyle(EditorStyles.label) @@ -225,6 +230,7 @@ internal struct EventDetails internal const string k_WarningMultiThreadedMsg = "The Frame Debugger requires multi-threaded renderer. If this error persists, try starting the Editor with -force-gfx-mt command line argument."; internal const string k_WarningLinuxOpenGLMsg = k_WarningMultiThreadedMsg + " On Linux, the editor does not support a multi-threaded renderer when using OpenGL."; internal const string k_DescriptionString = "Frame Debugger lets you step through draw calls and see how exactly frame is rendered. Click Enable!"; + internal const string k_TabbedWithPlaymodeErrorString = "Frame Debugger can not be docked with the Game Window when trying to debug the editor."; internal static readonly GUIContent s_RenderTargetText = EditorGUIUtility.TrTextContent("RenderTarget"); internal static readonly GUIContent s_CopyEventText = EditorGUIUtility.TrTextContent("Copy Event Info"); internal static readonly GUIContent s_CopyPropertyText = EditorGUIUtility.TrTextContent("Copy Property"); diff --git a/Editor/Mono/PerformanceTools/FrameDebuggerToolbarView.cs b/Editor/Mono/PerformanceTools/FrameDebuggerToolbarView.cs index fbd9e1f767..eea124a760 100644 --- a/Editor/Mono/PerformanceTools/FrameDebuggerToolbarView.cs +++ b/Editor/Mono/PerformanceTools/FrameDebuggerToolbarView.cs @@ -68,8 +68,7 @@ private void DrawConnectionDropdown(FrameDebuggerWindow frameDebuggerWindow, ICo if (isEnabled && ProfilerDriver.connectedProfiler != FrameDebuggerUtility.GetRemotePlayerGUID()) { // Switch from local to remote debugger or vice versa - FrameDebuggerUtility.SetEnabled(false, FrameDebuggerUtility.GetRemotePlayerGUID()); - FrameDebuggerUtility.SetEnabled(true, ProfilerDriver.connectedProfiler); + frameDebuggerWindow.OnConnectedProfilerChange(); } } diff --git a/Editor/Mono/PerformanceTools/FrameDebuggerTreeView.cs b/Editor/Mono/PerformanceTools/FrameDebuggerTreeView.cs index ea8b1c1574..088d282808 100644 --- a/Editor/Mono/PerformanceTools/FrameDebuggerTreeView.cs +++ b/Editor/Mono/PerformanceTools/FrameDebuggerTreeView.cs @@ -77,13 +77,9 @@ private void PingFrameEventObject(int selectedID) public void ReselectFrameEventIndex() { - int[] selection = m_TreeView.GetSelection(); - if (selection.Length > 0) - { - FrameDebuggerTreeViewItem item = m_TreeView.FindItem(selection[0]) as FrameDebuggerTreeViewItem; - if (item != null) - m_TreeView.SetSelection(new[] { item.m_EventIndex }, true); - } + FrameDebuggerTreeViewItem item = GetSelectedTreeViewItem(); + if (item != null) + SetSelection(item.m_EventIndex); } public void SelectFrameEventIndex(int eventIndex) @@ -92,14 +88,24 @@ public void SelectFrameEventIndex(int eventIndex) // different tree nodes could result in the same frame debugger event // limit, e.g. a hierarchy node sets last child event as the limit. // If the limit event is the same, then do not change the currently selected item. + FrameDebuggerTreeViewItem item = GetSelectedTreeViewItem(); + if (item == null || item.m_EventIndex != eventIndex) + SetSelection(eventIndex); + } + + private FrameDebuggerTreeViewItem GetSelectedTreeViewItem() + { int[] selection = m_TreeView.GetSelection(); if (selection.Length > 0) - { - FrameDebuggerTreeViewItem item = m_TreeView.FindItem(selection[0]) as FrameDebuggerTreeViewItem; - if (item != null && eventIndex == item.m_EventIndex) - return; - } + return m_TreeView.FindItem(selection[0]) as FrameDebuggerTreeViewItem; + + return null; + } + + private void SetSelection(int eventIndex) + { m_TreeView.SetSelection(new[] { eventIndex }, true); + m_FrameDebugger.RepaintOnLimitChange(); } public void DrawTree(Rect rect) @@ -148,17 +154,15 @@ protected override void OnContentGUI(Rect rect, int row, TreeViewItem itemRaw, s return; FrameDebuggerTreeViewItem item = (FrameDebuggerTreeViewItem)itemRaw; - string text; GUIContent tempContent; GUIStyle style; bool isParent = (item.hasChildren); - FontStyle fontStyle = (isParent) ? FontStyle.Bold : FontStyle.Normal; childCounter = (isParent) ? 1 : (childCounter + 1); // Draw background - style = FrameDebuggerStyles.Tree.s_RowText; tempContent = EditorGUIUtility.TempContent(""); + style = FrameDebuggerStyles.Tree.s_RowText; style.Draw(rect, tempContent, false, false, false, false); // indent @@ -169,30 +173,25 @@ protected override void OnContentGUI(Rect rect, int row, TreeViewItem itemRaw, s // child event count if (isParent) { - text = item.m_ChildEventCount.ToString(CultureInfo.InvariantCulture); - tempContent = EditorGUIUtility.TempContent(text); - - style = FrameDebuggerStyles.Tree.s_RowTextRight; - style.fontStyle = fontStyle; + tempContent = EditorGUIUtility.TempContent(item.m_ChildEventCount.ToString(CultureInfo.InvariantCulture)); Rect r = rect; r.width -= kSmallMargin; + style = FrameDebuggerStyles.Tree.s_RowTextRight; style.Draw(r, tempContent, false, false, false, false); // reduce width of available space for the name, so that it does not overlap event count rect.width -= style.CalcSize(tempContent).x + kSmallMargin * 2; } - style = FrameDebuggerStyles.Tree.s_RowText; - style.fontStyle = fontStyle; // draw event name - text = item.displayName; - - if (string.IsNullOrEmpty(text)) - text = FrameDebuggerStyles.Tree.k_UnknownScopeString; + if (string.IsNullOrEmpty(item.displayName)) + tempContent = EditorGUIUtility.TempContent(FrameDebuggerStyles.Tree.k_UnknownScopeString); + else + tempContent = EditorGUIUtility.TempContent(item.displayName); - tempContent = EditorGUIUtility.TempContent(text); + style = isParent ? FrameDebuggerStyles.Tree.s_RowTextBold : FrameDebuggerStyles.Tree.s_RowText; style.Draw(rect, tempContent, false, false, false, selected && focused); } diff --git a/Editor/Mono/PlayModeView/PlayModeView.cs b/Editor/Mono/PlayModeView/PlayModeView.cs index 274410ab0a..a03f8ed159 100644 --- a/Editor/Mono/PlayModeView/PlayModeView.cs +++ b/Editor/Mono/PlayModeView/PlayModeView.cs @@ -10,6 +10,7 @@ using UnityEditor.Modules; using UnityEditorInternal; using UnityEngine; +using UnityEngine.Bindings; using UnityEngine.Experimental.Rendering; using UnityEngine.Scripting; @@ -46,11 +47,9 @@ 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; + + protected bool m_SwitchingPlayModeViewType = false; private const int k_MaxSupportedDisplays = 8; @@ -143,18 +142,6 @@ public EnterPlayModeBehavior enterPlayModeBehavior 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 { @@ -162,21 +149,6 @@ 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; } @@ -199,6 +171,14 @@ public static bool openWindowOnEnteringPlayMode RenderTexture m_TargetTexture; ColorSpace m_CurrentColorSpace = ColorSpace.Uninitialized; + [FreeFunction] + extern protected static bool NeedToPerformRendering(); + + protected static bool renderViewCallNeededInOnGUI => + !EditorApplication.isPlaying || + (EditorApplication.isPlaying && NeedToPerformRendering() && !Unsupported.IsEditorPlayerLoopWaiting()); + + class RenderingView : IDisposable { bool disposed = false; @@ -241,11 +221,15 @@ protected RenderTexture RenderView(Vector2 mousePosition, bool clearTexture) using (var renderingView = new RenderingView(this)) { SetPlayModeViewSize(targetSize); - int width = Mathf.RoundToInt(targetSize.x); - int height = Mathf.RoundToInt(targetSize.y); - // This should be called configure virtual display or sth - EditorDisplayUtility.AddVirtualDisplay(targetDisplay, width, height); - // EditorDisplayManager.UpdateVirtualDisplay(this); + + // The target size and GUI rects are communicated to C++ as floats throughout the engine. This means the final size in pixels + // of the view will be calculated in C++ so we have to ensure we use the C++ float rounding conventions here. Failure to do so + // may result in subtly differences and subtle bugs (like the Screen class thinking something sized 640.5 is 641 pixels but the + // RenderTexture being allocated here only being 640 pixels in size with standard C# rounding used by Mathf.RoundToInt. + // So we use the c++ AwayFromZero convention when rounding here. + int width = (int)Math.Round(targetSize.x, MidpointRounding.AwayFromZero); + int height = (int)Math.Round(targetSize.y, MidpointRounding.AwayFromZero); + var currentTargetDisplay = 0; if (ModuleManager.ShouldShowMultiDisplayOption()) { @@ -308,8 +292,6 @@ protected internal 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 @@ -345,6 +327,11 @@ protected internal void SwapMainWindow(Type type) if (m_Parent is DockArea dockAreaParent) { + // Mark that this view is the one switching out so when we do the tab management we don't have + // the old tab rewrite the desired size when GameView.OnBackgroundResized() is called during + // SplitView.Reflow(). + m_SwitchingPlayModeViewType = true; + dockAreaParent.AddTab(window); dockAreaParent.RemoveTab(this); DestroyImmediate(this, true); @@ -502,9 +489,6 @@ protected void SetVSync(bool enable) protected void SetFocus(bool focused) { - if (suppressRenderingForFullscreen) - return; //suppressed views should not grab "play mode" focus - if (!focused && s_LastFocused == this) { InternalEditorUtility.OnGameViewFocus(false); @@ -522,21 +506,6 @@ protected void SetFocus(bool focused) 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) { @@ -556,55 +525,32 @@ public enum EnterPlayModeBehavior { PlayFocused, PlayMaximized, - PlayUnfocused, - PlayFullscreen + PlayUnfocused } void SetPlayModeWindowsStates(EnterPlayModeBehavior behavior) { - var isFullscreen = (behavior == EnterPlayModeBehavior.PlayFullscreen); + if(m_EnterPlayModeBehavior == behavior) + return; - this.m_EnterPlayModeBehavior = behavior; - this.fullscreenMonitorIdx = isFullscreen - ? GameViewOnPlayMenu.SelectedIndexToDisplayIndex(this.playModeBehaviorIdx) - : -1; + m_EnterPlayModeBehavior = behavior; + OnEnterPlayModeBehaviorChange(); + + if (behavior != EnterPlayModeBehavior.PlayMaximized) + return; foreach (var view in s_PlayModeViews) { - if (view == this) - { + if (view == this || view.m_EnterPlayModeBehavior != EnterPlayModeBehavior.PlayMaximized) 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; - } - } + // Only one play mode view can be maximized at a time. + view.m_EnterPlayModeBehavior = EnterPlayModeBehavior.PlayUnfocused; 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 68110ea3ba..5ab8baa7d0 100644 --- a/Editor/Mono/PlayerSettings.bindings.cs +++ b/Editor/Mono/PlayerSettings.bindings.cs @@ -46,6 +46,13 @@ public enum Il2CppCompilerConfiguration Master = 2, } + // Must be in sync with Il2CppStacktraceInformation enum in SerializationMetaFlags.h + public enum Il2CppStacktraceInformation + { + MethodOnly = 0, + MethodFileLineNumber = 1, + } + // Mac fullscreen mode public enum MacFullscreenMode { @@ -215,7 +222,53 @@ public enum SplashScreenStyle public enum GraphicsJobMode { Native = 0, - Legacy = 1 + Legacy = 1, + Split + } + + // Must be in sync with GfxThreadingMode enum in GfxDeviceTypes.h + public enum GfxThreadingMode + { + // Direct threading mode. + // Main thread writes native graphics commands and submits them directly. + Direct = 0, + + // SingleThreaded mode. + // Main thread writes Unity graphics commands that it later reads and converts to native graphics commands. + NonThreaded = 1, + + // MultiThreaded mode. + // Main thread writes Unity graphics commands. A Render thread reads Unity graphics commands and converts them to native graphics commands. + Threaded = 2, + + // Legacy Graphics Jobs ("Jobified Rendering"). + // Main thread writes Unity graphics commands and starts worker threads to do the same. + // A Render thread reads Unity graphics commands and converts them to native graphics commands. + ClientWorkerJobs = 3, + + // Native Graphics Jobs. + // Main thread writes Unity graphics commands. + // Render thread reads Unity graphics commands and converts them to native graphics commands. + // The render thread also starts worker threads to write native graphics commands. + ClientWorkerNativeJobs = 4, + + // Native Graphics Jobs without Render Thread. + // Main thread writes native graphics commands, starts worker threads to do the same, and submits them directly. + DirectNativeJobs = 5, + + // Split Graphics Jobs. + // Main thread starts worker threads to write Unity graphics commands. + // Render thread reads Unity graphics commands converts them to native graphics commands. + // The render thread also starts worker threads to write native graphics commands. + SplitJobs = 6 + } + + // Must be in sync with MeshDeformation enum in GfxDeviceTypes.h + public enum MeshDeformation + { + CPU = 0, + GPU = 1, + GPUBatched = 2 } // Must be in sync with IconKind enum in EditorOnlyPlayerSettings.h @@ -258,7 +311,7 @@ public enum NormalMapEncoding DXT5nm = 1 } - internal enum TextureCompressionFormat + public enum TextureCompressionFormat { Unknown = 0, ETC = 1, @@ -539,6 +592,9 @@ public static Guid productGUID // Enable receipt validation for the Mac App Store. public static extern bool useMacAppStoreValidation { get; set; } + // Enable advanced optimiztions for Dedicated Server builds + public static extern bool dedicatedServerOptimizations { get; set; } + // Define how to handle fullscreen mode in Mac OS X standalones [Obsolete("macFullscreenMode is deprecated, use fullScreenMode instead")] [StaticAccessor("PlayerSettingsBindings", StaticAccessorType.DoubleColon)] @@ -593,10 +649,13 @@ public static bool singlePassStereoRendering public static extern bool enableFrameTimingStats { get; set; } public static extern bool enableOpenGLProfilerGPURecorders { get; set; } + public static extern bool allowHDRDisplaySupport { get; set; } public static extern bool useHDRDisplay { get; set; } - public static extern D3DHDRDisplayBitDepth D3DHDRBitDepth { get; set; } + [Obsolete("D3DHDRBitDepth has been replaced by hdrBitDepth. (UnityUpgradable) -> hdrBitDepth", true)] + public static extern D3DHDRDisplayBitDepth D3DHDRBitDepth { [NativeName("GetHDRBitDepthForObseleteEnum")] get; [NativeName("SetHDRBitDepthForObseleteEnum")] set; } + public static extern HDRDisplayBitDepth hdrBitDepth { get; set; } // What happens with the fullscreen Window when it runs in the background @@ -726,6 +785,31 @@ internal static string GetPlatformName(BuildTargetGroup targetGroup) [StaticAccessor("PlayerSettingsBindings", StaticAccessorType.DoubleColon)] internal static extern void SetBatchingForPlatform(BuildTarget platform, int staticBatching, int dynamicBatching); + public static bool GetStaticBatchingForPlatform(BuildTarget platform) + { + PlayerSettings.GetBatchingForPlatform(platform, out var staticBatching, out _); + return staticBatching > 0; + } + + public static void SetStaticBatchingForPlatform(BuildTarget platform, bool enable) + { + PlayerSettings.GetBatchingForPlatform(platform, out _, out var dynamicBatching); + PlayerSettings.SetBatchingForPlatform(platform, enable == true ? 1 : 0, dynamicBatching); + } + + public static bool GetDynamicBatchingForPlatform(BuildTarget platform) + { + + PlayerSettings.GetBatchingForPlatform(platform, out _, out var dynamicBatching); + return dynamicBatching > 0; + } + + public static void SetDynamicBatchingForPlatform(BuildTarget platform, bool enable) + { + PlayerSettings.GetBatchingForPlatform(platform, out var staticBatching, out _); + PlayerSettings.SetBatchingForPlatform(platform, staticBatching, enable == true ? 1 : 0); + } + [StaticAccessor("PlayerSettingsBindings", StaticAccessorType.DoubleColon)] public static extern int GetShaderChunkSizeInMBForPlatform(BuildTarget buildTarget); @@ -1111,6 +1195,20 @@ public static void SetIl2CppCompilerConfiguration(NamedBuildTarget buildTarget, SetIl2CppCompilerConfigurationInternal(buildTarget.TargetName, configuration); } + [NativeThrows] + [StaticAccessor("GetPlayerSettings().GetEditorOnly()")] + [NativeMethod("GetIl2CppStacktraceInformation")] + private static extern Il2CppStacktraceInformation GetIl2CppStacktraceInformationInternal(string buildTargetName); + public static Il2CppStacktraceInformation GetIl2CppStacktraceInformation(NamedBuildTarget buildTarget) => + GetIl2CppStacktraceInformationInternal(buildTarget.TargetName); + + [NativeThrows] + [StaticAccessor("GetPlayerSettings().GetEditorOnlyForUpdate()")] + [NativeMethod("SetIl2CppStacktraceInformation")] + private static extern void SetIl2CppStacktraceInformationInternal(string buildTargetName, Il2CppStacktraceInformation option); + public static void SetIl2CppStacktraceInformation(NamedBuildTarget buildTarget, Il2CppStacktraceInformation option) => + SetIl2CppStacktraceInformationInternal(buildTarget.TargetName, option); + [NativeThrows] [StaticAccessor("GetPlayerSettings().GetEditorOnly()")] [NativeMethod("GetPlatformIncrementalIl2CppBuild")] @@ -1422,6 +1520,9 @@ public static UInt32 xboxSpeechDB [NativeProperty("GPUSkinning")] public static extern bool gpuSkinning { get; set; } + [NativeProperty("MeshDeformation")] + public static extern MeshDeformation meshDeformation { get; set; } + public static bool graphicsJobs { get { return GetGraphicsJobsForPlatform(EditorUserBuildSettings.activeBuildTarget); } @@ -1446,6 +1547,12 @@ public static GraphicsJobMode graphicsJobMode [StaticAccessor("PlayerSettingsBindings", StaticAccessorType.DoubleColon)] internal static extern void SetGraphicsJobModeForPlatform(BuildTarget platform, GraphicsJobMode gfxJobMode); + [StaticAccessor("PlayerSettingsBindings", StaticAccessorType.DoubleColon)] + internal static extern GfxThreadingMode GetGraphicsThreadingModeForPlatform(BuildTarget platform); + + [StaticAccessor("PlayerSettingsBindings", StaticAccessorType.DoubleColon)] + internal static extern void SetGraphicsThreadingModeForPlatform(BuildTarget platform, GfxThreadingMode gfxJobMode); + [StaticAccessor("GetPlayerSettings()")] public static extern bool GetWsaHolographicRemotingEnabled(); @@ -1680,6 +1787,14 @@ public static WindowsGamepadBackendHint windowsGamepadBackendHint [StaticAccessor("GetPlayerSettings().GetEditorOnlyForUpdate()")] internal static extern void SetDefaultTextureCompressionFormat(BuildTargetGroup platform, TextureCompressionFormat format); + [NativeMethod("GetTextureCompressionFormats")] + [StaticAccessor("GetPlayerSettings().GetEditorOnly()")] + internal static extern TextureCompressionFormat[] GetTextureCompressionFormatsImpl(BuildTargetGroup platform); + + [NativeMethod("SetTextureCompressionFormats")] + [StaticAccessor("GetPlayerSettings().GetEditorOnlyForUpdate()")] + internal static extern void SetTextureCompressionFormatsImpl(BuildTargetGroup platform, TextureCompressionFormat[] formats); + [FreeFunction("GetPlayerSettings().GetEditorOnly().RecompileScripts")] internal static extern void RecompileScripts(string reason, bool refreshProject = true); diff --git a/Editor/Mono/PlayerSettings.deprecated.cs b/Editor/Mono/PlayerSettings.deprecated.cs index 3c7deb6721..623ede4a10 100644 --- a/Editor/Mono/PlayerSettings.deprecated.cs +++ b/Editor/Mono/PlayerSettings.deprecated.cs @@ -4,6 +4,7 @@ using System; using UnityEngine; +using UnityEngine.Bindings; using EditorGraphicsSettings = UnityEditor.Rendering.EditorGraphicsSettings; @@ -167,6 +168,15 @@ public static AndroidTargetDevice targetDevice [Obsolete("minifyWithR8 is obsolete and has no effect anymore, since Android Gradle Plugin 7.0 always uses R8", false)] public static bool minifyWithR8 { get { return true; } set {} } + + [Obsolete("Renamed to match UI. Please use splitApplicationBinary instead. (UnityUpgradable) -> splitApplicationBinary", false)] + public static extern bool useAPKExpansionFiles + { + [NativeMethod("GetAndroidSplitApplicationBinary")] + get; + [NativeMethod("SetAndroidSplitApplicationBinary")] + set; + } } partial class iOS diff --git a/Editor/Mono/PlayerSettingsAndroid.bindings.cs b/Editor/Mono/PlayerSettingsAndroid.bindings.cs index 5c3fe0fdda..2b84a0fb8c 100644 --- a/Editor/Mono/PlayerSettingsAndroid.bindings.cs +++ b/Editor/Mono/PlayerSettingsAndroid.bindings.cs @@ -184,6 +184,20 @@ internal struct AndroidBanner public Texture2D banner; } + [Flags] + public enum AndroidApplicationEntry : uint + { + /// + /// Include entry which derives from Activity + /// - Activity https://developer.android.com/reference/android/app/Activity + /// + Activity = 1 << 0, + /// + /// Include entry which derives from Game Activity https://developer.android.com/games/agdk/game-activity + /// + GameActivity = 1 << 1 + } + // Player Settings is where you define various parameters for the final game that you will build in Unity. Some of these values are used in the Resolution Dialog that launches when you open a standalone game. public partial class PlayerSettings : UnityEngine.Object { @@ -452,9 +466,13 @@ public static extern bool licenseVerification [NativeMethod("GetAndroidLicenseVerification")] get; } - - // Use APK Expansion Files - public static extern bool useAPKExpansionFiles { get; set; } + public static extern bool splitApplicationBinary + { + [NativeMethod("GetAndroidSplitApplicationBinary")] + get; + [NativeMethod("SetAndroidSplitApplicationBinary")] + set; + } // Application should show ActivityIndicator when loading public static extern AndroidShowActivityIndicatorOnLoading showActivityIndicatorOnLoading @@ -558,9 +576,40 @@ public static extern bool optimizedFramePacing set; } + public static TextureCompressionFormat[] textureCompressionFormats + { + get + { + return GetTextureCompressionFormatsImpl(BuildTargetGroup.Android); + } + set + { + if (value == null || value.Length == 0) + { + throw new ArgumentException($"Android textureCompressionFormats can't be null or empty"); + } + foreach (var format in value) + { + if (format == TextureCompressionFormat.Unknown || format == TextureCompressionFormat.BPTC) + { + throw new ArgumentException($"{format} can't be used as a target texture compression for Android"); + } + } + SetTextureCompressionFormatsImpl(BuildTargetGroup.Android, value); + } + } + // Google Play App Dependencies info. [NativeProperty("AndroidReportGooglePlayAppDependencies", TargetType.Function)] public static extern bool reportGooglePlayAppDependencies { get; set; } + + public static extern AndroidApplicationEntry applicationEntry + { + [NativeMethod("GetAndroidApplicationEntry")] + get; + [NativeMethod("SetAndroidApplicationEntry")] + set; + } } } } diff --git a/Editor/Mono/PlayerSettingsDefaultTextureCompressionHandler.cs b/Editor/Mono/PlayerSettingsDefaultTextureCompressionHandler.cs new file mode 100644 index 0000000000..275e78d8de --- /dev/null +++ b/Editor/Mono/PlayerSettingsDefaultTextureCompressionHandler.cs @@ -0,0 +1,319 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditorInternal; +using UnityEngine; + + +namespace UnityEditor +{ + internal abstract class PlayerSettingsDefaultTextureCompressionHandler + { + private SerializedProperty m_BuildTargetDefaultTextureCompressionFormat; + private SerializedObject m_SerializedObject; + private SerializedProperty m_TextureArraySerializedProperty; + private GUIContent m_TextureCompressionFormatsGUI; + + private TextureCompressionFormat[] m_LastSavedFormat; + private List m_StoredTextureCompressionList; + private bool m_IsDirty = false; + private bool m_ToBeUpdated = true; + private ReorderableList m_TextureCompressionsList; + private Func m_AvailableTextureCompressionsProvider; + private Func m_IsPreset; + private Action m_EditorSerialization; + + private const string k_BuildTargetKey = "m_BuildTarget"; + private const string k_FormatsKey = "m_Formats"; + private const string k_BuildTargetDefaultTextureCompressionFormatName = "m_BuildTargetDefaultTextureCompressionFormat"; + + protected abstract string PlatformName { get; } + + protected abstract TextureCompressionFormat[] CurrentPlatformGlobalEditorTextureFormat { get; set; } + + protected abstract string ToString(TextureCompressionFormat textureCompression); + + private ReorderableList TextureCompressionsList + { + get + { + if (m_TextureCompressionsList == null) + { + var textureCompressionsList = GetStoredCompressionFormatOrDefaultOne(); + m_TextureCompressionsList = new ReorderableList(textureCompressionsList, typeof(TextureCompressionFormat), true, true, true, true) + { + onCanRemoveCallback = (list) => true, + onRemoveCallback = RemoveTextureCompressionElement, + onReorderCallback = ReorderTextureCompressionElement, + drawHeaderCallback = (rect) => GUI.Label(rect, m_TextureCompressionFormatsGUI, EditorStyles.label), + onAddDropdownCallback = AddTextureCompressionElement, + drawElementCallback = DrawTextureCompressionElement, + onChangedCallback = Onchanged, + elementHeight = 16 + }; + } + + return m_TextureCompressionsList; + } + } + + + private SerializedProperty TextureFormatArraySerializedProperty + { + get + { + if (m_TextureArraySerializedProperty == null) + { + foreach (SerializedProperty element in m_BuildTargetDefaultTextureCompressionFormat) + { + var buildTarget = element.FindPropertyRelative(k_BuildTargetKey).stringValue; + if (buildTarget == PlatformName) + { + m_TextureArraySerializedProperty = element.FindPropertyRelative(k_FormatsKey); + } + } + } + + return m_TextureArraySerializedProperty; + } + } + + public List StoredTextureCompressionList + { + get + { + if (m_StoredTextureCompressionList == null) + { + m_StoredTextureCompressionList = GetStoredCompressionFormatOrDefaultOne(); + } + + return m_StoredTextureCompressionList; + } + + private set { m_StoredTextureCompressionList = value; } + } + + protected bool IsGlobalProjectSetting + { + get { return !IsAPreset; } + } + + protected bool IsAPreset + { + get + { + if (m_IsPreset != null) + { + return m_IsPreset.Invoke(); + } + + return false; + } + } + + public void Reset() + { + m_TextureArraySerializedProperty = null; + m_TextureCompressionsList = null; + m_LastSavedFormat = null; + m_StoredTextureCompressionList = null; + m_IsDirty = false; + m_ToBeUpdated = true; + } + + public PlayerSettingsDefaultTextureCompressionHandler(Func availableTextureCompressionsProvider) + { + m_AvailableTextureCompressionsProvider = availableTextureCompressionsProvider; + } + + public PlayerSettingsDefaultTextureCompressionHandler SetupSerializedObject(SerializedObject serializedObject, string variableName = k_BuildTargetDefaultTextureCompressionFormatName) + { + m_SerializedObject = serializedObject; + m_BuildTargetDefaultTextureCompressionFormat = FindPropertyAssert(variableName); + return this; + } + + public PlayerSettingsDefaultTextureCompressionHandler SetEditorSerializationAction(Action editorSerialization) + { + m_EditorSerialization = editorSerialization; + return this; + } + + public PlayerSettingsDefaultTextureCompressionHandler SetIsPreset(Func isPreset) + { + m_IsPreset = isPreset; + return this; + } + + public PlayerSettingsDefaultTextureCompressionHandler SetGUIContent(GUIContent guiContent) + { + m_TextureCompressionFormatsGUI = guiContent; + return this; + } + + private SerializedProperty FindPropertyAssert(string name) + { + var property = m_SerializedObject.FindProperty(name); + if (property == null) + { + Debug.LogError($"Failed to find: {name}"); + } + + return property; + } + + public void RenderingSectionGUI() + { + EditorGUILayout.Space(); + var updated = false; + if (m_IsDirty) + { + updated = UpdateSerialization(); + } + + TextureCompressionsList.DoLayoutList(); + if (updated || m_ToBeUpdated) + { + var textureCompressions = GetStoredCompressionFormatOrDefaultOne(); + TextureCompressionsList.list = textureCompressions; + m_ToBeUpdated = false; + } + + EditorGUILayout.Space(); + } + + private List GetTextureCompressionFormatFromSerialization(SerializedProperty serializedProperty) + { + var formats = new List(); + if (serializedProperty != null) + { + var arraysize = serializedProperty.arraySize; + for (var i = 0; i < arraysize; i++) + { + var format = (TextureCompressionFormat)serializedProperty.GetArrayElementAtIndex(i).intValue; + formats.Add(format); + } + } + + return formats; + } + + private List GetStoredCompressionFormatOrDefaultOne() + { + var formats = GetTextureCompressionFormatFromSerialization(TextureFormatArraySerializedProperty); + + if (formats.Count > 0) + { + return formats; + } + + if (CurrentPlatformGlobalEditorTextureFormat != null) + { + return CurrentPlatformGlobalEditorTextureFormat.ToList(); + } + + return new List(); + } + + private bool UpdateTextureCompressionFormatSerialization(SerializedProperty serializedProperty, TextureCompressionFormat[] formats) + { + var same = m_LastSavedFormat != null && m_LastSavedFormat.Length == formats.Length && m_LastSavedFormat.SequenceEqual(formats); + if (same) return false; + m_LastSavedFormat = formats; + var intValues = Array.ConvertAll(formats, format => (int)format); + serializedProperty.SetValue(SerializedPropertyExtensions.Setter, intValues); + + return true; + } + + private bool UpdateSerialization() + { + var updated = UpdateTextureCompressionFormatSerialization(TextureFormatArraySerializedProperty, StoredTextureCompressionList.ToArray()); + if (updated) + { + m_BuildTargetDefaultTextureCompressionFormat.serializedObject.ApplyModifiedProperties(); + if (IsGlobalProjectSetting) + { + CurrentPlatformGlobalEditorTextureFormat = StoredTextureCompressionList.ToArray(); + m_EditorSerialization?.Invoke(); + } + + m_SerializedObject.ApplyModifiedProperties(); + + TextureCompressionsList.list = StoredTextureCompressionList; + } + + m_IsDirty = false; + return updated; + } + + private void AddTextureCompressionElement(Rect rect, ReorderableList list) + { + var availableTextureCompressions = m_AvailableTextureCompressionsProvider(); + var names = availableTextureCompressions + .Select(m => ToString(m)) + .ToArray(); + var enabled = availableTextureCompressions + .Select(m => !list.list.Contains(m)) + .ToArray(); + + EditorUtility.DisplayCustomMenu(rect, names, enabled, null, AddTextureCompressionMenuSelected, availableTextureCompressions); + } + + private void AddTextureCompressionMenuSelected(object userData, string[] options, int selected) + { + var textureCompressions = (TextureCompressionFormat[])userData; + if (textureCompressions[selected] == TextureCompressionFormat.DXTC_RGTC) + { + StoredTextureCompressionList.Remove(TextureCompressionFormat.DXTC); + } + else if (textureCompressions[selected] == TextureCompressionFormat.DXTC) + { + StoredTextureCompressionList.Remove(TextureCompressionFormat.DXTC_RGTC); + } + + StoredTextureCompressionList.Add(textureCompressions[selected]); + m_IsDirty = true; + } + + private string GetTextureFormatAsAString(TextureCompressionFormat[] formats) + { + return String.Join(",", formats); + } + + private void Onchanged(ReorderableList list) + { + m_IsDirty = true; + } + + private void RemoveTextureCompressionElement(ReorderableList list) + { + // don't allow removing the last TextureCompression + if (StoredTextureCompressionList.Count < 2) + { + EditorApplication.Beep(); + return; + } + + StoredTextureCompressionList.RemoveAt(list.index); + m_IsDirty = true; + } + + private void ReorderTextureCompressionElement(ReorderableList list) + { + var textureCompressionsList = (List)list.list; + StoredTextureCompressionList = textureCompressionsList; + m_IsDirty = true; + } + + private void DrawTextureCompressionElement(Rect rect, int index, bool selected, bool focused) + { + var textureCompression = TextureCompressionsList.list[index]; + GUI.Label(rect, ToString((TextureCompressionFormat)textureCompression), EditorStyles.label); + } + } +} diff --git a/Editor/Mono/PlayerSettingsIOS.bindings.cs b/Editor/Mono/PlayerSettingsIOS.bindings.cs index 96a0f9896f..1f6f2abd08 100644 --- a/Editor/Mono/PlayerSettingsIOS.bindings.cs +++ b/Editor/Mono/PlayerSettingsIOS.bindings.cs @@ -159,7 +159,7 @@ internal partial class iOSDeviceRequirementGroup private string m_VariantName; [FreeFunction("PlayerSettingsIOSBindings::SetOrAddDeviceRequirementForVariantNameImpl")] - extern private static void SetOrAddDeviceRequirementForVariantNameImpl(string name, int index, string[] keys, string[] values); + extern private static void SetOrAddDeviceRequirementForVariantNameImpl(string name, int index, [Unmarshalled] string[] keys, [Unmarshalled] string[] values); [NativeMethod(Name = "GetIOSDeviceRequirementCountForVariantName")] [StaticAccessor("GetPlayerSettings()", StaticAccessorType.Dot)] @@ -362,7 +362,7 @@ public static UnityEngine.iOS.SystemGestureDeferMode deferSystemGesturesMode } [NativeProperty("HideHomeButton")] - public static bool hideHomeButton { get; set; } + public extern static bool hideHomeButton { get; set; } [NativeProperty("IOSAppInBackgroundBehavior")] private extern static int appInBackgroundBehaviorInternal diff --git a/Editor/Mono/PlayerSettingsSwitch.bindings.cs b/Editor/Mono/PlayerSettingsSwitch.bindings.cs index 8665319ec8..245d984cc2 100644 --- a/Editor/Mono/PlayerSettingsSwitch.bindings.cs +++ b/Editor/Mono/PlayerSettingsSwitch.bindings.cs @@ -30,26 +30,26 @@ public enum ScreenResolutionBehavior Both = 3 } - // These language names should be match to the name descriptions where in an NMETA file. + // These language names should be match to the name descriptions where in an NMETA file and SwitchBuildUtils.Languages. // And, please notice that you have to increase numSwitchLanguages in EditorOnlyPlayerSettings.h when you add a new language here. public enum Languages { - AmericanEnglish, - BritishEnglish, - Japanese, - French, - German, - LatinAmericanSpanish, - Spanish, - Italian, - Dutch, - CanadianFrench, - Portuguese, - Russian, - SimplifiedChinese, - TraditionalChinese, - Korean, - BrazilianPortuguese, + AmericanEnglish = 0, + BritishEnglish = 1, + Japanese = 2, + French = 3, + German = 4, + LatinAmericanSpanish = 5, + Spanish = 6, + Italian = 7, + Dutch = 8, + CanadianFrench = 9, + Portuguese = 10, + Russian = 11, + SimplifiedChinese = 12, + TraditionalChinese = 13, + Korean = 14, + BrazilianPortuguese = 15, } public enum @@ -60,13 +60,6 @@ public enum RequiredWithNetworkServiceAccountAvailable = 2 } - public enum TouchScreenUsage - { - Supported = 0, - Required = 1, - None = 2 - } - public enum LogoHandling { Auto = 0, @@ -139,6 +132,10 @@ public enum SupportedNpadStyle [NativeProperty("switchUseCPUProfiler", TargetType.Field)] extern public static bool useSwitchCPUProfiler { get; set; } + // Whether to enable file system trace on Nintendo Switch CPU Profiler. + [NativeProperty("switchEnableFileSystemTrace", TargetType.Field)] + extern public static bool enableFileSystemTrace { get; set; } + // What LTO setting to use on Switch. [NativeProperty("switchLTOSetting", TargetType.Field)] extern public static int switchLTOSetting { get; set; } @@ -274,6 +271,15 @@ extern public static int NVNGraphicsFirmwareMemory [StaticAccessor("PlayerSettings", StaticAccessorType.DoubleColon)] extern public static int maximumSwitchNVNGraphicsFirmwareMemory { get; } + + [StaticAccessor("GetPlayerSettings()", StaticAccessorType.Dot)] + extern public static int switchMaxWorkerMultiple + { + [NativeMethod("GetSwitchKMaxWorkerMultiple")] + get; + [NativeMethod("SetSwitchKMaxWorkerMultiple")] + set; + } // Controls the behavior of Switch's auto-changing screen resolution [NativeProperty("switchScreenResolutionBehavior", TargetType.Field)] @@ -469,9 +475,6 @@ extern public static string releaseVersion [NativeProperty("switchStartupUserAccount", TargetType.Field)] extern public static StartupUserAccount startupUserAccount { get; set; } - [NativeProperty("switchTouchScreenUsage", TargetType.Field)] - extern public static TouchScreenUsage touchScreenUsage { get; set; } - [NativeProperty("switchSupportedLanguagesMask", TargetType.Field)] extern public static int supportedLanguages { get; set; } @@ -583,6 +586,9 @@ extern public static int[] ratingAgeArray [NativeProperty("switchSupportedNpadCount", TargetType.Field)] extern public static int supportedNpadCount { get; set; } + [NativeProperty("switchEnableTouchScreen", TargetType.Field)] + extern public static bool enableTouchScreen { get; set; } + // SocketConfigEnabled [NativeProperty("switchSocketConfigEnabled", TargetType.Field)] extern public static bool socketConfigEnabled { get; set; } @@ -623,10 +629,6 @@ extern public static int[] ratingAgeArray [NativeProperty("switchNetworkInterfaceManagerInitializeEnabled", TargetType.Field)] extern public static bool networkInterfaceManagerInitializeEnabled { get; set; } - // Player Connection Enabled - [NativeProperty("switchPlayerConnectionEnabled", TargetType.Field)] - extern public static bool playerConnectionEnabled { get; set; } - // Using the new path style system [NativeProperty("switchUseNewStyleFilepaths", TargetType.Field)] extern public static bool useNewStyleFilepaths { get; set; } diff --git a/Editor/Mono/Prefabs/PrefabImporter.bindings.cs b/Editor/Mono/Prefabs/PrefabImporter.bindings.cs index 2b7c1373ba..ec01abdfe2 100644 --- a/Editor/Mono/Prefabs/PrefabImporter.bindings.cs +++ b/Editor/Mono/Prefabs/PrefabImporter.bindings.cs @@ -11,6 +11,7 @@ namespace UnityEditor { [NativeHeader("Editor/Src/AssetPipeline/PrefabImporter.h")] [ExcludeFromPreset] + [HelpURL("https://docs.unity3d.com/2023.1/Documentation/Manual/Prefabs.html")] internal class PrefabImporter : AssetImporter { } diff --git a/Editor/Mono/Prefabs/PrefabImporterEditor.cs b/Editor/Mono/Prefabs/PrefabImporterEditor.cs index 7919ffb1ab..8d78721584 100644 --- a/Editor/Mono/Prefabs/PrefabImporterEditor.cs +++ b/Editor/Mono/Prefabs/PrefabImporterEditor.cs @@ -2,10 +2,11 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License -using UnityEngine; +using System.Collections.Generic; using UnityEditor.AssetImporters; using UnityEditor.SceneManagement; -using System.Collections.Generic; +using UnityEditor.UIElements; +using UnityEngine; namespace UnityEditor { @@ -40,8 +41,9 @@ struct TrackedAsset public Hash128 hash; } - List m_TempComponentsResults = new List(); - List m_DirtyPrefabAssets = new List(); + readonly List m_TempComponentsResults = new List(); + readonly List m_DirtyPrefabAssets = new List(); + bool m_HasPendingChanges; public override bool showImportedObject { get { return !hasMissingScripts; } } @@ -53,7 +55,7 @@ bool isTextFieldCaretShowing get { return EditorGUI.IsEditingTextField() && !EditorGUIUtility.textFieldHasSelection; } } - bool readyToAutoSave + internal bool readyToAutoSave { get { return !m_SavingHasFailed && !hasMissingScripts && GUIUtility.hotControl == 0 && !isTextFieldCaretShowing && !EditorApplication.isCompiling; } } @@ -66,15 +68,51 @@ bool hasMissingScripts public override void OnEnable() { base.OnEnable(); - EditorApplication.update += Update; + ObjectChangeEvents.changesPublished += ObjectChangeEventPublished; } public override void OnDisable() { - EditorApplication.update -= Update; + if (m_HasPendingChanges) + { + EditorApplication.update -= WaitToApplyChanges; + m_HasPendingChanges = false; + SaveDirtyPrefabAssets(false); + } + + ObjectChangeEvents.changesPublished -= ObjectChangeEventPublished; base.OnDisable(); } + private void ObjectChangeEventPublished(ref ObjectChangeEventStream stream) + { + if (m_HasPendingChanges) + return; + + for (int i = 0; i < stream.length; ++i) + { + if (stream.GetEventType(i) == ObjectChangeKind.ChangeGameObjectOrComponentProperties) + { + stream.GetChangeGameObjectOrComponentPropertiesEvent(i, out var changeGameObjectOrComponent); + var asset = EditorUtility.InstanceIDToObject(changeGameObjectOrComponent.instanceId); + if (IsTargetAsset(asset)) + { + if (!EditorFocusMonitor.AreBindableElementsSelected() && readyToAutoSave) + { + SaveDirtyPrefabAssets(true); + } + else + { + m_HasPendingChanges = true; + EditorApplication.update += WaitToApplyChanges; + } + + return; + } + } + } + } + protected override void Awake() { base.Awake(); @@ -102,14 +140,14 @@ void OnDestroy() SaveDirtyPrefabAssets(false); } - void Update() + void WaitToApplyChanges() { var time = EditorApplication.timeSinceStartup; if (time > m_NextUpdate) { m_NextUpdate = time + 0.2; - if (readyToAutoSave && HasDirtyPrefabAssets()) + if (!EditorFocusMonitor.AreBindableElementsSelected() && readyToAutoSave) SaveDirtyPrefabAssets(true); } } @@ -190,22 +228,28 @@ internal void SaveDirtyPrefabAssets(bool reloadInspectors) } } } + + // We reset the flag after the changes have been applied in order to avoid a new change being detected from the save operation. + if (m_HasPendingChanges) + { + EditorApplication.update -= WaitToApplyChanges; + m_HasPendingChanges = false; + } } } } - internal bool HasDirtyPrefabAssets() + bool IsTargetAsset(Object obj) { - if (assetTarget == null) - return false; + if (obj is Component component) + obj = component.gameObject; - if (typeof(GameObject) != assetTarget.GetType()) - return false; - - // We just check one target since we assume that a multi-edit will - // always edit that target. So no need to spend resources on checking - // all targets in multiselection. - return IsDirty((GameObject)assetTarget); + foreach (var asset in assetTargets) + { + if (obj == asset) + return true; + } + return false; } bool IsDirty(GameObject prefabAssetRoot) diff --git a/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesWindow.cs b/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesWindow.cs index 8d3bca32e8..9737c4f6d7 100644 --- a/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesWindow.cs +++ b/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesWindow.cs @@ -20,6 +20,7 @@ internal class PrefabOverridesWindow : PopupWindowContent RectOffset k_TreeViewPadding = new RectOffset(0, 0, 4, 4); const float k_HeaderHeight = 60f; const float k_ButtonWidth = 120; + const float k_ButtonWidthVariant = 201; const float k_ButtonWidthPadding = 10; //Padding in case the text is long to give the window a bit of margins around the buttons const float k_HeaderLeftMargin = 6; const float k_NoOverridesLabelHeight = 26f; @@ -629,7 +630,7 @@ void UpdateText(Texture assetIcon, string assetName) if (stage is PrefabStage && PrefabUtility.IsPartOfVariantPrefab(AssetDatabase.LoadAssetAtPath(stage.assetPath))) { applyAllContent = Styles.applyAllToBaseContent; - m_ApplyButtonWidth = GUI.skin.button.CalcSize(applyAllContent).x; + m_ApplyButtonWidth = k_ButtonWidthVariant; applySelectedContent = Styles.applySelectedToBaseContent; } diff --git a/Editor/Mono/Prefabs/PrefabUtility.bindings.cs b/Editor/Mono/Prefabs/PrefabUtility.bindings.cs index 4fc0198edc..254c580324 100644 --- a/Editor/Mono/Prefabs/PrefabUtility.bindings.cs +++ b/Editor/Mono/Prefabs/PrefabUtility.bindings.cs @@ -20,6 +20,12 @@ namespace UnityEditor [NativeHeader("Editor/Mono/Prefabs/PrefabUtility.bindings.h")] public sealed partial class PrefabUtility { + [StaticAccessor("PrefabInstance", StaticAccessorType.DoubleColon)] + extern internal static int defaultOverridesCount { get; } + + [StaticAccessor("PrefabInstance", StaticAccessorType.DoubleColon)] + extern internal static int defaultOverridesCountUsingRectTransform { get; } + // Returns the corresponding GameObject/Component from /source/, or null if it can't be found. [StaticAccessor("PrefabUtilityBindings", StaticAccessorType.DoubleColon)] extern private static Object GetCorrespondingObjectFromSource_internal([NotNull] Object obj); @@ -103,9 +109,8 @@ public sealed partial class PrefabUtility extern public static bool HasPrefabInstanceAnyOverrides(GameObject instanceRoot, bool includeDefaultOverrides); // Instantiate an asset that is referenced by a prefab and use it on the prefab instance. - [FreeFunction] - [NativeHeader("Editor/Src/Prefabs/AttachedPrefabAsset.h")] - extern public static Object InstantiateAttachedAsset([NotNull("NullExceptionObject")] Object targetObject); + [Obsolete("InstantiateAttachedAsset is deprecated")] + public static Object InstantiateAttachedAsset(Object targetObject) { return null; } // Force record property modifications by comparing against the parent prefab. [FreeFunction] diff --git a/Editor/Mono/Prefabs/PrefabUtility.cs b/Editor/Mono/Prefabs/PrefabUtility.cs index 1611c44b47..116df8343a 100644 --- a/Editor/Mono/Prefabs/PrefabUtility.cs +++ b/Editor/Mono/Prefabs/PrefabUtility.cs @@ -570,8 +570,12 @@ InteractionMode action { if (property.prefabOverride && property.propertyType == SerializedPropertyType.ManagedReference) { - allowWarnAboutApplyingPartsOfManagedReferences = false; // we should always be allowed to apply a managed reference root - ApplySinglePropertyAndRemoveOverride(property, prefabSourceSerializedObject, prefabSourceObject, isObjectOnRootInAsset, false, allowWarnAboutApplyingPartsOfManagedReferences, allowApplyDefaultOverride, serializedObjects, changedObjects, action, out _); + bool skipRestOfProperties = false; + ApplySinglePropertyAndRemoveOverride(property, prefabSourceSerializedObject, prefabSourceObject, isObjectOnRootInAsset, false, allowWarnAboutApplyingPartsOfManagedReferences, allowApplyDefaultOverride, serializedObjects, changedObjects, action, out skipRestOfProperties); + if (skipRestOfProperties) + return; + + allowWarnAboutApplyingPartsOfManagedReferences = false; // The managed reference was applied to the Asset so do not check for sub properties } var visitedManagedReferenceProperties = new HashSet(); @@ -621,11 +625,11 @@ InteractionMode action } } } - + internal static PropertyValueOriginInfo GetPropertyValueOriginInfo(SerializedProperty property) - { + { if (property == null) - return new PropertyValueOriginInfo("The property is null"); + return new PropertyValueOriginInfo("The property is null"); if (property.prefabOverride) return new PropertyValueOriginInfo("The property value is overriden in the currently open object"); @@ -1004,6 +1008,29 @@ internal static void RevertPropertyOverride(SerializedProperty instanceProperty, RaiseRevertedEvents(instanceProperty.serializedObject); } + internal static bool IsPropertyBeingDrivenByPrefabStage(SerializedProperty property) + { + Object target = property.serializedObject.targetObject; + GameObject go = GetGameObject(target); + if (go != null && go.scene.IsValid() && EditorSceneManager.IsPreviewScene(go.scene)) + { + var prefabStage = SceneManagement.PrefabStageUtility.GetCurrentPrefabStage(); + if (prefabStage != null && prefabStage.mode == SceneManagement.PrefabStage.Mode.InContext) + { + var propertyPath = property.propertyPath; + ScriptableObject driver = prefabStage; + if ( + (DrivenPropertyManagerInternal.IsDriving(driver, target, propertyPath)) + || + ((target is Transform || target is ParticleSystem || property.propertyType == SerializedPropertyType.Color) && DrivenPropertyManagerInternal.IsDrivingPartial(driver, target, propertyPath))) + { + return true; + } + } + } + return false; + } + internal static void RaiseRevertingEvents(SerializedObject serializedObject) { foreach (Object targetObject in serializedObject.targetObjects) @@ -1411,11 +1438,20 @@ public static void ApplyRemovedComponent(GameObject instanceGameObject, Componen Debug.LogError($"No undo was registered when removing {assetComponent.name} from {assetRoot.name}. \nError: {errorMessage}", assetRoot); } + var prefabInstanceObject = PrefabUtility.GetPrefabInstanceHandle(instanceGameObject); + + if (action == InteractionMode.UserAction) + Undo.RegisterCompleteObjectUndo(prefabInstanceObject, actionName); + + // Having registered undo on the Prefab instance we can now apply the removed component override using (var scope = new EditPrefabContentsScope(assetPath)) { //Search components in file that matches the FileIds of componentInAsset - void DeleteCorrespondingComponent(Component componentInAsset) + bool DeleteCorrespondingComponent(Component componentInAsset) { + if (componentInAsset == null) + return false; + var assetComponentId = Unsupported.GetFileIDHint(componentInAsset); var componentsOfTypeInAsset = scope.prefabContentsRoot.GetComponentsInChildren(componentInAsset.GetType(), true); @@ -1424,17 +1460,23 @@ void DeleteCorrespondingComponent(Component componentInAsset) if (Unsupported.GetOrGenerateFileIDHint(component) == assetComponentId) { Object.DestroyImmediate(component); - return; + return true; } } Debug.LogError($"Component {componentInAsset} could not be found and deleted from corresponding asset."); + return false; } - DeleteCorrespondingComponent(assetComponent); - if (coupledAssetComponent != null) - DeleteCorrespondingComponent(coupledAssetComponent); + if (DeleteCorrespondingComponent(assetComponent)) + { + RemoveRemovedComponentOverride(instanceGameObject, assetComponent); + + if (DeleteCorrespondingComponent(coupledAssetComponent)) + RemoveRemovedComponentOverride(instanceGameObject, coupledAssetComponent); + } } + // Register undo for the changed asset if (action == InteractionMode.UserAction && originalFileContent != null) { var guid = AssetDatabase.GUIDFromAssetPath(assetPath); @@ -1444,10 +1486,8 @@ void DeleteCorrespondingComponent(Component componentInAsset) Debug.LogError($"No undo was registered when removing {assetComponent.name} from {assetRoot.name}. \nError: {errorMessage}", assetRoot); } - var prefabInstanceObject = PrefabUtility.GetPrefabInstanceHandle(instanceGameObject); - - if (action == InteractionMode.UserAction) - Undo.RegisterCompleteObjectUndo(prefabInstanceObject, actionName); + // Ensure correct action name after asset undo + Undo.SetCurrentGroupName(actionName); RemoveRemovedComponentOverridesWhichAreInvalid(prefabInstanceObject); @@ -2040,7 +2080,7 @@ internal static void ValidatePath(GameObject instanceRoot, string path) // We allow relative paths outside the Assets folder so we do not throw if isValidAssetFolder is false bool isRootFolder = false; bool isImmutableFolder = false; - bool isValidAssetFolder = AssetDatabase.GetAssetFolderInfo(directory, out isRootFolder, out isImmutableFolder); + bool isValidAssetFolder = AssetDatabase.TryGetAssetFolderInfo(directory, out isRootFolder, out isImmutableFolder); if (isValidAssetFolder && isImmutableFolder) throw new ArgumentException("Saving Prefab to immutable folder is not allowed: '" + path + "'"); @@ -2345,6 +2385,10 @@ internal static void ThrowIfInvalidArgumentsForConvertToPrefabInstance(GameObjec if (plainGameObject.transform.GetType() != prefabAssetRoot.transform.GetType()) throw new InvalidOperationException(string.Format("Cannot convert the GameObject '{0}' with root transform of type {1} with a Prefab asset with root transform of type {2}. Transform types must match.", plainGameObject.name, plainGameObject.transform.GetType().Name, prefabAssetRoot.transform.GetType().Name)); + // Prefab Mode and EditPrefabContents scope handling + if (PrefabStageUtility.IsGameObjectThePrefabRootInAnyPrefabStage(plainGameObject) || (EditorSceneManager.IsPreviewSceneObject(plainGameObject) && plainGameObject.transform.parent == null)) + throw new InvalidOperationException("Replacing the root GameObject in a Prefab with a Prefab instance is not supported since it will break all overrides for existing instances of this Prefab, including their positions and rotations." + plainGameObject.name); + if (plainGameObject.hideFlags.HasFlag(HideFlags.DontSaveInEditor) || plainGameObject.transform.hideFlags.HasFlag(HideFlags.DontSaveInEditor)) throw new ArgumentException("Input GameObject is using the HideFlags.DontSaveInEditor flag which is not supported when converting to Prefab instance: GameObject: " + plainGameObject.name, nameof(plainGameObject)); @@ -3575,7 +3619,7 @@ internal static bool DoRemovePrefabInstanceUnusedOverridesDialog(InstanceOverrid { title = titleRemoveUnusedOverrides; message += "\n\n" + msgDetailsWrittenToTheLog; - if (EditorUtility.DisplayDialog(title, message, L10n.Tr("Yes"), L10n.Tr("No"))) + if (EditorUtility.DisplayDialog(title, message, L10n.Tr("Remove"), L10n.Tr("Cancel"))) return true; } else diff --git a/Editor/Mono/PreferencesWindow/AssetPipelinePreferences.cs b/Editor/Mono/PreferencesWindow/AssetPipelinePreferences.cs index 100cd6e52c..0853ec033f 100644 --- a/Editor/Mono/PreferencesWindow/AssetPipelinePreferences.cs +++ b/Editor/Mono/PreferencesWindow/AssetPipelinePreferences.cs @@ -299,22 +299,7 @@ void AssetImportGUI() void DoAutoRefreshMode() { - bool collabEnabled = Collab.instance.IsCollabEnabledForCurrentProject(); - using (new EditorGUI.DisabledScope(collabEnabled)) - { - if (collabEnabled) - { - // Don't keep toggle value in m_AutoRefresh since we don't want to save the overwritten value - EditorGUILayout.EnumPopup(Properties.autoRefresh, AssetPipelineAutoRefreshMode.Enabled); - - EditorGUILayout.Toggle(Properties.autoRefresh, true); - EditorGUILayout.HelpBox(Properties.autoRefreshHelpBox); - } - else - { - m_AutoRefresh = (AssetPipelineAutoRefreshMode)EditorGUILayout.EnumPopup(Properties.autoRefresh, m_AutoRefresh); - } - } + m_AutoRefresh = (AssetPipelineAutoRefreshMode)EditorGUILayout.EnumPopup(Properties.autoRefresh, m_AutoRefresh); } void DoImportWorkerCount() diff --git a/Editor/Mono/PreferencesWindow/CollectionsPreferences.cs b/Editor/Mono/PreferencesWindow/CollectionsPreferences.cs index 0190b49334..ee7bb326d7 100644 --- a/Editor/Mono/PreferencesWindow/CollectionsPreferences.cs +++ b/Editor/Mono/PreferencesWindow/CollectionsPreferences.cs @@ -98,21 +98,17 @@ static void SetLeakDetection(NativeLeakDetectionMode value) { case NativeLeakDetectionMode.Disabled: { - // In the case where someone enables, disables, then re-enables leak checking, we might miss some frees - // while disabled. So to avoid spurious leak warnings, just forgive the leaks every time someone disables - // leak checking through the menu. - UnsafeUtility.ForgiveLeaks(); Debug.LogWarning("Leak detection has been disabled. Leak warnings will not be generated, and all leaks up to now are forgotten."); break; } case NativeLeakDetectionMode.Enabled: { - Debug.Log("Leak detection has been enabled. Leak warnings will be generated upon exiting play mode."); + Debug.Log("Leak detection has been enabled. Leak warnings will be generated upon domain reload."); break; } case NativeLeakDetectionMode.EnabledWithStackTrace: { - Debug.Log("Leak detection with stack traces has been enabled. Leak warnings will be generated upon exiting play mode."); + Debug.Log("Leak detection with stack traces has been enabled. Leak warnings will be generated upon domain reload."); break; } default: diff --git a/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs b/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs index cdceeac7e8..1795e680b3 100644 --- a/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs +++ b/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs @@ -51,6 +51,7 @@ class GeneralProperties public static readonly GUIContent autoSaveScenesBeforeBuilding = EditorGUIUtility.TrTextContent("Auto-save scenes before building"); public static readonly GUIContent scriptChangesDuringPlay = EditorGUIUtility.TrTextContent("Script Changes While Playing"); public static readonly GUIContent editorFont = EditorGUIUtility.TrTextContent("Editor Font"); + public static readonly GUIContent editorTextSharpness = EditorGUIUtility.TrTextContent("Editor Text Sharpness"); public static readonly GUIContent editorSkin = EditorGUIUtility.TrTextContent("Editor Theme"); public static readonly GUIContent[] editorSkinOptions = { EditorGUIUtility.TrTextContent("Light"), EditorGUIUtility.TrTextContent("Dark") }; public static readonly GUIContent hierarchyHeader = EditorGUIUtility.TrTextContent("Hierarchy window"); @@ -131,6 +132,7 @@ class SceneViewProperties public static readonly GUIContent createObjectsAtWorldOrigin = EditorGUIUtility.TrTextContent("Create Objects at Origin", "Enable this preference to instantiate new 3D objects at World coordinates 0,0,0. Disable it to instantiate them at the Scene pivot (in front of the Scene view Camera)."); public static readonly GUIContent enableConstrainProportionsScalingForNewObjects = EditorGUIUtility.TrTextContent("Create Objects with Constrained Proportions scale on", "If enabled, scale in the transform component will be set to constrain proportions for new GameObjects by default"); public static readonly GUIContent useInspectorExpandedStateContent = EditorGUIUtility.TrTextContent("Auto-hide gizmos", "Automatically hide gizmos of Components collapsed in the Inspector"); + public static readonly GUIContent ignoreAlwaysRefreshWhenNotFocused = EditorGUIUtility.TrTextContent("Refresh the Scene view only when the Editor is in focus.", "If enabled, ignore the \"Always Refresh\" flag on the Scene view when the Editor is not the foregrounded application."); } class LanguageProperties @@ -193,6 +195,7 @@ private struct GICacheSettings private static SystemLanguage[] m_stableLanguages = { SystemLanguage.English }; private bool m_EnableCompilerMessagesLocalization; + private float m_EditorTextSharpness = 0.0f; private bool m_AllowAlphaNumericHierarchy = false; private PrefabStage.Mode m_DefaultPrefabModeFromHierarchy = PrefabStage.Mode.InContext; private bool m_Create3DObjectsAtOrigin = false; @@ -516,6 +519,8 @@ private void ShowGeneral(string searchContext) } } + m_EditorTextSharpness = EditorGUILayout.Slider(GeneralProperties.editorTextSharpness, m_EditorTextSharpness, -0.5f, 1.0f); + if (InternalEditorUtility.IsGpuDeviceSelectionSupported()) { // Cache gpu devices @@ -556,7 +561,7 @@ private void ShowGeneral(string searchContext) if (Application.platform == RuntimePlatform.WindowsEditor) { - var progressDialogDelay = EditorGUILayout.DelayedFloatField(GeneralProperties.progressDialogDelay, m_ProgressDialogDelay); + var progressDialogDelay = EditorGUILayout.FloatField(GeneralProperties.progressDialogDelay, m_ProgressDialogDelay); progressDialogDelay = Mathf.Clamp(progressDialogDelay, 0.1f, 1000.0f); if (progressDialogDelay != m_ProgressDialogDelay) { @@ -771,11 +776,7 @@ internal static SortedDictionary>> OrderPre private void RevertColors() { - foreach (KeyValuePair kvp in PrefSettings.Prefs()) - { - kvp.Value.ResetToDefault(); - EditorPrefs.SetString(kvp.Value.Name, kvp.Value.ToUniqueString()); - } + PrefSettings.RevertAll(); } private void ShowColors(string searchContext) @@ -824,6 +825,7 @@ private void ShowSceneView(string searchContext) m_Create3DObjectsAtOrigin = EditorGUILayout.Toggle(SceneViewProperties.createObjectsAtWorldOrigin, m_Create3DObjectsAtOrigin); m_EnableConstrainProportionsScalingForNewObjects = EditorGUILayout.Toggle(SceneViewProperties.enableConstrainProportionsScalingForNewObjects, m_EnableConstrainProportionsScalingForNewObjects); AnnotationUtility.useInspectorExpandedState = EditorGUILayout.Toggle(SceneViewProperties.useInspectorExpandedStateContent, AnnotationUtility.useInspectorExpandedState); + SceneView.s_PreferenceIgnoreAlwaysRefreshWhenNotFocused.value = EditorGUILayout.Toggle(SceneViewProperties.ignoreAlwaysRefreshWhenNotFocused, SceneView.s_PreferenceIgnoreAlwaysRefreshWhenNotFocused); GUILayout.Label("Handles", EditorStyles.boldLabel); Handles.s_LineThickness.value = EditorGUILayout.IntSlider(SceneViewProperties.handlesLineThickness, (int)Handles.s_LineThickness.value, 1, 5); @@ -1044,6 +1046,7 @@ private void ShowDeveloperMode(string searchContext) if (m_DeveloperModeDirty) { ApplyChangesToPrefs(); + EditorApplication.UpdateMainWindowTitle(); } } @@ -1109,6 +1112,9 @@ private void WritePreferences() EditorPrefs.SetString("Editor.kEditorLocale", m_SelectedLanguage); EditorPrefs.SetBool("Editor.kEnableCompilerMessagesLocalization", m_EnableCompilerMessagesLocalization); + EditorPrefs.SetFloat($"EditorTextSharpness_{EditorResources.GetFont(FontDef.Style.Normal).name}", m_EditorTextSharpness); + EditorApplication.RequestRepaintAllTexts(); + EditorPrefs.SetBool("AllowAlphaNumericHierarchy", m_AllowAlphaNumericHierarchy); EditorPrefs.SetInt("DefaultPrefabModeFromHierarchy", (int)m_DefaultPrefabModeFromHierarchy); @@ -1197,6 +1203,9 @@ private void ReadPreferences() m_EnableEditorLocalization = EditorPrefs.GetBool("Editor.kEnableEditorLocalization", true); m_SelectedLanguage = EditorPrefs.GetString("Editor.kEditorLocale", LocalizationDatabase.GetDefaultEditorLanguage().ToString()); m_EnableCompilerMessagesLocalization = EditorPrefs.GetBool("Editor.kEnableCompilerMessagesLocalization", false); + m_EditorTextSharpness = EditorPrefs.GetFloat($"EditorTextSharpness_{EditorResources.GetFont(FontDef.Style.Normal).name}", 0.0f); + EditorTextSettings.SetCurrentEditorSharpness(m_EditorTextSharpness); + m_AllowAlphaNumericHierarchy = EditorPrefs.GetBool("AllowAlphaNumericHierarchy", false); m_DefaultPrefabModeFromHierarchy = GetDefaultPrefabModeForHierarchy(); m_ProgressDialogDelay = EditorPrefs.GetFloat("EditorBusyProgressDialogDelay", 3.0f); diff --git a/Editor/Mono/Progress/Progress.bindings.cs b/Editor/Mono/Progress/Progress.bindings.cs index 97072a3db8..15f265adf7 100644 --- a/Editor/Mono/Progress/Progress.bindings.cs +++ b/Editor/Mono/Progress/Progress.bindings.cs @@ -79,6 +79,14 @@ internal enum Updates : uint EverythingChanged = 0xffffffff } + [NativeType(Header = k_NativeHeader)] + internal enum ExplicitLoggingState + { + NotSet, + Enabled, + Disabled + } + [StructLayout(LayoutKind.Sequential)] [NativeHeader(k_NativeHeader)] [RequiredByNativeCode(GenerateProxy = true)] @@ -471,7 +479,7 @@ internal static float GetMaxElapsedTime() return maxElapsedTime; } - // For testing purposes only. + // Anything below this line is for testing purposes only. internal static void ClearProgressItems() { s_ProgressItems.Clear(); @@ -482,6 +490,44 @@ internal static void ClearProgressItems() s_RemainingTime = TimeSpan.Zero; s_LastRemainingTimeUpdate = DateTime.Now; } + + internal static ExplicitLoggingState errorLoggingState + { + get => GetExplicitErrorLoggingState(); + set => SetExplicitErrorLoggingState(value); + } + internal static ExplicitLoggingState warningLoggingState + { + get => GetExplicitWarningLoggingState(); + set => SetExplicitWarningLoggingState(value); + } + + [NativeMethod(IsFreeFunction = true, IsThreadSafe = false, Name = "Editor::Progress::Internal_GetExplicitErrorLoggingState")] + static extern ExplicitLoggingState GetExplicitErrorLoggingState(); + + [NativeMethod(IsFreeFunction = true, IsThreadSafe = false, Name = "Editor::Progress::Internal_GetExplicitWarningLoggingState")] + static extern ExplicitLoggingState GetExplicitWarningLoggingState(); + + [NativeMethod(IsFreeFunction = true, IsThreadSafe = false, Name = "Editor::Progress::Internal_SetExplicitErrorLoggingState")] + static extern void SetExplicitErrorLoggingState(ExplicitLoggingState state); + + [NativeMethod(IsFreeFunction = true, IsThreadSafe = false, Name = "Editor::Progress::Internal_SetExplicitWarningLoggingState")] + static extern void SetExplicitWarningLoggingState(ExplicitLoggingState state); + + internal static bool manualUpdate + { + get => IsManualUpdate(); + set => SetManualUpdate(value); + } + + [NativeMethod(IsFreeFunction = true, IsThreadSafe = false, Name = "Editor::Progress::Internal_IsManualUpdate")] + static extern bool IsManualUpdate(); + + [NativeMethod(IsFreeFunction = true, IsThreadSafe = false, Name = "Editor::Progress::Internal_SetManualUpdate")] + static extern void SetManualUpdate(bool manualUpdate); + + [NativeMethod(IsFreeFunction = true, IsThreadSafe = false, Name = "Editor::Progress::ForceUpdateProgress")] + internal static extern void ForceUpdate(); } static class ProgressEnumExtensions diff --git a/Editor/Mono/ProjectBrowser.cs b/Editor/Mono/ProjectBrowser.cs index 3e9e4521d0..ea63e9483f 100644 --- a/Editor/Mono/ProjectBrowser.cs +++ b/Editor/Mono/ProjectBrowser.cs @@ -1012,15 +1012,6 @@ void ShowFolderContents(int folderInstanceID, bool revealAndFrameInFolderTree) if (folderInstanceID == kPackagesFolderInstanceId) folderPath = PackageManager.Folders.GetPackagesPath(); - - if (m_SearchFilter.GetState() == SearchFilter.State.FolderBrowsing) - { - if (m_SearchFilter.folders.Length == 1 && m_SearchFilter.folders[0] == folderPath) - { - return; // Already showing folder - } - } - if (!m_SkipHiddenPackages || PackageManagerUtilityInternal.IsPathInVisiblePackage(folderPath)) { m_SearchFilter.ClearSearch(); @@ -2896,7 +2887,8 @@ public void FrameObject(int instanceID, bool ping) frame = false; // If the item is visible then we can ping it however if it requires revealing then we can not and should indicate why(locked project view). - if ((m_ViewMode == ViewMode.TwoColumns && !m_ListArea.IsShowing(instanceID)) || (m_ViewMode == ViewMode.OneColumn && m_AssetTree.data.GetRow(instanceID) == -1)) + if ((m_ViewMode == ViewMode.TwoColumns && m_ListArea != null && !m_ListArea.IsShowing(instanceID)) + || (m_ViewMode == ViewMode.OneColumn && m_AssetTree != null && m_AssetTree.data.GetRow(instanceID) == -1)) { Repaint(); m_LockTracker.PingIcon(); @@ -3004,7 +2996,7 @@ internal static bool CanDeleteSelectedAssets() { var path = AssetDatabase.GetAssetPath(instanceID); bool isRootFolder, isImmutable; - if (string.IsNullOrEmpty(path) || !AssetDatabase.GetAssetFolderInfo(path, out isRootFolder, out isImmutable) || isRootFolder || isImmutable) + if (string.IsNullOrEmpty(path) || !AssetDatabase.TryGetAssetFolderInfo(path, out isRootFolder, out isImmutable) || isRootFolder || isImmutable) { return false; } diff --git a/Editor/Mono/ProjectBrowser/SavedSearchFilter.cs b/Editor/Mono/ProjectBrowser/SavedSearchFilter.cs index 9b6a27d0dc..0d757d0381 100644 --- a/Editor/Mono/ProjectBrowser/SavedSearchFilter.cs +++ b/Editor/Mono/ProjectBrowser/SavedSearchFilter.cs @@ -10,7 +10,6 @@ using UnityEditor.IMGUI.Controls; using UnityEngine; using UnityEditorInternal; -using UnityEditor.Collaboration; using Object = UnityEngine.Object; @@ -465,22 +464,6 @@ TreeViewItem BuildTreeView() root = item; else { - if (Collab.instance.collabFilters.ContainsSearchFilter(savedFilter.m_Name, savedFilter.m_Filter.FilterToSearchFieldString())) - { - if (!Collab.instance.IsCollabEnabledForCurrentProject()) - continue; - } - - if (SoftlockViewController.Instance.softLockFilters.ContainsSearchFilter(savedFilter.m_Name, savedFilter.m_Filter.FilterToSearchFieldString())) - { - if (CollabSettingsManager.IsAvailable(CollabSettingType.InProgressEnabled)) - { - if (!Collab.instance.IsCollabEnabledForCurrentProject() || !CollabSettingsManager.inProgressEnabled) - continue; - } - else - continue; - } items.Add(item); } } diff --git a/Editor/Mono/ProjectWindow/GlobSearchUtilities.cs b/Editor/Mono/ProjectWindow/GlobSearchUtilities.cs index 749c23bf0e..e6149e3e7b 100644 --- a/Editor/Mono/ProjectWindow/GlobSearchUtilities.cs +++ b/Editor/Mono/ProjectWindow/GlobSearchUtilities.cs @@ -11,8 +11,8 @@ namespace UnityEditor { static class GlobSearchUtilities { - static readonly Regex k_BasicSymbolsRegex = new Regex(@"(?\\\[..+\])|(?\\\*\\\*/)|(?\\\*\\\*)|(?\\\*)|(?\\\?)"); - static readonly Regex k_ComplexSymbolsRegex = new Regex(@"(?\\\(.+(?:\\\|.+)+\\\))"); + static readonly Regex k_BasicSymbolsRegex = new Regex(@"(?\\\[..+?\])|(?\\\*\\\*/)|(?\\\*\\\*)|(?\\\*)|(?\\\?)"); + static readonly Regex k_ComplexSymbolsRegex = new Regex(@"(?\\\(.+?(?:\\\|.+?)+\\\))"); static Dictionary> s_GlobToRegexMatch; static GlobSearchUtilities() diff --git a/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs b/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs index 468e653138..bf10cf0251 100644 --- a/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs +++ b/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs @@ -364,7 +364,7 @@ static void CreatePrefabVariant() string sourcePath = AssetDatabase.GetAssetPath(go); string sourceDir = Path.GetDirectoryName(sourcePath).ConvertSeparatorsToUnity(); - string variantPath = GetPrefabVariantPath(sourceDir, go.name); + string variantPath = GetPrefabVariantPath(sourceDir, go); StartNameEditingIfProjectWindowExists( 0, @@ -395,7 +395,7 @@ static GameObject[] CreatePrefabVariants(GameObject[] gameObjects) { string sourcePath = AssetDatabase.GetAssetPath(go); string sourceDir = Path.GetDirectoryName(sourcePath).ConvertSeparatorsToUnity(); - string variantPath = GetPrefabVariantPath(sourceDir, go.name); + string variantPath = GetPrefabVariantPath(sourceDir, go); variantPath = AssetDatabase.GenerateUniqueAssetPath(variantPath); var variant = PrefabUtility.CreateVariant(go, variantPath); @@ -412,9 +412,12 @@ static GameObject[] CreatePrefabVariants(GameObject[] gameObjects) return createdVariants.ToArray(); } - static string GetPrefabVariantPath(string folder, string gameObjectName) + static string GetPrefabVariantPath(string folder, GameObject gameObject) { - return string.Format("{0}/{1} Variant.prefab", folder, gameObjectName); + if (PrefabUtility.IsPartOfModelPrefab(gameObject)) + return string.Format("{0}/{1}.prefab", folder, gameObject.name); + else + return string.Format("{0}/{1} Variant.prefab", folder, gameObject.name); } public static void CreateAssetWithContent(string filename, string content, Texture2D icon = null) @@ -533,8 +536,8 @@ internal static string RemoveOrInsertNamespace(string content, string rootNamesp if (string.IsNullOrEmpty(rootNamespace)) { - content = Regex.Replace(content, $"((\\r\\n)|\\n)[ \\t]*{rootNamespaceBeginTag}[ \\t]*", string.Empty); - content = Regex.Replace(content, $"((\\r\\n)|\\n)[ \\t]*{rootNamespaceEndTag}[ \\t]*", string.Empty); + content = Regex.Replace(content, $"((\\r\\n)|\\n)?[ \\t]*{rootNamespaceBeginTag}[ \\t]*", string.Empty); + content = Regex.Replace(content, $"((\\r\\n)|\\n)?[ \\t]*{rootNamespaceEndTag}[ \\t]*", string.Empty); return content; } diff --git a/Editor/Mono/ProjectWindow/SearchFilter.cs b/Editor/Mono/ProjectWindow/SearchFilter.cs index d0a16ae7f9..de81e955bc 100644 --- a/Editor/Mono/ProjectWindow/SearchFilter.cs +++ b/Editor/Mono/ProjectWindow/SearchFilter.cs @@ -8,8 +8,6 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; -using UnityEditor.Collaboration; -using UnityEditor.Connect; namespace UnityEditor { @@ -44,10 +42,6 @@ public enum State [SerializeField] private string[] m_AssetBundleNames = new string[0]; [SerializeField] - private string[] m_VersionControlStates = new string[0]; - [SerializeField] - private string[] m_SoftLockControlStates = new string[0]; - [SerializeField] private int[] m_ReferencingInstanceIDs = new int[0]; [SerializeField] private int[] m_SceneHandles; @@ -74,12 +68,13 @@ public enum State [SerializeField] private ImportLogFlags m_ImportLogFlags; + [SerializeField] + private bool m_FilterByTypeIntersection; + // Interface public string nameFilter { get { return m_NameFilter; } set { m_NameFilter = value; }} public string[] classNames { get { return m_ClassNames; } set { m_ClassNames = value; }} public string[] assetLabels { get { return m_AssetLabels; } set { m_AssetLabels = value; }} - public string[] versionControlStates { get { return m_VersionControlStates; } set { m_VersionControlStates = value; }} - public string[] softLockControlStates { get { return m_SoftLockControlStates; } set { m_SoftLockControlStates = value; }} public string[] assetBundleNames { get { return m_AssetBundleNames; } set { m_AssetBundleNames = value; }} public int[] referencingInstanceIDs { get { return m_ReferencingInstanceIDs; } set { m_ReferencingInstanceIDs = value; }} public int[] sceneHandles { get { return m_SceneHandles; } set { m_SceneHandles = value; }} @@ -94,6 +89,7 @@ public enum State public bool anyWithAssetOrigin { get { return m_AnyWithAssetOrigin; } set { m_AnyWithAssetOrigin = value; }} public string originalText { get => m_OriginalText; set => m_OriginalText = value; } public ImportLogFlags importLogFlags { get => m_ImportLogFlags; set => m_ImportLogFlags = value; } + internal bool filterByTypeIntersection { get => m_FilterByTypeIntersection; set => m_FilterByTypeIntersection = value; } public void ClearSearch() { @@ -107,11 +103,10 @@ public void ClearSearch() m_Globs = new string[0]; m_ProductIds = new int[0]; m_AnyWithAssetOrigin = false; - m_VersionControlStates = new string[0]; - m_SoftLockControlStates = new string[0]; m_ShowAllHits = false; m_SkipHidden = false; m_ImportLogFlags = ImportLogFlags.None; + m_FilterByTypeIntersection = false; } bool IsNullOrEmpty(T[] list) @@ -130,8 +125,6 @@ public State GetState() !IsNullOrEmpty(m_ReferencingInstanceIDs) || m_ImportLogFlags != ImportLogFlags.None; - isSearchActive = isSearchActive || !IsNullOrEmpty(m_VersionControlStates); - isSearchActive = isSearchActive || !IsNullOrEmpty(m_SoftLockControlStates); bool foldersActive = !IsNullOrEmpty(m_Folders); @@ -193,16 +186,6 @@ public bool SetNewFilter(SearchFilter newFilter) m_Folders = newFilter.m_Folders; changed = true; } - if (newFilter.m_VersionControlStates != m_VersionControlStates) - { - m_VersionControlStates = newFilter.m_VersionControlStates; - changed = true; - } - if (newFilter.m_SoftLockControlStates != m_SoftLockControlStates) - { - m_SoftLockControlStates = newFilter.m_SoftLockControlStates; - changed = true; - } if (newFilter.m_AssetLabels != m_AssetLabels) { m_AssetLabels = newFilter.m_AssetLabels; @@ -265,6 +248,12 @@ public bool SetNewFilter(SearchFilter newFilter) changed = true; } + if (newFilter.m_FilterByTypeIntersection != m_FilterByTypeIntersection) + { + m_FilterByTypeIntersection = newFilter.m_FilterByTypeIntersection; + changed = true; + } + return changed; } @@ -281,11 +270,6 @@ public override string ToString() if (m_AssetLabels != null && m_AssetLabels.Length > 0) result += "[Labels: " + m_AssetLabels[0] + "]"; - if (m_VersionControlStates != null && m_VersionControlStates.Length > 0) - result += "[VersionStates: " + m_VersionControlStates[0] + "]"; - - if (m_SoftLockControlStates != null && m_SoftLockControlStates.Length > 0) - result += "[SoftLockStates: " + m_SoftLockControlStates[0] + "]"; if (m_AssetBundleNames != null && m_AssetBundleNames.Length > 0) result += "[AssetBundleNames: " + m_AssetBundleNames[0] + "]"; @@ -322,6 +306,11 @@ public override string ToString() result += $"[ImportLog: {ImportLog.Filters.WarningsStr}]"; } + if (m_FilterByTypeIntersection) + { + result += $"[FilterByTypeIntersection]"; + } + return result; } @@ -341,8 +330,6 @@ internal string FilterToSearchFieldString() // See SearchUtility.cs for search tokens AddToString(FormatFilterTokenForSearchEngine("t"), m_ClassNames, ref result); AddToString(FormatFilterTokenForSearchEngine("l"), m_AssetLabels, ref result); - AddToString("v:", m_VersionControlStates, ref result); - AddToString("s:", m_SoftLockControlStates, ref result); AddToString("b:", m_AssetBundleNames, ref result); AddToString("glob:", m_Globs.Select(a => $"\"{a}\"").ToArray(), ref result); AddToString("assetorigin.productid:", m_ProductIds.Select(o => $"\"{o}\"").ToArray(), ref result); diff --git a/Editor/Mono/ProjectWindow/SearchableEditorWindow.cs b/Editor/Mono/ProjectWindow/SearchableEditorWindow.cs index 3fcbc5d595..77ac4cd3ef 100644 --- a/Editor/Mono/ProjectWindow/SearchableEditorWindow.cs +++ b/Editor/Mono/ProjectWindow/SearchableEditorWindow.cs @@ -72,18 +72,7 @@ private static void OnSearchForReferencesToComponent(MenuCommand command) { var component = command.context as Component; if (component) - { - var searchFilter = "ref:" + component.GetInstanceID() + ":"; - foreach (SearchableEditorWindow sw in searchableWindows) - { - if (sw.m_HierarchyType == HierarchyType.GameObjects) - { - sw.SetSearchFilter(searchFilter, SearchMode.All, false, false); - sw.m_HasSearchFilterFocus = true; - sw.Repaint(); - } - } - } + SearchForReferencesToInstanceID(component.GetInstanceID()); } [MenuItem("CONTEXT/Component/Properties...", false, 99999)] diff --git a/Editor/Mono/SceneHierarchy.cs b/Editor/Mono/SceneHierarchy.cs index 4a577b6840..ae07312a8a 100644 --- a/Editor/Mono/SceneHierarchy.cs +++ b/Editor/Mono/SceneHierarchy.cs @@ -463,14 +463,27 @@ void PlayModeStateChanged(PlayModeStateChange state) void OnSceneCreated(Scene scene, NewSceneSetup setup, NewSceneMode mode) { + ClearSearchSessionIfAny(); ExpandTreeViewItem(scene.handle, true); } void OnSceneOpened(Scene scene, OpenSceneMode mode) { + ClearSearchSessionIfAny(); ExpandTreeViewItem(scene.handle, true); } + void ClearSearchSessionIfAny() + { + var dataSource = treeView.data as GameObjectTreeViewDataSource; + if (dataSource != null && !string.IsNullOrEmpty(dataSource.searchString)) + { + var oldSearch = dataSource.searchString; + dataSource.searchString = ""; + dataSource.searchString = oldSearch; + } + } + internal void ExpandTreeViewItem(int id, bool expand) { var dataSource = treeView.data as TreeViewDataSource; @@ -1195,6 +1208,16 @@ void CreateGameObjectContextClick(GenericMenu menu, int contextClickedItemID) else menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Select Children")); + menu.AddSeparator(""); + if (Selection.activeGameObject) + { + menu.AddItem(EditorGUIUtility.TrTextContent("Find References in Scene"), false, FindReferenceInScene); + } + else + { + menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Find References in Scene")); + } + menu.AddSeparator(""); GameObject selectedObject = null; @@ -1378,6 +1401,14 @@ void CreateGameObjectContextClick(GenericMenu menu, int contextClickedItemID) menu.ShowAsContext(); } + private void FindReferenceInScene() + { + var selectedObject = Selection.activeObject; + if (!selectedObject) + return; + SearchableEditorWindow.SearchForReferencesToInstanceID(selectedObject.GetInstanceID()); + } + protected void AddCreateGameObjectItemsToSceneMenu(GenericMenu menu, Scene scene) { AddCreateGameObjectItemsToMenu(menu, Selection.transforms.Select(t => t.gameObject).ToArray(), false, false, true, scene.handle, MenuUtils.ContextMenuOrigin.Scene); diff --git a/Editor/Mono/SceneManagement/EditorSceneManager.cs b/Editor/Mono/SceneManagement/EditorSceneManager.cs index 8ca4e14cb7..0d5a95feb9 100644 --- a/Editor/Mono/SceneManagement/EditorSceneManager.cs +++ b/Editor/Mono/SceneManagement/EditorSceneManager.cs @@ -10,6 +10,7 @@ namespace UnityEditor.SceneManagement { public sealed partial class EditorSceneManager { + public delegate void SceneManagerSetupRestoredCallback(Scene[] scenes); public delegate void NewSceneCreatedCallback(Scene scene, NewSceneSetup setup, NewSceneMode mode); public delegate void SceneOpeningCallback(string path, OpenSceneMode mode); public delegate void SceneOpenedCallback(Scene scene, OpenSceneMode mode); @@ -19,30 +20,41 @@ public sealed partial class EditorSceneManager public delegate void SceneSavedCallback(Scene scene); public delegate void SceneDirtiedCallback(Scene scene); + public static event SceneManagerSetupRestoredCallback sceneManagerSetupRestored + { + add => m_SceneManagerSetupRestoredEvent.Add(value); + remove => m_SceneManagerSetupRestoredEvent.Remove(value); + } + private static EventWithPerformanceTracker m_SceneManagerSetupRestoredEvent = new EventWithPerformanceTracker($"{nameof(EditorSceneManager)}.{nameof(sceneManagerSetupRestored)}"); + public static event NewSceneCreatedCallback newSceneCreated { add => m_NewSceneCreatedEvent.Add(value); remove => m_NewSceneCreatedEvent.Remove(value); } private static EventWithPerformanceTracker m_NewSceneCreatedEvent = new EventWithPerformanceTracker($"{nameof(EditorSceneManager)}.{nameof(newSceneCreated)}"); + public static event SceneOpeningCallback sceneOpening { add => m_SceneOpeningEvent.Add(value); remove => m_SceneOpeningEvent.Remove(value); } private static EventWithPerformanceTracker m_SceneOpeningEvent = new EventWithPerformanceTracker($"{nameof(EditorSceneManager)}.{nameof(sceneOpening)}"); + public static event SceneOpenedCallback sceneOpened { add => m_SceneOpenedEvent.Add(value); remove => m_SceneOpenedEvent.Remove(value); } private static EventWithPerformanceTracker m_SceneOpenedEvent = new EventWithPerformanceTracker($"{nameof(EditorSceneManager)}.{nameof(sceneOpened)}"); + public static event SceneClosingCallback sceneClosing { add => m_SceneClosingEvent.Add(value); remove => m_SceneClosingEvent.Remove(value); } private static EventWithPerformanceTracker m_SceneClosingEvent = new EventWithPerformanceTracker($"{nameof(EditorSceneManager)}.{nameof(sceneClosing)}"); + public static event SceneClosedCallback sceneClosed { add => m_SceneClosedEvent.Add(value); @@ -68,6 +80,13 @@ public static event SceneDirtiedCallback sceneDirtied } private static EventWithPerformanceTracker m_SceneDirtiedEvent = new EventWithPerformanceTracker($"{nameof(EditorSceneManager)}.{nameof(sceneDirtied)}"); + [RequiredByNativeCode] + private static void Internal_SceneManagerSetupRestored(Scene[] scenes) + { + foreach (var evt in m_SceneManagerSetupRestoredEvent) + evt(scenes); + } + [RequiredByNativeCode] private static void Internal_NewSceneCreated(Scene scene, NewSceneSetup setup, NewSceneMode mode) { diff --git a/Editor/Mono/SceneManagement/StageManager/PrefabStage/PrefabStage.cs b/Editor/Mono/SceneManagement/StageManager/PrefabStage/PrefabStage.cs index 37c2909acb..94628f8326 100644 --- a/Editor/Mono/SceneManagement/StageManager/PrefabStage/PrefabStage.cs +++ b/Editor/Mono/SceneManagement/StageManager/PrefabStage/PrefabStage.cs @@ -47,6 +47,8 @@ static Styles() } } + internal static string s_PrefabInContextPreviewValuesTooltip = L10n.Tr("This property is previewing the overridden value on the Prefab instance.\n\nTo edit this property, open this Prefab Asset in isolation by pressing the modifier key [Alt] while you open it."); + public enum Mode { InIsolation, @@ -451,7 +453,23 @@ void SetPrefabInstanceHiddenForInContextEditing(bool hide) StageUtility.SetPrefabInstanceHiddenForInContextEditing(m_OpenedFromInstanceRoot, hide); } - static List s_ReusableCanvasList = new List(); + void UpdateSortableComponentsWithStagePriority(int stagePriority) + { + List rendererList = new List(); + List canvasList = new List(); + + // Renderer components requires to know there is stage priority when entering context and isolation + // mode. If this is not shared, their sorting order will not be evaluated accordingly since they are not + // in the same layer. + m_PrefabContentsRoot.GetComponentsInChildren(true, rendererList); + m_PrefabContentsRoot.GetComponentsInChildren(true, canvasList); + + foreach (Renderer renderer in rendererList) + renderer.stagePriority = (byte)stagePriority; + + foreach (Canvas canvas in canvasList) + canvas.stagePriority = (byte)stagePriority; + } bool LoadStage() { @@ -496,12 +514,15 @@ bool LoadStage() m_PrefabContentsRoot = PrefabStageUtility.LoadPrefabIntoPreviewScene(m_PrefabAssetPath, scene); if (m_PrefabContentsRoot != null) { + // Corresponds to which breadcrumb this is. + var stagePriority = StageNavigationManager.instance.stageHistory.IndexOf(this); + if (isUIPrefab) { - m_PrefabContentsRoot.GetComponentsInChildren(true, s_ReusableCanvasList); + UpdateSortableComponentsWithStagePriority(stagePriority); if (m_Mode == Mode.InIsolation && m_PrefabContentsRoot.transform.parent == null) - PrefabStageUtility.HandleUIReparentingIfNeeded(m_PrefabContentsRoot, 0); + PrefabStageUtility.HandleUIReparentingIfNeeded(m_PrefabContentsRoot, stagePriority); } m_PrefabFileIcon = DeterminePrefabFileIconFromInstanceRootGameObject(); @@ -540,6 +561,8 @@ bool LoadStage() { dummyCanvas.sortingOrder = instanceCanvas.sortingOrder; dummyCanvas.referencePixelsPerUnit = instanceCanvas.referencePixelsPerUnit; + dummyCanvas.stagePriority = (byte)stagePriority; + dummyCanvas.sortingLayerID = instanceCanvas.sortingLayerID; } } @@ -651,7 +674,7 @@ protected internal override void OnReturnToStage() } } - bool HasPatchedPropertyModificationsFor(UnityEngine.Object obj, string partialPropertyName) + internal bool HasPatchedPropertyModificationsFor(UnityEngine.Object obj, string partialPropertyName) { if (m_PatchedProperties == null) return false; @@ -1239,7 +1262,15 @@ void DetectPrefabFileIconChange() void DetectSceneDirtinessChange() { if (scene.dirtyID != m_LastSceneDirtyID) + { + // We want to make sure that all the new sortable components (e.g, canvas, renderer) being added have + // the correct StagePriority assigned to them. Otherwise they won't be sorted accordingly when in + // context/isolation mode. + if (PrefabStageUtility.IsUIPrefab(m_PrefabAssetPath)) + UpdateSortableComponentsWithStagePriority(StageNavigationManager.instance.stageHistory.IndexOf(this)); + SceneView.RepaintAll(); + } m_LastSceneDirtyID = scene.dirtyID; } @@ -1916,7 +1947,8 @@ internal bool showOverrides if (prefabStage != null) prefabStage.RefreshPatchedProperties(); } - EditorApplication.RequestRepaintAllViews(); + + PropertyEditor.ClearAndRebuildAll(); } } @@ -1942,7 +1974,7 @@ void VisualizeOverridesToggle() void CachePrefabFolderInfo() { bool isRootFolder; - m_IsPrefabInValidAssetFolder = AssetDatabase.GetAssetFolderInfo(m_PrefabAssetPath, out isRootFolder, out m_IsPrefabInImmutableFolder); + m_IsPrefabInValidAssetFolder = AssetDatabase.TryGetAssetFolderInfo(m_PrefabAssetPath, out isRootFolder, out m_IsPrefabInImmutableFolder); } bool IsPrefabInImmutableFolder() diff --git a/Editor/Mono/SceneManagement/StageManager/PrefabStage/PrefabStageUtility.cs b/Editor/Mono/SceneManagement/StageManager/PrefabStage/PrefabStageUtility.cs index 0c76a3cf06..325835bd3d 100644 --- a/Editor/Mono/SceneManagement/StageManager/PrefabStage/PrefabStageUtility.cs +++ b/Editor/Mono/SceneManagement/StageManager/PrefabStage/PrefabStageUtility.cs @@ -235,7 +235,9 @@ internal static bool CheckIfAnyComponentShouldBlockPrefabModeInPlayMode(string p if (warnList.Count > 0) { - string blockingNames = string.Join(", ", warnList.Select(e => MonoScript.FromMonoBehaviour(e).name).ToArray()); + string blockingNames = string.Join(", ", warnList.Select(e => MonoScript.FromMonoBehaviour(e).name).Distinct().ToArray()); + if (blockingNames.Length > 1000) + blockingNames = blockingNames.Substring(0, 1000) + "\n..."; return EditorUtility.DisplayDialog( L10n.Tr("Risk of unwanted modifications"), string.Format( @@ -665,13 +667,14 @@ internal static GUIContent GetPrefabButtonContent(int instanceID) { GUIContent result; var defaultPrefabMode = PreferencesProvider.GetDefaultPrefabModeForHierarchy(); + var modifierKey = Application.platform == RuntimePlatform.OSXEditor ? "Option" : "Alt"; switch (defaultPrefabMode) { case PrefabStage.Mode.InContext: - result = new GUIContent("", null, $"Open Prefab Asset in context.\nPress modifier key [Alt] to open in isolation."); + result = new GUIContent("", null, $"Open Prefab Asset in context.\nPress the {modifierKey} modifier key to open in isolation."); break; case PrefabStage.Mode.InIsolation: - result = new GUIContent("", null, "Open Prefab Asset in isolation.\nPress modifier key [Alt] to open in context."); + result = new GUIContent("", null, $"Open Prefab Asset in isolation.\nPress the {modifierKey} modifier key to open in context."); break; default: result = new GUIContent(""); diff --git a/Editor/Mono/SceneManagement/StageManager/StageUtility.bindings.cs b/Editor/Mono/SceneManagement/StageManager/StageUtility.bindings.cs index 3afb05e064..4d50daea01 100644 --- a/Editor/Mono/SceneManagement/StageManager/StageUtility.bindings.cs +++ b/Editor/Mono/SceneManagement/StageManager/StageUtility.bindings.cs @@ -61,5 +61,8 @@ public static partial class StageUtility [StaticAccessor("StageUtilityBindings", StaticAccessorType.DoubleColon)] extern private static void CallAwakeFromLoadOnSubHierarchyInternal([NotNull("NullExceptionObject")] GameObject prefabInstanceRoot); + + [StaticAccessor("StageUtility", StaticAccessorType.DoubleColon)] + extern internal static bool IsGizmoCulledBySceneCullingMasksOrFocusedScene([NotNull] GameObject gameObject, [NotNull] Camera camera); } } diff --git a/Editor/Mono/SceneModeWindows/DefaultLightingExplorerExtension.cs b/Editor/Mono/SceneModeWindows/DefaultLightingExplorerExtension.cs index ca1dd05b43..3f1cfe4372 100644 --- a/Editor/Mono/SceneModeWindows/DefaultLightingExplorerExtension.cs +++ b/Editor/Mono/SceneModeWindows/DefaultLightingExplorerExtension.cs @@ -23,6 +23,7 @@ private static class Styles public static readonly GUIContent Shape = EditorGUIUtility.TrTextContent("Shape"); public static readonly GUIContent Mode = EditorGUIUtility.TrTextContent("Mode"); public static readonly GUIContent Color = EditorGUIUtility.TrTextContent("Color"); + public static readonly GUIContent Range = EditorGUIUtility.TrTextContent("Range"); public static readonly GUIContent Intensity = EditorGUIUtility.TrTextContent("Intensity"); public static readonly GUIContent IndirectMultiplier = EditorGUIUtility.TrTextContent("Indirect Multiplier"); public static readonly GUIContent ShadowType = EditorGUIUtility.TrTextContent("Shadows"); @@ -232,7 +233,7 @@ protected virtual LightingExplorerTableColumn[] Get2DLightColumns() EditorGUI.LabelField(r, Styles.Mixed); } }), // 8: Target Sorting Layer - new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Custom, Styles.ShadowIntensityEnabled, "m_ShadowIntensityEnabled", 50, (r, prop, dep) => // 9 + new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Custom, Styles.ShadowIntensityEnabled, "m_ShadowsEnabled", 50, (r, prop, dep) => // 9 { if (prop != null) { @@ -340,7 +341,19 @@ protected virtual LightingExplorerTableColumn[] GetLightColumns() } }, null, null, new int[] { 2 }), // 4: Mode new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Color, Styles.Color, "m_Color", 70), // 5: Color - new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Float, Styles.Intensity, "m_Intensity", 60), // 6: Intensity + new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Float, Styles.Range, "m_Range", 60, (r, prop, dep) => + { + var lightType = prop.serializedObject.FindProperty("m_Type"); + if (lightType != null) + { + bool directionalLight = lightType.enumValueIndex == (int)LightType.Directional; + if (!directionalLight) + { + EditorGUI.PropertyField(r, prop, GUIContent.none); + } + } + }), // 6: Range + new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Float, Styles.Intensity, "m_Intensity", 60), // 7: Intensity new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Float, Styles.IndirectMultiplier, "m_BounceIntensity", 110, (r, prop, dep) => { bool realtimeLight = dep.Length > 1 && dep[0].intValue == (int)LightmapBakeType.Realtime; @@ -349,7 +362,7 @@ protected virtual LightingExplorerTableColumn[] GetLightColumns() { EditorGUI.PropertyField(r, prop, GUIContent.none); } - }, null, null, new int[] { 4 }), // 7: Indirect Multiplier + }, null, null, new int[] { 4 }), // 8: Indirect Multiplier new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Enum, Styles.ShadowType, "m_Shadows.m_Type", 100, (r, prop, dep) => { bool areaLight = dep.Length > 1 && (dep[0].enumValueIndex == (int)LightType.Rectangle || dep[0].enumValueIndex == (int)LightType.Disc); @@ -370,7 +383,7 @@ protected virtual LightingExplorerTableColumn[] GetLightColumns() { EditorGUI.PropertyField(r, prop, GUIContent.none); } - }, null, null, new int[] { 2 }), // 8: Shadow Type + }, null, null, new int[] { 2 }), // 9: Shadow Type }; } diff --git a/Editor/Mono/SceneModeWindows/LightingExplorerExtensionAttribute.deprecated.cs b/Editor/Mono/SceneModeWindows/LightingExplorerExtensionAttribute.deprecated.cs new file mode 100644 index 0000000000..19696e0623 --- /dev/null +++ b/Editor/Mono/SceneModeWindows/LightingExplorerExtensionAttribute.deprecated.cs @@ -0,0 +1,17 @@ +// 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 UnityEditor.Rendering; +using UnityEngine.Rendering; + +namespace UnityEditor; + +[Obsolete($"{nameof(LightingExplorerExtensionAttribute)} is deprecated. Use {nameof(SupportedOnRenderPipelineAttribute)} instead. #from(23.1) (UnityUpgradable) -> UnityEngine.Rendering.SupportedOnRenderPipelineAttribute", false)] +[AttributeUsage(AttributeTargets.Class)] +public class LightingExplorerExtensionAttribute : ScriptableRenderPipelineExtensionAttribute +{ + public LightingExplorerExtensionAttribute(Type renderPipeline) + : base(renderPipeline) {} +} diff --git a/Editor/Mono/SceneModeWindows/LightingExplorerWindow.cs b/Editor/Mono/SceneModeWindows/LightingExplorerWindow.cs index 8dbd8ca957..40ce468a99 100644 --- a/Editor/Mono/SceneModeWindows/LightingExplorerWindow.cs +++ b/Editor/Mono/SceneModeWindows/LightingExplorerWindow.cs @@ -13,12 +13,6 @@ namespace UnityEditor //Attribute that should be deprecated in 2020.1 //Will be replaced by ScriptableRenderPipelineAttribute //Kept for package compatibility and user SRP compatibility at the moment - [AttributeUsage(AttributeTargets.Class)] - public class LightingExplorerExtensionAttribute : ScriptableRenderPipelineExtensionAttribute - { - public LightingExplorerExtensionAttribute(Type renderPipeline) - : base(renderPipeline) {} - } public interface ILightingExplorerExtension { @@ -59,6 +53,12 @@ void OnEnable() } void OnDisable() + { + OnDisableTabsAndExtension(); + EditorApplication.searchChanged -= Repaint; + } + + void OnDisableTabsAndExtension() { if (m_TableTabs != null) { @@ -72,8 +72,6 @@ void OnDisable() { m_CurrentLightingExplorerExtension.OnDisable(); } - - EditorApplication.searchChanged -= Repaint; } void OnInspectorUpdate() @@ -92,7 +90,7 @@ void OnSelectionChange() { if (i == (m_TableTabs.Length - 1)) // last tab containing materials { - int[] selectedIds = UnityEngine.Object.FindObjectsOfType().Where((MeshRenderer mr) => { + int[] selectedIds = UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsSortMode.InstanceID).Where((MeshRenderer mr) => { return Selection.instanceIDs.Contains(mr.gameObject.GetInstanceID()); }).SelectMany(meshRenderer => meshRenderer.sharedMaterials).Where((Material m) => { return m != null && (m.globalIlluminationFlags & MaterialGlobalIlluminationFlags.AnyEmissive) != 0; @@ -165,10 +163,7 @@ private void UpdateTabs() { m_CurrentSRPType = SRPType; - if (m_CurrentLightingExplorerExtension != null) - { - m_CurrentLightingExplorerExtension.OnDisable(); - } + OnDisableTabsAndExtension(); m_CurrentLightingExplorerExtension = GetLightExplorerExtension(SRPType); m_CurrentLightingExplorerExtension.OnEnable(); @@ -185,29 +180,23 @@ private void UpdateTabs() } } - private ILightingExplorerExtension GetDefaultLightingExplorerExtension() + ILightingExplorerExtension GetDefaultLightingExplorerExtension() { - if (s_DefaultLightingExplorerExtension == null) - { - s_DefaultLightingExplorerExtension = new DefaultLightingExplorerExtension(); - } - return s_DefaultLightingExplorerExtension; + return s_DefaultLightingExplorerExtension ??= new DefaultLightingExplorerExtension(); } - private ILightingExplorerExtension GetLightExplorerExtension(System.Type currentSRPType) + ILightingExplorerExtension GetLightExplorerExtension(Type currentSRPType) { if (currentSRPType == null) return GetDefaultLightingExplorerExtension(); - Type extensionType = RenderPipelineEditorUtility.FetchFirstCompatibleTypeUsingScriptableRenderPipelineExtension(); - if (extensionType != null) - { - ILightingExplorerExtension extension = (ILightingExplorerExtension)System.Activator.CreateInstance(extensionType); - return extension; - } + var extensionType = RenderPipelineEditorUtility.GetDerivedTypesSupportedOnCurrentPipeline().FirstOrDefault(); + if (extensionType == null) + return GetDefaultLightingExplorerExtension(); + var extension = (ILightingExplorerExtension) Activator.CreateInstance(extensionType); + return extension; // no light explorer extension found for current srp, return the default one - return GetDefaultLightingExplorerExtension(); } } } diff --git a/Editor/Mono/SceneModeWindows/LightingWindow.cs b/Editor/Mono/SceneModeWindows/LightingWindow.cs index 31a207d042..7965c357a6 100644 --- a/Editor/Mono/SceneModeWindows/LightingWindow.cs +++ b/Editor/Mono/SceneModeWindows/LightingWindow.cs @@ -63,7 +63,7 @@ enum Mode BakedLightmaps } - const string kGlobalIlluminationUnityManualPage = "file:///unity/Manual/GlobalIllumination.html"; + const string kGlobalIlluminationUnityManualPage = "https://docs.unity3d.com/Manual/lighting-window.html"; int m_SelectedModeIndex = 0; List m_Modes = null; @@ -124,6 +124,18 @@ internal void SetSelectedTabIndex(int index) } } + // Repaint when MRays/sec changes + float m_LastRepaintedMraysPerSec; + protected void Update() + { + float totalNow = Lightmapping.GetLightmapBakePerformanceTotal(); + if (Math.Abs(totalNow - m_LastRepaintedMraysPerSec) < s_MraysPerSecRepaintThreshold) + return; + + m_LastRepaintedMraysPerSec = totalNow; + Repaint(); + } + void OnEnable() { s_Window = this; @@ -428,19 +440,6 @@ void BakeDropDownCallback(object data) } } - private bool IsPackageUsed(string packageName) - { - PackageManager.PackageInfo[] allInfo = PackageManager.PackageInfo.GetAllRegisteredPackages(); - foreach (PackageManager.PackageInfo info in allInfo) - { - if (info.name == packageName) - { - return true; - } - } - return false; - } - void Buttons(Mode selectedMode) { using (new EditorGUI.DisabledScope(EditorApplication.isPlayingOrWillChangePlaymode)) @@ -450,42 +449,39 @@ void Buttons(Mode selectedMode) EditorGUILayout.HelpBox(Lightmapping.lightingDataAsset.validityErrorMessage, MessageType.Warning); } - bool entitiesPackage = IsPackageUsed("com.unity.entities"); + bool isEntitiesPackageUsed = PackageManager.PackageInfo.IsPackageRegistered("com.unity.entities"); + bool iterativeInLightingSettings = (m_WorkflowMode.intValue == (int)Lightmapping.GIWorkflowMode.Iterative); - if (entitiesPackage) - EditorGUILayout.HelpBox("Auto Generate mode is unavailable when the Entities package is installed. When you generate lighting in a project where you have installed the Entities package, the Unity Editor opens all loaded subscenes. This may slow down Editor performance.", MessageType.Warning); + if (isEntitiesPackageUsed && iterativeInLightingSettings) + EditorGUILayout.HelpBox("Unity ignores Auto Generate mode when the Entities package is installed. When you generate lighting in a project where you have installed the Entities package, the Unity Editor opens all loaded subscenes. This may slow down Editor performance.", MessageType.Warning); EditorGUILayout.Space(); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); - bool iterative = (m_WorkflowMode.intValue == (int)Lightmapping.GIWorkflowMode.Iterative); - Rect rect = GUILayoutUtility.GetRect(Styles.continuousBakeLabel, GUIStyle.none); EditorGUI.BeginProperty(rect, Styles.continuousBakeLabel, m_WorkflowMode); // Auto Generate checkbox. EditorGUI.BeginChangeCheck(); - using (new EditorGUI.DisabledScope(m_LightingSettingsReadOnlyMode || entitiesPackage)) + using (new EditorGUI.DisabledScope(m_LightingSettingsReadOnlyMode)) { - if (entitiesPackage) - iterative = false; - - iterative = GUILayout.Toggle(iterative, Styles.continuousBakeLabel); + iterativeInLightingSettings = GUILayout.Toggle(iterativeInLightingSettings, Styles.continuousBakeLabel); } if (EditorGUI.EndChangeCheck()) { - m_WorkflowMode.intValue = (int)(iterative ? Lightmapping.GIWorkflowMode.Iterative : Lightmapping.GIWorkflowMode.OnDemand); + m_WorkflowMode.intValue = (int)(iterativeInLightingSettings ? Lightmapping.GIWorkflowMode.Iterative : Lightmapping.GIWorkflowMode.OnDemand); } EditorGUI.EndProperty(); - using (new EditorGUI.DisabledScope(iterative)) + bool resultingIterative = iterativeInLightingSettings && !isEntitiesPackageUsed; + using (new EditorGUI.DisabledScope(resultingIterative)) { // Bake button if we are not currently baking - bool showBakeButton = iterative || !Lightmapping.isRunning; + bool showBakeButton = resultingIterative || !Lightmapping.isRunning; if (showBakeButton) { var customTab = m_Tabs[selectedMode] as LightingWindowTab; @@ -504,11 +500,6 @@ void Buttons(Mode selectedMode) else { var settings = Lightmapping.GetLightingSettingsOrDefaultsFallback(); - // Only show Force Stop when using the PathTracer backend - if (settings.bakedGI && GUILayout.Button("Force Stop", GUILayout.Width(Styles.ButtonWidth))) - { - Lightmapping.ForceStop(); - } if (GUILayout.Button("Cancel", GUILayout.Width(Styles.ButtonWidth))) { Lightmapping.Cancel(); @@ -539,143 +530,187 @@ private void DoBakeReflectionProbes() internal static void Summary() { - long totalMemorySize = 0; - int lightmapCount = 0; - Dictionary sizes = new Dictionary(); - bool directionalLightmapsMode = false; - bool shadowmaskMode = false; - foreach (LightmapData ld in LightmapSettings.lightmaps) - { - if (ld.lightmapColor == null) - continue; - lightmapCount++; + bool autoGenerate = Lightmapping.GetLightingSettingsOrDefaultsFallback().autoGenerate; - Vector2 texSize = new Vector2(ld.lightmapColor.width, ld.lightmapColor.height); - if (sizes.ContainsKey(texSize)) - sizes[texSize]++; - else - sizes.Add(texSize, 1); - - totalMemorySize += TextureUtil.GetStorageMemorySizeLong(ld.lightmapColor); - if (ld.lightmapDir) + // Show the number of lightmaps: + { + int lightmapCount = 0; + long totalMemorySize = 0; + StringBuilder sizesString = new(); + var sizes = new Dictionary(); + if (!autoGenerate && Lightmapping.isRunning) // These are the lightmaps that will be baked or is being baked. { - totalMemorySize += TextureUtil.GetStorageMemorySizeLong(ld.lightmapDir); - directionalLightmapsMode = true; + RunningBakeInfo info = Lightmapping.GetRunningBakeInfo(); + lightmapCount = info.lightmapSizes.Length; + var probesCount = info.probePositions; + string lightmapsPlural = lightmapCount != 1 ? "s" : string.Empty; + string probesPlural = probesCount != 1 ? "s" : string.Empty; + string probesString = probesCount != 0 ? $"{probesCount} probe{probesPlural} and " : string.Empty; + sizesString.Append($"Baking {probesString}{lightmapCount} lightmap{lightmapsPlural}"); + + foreach (var ld in info.lightmapSizes) + if (sizes.ContainsKey(ld)) + sizes[ld]++; + else + sizes.Add(ld, 1); } - if (ld.shadowMask) + else // These are the lightmaps that were baked last. { - totalMemorySize += TextureUtil.GetStorageMemorySizeLong(ld.shadowMask); - shadowmaskMode = true; + bool shadowmaskMode = false; + foreach (LightmapData ld in LightmapSettings.lightmaps) + { + if (ld.lightmapColor == null) + continue; + lightmapCount++; + + LightmapSize ls = new() { width = ld.lightmapColor.width, height = ld.lightmapColor.height}; + if (sizes.ContainsKey(ls)) + sizes[ls]++; + else + sizes.Add(ls, 1); + + totalMemorySize += TextureUtil.GetStorageMemorySizeLong(ld.lightmapColor); + if (ld.lightmapDir) + totalMemorySize += TextureUtil.GetStorageMemorySizeLong(ld.lightmapDir); + + if (ld.shadowMask) + { + totalMemorySize += TextureUtil.GetStorageMemorySizeLong(ld.shadowMask); + shadowmaskMode = true; + } + } + + sizesString.Append(lightmapCount); + sizesString.Append(" lightmap"); + if (lightmapCount != 1) sizesString.Append("s"); + if (shadowmaskMode) + { + sizesString.Append(" with Shadowmask"); + if (lightmapCount != 1) sizesString.Append("s"); + } } - } - StringBuilder sizesString = new StringBuilder(); - sizesString.Append(lightmapCount); - sizesString.Append((directionalLightmapsMode ? " Directional" : " Non-Directional")); - sizesString.Append(" Lightmap"); - if (lightmapCount != 1) sizesString.Append("s"); - if (shadowmaskMode) - { - sizesString.Append(" with Shadowmask"); - if (lightmapCount != 1) sizesString.Append("s"); - } - bool first = true; - foreach (var s in sizes) - { - sizesString.Append(first ? ": " : ", "); - first = false; - if (s.Value > 1) + bool first = true; + foreach (KeyValuePair s in sizes) { - sizesString.Append(s.Value); + sizesString.Append(first ? ": " : ", "); + first = false; + if (s.Value > 1) + { + sizesString.Append(s.Value); + sizesString.Append("x"); + } + + sizesString.Append(s.Key.width.ToString(CultureInfo.InvariantCulture.NumberFormat)); sizesString.Append("x"); + sizesString.Append(s.Key.height.ToString(CultureInfo.InvariantCulture.NumberFormat)); + sizesString.Append("px"); } - sizesString.Append(s.Key.x.ToString(CultureInfo.InvariantCulture.NumberFormat)); - sizesString.Append("x"); - sizesString.Append(s.Key.y.ToString(CultureInfo.InvariantCulture.NumberFormat)); - sizesString.Append("px"); - } - sizesString.Append(" "); - GUILayout.BeginHorizontal(); + sizesString.Append(" "); - GUILayout.BeginVertical(); - GUILayout.Label(sizesString.ToString(), Styles.labelStyle); - GUILayout.EndVertical(); + GUILayout.BeginHorizontal(); - GUILayout.BeginVertical(); - GUILayout.Label(EditorUtility.FormatBytes(totalMemorySize), Styles.labelStyle); - GUILayout.Label((lightmapCount == 0 ? "No Lightmaps" : ""), Styles.labelStyle); - GUILayout.EndVertical(); + GUILayout.BeginVertical(); + GUILayout.Label(sizesString.ToString(), Styles.labelStyle); + GUILayout.EndVertical(); - GUILayout.EndHorizontal(); + if (totalMemorySize != 0) + { + GUILayout.BeginVertical(); + GUILayout.Label(EditorUtility.FormatBytes(totalMemorySize), Styles.labelStyle); + GUILayout.Label((lightmapCount == 0 ? "No Lightmaps" : ""), Styles.labelStyle); + GUILayout.EndVertical(); + } + + GUILayout.EndHorizontal(); + } GUILayout.BeginVertical(); - GUILayout.Label("Occupied Texels: " + InternalEditorUtility.CountToString(Lightmapping.occupiedTexelCount), Styles.labelStyle); - if (Lightmapping.isRunning) + if (autoGenerate) { - int numLightmapsInView = 0; - int numConvergedLightmapsInView = 0; - int numNotConvergedLightmapsInView = 0; + GUILayout.Label("Occupied Texels: " + InternalEditorUtility.CountToString(Lightmapping.occupiedTexelCount), Styles.labelStyle); + if (Lightmapping.isRunning) + { + int numLightmapsInView = 0; + int numConvergedLightmapsInView = 0; + int numNotConvergedLightmapsInView = 0; - int numLightmapsNotInView = 0; - int numConvergedLightmapsNotInView = 0; - int numNotConvergedLightmapsNotInView = 0; + int numLightmapsNotInView = 0; + int numConvergedLightmapsNotInView = 0; + int numNotConvergedLightmapsNotInView = 0; - int numInvalidConvergenceLightmaps = 0; - int numLightmaps = LightmapSettings.lightmaps.Length; - for (int i = 0; i < numLightmaps; ++i) - { - LightmapConvergence lc = Lightmapping.GetLightmapConvergence(i); - if (!lc.IsValid()) + int numInvalidConvergenceLightmaps = 0; + int numLightmaps = LightmapSettings.lightmaps.Length; + for (int i = 0; i < numLightmaps; ++i) { - numInvalidConvergenceLightmaps++; - continue; - } + LightmapConvergence lc = Lightmapping.GetLightmapConvergence(i); + if (!lc.IsValid()) + { + numInvalidConvergenceLightmaps++; + continue; + } - if (Lightmapping.GetVisibleTexelCount(i) > 0) - { - numLightmapsInView++; - if (lc.IsConverged()) - numConvergedLightmapsInView++; + if (Lightmapping.GetVisibleTexelCount(i) > 0) + { + numLightmapsInView++; + if (lc.IsConverged()) + numConvergedLightmapsInView++; + else + numNotConvergedLightmapsInView++; + } else - numNotConvergedLightmapsInView++; + { + numLightmapsNotInView++; + if (lc.IsConverged()) + numConvergedLightmapsNotInView++; + else + numNotConvergedLightmapsNotInView++; + } } - else + if (Lightmapping.atlasCount > 0) { - numLightmapsNotInView++; - if (lc.IsConverged()) - numConvergedLightmapsNotInView++; - else - numNotConvergedLightmapsNotInView++; + int convergedMaps = numConvergedLightmapsInView + numConvergedLightmapsNotInView; + GUILayout.Label("Lightmap convergence: (" + convergedMaps + "/" + Lightmapping.atlasCount + ")", Styles.labelStyle); } + EditorGUILayout.LabelField("Lightmaps in view: " + numLightmapsInView, Styles.labelStyle); + EditorGUI.indentLevel += 1; + EditorGUILayout.LabelField("Converged: " + numConvergedLightmapsInView, Styles.labelStyle); + EditorGUILayout.LabelField("Not Converged: " + numNotConvergedLightmapsInView, Styles.labelStyle); + EditorGUI.indentLevel -= 1; + EditorGUILayout.LabelField("Lightmaps not in view: " + numLightmapsNotInView, Styles.labelStyle); + EditorGUI.indentLevel += 1; + EditorGUILayout.LabelField("Converged: " + numConvergedLightmapsNotInView, Styles.labelStyle); + EditorGUILayout.LabelField("Not Converged: " + numNotConvergedLightmapsNotInView, Styles.labelStyle); + EditorGUI.indentLevel -= 1; + + LightProbesConvergence lpc = Lightmapping.GetLightProbesConvergence(); + if (lpc.IsValid() && lpc.probeSetCount > 0) + GUILayout.Label("Light Probes convergence: (" + lpc.convergedProbeSetCount + "/" + lpc.probeSetCount + ")", Styles.labelStyle); } - if (Lightmapping.atlasCount > 0) + float bakeTime = Lightmapping.GetLightmapBakeTimeTotal(); + } + + // We show baking device and performance even when not baking, so the user can see the information after a long bake: + { + string deviceName = Lightmapping.GetLightmapBakeGPUDeviceName(); + if (deviceName.Length > 0) + GUILayout.Label("Baking device: " + deviceName, Styles.labelStyle); + + float mraysPerSec = Lightmapping.GetLightmapBakePerformanceTotal(); { - int convergedMaps = numConvergedLightmapsInView + numConvergedLightmapsNotInView; - GUILayout.Label("Lightmap convergence: (" + convergedMaps + "/" + Lightmapping.atlasCount + ")", Styles.labelStyle); + string text; + if (mraysPerSec >= 0.0) + text = @$"Bake Performance: {mraysPerSec.ToString("0.00", CultureInfo.InvariantCulture.NumberFormat)} mrays/sec"; + else + text = ""; + GUILayout.Label(text, Styles.labelStyle); } - EditorGUILayout.LabelField("Lightmaps in view: " + numLightmapsInView, Styles.labelStyle); - EditorGUI.indentLevel += 1; - EditorGUILayout.LabelField("Converged: " + numConvergedLightmapsInView, Styles.labelStyle); - EditorGUILayout.LabelField("Not Converged: " + numNotConvergedLightmapsInView, Styles.labelStyle); - EditorGUI.indentLevel -= 1; - EditorGUILayout.LabelField("Lightmaps not in view: " + numLightmapsNotInView, Styles.labelStyle); - EditorGUI.indentLevel += 1; - EditorGUILayout.LabelField("Converged: " + numConvergedLightmapsNotInView, Styles.labelStyle); - EditorGUILayout.LabelField("Not Converged: " + numNotConvergedLightmapsNotInView, Styles.labelStyle); - EditorGUI.indentLevel -= 1; - - LightProbesConvergence lpc = Lightmapping.GetLightProbesConvergence(); - if (lpc.IsValid() && lpc.probeSetCount > 0) - GUILayout.Label("Light Probes convergence: (" + lpc.convergedProbeSetCount + "/" + lpc.probeSetCount + ")", Styles.labelStyle); } - float bakeTime = Lightmapping.GetLightmapBakeTimeTotal(); - float mraysPerSec = Lightmapping.GetLightmapBakePerformanceTotal(); - if (mraysPerSec >= 0.0) - GUILayout.Label("Bake Performance: " + mraysPerSec.ToString("0.00", CultureInfo.InvariantCulture.NumberFormat) + " mrays/sec", Styles.labelStyle); + if (!Lightmapping.isRunning) { - float bakeTimeRaw = Lightmapping.GetLightmapBakeTimeRaw(); + float bakeTime = Lightmapping.GetLightmapBakeTimeTotal(); if (bakeTime >= 0.0) { int time = (int)bakeTime; @@ -684,37 +719,19 @@ internal static void Summary() int timeM = time / 60; time -= 60 * timeM; int timeS = time; + int decimalPart = (int)(bakeTime % 1 * 100); - int timeRaw = (int)bakeTimeRaw; - int timeRawH = timeRaw / 3600; - timeRaw -= 3600 * timeRawH; - int timeRawM = timeRaw / 60; - timeRaw -= 60 * timeRawM; - int timeRawS = timeRaw; - - int oHeadTime = Math.Max(0, (int)(bakeTime - bakeTimeRaw)); - int oHeadTimeH = oHeadTime / 3600; - oHeadTime -= 3600 * oHeadTimeH; - int oHeadTimeM = oHeadTime / 60; - oHeadTime -= 60 * oHeadTimeM; - int oHeadTimeS = oHeadTime; - - - GUILayout.Label("Total Bake Time: " + timeH.ToString("0") + ":" + timeM.ToString("00") + ":" + timeS.ToString("00"), Styles.labelStyle); - if (Unsupported.IsDeveloperBuild()) - GUILayout.Label("(Raw Bake Time: " + timeRawH.ToString("0") + ":" + timeRawM.ToString("00") + ":" + timeRawS.ToString("00") + ", Overhead: " + oHeadTimeH.ToString("0") + ":" + oHeadTimeM.ToString("00") + ":" + oHeadTimeS.ToString("00") + ")", Styles.labelStyle); + GUILayout.Label($"Total Bake Time: {timeH:00}:{timeM:00}:{timeS:00}.{decimalPart:00}", Styles.labelStyle); } } - string deviceName = Lightmapping.GetLightmapBakeGPUDeviceName(); - if (deviceName.Length > 0) - GUILayout.Label("Baking device: " + deviceName, Styles.labelStyle); GUILayout.EndVertical(); } internal static LightingWindow s_Window; + private static readonly double s_MraysPerSecRepaintThreshold = 0.01; internal static bool isShown => s_Window && !s_Window.docked; - [MenuItem("Window/Rendering/Lighting", false, 1)] + [MenuItem("Window/Rendering/Lighting %9", false, 1)] internal static void CreateLightingWindow() { LightingWindow window = EditorWindow.GetWindow(); diff --git a/Editor/Mono/SceneModeWindows/LightingWindowEnvironmentTab.cs b/Editor/Mono/SceneModeWindows/LightingWindowEnvironmentTab.cs index c679494dca..a4426b2487 100644 --- a/Editor/Mono/SceneModeWindows/LightingWindowEnvironmentTab.cs +++ b/Editor/Mono/SceneModeWindows/LightingWindowEnvironmentTab.cs @@ -3,6 +3,7 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; +using System.Linq; using Object = UnityEngine.Object; using UnityEngine; using UnityEngine.Rendering; @@ -79,7 +80,7 @@ LightingWindowEnvironmentSection environmentEditor { get { - var currentSRP = GraphicsSettings.currentRenderPipeline?.GetType(); + var currentSRP = GraphicsSettings.currentRenderPipelineAssetType; if (m_EnvironmentSection != null && m_SRP != currentSRP) { m_SRP = currentSRP; @@ -89,9 +90,7 @@ LightingWindowEnvironmentSection environmentEditor if (m_EnvironmentSection == null) { - Type extensionType = RenderPipelineEditorUtility.FetchFirstCompatibleTypeUsingScriptableRenderPipelineExtension(); - if (extensionType == null) - extensionType = typeof(DefaultEnvironmentSectionExtension); + var extensionType = RenderPipelineEditorUtility.GetDerivedTypesSupportedOnCurrentPipeline().FirstOrDefault() ?? typeof(DefaultEnvironmentSectionExtension); LightingWindowEnvironmentSection extension = (LightingWindowEnvironmentSection)Activator.CreateInstance(extensionType); m_EnvironmentSection = extension; m_EnvironmentSection.OnEnable(); @@ -129,7 +128,7 @@ Editor otherRenderingEditor public void OnEnable() { - m_SRP = GraphicsSettings.currentRenderPipeline?.GetType(); + m_SRP = GraphicsSettings.currentRenderPipelineAssetType; m_ShowOtherSettings = new SavedBool($"LightingWindow.ShowOtherSettings", true); } diff --git a/Editor/Mono/SceneModeWindows/LightingWindowLightingTab.cs b/Editor/Mono/SceneModeWindows/LightingWindowLightingTab.cs index 1f001c3b91..f8bc2140d9 100644 --- a/Editor/Mono/SceneModeWindows/LightingWindowLightingTab.cs +++ b/Editor/Mono/SceneModeWindows/LightingWindowLightingTab.cs @@ -32,6 +32,7 @@ class Styles public static readonly GUIContent displayOcclusion = EditorGUIUtility.TrTextContent("Display Occlusion"); public static readonly GUIContent highlightInvalidCells = EditorGUIUtility.TrTextContent("Highlight Invalid Cells", "Highlight the invalid cells that cannot be used for probe interpolation."); public static readonly GUIContent progressiveGPUBakingDevice = EditorGUIUtility.TrTextContent("GPU Baking Device", "Will list all available GPU devices."); + public static readonly GUIContent gpuBakingProfile = EditorGUIUtility.TrTextContent("GPU Baking Profile", "The profile chosen for trading off between performance and memory usage when baking using the GPU."); public static readonly GUIContent progressiveGPUChangeWarning = EditorGUIUtility.TrTextContent("Changing the compute device used by the Progressive GPU Lightmapper requires the editor to be relaunched. Do you want to change device and restart?"); public static readonly GUIContent concurrentJobs = EditorGUIUtility.TrTextContent("Concurrent Jobs", "The amount of simultaneously scheduled jobs."); public static readonly GUIContent progressiveGPUUnknownDeviceInfo = EditorGUIUtility.TrTextContent("No devices found. Please start an initial bake to make this information available."); @@ -50,13 +51,26 @@ class Styles EditorGUIUtility.TrTextContent("Low"), EditorGUIUtility.TrTextContent("High") }; + + // Keep in sync with BakingProfile.h::BakingProfile + public static readonly int bakingProfileDefault = 2; + public static readonly int[] bakingProfileValues = {0, 1, 2, 3, 4}; + public static readonly GUIContent[] bakingProfileStrings = + { + EditorGUIUtility.TrTextContent("Highest Performance"), + EditorGUIUtility.TrTextContent("High Performance"), + EditorGUIUtility.TrTextContent("Automatic"), + EditorGUIUtility.TrTextContent("Low Memory Usage"), + EditorGUIUtility.TrTextContent("Lowest Memory Usage"), + }; } SavedBool m_ShowLightingSettings; SavedBool m_ShowWorkflowSettings; SavedBool m_ShowProbeDebugSettings; Vector2 m_ScrollPosition = Vector2.zero; - string m_LightmappingDeviceIndex = "lightmappingDeviceIndex"; + string m_LightmappingDeviceIndexKey = "lightmappingDeviceIndex"; + string m_BakingProfileKey = "lightmappingBakingProfile"; LightingWindowBakeSettings m_BakeSettings; SerializedObject m_LightmapSettings; @@ -179,7 +193,7 @@ void WorkflowSettingsGUI() GUIContent[] lightmappingDeviceStrings = devicesAndPlatforms.Select(x => new GUIContent(x.name)).ToArray(); int bakingDeviceAndPlatform = -1; - string configDeviceAndPlatform = EditorUserSettings.GetConfigValue(m_LightmappingDeviceIndex); + string configDeviceAndPlatform = EditorUserSettings.GetConfigValue(m_LightmappingDeviceIndexKey); if (configDeviceAndPlatform != null) { bakingDeviceAndPlatform = Int32.Parse(configDeviceAndPlatform); @@ -199,7 +213,7 @@ void WorkflowSettingsGUI() { if (EditorUtility.DisplayDialog("Warning", Styles.progressiveGPUChangeWarning.text, "OK", "Cancel")) { - EditorUserSettings.SetConfigValue(m_LightmappingDeviceIndex, bakingDeviceAndPlatform.ToString()); + EditorUserSettings.SetConfigValue(m_LightmappingDeviceIndexKey, bakingDeviceAndPlatform.ToString()); DeviceAndPlatform selectedDeviceAndPlatform = devicesAndPlatforms[bakingDeviceAndPlatform]; EditorApplication.CloseAndRelaunch(new string[] { "-OpenCL-PlatformAndDeviceIndices", selectedDeviceAndPlatform.platformId.ToString(), selectedDeviceAndPlatform.deviceId.ToString() }); } @@ -215,23 +229,42 @@ void WorkflowSettingsGUI() EditorGUILayout.HelpBox(Styles.progressiveGPUUnknownDeviceInfo.text, MessageType.Info); } + + // Handle the baking profile setting + int bakingProfile = Styles.bakingProfileDefault; + string bakingProfileString = EditorUserSettings.GetConfigValue(m_BakingProfileKey); + if (bakingProfileString != null) + { + if (Int32.TryParse(bakingProfileString, out int bakingProfileStoredValue)) + { + const Int32 maxBakingProfile = 4; // Keep in sync with kMaxBakingProfile (C++). + if (bakingProfileStoredValue >= 0 && bakingProfileStoredValue <= maxBakingProfile) + bakingProfile = bakingProfileStoredValue; + } + } + bakingProfile = EditorGUILayout.IntPopup(Styles.gpuBakingProfile, bakingProfile, Styles.bakingProfileStrings, Styles.bakingProfileValues); + EditorUserSettings.SetConfigValue(m_BakingProfileKey, bakingProfile.ToString()); } - m_ShowProbeDebugSettings.value = EditorGUILayout.Foldout(m_ShowProbeDebugSettings.value, Styles.lightProbeVisualization, true); - if (m_ShowProbeDebugSettings.value) + if (!UnityEngine.Rendering.SupportedRenderingFeatures.active.overridesLightProbeSystem) { - EditorGUI.BeginChangeCheck(); + m_ShowProbeDebugSettings.value = EditorGUILayout.Foldout(m_ShowProbeDebugSettings.value, Styles.lightProbeVisualization, true); + + if (m_ShowProbeDebugSettings.value) + { + EditorGUI.BeginChangeCheck(); - EditorGUI.indentLevel++; - LightProbeVisualization.lightProbeVisualizationMode = (LightProbeVisualization.LightProbeVisualizationMode)EditorGUILayout.EnumPopup(LightProbeVisualization.lightProbeVisualizationMode); - LightProbeVisualization.showInterpolationWeights = EditorGUILayout.Toggle(Styles.displayWeights, LightProbeVisualization.showInterpolationWeights); - LightProbeVisualization.showOcclusions = EditorGUILayout.Toggle(Styles.displayOcclusion, LightProbeVisualization.showOcclusions); - LightProbeVisualization.highlightInvalidCells = EditorGUILayout.Toggle(Styles.highlightInvalidCells, LightProbeVisualization.highlightInvalidCells); - EditorGUI.indentLevel--; + EditorGUI.indentLevel++; + LightProbeVisualization.lightProbeVisualizationMode = (LightProbeVisualization.LightProbeVisualizationMode)EditorGUILayout.EnumPopup(LightProbeVisualization.lightProbeVisualizationMode); + LightProbeVisualization.showInterpolationWeights = EditorGUILayout.Toggle(Styles.displayWeights, LightProbeVisualization.showInterpolationWeights); + LightProbeVisualization.showOcclusions = EditorGUILayout.Toggle(Styles.displayOcclusion, LightProbeVisualization.showOcclusions); + LightProbeVisualization.highlightInvalidCells = EditorGUILayout.Toggle(Styles.highlightInvalidCells, LightProbeVisualization.highlightInvalidCells); + EditorGUI.indentLevel--; - if (EditorGUI.EndChangeCheck()) - EditorApplication.SetSceneRepaintDirty(); + if (EditorGUI.EndChangeCheck()) + EditorApplication.SetSceneRepaintDirty(); + } } // If either auto ambient or auto reflection baking is supported, show the SkyManager toggle. diff --git a/Editor/Mono/SceneModeWindows/LightingWindowLightmapPreviewTab.cs b/Editor/Mono/SceneModeWindows/LightingWindowLightmapPreviewTab.cs index 2f50599f2c..9a8d8d1820 100644 --- a/Editor/Mono/SceneModeWindows/LightingWindowLightmapPreviewTab.cs +++ b/Editor/Mono/SceneModeWindows/LightingWindowLightmapPreviewTab.cs @@ -176,7 +176,6 @@ private void LightmapListGUI(LightmapData[] lightmaps, VisualisationGITexture[] } GUILayout.EndVertical(); - LightmapDebugInfo(i); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.Space(5); @@ -302,159 +301,10 @@ private void DebugInfoSection(LightmapData[] lightmaps) } } - { - Dictionary> gbufferHashToLightmapIndices = new Dictionary>(); - for (int i = 0; i < lightmaps.Length; i++) - { - Hash128 gbufferHash; - if (Lightmapping.GetGBufferHash(i, out gbufferHash)) - { - if (!gbufferHashToLightmapIndices.ContainsKey(gbufferHash)) - gbufferHashToLightmapIndices.Add(gbufferHash, new SortedList()); - - gbufferHashToLightmapIndices[gbufferHash].Add(i, i); - } - } - - float totalGBuffersSize = 0.0f; - float totalLightmapsSize = 0.0f; - - foreach (var entry in gbufferHashToLightmapIndices) - { - Hash128 gbufferHash = entry.Key; - float gbufferDataSize = Lightmapping.GetGBufferMemory(ref gbufferHash); - totalGBuffersSize += gbufferDataSize; - - SortedList lightmapIndices = entry.Value; - foreach (var i in lightmapIndices) - { - LightmapMemory lightmapMemory = Lightmapping.GetLightmapMemory(i.Value); - totalLightmapsSize += lightmapMemory.lightmapDataSizeCPU; - totalLightmapsSize += lightmapMemory.lightmapTexturesSize; - } - } - - string foldoutNameFull = String.Format( - "G-buffers ({0}) | Lightmaps ({1})", - SizeString(totalGBuffersSize), - SizeString(totalLightmapsSize)); - - if (lightmaps.Length > 0) - EditorGUILayout.FoldoutTitlebar(false, new GUIContent(foldoutNameFull), true); - } - - System.UInt64[] dummyCounts = new System.UInt64[0]; - - { - MemLabels labels = Lightmapping.GetLightProbeMemLabels(); - ShowObjectNamesSizesAndCounts("Light Probes", kEditorPrefsLightProbes, labels.labels, labels.sizes, dummyCounts); - } - - { - MemLabels labels = Lightmapping.GetTransmissionTexturesMemLabels(); - ShowObjectNamesSizesAndCounts("Transmission textures", kEditorPrefsTransmissionTextures, labels.labels, labels.sizes, dummyCounts); - } - - { - MemLabels labels = Lightmapping.GetMaterialTexturesMemLabels(); - ShowObjectNamesSizesAndCounts("Albedo/emissive textures", kEditorPrefsMaterialTextures, labels.labels, labels.sizes, dummyCounts); - } - - { - GeoMemLabels labels = Lightmapping.GetGeometryMemory(); - ShowObjectNamesSizesAndCounts("Geometry data", kEditorPrefsGeometryData, labels.labels, labels.sizes, labels.triCounts); - } - - { - // Note: this needs to go last. - // It simply shows all the memory labels that were not explicitly queried after the Lightmapping.ResetExplicitlyShownMemLabels() call. - MemLabels labels = Lightmapping.GetNotShownMemLabels(); - string remainingEntriesFoldoutName = Lightmapping.isProgressiveLightmapperDone ? "Leaks" : "In-flight"; - ShowObjectNamesSizesAndCounts(remainingEntriesFoldoutName, kEditorPrefsInFlight, labels.labels, labels.sizes, dummyCounts); - } - EditorGUILayout.Space(); EditorGUIUtility.labelWidth = oldWidth; } - private void LightmapDebugInfo(int index) - { - if (!showDebugInfo) - return; - - GUILayout.Space(5); - GUILayout.BeginVertical(); - - LightmapConvergence lc = Lightmapping.GetLightmapConvergence(index); - if (lc.IsValid()) - { - ulong occupiedTexels = (ulong)lc.occupiedTexelCount; - if (lc.tilingMode > 0) - { - // Make sure we display the total amount of occupied texels once lightmap is converged - // If tiling is on, and lightmap is not converged, display the occupied texel count in current tile - if (lc.IsConverged()) - { - GUILayout.Label("Occupied: " + InternalEditorUtility.CountToString(occupiedTexels), EditorStyles.miniLabel); - GUILayout.Label("Baked using " + lc.GetTileCount() + " tiles", EditorStyles.miniLabel); - } - else - { - occupiedTexels = (ulong)lc.occupiedTexelCountInCurrentTile; - GUILayout.Label("Occupied (in tile): " + InternalEditorUtility.CountToString(occupiedTexels), EditorStyles.miniLabel); - GUILayout.Label("Baking pass (#tile): " + (lc.tilingPassNum + 1) + "/" + lc.GetTileCount(), EditorStyles.miniLabel); - } - } - else - { - GUILayout.Label("Occupied: " + InternalEditorUtility.CountToString(occupiedTexels), EditorStyles.miniLabel); - } - GUIContent direct = EditorGUIUtility.TrTextContent("Direct: " + lc.minDirectSamples + " / " + lc.maxDirectSamples + " / " + lc.avgDirectSamples + "", "min / max / avg samples per texel"); - GUILayout.Label(direct, EditorStyles.miniLabel); - - GUIContent gi = EditorGUIUtility.TrTextContent("GI: " + lc.minGISamples + " / " + lc.maxGISamples + " / " + lc.avgGISamples + "", "min / max / avg samples per texel"); - GUILayout.Label(gi, EditorStyles.miniLabel); - - GUIContent env = EditorGUIUtility.TrTextContent("Environment: " + lc.minEnvSamples + " / " + lc.maxEnvSamples + " / " + lc.avgEnvSamples + "", "min / max / avg samples per texel"); - GUILayout.Label(env, EditorStyles.miniLabel); - } - else - { - GUILayout.Label("Occupied: N/A", EditorStyles.miniLabel); - GUILayout.Label("Direct: N/A", EditorStyles.miniLabel); - GUILayout.Label("GI: N/A", EditorStyles.miniLabel); - GUILayout.Label("Environment: N/A", EditorStyles.miniLabel); - } - float mraysPerSec = Lightmapping.GetLightmapBakePerformance(index); - if (mraysPerSec >= 0.0) - GUILayout.Label(mraysPerSec.ToString("0.00", CultureInfo.InvariantCulture.NumberFormat) + " mrays/sec", EditorStyles.miniLabel); - else - GUILayout.Label("N/A mrays/sec", EditorStyles.miniLabel); - - GUILayout.EndVertical(); - GUILayout.Space(5); - GUILayout.BeginVertical(); - - LightmapMemory lightmapMemory = Lightmapping.GetLightmapMemory(index); - GUILayout.Label("Lightmap data: " + SizeString(lightmapMemory.lightmapDataSizeCPU), EditorStyles.miniLabel); - - GUIContent lightmapTexturesSizeContent = null; - if (lightmapMemory.lightmapTexturesSize > 0.0f) - lightmapTexturesSizeContent = EditorGUIUtility.TrTextContent("Lightmap textures: " + SizeString(lightmapMemory.lightmapTexturesSize)); - else - lightmapTexturesSizeContent = EditorGUIUtility.TrTextContent("Lightmap textures: N/A", "This lightmap has converged and is not owned by the Progressive Lightmapper anymore."); - GUILayout.Label(lightmapTexturesSizeContent, EditorStyles.miniLabel); - - GUIContent GPUSizeContent = null; - if (lightmapMemory.lightmapDataSizeGPU > 0.0f) - GPUSizeContent = EditorGUIUtility.TrTextContent("GPU memory: " + SizeString(lightmapMemory.lightmapDataSizeGPU)); - else - GPUSizeContent = EditorGUIUtility.TrTextContent("GPU memory: N/A"); - GUILayout.Label(GPUSizeContent, EditorStyles.miniLabel); - - GUILayout.EndVertical(); - } - private void ShowObjectNamesSizesAndCounts(string foldoutName, string editorPrefsName, string[] objectNames, float[] sizes, System.UInt64[] counts) { Debug.Assert(objectNames.Length == sizes.Length); diff --git a/Editor/Mono/SceneModeWindows/TierSettingsWindow.cs b/Editor/Mono/SceneModeWindows/TierSettingsWindow.cs deleted file mode 100644 index c7d0776e5e..0000000000 --- a/Editor/Mono/SceneModeWindows/TierSettingsWindow.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Unity C# reference source -// Copyright (c) Unity Technologies. For terms of use, see -// https://unity3d.com/legal/licenses/Unity_Reference_Only_License - -using System; -using System.Collections.Generic; -using System.Linq; -using UnityEngine; - -using Object = UnityEngine.Object; -using EditorGraphicsSettings = UnityEditor.Rendering.EditorGraphicsSettings; -using TierSettingsEditor = UnityEditor.GraphicsSettingsWindow.TierSettingsEditor; - -namespace UnityEditor -{ - internal partial class TierSettingsWindow : EditorWindow - { - static TierSettingsWindow s_Instance; - static public void CreateWindow() - { - s_Instance = EditorWindow.GetWindow(); - s_Instance.minSize = new Vector2(600, 300); - s_Instance.titleContent = EditorGUIUtility.TrTextContent("Tier Settings"); - } - - internal static TierSettingsWindow GetInstance() - { - return s_Instance; - } - - Editor m_TierSettingsEditor; - - void OnEnable() - { - s_Instance = this; - } - - void OnDisable() - { - DestroyImmediate(m_TierSettingsEditor); m_TierSettingsEditor = null; - if (s_Instance == this) - s_Instance = null; - } - - Object graphicsSettings - { - get { return UnityEngine.Rendering.GraphicsSettings.GetGraphicsSettings(); } - } - Editor tierSettingsEditor - { - get - { - Editor.CreateCachedEditor(graphicsSettings, typeof(TierSettingsEditor), ref m_TierSettingsEditor); - ((TierSettingsEditor)m_TierSettingsEditor).verticalLayout = false; - return m_TierSettingsEditor; - } - } - - void OnGUI() - { - tierSettingsEditor.OnInspectorGUI(); - } - } -} diff --git a/Editor/Mono/SceneView/RectSelection.cs b/Editor/Mono/SceneView/RectSelection.cs index 0c81f7428c..6ca392e828 100644 --- a/Editor/Mono/SceneView/RectSelection.cs +++ b/Editor/Mono/SceneView/RectSelection.cs @@ -67,7 +67,7 @@ public void OnGUI() case EventType.MouseDrag: if (GUIUtility.hotControl == id) { - if (!m_RectSelecting && (mousePos - m_SelectStartPoint).magnitude > 6f) + if (!m_RectSelecting && (mousePos - m_SelectStartPoint).magnitude > 6f && Tools.s_LockedViewTool == ViewTool.None) { EditorApplication.modifierKeysChanged += SendCommandsOnModifierKeys; m_RectSelecting = true; @@ -277,7 +277,6 @@ static void UpdateSelection(Object[] existingSelection, Object[] newObjects, Sel Selection.objects = newObjects; break; } - GUIUtility.ExitGUI(); } // When rect selecting, we update the selected objects based on which modifier keys are currently held down, diff --git a/Editor/Mono/SceneView/SceneView.cs b/Editor/Mono/SceneView/SceneView.cs index b45f6b47c7..07fcdea0b3 100644 --- a/Editor/Mono/SceneView/SceneView.cs +++ b/Editor/Mono/SceneView/SceneView.cs @@ -151,6 +151,7 @@ private set public static Color selectedOutlineColor => kSceneViewSelectedOutline.Color; public bool isUsingSceneFiltering => UseSceneFiltering(); + internal static SavedBool s_PreferenceIgnoreAlwaysRefreshWhenNotFocused = new SavedBool("SceneView.ignoreAlwaysRefreshWhenNotFocused", false); internal static SavedBool s_PreferenceEnableFilteringWhileSearching = new SavedBool("SceneView.enableFilteringWhileSearching", true); internal static SavedBool s_PreferenceEnableFilteringWhileLodGroupEditing = new SavedBool("SceneView.enableFilteringWhileLodGroupEditing", true); @@ -400,11 +401,18 @@ public bool alwaysRefresh set => m_AlwaysRefresh = value; } + public bool showClouds + { + get => m_ShowClouds; + set => m_ShowClouds = value; + } + public bool showSkybox = true; public bool showFlares = true; public bool showImageEffects = true; public bool showParticleSystems = true; public bool showVisualEffectGraphs = true; + bool m_ShowClouds = true; public bool fogEnabled => fxEnabled && showFog; // marked obsolete by @karlh 2020/4/14 @@ -413,6 +421,7 @@ public bool alwaysRefresh public bool materialUpdateEnabled => alwaysRefreshEnabled; public bool alwaysRefreshEnabled => fxEnabled && alwaysRefresh; public bool skyboxEnabled => fxEnabled && showSkybox; + public bool cloudsEnabled => fxEnabled && showClouds; public bool flaresEnabled => fxEnabled && showFlares; public bool imageEffectsEnabled => fxEnabled && showImageEffects; public bool particleSystemsEnabled => fxEnabled && showParticleSystems; @@ -433,6 +442,7 @@ public SceneViewState(SceneViewState other) showFog = other.showFog; alwaysRefresh = other.alwaysRefresh; showSkybox = other.showSkybox; + showClouds = other.showClouds; showFlares = other.showFlares; showImageEffects = other.showImageEffects; showParticleSystems = other.showParticleSystems; @@ -449,7 +459,7 @@ public bool allEnabled { get { - bool all = showFog && alwaysRefresh && showSkybox && showFlares && showImageEffects && showParticleSystems; + bool all = showFog && alwaysRefresh && showSkybox && showClouds && showFlares && showImageEffects && showParticleSystems; if (UnityEngine.VFX.VFXManager.activateVFX) all = all && showVisualEffectGraphs; return all; @@ -467,6 +477,7 @@ public void SetAllEnabled(bool value) showFog = value; alwaysRefresh = value; showSkybox = value; + showClouds = value; showFlares = value; showImageEffects = value; showParticleSystems = value; @@ -1097,6 +1108,26 @@ internal bool viewIsLockedToObject } [RequiredByNativeCode] + static void FrameSelectedMenuItem(bool locked) + { + var command = locked ? EventCommandNames.FrameSelectedWithLock : EventCommandNames.FrameSelected; + + var win = focusedWindow; + var ret = win != null && win.SendEvent(EditorGUIUtility.CommandEvent(command)); + + if (!ret) + { + win = mouseOverWindow; + ret = win != null && win.SendEvent(EditorGUIUtility.CommandEvent(command)); + } + + // if no hovered or focused window used the frame command, send the command to the last active scene view. + // as a special case, if the window that used the frame command was hierarchy, also send a frame event to + // the last active scene view. + if ((!ret || win is SceneHierarchyWindow) && lastActiveSceneView != null) + lastActiveSceneView.SendEvent(EditorGUIUtility.CommandEvent(command)); + } + public static bool FrameLastActiveSceneView() { if (lastActiveSceneView == null) @@ -1104,7 +1135,6 @@ public static bool FrameLastActiveSceneView() return lastActiveSceneView.SendEvent(EditorGUIUtility.CommandEvent(EventCommandNames.FrameSelected)); } - [RequiredByNativeCode] public static bool FrameLastActiveSceneViewWithLock() { if (lastActiveSceneView == null) @@ -1303,7 +1333,8 @@ VisualElement CreateCameraRectVisualElement() name = s_CameraRectVisualElementName, pickingMode = PickingMode.Position, viewDataKey = name, - renderHints = RenderHints.ClipWithScissors + renderHints = RenderHints.ClipWithScissors, + requireMeasureFunction = false }; UIElementsEditorUtility.AddDefaultEditorStyleSheets(root); @@ -1557,7 +1588,7 @@ void RefreshAudioPlay() } else { - if (!source.isPlaying) + if (!source.isPlaying && source.isActiveAndEnabled) source.Play(); } } @@ -1891,8 +1922,8 @@ private void CreateCameraTargetTexture(Rect cameraRect, bool hdr) } Rect actualCameraRect = Handles.GetCameraRect(cameraRect); - int width = (int)actualCameraRect.width; - int height = (int)actualCameraRect.height; + int width = (int)Mathf.Max(1f, actualCameraRect.width); + int height = (int)Mathf.Max(1f, actualCameraRect.height); if (m_SceneTargetTexture == null) { @@ -2309,7 +2340,7 @@ void DoOnPreSceneGUICallbacks(Rect cameraRect) { // Don't do callbacks in search mode, as editors calling Handles.BeginGUI // will break camera setup. - if (UseSceneFiltering()) + if (hasSearchFilter) return; CallOnPreSceneGUI(); @@ -2948,8 +2979,19 @@ internal bool CheckDrawModeForRenderingPath(DrawCameraMode mode) return true; } - private void SetSceneCameraHDRAndDepthModes() + private void UpdateSceneCameraSettings() { + var mainCamera = GetMainCamera(); + + // update physical camera properties + if (mainCamera != null) + { + m_Camera.iso = mainCamera.iso; + m_Camera.shutterSpeed = mainCamera.shutterSpeed; + m_Camera.aperture = mainCamera.aperture; + m_Camera.anamorphism = mainCamera.anamorphism; + } + if (!m_SceneIsLit || !DoesCameraDrawModeSupportHDR(m_CameraMode.drawMode)) { m_Camera.allowHDR = false; @@ -2957,7 +2999,7 @@ private void SetSceneCameraHDRAndDepthModes() m_Camera.clearStencilAfterLightingPass = false; return; } - var mainCamera = GetMainCamera(); + if (mainCamera == null) { m_Camera.allowHDR = false; @@ -2995,6 +3037,7 @@ void SetupCamera() ParticleSystemEditorUtils.renderInSceneView = m_SceneViewState.particleSystemsEnabled; UnityEngine.VFX.VFXManager.renderInSceneView = m_SceneViewState.visualEffectGraphsEnabled; SceneVisibilityManager.instance.enableSceneVisibility = m_SceneVisActive; + m_Camera.renderCloudsInSceneView = m_SceneViewState.cloudsEnabled; ResetIfNaN(); m_Camera.transform.rotation = m_2DMode && !m_Rotation.isAnimating ? Quaternion.identity : m_Rotation.value; @@ -3042,7 +3085,8 @@ void SetupCamera() m_Camera.renderingPath = GetSceneViewRenderingPath(); if (!CheckDrawModeForRenderingPath(m_CameraMode.drawMode)) m_CameraMode = GetBuiltinCameraMode(DrawCameraMode.Textured); - SetSceneCameraHDRAndDepthModes(); + + UpdateSceneCameraSettings(); if (m_CameraMode.drawMode == DrawCameraMode.Textured || m_CameraMode.drawMode == DrawCameraMode.TexturedWire || @@ -3103,8 +3147,17 @@ void OnBecameInvisible() void UpdateAnimatedMaterials() { var repaint = false; - if (m_lastRenderedTime + 0.033f < EditorApplication.timeSinceStartup) + + // Ensure that we in fact do want to paint when not in focus. + if (!EditorApplication.isFocused && s_PreferenceIgnoreAlwaysRefreshWhenNotFocused.value) + { + // We're going to capture this condition, so that it doesnt fall through. + } + else if (m_lastRenderedTime + 0.033f < EditorApplication.timeSinceStartup) + { repaint = sceneViewState.alwaysRefreshEnabled; + } + repaint |= LODUtility.IsLODAnimating(m_Camera); if (repaint) @@ -3456,7 +3509,7 @@ void CommandsGUI() case EventCommandNames.SelectAll: if (execute) { - var gameObjects = FindObjectsOfType(); + var gameObjects = FindObjectsByType(FindObjectsSortMode.InstanceID); var objs = new List(gameObjects.Length); foreach (var go in gameObjects) if (SceneVisibilityManager.instance.IsSelectable(go)) @@ -3473,7 +3526,9 @@ void CommandsGUI() break; case EventCommandNames.InvertSelection: if (execute) - Selection.objects = FindObjectsOfType().Except(Selection.gameObjects).Where(SceneVisibilityManager.instance.IsSelectable).ToArray(); + { + Selection.objects = FindObjectsByType(FindObjectsSortMode.InstanceID).Except(Selection.gameObjects).Where(SceneVisibilityManager.instance.IsSelectable).ToArray(); + } Event.current.Use(); break; case EventCommandNames.SelectChildren: @@ -3563,7 +3618,7 @@ internal bool IsGameObjectInThisSceneView(GameObject gameObject) if (gameObject == null) return false; - return StageUtility.IsGameObjectRenderedByCamera(gameObject, camera); + return !StageUtility.IsGizmoCulledBySceneCullingMasksOrFocusedScene(gameObject, camera); } public bool FrameSelected() @@ -4010,7 +4065,8 @@ void CopyLastActiveSceneViewSettings() m_CameraSettings = new CameraSettings(lastActiveSceneView.m_CameraSettings); m_2DMode = view.m_2DMode; pivot = view.pivot; - rotation = view.rotation; + if(!m_2DMode) + rotation = view.rotation; size = view.size; m_Ortho.value = view.orthographic; if (m_Grid == null) diff --git a/Editor/Mono/SceneView/SceneViewMotion.cs b/Editor/Mono/SceneView/SceneViewMotion.cs index 4102697171..e8d8482bca 100644 --- a/Editor/Mono/SceneView/SceneViewMotion.cs +++ b/Editor/Mono/SceneView/SceneViewMotion.cs @@ -11,6 +11,29 @@ namespace UnityEditor { internal static class SceneViewMotion { + const string k_TemporaryPanTool2D = "Scene View/Temporary Pan Tool for 2D Mode"; + const string k_TemporaryPanTool1 = "Scene View/Temporary Pan Tool 1"; + const string k_TemporaryPanTool2 = "Scene View/Temporary Pan Tool 2"; + const string k_TemporaryPanTool3 = "Scene View/Temporary Pan Tool 3"; + const string k_TemporaryPanTool4 = "Scene View/Temporary Pan Tool 4"; + const string k_TemporaryZoomTool1 = "Scene View/Temporary Zoom Tool 1"; + const string k_TemporaryZoomTool2 = "Scene View/Temporary Zoom Tool 2"; + const string k_TemporaryOrbitTool = "Scene View/Temporary Orbit Tool"; + const string k_TemporaryFpsTool = "Scene View/Temporary FPS Tool"; + + static readonly ShortcutEntry[] ViewToolShortcuts = + { + ShortcutIntegration.instance.directory.FindShortcutEntry(k_TemporaryPanTool2D), + ShortcutIntegration.instance.directory.FindShortcutEntry(k_TemporaryPanTool1), + ShortcutIntegration.instance.directory.FindShortcutEntry(k_TemporaryPanTool2), + ShortcutIntegration.instance.directory.FindShortcutEntry(k_TemporaryPanTool3), + ShortcutIntegration.instance.directory.FindShortcutEntry(k_TemporaryPanTool4), + ShortcutIntegration.instance.directory.FindShortcutEntry(k_TemporaryZoomTool1), + ShortcutIntegration.instance.directory.FindShortcutEntry(k_TemporaryZoomTool2), + ShortcutIntegration.instance.directory.FindShortcutEntry(k_TemporaryOrbitTool), + ShortcutIntegration.instance.directory.FindShortcutEntry(k_TemporaryFpsTool), + }; + [NonSerialized] static bool s_Initialized; static SceneView s_CurrentSceneView; // The SceneView that is calling OnGUI @@ -52,7 +75,8 @@ internal static bool viewToolActive { if (Event.current != null) UpdateViewToolState(Event.current); - return s_ViewToolActive; + + return s_ViewToolActive || Tools.current == Tool.View; } } internal static event Action viewToolActiveChanged; @@ -67,6 +91,7 @@ static void Init() s_Initialized = true; } + [ReserveModifiers(ShortcutModifiers.Shift)] class SceneViewViewport : IShortcutToolContext { public bool active => IsActive; @@ -75,51 +100,59 @@ public static bool IsActive { get { - var sceneViewFocus = EditorWindow.focusedWindow?.GetType() == typeof(SceneView); - + var sceneViewFocus = EditorWindow.focusedWindow is SceneView; return sceneViewFocus && s_ViewportsUnderMouse; } } } + [ReserveModifiers(ShortcutModifiers.Shift)] class SceneViewViewport2D : IShortcutToolContext { public bool active => SceneViewViewport.IsActive && ((SceneView.lastActiveSceneView?.in2DMode ?? false) || (SceneView.lastActiveSceneView?.isRotationLocked ?? false)); } + [ReserveModifiers(ShortcutModifiers.Shift)] class SceneViewViewport3D : IShortcutToolContext { public bool active => SceneViewViewport.IsActive && ((!SceneView.lastActiveSceneView?.in2DMode ?? false) && (!SceneView.lastActiveSceneView?.isRotationLocked ?? false)); } - [ClutchShortcut("Scene View/Temporary Pan Tool for 2D Mode", typeof(SceneViewViewport2D), KeyCode.Mouse1)] - [ClutchShortcut("Scene View/Temporary Pan Tool 1", typeof(SceneViewViewport), KeyCode.Mouse2)] - [ClutchShortcut("Scene View/Temporary Pan Tool 2", typeof(SceneViewViewport), KeyCode.Mouse2, ShortcutModifiers.Alt)] - [ClutchShortcut("Scene View/Temporary Pan Tool 3", typeof(SceneViewViewport), KeyCode.Mouse0, ShortcutModifiers.Action | ShortcutModifiers.Alt)] - [ClutchShortcut("Scene View/Temporary Pan Tool 4", typeof(SceneViewViewport), KeyCode.Mouse2, ShortcutModifiers.Action | ShortcutModifiers.Alt)] + [ClutchShortcut(k_TemporaryPanTool2D, typeof(SceneViewViewport2D), KeyCode.Mouse1)] + [ClutchShortcut(k_TemporaryPanTool1, typeof(SceneViewViewport), KeyCode.Mouse2)] + [ClutchShortcut(k_TemporaryPanTool2, typeof(SceneViewViewport), KeyCode.Mouse2, ShortcutModifiers.Alt)] + [ClutchShortcut(k_TemporaryPanTool3, typeof(SceneViewViewport), KeyCode.Mouse0, ShortcutModifiers.Action | ShortcutModifiers.Alt)] + [ClutchShortcut(k_TemporaryPanTool4, typeof(SceneViewViewport), KeyCode.Mouse2, ShortcutModifiers.Action | ShortcutModifiers.Alt)] static void TemporaryPan(ShortcutArguments args) { - if (args.stage == ShortcutStage.Begin) TemporaryTool(ViewTool.Pan); - else HandleMouseUp(s_CurrentSceneView, s_ViewToolID, 0, 0); + if (args.stage == ShortcutStage.Begin) + TemporaryTool(ViewTool.Pan); + else + HandleMouseUp(s_CurrentSceneView, s_ViewToolID, 0, 0); } - [ClutchShortcut("Scene View/Temporary Zoom Tool", typeof(SceneViewViewport), KeyCode.Mouse1, ShortcutModifiers.Alt)] + [ClutchShortcut(k_TemporaryZoomTool1, typeof(SceneViewViewport), KeyCode.Mouse1, ShortcutModifiers.Alt)] + [ClutchShortcut(k_TemporaryZoomTool2, typeof(SceneViewViewport), KeyCode.Mouse1, ShortcutModifiers.Action | ShortcutModifiers.Alt)] static void TemporaryZoom(ShortcutArguments args) { - if (args.stage == ShortcutStage.Begin) TemporaryTool(ViewTool.Zoom); - else HandleMouseUp(s_CurrentSceneView, s_ViewToolID, 0, 0); + if (args.stage == ShortcutStage.Begin) + TemporaryTool(ViewTool.Zoom); + else + HandleMouseUp(s_CurrentSceneView, s_ViewToolID, 0, 0); } - [ClutchShortcut("Scene View/Temporary Orbit Tool", typeof(SceneViewViewport), KeyCode.Mouse0, ShortcutModifiers.Alt)] + [ClutchShortcut(k_TemporaryOrbitTool, typeof(SceneViewViewport), KeyCode.Mouse0, ShortcutModifiers.Alt)] static void TemporaryOrbit(ShortcutArguments args) { - if (args.stage == ShortcutStage.Begin) TemporaryTool(ViewTool.Orbit); - else HandleMouseUp(s_CurrentSceneView, s_ViewToolID, 0, 0); + if (args.stage == ShortcutStage.Begin) + TemporaryTool(ViewTool.Orbit); + else + HandleMouseUp(s_CurrentSceneView, s_ViewToolID, 0, 0); } - [ClutchShortcut("Scene View/Temporary FPS Tool", typeof(SceneViewViewport3D), KeyCode.Mouse1)] + [ClutchShortcut(k_TemporaryFpsTool, typeof(SceneViewViewport3D), KeyCode.Mouse1)] static void TemporaryFPS(ShortcutArguments args) { if (args.stage == ShortcutStage.Begin) @@ -140,9 +173,14 @@ static void TemporaryTool(ViewTool tool) Tools.s_LockedViewTool = Tools.viewTool = tool; s_CurrentState = MotionState.kDragging; HandleMouseDown(SceneView.lastActiveSceneView, s_ViewToolID, Event.current?.button ?? 0); - UpdateViewToolState(Event.current); - if(Event.current != null) shortcutKey = Event.current.isMouse ? KeyCode.Mouse0 + Event.current.button : Event.current.keyCode; - else shortcutKey = KeyCode.None; + // We should only allow mouse jumping when the shortcuts are enabled. + EditorGUIUtility.SetWantsMouseJumping(1); + // Do not pass the event here to avoid string comparisons in the method as this is not needed here. + UpdateViewToolState(null); + if(Event.current != null) + shortcutKey = Event.current.isMouse ? KeyCode.Mouse0 + Event.current.button : Event.current.keyCode; + else + shortcutKey = KeyCode.None; } public static void DoViewTool(SceneView view) @@ -199,12 +237,21 @@ public static void DoViewTool(SceneView view) HandleMouseDrag(view, id); } - if (shortcutKey != KeyCode.None && Tools.viewTool != ViewTool.None) GUIUtility.hotControl = s_ViewToolID; + if (shortcutKey != KeyCode.None && Tools.viewTool != ViewTool.None) + GUIUtility.hotControl = s_ViewToolID; } static void UpdateViewToolState(Event evt) { - bool shouldBeActive = Tools.s_LockedViewTool != ViewTool.None; + bool shouldBeActive = false; + if (evt?.type == EventType.MouseDown) + { + var eventKeyCombination = new[] { KeyCombination.FromInput(evt) }; + foreach (var shortcut in ViewToolShortcuts) + shouldBeActive |= shortcut?.StartsWith(eventKeyCombination) ?? false; + } + + shouldBeActive |= Tools.s_LockedViewTool != ViewTool.None || Tools.current == Tool.View; if (shouldBeActive != s_ViewToolActive) { s_ViewToolActive = shouldBeActive; @@ -260,7 +307,7 @@ private static void HandleMouseDown(SceneView view, int id, int button) if (Toolbar.get) Toolbar.get.Repaint(); - EditorGUIUtility.SetWantsMouseJumping(1); + s_ActiveSceneView = s_CurrentSceneView; if (Tools.s_LockedViewTool == ViewTool.None && Tools.current == Tool.View) @@ -296,7 +343,7 @@ internal static void ResetMotion() private static void HandleMouseUp(SceneView view, int id, int button, int clickCount) { - if (GUIUtility.hotControl == id && (shortcutKey == KeyCode.None || shortcutKey == (Event.current.keyCode == KeyCode.None ? KeyCode.Mouse0 + Event.current.button : Event.current.keyCode))) + if (Event.current != null && GUIUtility.hotControl == id && (shortcutKey == KeyCode.None || shortcutKey == (Event.current.keyCode == KeyCode.None ? KeyCode.Mouse0 + Event.current.button : Event.current.keyCode))) { // Move pivot to clicked point. if (Tools.s_LockedViewTool == ViewTool.Pan && !s_Drag) diff --git a/Editor/Mono/ScriptAttributeGUI/Implementations/ExposedReferenceDrawer.cs b/Editor/Mono/ScriptAttributeGUI/Implementations/ExposedReferenceDrawer.cs index 4641e216af..f3206d7eff 100644 --- a/Editor/Mono/ScriptAttributeGUI/Implementations/ExposedReferenceDrawer.cs +++ b/Editor/Mono/ScriptAttributeGUI/Implementations/ExposedReferenceDrawer.cs @@ -2,12 +2,9 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License -using System.Collections; -using System.Collections.Generic; -using UnityEngine; using UnityEditor; +using UnityEngine; using UnityEngine.UIElements; -using UnityEditor.UIElements; using ObjectField = UnityEditor.UIElements.ObjectField; abstract class BaseExposedPropertyDrawer : UnityEditor.PropertyDrawer @@ -17,7 +14,7 @@ abstract class BaseExposedPropertyDrawer : UnityEditor.PropertyDrawer private static Color kMissingOverrideColor = new Color(1.0f, 0.11f, 0.11f, 1.0f); protected static string kSetExposedPropertyMsg = "Set Exposed Property"; protected static string kClearExposedPropertyMsg = "Clear Exposed Property"; - private const string kVisualElementName = "ExposedReference"; + internal const string kVisualElementName = "ExposedReference"; internal readonly GUIContent ExposePropertyContent = EditorGUIUtility.TrTextContent("Expose Property"); internal readonly GUIContent UnexposePropertyContent = EditorGUIUtility.TrTextContent("Unexpose Property"); @@ -149,8 +146,7 @@ public override VisualElement CreatePropertyGUI(SerializedProperty prop) ObjectField obj = new ObjectField() { name = kVisualElementName, - label = prop.displayName, - bindingPath = m_Item.exposedPropertyDefault.propertyPath, + label = preferredLabel, objectType = typeOfExposedReference, value = m_Item.currentReferenceValue, allowSceneObjects = m_Item.exposedPropertyTable != null @@ -159,13 +155,28 @@ public override VisualElement CreatePropertyGUI(SerializedProperty prop) obj.RegisterValueChangedCallback(SetReference); obj.AddManipulator(new ContextualMenuManipulator(BuildContextualMenu)); + Undo.UndoRedoCallback undoRedoCallback = () => + { + m_Item.UpdateValue(); + obj.SetValueWithoutNotify(m_Item.currentReferenceValue); + }; + + obj.RegisterCallback(evt => Undo.undoRedoPerformed += undoRedoCallback); + obj.RegisterCallback(evt => Undo.undoRedoPerformed -= undoRedoCallback); + return obj; } void SetReference(ChangeEvent evt) { SetReference(evt.newValue); - m_Item.currentReferenceValue = evt.newValue; + if (m_Item.currentReferenceValue != evt.newValue) + { + m_Item.currentReferenceValue = evt.newValue; + + //save the modified SerializedObject since we are bypassing the binding system + m_Item.exposedPropertyName.serializedObject.ApplyModifiedProperties(); + } } internal void SetReference(Object newValue) @@ -189,10 +200,10 @@ internal void SetReference(Object newValue) } else { - var guid = UnityEditor.GUID.Generate(); - var str = guid.ToString(); + var str = UnityEditor.GUID.Generate().ToString(); m_Item.exposedPropertyNameString = str; m_Item.exposedPropertyName.stringValue = str; + m_Item.propertyMode = ExposedPropertyMode.NamedGUID; Undo.RecordObject(m_Item.exposedPropertyTable as UnityEngine.Object, kSetExposedPropertyMsg); m_Item.exposedPropertyTable.SetReferenceValue(m_Item.exposedPropertyNameString, newValue); diff --git a/Editor/Mono/ScriptAttributeGUI/Implementations/ExposedReferenceObject.cs b/Editor/Mono/ScriptAttributeGUI/Implementations/ExposedReferenceObject.cs index b8287fb590..18da27015a 100644 --- a/Editor/Mono/ScriptAttributeGUI/Implementations/ExposedReferenceObject.cs +++ b/Editor/Mono/ScriptAttributeGUI/Implementations/ExposedReferenceObject.cs @@ -33,6 +33,11 @@ internal ExposedReferenceObject(SerializedProperty property) propertyMode = BaseExposedPropertyDrawer.GetExposedPropertyMode(exposedPropertyNameString); exposedPropertyTable = GetExposedPropertyTable(property); currentOverrideState = BaseExposedPropertyDrawer.OverrideState.DefaultValue; + UpdateValue(); + } + + public void UpdateValue() + { currentReferenceValue = Resolve(out m_CurrentOverrideState); } diff --git a/Editor/Mono/ScriptAttributeGUI/Implementations/PropertyDrawers.cs b/Editor/Mono/ScriptAttributeGUI/Implementations/PropertyDrawers.cs index 49ba65313c..38279a7c6d 100644 --- a/Editor/Mono/ScriptAttributeGUI/Implementations/PropertyDrawers.cs +++ b/Editor/Mono/ScriptAttributeGUI/Implementations/PropertyDrawers.cs @@ -33,7 +33,7 @@ public override VisualElement CreatePropertyGUI(SerializedProperty property) if (property.propertyType == SerializedPropertyType.Float) { - var slider = new Slider(property.displayName, range.min, range.max); + var slider = new Slider(preferredLabel, range.min, range.max); slider.AddToClassList(Slider.alignedFieldUssClassName); slider.bindingPath = property.propertyPath; slider.showInputField = true; @@ -41,7 +41,7 @@ public override VisualElement CreatePropertyGUI(SerializedProperty property) } else if (property.propertyType == SerializedPropertyType.Integer) { - var intSlider = new SliderInt(property.displayName, (int)range.min, (int)range.max); + var intSlider = new SliderInt(preferredLabel, (int)range.min, (int)range.max); intSlider.AddToClassList(SliderInt.alignedFieldUssClassName); intSlider.bindingPath = property.propertyPath; intSlider.showInputField = true; @@ -124,12 +124,12 @@ public override VisualElement CreatePropertyGUI(SerializedProperty property) { if (property.type == "float") { - newField = new FloatField(property.displayName); + newField = new FloatField(preferredLabel); ((BaseField)newField).onValidateValue += OnValidateValue; } else if (property.type == "double") { - newField = new DoubleField(property.displayName); + newField = new DoubleField(preferredLabel); ((BaseField)newField).onValidateValue += OnValidateValue; } } @@ -137,38 +137,48 @@ public override VisualElement CreatePropertyGUI(SerializedProperty property) { if (property.type == "int") { - newField = new IntegerField(property.displayName); + newField = new IntegerField(preferredLabel); ((BaseField)newField).onValidateValue += OnValidateValue; } else if (property.type == "long") { - newField = new LongField(property.displayName); + newField = new LongField(preferredLabel); ((BaseField)newField).onValidateValue += OnValidateValue; } + else if (property.type == "uint") + { + newField = new UnsignedIntegerField(preferredLabel); + ((BaseField)newField).onValidateValue += OnValidateValue; + } + else if (property.type == "ulong") + { + newField = new UnsignedLongField(preferredLabel); + ((BaseField)newField).onValidateValue += OnValidateValue; + } } else if (property.propertyType == SerializedPropertyType.Vector2) { - newField = new Vector2Field(property.displayName); + newField = new Vector2Field(preferredLabel); ((BaseField)newField).onValidateValue += OnValidateValue; } else if (property.propertyType == SerializedPropertyType.Vector2Int) { - newField = new Vector2IntField(property.displayName); + newField = new Vector2IntField(preferredLabel); ((BaseField)newField).onValidateValue += OnValidateValue; } else if (property.propertyType == SerializedPropertyType.Vector3) { - newField = new Vector3Field(property.displayName); + newField = new Vector3Field(preferredLabel); ((BaseField)newField).onValidateValue += OnValidateValue; } else if (property.propertyType == SerializedPropertyType.Vector3Int) { - newField = new Vector3IntField(property.displayName); + newField = new Vector3IntField(preferredLabel); ((BaseField)newField).onValidateValue += OnValidateValue; } else if (property.propertyType == SerializedPropertyType.Vector4) { - newField = new Vector4Field(property.displayName); + newField = new Vector4Field(preferredLabel); ((BaseField)newField).onValidateValue += OnValidateValue; } @@ -203,6 +213,16 @@ private long OnValidateValue(long value) return Math.Max((long)minAttribute.min, value); } + private uint OnValidateValue(uint value) + { + return Math.Max((uint)minAttribute.min, value); + } + + private ulong OnValidateValue(ulong value) + { + return Math.Max((ulong)minAttribute.min, value); + } + private Vector2 OnValidateValue(Vector2 value) { return new Vector2(Mathf.Max(minAttribute.min, value.x), Mathf.Max(minAttribute.min, value.y)); @@ -262,7 +282,7 @@ public override VisualElement CreatePropertyGUI(SerializedProperty property) if (property.propertyType == SerializedPropertyType.String) { var lines = ((MultilineAttribute)attribute).lines; - var field = new TextField(property.displayName); + var field = new TextField(preferredLabel); field.multiline = true; field.bindingPath = property.propertyPath; field.style.height = EditorGUI.kSingleLineHeight + (lines - 1) * kLineHeight; @@ -284,7 +304,7 @@ public override float GetPropertyHeight(SerializedProperty property, GUIContent [CustomPropertyDrawer(typeof(TextAreaAttribute))] internal sealed class TextAreaDrawer : PropertyDrawer { - private const int kLineHeight = 13; + private const int kLineHeight = 15; private static string s_InvalidTypeMessage = L10n.Tr("Use TextAreaDrawer with string."); private Vector2 m_ScrollPosition = new Vector2(); @@ -311,31 +331,39 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten public override VisualElement CreatePropertyGUI(SerializedProperty property) { - if (property.propertyType == SerializedPropertyType.String) + if (property.propertyType != SerializedPropertyType.String) + return new Label(s_InvalidTypeMessage); + + var textAreaAttribute = attribute as TextAreaAttribute; + + // Label + first line + var initialHeight = EditorGUI.kSingleLineHeight + EditorGUI.kSingleLineHeight; + + var minHeight = initialHeight + (textAreaAttribute!.minLines - 1) * kLineHeight; + var maxHeight = initialHeight + (textAreaAttribute!.maxLines - 1) * kLineHeight; + + if (maxHeight < minHeight) + maxHeight = minHeight; + + var textField = new TextField { - var textAreaAttribute = attribute as TextAreaAttribute; - var element = new VisualElement(); - var label = new Label(property.displayName); - var scrollView = new ScrollView(); - var textField = new TextField(); - textField.multiline = true; - var minHeight = EditorGUI.kSingleLineHeight + (textAreaAttribute.minLines - 1) * kLineHeight; - var maxHeight = minHeight; - - element.Add(label); - element.Add(scrollView); - - scrollView.Add(textField); - scrollView.style.minHeight = minHeight; - scrollView.style.maxHeight = maxHeight; - - textField.style.minHeight = minHeight; - textField.bindingPath = property.propertyPath; - - return element; - } + label = preferredLabel, + multiline = true, + style = + { + flexDirection = FlexDirection.Column, + whiteSpace = WhiteSpace.Normal, + minHeight = minHeight, + maxHeight = maxHeight + } + }; - return new Label(s_InvalidTypeMessage); + textField.verticalScrollerVisibility = ScrollerVisibility.Auto; + + textField.style.minHeight = minHeight; + textField.bindingPath = property.propertyPath; + + return textField; } public override float GetPropertyHeight(SerializedProperty property, GUIContent label) @@ -384,7 +412,7 @@ public override VisualElement CreatePropertyGUI(SerializedProperty property) if (property.propertyType == SerializedPropertyType.Color) { var colorUsage = (ColorUsageAttribute)attribute; - var field = new ColorField(property.displayName); + var field = new ColorField(preferredLabel); field.showAlpha = colorUsage.showAlpha; field.hdr = colorUsage.hdr; field.bindingPath = property.propertyPath; @@ -418,7 +446,7 @@ public override VisualElement CreatePropertyGUI(SerializedProperty property) if (property.propertyType == SerializedPropertyType.Gradient) { var gradientUsage = (GradientUsageAttribute)attribute; - var field = new GradientField(property.displayName); + var field = new GradientField(preferredLabel); field.hdr = gradientUsage.hdr; field.colorSpace = gradientUsage.colorSpace; field.bindingPath = property.propertyPath; @@ -454,12 +482,12 @@ public override VisualElement CreatePropertyGUI(SerializedProperty property) { if (property.type == "float") { - newField = new FloatField(property.displayName); + newField = new FloatField(preferredLabel); ((TextInputBaseField)newField).isDelayed = true; } else if (property.type == "double") { - newField = new DoubleField(property.displayName); + newField = new DoubleField(preferredLabel); ((TextInputBaseField)newField).isDelayed = true; } } @@ -467,18 +495,18 @@ public override VisualElement CreatePropertyGUI(SerializedProperty property) { if (property.type == "int") { - newField = new IntegerField(property.displayName); + newField = new IntegerField(preferredLabel); ((TextInputBaseField)newField).isDelayed = true; } else if (property.type == "long") { - newField = new LongField(property.displayName); + newField = new LongField(preferredLabel); ((TextInputBaseField)newField).isDelayed = true; } } else if (property.propertyType == SerializedPropertyType.String) { - newField = new TextField(property.displayName); + newField = new TextField(preferredLabel); ((TextInputBaseField)newField).isDelayed = true; } diff --git a/Editor/Mono/ScriptAttributeGUI/PropertyDrawer.cs b/Editor/Mono/ScriptAttributeGUI/PropertyDrawer.cs index f6103f0cc4..5dc8cc3d84 100644 --- a/Editor/Mono/ScriptAttributeGUI/PropertyDrawer.cs +++ b/Editor/Mono/ScriptAttributeGUI/PropertyDrawer.cs @@ -14,6 +14,7 @@ public abstract class PropertyDrawer : GUIDrawer { internal PropertyAttribute m_Attribute; internal FieldInfo m_FieldInfo; + internal string m_PreferredLabel; // The [[PropertyAttribute]] for the property. Not applicable for custom class drawers. (RO) public PropertyAttribute attribute { get { return m_Attribute; } } @@ -21,6 +22,9 @@ public abstract class PropertyDrawer : GUIDrawer // The reflection FieldInfo for the member this property represents. (RO) public FieldInfo fieldInfo { get { return m_FieldInfo; } } + // The preferred label for this property. + public string preferredLabel => m_PreferredLabel; + internal void OnGUISafe(Rect position, SerializedProperty property, GUIContent label) { ScriptAttributeUtility.s_DrawerStack.Push(this); diff --git a/Editor/Mono/ScriptAttributeGUI/PropertyHandler.cs b/Editor/Mono/ScriptAttributeGUI/PropertyHandler.cs index dff46e8072..d3111dfc6e 100644 --- a/Editor/Mono/ScriptAttributeGUI/PropertyHandler.cs +++ b/Editor/Mono/ScriptAttributeGUI/PropertyHandler.cs @@ -29,6 +29,7 @@ internal PropertyDrawer propertyDrawer } internal List decoratorDrawers => m_DecoratorDrawers; + internal bool skipDecoratorDrawers { get; set; } private int m_NestingLevel; @@ -153,7 +154,8 @@ internal bool OnGUI(Rect position, SerializedProperty property, GUIContent label float propHeight = position.height; position.height = 0; - if (m_DecoratorDrawers != null && !isCurrentlyNested) + + if (!skipDecoratorDrawers && m_DecoratorDrawers != null && !isCurrentlyNested) { foreach (DecoratorDrawer decorator in m_DecoratorDrawers) { @@ -302,7 +304,7 @@ public float GetHeight(SerializedProperty property, GUIContent label, bool inclu { float height = 0; - if (m_DecoratorDrawers != null && !isCurrentlyNested) + if (!skipDecoratorDrawers && m_DecoratorDrawers != null && !isCurrentlyNested) foreach (DecoratorDrawer drawer in m_DecoratorDrawers) height += drawer.GetHeight(); @@ -346,9 +348,9 @@ public float GetHeight(SerializedProperty property, GUIContent label, bool inclu bool childrenAreExpanded = property.isExpanded && EditorGUI.HasVisibleChildFields(property); // Loop through all child properties - var tc = EditorGUIUtility.TempContent(property.localizedDisplayName, tooltip); if (childrenAreExpanded) { + var tc = EditorGUIUtility.TempContent(property.localizedDisplayName, tooltip); SerializedProperty endProperty = property.GetEndProperty(); while (property.NextVisible(childrenAreExpanded) && !SerializedProperty.EqualContents(property, endProperty)) { diff --git a/Editor/Mono/ScriptAttributeGUI/ScriptAttributeUtility.cs b/Editor/Mono/ScriptAttributeGUI/ScriptAttributeUtility.cs index 6542e04cd1..97924da5a4 100644 --- a/Editor/Mono/ScriptAttributeGUI/ScriptAttributeUtility.cs +++ b/Editor/Mono/ScriptAttributeGUI/ScriptAttributeUtility.cs @@ -284,11 +284,11 @@ internal static bool GetTypeFromManagedReferenceFullTypeName(string managedRefer { managedReferenceInstanceType = null; - var parts = managedReferenceFullTypename.Split(' '); - if (parts.Length == 2) + var splitIndex = managedReferenceFullTypename.IndexOf(' '); + if (splitIndex > 0) { - var assemblyPart = parts[0]; - var nsClassnamePart = parts[1]; + var assemblyPart = managedReferenceFullTypename.Substring(0, splitIndex); + var nsClassnamePart = managedReferenceFullTypename.Substring(splitIndex); managedReferenceInstanceType = Type.GetType($"{nsClassnamePart}, {assemblyPart}"); } diff --git a/Editor/Mono/ScriptReloadProperties.cs b/Editor/Mono/ScriptReloadProperties.cs index b57cbd2b1b..3bb23a2d91 100644 --- a/Editor/Mono/ScriptReloadProperties.cs +++ b/Editor/Mono/ScriptReloadProperties.cs @@ -67,7 +67,7 @@ private void ManagedStore() EditorGUI_TextEditor_cursorIndex = EditorGUI.s_RecycledEditor.cursorIndex; EditorGUI_TextEditor_selectIndex = EditorGUI.s_RecycledEditor.selectIndex; EditorGUI_TextEditor_controlID = EditorGUI.s_RecycledEditor.controlID; - EditorGUI_TextEditor_hasHorizontalCursorPos = EditorGUI.s_RecycledEditor.hasHorizontalCursorPos; + EditorGUI_TextEditor_hasHorizontalCursorPos = EditorGUI.s_RecycledEditor.hasHorizontalCursor; EditorGUI_TextEditor_scrollOffset = EditorGUI.s_RecycledEditor.scrollOffset; EditorGUI_TextEditor_hasFocus = EditorGUI.s_RecycledEditor.m_HasFocus; EditorGUI_TextEditor_graphicalCursorPos = EditorGUI.s_RecycledEditor.graphicalCursorPos; @@ -77,7 +77,7 @@ private void ManagedStore() EditorGUI_DelayedTextEditor_cursorIndex = EditorGUI.s_DelayedTextEditor.cursorIndex; EditorGUI_DelayedTextEditor_selectIndex = EditorGUI.s_DelayedTextEditor.selectIndex; EditorGUI_DelayedTextEditor_controlID = EditorGUI.s_DelayedTextEditor.controlID; - EditorGUI_DelayedTextEditor_hasHorizontalCursorPos = EditorGUI.s_DelayedTextEditor.hasHorizontalCursorPos; + EditorGUI_DelayedTextEditor_hasHorizontalCursorPos = EditorGUI.s_DelayedTextEditor.hasHorizontalCursor; EditorGUI_DelayedTextEditor_scrollOffset = EditorGUI.s_DelayedTextEditor.scrollOffset; EditorGUI_DelayedTextEditor_hasFocus = EditorGUI.s_DelayedTextEditor.m_HasFocus; EditorGUI_DelayedTextEditor_graphicalCursorPos = EditorGUI.s_DelayedTextEditor.graphicalCursorPos; @@ -93,7 +93,7 @@ private void ManagedLoad() EditorGUI.s_RecycledEditor.cursorIndex = EditorGUI_TextEditor_cursorIndex; EditorGUI.s_RecycledEditor.selectIndex = EditorGUI_TextEditor_selectIndex; EditorGUI.s_RecycledEditor.controlID = EditorGUI_TextEditor_controlID; - EditorGUI.s_RecycledEditor.hasHorizontalCursorPos = EditorGUI_TextEditor_hasHorizontalCursorPos; + EditorGUI.s_RecycledEditor.hasHorizontalCursor = EditorGUI_TextEditor_hasHorizontalCursorPos; EditorGUI.s_RecycledEditor.scrollOffset = EditorGUI_TextEditor_scrollOffset; EditorGUI.s_RecycledEditor.m_HasFocus = EditorGUI_TextEditor_hasFocus; EditorGUI.s_RecycledEditor.graphicalCursorPos = EditorGUI_TextEditor_graphicalCursorPos; @@ -103,7 +103,7 @@ private void ManagedLoad() EditorGUI.s_DelayedTextEditor.cursorIndex = EditorGUI_DelayedTextEditor_cursorIndex; EditorGUI.s_DelayedTextEditor.selectIndex = EditorGUI_DelayedTextEditor_selectIndex; EditorGUI.s_DelayedTextEditor.controlID = EditorGUI_DelayedTextEditor_controlID; - EditorGUI.s_DelayedTextEditor.hasHorizontalCursorPos = EditorGUI_DelayedTextEditor_hasHorizontalCursorPos; + EditorGUI.s_DelayedTextEditor.hasHorizontalCursor = EditorGUI_DelayedTextEditor_hasHorizontalCursorPos; EditorGUI.s_DelayedTextEditor.scrollOffset = EditorGUI_DelayedTextEditor_scrollOffset; EditorGUI.s_DelayedTextEditor.m_HasFocus = EditorGUI_DelayedTextEditor_hasFocus; EditorGUI.s_DelayedTextEditor.graphicalCursorPos = EditorGUI_DelayedTextEditor_graphicalCursorPos; diff --git a/Editor/Mono/ScriptableSingleton.cs b/Editor/Mono/ScriptableSingleton.cs index 2eae2e1973..836f04ce39 100644 --- a/Editor/Mono/ScriptableSingleton.cs +++ b/Editor/Mono/ScriptableSingleton.cs @@ -110,7 +110,9 @@ private static void CreateAndLoad() { // Create T t = CreateInstance(); - t.hideFlags = HideFlags.HideAndDontSave; + + // Editing should be allowed, but the user is responsible for calling Save() (case uum-40767) + t.hideFlags = HideFlags.HideAndDontSave & ~HideFlags.NotEditable; } System.Diagnostics.Debug.Assert(s_Instance != null); diff --git a/Editor/Mono/Scripting/APIUpdater/APIUpdaterAssemblyHelper.cs b/Editor/Mono/Scripting/APIUpdater/APIUpdaterAssemblyHelper.cs index ee0fe213a9..08092954dc 100644 --- a/Editor/Mono/Scripting/APIUpdater/APIUpdaterAssemblyHelper.cs +++ b/Editor/Mono/Scripting/APIUpdater/APIUpdaterAssemblyHelper.cs @@ -47,8 +47,8 @@ internal static int Run(string arguments, string workingDir, out string stdOut, return assemblyUpdaterProcess.ExitCode; } - - static string AssemblyUpdaterPath() => EditorApplication.applicationContentsPath + "/Tools/ScriptUpdater/AssemblyUpdater.exe"; + + static string AssemblyUpdaterPath() => $"{EditorApplication.applicationContentsPath}/Tools/Compilation/ApiUpdater/AssemblyUpdater.dll"; internal static string ArgumentsForUpdateAssembly(string assemblyPath, string tempOutputPath, IEnumerable updateConfigSourcePaths) { diff --git a/Editor/Mono/Scripting/Compilers/APIUpdaterHelper.cs b/Editor/Mono/Scripting/APIUpdater/APIUpdaterHelper.cs similarity index 94% rename from Editor/Mono/Scripting/Compilers/APIUpdaterHelper.cs rename to Editor/Mono/Scripting/APIUpdater/APIUpdaterHelper.cs index ac35565b20..8998807262 100644 --- a/Editor/Mono/Scripting/Compilers/APIUpdaterHelper.cs +++ b/Editor/Mono/Scripting/APIUpdater/APIUpdaterHelper.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Reflection; using System.Text; +using System.Runtime.InteropServices; using Mono.Cecil; using NiceIO; using UnityEditor.PackageManager; @@ -18,10 +19,14 @@ using UnityEngine.Scripting.APIUpdating; using static System.Environment; -namespace UnityEditor.Scripting.Compilers +namespace UnityEditor.Scripting { internal static class APIUpdaterHelper { + private static string ComputePlatformExecutableExtension() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : string.Empty; + + internal static readonly string PlatformExecutableExtensionWithDot = ComputePlatformExecutableExtension(); + public static bool IsInPackage(this string filePath) { return EditorCompilationInterface.Instance.IsPathInPackageDirectory(filePath); diff --git a/Editor/Mono/Scripting/APIUpdater/APIUpdaterManager.bindings.cs b/Editor/Mono/Scripting/APIUpdater/APIUpdaterManager.bindings.cs index ec3858667b..c4a2d3323e 100644 --- a/Editor/Mono/Scripting/APIUpdater/APIUpdaterManager.bindings.cs +++ b/Editor/Mono/Scripting/APIUpdater/APIUpdaterManager.bindings.cs @@ -9,7 +9,6 @@ using UnityEditor.Scripting; using UnityEditor.Scripting.APIUpdater; using UnityEditor.Scripting.ScriptCompilation; -using UnityEditor.Scripting.Compilers; using UnityEditor.Utils; using UnityEditor.VersionControl; using UnityEditor.PackageManager; @@ -300,7 +299,7 @@ private static int ProcessSuccessfulUpdates(AssemblyUpdaterUpdateTask[] tasks) APIUpdaterLogger.WriteToFile("Assemblies not requiring updates:"); foreach (var noUpdateRequired in noUpdatesRequiredAssemblies) { - APIUpdaterLogger.WriteToFile($"{noUpdateRequired.Candidate.Path} (stdout below):{Environment.NewLine}{noUpdateRequired.StdOut}{Environment.NewLine}"); + APIUpdaterLogger.WriteToFile($"{noUpdateRequired.Candidate.Path}{FormattedStoutFrom(noUpdateRequired)}"); } } @@ -366,6 +365,14 @@ IEnumerable FilterOutLocalAndEmbeddedPackagesWhenAskingForConsent(IEnume yield return path; } } + + static string FormattedStoutFrom(AssemblyUpdaterUpdateTask task) + { + if (task.StdOut.Length == 0) + return string.Empty; + + return $" (stdout below):{Environment.NewLine}{task.StdOut}{Environment.NewLine}"; + } } private static void RunAssemblyUpdaterTask(object o) diff --git a/Editor/Mono/Scripting/Compilers/CompilerOutputParserBase.cs b/Editor/Mono/Scripting/Compilers/CompilerOutputParserBase.cs index 166ccfe87e..190c4f08e4 100644 --- a/Editor/Mono/Scripting/Compilers/CompilerOutputParserBase.cs +++ b/Editor/Mono/Scripting/Compilers/CompilerOutputParserBase.cs @@ -101,6 +101,11 @@ public virtual IEnumerable Parse(string[] errorOutput, string[] return msgs; } + protected virtual bool ShouldParseLine(string line) + { + return true; + } + protected abstract string GetErrorIdentifier(); protected virtual string GetInformationIdentifier() diff --git a/Editor/Mono/Scripting/ScriptCompilation/AssemblyGraphBuilder.cs b/Editor/Mono/Scripting/ScriptCompilation/AssemblyGraphBuilder.cs index 05956a9893..1084ae54f8 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/AssemblyGraphBuilder.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/AssemblyGraphBuilder.cs @@ -12,7 +12,7 @@ namespace UnityEditor.Scripting.ScriptCompilation internal interface IAssemblyGraphBuilder { Dictionary> Match( - IReadOnlyCollection sources); + IReadOnlyCollection sources, bool logWarningOnMiss = true); } internal class AssemblyGraphBuilder : IAssemblyGraphBuilder @@ -131,7 +131,7 @@ public void Initialize(IReadOnlyCollection assemblies, } public Dictionary> Match( - IReadOnlyCollection sources) + IReadOnlyCollection sources, bool logWarningOnMiss = true) { var assemblyNameSources = new Dictionary>(); @@ -144,8 +144,12 @@ public Dictionary> Match( if (currentMatchingAssemblyDefinition == null) { - Console.WriteLine( - $"Script '{source}' will not be compiled because it exists outside the Assets folder and does not to belong to any assembly definition file."); + if (logWarningOnMiss) + { + Console.WriteLine( + $"Script '{source}' will not be compiled because it exists outside the Assets folder and does not to belong to any assembly definition file."); + } + continue; } diff --git a/Editor/Mono/Scripting/ScriptCompilation/AssetPathVersionMetaData.cs b/Editor/Mono/Scripting/ScriptCompilation/AssetPathVersionMetaData.cs index 780289633d..24f0299c58 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/AssetPathVersionMetaData.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/AssetPathVersionMetaData.cs @@ -26,6 +26,24 @@ class VersionMetaData public string Name; public string Version; public VersionType Type; + + public VersionMetaData(string name) + { + Name = name; + } + + public VersionMetaData(string name, string version) + { + Name = name; + Version = version; + } + + public VersionMetaData(string name, string version, VersionType type) + { + Name = name; + Version = version; + Type = type; + } } diff --git a/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/BeeScriptCompilation.cs b/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/BeeScriptCompilation.cs index 006e2e5cfe..ae49eba4f4 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/BeeScriptCompilation.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/BeeScriptCompilation.cs @@ -48,7 +48,7 @@ public static ScriptCompilationData ScriptCompilationDataFor( : AssemblyDataFrom(CodeGenAssemblies(CompilationPipeline.GetScriptAssemblies(editorCompilation, AssembliesType.Editor, extraScriptingDefines))); } - var movedFromExtractorPath = EditorApplication.applicationContentsPath + $"/Tools/ScriptUpdater/ApiUpdater.MovedFromExtractor.exe"; + var movedFromExtractorPath = $"{EditorApplication.applicationContentsPath}/Tools/Compilation/ApiUpdater/ApiUpdater.MovedFromExtractor.dll"; var dotNetSdkRoslynPath = EditorApplication.applicationContentsPath + $"/DotNetSdkRoslyn"; var localization = "en-US"; @@ -129,7 +129,7 @@ private static AssemblyData[] AssemblyDataFrom(ScriptAssembly[] assemblies) private static AssemblyData AssemblyDataFrom(ScriptAssembly a, ScriptAssembly[] allAssemblies, int index) { - Array.Sort(a.Files, StringComparer.InvariantCulture); + Array.Sort(a.Files, StringComparer.Ordinal); var references = a.ScriptAssemblyReferences.Select(r => Array.IndexOf(allAssemblies, r)).ToArray(); Array.Sort(references); return new AssemblyData diff --git a/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/UnityScriptUpdater.cs b/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/UnityScriptUpdater.cs index 86a11592df..eb40258215 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/UnityScriptUpdater.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/BeeDriver/UnityScriptUpdater.cs @@ -38,7 +38,7 @@ public UnityScriptUpdater(NPath projectRoot) static RunnableProgram DefaultScriptUpdaterProgram() { - NPath scriptUpdaterExe = $"{EditorApplication.applicationContentsPath}/Tools/ScriptUpdater/ScriptUpdater.exe"; + NPath scriptUpdaterExe = $"{EditorApplication.applicationContentsPath}/Tools/Compilation/ApiUpdater/ScriptUpdater.dll"; return new SystemProcessRunnableProgram(NetCoreRunProgram.NetCoreRunPath, new[] {scriptUpdaterExe.InQuotes()}, stdOutMode: StdOutMode.LogStdOutOnFinish); } diff --git a/Editor/Mono/Scripting/ScriptCompilation/CustomScriptAssembly.cs b/Editor/Mono/Scripting/ScriptCompilation/CustomScriptAssembly.cs index bafdba1c32..7b5d10b7a8 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/CustomScriptAssembly.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/CustomScriptAssembly.cs @@ -285,7 +285,7 @@ public AssemblyFlags AssemblyFlags } bool rootFolder, immutable; - bool imported = AssetDatabase.GetAssetFolderInfo(PathPrefix, out rootFolder, out immutable); + bool imported = AssetDatabase.TryGetAssetFolderInfo(PathPrefix, out rootFolder, out immutable); // Do not emit warnings for immutable (package) folders, // as the user cannot do anything to fix them. @@ -358,6 +358,7 @@ static CustomScriptAssembly() new CustomScriptAssemblyPlatform("LinuxStandalone32", BuildTarget.StandaloneLinux), new CustomScriptAssemblyPlatform("LinuxStandaloneUniversal", BuildTarget.StandaloneLinuxUniversal), new CustomScriptAssemblyPlatform("Lumin", BuildTarget.Lumin), + new CustomScriptAssemblyPlatform("Stadia", BuildTarget.Stadia), }; #pragma warning restore 0618 } diff --git a/Editor/Mono/Scripting/ScriptCompilation/EditorBuildRules.cs b/Editor/Mono/Scripting/ScriptCompilation/EditorBuildRules.cs index 9864da668a..ba10a39ac8 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/EditorBuildRules.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/EditorBuildRules.cs @@ -332,7 +332,7 @@ internal static void AddScriptAssemblyReferences(ref ScriptAssembly scriptAssemb if (shouldProcessPredefinedCustomTargets && assemblies.PredefinedAssembliesCustomTargetReferences != null) predefinedCustomTargetReferences = assemblies.PredefinedAssembliesCustomTargetReferences; - var unityReferences = Array.Empty(); + var unityReferences = new HashSet(); // Add Unity assemblies (UnityEngine.dll, UnityEditor.dll) references, as long as the target // doesn't specify that it doesn't want them. @@ -340,8 +340,13 @@ internal static void AddScriptAssemblyReferences(ref ScriptAssembly scriptAssemb { // Add predefined custom target references in a hash-set for fast lookup var predefinedCustomTargetRefs = new HashSet(predefinedCustomTargetReferences.Select(x => x.Filename)); - unityReferences = GetUnityReferences(scriptAssembly, targetAssembly, assemblies.UnityAssemblies, predefinedCustomTargetRefs, settings.CompilationOptions, UnityReferencesOptions.None); - references.AddRange(unityReferences.Select(r => r.Path)); + foreach (var unityReference in GetUnityReferences(scriptAssembly, targetAssembly, + assemblies.UnityAssemblies, predefinedCustomTargetRefs, settings.CompilationOptions, + UnityReferencesOptions.None)) + { + unityReferences.Add(Path.GetFileName(unityReference.Path)); + references.Add(unityReference.Path); + } } AddTestRunnerCustomReferences(ref targetAssembly, assemblies.CustomTargetAssemblies); @@ -354,7 +359,7 @@ internal static void AddScriptAssemblyReferences(ref ScriptAssembly scriptAssemb // If the assembly already showed up in the unity references, don't reference it here. // This can happen when an assembly is configured as a unity assembly override, but // overrides are disabled. The Unity assembly should take precedence in that case. - if (unityReferences.Any(r => Path.GetFileName(r.Path) == reference.Filename)) + if (unityReferences.Contains(reference.Filename)) continue; // Add ScriptAssembly references to other dirty script assemblies that also need to be rebuilt. diff --git a/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs b/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs index 497ecaebde..4f6a579580 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs @@ -65,6 +65,8 @@ public struct TargetAssemblyInfo { public string Name; public AssemblyFlags Flags; + + public static readonly TargetAssemblyInfo Unknown = new TargetAssemblyInfo { Flags = AssemblyFlags.None, Name = null }; } public struct CustomScriptAssemblyAndReference @@ -1368,7 +1370,11 @@ public TargetAssemblyInfo GetTargetAssembly(string scriptPath) path = Path.Combine(projectDirectory, scriptPath); } - var matchedAssembly = GetAssemblyGraphBuilder().Match(new []{path}); + var matchedAssembly = GetAssemblyGraphBuilder().Match(new []{path}, false); + if (matchedAssembly.Count == 0) + { + return TargetAssemblyInfo.Unknown; + } TargetAssembly targetAssembly; var scriptAssembly = matchedAssembly.Single().Key; diff --git a/Editor/Mono/Scripting/ScriptCompilation/ILPostProcessingProgram.cs b/Editor/Mono/Scripting/ScriptCompilation/ILPostProcessingProgram.cs index f3c67c6ecf..702ec1bd37 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/ILPostProcessingProgram.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/ILPostProcessingProgram.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; +using System.Globalization; using System.IO; using System.Linq; using System.Security.Cryptography; @@ -12,6 +13,7 @@ using UnityEditor.Utils; using UnityEngine; using UnityEngine.Bindings; +using UnityEngine.Scripting; namespace UnityEditor.Scripting.ScriptCompilation { @@ -30,5 +32,42 @@ public virtual string EnsureRunningAndGetSocketOrNamedPipe() [NativeHeader("Editor/Src/ScriptCompilation/ILPPExternalProcess.h")] [FreeFunction("ILPPExternalProcess::EnsureRunningAndGetSocketOrNamedPipe", IsThreadSafe = true)] private static extern string EnsureRunningAndGetSocketOrNamedPipeImpl(); + + [RequiredByNativeCode(GenerateProxy = true)] + private static void KillLingeringILPPRunner() + { + var pidFilePath = Path.Combine("Library", "ilpp.pid"); + if (!File.Exists(pidFilePath)) + { + return; + } + int processId; + try + { + processId = int.Parse(File.ReadAllText(pidFilePath), CultureInfo.InvariantCulture); + } + catch(FormatException) { + // corrupted pid file, return + return; + } + try + { + var process = Process.GetProcessById(processId); + if(process.ProcessName == "Unity.ILPP.Runner" || process.ProcessName == "Unity.ILPP.Runner.exe") + { + UnityEngine.Debug.Log($"Found a lingering IL Post Processing runner process with PID {processId}. Killing it."); + process.Kill(); + } + } + catch (ArgumentException) + { + // no process with this ID + return; + } + catch (InvalidOperationException) + { + // process exitted while the Process instance is manipulated + } + } } } diff --git a/Editor/Mono/Scripting/ScriptCompilation/PostProcessorOutputParser.cs b/Editor/Mono/Scripting/ScriptCompilation/PostProcessorOutputParser.cs index 30e34b42af..f65f52e72a 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/PostProcessorOutputParser.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/PostProcessorOutputParser.cs @@ -2,8 +2,12 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using System; +using System.Collections.Generic; using System.Text.RegularExpressions; using UnityEditor.Scripting.Compilers; +using UnityEditorInternal; +using UnityEngine; namespace UnityEditor.Scripting.ScriptCompilation { @@ -11,6 +15,67 @@ internal class PostProcessorOutputParser : CompilerOutputParserBase { private static Regex sCompilerOutput = new Regex(@"(?[^:]*)\((?\d+),(?\d+)\):\s*(?warning|error)\s*(?.*)", RegexOptions.ExplicitCapture | RegexOptions.Compiled); + protected override bool ShouldParseLine(string line) + { + return line.Contains("warning", StringComparison.Ordinal) || + line.Contains("error", StringComparison.Ordinal); + } + + public override IEnumerable Parse(string[] errorOutput, string[] standardOutput, bool compilationHadFailure, string assemblyName_unused = null) + { + var hasErrors = false; + var msgs = new List(); + var regex = GetOutputRegex(); + var internalErrorRegex = GetInternalErrorOutputRegex(); + + var isWarningError = false; + var completeWarningErrors = new List(); + for (int i = 0; i < errorOutput.Length; i++) + { + if (!ShouldParseLine(errorOutput[i])) + { + if (isWarningError) + completeWarningErrors[completeWarningErrors.Count - 1] += "\n" + errorOutput[i]; + + continue; + } + + if (regex.Match(errorOutput[i]).Success || (internalErrorRegex != null && internalErrorRegex.Match(errorOutput[i]).Success)) + { + completeWarningErrors.Add(errorOutput[i]); + isWarningError = true; + } + else if (completeWarningErrors.Count > 0) + completeWarningErrors[completeWarningErrors.Count - 1] += "\n" + errorOutput[i]; + } + + foreach (var line in completeWarningErrors) + { + //Jamplus can fail with enormous lines in the stdout, parsing of which can take 30! seconds. + var line2 = line.Length > 1000 ? line.Substring(0, 100) : line; + var regrexLineToMatch = line2.Split("\n"); + Match m = regex.Match(regrexLineToMatch[0]); + if (!m.Success) + { + if (internalErrorRegex != null) + m = internalErrorRegex.Match(regrexLineToMatch[0]); + if (!m.Success) + continue; + } + CompilerMessage message = CreateCompilerMessageFromMatchedRegex(line, m, GetErrorIdentifier(), GetInformationIdentifier()); + + if (message.type == CompilerMessageType.Error) + hasErrors = true; + + msgs.Add(message); + } + if (compilationHadFailure && !hasErrors) + { + msgs.Add(CreateInternalCompilerErrorMessage(errorOutput)); + } + return msgs; + } + protected override string GetInformationIdentifier() { return default; diff --git a/Editor/Mono/Search/SearchService.cs b/Editor/Mono/Search/SearchService.cs index d8fa52f3e3..3292b92801 100644 --- a/Editor/Mono/Search/SearchService.cs +++ b/Editor/Mono/Search/SearchService.cs @@ -220,21 +220,6 @@ public enum SyncSearchEvent public static event Action syncSearchChanged; - static SearchService() - { - Build.BuildDefines.getScriptCompilationDefinesDelegates += AddSearchServiceBuildDefines; - } - - private static void AddSearchServiceBuildDefines(BuildTarget target, HashSet defines) - { - defines.Add("USE_SEARCH_ENGINE_API"); - defines.Add("USE_SEARCH_TABLE"); - defines.Add("USE_SEARCH_MODULE"); - defines.Add("USE_PROPERTY_DATABASE"); - defines.Add("USE_QUERY_BUILDER"); - defines.Add("USE_SEARCH_EXTENSION_API"); - } - public static void NotifySyncSearchChanged(SyncSearchEvent evt, string syncViewId, string searchQuery) { syncSearchChanged?.Invoke(evt, syncViewId, searchQuery); diff --git a/Editor/Mono/SearchUtility.cs b/Editor/Mono/SearchUtility.cs index bc460b41ad..013049fa40 100644 --- a/Editor/Mono/SearchUtility.cs +++ b/Editor/Mono/SearchUtility.cs @@ -6,8 +6,6 @@ using System.Collections.Generic; using UnityEditor.AssetImporters; using Object = UnityEngine.Object; -using UnityEditor.Collaboration; -using UnityEditor.Connect; namespace UnityEditor { @@ -119,25 +117,6 @@ internal static bool CheckForKeyWords(string searchString, SearchFilter filter, parsed = true; } - // Support: 'v:versionState' syntax - if (HasFilter(searchString, "v")) - { - string versionStateString = searchString.Substring(2); - List tmp = new List(filter.versionControlStates); - tmp.Add(versionStateString); - filter.versionControlStates = tmp.ToArray(); - parsed = true; - } - - // Support: 's:softLockState' syntax - if (HasFilter(searchString, "s")) - { - string softLockStateString = searchString.Substring(2); - List tmp = new List(filter.softLockControlStates); - tmp.Add(softLockStateString); - filter.softLockControlStates = tmp.ToArray(); - parsed = true; - } // Support: 'a:area' syntax if (HasFilter(searchString, "a")) { diff --git a/Editor/Mono/Selection.bindings.cs b/Editor/Mono/Selection.bindings.cs index 626077c6c9..f9f8745e85 100644 --- a/Editor/Mono/Selection.bindings.cs +++ b/Editor/Mono/Selection.bindings.cs @@ -82,13 +82,13 @@ extern public static Object activeObject } [StaticAccessor("SelectionBindings", StaticAccessorType.DoubleColon)] - extern internal static void SetSelectionWithActiveObject(Object[] newSelection, Object activeObject); + extern internal static void SetSelectionWithActiveObject([Unmarshalled] Object[] newSelection, Object activeObject); [StaticAccessor("SelectionBindings", StaticAccessorType.DoubleColon)] extern internal static void SetSelectionWithActiveInstanceID([NotNull("NullReferenceException")] int[] newSelection, int activeObject); [StaticAccessor("SelectionBindings", StaticAccessorType.DoubleColon)] - internal static extern void SetFullSelection(Object[] newSelection, Object activeObject, Object context, DataMode dataModeHint); + internal static extern void SetFullSelection([Unmarshalled] Object[] newSelection, Object activeObject, Object context, DataMode dataModeHint); [StaticAccessor("SelectionBindings", StaticAccessorType.DoubleColon)] internal static extern void SetFullSelectionByID([NotNull("NullReferenceException")]int[] newSelection, int activeObjectInstanceID, int contextInstanceID, DataMode dataModeHint); @@ -121,7 +121,7 @@ internal extern static DataMode dataModeHint // The actual unfiltered selection from the Scene. [StaticAccessor("SelectionBindings", StaticAccessorType.DoubleColon)] - extern public static Object[] objects { get; set; } + extern public static Object[] objects { get; [param:Unmarshalled] set; } // The actual unfiltered selection from the Scene returned as instance ids instead of ::ref::objects. [StaticAccessor("SelectionBindings", StaticAccessorType.DoubleColon)] diff --git a/Editor/Mono/SerializedObject.bindings.cs b/Editor/Mono/SerializedObject.bindings.cs index dfb176f481..49e18c5a18 100644 --- a/Editor/Mono/SerializedObject.bindings.cs +++ b/Editor/Mono/SerializedObject.bindings.cs @@ -208,6 +208,12 @@ internal extern InspectorMode inspectorMode set; } + internal extern DataMode inspectorDataMode + { + get; + set; + } + // Does the serialized object represents multiple objects due to multi-object editing? (RO) public extern bool isEditingMultipleObjects { diff --git a/Editor/Mono/SerializedProperty/SerializedPropertyTable.cs b/Editor/Mono/SerializedProperty/SerializedPropertyTable.cs index 667d05bee6..41f9da3574 100644 --- a/Editor/Mono/SerializedProperty/SerializedPropertyTable.cs +++ b/Editor/Mono/SerializedProperty/SerializedPropertyTable.cs @@ -123,13 +123,14 @@ public void OnGUI() r.y += m_FilterHeight; Rect tableRect = r; + // filter + m_TreeView.OnFilterGUI(filterRect); + // table Profiler.BeginSample("TreeView.OnGUI"); m_TreeView.OnGUI(tableRect); Profiler.EndSample(); - m_TreeView.OnFilterGUI(filterRect); - if (m_TreeView.IsFilteredDirty()) m_TreeView.Reload(); diff --git a/Editor/Mono/SerializedPropertyExtensions.cs b/Editor/Mono/SerializedPropertyExtensions.cs index 968b5038c2..a017f20690 100644 --- a/Editor/Mono/SerializedPropertyExtensions.cs +++ b/Editor/Mono/SerializedPropertyExtensions.cs @@ -2,6 +2,9 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using System; +using System.Collections.Generic; +using UnityEngine; using UnityEngine.Assertions; namespace UnityEditor @@ -21,6 +24,7 @@ public static bool TryGetMapEntry(this SerializedProperty map, string key, out S return true; } } + entry = null; return false; } @@ -38,6 +42,7 @@ public static bool TryGetMapEntry(this SerializedProperty map, int key, out Seri return true; } } + entry = null; return false; } @@ -51,6 +56,7 @@ public static bool TryGetMapEntry(this SerializedProperty map, string key, strin Assert.IsTrue(innerMap.type == "map"); return innerMap.TryGetMapEntry(elementMapKey, out entry); } + entry = null; return false; } @@ -188,5 +194,79 @@ public static void SetMapValue(this SerializedProperty map, string key, string e innerMap.SetMapValue(elementMapKey, elementMapValue); } } + + public static void SetValue(this SerializedProperty serializedProperty, Action serialziedPropertySetter, IList dstValues) + { + if (serializedProperty != null && serializedProperty.isArray && serialziedPropertySetter != null) + { + var numElementSerialized = serializedProperty.arraySize; + var numElementToInsert = dstValues.Count; + if (numElementSerialized > numElementToInsert) + { + for (var i = numElementSerialized - 1; i >= numElementToInsert; i--) + { + serializedProperty.DeleteArrayElementAtIndex(i); + } + } + + for (var i = 0; i < numElementToInsert; i++) + { + if (i >= numElementSerialized) + { + serializedProperty.InsertArrayElementAtIndex(i); + numElementSerialized = serializedProperty.arraySize; + } + + var objToEdit = serializedProperty.GetArrayElementAtIndex(i); + DataSourceType newValue = dstValues[i]; + serialziedPropertySetter.Invoke(objToEdit, newValue); + } + } + } + + public static void Setter(SerializedProperty serializedProperty, int value) + { + serializedProperty.intValue = value; + } + + public static void Setter(SerializedProperty serializedProperty, double value) + { + serializedProperty.doubleValue = value; + } + + public static void Setter(SerializedProperty serializedProperty, bool value) + { + serializedProperty.boolValue = value; + } + + public static void Setter(SerializedProperty serializedProperty, string value) + { + serializedProperty.stringValue = value; + } + + public static void Setter(SerializedProperty serializedProperty, long value) + { + serializedProperty.longValue = value; + } + + public static void Setter(SerializedProperty serializedProperty, Color value) + { + serializedProperty.colorValue = value; + } + + public static void Setter(SerializedProperty serializedProperty, AnimationCurve value) + { + serializedProperty.animationCurveValue = value; + } + + public static void Setter(SerializedProperty serializedProperty, Bounds value) + { + serializedProperty.boundsValue = value; + } + + public static void Setter(SerializedProperty serializedProperty, object value) + { + serializedProperty.boxedValue = value; + } } } diff --git a/Editor/Mono/Settings.cs b/Editor/Mono/Settings.cs index 5dd4feb09f..e50899f978 100644 --- a/Editor/Mono/Settings.cs +++ b/Editor/Mono/Settings.cs @@ -179,6 +179,7 @@ internal class PrefSettings { static List m_AddedPrefs = new List(); static SortedList m_Prefs = new SortedList(); + public static Action settingChanged; static internal void Add(IPrefType value) { @@ -218,6 +219,7 @@ static internal void Set(string name, T value) EditorPrefs.SetString(name, value.ToUniqueString()); m_Prefs[name] = value; + settingChanged?.Invoke(name, typeof(T)); } static internal IEnumerable> Prefs() @@ -232,6 +234,16 @@ static internal IEnumerable> Prefs() } } + static internal void RevertAll() + { + foreach (KeyValuePair kvp in Prefs()) + { + kvp.Value.ResetToDefault(); + EditorPrefs.SetString(kvp.Value.Name, kvp.Value.ToUniqueString()); + settingChanged?.Invoke(kvp.Key, typeof(T)); + } + } + static void Load() { if (!m_AddedPrefs.Any()) diff --git a/Editor/Mono/SettingsWindow/GraphicsSettingsEditors.cs b/Editor/Mono/SettingsWindow/GraphicsSettingsEditors.cs deleted file mode 100644 index b0239880f8..0000000000 --- a/Editor/Mono/SettingsWindow/GraphicsSettingsEditors.cs +++ /dev/null @@ -1,651 +0,0 @@ -// 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 UnityEditor.Build; -using UnityEngine.Rendering; -using EditorGraphicsSettings = UnityEditor.Rendering.EditorGraphicsSettings; -using TierSettings = UnityEditor.Rendering.TierSettings; -using GraphicsTier = UnityEngine.Rendering.GraphicsTier; -using ShaderQuality = UnityEditor.Rendering.ShaderQuality; - -// these are smallish editor's we use for GraphicsSettings editing - -namespace UnityEditor -{ - // - // builtin shaders - // - - internal partial class GraphicsSettingsWindow - { - internal class BuiltinShaderSettings - { - internal enum BuiltinShaderMode - { - None = 0, - Builtin, - Custom - } - - readonly SerializedProperty m_Mode; - readonly SerializedProperty m_Shader; - readonly GUIContent m_Label; - - internal BuiltinShaderSettings(GUIContent label, string name, SerializedObject serializedObject) - { - m_Mode = serializedObject.FindProperty(name + ".m_Mode"); - m_Shader = serializedObject.FindProperty(name + ".m_Shader"); - m_Label = label; - } - - internal void DoGUI() - { - EditorGUILayout.PropertyField(m_Mode, m_Label); - if (m_Mode.intValue == (int)BuiltinShaderMode.Custom) - EditorGUILayout.PropertyField(m_Shader); - } - } - - internal class BuiltinShadersEditor : Editor - { - BuiltinShaderSettings m_Deferred; - BuiltinShaderSettings m_DeferredReflections; - BuiltinShaderSettings m_ScreenSpaceShadows; - BuiltinShaderSettings m_DepthNormals; - BuiltinShaderSettings m_MotionVectors; - BuiltinShaderSettings m_LightHalo; - BuiltinShaderSettings m_LensFlare; - - internal class Styles - { - public static GUIContent deferredString = EditorGUIUtility.TrTextContent("Deferred", "Shader used for Deferred Shading."); - public static GUIContent deferredReflString = EditorGUIUtility.TrTextContent("Deferred Reflections", "Shader used for Deferred reflection probes."); - public static GUIContent screenShadowsString = EditorGUIUtility.TrTextContent("Screen Space Shadows", "Shader used for screen-space cascaded shadows."); - public static GUIContent depthNormalsString = EditorGUIUtility.TrTextContent("Depth Normals", "Shader used for depth and normals texture when enabled on a Camera."); - public static GUIContent motionVectorsString = EditorGUIUtility.TrTextContent("Motion Vectors", "Shader for generation of Motion Vectors when the rendering camera has renderMotionVectors set to true."); - public static GUIContent lightHaloString = EditorGUIUtility.TrTextContent("Light Halo", "Default Shader used for light halos."); - public static GUIContent lensFlareString = EditorGUIUtility.TrTextContent("Lens Flare", "Default Shader used for lens flares."); - } - - public void OnEnable() - { - m_Deferred = new BuiltinShaderSettings(Styles.deferredString, "m_Deferred", serializedObject); - m_DeferredReflections = new BuiltinShaderSettings(Styles.deferredReflString, "m_DeferredReflections", serializedObject); - m_ScreenSpaceShadows = new BuiltinShaderSettings(Styles.screenShadowsString, "m_ScreenSpaceShadows", serializedObject); - m_DepthNormals = new BuiltinShaderSettings(Styles.depthNormalsString, "m_DepthNormals", serializedObject); - m_MotionVectors = new BuiltinShaderSettings(Styles.motionVectorsString, "m_MotionVectors", serializedObject); - m_LightHalo = new BuiltinShaderSettings(Styles.lightHaloString, "m_LightHalo", serializedObject); - m_LensFlare = new BuiltinShaderSettings(Styles.lensFlareString, "m_LensFlare", serializedObject); - } - - public override void OnInspectorGUI() - { - serializedObject.Update(); - - m_Deferred.DoGUI(); - - // deferred reflections being off affects forward vs deferred style probe rendering; - // need to reload shaders for new platform macro to live update - EditorGUI.BeginChangeCheck(); - m_DeferredReflections.DoGUI(); - if (EditorGUI.EndChangeCheck()) - ShaderUtil.ReloadAllShaders(); - - m_ScreenSpaceShadows.DoGUI(); - m_DepthNormals.DoGUI(); - m_MotionVectors.DoGUI(); - m_LightHalo.DoGUI(); - m_LensFlare.DoGUI(); - - serializedObject.ApplyModifiedProperties(); - } - } - } - - - // - // video shaders - // - - - internal partial class GraphicsSettingsWindow - { - internal class VideoShadersEditor : Editor - { - SerializedProperty m_VideoShadersIncludeMode; - - internal class Styles - { - public static GUIContent modeString = EditorGUIUtility.TrTextContent("Video", "Shaders used by the VideoPlayer decoding and compositing."); - } - - public void OnEnable() - { - m_VideoShadersIncludeMode = serializedObject.FindProperty("m_VideoShadersIncludeMode"); - } - - public override void OnInspectorGUI() - { - serializedObject.Update(); - - EditorGUILayout.PropertyField(m_VideoShadersIncludeMode, Styles.modeString); - - serializedObject.ApplyModifiedProperties(); - } - } - } - - - // - // always included shaders - // - - - internal partial class GraphicsSettingsWindow - { - internal class AlwaysIncludedShadersEditor : Editor - { - SerializedProperty m_AlwaysIncludedShaders; - - public void OnEnable() - { - m_AlwaysIncludedShaders = serializedObject.FindProperty("m_AlwaysIncludedShaders"); - m_AlwaysIncludedShaders.isExpanded = true; - } - - public override void OnInspectorGUI() - { - serializedObject.Update(); - - EditorGUILayout.PropertyField(m_AlwaysIncludedShaders, true); - - serializedObject.ApplyModifiedProperties(); - } - } - } - - - // - // shader stripping - // - - - internal partial class GraphicsSettingsWindow - { - internal class ShaderStrippingEditor : Editor - { - SerializedProperty m_LightmapStripping; - SerializedProperty m_LightmapKeepPlain; - SerializedProperty m_LightmapKeepDirCombined; - SerializedProperty m_LightmapKeepDynamicPlain; - SerializedProperty m_LightmapKeepDynamicDirCombined; - SerializedProperty m_LightmapKeepShadowMask; - SerializedProperty m_LightmapKeepSubtractive; - SerializedProperty m_FogStripping; - SerializedProperty m_FogKeepLinear; - SerializedProperty m_FogKeepExp; - SerializedProperty m_FogKeepExp2; - SerializedProperty m_InstancingStripping; - SerializedProperty m_BrgStripping; - - public void OnEnable() - { - m_LightmapStripping = serializedObject.FindProperty("m_LightmapStripping"); - m_LightmapKeepPlain = serializedObject.FindProperty("m_LightmapKeepPlain"); - m_LightmapKeepDirCombined = serializedObject.FindProperty("m_LightmapKeepDirCombined"); - m_LightmapKeepDynamicPlain = serializedObject.FindProperty("m_LightmapKeepDynamicPlain"); - m_LightmapKeepDynamicDirCombined = serializedObject.FindProperty("m_LightmapKeepDynamicDirCombined"); - m_LightmapKeepShadowMask = serializedObject.FindProperty("m_LightmapKeepShadowMask"); - m_LightmapKeepSubtractive = serializedObject.FindProperty("m_LightmapKeepSubtractive"); - m_FogStripping = serializedObject.FindProperty("m_FogStripping"); - m_FogKeepLinear = serializedObject.FindProperty("m_FogKeepLinear"); - m_FogKeepExp = serializedObject.FindProperty("m_FogKeepExp"); - m_FogKeepExp2 = serializedObject.FindProperty("m_FogKeepExp2"); - m_InstancingStripping = serializedObject.FindProperty("m_InstancingStripping"); - m_BrgStripping = serializedObject.FindProperty("m_BrgStripping"); - } - - public override void OnInspectorGUI() - { - serializedObject.Update(); - - bool calcLightmapStripping = false, calcFogStripping = false; - - EditorGUILayout.PropertyField(m_LightmapStripping, Styles.lightmapModes); - - if (m_LightmapStripping.intValue != 0) - { - EditorGUI.indentLevel++; - EditorGUILayout.PropertyField(m_LightmapKeepPlain, Styles.lightmapPlain); - EditorGUILayout.PropertyField(m_LightmapKeepDirCombined, Styles.lightmapDirCombined); - EditorGUILayout.PropertyField(m_LightmapKeepDynamicPlain, Styles.lightmapDynamicPlain); - EditorGUILayout.PropertyField(m_LightmapKeepDynamicDirCombined, Styles.lightmapDynamicDirCombined); - EditorGUILayout.PropertyField(m_LightmapKeepShadowMask, Styles.lightmapKeepShadowMask); - EditorGUILayout.PropertyField(m_LightmapKeepSubtractive, Styles.lightmapKeepSubtractive); - EditorGUILayout.Space(); - - EditorGUILayout.BeginHorizontal(); - EditorGUILayout.PrefixLabel(GUIContent.Temp(" "), EditorStyles.miniButton); - - if (GUILayout.Button(Styles.lightmapFromScene, EditorStyles.miniButton, GUILayout.ExpandWidth(false))) - calcLightmapStripping = true; - - EditorGUILayout.EndHorizontal(); - EditorGUI.indentLevel--; - EditorGUILayout.Space(); - } - - EditorGUILayout.PropertyField(m_FogStripping, Styles.fogModes); - if (m_FogStripping.intValue != 0) - { - EditorGUI.indentLevel++; - EditorGUILayout.PropertyField(m_FogKeepLinear, Styles.fogLinear); - EditorGUILayout.PropertyField(m_FogKeepExp, Styles.fogExp); - EditorGUILayout.PropertyField(m_FogKeepExp2, Styles.fogExp2); - EditorGUILayout.Space(); - - EditorGUILayout.BeginHorizontal(); - EditorGUILayout.PrefixLabel(GUIContent.Temp(" "), EditorStyles.miniButton); - - if (GUILayout.Button(Styles.fogFromScene, EditorStyles.miniButton, GUILayout.ExpandWidth(false))) - calcFogStripping = true; - - EditorGUILayout.EndHorizontal(); - EditorGUI.indentLevel--; - EditorGUILayout.Space(); - } - - EditorGUILayout.PropertyField(m_InstancingStripping, Styles.instancingVariants); - EditorGUILayout.PropertyField(m_BrgStripping, Styles.brgVariants); - - serializedObject.ApplyModifiedProperties(); - - // need to do these after ApplyModifiedProperties, since it changes their values from native code - if (calcLightmapStripping) - ShaderUtil.CalculateLightmapStrippingFromCurrentScene(); - if (calcFogStripping) - ShaderUtil.CalculateFogStrippingFromCurrentScene(); - } - - internal partial class Styles - { - public static readonly GUIContent shaderSettings = EditorGUIUtility.TrTextContent("Platform shader settings"); - public static readonly GUIContent builtinSettings = EditorGUIUtility.TrTextContent("Built-in shader settings"); - public static readonly GUIContent shaderPreloadSettings = EditorGUIUtility.TrTextContent("Shader preloading"); - - public static readonly GUIContent lightmapModes = EditorGUIUtility.TrTextContent("Lightmap Modes"); - public static readonly GUIContent lightmapPlain = EditorGUIUtility.TrTextContent("Baked Non-Directional", "Include support for baked non-directional lightmaps."); - public static readonly GUIContent lightmapDirCombined = EditorGUIUtility.TrTextContent("Baked Directional", "Include support for baked directional lightmaps."); - public static readonly GUIContent lightmapKeepShadowMask = EditorGUIUtility.TrTextContent("Baked Shadowmask", "Include support for baked shadow occlusion."); - public static readonly GUIContent lightmapKeepSubtractive = EditorGUIUtility.TrTextContent("Baked Subtractive", "Include support for baked subtractive lightmaps."); - public static readonly GUIContent lightmapDynamicPlain = EditorGUIUtility.TrTextContent("Realtime Non-Directional", "Include support for realtime non-directional lightmaps."); - public static readonly GUIContent lightmapDynamicDirCombined = EditorGUIUtility.TrTextContent("Realtime Directional", "Include support for realtime directional lightmaps."); - public static readonly GUIContent lightmapFromScene = EditorGUIUtility.TrTextContent("Import From Current Scene", "Calculate lightmap modes used by the current scene."); - - public static readonly GUIContent fogModes = EditorGUIUtility.TrTextContent("Fog Modes"); - public static readonly GUIContent fogLinear = EditorGUIUtility.TrTextContent("Linear", "Include support for Linear fog."); - public static readonly GUIContent fogExp = EditorGUIUtility.TrTextContent("Exponential", "Include support for Exponential fog."); - public static readonly GUIContent fogExp2 = EditorGUIUtility.TrTextContent("Exponential Squared", "Include support for Exponential Squared fog."); - public static readonly GUIContent fogFromScene = EditorGUIUtility.TrTextContent("Import From Current Scene", "Calculate fog modes used by the current scene."); - - public static readonly GUIContent instancingVariants = EditorGUIUtility.TrTextContent("Instancing Variants"); - public static readonly GUIContent brgVariants = EditorGUIUtility.TrTextContent("BatchRendererGroup Variants"); - - public static readonly GUIContent shaderPreloadSave = EditorGUIUtility.TrTextContent("Save to asset...", "Save currently tracked shaders into a Shader Variant Manifest asset."); - public static readonly GUIContent shaderPreloadClear = EditorGUIUtility.TrTextContent("Clear", "Clear currently tracked shader variant information."); - } - } - } - - - // - // preloaded shaders - // - - - internal partial class GraphicsSettingsWindow - { - internal class ShaderPreloadEditor : Editor - { - SerializedProperty m_PreloadedShaders; - SerializedProperty m_PreloadShadersBatchTimeLimit; - bool m_DelayShaderPreload; - int m_PreloadShadersTimeLimit; - - public void OnEnable() - { - m_PreloadedShaders = serializedObject.FindProperty("m_PreloadedShaders"); - m_PreloadedShaders.isExpanded = true; - m_PreloadShadersBatchTimeLimit = serializedObject.FindProperty("m_PreloadShadersBatchTimeLimit"); - LoadShaderPreloadingDelay(); - } - - public override void OnInspectorGUI() - { - serializedObject.Update(); - - serializedObject.ApplyModifiedProperties(); - - EditorGUILayout.PropertyField(m_PreloadedShaders, true); - - m_DelayShaderPreload = EditorGUILayout.Toggle("Preload shaders after showing first scene", m_DelayShaderPreload); - if (m_DelayShaderPreload) - m_PreloadShadersTimeLimit = EditorGUILayout.IntField("Preload time limit per frame (ms)", m_PreloadShadersTimeLimit); - SaveShaderPreloadingDelay(); - - EditorGUILayout.Space(); - GUILayout.Label( - string.Format("Currently tracked: {0} shaders {1} total variants", - ShaderUtil.GetCurrentShaderVariantCollectionShaderCount(), ShaderUtil.GetCurrentShaderVariantCollectionVariantCount() - ) - ); - - EditorGUILayout.BeginHorizontal(); - GUILayout.FlexibleSpace(); - if (GUILayout.Button(Styles.shaderPreloadSave, EditorStyles.miniButton)) - { - string message = "Save shader variant collection"; - string assetPath = EditorUtility.SaveFilePanelInProject("Save Shader Variant Collection", "NewShaderVariants", "shadervariants", message, ProjectWindowUtil.GetActiveFolderPath()); - if (!string.IsNullOrEmpty(assetPath)) - ShaderUtil.SaveCurrentShaderVariantCollection(assetPath); - GUIUtility.ExitGUI(); - } - if (GUILayout.Button(Styles.shaderPreloadClear, EditorStyles.miniButton)) - ShaderUtil.ClearCurrentShaderVariantCollection(); - EditorGUILayout.EndHorizontal(); - - serializedObject.ApplyModifiedProperties(); - } - - void LoadShaderPreloadingDelay() - { - m_PreloadShadersTimeLimit = m_PreloadShadersBatchTimeLimit.intValue; - m_DelayShaderPreload = m_PreloadShadersTimeLimit >= 0; - if (!m_DelayShaderPreload) - m_PreloadShadersTimeLimit = 0; - } - - void SaveShaderPreloadingDelay() - { - var newVal = m_DelayShaderPreload ? m_PreloadShadersTimeLimit : -1; - if (m_PreloadShadersBatchTimeLimit.intValue != newVal) - m_PreloadShadersBatchTimeLimit.intValue = newVal; - } - - internal partial class Styles - { - public static readonly GUIContent shaderPreloadSave = EditorGUIUtility.TrTextContent("Save to asset...", "Save currently tracked shaders into a Shader Variant Manifest asset."); - public static readonly GUIContent shaderPreloadClear = EditorGUIUtility.TrTextContent("Clear", "Clear currently tracked shader variant information."); - } - } - } - - - // - // tier settings - // - - - internal partial class GraphicsSettingsWindow - { - internal class TierSettingsEditor : Editor - { - public bool verticalLayout = false; - - internal void OnFieldLabelsGUI(bool vertical) - { - bool usingSRP = GraphicsSettings.currentRenderPipeline != null; - - if (!vertical) - EditorGUILayout.LabelField(Styles.standardShaderSettings, EditorStyles.boldLabel); - - if (!usingSRP) - { - EditorGUILayout.LabelField(Styles.standardShaderQuality); - EditorGUILayout.LabelField(Styles.reflectionProbeBoxProjection); - EditorGUILayout.LabelField(Styles.reflectionProbeBlending); - EditorGUILayout.LabelField(Styles.detailNormalMap); - EditorGUILayout.LabelField(Styles.semitransparentShadows); - } - - if (SupportedRenderingFeatures.active.lightProbeProxyVolumes) - EditorGUILayout.LabelField(Styles.enableLPPV); - - if (!vertical) - { - EditorGUILayout.LabelField(Styles.empty, EditorStyles.boldLabel); - EditorGUILayout.LabelField(Styles.renderingSettings, EditorStyles.boldLabel); - } - - if (!usingSRP) - { - EditorGUILayout.LabelField(Styles.cascadedShadowMaps); - EditorGUILayout.LabelField(Styles.prefer32BitShadowMaps); - EditorGUILayout.LabelField(Styles.useHDR); - EditorGUILayout.LabelField(Styles.hdrMode); - EditorGUILayout.LabelField(Styles.renderingPath); - } - - if (SupportedRenderingFeatures.IsLightmapBakeTypeSupported(LightmapBakeType.Realtime)) - EditorGUILayout.LabelField(Styles.realtimeGICPUUsage); - } - - // custom enum handling - internal partial class Styles - { - public static readonly GUIContent[] shaderQualityName = - { EditorGUIUtility.TrTextContent("Low"), EditorGUIUtility.TrTextContent("Medium"), EditorGUIUtility.TrTextContent("High") }; - public static readonly int[] shaderQualityValue = - { (int)ShaderQuality.Low, (int)ShaderQuality.Medium, (int)ShaderQuality.High }; - - public static readonly GUIContent[] renderingPathName = - { EditorGUIUtility.TrTextContent("Forward"), EditorGUIUtility.TrTextContent("Deferred"), EditorGUIUtility.TrTextContent("Legacy Vertex Lit") }; - public static readonly int[] renderingPathValue = - { (int)RenderingPath.Forward, (int)RenderingPath.DeferredShading, (int)RenderingPath.VertexLit }; - - public static readonly GUIContent[] hdrModeName = - { EditorGUIUtility.TrTextContent("FP16"), EditorGUIUtility.TrTextContent("R11G11B10") }; - public static readonly int[] hdrModeValue = - { (int)CameraHDRMode.FP16, (int)CameraHDRMode.R11G11B10}; - - public static readonly GUIContent[] realtimeGICPUUsageName = - { EditorGUIUtility.TrTextContent("Low"), EditorGUIUtility.TrTextContent("Medium"), EditorGUIUtility.TrTextContent("High"), EditorGUIUtility.TrTextContent("Unlimited")}; - public static readonly int[] realtimeGICPUUsageValue = - { (int)RealtimeGICPUUsage.Low, (int)RealtimeGICPUUsage.Medium, (int)RealtimeGICPUUsage.High, (int)RealtimeGICPUUsage.Unlimited }; - } - - internal ShaderQuality ShaderQualityPopup(ShaderQuality sq) - { - return (ShaderQuality)EditorGUILayout.IntPopup((int)sq, Styles.shaderQualityName, Styles.shaderQualityValue); - } - - internal RenderingPath RenderingPathPopup(RenderingPath rp) - { - return (RenderingPath)EditorGUILayout.IntPopup((int)rp, Styles.renderingPathName, Styles.renderingPathValue); - } - - internal CameraHDRMode HDRModePopup(CameraHDRMode mode) - { - return (CameraHDRMode)EditorGUILayout.IntPopup((int)mode, Styles.hdrModeName, Styles.hdrModeValue); - } - - internal RealtimeGICPUUsage RealtimeGICPUUsagePopup(RealtimeGICPUUsage usage) - { - return (RealtimeGICPUUsage)EditorGUILayout.IntPopup((int)usage, Styles.realtimeGICPUUsageName, Styles.realtimeGICPUUsageValue); - } - - internal void OnTierGUI(BuildPlatform platform, GraphicsTier tier, bool vertical) - { - TierSettings ts = EditorGraphicsSettings.GetTierSettings(platform.namedBuildTarget, tier); - - EditorGUI.BeginChangeCheck(); - - - if (!vertical) - EditorGUILayout.LabelField(Styles.empty, EditorStyles.boldLabel); - - bool usingSRP = GraphicsSettings.currentRenderPipeline != null; - if (!usingSRP) - { - ts.standardShaderQuality = ShaderQualityPopup(ts.standardShaderQuality); - ts.reflectionProbeBoxProjection = EditorGUILayout.Toggle(ts.reflectionProbeBoxProjection); - ts.reflectionProbeBlending = EditorGUILayout.Toggle(ts.reflectionProbeBlending); - ts.detailNormalMap = EditorGUILayout.Toggle(ts.detailNormalMap); - ts.semitransparentShadows = EditorGUILayout.Toggle(ts.semitransparentShadows); - } - - if (SupportedRenderingFeatures.active.lightProbeProxyVolumes) - ts.enableLPPV = EditorGUILayout.Toggle(ts.enableLPPV); - - if (!vertical) - { - EditorGUILayout.LabelField(Styles.empty, EditorStyles.boldLabel); - EditorGUILayout.LabelField(Styles.empty, EditorStyles.boldLabel); - } - - if (!usingSRP) - { - ts.cascadedShadowMaps = EditorGUILayout.Toggle(ts.cascadedShadowMaps); - ts.prefer32BitShadowMaps = EditorGUILayout.Toggle(ts.prefer32BitShadowMaps); - ts.hdr = EditorGUILayout.Toggle(ts.hdr); - ts.hdrMode = HDRModePopup(ts.hdrMode); - ts.renderingPath = RenderingPathPopup(ts.renderingPath); - } - - if (SupportedRenderingFeatures.IsLightmapBakeTypeSupported(LightmapBakeType.Realtime)) - ts.realtimeGICPUUsage = RealtimeGICPUUsagePopup(ts.realtimeGICPUUsage); - - if (EditorGUI.EndChangeCheck()) - { - // TODO: it should be doable in c# now as we "expose" GraphicsSettings anyway - EditorGraphicsSettings.RegisterUndo(); - EditorGraphicsSettings.SetTierSettings(platform.namedBuildTarget, tier, ts); - } - } - - internal void OnGuiHorizontal(BuildPlatform platform) - { - EditorGUILayout.BeginHorizontal(); - - EditorGUILayout.BeginVertical(); - EditorGUIUtility.labelWidth = 140; - EditorGUILayout.LabelField(Styles.empty, EditorStyles.boldLabel); - OnFieldLabelsGUI(false); - EditorGUILayout.LabelField(Styles.empty, EditorStyles.boldLabel); - EditorGUILayout.LabelField(Styles.autoSettings, EditorStyles.boldLabel); - EditorGUILayout.EndVertical(); - - EditorGUIUtility.labelWidth = 50; - foreach (GraphicsTier tier in Enum.GetValues(typeof(GraphicsTier))) - { - bool autoSettings = EditorGraphicsSettings.AreTierSettingsAutomatic(platform.namedBuildTarget.ToBuildTargetGroup(), tier); - - EditorGUILayout.BeginVertical(); - EditorGUILayout.LabelField(Styles.tierName[(int)tier], EditorStyles.boldLabel); - using (new EditorGUI.DisabledScope(autoSettings)) - OnTierGUI(platform, tier, false); - - EditorGUILayout.LabelField(Styles.empty, EditorStyles.boldLabel); - EditorGUI.BeginChangeCheck(); - autoSettings = EditorGUILayout.Toggle(autoSettings); - if (EditorGUI.EndChangeCheck()) - { - EditorGraphicsSettings.RegisterUndo(); - EditorGraphicsSettings.MakeTierSettingsAutomatic(platform.namedBuildTarget.ToBuildTargetGroup(), tier, autoSettings); - EditorGraphicsSettings.OnUpdateTierSettings(platform.namedBuildTarget.ToBuildTargetGroup(), true); - } - EditorGUILayout.EndVertical(); - } - EditorGUIUtility.labelWidth = 0; - - EditorGUILayout.EndHorizontal(); - } - - internal void OnGuiVertical(BuildPlatform platform) - { - EditorGUILayout.BeginVertical(); - foreach (GraphicsTier tier in Enum.GetValues(typeof(GraphicsTier))) - { - bool autoSettings = EditorGraphicsSettings.AreTierSettingsAutomatic(platform.namedBuildTarget.ToBuildTargetGroup(), tier); - EditorGUI.BeginChangeCheck(); - { - GUILayout.BeginHorizontal(); - EditorGUIUtility.labelWidth = 80; - EditorGUILayout.LabelField(Styles.tierName[(int)tier], EditorStyles.boldLabel); - GUILayout.FlexibleSpace(); - EditorGUIUtility.labelWidth = 80; - autoSettings = EditorGUILayout.Toggle(Styles.autoSettings, autoSettings); - GUILayout.EndHorizontal(); - } - - if (EditorGUI.EndChangeCheck()) - { - EditorGraphicsSettings.RegisterUndo(); - EditorGraphicsSettings.MakeTierSettingsAutomatic(platform.namedBuildTarget.ToBuildTargetGroup(), tier, autoSettings); - EditorGraphicsSettings.OnUpdateTierSettings(platform.namedBuildTarget.ToBuildTargetGroup(), true); - } - - using (new EditorGUI.DisabledScope(autoSettings)) - { - EditorGUI.indentLevel++; - EditorGUILayout.BeginHorizontal(); - - EditorGUILayout.BeginVertical(); - EditorGUIUtility.labelWidth = 140; - OnFieldLabelsGUI(true); - EditorGUILayout.EndVertical(); - - EditorGUILayout.BeginVertical(); - EditorGUIUtility.labelWidth = 50; - OnTierGUI(platform, tier, true); - EditorGUILayout.EndVertical(); - - GUILayout.EndHorizontal(); - EditorGUI.indentLevel--; - } - } - GUILayout.EndVertical(); - EditorGUIUtility.labelWidth = 0; - } - - public override void OnInspectorGUI() - { - BuildPlatform[] validPlatforms = BuildPlatforms.instance.GetValidPlatforms().ToArray(); - BuildPlatform platform = validPlatforms[EditorGUILayout.BeginPlatformGrouping(validPlatforms, null, EditorStyles.frameBox)]; - - if (verticalLayout) OnGuiVertical(platform); - else OnGuiHorizontal(platform); - - EditorGUILayout.EndPlatformGrouping(); - } - - internal partial class Styles - { - public static readonly GUIContent[] tierName = - { EditorGUIUtility.TrTextContent("Low (Tier 1)"), EditorGUIUtility.TrTextContent("Medium (Tier 2)"), EditorGUIUtility.TrTextContent("High (Tier 3)") }; - - public static readonly GUIContent empty = EditorGUIUtility.TextContent(""); - public static readonly GUIContent autoSettings = EditorGUIUtility.TrTextContent("Use Defaults"); - - public static readonly GUIContent standardShaderSettings = EditorGUIUtility.TrTextContent("Standard Shader"); - public static readonly GUIContent renderingSettings = EditorGUIUtility.TrTextContent("Rendering"); - - public static readonly GUIContent standardShaderQuality = EditorGUIUtility.TrTextContent("Standard Shader Quality"); - public static readonly GUIContent reflectionProbeBoxProjection = EditorGUIUtility.TrTextContent("Reflection Probes Box Projection", "Enable projection for reflection UV mappings on Reflection Probes."); - public static readonly GUIContent reflectionProbeBlending = EditorGUIUtility.TrTextContent("Reflection Probes Blending", "Gradually fade out one probe's cubemap while fading in the other's as the reflective object passes from one zone to the other."); - public static readonly GUIContent detailNormalMap = EditorGUIUtility.TrTextContent("Detail Normal Map", "Enable Detail (secondary) Normal Map sampling for up-close viewing, if assigned."); - public static readonly GUIContent cascadedShadowMaps = EditorGUIUtility.TrTextContent("Cascaded Shadows"); - public static readonly GUIContent prefer32BitShadowMaps = EditorGUIUtility.TrTextContent("Prefer 32-bit shadow maps", "Enable 32-bit float shadow map when you are targeting PS4 or platforms using DX11 or DX12."); - public static readonly GUIContent semitransparentShadows = EditorGUIUtility.TrTextContent("Enable Semitransparent Shadows"); - public static readonly GUIContent enableLPPV = EditorGUIUtility.TrTextContent("Enable Light Probe Proxy Volume", "Enable rendering a 3D grid of interpolated Light Probes inside a Bounding Volume."); - public static readonly GUIContent renderingPath = EditorGUIUtility.TrTextContent("Rendering Path", "Choose how Unity should render graphics. Different rendering paths affect the performance of your game, and how lighting and shading are calculated."); - public static readonly GUIContent useHDR = EditorGUIUtility.TrTextContent("Use HDR", "Enable High Dynamic Range rendering for this tier."); - public static readonly GUIContent hdrMode = EditorGUIUtility.TrTextContent("HDR Mode", "Color render texture format for the HDR buffer to use when HDR is enabled."); - public static readonly GUIContent realtimeGICPUUsage = EditorGUIUtility.TrTextContent("Realtime Global Illumination CPU Usage", "How many CPU worker threads to create for Realtime Global Illumination lighting calculations in the Player. Increasing this makes the system react faster to changes in lighting at a cost of using more CPU time. The higher the CPU Usage value, the more worker threads are created for solving Realtime GI."); - } - } - } -} diff --git a/Editor/Mono/ShaderUtil.bindings.cs b/Editor/Mono/ShaderUtil.bindings.cs index 559be05539..622eb02d92 100644 --- a/Editor/Mono/ShaderUtil.bindings.cs +++ b/Editor/Mono/ShaderUtil.bindings.cs @@ -209,7 +209,6 @@ public static void UpdateShaderAsset(Shader shader, string source, bool compileI extern internal static void OpenCompiledShader(Shader shader, int mode, int externPlatformsMask, bool includeAllVariants, bool preprocessOnly, bool stripLineDirectives); - extern internal static void CompileShaderForTargetCompilerPlatform(Shader shader, ShaderCompilerPlatform platform); extern internal static void OpenCompiledComputeShader(ComputeShader shader, bool allVariantsAndPlatforms, bool showPreprocessed, bool stripLineDirectives); extern internal static void OpenParsedSurfaceShader(Shader shader); @@ -343,9 +342,9 @@ extern internal static ShaderData.PreprocessedVariant PreprocessShaderVariant([N [FreeFunction("ShaderUtil::GetPassKeywords")] extern private static LocalKeyword[] GetPassAllStageKeywords(Shader s, in PassIdentifier passIdentifier); [FreeFunction("ShaderUtil::GetPassKeywords")] extern private static LocalKeyword[] GetPassStageKeywords(Shader s, in PassIdentifier passIdentifier, ShaderType shaderType); [FreeFunction("ShaderUtil::GetPassKeywords")] extern private static LocalKeyword[] GetPassStageKeywordsForAPI(Shader s, in PassIdentifier passIdentifier, ShaderType shaderType, ShaderCompilerPlatform shaderCompilerPlatform); - [FreeFunction("ShaderUtil::PassHasKeyword")] extern private static bool PassAnyStageHasKeyword(Shader s, in PassIdentifier passIdentifier, in LocalKeyword keyword); - [FreeFunction("ShaderUtil::PassHasKeyword")] extern private static bool PassStageHasKeyword(Shader s, in PassIdentifier passIdentifier, in LocalKeyword keyword, ShaderType shaderType); - [FreeFunction("ShaderUtil::PassHasKeyword")] extern private static bool PassStageHasKeywordForAPI(Shader s, in PassIdentifier passIdentifier, in LocalKeyword keyword, ShaderType shaderType, ShaderCompilerPlatform shaderCompilerPlatform); + [FreeFunction("ShaderUtil::PassHasKeyword")] extern private static bool PassAnyStageHasKeyword(Shader s, in PassIdentifier passIdentifier, uint keywordIndex); + [FreeFunction("ShaderUtil::PassHasKeyword")] extern private static bool PassStageHasKeyword(Shader s, in PassIdentifier passIdentifier, uint keywordIndex, ShaderType shaderType); + [FreeFunction("ShaderUtil::PassHasKeyword")] extern private static bool PassStageHasKeywordForAPI(Shader s, in PassIdentifier passIdentifier, uint keywordIndex, ShaderType shaderType, ShaderCompilerPlatform shaderCompilerPlatform); public static LocalKeyword[] GetPassKeywords(Shader s, in PassIdentifier passIdentifier) { @@ -364,17 +363,17 @@ public static LocalKeyword[] GetPassKeywords(Shader s, in PassIdentifier passIde public static bool PassHasKeyword(Shader s, in PassIdentifier passIdentifier, in LocalKeyword keyword) { - return PassAnyStageHasKeyword(s, passIdentifier, keyword); + return PassAnyStageHasKeyword(s, passIdentifier, keyword.m_Index); } public static bool PassHasKeyword(Shader s, in PassIdentifier passIdentifier, in LocalKeyword keyword, ShaderType shaderType) { - return PassStageHasKeyword(s, passIdentifier, keyword, shaderType); + return PassStageHasKeyword(s, passIdentifier, keyword.m_Index, shaderType); } public static bool PassHasKeyword(Shader s, in PassIdentifier passIdentifier, in LocalKeyword keyword, ShaderType shaderType, ShaderCompilerPlatform shaderCompilerPlatform) { - return PassStageHasKeywordForAPI(s, passIdentifier, keyword, shaderType, shaderCompilerPlatform); + return PassStageHasKeywordForAPI(s, passIdentifier, keyword.m_Index, shaderType, shaderCompilerPlatform); } } } diff --git a/Editor/Mono/Shaders/ShaderKeywordFilterData.cs b/Editor/Mono/Shaders/ShaderKeywordFilterData.cs index 34dade6ff2..8941e8c86c 100644 --- a/Editor/Mono/Shaders/ShaderKeywordFilterData.cs +++ b/Editor/Mono/Shaders/ShaderKeywordFilterData.cs @@ -189,12 +189,15 @@ private static Constraints GetConstraintAttributes(ICustomAttributeProvider ap) // settings tree structure out of it. // Returns null if no filter attributes were found. // This method is called recursively for the tree traversing purposes. - internal static SettingsNode GatherFilterData(string nodeName, object containerObject, Constraints parentConstraints = null) + internal static SettingsNode GatherFilterData(string nodeName, object containerObject, HashSet visited, Constraints parentConstraints = null) { SettingsNode node = null; // defer construction to when filter data is actually found if (containerObject == null) return node; + if (!visited.Add(containerObject)) + return node; + var containerConstraints = GetConstraintAttributes(containerObject.GetType()); if (parentConstraints != null) { @@ -233,7 +236,8 @@ internal static SettingsNode GatherFilterData(string nodeName, object containerO bool hasDifferentChildren = (node != null) && (node.m_Children.Count > 0); foreach (var e in enumerable) { - SettingsNode newBranch = GatherFilterData(f.Name, e, containerConstraints); + var childVisited = new HashSet(visited); + SettingsNode newBranch = GatherFilterData(f.Name, e, childVisited, containerConstraints); if (newBranch != null) { if (node == null) @@ -252,7 +256,8 @@ internal static SettingsNode GatherFilterData(string nodeName, object containerO } else if (!type.IsValueType || (type.IsValueType && !type.IsPrimitive && !type.IsEnum)) // class or struct { - var nestedNode = GatherFilterData(f.Name, value, containerConstraints); + var childVisited = new HashSet(visited); + var nestedNode = GatherFilterData(f.Name, value, childVisited, containerConstraints); if (nestedNode != null) { if (node == null) diff --git a/Editor/Mono/Shaders/ShaderKeywordFilterUtil.cs b/Editor/Mono/Shaders/ShaderKeywordFilterUtil.cs index 1b0eb60286..3e923fe666 100644 --- a/Editor/Mono/Shaders/ShaderKeywordFilterUtil.cs +++ b/Editor/Mono/Shaders/ShaderKeywordFilterUtil.cs @@ -84,6 +84,40 @@ internal struct ConstraintState [RequiredByNativeCode] internal static class ShaderKeywordFilterUtil { + internal struct CachedFilterData + { + public Hash128 dependencyHash; + public SettingsNode settingsNode; + }; + + // In memory cache for filter data per renderpipeline asset. + // This is to avoid redundant attribute search for each shader/pass/stage. + internal static Dictionary PerAssetFilterDataCache = new Dictionary(); + + internal static SettingsNode GetFilterDataCached(string nodeName, UnityEngine.Object containerObject) + { + string assetPath = AssetDatabase.GetAssetPath(containerObject); + Hash128 dependencyHash = AssetDatabase.GetAssetDependencyHash(assetPath); + + CachedFilterData cachedData; + if (PerAssetFilterDataCache.TryGetValue(assetPath, out cachedData)) + { + // Cached data is valid only if dependency hash hasn't changed + if (cachedData.dependencyHash == dependencyHash) + return cachedData.settingsNode; + } + + // No valid data found in the cache so we need to do the full processing + // and then enter the result into the cache. + var visited = new HashSet(); + cachedData.dependencyHash = dependencyHash; + cachedData.settingsNode = SettingsNode.GatherFilterData(nodeName, containerObject, visited); + + PerAssetFilterDataCache[assetPath] = cachedData; + + return cachedData.settingsNode; + } + // For the current build target with given constraint state, gets the list of active filter rule sets. [RequiredByNativeCode] internal static SettingsVariant[] GetKeywordFilterVariants(string buildTargetGroupName, ConstraintState constraintState) @@ -98,7 +132,7 @@ internal static SettingsVariant[] GetKeywordFilterVariants(string buildTargetGro if (rpAsset == null) continue; - var node = SettingsNode.GatherFilterData(rpAsset.name, rpAsset); + var node = GetFilterDataCached(rpAsset.name, rpAsset); if (node != null) root.Children.Add(node); } diff --git a/Editor/Mono/Tutorial/Highlighter.bindings.cs b/Editor/Mono/Tutorial/Highlighter.bindings.cs index a3dba272a8..421b68fddd 100644 --- a/Editor/Mono/Tutorial/Highlighter.bindings.cs +++ b/Editor/Mono/Tutorial/Highlighter.bindings.cs @@ -32,8 +32,8 @@ public static extern string activeText [NativeProperty("s_ActiveVisible", false, TargetType.Field)] public static extern bool activeVisible { get; private set; } - [NativeProperty("s_ActiveIsImguiContainer", false, TargetType.Field)] - internal static extern bool activeIsImguiContainer { get; private set; } + [NativeProperty("s_UseUIToolkitScrolling", false, TargetType.Field)] + internal static extern bool useUIToolkitScrolling { get; private set; } [NativeProperty("k_Padding", false, TargetType.Field)] internal static extern float padding { get; } diff --git a/Editor/Mono/Tutorial/Highlighter.cs b/Editor/Mono/Tutorial/Highlighter.cs index cbbb708542..0aa0cc6fd8 100644 --- a/Editor/Mono/Tutorial/Highlighter.cs +++ b/Editor/Mono/Tutorial/Highlighter.cs @@ -29,11 +29,14 @@ public partial class Highlighter private const float kPulseSpeed = 0.45f; // Pulses per second private const float kPopupDuration = 0.33f; // How long time in seconds the popup takes private const int kExpansionMovementSize = 5; // How many pixels the rect expands out in the pulsing movement + // Twice the IMGUI speed because UIToolkit ScrollView scroll offset has a frame delay between updates + private static readonly float kUIToolkitScrollSpeed = scrollSpeed * 2; private static bool s_RecursionLock = false; private static VisualElement activeElement = null; private static ScrollView activeScrollView = null; + private static bool activeIsImgui = false; private static GUIStyle s_HighlightStyle; private static GUIStyle highlightStyle @@ -54,8 +57,9 @@ public static void Stop() activeVisible = false; activeText = string.Empty; activeElement = null; - activeIsImguiContainer = false; + activeIsImgui = false; activeScrollView = null; + useUIToolkitScrolling = true; activeRect = new Rect(); searchMode = HighlightSearchMode.None; @@ -148,7 +152,7 @@ private static void Update() // If view's actualView has changed, we might still find a property with the right name in the other view, // but it's not the one we're looking for. - var elementHidden = activeElement != null && (!activeElement.isHierarchyDisplayed || activeElement.panel == null); + var elementHidden = activeElement != null && (!activeElement.areAncestorsAndSelfDisplayed || activeElement.panel == null); if (activeRect.width == 0 || !ViewWindowIsActive() || elementHidden) { EditorApplication.update -= Update; @@ -165,7 +169,7 @@ private static void Update() Search(); } - if (isUIToolkitWindow) + if (useUIToolkitScrolling) HandleScroll(); // Keep elapsed time explicitly rather than measuring time since highlight began. @@ -246,7 +250,7 @@ private static bool Search() { searchMode = s_SearchMode; - if (isUIToolkitWindow && !activeIsImguiContainer) + if (isUIToolkitWindow && !activeIsImgui) { var found = activeElement != null; if (!found) @@ -261,15 +265,15 @@ private static bool Search() return true; } - activeIsImguiContainer = true; + activeIsImgui = true; } // Try IMGUI s_View.RepaintImmediately(); if (searchMode == HighlightSearchMode.None) { - if (!activeIsImguiContainer) - return true; // Success - control was found in pure Imgui window + if (activeIsImgui && !useUIToolkitScrolling) + return true; // Success - control was found in window that doesn't use UIToolkit ScrollView if (activeElement != null) return true; // Already found the IMGUIContainer @@ -280,10 +284,8 @@ private static bool Search() r.y -= windowPos.y; activeElement = SearchIMGUIContainer(s_ViewWindow.rootVisualElement, r.center); - if (activeElement != null) - return true; - - activeIsImguiContainer = false; + useUIToolkitScrolling = activeScrollView != null; + return true; } s_SearchMode = HighlightSearchMode.None; @@ -358,7 +360,7 @@ private static void HandleScroll() var paddedRect = new Rect(r.x - padding, r.y - padding, r.width + padding * 2, r.height + padding * 2); - activeVisible = !ScrollTowardsRect(activeScrollView, paddedRect, scrollSpeed); + activeVisible = !ScrollTowardsRect(activeScrollView, paddedRect, kUIToolkitScrollSpeed); } else { diff --git a/Editor/Mono/TypeCache.bindings.cs b/Editor/Mono/TypeCache.bindings.cs index 21289569b2..4342f88262 100644 --- a/Editor/Mono/TypeCache.bindings.cs +++ b/Editor/Mono/TypeCache.bindings.cs @@ -115,7 +115,7 @@ public int IndexOf(object item) static extern Type GetValue(IntPtr key, int index); [ThreadSafe] - extern void Internal_CopyTo(Type[] array, int arrayIndex); + extern void Internal_CopyTo([Unmarshalled] Type[] array, int arrayIndex); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); @@ -338,7 +338,7 @@ public int IndexOf(object item) static extern MethodInfo GetValue(IntPtr key, int index); [ThreadSafe] - extern void Internal_CopyTo(MethodInfo[] array, int arrayIndex); + extern void Internal_CopyTo([Unmarshalled] MethodInfo[] array, int arrayIndex); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); @@ -562,7 +562,7 @@ public int IndexOf(object item) static extern FieldInfo GetValue(IntPtr key, int index); [ThreadSafe] - extern void Internal_CopyTo(FieldInfo[] array, int arrayIndex); + extern void Internal_CopyTo([Unmarshalled] FieldInfo[] array, int arrayIndex); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/Editor/Mono/UIElements/Controls/ColorField.cs b/Editor/Mono/UIElements/Controls/ColorField.cs index a8d7e3ad70..bbf6a4ae0f 100644 --- a/Editor/Mono/UIElements/Controls/ColorField.cs +++ b/Editor/Mono/UIElements/Controls/ColorField.cs @@ -2,16 +2,21 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using Unity.Properties; using UnityEngine; using UnityEngine.UIElements; namespace UnityEditor.UIElements { /// - /// Makes a field for selecting a color. + /// Makes a field for selecting a color. For more information, refer to [[wiki:UIE-uxml-element-ColorField|UXML element ColorField]]. /// public class ColorField : BaseField { + internal static readonly DataBindingProperty showEyeDropperProperty = nameof(showEyeDropper); + internal static readonly DataBindingProperty showAlphaProperty = nameof(showAlpha); + internal static readonly DataBindingProperty hdrProperty = nameof(hdr); + /// /// Instantiates a using the data read from a UXML file. /// @@ -45,39 +50,50 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext /// /// If true, the color picker will show the eyedropper control. If false, the color picker won't show the eyedropper control. /// + [CreateProperty] public bool showEyeDropper { get => m_ShowEyeDropper; set { + var previous = m_ShowEyeDropper; m_ShowEyeDropper = value; if (m_EyeDropperElement != null) m_EyeDropperElement.style.display = m_ShowEyeDropper ? DisplayStyle.Flex : DisplayStyle.None; + + if (previous != m_ShowEyeDropper) + NotifyPropertyChanged(showEyeDropperProperty); } } /// /// If true, allows the user to set an alpha value for the color. If false, hides the alpha component. /// + [CreateProperty] public bool showAlpha { get => m_ShowAlpha; set { + var previous = m_ShowAlpha; m_ShowAlpha = value; if (m_AlphaElement != null) m_AlphaElement.style.display = m_ShowAlpha ? DisplayStyle.Flex : DisplayStyle.None; + if (previous != m_ShowAlpha) + NotifyPropertyChanged(showAlphaProperty); } } /// /// If true, treats the color as an HDR value. If false, treats the color as a standard LDR value. /// + [CreateProperty] public bool hdr { get => m_HDR; set { + var previous = m_HDR; m_HDR = value; if (m_HDRLabel != null) m_HDRLabel.style.display = m_HDR ? DisplayStyle.Flex : DisplayStyle.None; @@ -86,6 +102,9 @@ public bool hdr if (m_AlphaGradientContainer != null) m_AlphaGradientContainer.style.display = m_HDR ? DisplayStyle.Flex : DisplayStyle.None; UpdateColorProperties(this.value); + + if (previous != m_HDR) + NotifyPropertyChanged(hdrProperty); } } @@ -213,8 +232,10 @@ public ColorField(string label) m_ColorContainer.RegisterCallback(OnAttach); m_ColorContainer.RegisterCallback(OnColorFieldClicked); + m_ColorContainer.RegisterCallback(OnColorFieldClickedCompatibilityEventHandler); visualInput.RegisterCallback(OnColorFieldKeyDown); m_EyeDropperElement.RegisterCallback(OnEyeDropperClicked); + m_EyeDropperElement.RegisterCallback(OnEyeDropperClickedCompatibilityEventHandler); RegisterCallback(OnCommandExecute); m_ColorContainer.AddManipulator(new ContextualMenuManipulator(BuildContextualMenu)); @@ -259,6 +280,15 @@ void OnColorFieldClicked(PointerDownEvent evt) } } + static void OnColorFieldClickedCompatibilityEventHandler(MouseDownEvent evt) + { + var mdeInternal = (IMouseEventInternal)evt; + if (evt.button == (int) MouseButton.LeftMouse && mdeInternal.sourcePointerEvent != null && ((EventBase)mdeInternal.sourcePointerEvent).isPropagationStopped) + { + evt.StopPropagation(); + } + } + void ShowColorPicker() { ColorPicker.Show((c) => @@ -290,6 +320,15 @@ void OnEyeDropperClicked(PointerDownEvent evt) evt.StopPropagation(); } + static void OnEyeDropperClickedCompatibilityEventHandler(MouseDownEvent evt) + { + var mdeInternal = (IMouseEventInternal)evt; + if (evt.button == (int) MouseButton.LeftMouse && mdeInternal.sourcePointerEvent != null && ((EventBase)mdeInternal.sourcePointerEvent).isPropagationStopped) + { + evt.StopPropagation(); + } + } + bool ShouldStopWatchingEyeDropper() { if (EyeDropper.IsOpened) diff --git a/Editor/Mono/UIElements/Controls/CurveField.cs b/Editor/Mono/UIElements/Controls/CurveField.cs index 83cc6c5b32..a8e94af648 100644 --- a/Editor/Mono/UIElements/Controls/CurveField.cs +++ b/Editor/Mono/UIElements/Controls/CurveField.cs @@ -3,6 +3,7 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; +using Unity.Properties; using UnityEngine; using UnityEditorInternal; using UnityEngine.UIElements; @@ -12,10 +13,12 @@ namespace UnityEditor.UIElements { /// - /// Makes a field for editing an . + /// Makes a field for editing an . For more information, refer to [[wiki:UIE-uxml-element-CurveField|UXML element CurveField]]. /// public class CurveField : BaseField { + internal static readonly DataBindingProperty renderModeProperty = nameof(renderMode); + /// /// Instantiates a using the data read from a UXML file. /// @@ -89,6 +92,7 @@ public enum RenderMode /// /// The RenderMode of CurveField. The default is RenderMode.Default. /// + [CreateProperty] public RenderMode renderMode { get { return m_RenderMode; } @@ -124,6 +128,7 @@ public RenderMode renderMode } m_TextureDirty = true; + NotifyPropertyChanged(renderModeProperty); } } } @@ -159,6 +164,7 @@ public override AnimationCurve value evt.elementTarget = this; SetValueWithoutNotify(value); SendEvent(evt); + NotifyPropertyChanged(valueProperty); } } else @@ -204,6 +210,9 @@ public CurveField(string label) visualInput.Add(borderElement); RegisterCallback(OnCustomStyleResolved); + RegisterCallback(OnKeyDown); + RegisterCallback(OnPointerDown); + RegisterCallback(OnMouseDownCompatibilityEvent); visualInput.generateVisualContent += OnGenerateVisualContent; } @@ -274,42 +283,51 @@ void ShowCurveEditor() CurveEditorWindow.color = curveColor; } - [EventInterest(typeof(KeyDownEvent), typeof(PointerDownEvent), typeof(DetachFromPanelEvent), - typeof(GeometryChangedEvent))] - protected override void ExecuteDefaultAction(EventBase evt) + void OnKeyDown(KeyDownEvent kde) { - base.ExecuteDefaultAction(evt); - - if (evt == null) - { - return; - } - - var showCurveEditor = false; - KeyDownEvent kde = (evt as KeyDownEvent); if (kde != null) { if ((kde.keyCode == KeyCode.Space) || (kde.keyCode == KeyCode.KeypadEnter) || (kde.keyCode == KeyCode.Return)) { - showCurveEditor = true; + ShowCurveEditor(); + kde.StopPropagation(); } } - else if ((evt as PointerDownEvent)?.button == (int)MouseButton.LeftMouse) + } + + void OnPointerDown(PointerDownEvent evt) + { + if (visualInput.ContainsPoint(visualInput.WorldToLocal(evt.position)) && evt.button == (int)MouseButton.LeftMouse) { - var mde = (PointerDownEvent)evt; - if (visualInput.ContainsPoint(visualInput.WorldToLocal(mde.position))) - { - showCurveEditor = true; - } + ShowCurveEditor(); + evt.StopPropagation(); } + } - if (showCurveEditor) - ShowCurveEditor(); - else if (evt.eventTypeId == DetachFromPanelEvent.TypeId()) + void OnMouseDownCompatibilityEvent(MouseDownEvent evt) + { + var mdeInternal = (IMouseEventInternal)evt; + if (visualInput.ContainsPoint(visualInput.WorldToLocal(evt.mousePosition)) && mdeInternal.sourcePointerEvent != null && ((EventBase)mdeInternal.sourcePointerEvent).isPropagationStopped) + { + evt.StopPropagation(); + } + } + + [EventInterest(typeof(DetachFromPanelEvent), typeof(GeometryChangedEvent))] + protected override void ExecuteDefaultAction(EventBase evt) + { + base.ExecuteDefaultAction(evt); + + if (evt == null) + { + return; + } + + if (evt.eventTypeId == DetachFromPanelEvent.TypeId()) OnDetach(); - if (evt.eventTypeId == GeometryChangedEvent.TypeId()) + else if (evt.eventTypeId == GeometryChangedEvent.TypeId()) m_TextureDirty = true; } diff --git a/Editor/Mono/UIElements/Controls/EnumFlagsField.cs b/Editor/Mono/UIElements/Controls/EnumFlagsField.cs index f1e3d76368..c524739654 100644 --- a/Editor/Mono/UIElements/Controls/EnumFlagsField.cs +++ b/Editor/Mono/UIElements/Controls/EnumFlagsField.cs @@ -12,11 +12,13 @@ namespace UnityEditor.UIElements /// /// Makes a dropdown for switching between enum flag values that are marked with the Flags attribute. /// - /// + /// /// An option for the value 0 with name "Nothing" and an option for the value ~0 (that is, all bits set) with the /// name "Everything" are always displayed at the top of the menu. The names for the values 0 and ~0 can be /// overriden by defining these values in the enum type. - /// + /// + /// For more information, refer to [[wiki:UIE-uxml-element-EnumFlagsField|UXML element EnumFlagsField]]. + /// public class EnumFlagsField : BaseMaskField { /// @@ -186,9 +188,15 @@ internal void PopulateDataFromType(Type enumType) internal override string GetNothingName() { - if (m_EnumData.flagValues != null && m_EnumData.flagValues.Length > 0 && m_EnumData.flagValues[0] == 0) + if (m_EnumData.flagValues is not { Length: > 0 }) + return base.GetNothingName(); + + for (var i = 0; i < m_EnumData.flagValues.Length; i++) { - return m_EnumData.displayNames[0]; + if (m_EnumData.flagValues[i] == 0) + { + return m_EnumData.displayNames[i]; + } } return base.GetNothingName(); @@ -196,10 +204,15 @@ internal override string GetNothingName() internal override string GetEverythingName() { - if (m_EnumData.flagValues != null && m_EnumData.flagValues.Length > 0 - && m_EnumData.flagValues[^1] == ~0) + if (m_EnumData.flagValues is not { Length: > 0 }) + return base.GetEverythingName(); + + for (var i = 0; i < m_EnumData.flagValues.Length; i++) { - return m_EnumData.displayNames[^1]; + if (m_EnumData.flagValues[i] == ~0) + { + return m_EnumData.displayNames[i]; + } } return base.GetEverythingName(); diff --git a/Editor/Mono/UIElements/Controls/GradientField.cs b/Editor/Mono/UIElements/Controls/GradientField.cs index 48dd41f48d..90ce05fafe 100644 --- a/Editor/Mono/UIElements/Controls/GradientField.cs +++ b/Editor/Mono/UIElements/Controls/GradientField.cs @@ -3,6 +3,7 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; +using Unity.Properties; using UnityEngine; using UnityEditorInternal; using UnityEngine.UIElements; @@ -11,10 +12,13 @@ namespace UnityEditor.UIElements { /// - /// Makes a field for editing an . + /// Makes a field for editing an . For more information, refer to [[wiki:UIE-uxml-element-GradientField|UXML element GradientField]]. /// public class GradientField : BaseField { + internal static readonly DataBindingProperty colorSpaceProperty = nameof(colorSpace); + internal static readonly DataBindingProperty hdrProperty = nameof(hdr); + static readonly GradientColorKey k_WhiteKeyBegin = new GradientColorKey(Color.white, 0); static readonly GradientColorKey k_WhiteKeyEnd = new GradientColorKey(Color.white, 1); static readonly GradientAlphaKey k_AlphaKeyBegin = new GradientAlphaKey(1, 0); @@ -53,19 +57,47 @@ public override Gradient value evt.elementTarget = this; SetValueWithoutNotify(value); SendEvent(evt); + NotifyPropertyChanged(valueProperty); } } } } + private ColorSpace m_ColorSpace; + /// /// The color space currently used by the field. /// - public ColorSpace colorSpace { get; set; } + [CreateProperty] + public ColorSpace colorSpace + { + get => m_ColorSpace; + set + { + if (m_ColorSpace == value) + return; + m_ColorSpace = value; + NotifyPropertyChanged(colorSpaceProperty); + } + } + + private bool m_Hdr; + /// /// If true, treats the color as an HDR value. If false, treats the color as a standard LDR value. /// - public bool hdr { get; set; } + [CreateProperty] + public bool hdr + { + get => m_Hdr; + set + { + if (m_Hdr == value) + return; + m_Hdr = value; + NotifyPropertyChanged(hdrProperty); + } + } internal static Gradient GradientCopy(Gradient other) { @@ -134,11 +166,10 @@ public GradientField(string label) rawValue = new Gradient(); } - [EventInterest(typeof(KeyDownEvent), typeof(MouseDownEvent), - typeof(DetachFromPanelEvent), typeof(AttachToPanelEvent))] - protected override void ExecuteDefaultAction(EventBase evt) + [EventInterest(typeof(KeyDownEvent), typeof(MouseDownEvent))] + protected override void ExecuteDefaultActionAtTarget(EventBase evt) { - base.ExecuteDefaultAction(evt); + base.ExecuteDefaultActionAtTarget(evt); if (evt == null) { @@ -168,8 +199,21 @@ protected override void ExecuteDefaultAction(EventBase evt) if (showGradientPicker) { ShowGradientPicker(); + evt.StopPropagation(); } - else if (evt.eventTypeId == DetachFromPanelEvent.TypeId()) + } + + [EventInterest(typeof(DetachFromPanelEvent), typeof(AttachToPanelEvent))] + protected override void ExecuteDefaultAction(EventBase evt) + { + base.ExecuteDefaultAction(evt); + + if (evt == null) + { + return; + } + + if (evt.eventTypeId == DetachFromPanelEvent.TypeId()) OnDetach(); else if (evt.eventTypeId == AttachToPanelEvent.TypeId()) OnAttach(); diff --git a/Editor/Mono/UIElements/Controls/LayerField.cs b/Editor/Mono/UIElements/Controls/LayerField.cs index 80bce447f2..27a00b05f6 100644 --- a/Editor/Mono/UIElements/Controls/LayerField.cs +++ b/Editor/Mono/UIElements/Controls/LayerField.cs @@ -11,7 +11,7 @@ namespace UnityEditor.UIElements { /// - /// A editor. + /// A LayerField editor. For more information, refer to [[wiki:UIE-uxml-element-LayerField|UXML element LayerField]]. /// public class LayerField : PopupField { diff --git a/Editor/Mono/UIElements/Controls/LayerMaskField.cs b/Editor/Mono/UIElements/Controls/LayerMaskField.cs index ceb3de3b7c..19e9e8a898 100644 --- a/Editor/Mono/UIElements/Controls/LayerMaskField.cs +++ b/Editor/Mono/UIElements/Controls/LayerMaskField.cs @@ -11,7 +11,7 @@ namespace UnityEditor.UIElements { /// - /// Make a field for layer as masks. + /// A LayerMaskField editor. For more information, refer to [[wiki:UIE-uxml-element-LayerMaskField|UXML element LayerMaskField]]. /// public class LayerMaskField : MaskField { diff --git a/Editor/Mono/UIElements/Controls/MaskField.cs b/Editor/Mono/UIElements/Controls/MaskField.cs index a5b30dd2dd..eda19c5bbb 100644 --- a/Editor/Mono/UIElements/Controls/MaskField.cs +++ b/Editor/Mono/UIElements/Controls/MaskField.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using Unity.Properties; using System.Text; using UnityEngine.Pool; using UnityEngine.UIElements; @@ -15,6 +16,8 @@ namespace UnityEditor.UIElements /// public abstract class BaseMaskField : BasePopupField { + internal static readonly DataBindingProperty choicesMasksProperty = nameof(choicesMasks); + internal abstract TChoice MaskToValue(int newMask); internal abstract int ValueToMask(TChoice value); @@ -126,6 +129,7 @@ public override List choices // Make sure to update the text displayed SetValueWithoutNotify(rawValue); + NotifyPropertyChanged(choicesProperty); } } @@ -142,6 +146,7 @@ internal virtual string GetEverythingName() /// /// The list of list of masks for every specific choice to display in the popup menu. /// + [CreateProperty] public virtual List choicesMasks { get { return m_UserChoicesMasks; } @@ -167,6 +172,7 @@ public virtual List choicesMasks // Make sure to update the text displayed SetValueWithoutNotify(rawValue); } + NotifyPropertyChanged(choicesMasksProperty); } } @@ -257,7 +263,7 @@ internal string GetDisplayedValue(int itemIndex) // Find the actual index of the selected choice... foreach (int itemMask in m_UserChoicesMasks) { - if ((itemMask & itemIndex) == itemIndex) + if (itemMask != ~0 && ((itemMask & itemIndex) == itemIndex)) { indexOfValue = m_UserChoicesMasks.IndexOf(itemMask); break; @@ -543,7 +549,7 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext string choicesFromBag = m_MaskChoices.GetValueFromBag(bag, cc); - var listOfChoices = ParseChoiceList(choicesFromBag); + var listOfChoices = UxmlUtility.ParseStringListAttribute(choicesFromBag); if (listOfChoices != null && listOfChoices.Count > 0) { diff --git a/Editor/Mono/UIElements/Controls/ObjectField.cs b/Editor/Mono/UIElements/Controls/ObjectField.cs index ad9557a538..a2eee909d2 100644 --- a/Editor/Mono/UIElements/Controls/ObjectField.cs +++ b/Editor/Mono/UIElements/Controls/ObjectField.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using Unity.Properties; using UnityEngine; using UnityEngine.UIElements; using Object = UnityEngine.Object; @@ -15,6 +16,9 @@ namespace UnityEditor.UIElements /// public class ObjectField : BaseField { + internal static readonly DataBindingProperty objectTypeProperty = nameof(objectType); + internal static readonly DataBindingProperty allowSceneObjectsProperty = nameof(allowSceneObjects); + /// /// Instantiates an using the data read from a UXML file. /// @@ -61,6 +65,7 @@ public override void SetValueWithoutNotify(Object newValue) /// /// The type of the objects that can be assigned. /// + [CreateProperty] public Type objectType { get { return m_objectType; } @@ -70,6 +75,7 @@ public Type objectType { m_objectType = value; UpdateDisplay(); + NotifyPropertyChanged(objectTypeProperty); } } } @@ -79,22 +85,35 @@ internal void SetObjectTypeWithoutDisplayUpdate(Type type) m_objectType = type; } + private bool m_AllowSceneObjects; + /// /// Allows scene objects to be assigned to the field. /// - public bool allowSceneObjects { get; set; } + [CreateProperty] + public bool allowSceneObjects + { + get => m_AllowSceneObjects; + set + { + if (m_AllowSceneObjects == value) + return; + m_AllowSceneObjects = value; + NotifyPropertyChanged(allowSceneObjectsProperty); + } + } protected override void UpdateMixedValueContent() { m_ObjectFieldDisplay?.ShowMixedValue(showMixedValue); } - internal void UpdateDisplay() + internal virtual void UpdateDisplay() { m_ObjectFieldDisplay.Update(); } - private class ObjectFieldDisplay : VisualElement + internal class ObjectFieldDisplay : VisualElement { private readonly ObjectField m_ObjectField; private readonly Image m_ObjectIcon; @@ -102,7 +121,7 @@ private class ObjectFieldDisplay : VisualElement static readonly string ussClassName = "unity-object-field-display"; static readonly string iconUssClassName = ussClassName + "__icon"; - static readonly string labelUssClassName = ussClassName + "__label"; + internal static readonly string labelUssClassName = ussClassName + "__label"; static readonly string acceptDropVariantUssClassName = ussClassName + "--accept-drop"; internal void ShowMixedValue(bool show) @@ -241,6 +260,7 @@ private void OnKeyboardEnter() private void OnKeyboardDelete() { + m_ObjectField.SetProperty(serializedPropertyKey, null); m_ObjectField.value = null; } @@ -297,12 +317,15 @@ public ObjectFieldSelector(ObjectField objectField) } [EventInterest(typeof(MouseDownEvent))] - protected override void ExecuteDefaultAction(EventBase evt) + protected override void ExecuteDefaultActionAtTarget(EventBase evt) { - base.ExecuteDefaultAction(evt); + base.ExecuteDefaultActionAtTarget(evt); if ((evt as MouseDownEvent)?.button == (int)MouseButton.LeftMouse) + { m_ObjectField.ShowObjectSelector(); + evt.StopPropagation(); + } } } diff --git a/Editor/Mono/UIElements/Controls/PropertyControl.cs b/Editor/Mono/UIElements/Controls/PropertyControl.cs deleted file mode 100644 index 793e6875c9..0000000000 --- a/Editor/Mono/UIElements/Controls/PropertyControl.cs +++ /dev/null @@ -1,310 +0,0 @@ -// Unity C# reference source -// Copyright (c) Unity Technologies. For terms of use, see -// https://unity3d.com/legal/licenses/Unity_Reference_Only_License - -using System; -using System.Collections.Generic; -using System.Linq; -using UnityEngine; -using UnityEngine.UIElements; - -namespace UnityEditor.UIElements -{ - static class BuiltInTypeConverter - { - static Dictionary s_TypeDictionary; - - static BuiltInTypeConverter() - { - if (s_TypeDictionary == null) - { - s_TypeDictionary = new Dictionary(); - s_TypeDictionary.Add("bool", typeof(long)); - s_TypeDictionary.Add("byte", typeof(byte)); - s_TypeDictionary.Add("sbyte", typeof(sbyte)); - s_TypeDictionary.Add("char", typeof(char)); - s_TypeDictionary.Add("decimal", typeof(decimal)); - s_TypeDictionary.Add("double", typeof(double)); - s_TypeDictionary.Add("float", typeof(float)); - s_TypeDictionary.Add("int", typeof(int)); - s_TypeDictionary.Add("uint", typeof(uint)); - s_TypeDictionary.Add("long", typeof(long)); - s_TypeDictionary.Add("ulong", typeof(ulong)); - s_TypeDictionary.Add("object", typeof(object)); - s_TypeDictionary.Add("short", typeof(short)); - s_TypeDictionary.Add("ushort", typeof(ushort)); - s_TypeDictionary.Add("string", typeof(string)); - } - } - - public static Type GetTypeFromName(string typeName) - { - Type t; - if (!s_TypeDictionary.TryGetValue(typeName, out t)) - { - t = Type.GetType(typeName); - } - - return t; - } - } - - [Obsolete("Please use PropertyField instead", false)] - internal class PropertyControl : BaseField - { - public new class UxmlFactory : UxmlFactory, UxmlTraits> - { - public override string uxmlName - { - get - { - string name = typeof(PropertyControl).Name; - if (name.Contains("`")) - { - name = name.Substring(0, name.IndexOf("`")); - } - return name; - } - } - - public override string uxmlQualifiedName - { - get { return uxmlNamespace + "." + uxmlName; } - } - - public override bool AcceptsAttributeBag(IUxmlAttributes bag, CreationContext cc) - { - string type = m_Traits.GetValueType(bag, cc); - if (BuiltInTypeConverter.GetTypeFromName(type) == typeof(TType)) - { - return true; - } - - return false; - } - } - - public new class UxmlTraits : BaseField.UxmlTraits - { - UxmlStringAttributeDescription m_TypeOf = new UxmlStringAttributeDescription { name = "value-type", obsoleteNames = new[] {"typeOf"}, use = UxmlAttributeDescription.Use.Required }; - UxmlStringAttributeDescription m_Value = new UxmlStringAttributeDescription { name = "value" }; - - public UxmlTraits() - { - m_TypeOf.restriction = new UxmlEnumeration - { - values = Enum.GetValues(typeof(DataType)).Cast() - .Where(dt => dt != DataType.Unsupported) - .Select(dt => dt.ToString()) - }; - } - - public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc) - { - base.Init(ve, bag, cc); - - var initValue = m_Value.GetValueFromBag(bag, cc); - ((PropertyControl)ve).SetValueWithoutNotify(((PropertyControl)ve).StringToValue(initValue)); - ((PropertyControl)ve).label = ((BaseField)ve).label; - ((BaseField)ve).label = null; - } - - public string GetValueType(IUxmlAttributes bag, CreationContext cc) - { - return m_TypeOf.GetValueFromBag(bag, cc); - } - } - - enum DataType - { - Long, - Double, - Int, - Float, - String, - Unsupported - } - - private Func GetValueDelegate; - private Action SetValueDelegate; - - private Label m_Label; - private BaseField m_Field; - - public new static readonly string ussClassName = "unity-property-control"; - public new static readonly string labelUssClassName = ussClassName + "__label"; - public static readonly string draggerUssClassName = ussClassName + "__dragger"; - public static readonly string controlUssClassName = ussClassName + "__control"; - - public PropertyControl() - : this("") - {} - - public PropertyControl(string labelText) - : base(null) - { - Init("", labelText); - } - - private void Init(string initialValue, string labelText) - { - AddToClassList(ussClassName); - focusable = true; - CacheType(); - - m_Label = new Label(labelText); - m_Label.AddToClassList(labelUssClassName); - - Add(m_Label); - CreateControl(); - SetValueWithoutNotify(StringToValue(initialValue)); - - EnableMouseDraggerForNumericType(); - } - - DataType dataType { get; set; } - - private void CacheType() - { - var ttype = typeof(TType); - if (ttype == typeof(long)) - dataType = DataType.Long; - else if (ttype == typeof(int)) - dataType = DataType.Int; - else if (ttype == typeof(float)) - dataType = DataType.Float; - else if (ttype == typeof(double)) - dataType = DataType.Double; - else if (ttype == typeof(string)) - dataType = DataType.String; - else - { - dataType = DataType.Unsupported; - throw new NotSupportedException($"Unsupported type for PropertyControl {ttype}"); - } - } - - private void EnableMouseDraggerForNumericType() - { - switch (dataType) - { - case DataType.Long: - AddLabelDragger(); - break; - case DataType.Int: - AddLabelDragger(); - break; - case DataType.Float: - AddLabelDragger(); - break; - case DataType.Double: - AddLabelDragger(); - break; - default: - break; - } - } - - private void AddLabelDragger() - { - var dragger = new FieldMouseDragger((IValueField)m_Field); - dragger.SetDragZone(m_Label); - - m_Label.AddToClassList(draggerUssClassName); - } - - private static TTo ConvertType(TFrom value) - { - return (TTo)Convert.ChangeType(value, typeof(TTo)); - } - - private void CreateControl() where TControlType : BaseField, new() - { - var c = new TControlType(); - GetValueDelegate = () => ConvertType(c.value); - SetValueDelegate = (x) => c.value = ConvertType(x); - m_Field = c as BaseField; - } - - private void CreateControl() - { - switch (dataType) - { - case DataType.Long: - CreateControl(); - break; - case DataType.Int: - CreateControl(); - break; - case DataType.Float: - CreateControl(); - break; - case DataType.Double: - CreateControl(); - break; - case DataType.String: - CreateControl(); - break; - } - - if (m_Field == null) - throw new NotSupportedException($"Unsupported type attribute: {typeof(TType)}"); - - m_Field.AddToClassList(controlUssClassName); - m_Field.RegisterValueChangedCallback(OnFieldValueChanged); - Add(m_Field); - } - - public new string label - { - get { return m_Label.text; } - set { m_Label.text = value; } - } - - private TType StringToValue(string str) - { - switch (dataType) - { - case DataType.Long: - case DataType.Int: - { - long v; - EditorGUI.StringToLong(str, out v); - return ConvertType(v); - } - case DataType.Double: - case DataType.Float: - { - double v; - EditorGUI.StringToDouble(str, out v); - return ConvertType(v); - } - } - return ConvertType(str); - } - - private void OnFieldValueChanged(ChangeEvent evt) - { - using (ChangeEvent newEvent = ChangeEvent.GetPooled(evt.previousValue, evt.newValue)) - { - newEvent.elementTarget = this; - SendEvent(newEvent); - } - } - - public override void SetValueWithoutNotify(TType newValue) - { - m_Field.SetValueWithoutNotify(newValue); - } - - public override TType value - { - get { return GetValueDelegate(); } - set { SetValueDelegate(value); } - } - - protected override void UpdateMixedValueContent() - { - } - } -} diff --git a/Editor/Mono/UIElements/Controls/PropertyField.cs b/Editor/Mono/UIElements/Controls/PropertyField.cs index 52963bbe61..e4c573d6a6 100644 --- a/Editor/Mono/UIElements/Controls/PropertyField.cs +++ b/Editor/Mono/UIElements/Controls/PropertyField.cs @@ -12,13 +12,14 @@ namespace UnityEditor.UIElements { /// - /// A SerializedProperty wrapper VisualElement that, on Bind(), will generate the correct field elements with the correct bindingPaths. + /// A SerializedProperty wrapper VisualElement that, on Bind(), will generate the correct field elements with the correct binding paths. For more information, refer to [[wiki:UIE-uxml-element-PropertyField|UXML element PropertyField]]. /// public class PropertyField : VisualElement, IBindable { private static readonly Regex s_MatchPPtrTypeName = new Regex(@"PPtr\<(\w+)\>"); internal static readonly string foldoutTitleBoundLabelProperty = "unity-foldout-bound-title"; internal static readonly string decoratorDrawersContainerClassName = "unity-decorator-drawers-container"; + static readonly string listViewNamePrefix = "unity-list-"; /// /// Instantiates a using the data read from a UXML file. @@ -72,16 +73,17 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext /// public string label { get; set; } - private SerializedProperty m_SerializedProperty; - private string m_SerializedPropertyReferenceTypeName; - private PropertyField m_ParentPropertyField; - private int m_FoldoutDepth; - private VisualElement m_InspectorElement; - private VisualElement m_ContextWidthElement; + SerializedObject m_SerializedObject; + SerializedProperty m_SerializedProperty; + string m_SerializedPropertyReferenceTypeName; + PropertyField m_ParentPropertyField; + int m_FoldoutDepth; + VisualElement m_InspectorElement; + VisualElement m_ContextWidthElement; - private int m_DrawNestingLevel; - private PropertyField m_DrawParentProperty; - private VisualElement m_DecoratorDrawersContainer; + int m_DrawNestingLevel; + PropertyField m_DrawParentProperty; + VisualElement m_DecoratorDrawersContainer; SerializedProperty serializedProperty => m_SerializedProperty; @@ -200,8 +202,9 @@ void Reset(SerializedProperty newProperty) bool newPropertyTypeIsDifferent = true; + var serializedObjectIsValid = m_SerializedObject != null && m_SerializedObject.m_NativeObjectPtr != IntPtr.Zero && m_SerializedObject.isValid; - if (m_SerializedProperty != null && m_SerializedProperty.isValid && newPropertyType == m_SerializedProperty.propertyType) + if (serializedObjectIsValid && m_SerializedProperty != null && m_SerializedProperty.isValid && newPropertyType == m_SerializedProperty.propertyType) { if(newPropertyType == SerializedPropertyType.ManagedReference) { @@ -215,7 +218,7 @@ void Reset(SerializedProperty newProperty) m_SerializedProperty = newProperty; m_SerializedPropertyReferenceTypeName = newPropertyTypeName; - + m_SerializedObject = newProperty.serializedObject; // if we already have a serialized property, determine if the property field can be reused without reset // this is only supported for non propertydrawer types @@ -261,6 +264,7 @@ void Reset(SerializedProperty newProperty) { if (handler.hasPropertyDrawer) { + handler.propertyDrawer.m_PreferredLabel = label ?? serializedProperty.localizedDisplayName; customPropertyGUI = handler.propertyDrawer.CreatePropertyGUI(m_SerializedProperty); if (customPropertyGUI == null) @@ -299,7 +303,7 @@ private void ResetDecoratorDrawers(PropertyHandler handler) { var decorators = handler.decoratorDrawers; - if (decorators == null || decorators.Count == 0) + if (decorators == null || decorators.Count == 0 || m_DrawNestingLevel > 0) { if (m_DecoratorDrawersContainer != null) { @@ -333,6 +337,7 @@ private void ResetDecoratorDrawers(PropertyHandler handler) decoratorRect.height = decorator.GetHeight(); decoratorRect.width = resolvedStyle.width; decorator.OnGUI(decoratorRect); + ve.style.height = decoratorRect.height; }); ve.style.height = decorator.GetHeight(); } @@ -360,6 +365,12 @@ private VisualElement CreatePropertyIMGUIContainer() if (!serializedProperty.isValid) return; + if (m_InspectorElement is InspectorElement inspectorElement) + { + //set the current PropertyHandlerCache to the current editor + ScriptAttributeUtility.propertyHandlerCache = inspectorElement.editor.propertyHandlerCache; + } + EditorGUI.BeginChangeCheck(); serializedProperty.serializedObject.Update(); @@ -391,6 +402,9 @@ private VisualElement CreatePropertyIMGUIContainer() var handler = ScriptAttributeUtility.GetHandler(serializedProperty); using (var nestingContext = handler.ApplyNestingContext(m_DrawNestingLevel)) { + // Decorator drawers are already handled on the uitk side + handler.skipDecoratorDrawers = true; + if (label == null) { EditorGUILayout.PropertyField(serializedProperty, true); @@ -575,10 +589,13 @@ void RefreshChildrenProperties(SerializedProperty property, bool bindNewFields) private VisualElement CreateFoldout(SerializedProperty property, object originalField = null) { property = property.Copy(); - var foldout = originalField != null && originalField is Foldout ? originalField as Foldout : new Foldout(); - bool hasCustomLabel = !string.IsNullOrEmpty(label); + if (originalField is not Foldout foldout) + { + foldout = new Foldout(); + } + + var hasCustomLabel = !string.IsNullOrEmpty(label); foldout.text = hasCustomLabel ? label : property.localizedDisplayName; - foldout.value = property.isExpanded; foldout.bindingPath = property.propertyPath; foldout.name = "unity-foldout-" + property.propertyPath; @@ -633,6 +650,13 @@ private TField ConfigureField(TField field, SerializedProperty p field.name = "unity-input-" + property.propertyPath; field.label = fieldLabel; + ConfigureFieldStyles(field); + + return field; + } + + internal static void ConfigureFieldStyles(TField field) where TField : BaseField + { field.labelElement.AddToClassList(labelUssClassName); field.visualInput.AddToClassList(inputUssClassName); field.AddToClassList(BaseField.alignedFieldUssClassName); @@ -644,30 +668,36 @@ private TField ConfigureField(TField field, SerializedProperty p { x.AddToClassList(BaseField.alignedFieldUssClassName); }); - - - return field; } VisualElement ConfigureListView(ListView listView, SerializedProperty property, Func factory) { - listView ??= factory(); + if (listView == null) + { + listView = factory(); + listView.showBorder = true; + listView.selectionType = SelectionType.Multiple; + listView.showAddRemoveFooter = true; + listView.showBoundCollectionSize = true; + listView.showFoldoutHeader = true; + listView.virtualizationMethod = CollectionVirtualizationMethod.DynamicHeight; + listView.showAlternatingRowBackgrounds = AlternatingRowBackground.None; + listView.itemsSourceSizeChanged += DispatchPropertyChangedEvent; + } + var propertyCopy = property.Copy(); - listView.reorderMode = ListViewReorderMode.Animated; - listView.reorderable = PropertyHandler.IsArrayReorderable(property); - listView.showBorder = true; - listView.showAddRemoveFooter = true; - listView.showBoundCollectionSize = true; - listView.showFoldoutHeader = true; + var listViewName = $"{listViewNamePrefix}{property.propertyPath}"; listView.headerTitle = string.IsNullOrEmpty(label) ? propertyCopy.localizedDisplayName : label; - listView.virtualizationMethod = CollectionVirtualizationMethod.DynamicHeight; listView.userData = propertyCopy; - listView.showAlternatingRowBackgrounds = AlternatingRowBackground.None; listView.bindingPath = property.propertyPath; - listView.viewDataKey = property.propertyPath; - listView.name = "unity-list-" + property.propertyPath; - listView.headerFoldout.viewDataKey = property.propertyPath; - listView.Bind(property.serializedObject); + listView.viewDataKey = listViewName; + listView.name = listViewName; + + // Make list view foldout react even when disabled, like EditorGUILayout.Foldout. + var toggle = listView.Q(className: Foldout.toggleUssClassName); + if (toggle != null) + toggle.m_Clickable.acceptClicksIfDisabled = true; + return listView; } @@ -687,6 +717,13 @@ private VisualElement CreateOrUpdateFieldFromProperty(SerializedProperty propert if (property.type == "long") return ConfigureField(originalField as LongField, property, () => new LongField()); + if (property.type == "ulong") + return ConfigureField(originalField as UnsignedLongField, property, + () => new UnsignedLongField()); + if (property.type == "uint") + return ConfigureField(originalField as UnsignedIntegerField, property, + () => new UnsignedIntegerField()); + { var intField = ConfigureField(originalField as IntegerField, property, () => new IntegerField()) as IntegerField; @@ -739,12 +776,17 @@ private VisualElement CreateOrUpdateFieldFromProperty(SerializedProperty propert { if (!objectTypes.Name.Equals(targetTypeName, StringComparison.OrdinalIgnoreCase)) continue; + + // We ignore C# types as they can can be confused with a built-in type with the same name, + // we can use the FieldInfo to find MonoScript types. (UUM-29499) + if (typeof(MonoBehaviour).IsAssignableFrom(objectTypes) || typeof(ScriptableObject).IsAssignableFrom(objectTypes)) + continue; + requiredType = objectTypes; break; } } - field.SetProperty(ObjectField.serializedPropertyKey, property); field.SetObjectTypeWithoutDisplayUpdate(requiredType); field.UpdateDisplay(); @@ -902,7 +944,6 @@ private void RegisterPropertyChangesOnCustomDrawerElement(VisualElement customPr customPropertyDrawer.RegisterCallback>((changeEvent) => AsyncDispatchPropertyChangedEvent()); customPropertyDrawer.RegisterCallback>((changeEvent) => AsyncDispatchPropertyChangedEvent()); customPropertyDrawer.RegisterCallback>((changeEvent) => AsyncDispatchPropertyChangedEvent()); - customPropertyDrawer.RegisterCallback>((changeEvent) => AsyncDispatchPropertyChangedEvent()); customPropertyDrawer.RegisterCallback>((changeEvent) => AsyncDispatchPropertyChangedEvent()); customPropertyDrawer.RegisterCallback>((changeEvent) => AsyncDispatchPropertyChangedEvent()); customPropertyDrawer.RegisterCallback>((changeEvent) => AsyncDispatchPropertyChangedEvent()); diff --git a/Editor/Mono/UIElements/Controls/Toolbar/SearchFieldBase.cs b/Editor/Mono/UIElements/Controls/Toolbar/SearchFieldBase.cs index e27992a6f9..068295c99c 100644 --- a/Editor/Mono/UIElements/Controls/Toolbar/SearchFieldBase.cs +++ b/Editor/Mono/UIElements/Controls/Toolbar/SearchFieldBase.cs @@ -2,6 +2,8 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using System.Collections.Generic; +using Unity.Properties; using UnityEngine; using UnityEngine.UIElements; @@ -13,6 +15,8 @@ namespace UnityEditor.UIElements public abstract class SearchFieldBase : VisualElement, INotifyValueChanged where TextInputType : TextInputBaseField, new() { + internal static readonly DataBindingProperty valueProperty = nameof(value); + private readonly Button m_SearchButton; private readonly Button m_CancelButton; private readonly TextInputType m_TextField; @@ -35,10 +39,17 @@ protected Button searchButton /// /// If the new value is different from the current value, this method notifies registered callbacks with a . /// + [CreateProperty] public T value { get { return m_TextField.value; } - set { m_TextField.value = value; } + set + { + var previous = m_TextField.value; + m_TextField.value = value; + if (!EqualityComparer.Default.Equals(m_TextField.value, previous)) + NotifyPropertyChanged(valueProperty); + } } /// diff --git a/Editor/Mono/UIElements/Controls/Toolbar/Toolbar.cs b/Editor/Mono/UIElements/Controls/Toolbar/Toolbar.cs index 051f644454..a621abc7b5 100644 --- a/Editor/Mono/UIElements/Controls/Toolbar/Toolbar.cs +++ b/Editor/Mono/UIElements/Controls/Toolbar/Toolbar.cs @@ -7,7 +7,7 @@ namespace UnityEditor.UIElements { /// - /// A toolbar for tool windows. + /// A toolbar for tool windows. For more information, refer to [[wiki:UIE-uxml-element-Toolbar|UXML element Toolbar]]. /// public class Toolbar : VisualElement { diff --git a/Editor/Mono/UIElements/Controls/Toolbar/ToolbarBreadcrumbs.cs b/Editor/Mono/UIElements/Controls/Toolbar/ToolbarBreadcrumbs.cs index d66a1a8719..09843a4803 100644 --- a/Editor/Mono/UIElements/Controls/Toolbar/ToolbarBreadcrumbs.cs +++ b/Editor/Mono/UIElements/Controls/Toolbar/ToolbarBreadcrumbs.cs @@ -9,7 +9,7 @@ namespace UnityEditor.UIElements { /// - /// Creates a breadcrumb UI element for the toolbar to help users navigate a hierarchy. For example, the visual scripting breadcrumb toolbar makes it easier to explore scripts because users can jump to any level of the script by clicking a breadcrumb item. + /// Creates a breadcrumb UI element for the toolbar to help users navigate a hierarchy. For example, the visual scripting breadcrumb toolbar makes it easier to explore scripts because users can jump to any level of the script by clicking a breadcrumb item. For more information, refer to [[wiki:UIE-uxml-element-ToolbarBreadcrumbs|UXML element ToolbarBreadcrumbs]]. /// /// /// Represents a breadcrumb trail to facilitate navigation between related items in a hierarchy. diff --git a/Editor/Mono/UIElements/Controls/Toolbar/ToolbarButton.cs b/Editor/Mono/UIElements/Controls/Toolbar/ToolbarButton.cs index 77ae366fea..04f028e864 100644 --- a/Editor/Mono/UIElements/Controls/Toolbar/ToolbarButton.cs +++ b/Editor/Mono/UIElements/Controls/Toolbar/ToolbarButton.cs @@ -8,7 +8,7 @@ namespace UnityEditor.UIElements { /// - /// A button for the toolbar. + /// A button for the toolbar. For more information, refer to [[wiki:UIE-uxml-element-ToolbarButton|UXML element ToolbarButton]]. /// public class ToolbarButton : Button { diff --git a/Editor/Mono/UIElements/Controls/Toolbar/ToolbarMenu.cs b/Editor/Mono/UIElements/Controls/Toolbar/ToolbarMenu.cs index c100eac81b..68797ce008 100644 --- a/Editor/Mono/UIElements/Controls/Toolbar/ToolbarMenu.cs +++ b/Editor/Mono/UIElements/Controls/Toolbar/ToolbarMenu.cs @@ -2,15 +2,19 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using Unity.Properties; using UnityEngine.UIElements; namespace UnityEditor.UIElements { /// - /// A drop-down menu for the toolbar. + /// A drop-down menu for the toolbar. For more information, refer to [[wiki:UIE-uxml-element-ToolbarMenu|UXML element ToolbarMenu]]. /// public class ToolbarMenu : TextElement, IToolbarMenuElement { + internal static readonly DataBindingProperty menuProperty = nameof(menu); + internal static readonly DataBindingProperty variantProperty = nameof(variant); + /// /// Instantiates a using the data read from a UXML file. /// @@ -38,6 +42,7 @@ public enum Variant /// /// The menu. /// + [CreateProperty(ReadOnly = true)] public DropdownMenu menu { get; } public override string text { @@ -93,13 +98,18 @@ public ToolbarMenu() /// /// The display styles that you can use when creating menus. /// + [CreateProperty] public Variant variant { get { return m_Variant; } set { + var previous = m_Variant; m_Variant = value; EnableInClassList(popupVariantUssClassName, value == Variant.Popup); + + if (previous != m_Variant) + NotifyPropertyChanged(variantProperty); } } } diff --git a/Editor/Mono/UIElements/Controls/Toolbar/ToolbarPopupSearchField.cs b/Editor/Mono/UIElements/Controls/Toolbar/ToolbarPopupSearchField.cs index 64e9155d17..55195eeaad 100644 --- a/Editor/Mono/UIElements/Controls/Toolbar/ToolbarPopupSearchField.cs +++ b/Editor/Mono/UIElements/Controls/Toolbar/ToolbarPopupSearchField.cs @@ -2,15 +2,18 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using Unity.Properties; using UnityEngine.UIElements; namespace UnityEditor.UIElements { /// - /// The pop-up search field for the toolbar. The search field includes a menu button. + /// The pop-up search field for the toolbar. The search field includes a menu button. For more information, refer to [[wiki:UIE-uxml-element-ToolbarPopupSearchField|UXML element ToolbarPopupSearchField]]. /// public class ToolbarPopupSearchField : ToolbarSearchField, IToolbarMenuElement { + internal static readonly DataBindingProperty menuProperty = nameof(menu); + /// /// Instantiates a using the data read from a UXML file. /// @@ -28,6 +31,7 @@ public class ToolbarPopupSearchField : ToolbarSearchField, IToolbarMenuElement /// /// The menu used by the pop-up search field element. /// + [CreateProperty(ReadOnly = true)] public DropdownMenu menu { get; } /// diff --git a/Editor/Mono/UIElements/Controls/Toolbar/ToolbarSearchField.cs b/Editor/Mono/UIElements/Controls/Toolbar/ToolbarSearchField.cs index 61f99f5e34..263438e566 100644 --- a/Editor/Mono/UIElements/Controls/Toolbar/ToolbarSearchField.cs +++ b/Editor/Mono/UIElements/Controls/Toolbar/ToolbarSearchField.cs @@ -9,7 +9,7 @@ namespace UnityEditor.UIElements { /// - /// A search field for the toolbar. + /// A search field for the toolbar. For more information, refer to [[wiki:UIE-uxml-element-ToolbarSearchField|UXML element ToolbarSearchField]]. /// public class ToolbarSearchField : SearchFieldBase { @@ -43,26 +43,6 @@ public class ToolbarSearchField : SearchFieldBase get { return base.searchButton; } } - /// - /// The object currently being exposed by the field. - /// - /// - /// If the new value is different from the current value, this method notifies registered callbacks with a of type string. - /// - public new string value - { - get { return base.value; } - set { base.value = value; } - } - - /// - /// Sets the value for the toolbar search field without sending a change event. - /// - public override void SetValueWithoutNotify(string newValue) - { - base.SetValueWithoutNotify(newValue); - } - // KEEP ABOVE CODE TO BE BACKWARD COMPATIBLE WITH 2019.1 ToolbarSearchField /// diff --git a/Editor/Mono/UIElements/Controls/Toolbar/ToolbarSpacer.cs b/Editor/Mono/UIElements/Controls/Toolbar/ToolbarSpacer.cs index 4d1b19cf6c..fe07a12b30 100644 --- a/Editor/Mono/UIElements/Controls/Toolbar/ToolbarSpacer.cs +++ b/Editor/Mono/UIElements/Controls/Toolbar/ToolbarSpacer.cs @@ -3,15 +3,18 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; +using Unity.Properties; using UnityEngine.UIElements; namespace UnityEditor.UIElements { /// - /// A toolbar spacer of static size. + /// A toolbar spacer of static size. For more information, refer to [[wiki:UIE-uxml-element-ToolbarSpacer|UXML element ToolbarSpacer]]. /// public class ToolbarSpacer : VisualElement { + internal static readonly DataBindingProperty flexProperty = nameof(flex); + /// /// Instantiates a using the data read from a UXML file. /// @@ -45,6 +48,7 @@ public ToolbarSpacer() /// /// Return true if the spacer stretches or shrinks to occupy available space. /// + [CreateProperty] public bool flex { get { return ClassListContains(flexibleSpacerVariantUssClassName); } @@ -53,6 +57,7 @@ public bool flex if (flex != value) { EnableInClassList(flexibleSpacerVariantUssClassName, value); + NotifyPropertyChanged(flexProperty); } } } diff --git a/Editor/Mono/UIElements/Controls/Toolbar/ToolbarToggle.cs b/Editor/Mono/UIElements/Controls/Toolbar/ToolbarToggle.cs index a557466f56..ddcccf7e3e 100644 --- a/Editor/Mono/UIElements/Controls/Toolbar/ToolbarToggle.cs +++ b/Editor/Mono/UIElements/Controls/Toolbar/ToolbarToggle.cs @@ -7,7 +7,7 @@ namespace UnityEditor.UIElements { /// - /// A toggle for the toolbar. + /// A toggle for the toolbar. For more information, refer to [[wiki:UIE-uxml-element-ToolbarToggle|UXML element ToolbarToggle]]. /// public class ToolbarToggle : Toggle { diff --git a/Editor/Mono/UIElements/DefaultMainToolbar.cs b/Editor/Mono/UIElements/DefaultMainToolbar.cs index b68710edb2..05a28c9df5 100644 --- a/Editor/Mono/UIElements/DefaultMainToolbar.cs +++ b/Editor/Mono/UIElements/DefaultMainToolbar.cs @@ -16,6 +16,7 @@ static IEnumerable leftToolbar { yield return "Services/Account"; yield return "Services/Cloud"; + yield return "Editor Utility/Store"; yield return "Editor Utility/Imgui Subtoolbars"; } } diff --git a/Editor/Mono/UIElements/EditorFocusMonitor.cs b/Editor/Mono/UIElements/EditorFocusMonitor.cs new file mode 100644 index 0000000000..88d75a999a --- /dev/null +++ b/Editor/Mono/UIElements/EditorFocusMonitor.cs @@ -0,0 +1,28 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using UnityEngine.UIElements; + +namespace UnityEditor.UIElements +{ + class EditorFocusMonitor + { + /// + /// Checks if any Editor windows have a bindable element currently focused. + /// + /// True if a bindable element is focused; false otherwise. + public static bool AreBindableElementsSelected() + { + foreach (var window in EditorWindow.activeEditorWindows) + { + var focusController = window.rootVisualElement?.panel?.focusController; + + // We only care about elements that are bindable, as they are the only ones that could be making changes to the prefab. + if (focusController?.focusedElement is BindableElement) + return true; + } + return false; + } + } +} diff --git a/Editor/Mono/UIElements/Inspector/EditorElement.cs b/Editor/Mono/UIElements/Inspector/EditorElement.cs index a2068cf1d7..59c15f1805 100644 --- a/Editor/Mono/UIElements/Inspector/EditorElement.cs +++ b/Editor/Mono/UIElements/Inspector/EditorElement.cs @@ -94,6 +94,9 @@ internal EditorElement(int editorIndex, IPropertyView iw, Editor[] editors, bool m_IsCulled = isCulled; pickingMode = PickingMode.Ignore; + var editor = editors == null || editors.Length == 0 || m_EditorIndex < 0 || m_EditorIndex >= editors.Length ? null : editors[m_EditorIndex]; + name = GetNameFromEditor(editor); + if (isCulled) { InitCulled(editors); @@ -126,7 +129,7 @@ void Init(Editor[] editors) { PopulateCache(editors); m_EditorTarget = editor.targets[0]; - var editorTitle = ObjectNames.GetInspectorTitle(m_EditorTarget); + var editorTitle = ObjectNames.GetInspectorTitle(m_EditorTarget, editor.targets.Length > 1); m_Header = BuildHeaderElement(editorTitle); m_Footer = BuildFooterElement(editorTitle); @@ -134,6 +137,13 @@ void Init(Editor[] editors) Add(m_Header); Add(m_Footer); + // For GameObjects we want to ensure the first component's title bar is flush with the header, + // so we apply a small offset to the margin. (UUM-16138) + if (m_EditorTarget is GameObject) + { + AddToClassList("game-object-inspector"); + } + if (InspectorElement.disabledThrottling) CreateInspectorElement(); } @@ -143,23 +153,7 @@ InspectorElement BuildInspectorElement() var editors = PopulateCache(); var editorTitle = ObjectNames.GetInspectorTitle(m_EditorTarget); - InspectorElement.Mode inspectorElementMode; - - var propertyEditor = inspectorWindow as PropertyEditor; - - if (null == propertyEditor || propertyEditor.inspectorElementModeOverride == 0) - { - inspectorElementMode = InspectorElement.GetModeFromInspectorMode(inspectorWindow.inspectorMode); - - if (!EditorSettings.inspectorUseIMGUIDefaultInspector) - inspectorElementMode &= ~(InspectorElement.Mode.IMGUIDefault); - } - else - { - inspectorElementMode = (InspectorElement.Mode) propertyEditor.inspectorElementModeOverride; - } - - var inspectorElement = new InspectorElement(editor, inspectorElementMode) + var inspectorElement = new InspectorElement(editor) { focusable = false, name = editorTitle + "Inspector", @@ -203,6 +197,7 @@ public void Reinit(int editorIndex, Editor[] editors) PopulateCache(editors); Object editorTarget = editor.targets[0]; + name = GetNameFromEditor(editor); string editorTitle = ObjectNames.GetInspectorTitle(editorTarget); // If the target change we need to invalidate IMGUI container cached measurements @@ -219,13 +214,12 @@ public void Reinit(int editorIndex, Editor[] editors) m_Header.onGUIHandler = HeaderOnGUI; m_Footer.onGUIHandler = FooterOnGUI; - name = editorTitle; m_Header.name = editorTitle + "Header"; m_Footer.name = editorTitle + "Footer"; if (m_InspectorElement != null) { - m_InspectorElement.AssignExistingEditor(editor); + m_InspectorElement.SetEditor(editor); m_InspectorElement.name = editorTitle + "Inspector"; // InspectorElement should be enabled only if the Editor is open for edit. @@ -240,6 +234,9 @@ public void CreateInspectorElement() if (null == editor || null != m_InspectorElement || m_IsCulled) return; + //set the current PropertyHandlerCache to the current editor + ScriptAttributeUtility.propertyHandlerCache = editor.propertyHandlerCache; + // Need to update the cache for multi-object edit detection. if (editor.targets.Length != Selection.objects.Length) inspectorWindow.tracker.RebuildIfNecessary(); @@ -254,6 +251,13 @@ public void CreateInspectorElement() } } + string GetNameFromEditor(Editor editor) + { + return editor == null ? + "Nothing Selected" : + $"{editor.GetType().Name}_{editor.targets[0].GetType().Name}_{editor.targets[0].GetInstanceID()}"; + } + void UpdateInspectorVisibility() { if (editor.CanBeExpandedViaAFoldoutWithoutUpdate()) @@ -483,17 +487,6 @@ Rect DrawEditorSmallHeader(Editor[] editors, Object target, bool wasVisible) if (currentEditor == null) return GUILayoutUtility.GetLastRect(); - // ensure first component's title bar is flush with the header - if (EditorNeedsVerticalOffset(editors, target)) - { - // TODO: Check if we can fix this in the GameObjectInspector instead - GUILayout.Space( - -3f // move back up so line overlaps - - EditorStyles.inspectorBig.margin.bottom - - EditorStyles.inspectorTitlebar.margin.top // move back up margins - ); - } - using (new EditorGUI.DisabledScope(!currentEditor.IsEnabled())) { bool isVisible = EditorGUILayout.InspectorTitlebar(wasVisible, currentEditor); diff --git a/Editor/Mono/UIElements/Inspector/InspectorElement.cs b/Editor/Mono/UIElements/Inspector/InspectorElement.cs index fc27802550..5e4504c580 100644 --- a/Editor/Mono/UIElements/Inspector/InspectorElement.cs +++ b/Editor/Mono/UIElements/Inspector/InspectorElement.cs @@ -3,13 +3,11 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; -using System.Collections.Generic; using Unity.Profiling; using UnityEditor.Profiling; using UnityEngine; using UnityEngine.UIElements; using Object = UnityEngine.Object; -using AssetImporterEditor = UnityEditor.AssetImporters.AssetImporterEditor; namespace UnityEditor.UIElements { @@ -21,14 +19,34 @@ namespace UnityEditor.UIElements /// public class InspectorElement : BindableElement { + /// + /// The framework that should be used to build and run the UI. This is only used for default inspectors (i.e. editors of type ) + /// + internal enum DefaultInspectorFramework + { + /// + /// UIToolkit should be used to generate property fields for default inspectors. + /// + UIToolkit, + + /// + /// IMGUI should be used to generate property fields for default inspectors. These will be placed inside of a . + /// + IMGUI + } + + static readonly ProfilerMarker k_CreateInspectorElementFromSerializedObject = new ProfilerMarker("InspectorElement.CreateInspectorElementFromSerializedObject"); + /// /// USS class name of elements of this type. /// public static readonly string ussClassName = "unity-inspector-element"; + /// /// USS class name of custom inspector elements in elements of this type. /// public static readonly string customInspectorUssClassName = ussClassName + "__custom-inspector-container"; + /// /// USS class name of IMGUI containers in elements of this type. /// @@ -38,6 +56,7 @@ public class InspectorElement : BindableElement /// USS class name of elements of this type, when they are displayed in IMGUI inspector mode. /// public static readonly string iMGUIInspectorVariantUssClassName = ussClassName + "--imgui"; + /// /// USS class name of elements of this type, when they are displayed in UIElements inspector mode. /// @@ -47,31 +66,40 @@ public class InspectorElement : BindableElement /// USS class name of elements of this type, when no inspector is found. /// public static readonly string noInspectorFoundVariantUssClassName = ussClassName + "--no-inspector-found"; + /// /// USS class name of elements of this type, when they are displayed in UIElements custom mode. /// public static readonly string uIECustomVariantUssClassName = ussClassName + "--uie-custom"; + /// /// USS class name of elements of this type, when they are displayed in IMGUI custom mode. /// public static readonly string iMGUICustomVariantUssClassName = ussClassName + "--imgui-custom"; + /// /// USS class name of elements of this type, when they are displayed in IMGUI default mode. /// public static readonly string iMGUIDefaultVariantUssClassName = ussClassName + "--imgui-default"; + /// /// USS class name of elements of this type, when they are displayed in UIElements default mode. /// public static readonly string uIEDefaultVariantUssClassName = ussClassName + "--uie-default"; + /// /// USS class name of elements of this type, when they are displayed in debug USS mode. /// public static readonly string debugVariantUssClassName = ussClassName + "--debug"; + /// /// USS class name of elements of this type, when they are displayed in debug internal mode. /// public static readonly string debugInternalVariantUssClassName = ussClassName + "--debug-internal"; + /// + /// USS class name of elements of this type, when they are displayed without a missing script type. + /// internal static readonly string noScriptErrorContainerName = "unity-inspector-no-script-error-container"; internal static bool disabledThrottling { get; set; } = false; @@ -95,204 +123,289 @@ public UxmlTraits() } } - [Flags] - internal enum Mode - { - UIECustom = 1 << 0, - IMGUICustom = 1 << 1, - IMGUIDefault = 1 << 2, - UIEDefault = 1 << 3, - - DebugMod = 1 << 4, - DebugInternalMod = 1 << 5, - - Normal = UIECustom | IMGUICustom | IMGUIDefault | UIEDefault, - Default = IMGUIDefault | UIEDefault, - Custom = UIECustom | IMGUICustom, - IMGUI = IMGUICustom | IMGUIDefault, - UIE = UIECustom | UIEDefault, - - Debug = Default | DebugMod, - DebugInternal = Default | DebugInternalMod - } + /// + /// Gets the default backend to use based on the current editor settings. + /// + /// The default backend type to use. + static DefaultInspectorFramework GetDefaultInspectorFramework() => EditorSettings.inspectorUseIMGUIDefaultInspector ? DefaultInspectorFramework.IMGUI : DefaultInspectorFramework.UIToolkit; - internal Mode mode { get; private set; } + /// + /// The editor this element is inspecting. + /// + /// An must ALWAYS be backed by an . If one does not exist the inspector will create and manage one. + /// + /// + Editor m_Editor; - internal Editor editor - { - get { return m_Editor; } - private set - { - if (m_Editor != value) - { - DestroyOwnedEditor(); - m_Editor = value; - ownsEditor = false; - } - } - } + /// + /// Flag indicating if the InspectorElement is managing it's own editor instance. + /// + bool m_OwnsEditor; + + /// + /// The currently bound serialized object. + /// + SerializedObject m_BoundObject; + + /// + /// The currently bound object type, this can be used as an optional optimization to avoid rebuilding the UI if the type doesn't change. + /// + Type m_BoundObjectType; - private string m_TrackerName; - internal string trackerName => m_TrackerName ?? (m_TrackerName = GetInspectorTrackerName(this)); + /// + /// The root element of this inspector. This can either be a full visual hierarchy or an IMGUIContainer. + /// + VisualElement m_InspectorElement; - internal bool ownsEditor { get; private set; } = false; + /// + /// The default framework to use for generic inspectors. + /// + DefaultInspectorFramework m_DefaultInspectorFramework; - internal SerializedObject boundObject { get; private set; } + /// + /// The cached tracker name. + /// + string m_TrackerName; - internal VisualElement prefabOverrideBlueBarsContainer { get; private set; } - internal VisualElement livePropertyYellowBarsContainer { get; private set; } + bool m_IgnoreOnInspectorGUIErrors; + bool m_IsOpenForEdit; + bool m_InvalidateGUIBlockCache = true; + bool m_Rebind; - private bool m_IgnoreOnInspectorGUIErrors; + /// + /// Gets or sets the editor backing this inspector element. + /// + internal Editor editor => m_Editor; - static readonly ProfilerMarker k_Reset = new ProfilerMarker("InspectorElement.Reset"); - static readonly ProfilerMarker k_CreateInspectorGUI = new ProfilerMarker("InspectorElement.CreateInspectorGUI"); + /// + /// Returns true if the inspector element owns and manages it's own editor instance. + /// + internal bool ownsEditor => m_OwnsEditor; /// - /// InspectorElement constructor. + /// Returns the current tracker name for this element. This is used for editor performance tracking. /// - public InspectorElement() : this(null as Object) {} + internal string trackerName => m_TrackerName ??= GetInspectorTrackerName(this); /// - /// InspectorElement constructor. + /// The currently bound object. /// - /// Create a SerializedObject from given obj and automatically Bind() to it. - public InspectorElement(Object obj) : this(obj, Mode.Normal) {} + internal SerializedObject boundObject => m_BoundObject; - internal InspectorElement(Object obj, Mode mode) - { - m_IgnoreOnInspectorGUIErrors = false; + /// + /// Visual element reference for prefab override bar. + /// + internal VisualElement prefabOverrideBlueBarsContainer { get; private set; } - pickingMode = PickingMode.Ignore; - AddToClassList(ussClassName); + /// + /// Visual element reference for live property bar. + /// + internal VisualElement livePropertyYellowBarsContainer { get; private set; } - this.mode = mode; - if (obj == null) + /// + /// Gets or sets the default inspector framework to use for this inspector. This will take affect during the next bind. + /// + internal DefaultInspectorFramework defaultInspectorFramework + { + get => m_DefaultInspectorFramework; + set { - if (!GenericInspector.ObjectIsMonoBehaviourOrScriptableObjectWithoutScript(obj)) - { - return; - } + m_Rebind = true; + m_DefaultInspectorFramework = value; } - - this.Bind(new SerializedObject(obj)); } /// - /// InspectorElement constructor. + /// Returns the editor performance tracker name for this inspector element. /// - /// Create a SerializedObject from given obj and automatically Bind() to it. - public InspectorElement(SerializedObject obj) : this(obj, Mode.Normal) {} - - internal InspectorElement(SerializedObject obj, Mode mode) + /// The element to generate a name for. + /// The generated performance tracker name. + internal static string GetInspectorTrackerName(VisualElement element) { - pickingMode = PickingMode.Ignore; - AddToClassList(ussClassName); + var editorElementParent = element.parent as EditorElement; - this.mode = mode; - if (obj.targetObject == null) + return editorElementParent == null + ? $"Editor.Unknown.OnInspectorGUI" + : $"Editor.{editorElementParent.name}.OnInspectorGUI"; + } + + /// + /// Constructs a instance for a given target. + /// + static SerializedObject CreateSerializedObjectForTarget(Object target) + { + if (target == null) { - if (!GenericInspector.ObjectIsMonoBehaviourOrScriptableObjectWithoutScript(obj.targetObject)) - { - return; - } + // ReSharper disable once ExpressionIsAlwaysNull + // Check if this is a 'fake null' object (i.e. equals operator reports null but the object still exists) + if (!GenericInspector.ObjectIsMonoBehaviourOrScriptableObjectWithoutScript(target)) + return null; } - this.Bind(obj); + return new SerializedObject(target); } /// - /// InspectorElement constructor. + /// Initialized a new instance of . + /// + public InspectorElement() : this(null as Object) {} + + /// + /// Initialized a new instance of for the specified . + /// + /// The object to bind to. + public InspectorElement(Object obj) : this(CreateSerializedObjectForTarget(obj), GetDefaultInspectorFramework()) {} + + /// + /// Initialized a new instance of for the specified . /// - public InspectorElement(Editor editor) : this(editor, Mode.Normal) {} + /// The object to bind to. + public InspectorElement(SerializedObject obj) : this(obj, GetDefaultInspectorFramework()) {} - internal InspectorElement(Editor editor, Mode mode) + /// + /// Initialized a new instance of for the specified . + /// + /// The editor to bind to. + public InspectorElement(Editor editor) : this(editor, GetDefaultInspectorFramework()) {} + + internal InspectorElement(Object obj, DefaultInspectorFramework defaultInspectorFramework) : this(new SerializedObject(obj), null, defaultInspectorFramework) { } + internal InspectorElement(SerializedObject serializedObject, DefaultInspectorFramework defaultInspectorFramework) : this(serializedObject, null, defaultInspectorFramework) {} + internal InspectorElement(Editor editor, DefaultInspectorFramework defaultInspectorFramework) : this(editor.serializedObject, editor, defaultInspectorFramework) {} + + InspectorElement(SerializedObject obj, Editor editor, DefaultInspectorFramework defaultInspectorFramework) { pickingMode = PickingMode.Ignore; AddToClassList(ussClassName); - this.mode = mode; + // Register ui callbacks + RegisterCallback(OnAttachToPanel); + RegisterCallback(OnDetachFromPanel); - this.editor = editor; + prefabOverrideBlueBarsContainer = new VisualElement + { + name = BindingExtensions.prefabOverrideBarContainerName, + style = { position = Position.Absolute } + }; - if (editor.targets.Length == 0) + livePropertyYellowBarsContainer = new VisualElement { - return; - } + name = BindingExtensions.livePropertyBarContainerName, + style = { position = Position.Absolute } + }; - var targetObject = editor.targets[0]; - if (targetObject == null) + Add(prefabOverrideBlueBarsContainer); + Add(livePropertyYellowBarsContainer); + + m_DefaultInspectorFramework = defaultInspectorFramework; + + // Find or construct an editor for this object. + if (editor == null) { - if (!GenericInspector.ObjectIsMonoBehaviourOrScriptableObjectWithoutScript(targetObject)) - { + if (obj == null) return; - } + + m_Editor = Editor.CreateEditor(obj.targetObject); + m_OwnsEditor = true; } + else + { + m_Editor = editor; - this.Bind(editor.serializedObject); - } + // If an editor was provided but the targets are not set. + // This should never happen in normal operation since all multi argument constructors are internal/private. + if (m_Editor.targets.Length == 0) + return; + } - void OnDetachFromPanel(DetachFromPanelEvent evt) - { - DestroyOwnedEditor(); + // We have a valid target, initiate binding. + this.Bind(obj); } - internal void AssignExistingEditor(Editor value) + void OnAttachToPanel(AttachToPanelEvent evt) { - if (m_Editor != value) + if (boundObject != null && m_Editor == null) { - editor = value; - PartialReset(m_Editor.serializedObject); + m_Rebind = true; + this.Bind(boundObject); } } - void DestroyOwnedEditor() + void OnDetachFromPanel(DetachFromPanelEvent evt) { - if (ownsEditor && editor != null) - { - Object.DestroyImmediate(editor); - editor = null; - ownsEditor = false; - RegisterCallback(OnAttachToPanel); - } - - UnregisterCallback(OnDetachFromPanel); + DestroyOwnedEditor(); } - void OnAttachToPanel(AttachToPanelEvent evt) + /// + /// Destroys and cleans up the editor instance managed by this inspector element. If any. + /// + void DestroyOwnedEditor() { - Reset(boundObject); - UnregisterCallback(OnAttachToPanel); + if (!m_OwnsEditor || m_Editor == null) + return; + + Object.DestroyImmediate(m_Editor); + + m_Editor = null; + m_OwnsEditor = false; } - internal static Mode GetModeFromInspectorMode(InspectorMode mode) + [EventInterest(typeof(SerializedObjectBindEvent))] + protected override void ExecuteDefaultActionAtTarget(EventBase evt) { - switch (mode) + base.ExecuteDefaultActionAtTarget(evt); + + var bindEvent = evt as SerializedObjectBindEvent; + if (bindEvent == null) + return; + + // Case 1336093. nested InspectorElement for other editors have their own BindTree processes, + // so we need to ignore SerializedObjectBindEvent that aren't meant for them. + // We use the DataSource property to store a reference to the target object that is being bound + // so we can ignore the binding process that targets a parent inspector. + var dataSource = GetProperty(BindingExtensions.s_DataSourceProperty); + if (dataSource != null && dataSource != bindEvent.bindObject) { - case InspectorMode.Debug: - return Mode.Debug; - case InspectorMode.DebugInternal: - return Mode.DebugInternal; - default: - return Mode.Normal; + evt.StopPropagation(); + return; + } + + // Determine if we need to rebuild. This should only be done when our serialized object target changes or something forces us to rebuild (i.e. backend changing). + var shouldRebuildElements = m_Rebind || m_BoundObject != bindEvent.bindObject; + + if (shouldRebuildElements) + { + m_Rebind = false; + CreateInspectorElementFromSerializedObject(bindEvent.bindObject); } } - private void Reset(SerializedObject bindObject) + /// + /// Assigns the given editor to this inspector element instance. This will trigger a re-bind. + /// + /// The editor to assign. + internal void SetEditor(Editor value) { - k_Reset.Begin(); + // NOTE: We need a special check here for undo/redo cases when the script is changing. + var targetTypeMatches = value.target && value.target.GetType() == m_BoundObjectType; + var editorInstanceMatches = m_Editor == value; - Clear(); + if (targetTypeMatches && editorInstanceMatches) + return; - prefabOverrideBlueBarsContainer = new VisualElement(); - prefabOverrideBlueBarsContainer.name = BindingExtensions.prefabOverrideBarContainerName; - prefabOverrideBlueBarsContainer.style.position = Position.Absolute; - Add(prefabOverrideBlueBarsContainer); + DestroyOwnedEditor(); - livePropertyYellowBarsContainer = new VisualElement(); - livePropertyYellowBarsContainer.name = BindingExtensions.livePropertyBarContainerName; - livePropertyYellowBarsContainer.style.position = Position.Absolute; - Add(livePropertyYellowBarsContainer); + m_Editor = value; + m_Rebind = true; + + this.Bind(m_Editor.serializedObject); + } + + void ClearInspectorElement() + { + // Clear any previously generated element. + m_InspectorElement?.RemoveFromHierarchy(); + // Clear all top level styles RemoveFromClassList(iMGUIInspectorVariantUssClassName); RemoveFromClassList(uIEInspectorVariantUssClassName); RemoveFromClassList(noInspectorFoundVariantUssClassName); @@ -302,120 +415,96 @@ private void Reset(SerializedObject bindObject) RemoveFromClassList(uIEDefaultVariantUssClassName); RemoveFromClassList(debugVariantUssClassName); RemoveFromClassList(debugInternalVariantUssClassName); - - if (bindObject == null) - return; - - var editor = GetOrCreateEditor(bindObject); - if (editor == null) - { - return; - } - - boundObject = bindObject; - - CreateInspectorGUI(false); - - k_Reset.End(); } - private void PartialReset(SerializedObject bindObject) + void CreateInspectorElementFromSerializedObject(SerializedObject bindObject) { - boundObject = bindObject; - if (boundObject == null) - { - Reset(null); - return; - } - - CreateInspectorGUI(true); - } + k_CreateInspectorElementFromSerializedObject.Begin(); - void CreateInspectorGUI(bool updateBinding) - { - k_CreateInspectorGUI.Begin(); + // Unpack the given serialized object. We want to cache the target type to facilitate re-using UI later down the pipeline. + var targetObject = bindObject?.targetObject; + var targetObjectType = targetObject != null ? targetObject.GetType() : null; - Clear(); + var bindObjectTypeMatches = m_BoundObjectType == targetObjectType; - // Add prefabOverrideBlueBarsContainer again. - if (prefabOverrideBlueBarsContainer != null) - { - Add(prefabOverrideBlueBarsContainer); - } + m_BoundObject = bindObject; + m_BoundObjectType = targetObjectType; - // Add livePropertyYellowBarsContainer again. - if (livePropertyYellowBarsContainer != null) + if (m_BoundObject == null) { - Add(livePropertyYellowBarsContainer); + ClearInspectorElement(); + return; } - var element = CreateInspectorElementFromEditor(editor, true) ?? CreateDefaultInspector(boundObject); - - if (element != null && element != this) - hierarchy.Add(element); - - if (updateBinding) - element?.Bind(boundObject); + m_Editor = GetOrCreateEditor(bindObject); - k_CreateInspectorGUI.End(); - } + if (m_Editor != null) + { + if (m_Editor is GenericInspector) + { + // Only re-build default inspectors when the type changes or when using the IMGUI backend. + // NOTE: When using IMGUI the delegate captures locals so we must rebuild it. + if (!bindObjectTypeMatches || m_InspectorElement == null || m_DefaultInspectorFramework == DefaultInspectorFramework.IMGUI) + { + ClearInspectorElement(); - [EventInterest(typeof(SerializedObjectBindEvent))] - protected override void ExecuteDefaultActionAtTarget(EventBase evt) - { - base.ExecuteDefaultActionAtTarget(evt); + // When looking at a generic inspector we choose the backend based on a user provided default. + switch (m_DefaultInspectorFramework) + { + case DefaultInspectorFramework.UIToolkit: + m_InspectorElement = CreateInspectorElementUsingUIToolkit(m_Editor); + break; + case DefaultInspectorFramework.IMGUI: + m_InspectorElement = CreateInspectorElementUsingIMGUI(m_Editor); + break; + } + } + } + else + { + // Always clear and re-build when dealing with custom inspectors. User code is assumed to take a reference to the ScriptableObject in `CreateInspectorGUI()`. + ClearInspectorElement(); - var bindEvent = evt as SerializedObjectBindEvent; - if (bindEvent == null) - return; + // This is a custom editor type. Try to use UI toolkit first with an IMGUI fallback. + m_InspectorElement = CreateInspectorElementUsingUIToolkit(m_Editor) ?? CreateInspectorElementUsingIMGUI(m_Editor); + m_InspectorElement.AddToClassList(customInspectorUssClassName); + } - // Case 1336093. nested InspectorElement for other editors have their own BindTree processes, - // so we need to ignore SerializedObjectBindEvent that aren't meant for them. - // We use the DataSource property to store a reference to the target object that is being bound - // so we can ignore the binding process that targets a parent inspector. - var dataSource = this.GetProperty(BindingExtensions.s_DataSourceProperty); - if (dataSource != null && dataSource != bindEvent.bindObject) - { - evt.StopPropagation(); - return; + // Re-add the generated element if it was re-created. + if (m_InspectorElement != null && m_InspectorElement.parent != this) + hierarchy.Add(m_InspectorElement); } - Reset(bindEvent.bindObject); + k_CreateInspectorElementFromSerializedObject.End(); } - private Editor GetOrCreateEditor(SerializedObject serializedObject) + /// + /// This method handles all the internals of getting a object for the given serialized object. + /// + /// The serialized object to get an editor for. + /// The found or created instance. + Editor GetOrCreateEditor(SerializedObject serializedObject) { - var target = serializedObject?.targetObject; + Object target = null; - if (editor != null) - { - if (editor.target == target || editor.serializedObject == serializedObject) - return editor; + if (serializedObject != null && serializedObject.m_NativeObjectPtr != IntPtr.Zero) + target = serializedObject.targetObject; - if (ownsEditor) - { - DestroyOwnedEditor(); - } - } - - foreach (var inspectorWindow in InspectorWindow.GetInspectors()) + if (m_Editor != null) { - foreach (var trackerEditor in inspectorWindow.tracker.activeEditors) - { - if (trackerEditor.target == target || trackerEditor.serializedObject == serializedObject) - { - return editor = trackerEditor; - } - } - } + // First try to re-use the instance we have on hand. If this matches our given object we can simply re-use it. + if (m_Editor.target == target || m_Editor.serializedObject == serializedObject) + return m_Editor; - RegisterCallback(OnDetachFromPanel); + // We need to generate a new editor instance, first cleanup any owned editor resources we have. + DestroyOwnedEditor(); + } - var ed = Editor.CreateEditor(serializedObject?.targetObject); - editor = ed; - ownsEditor = true; + // Fallback to creating our own editor instance for the given object. + m_Editor = Editor.CreateEditor(target); + m_OwnsEditor = true; - return ed; + return m_Editor; } /// @@ -436,12 +525,17 @@ public static void FillDefaultInspector(VisualElement container, SerializedObjec { do { - var field = new PropertyField(property); - field.name = "PropertyField:" + property.propertyPath; + var field = new PropertyField(property) + { + name = "PropertyField:" + property.propertyPath + }; if (property.propertyPath == "m_Script") { - if (serializedObject.targetObject != null || property.objectReferenceValue != null || isPartOfPrefabInstance) + // Allow script re-assignment in debug mode. + var isDebugMode = editor != null && (editor.inspectorMode == InspectorMode.Debug || editor.inspectorMode == InspectorMode.DebugInternal); + + if (!isDebugMode && (serializedObject.targetObject != null || property.objectReferenceValue != null || isPartOfPrefabInstance)) field.SetEnabled(false); } @@ -451,105 +545,70 @@ public static void FillDefaultInspector(VisualElement container, SerializedObjec } if (serializedObject.targetObject == null) - AddMissingScriptLabel(container, serializedObject, isPartOfPrefabInstance); - } - - private VisualElement CreateDefaultInspector(SerializedObject serializedObject) - { - if (serializedObject == null) - return null; - - if (GenericInspector.MissingSerializeReference(editor.target)) { - Add(new HelpBox(GenericInspector.GetMissingSerializeRefererenceMessageContainer(), HelpBoxMessageType.Warning)); - } - - FillDefaultInspector(this, serializedObject, editor); + var scriptProperty = serializedObject.FindProperty("m_Script"); - if ((mode & Mode.DebugMod) > 0) - AddToClassList(debugVariantUssClassName); - else if ((mode & Mode.DebugInternalMod) > 0) - AddToClassList(debugInternalVariantUssClassName); - - AddToClassList(uIEDefaultVariantUssClassName); - AddToClassList(uIEInspectorVariantUssClassName); - - return this; - } - - static bool AddMissingScriptLabel(VisualElement container, SerializedObject serializedObject, bool isPartOfPrefabInstance) - { - SerializedProperty scriptProperty = serializedObject.FindProperty("m_Script"); - if (scriptProperty != null) - { - var noScriptErrorContainer = new IMGUIContainer(() => GenericInspector.ShowScriptNotLoadedWarning(scriptProperty, isPartOfPrefabInstance)); - noScriptErrorContainer.name = noScriptErrorContainerName; - container.Add(noScriptErrorContainer); - return true; + if (scriptProperty != null) + { + var noScriptErrorContainer = new IMGUIContainer(() => + { + if (scriptProperty.isValid) + GenericInspector.ShowScriptNotLoadedWarning(scriptProperty, isPartOfPrefabInstance); + }); + noScriptErrorContainer.name = noScriptErrorContainerName; + container.Add(noScriptErrorContainer); + } } - - return false; } - internal static bool SetWideModeForWidth(VisualElement displayElement) + VisualElement CreateInspectorElementUsingUIToolkit(Editor targetEditor) { - var previousWideMode = EditorGUIUtility.wideMode; - - float inspectorWidth = 0; + var element = targetEditor.CreateInspectorGUI(); - // the inspector's width can be NaN if this is our first layout check. - // or when the inspector display is changed from none to flex, the width will be zero during the measuring phase. - // we try to find a parent with a a width. If none are found, we'll set wideMode to true to avoid computing - // too tall an inspector on the first layout calculation - while (displayElement != null && (float.IsNaN(inspectorWidth) || inspectorWidth == 0)) + if (element != null) { - inspectorWidth = displayElement.layout.width; - displayElement = displayElement.hierarchy.parent; - } + // Decorate the InspectorElement based on the editor type (i.e. custom vs generic). + if (targetEditor is GenericInspector) + { + AddToClassList(uIEDefaultVariantUssClassName); + AddToClassList(uIEInspectorVariantUssClassName); - if (!float.IsNaN(inspectorWidth) && inspectorWidth > 0) - { - EditorGUIUtility.wideMode = inspectorWidth > Editor.k_WideModeMinWidth; - EditorGUIUtility.currentViewWidth = inspectorWidth; - } - else - { - EditorGUIUtility.wideMode = true; + switch (targetEditor.inspectorMode) + { + case InspectorMode.Debug: + AddToClassList(debugVariantUssClassName); + break; + case InspectorMode.DebugInternal: + AddToClassList(debugInternalVariantUssClassName); + break; + } + } + else + { + AddToClassList(uIECustomVariantUssClassName); + + if (editor.UseDefaultMargins()) + AddToClassList(uIEInspectorVariantUssClassName); + } } - return previousWideMode; + return element; } - IMGUIContainer m_IMGUIContainer; - - private VisualElement CreateIMGUIInspectorFromEditor(SerializedObject serializedObject, Editor editor, - bool reuseIMGUIContainer) + VisualElement CreateInspectorElementUsingIMGUI(Editor targetEditor) { - if ((mode & (Mode.IMGUICustom | Mode.IMGUIDefault)) == 0) - return null; - - if ((mode & Mode.IMGUICustom) > 0 && (mode & Mode.IMGUIDefault) == 0 && editor is GenericInspector) - return null; - - if ((mode & Mode.IMGUICustom) == 0 && (mode & Mode.IMGUIDefault) > 0 && !(editor is GenericInspector) && !(editor is AssetImporterEditor) && !(editor is GameObjectInspector)) - { - editor = ScriptableObject.CreateInstance(); - editor.hideFlags = HideFlags.HideAndDontSave; - editor.InternalSetTargets(new[] { serializedObject.targetObject }); - } - - if (editor is GenericInspector) + if (targetEditor is GenericInspector) { AddToClassList(iMGUIDefaultVariantUssClassName); - if ((mode & Mode.DebugMod) > 0) - { - AddToClassList(debugVariantUssClassName); - editor.inspectorMode = InspectorMode.Debug; - } - else if ((mode & Mode.DebugInternalMod) > 0) + + switch (targetEditor.inspectorMode) { - AddToClassList(debugInternalVariantUssClassName); - editor.inspectorMode = InspectorMode.DebugInternal; + case InspectorMode.Debug: + AddToClassList(debugVariantUssClassName); + break; + case InspectorMode.DebugInternal: + AddToClassList(debugInternalVariantUssClassName); + break; } } else @@ -557,16 +616,8 @@ private VisualElement CreateIMGUIInspectorFromEditor(SerializedObject serialized AddToClassList(iMGUICustomVariantUssClassName); } - IMGUIContainer inspector; - // Reusing the existing IMGUIContainer allows us to re-use the existing gui state, when we are drawing the same inspector this will let us keep the same control ids - if (reuseIMGUIContainer && m_IMGUIContainer != null) - { - inspector = m_IMGUIContainer; - } - else - { - inspector = new IMGUIContainer(); - } + // Always try to re-use the imgui container if possible. + var inspector = m_InspectorElement as IMGUIContainer ?? new IMGUIContainer(); m_IgnoreOnInspectorGUIErrors = false; inspector.onGUIHandler = () => @@ -593,42 +644,23 @@ private VisualElement CreateIMGUIInspectorFromEditor(SerializedObject serialized // rely heavily on yields and timing of UI redraws. Yes.. // // case 1119612 - if (editor.m_SerializedObject == null) + if (targetEditor.m_SerializedObject == null) { - editor.Repaint(); + targetEditor.Repaint(); m_IgnoreOnInspectorGUIErrors = true; } - if ((editor.target == null && !GenericInspector.ObjectIsMonoBehaviourOrScriptableObjectWithoutScript(editor.target)) || - !editor.serializedObject.isValid) + if ((targetEditor.target == null && !GenericInspector.ObjectIsMonoBehaviourOrScriptableObjectWithoutScript(targetEditor.target)) || + !targetEditor.serializedObject.isValid) { return; } EditorGUIUtility.ResetGUIState(); - using (new EditorGUI.DisabledScope(!editor.IsEnabled())) + using (new EditorGUI.DisabledScope(!targetEditor.IsEnabled() || !enabledInHierarchy)) { - var genericEditor = editor as GenericInspector; - if (genericEditor != null) - { - switch (mode) - { - case Mode.Normal: - genericEditor.inspectorMode = InspectorMode.Normal; - break; - case Mode.Default: - genericEditor.inspectorMode = InspectorMode.Debug; - break; - case Mode.Custom: - genericEditor.inspectorMode = InspectorMode.DebugInternal; - break; - case Mode.IMGUI: - break; - } - } - //set the current PropertyHandlerCache to the current editor - ScriptAttributeUtility.propertyHandlerCache = editor.propertyHandlerCache; + ScriptAttributeUtility.propertyHandlerCache = targetEditor.propertyHandlerCache; var originalViewWidth = EditorGUIUtility.currentViewWidth; var originalHierarchyMode = EditorGUIUtility.hierarchyMode; @@ -636,7 +668,7 @@ private VisualElement CreateIMGUIInspectorFromEditor(SerializedObject serialized var originalWideMode = SetWideModeForWidth(inspector); - GUIStyle editorWrapper = (editor.UseDefaultMargins() && editor.CanBeExpandedViaAFoldoutWithoutUpdate() + GUIStyle editorWrapper = (targetEditor.UseDefaultMargins() && targetEditor.CanBeExpandedViaAFoldoutWithoutUpdate() ? EditorStyles.inspectorDefaultMargins : GUIStyle.none); try @@ -655,34 +687,34 @@ private VisualElement CreateIMGUIInspectorFromEditor(SerializedObject serialized var layoutCacheState = GUILayoutUtility.current.State; try { - var rebuildOptimizedGUIBlocks = GetRebuildOptimizedGUIBlocks(editor.target); - rebuildOptimizedGUIBlocks |= editor.isInspectorDirty; - float height; - if (editor.GetOptimizedGUIBlock(rebuildOptimizedGUIBlocks, visible, out height)) + var rebuildOptimizedGUIBlocks = GetRebuildOptimizedGUIBlocks(targetEditor.target); + rebuildOptimizedGUIBlocks |= targetEditor.isInspectorDirty; + + if (targetEditor.GetOptimizedGUIBlock(rebuildOptimizedGUIBlocks, visible, out var height)) { - var contentRect = GUILayoutUtility.GetRect(0, visible ? height : 0); + var contentHeightRect = GUILayoutUtility.GetRect(0, visible ? height : 0); // Layout events are ignored in the optimized code path // The exception is when we are drawing a GenericInspector, they always use the optimized path and must therefore run at least one layout calculation in it - if (Event.current.type == EventType.Layout && !(editor is GenericInspector)) + if (Event.current.type == EventType.Layout && !(targetEditor is GenericInspector)) { return; } - InspectorWindowUtils.DrawAddedComponentBackground(contentRect, editor.targets); + InspectorWindowUtils.DrawAddedComponentBackground(contentHeightRect, targetEditor.targets); // Draw content if (visible) { GUI.changed = false; - editor.OnOptimizedInspectorGUI(contentRect); + targetEditor.OnOptimizedInspectorGUI(contentHeightRect); } } else { - InspectorWindowUtils.DrawAddedComponentBackground(contentRect, editor.targets); + InspectorWindowUtils.DrawAddedComponentBackground(contentRect, targetEditor.targets); using (new EditorPerformanceTracker(trackerName)) - editor.OnInspectorGUI(); + targetEditor.OnInspectorGUI(); } } catch (Exception e) @@ -707,7 +739,7 @@ private VisualElement CreateIMGUIInspectorFromEditor(SerializedObject serialized { if (GUI.changed) { - // This forces a relayout of all imguicontainers in this inspector window. + // This forces a re-layout of all imgui containers in this inspector window. // fixes part of case 1148706 var element = inspector.GetFirstAncestorOfType(); if (element != null) @@ -721,10 +753,6 @@ private VisualElement CreateIMGUIInspectorFromEditor(SerializedObject serialized }; inspector.style.overflow = Overflow.Visible; - m_IMGUIContainer = inspector; - - if (!(editor is GenericInspector)) - inspector.AddToClassList(customInspectorUssClassName); inspector.AddToClassList(iMGUIContainerUssClassName); @@ -733,63 +761,36 @@ private VisualElement CreateIMGUIInspectorFromEditor(SerializedObject serialized return inspector; } - internal static string GetInspectorTrackerName(VisualElement el) + internal static bool SetWideModeForWidth(VisualElement displayElement) { - var editorElementParent = el.parent as EditorElement; - if (editorElementParent == null) - return $"Editor.Unknown.OnInspectorGUI"; + var previousWideMode = EditorGUIUtility.wideMode; - return $"Editor.{editorElementParent.name}.OnInspectorGUI"; - } + float inspectorWidth = 0; - private VisualElement CreateInspectorElementFromEditor(Editor editor, bool reuseIMGUIContainer = false) - { - var serializedObject = editor.serializedObject; - var target = editor.targets[0]; - if (target == null) + // the inspector's width can be NaN if this is our first layout check. + // or when the inspector display is changed from none to flex, the width will be zero during the measuring phase. + // we try to find a parent with a a width. If none are found, we'll set wideMode to true to avoid computing + // too tall an inspector on the first layout calculation + while (displayElement != null && (float.IsNaN(inspectorWidth) || inspectorWidth == 0)) { - if (!GenericInspector.ObjectIsMonoBehaviourOrScriptableObjectWithoutScript(target)) - { - return null; - } + inspectorWidth = displayElement.layout.width; + displayElement = displayElement.hierarchy.parent; } - VisualElement inspectorElement = null; - - if ((mode & Mode.UIECustom) > 0) + if (!float.IsNaN(inspectorWidth) && inspectorWidth > 0) { - inspectorElement = editor.CreateInspectorGUI(); - - if (inspectorElement != null) - { - AddToClassList(uIECustomVariantUssClassName); - if (editor.UseDefaultMargins()) - AddToClassList(uIEInspectorVariantUssClassName); - inspectorElement.AddToClassList(customInspectorUssClassName); - } + EditorGUIUtility.wideMode = inspectorWidth > Editor.k_WideModeMinWidth; + EditorGUIUtility.currentViewWidth = inspectorWidth; } - - if (inspectorElement == null) - inspectorElement = CreateIMGUIInspectorFromEditor(serializedObject, editor, reuseIMGUIContainer); - - if (inspectorElement == null && (mode & Mode.UIEDefault) > 0) - inspectorElement = CreateDefaultInspector(serializedObject); - - if (inspectorElement == null) + else { - AddToClassList(noInspectorFoundVariantUssClassName); - AddToClassList(uIEInspectorVariantUssClassName); - inspectorElement = new Label("No inspector found given the current Inspector.Mode."); + EditorGUIUtility.wideMode = true; } - return inspectorElement; + return previousWideMode; } - bool m_IsOpenForEdit; - bool m_InvalidateGUIBlockCache = true; - Editor m_Editor; - - private bool GetRebuildOptimizedGUIBlocks(Object inspectedObject) + bool GetRebuildOptimizedGUIBlocks(Object inspectedObject) { var rebuildOptimizedGUIBlocks = false; diff --git a/Editor/Mono/UIElements/StyleSheets/StyleSheetImporterImpl.cs b/Editor/Mono/UIElements/StyleSheets/StyleSheetImporterImpl.cs index fe4fbfc774..051d77b237 100644 --- a/Editor/Mono/UIElements/StyleSheets/StyleSheetImporterImpl.cs +++ b/Editor/Mono/UIElements/StyleSheets/StyleSheetImporterImpl.cs @@ -122,6 +122,11 @@ public virtual UnityEngine.Object DeclareDependencyAndLoad(string path, string s if (o.name == subAssetPath) return o; } + + // We sometimes include the main asset name in the sub-asset name. (UUM-49355) + if (mainAsset != null && mainAsset.name == subAssetPath) + return mainAsset; + return null; } @@ -344,9 +349,9 @@ private struct StoredAsset } } + using (var so = new SerializedObject(font)) { - var so = new SerializedObject(font); - var oldTex = so.FindProperty("m_Texture").objectReferenceValue; + var oldTex = so.FindProperty("m_Texture").objectReferenceValue; if (oldTex != null) { //Reuse the same texture if the reference was equal diff --git a/Editor/Mono/UIElements/StyleSheets/ThemeStyleSheetImporter.cs b/Editor/Mono/UIElements/StyleSheets/ThemeStyleSheetImporter.cs index a3820cc021..70bb42d9a1 100644 --- a/Editor/Mono/UIElements/StyleSheets/ThemeStyleSheetImporter.cs +++ b/Editor/Mono/UIElements/StyleSheets/ThemeStyleSheetImporter.cs @@ -11,7 +11,7 @@ namespace UnityEditor.UIElements.StyleSheets { // Make sure style sheets importer after allowed dependent assets: textures, fonts, json and uss. // Has to be higher then AssetImportOrder.kImportOrderLate - [ScriptedImporter(version: 3, ext: "tss", importQueueOffset: 1101)] + [ScriptedImporter(version: 4, ext: "tss", importQueueOffset: 1101)] [ExcludeFromPreset] class ThemeStyleSheetImporter : StyleSheetImporter { diff --git a/Editor/Mono/Undo/Undo.bindings.cs b/Editor/Mono/Undo/Undo.bindings.cs index 2ac5ff4d9b..6a4d59b13d 100644 --- a/Editor/Mono/Undo/Undo.bindings.cs +++ b/Editor/Mono/Undo/Undo.bindings.cs @@ -64,9 +64,15 @@ public void Dispose() [NativeHeader("Editor/Src/Undo/ObjectUndo.h")] [NativeHeader("Editor/Mono/Undo/Undo.bindings.h")] [NativeHeader("Editor/Src/Undo/AssetUndo.h")] - public partial class Undo { + [StaticAccessor("UndoBindings", StaticAccessorType.DoubleColon)] + public static extern bool isProcessing + { + [NativeMethod("GetIsProcessing")] + get; + } + [StaticAccessor("UndoBindings", StaticAccessorType.DoubleColon)] private static extern void GetRecordsInternal(object undoRecords, out int undoCursorPos); @@ -105,7 +111,7 @@ public static void RegisterCompleteObjectUndo(Object[] objectsToUndo, string nam [NativeThrows] [StaticAccessor("UndoBindings", StaticAccessorType.DoubleColon)] - private static extern void RegisterCompleteObjectUndoMultiple([NotNull] Object identifier, Object[] objectsToUndo, string name, int namePriority); + private static extern void RegisterCompleteObjectUndoMultiple([NotNull] Object identifier, [Unmarshalled] Object[] objectsToUndo, string name, int namePriority); public static void SetTransformParent(Transform transform, Transform newParent, string name) { @@ -119,6 +125,9 @@ public static void SetTransformParent(Transform transform, Transform newParent, [StaticAccessor("UndoBindings", StaticAccessorType.DoubleColon)] public static extern void MoveGameObjectToScene([NotNull] GameObject go, Scene scene, string name); + [StaticAccessor("UndoBindings", StaticAccessorType.DoubleColon)] + public static extern void SetSiblingIndex([NotNull] Transform transform, int siblingIndex, string name); + // Register the state of a Unity Object so the user can later undo back to that state. public static void RegisterCreatedObjectUndo(Object objectToUndo, string name) { @@ -188,7 +197,7 @@ public static void RecordObjects(Object[] objectsToUndo, string name) [NativeThrows] [StaticAccessor("UndoBindings", StaticAccessorType.DoubleColon)] - private static extern void RecordObjectsInternal(Object[] objectToUndo, int size, string name); + private static extern void RecordObjectsInternal([Unmarshalled] Object[] objectToUndo, int size, string name); [StaticAccessor("GetUndoManager()", StaticAccessorType.Dot)] [NativeMethod("ClearUndoIdentifier")] diff --git a/Editor/Mono/Unsupported.bindings.cs b/Editor/Mono/Unsupported.bindings.cs index 6523eaf5c4..841dd06285 100644 --- a/Editor/Mono/Unsupported.bindings.cs +++ b/Editor/Mono/Unsupported.bindings.cs @@ -80,6 +80,8 @@ public static bool IsDeveloperBuild() public static extern bool IsNativeCodeBuiltInReleaseMode(); + internal static extern void CheckCorrectAwakeUsage(); + [FreeFunction(IsThreadSafe = true)] public static extern string GetBaseUnityDeveloperFolder(); @@ -88,6 +90,9 @@ public static bool IsDeveloperBuild() [FreeFunction("GetSceneTracker().FlushDirty")] public static extern void SceneTrackerFlushDirty(); + [FreeFunction("GetSceneTracker().TickHierarchyHasChanged")] + internal static extern void TickHierarchyHasChanged(); + [FreeFunction("GetScreenManager().SetAllowCursorHide")] public static extern void SetAllowCursorHide(bool allow); diff --git a/Editor/Mono/Unwrapping.bindings.cs b/Editor/Mono/Unwrapping.bindings.cs index eb66678f06..40e0f8cf59 100644 --- a/Editor/Mono/Unwrapping.bindings.cs +++ b/Editor/Mono/Unwrapping.bindings.cs @@ -44,6 +44,9 @@ public static Vector2[] GeneratePerTriangleUV(Mesh src) // Will generate per-triangle uv (3 uv pairs for each triangle) with provided settings public static Vector2[] GeneratePerTriangleUV(Mesh src, UnwrapParam settings) { + if (src == null) + throw new ArgumentNullException("src"); + return GeneratePerTriangleUVImpl(src, settings); } diff --git a/Editor/Mono/Utils/Program.cs b/Editor/Mono/Utils/Program.cs index a4dd94b420..a53ab53fbd 100644 --- a/Editor/Mono/Utils/Program.cs +++ b/Editor/Mono/Utils/Program.cs @@ -47,6 +47,7 @@ public void Start(EventHandler exitCallback) _process.StartInfo.RedirectStandardInput = true; _process.StartInfo.RedirectStandardError = true; _process.StartInfo.RedirectStandardOutput = true; + _process.StartInfo.StandardInputEncoding = new UTF8Encoding(false); _process.StartInfo.UseShellExecute = false; _process.Start(); diff --git a/Editor/Mono/Utils/WebURLs.bindings.cs b/Editor/Mono/Utils/WebURLs.bindings.cs index 0b8b271d98..2ae1870a82 100644 --- a/Editor/Mono/Utils/WebURLs.bindings.cs +++ b/Editor/Mono/Utils/WebURLs.bindings.cs @@ -15,8 +15,8 @@ internal static class WebURLs public static extern string unityConnect { get; } [NativeProperty("kURLUnityForum", true, TargetType.Field)] public static extern string unityForum { get; } - [NativeProperty("kURLUnityAnswers", true, TargetType.Field)] - public static extern string unityAnswers { get; } + [NativeProperty("kURLUnityDiscussions", true, TargetType.Field)] + public static extern string unityDiscussions { get; } [NativeProperty("kURLUnityFeedback", true, TargetType.Field)] public static extern string unityFeedback { get; } [NativeProperty("kURLWhatsNewPage", true, TargetType.Field)] diff --git a/Editor/Mono/VersionControlSettings.bindings.cs b/Editor/Mono/VersionControlSettings.bindings.cs index 8249de6ce7..bdc8633a48 100644 --- a/Editor/Mono/VersionControlSettings.bindings.cs +++ b/Editor/Mono/VersionControlSettings.bindings.cs @@ -67,6 +67,10 @@ public static extern string mode set; } + [StaticAccessor("GetVersionControlSettings()", StaticAccessorType.Dot)] + [ExcludeFromDocs] + public static extern bool trackPackagesOutsideProject { get; set; } + [StaticAccessor("GetEditorUserSettings()", StaticAccessorType.Dot)] private static extern string GetConfigValue(string name); diff --git a/External/JsonParsers/MiniJson/MiniJSON.cs b/External/JsonParsers/MiniJson/MiniJSON.cs index e7e5be0e31..27efbc376f 100644 --- a/External/JsonParsers/MiniJson/MiniJSON.cs +++ b/External/JsonParsers/MiniJson/MiniJSON.cs @@ -322,16 +322,13 @@ object ParseNumber () { string number = NextWord; - if (number.IndexOf ('.') == -1) - { - long parsedInt; - Int64.TryParse (number, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out parsedInt); - return parsedInt; - } + if (Int64.TryParse(number, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out var parsedInt)) + { + return parsedInt; + } - double parsedDouble; - Double.TryParse (number, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out parsedDouble); - return parsedDouble; + Double.TryParse(number, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out var parsedDouble); + return parsedDouble; } void EatWhitespace () 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 deleted file mode 100644 index 5cbb4ad1cd..0000000000 --- a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Inspector/BuilderInspectorAttributes.cs +++ /dev/null @@ -1,1183 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Reflection; -using System.Text.RegularExpressions; -using UnityEditor; -using UnityEditor.UIElements; -using UnityEditor.UIElements.StyleSheets; -using UnityEngine; -using UnityEngine.UIElements; -using Object = UnityEngine.Object; - -namespace Unity.UI.Builder -{ - internal class BuilderInspectorAttributes : IBuilderInspectorSection - { - static readonly Dictionary s_CachedTypeInfos = new Dictionary(); - - static readonly UnityEngine.Pool.ObjectPool s_TypeNameItemPool = - new UnityEngine.Pool.ObjectPool( - () => new BuilderAttributeTypeName(), - null, - c => c.ClearType()); - - static TypeInfo GetTypeInfo(Type type) - { - if (!s_CachedTypeInfos.TryGetValue(type, out var typeInfo)) - s_CachedTypeInfos[type] = typeInfo = new TypeInfo(type); - return typeInfo; - } - - internal readonly struct TypeInfo - { - public readonly Type type; - public readonly string value; - - public TypeInfo(Type type) - { - this.type = type; - this.value = $"{type.FullName}, {type.Assembly.GetName().Name}"; - } - } - - BuilderInspector m_Inspector; - BuilderSelection m_Selection; - PersistedFoldout m_AttributesSection; - - VisualElement currentVisualElement => m_Inspector.currentVisualElement; - - public VisualElement root => m_AttributesSection; - - public BuilderInspectorAttributes(BuilderInspector inspector) - { - m_Inspector = inspector; - m_Selection = inspector.selection; - - m_AttributesSection = m_Inspector.Q("inspector-attributes-foldout"); - } - - public void Refresh() - { - m_AttributesSection.Clear(); - - if (currentVisualElement == null) - return; - - if (m_Selection.selectionType != BuilderSelectionType.Element && - m_Selection.selectionType != BuilderSelectionType.ElementInTemplateInstance && - m_Selection.selectionType != BuilderSelectionType.ElementInControlInstance) - return; - - if (m_Selection.selectionType == BuilderSelectionType.ElementInTemplateInstance && - string.IsNullOrEmpty(currentVisualElement.name)) - { - var helpBox = new HelpBox(); - helpBox.AddToClassList(BuilderConstants.InspectorClassHelpBox); - helpBox.text = BuilderConstants.NoNameElementAttributes; - - m_AttributesSection.Add(helpBox); - } - - GenerateAttributeFields(); - - // Forward focus to the panel header. - m_AttributesSection - .Query() - .Where(e => e.focusable) - .ForEach((e) => m_Inspector.AddFocusable(e)); - } - - public void Enable() - { - m_AttributesSection.contentContainer.SetEnabled(true); - } - - public void Disable() - { - m_AttributesSection.contentContainer.SetEnabled(false); - } - - void GenerateAttributeFields() - { - var attributeList = currentVisualElement.GetAttributeDescriptions(); - - foreach (var attribute in attributeList) - { - if (attribute == null || attribute.name == null || IsAttributeIgnored(attribute)) - continue; - - CreateAttributeRow(m_AttributesSection, attribute); - } - } - - static bool IsAttributeIgnored(UxmlAttributeDescription attribute) - { - // Temporary check until we add an "obsolete" mechanism to uxml attribute description. - return attribute.name == "show-horizontal-scroller" || attribute.name == "show-vertical-scroller" || attribute.name == "name"; - } - - BuilderStyleRow CreateAttributeRow(VisualElement parent, UxmlAttributeDescription attribute) - { - var attributeType = attribute.GetType(); - - // Generate field label. - var fieldLabel = BuilderNameUtilities.ConvertDashToHuman(attribute.name); - BindableElement fieldElement; - if (attribute is UxmlStringAttributeDescription) - { - // Hard-coded - if (attribute.name.Equals("value") && currentVisualElement is EnumField enumField) - { - var uiField = new EnumField("Value"); - if (null != enumField.value) - uiField.Init(enumField.value, enumField.includeObsoleteValues); - else - uiField.SetValueWithoutNotify(null); - uiField.RegisterValueChangedCallback(evt => - PostAttributeValueChange(uiField, uiField.value.ToString())); - fieldElement = uiField; - } - else if (attribute.name.Equals("value") && currentVisualElement is EnumFlagsField enumFlagsField) - { - var uiField = new EnumFlagsField("Value"); - uiField.RegisterValueChangedCallback(OnAttributeValueChange); - if (null != enumFlagsField.value) - uiField.Init(enumFlagsField.value, enumFlagsField.includeObsoleteValues); - else - uiField.SetValueWithoutNotify(null); - uiField.RegisterValueChangedCallback(evt => - PostAttributeValueChange(uiField, uiField.value.ToString())); - fieldElement = uiField; - } - else if (attribute.name.Equals("value") && currentVisualElement is TagField tagField) - { - var uiField = new TagField("Value"); - uiField.RegisterValueChangedCallback(evt => - PostAttributeValueChange(uiField, uiField.value.ToString())); - fieldElement = uiField; - } - else - { - var uiField = new TextField(fieldLabel); - if (attribute.name.Equals("name") || attribute.name.Equals("view-data-key")) - uiField.RegisterValueChangedCallback(e => - { - OnValidatedAttributeValueChange(e, BuilderNameUtilities.attributeRegex, - BuilderConstants.AttributeValidationSpacialCharacters); - }); - else if (attribute.name.Equals("binding-path")) - uiField.RegisterValueChangedCallback(e => - { - OnValidatedAttributeValueChange(e, BuilderNameUtilities.bindingPathAttributeRegex, - BuilderConstants.BindingPathAttributeValidationSpacialCharacters); - }); - else - uiField.RegisterValueChangedCallback(OnAttributeValueChange); - - if (attribute.name.Equals("text") || attribute.name.Equals("label")) - { - uiField.multiline = true; - uiField.AddToClassList(BuilderConstants.InspectorMultiLineTextFieldClassName); - } - - fieldElement = uiField; - } - } - else if (attribute is UxmlFloatAttributeDescription) - { - var uiField = new FloatField(fieldLabel); - uiField.RegisterValueChangedCallback(OnAttributeValueChange); - fieldElement = uiField; - } - else if (attribute is UxmlDoubleAttributeDescription) - { - var uiField = new DoubleField(fieldLabel); - uiField.RegisterValueChangedCallback(OnAttributeValueChange); - fieldElement = uiField; - } - else if (attribute is UxmlIntAttributeDescription) - { - if (attribute.name.Equals("value") && currentVisualElement is LayerField) - { - var uiField = new LayerField("Value"); - uiField.RegisterValueChangedCallback(OnAttributeValueChange); - fieldElement = uiField; - } - else if (attribute.name.Equals("value") && currentVisualElement is LayerMaskField) - { - var uiField = new LayerMaskField("Value"); - uiField.RegisterValueChangedCallback(OnAttributeValueChange); - fieldElement = uiField; - } - else - { - var uiField = new IntegerField(fieldLabel); - uiField.RegisterValueChangedCallback(OnAttributeValueChange); - fieldElement = uiField; - } - } - else if (attribute is UxmlLongAttributeDescription) - { - var uiField = new LongField(fieldLabel); - uiField.RegisterValueChangedCallback(OnAttributeValueChange); - fieldElement = uiField; - } - else if (attribute is UxmlBoolAttributeDescription) - { - var uiField = new Toggle(fieldLabel); - uiField.RegisterValueChangedCallback(OnAttributeValueChange); - fieldElement = uiField; - } - else if (attribute is UxmlColorAttributeDescription) - { - var uiField = new ColorField(fieldLabel); - uiField.RegisterValueChangedCallback(OnAttributeValueChange); - fieldElement = uiField; - } - else if (attributeType.IsGenericType && attributeType.GetGenericTypeDefinition() == typeof(UxmlAssetAttributeDescription<>)) - { - var assetType = attributeType.GetGenericArguments()[0]; - var uiField = new ObjectField(fieldLabel) { objectType = assetType }; - uiField.RegisterValueChangedCallback(OnAssetAttributeValueChange); - fieldElement = uiField; - } - else if (attributeType.IsGenericType && - !attributeType.GetGenericArguments()[0].IsEnum && - attributeType.GetGenericArguments()[0] is Type) - { - var desiredType = attributeType.GetGenericArguments()[0]; - - var uiField = new TextField(fieldLabel) { isDelayed = true }; - - var completer = new FieldSearchCompleter(uiField); - uiField.RegisterCallback>((evt, c) => - { - // When possible, the popup should have the same width as the input field, so that the auto-complete - // characters will try to match said input field. - c.popup.anchoredControl = evt.elementTarget.Q(className: "unity-text-field__input"); - }, completer); - completer.matcherCallback += (str, info) => info.value.IndexOf(str, StringComparison.OrdinalIgnoreCase) >= 0; - completer.itemHeight = 36; - completer.dataSourceCallback += () => - { - var desiredTypeInfo = new TypeInfo(desiredType); - - return TypeCache.GetTypesDerivedFrom(desiredType) - .Where(t => !t.IsGenericType) - // Remove UIBuilder types from the list - .Where(t => t.Assembly != GetType().Assembly) - .Select(GetTypeInfo) - .Append(desiredTypeInfo); - }; - completer.getTextFromDataCallback += info => info.value; - completer.makeItem = () => s_TypeNameItemPool.Get(); - completer.destroyItem = e => - { - if (e is BuilderAttributeTypeName typeItem) - s_TypeNameItemPool.Release(typeItem); - }; - completer.bindItem = (v, i) => - { - if (v is BuilderAttributeTypeName l) - l.SetType(completer.results[i].type, completer.textField.text); - }; - - uiField.RegisterValueChangedCallback(e => OnValidatedTypeAttributeChange(e, desiredType)); - - fieldElement = uiField; - uiField.RegisterCallback>((evt, c) => - { - c.popup.RemoveFromHierarchy(); - }, completer); - uiField.userData = completer; - } - else if (attributeType.IsGenericType && attributeType.GetGenericArguments()[0].IsEnum) - { - var propInfo = attributeType.GetProperty("defaultValue"); - var defaultEnumValue = propInfo.GetValue(attribute, null) as Enum; - - if (defaultEnumValue.GetType().GetCustomAttribute() == null) - { - // Create and initialize the EnumField. - var uiField = new EnumField(fieldLabel); - uiField.Init(defaultEnumValue); - - uiField.RegisterValueChangedCallback(OnAttributeValueChange); - fieldElement = uiField; - } - else - { - // Create and initialize the EnumFlagsField. - var uiField = new EnumFlagsField(fieldLabel); - uiField.Init(defaultEnumValue); - - uiField.RegisterValueChangedCallback(OnAttributeValueChange); - fieldElement = uiField; - } - } - else - { - var uiField = new TextField(fieldLabel); - uiField.RegisterValueChangedCallback(OnAttributeValueChange); - fieldElement = uiField; - } - - // Create row. - var styleRow = new BuilderStyleRow(); - styleRow.Add(fieldElement); - - // Link the field. - fieldElement.SetProperty(BuilderConstants.InspectorLinkedStyleRowVEPropertyName, styleRow); - fieldElement.SetProperty(BuilderConstants.InspectorLinkedAttributeDescriptionVEPropertyName, attribute); - - // Ensure the row is added to the inspector hierarchy before refreshing - parent.Add(styleRow); - - // Set initial value. - RefreshAttributeField(fieldElement); - - // Setup field binding path. - fieldElement.bindingPath = attribute.name; - fieldElement.tooltip = attribute.name; - - // Context menu. - styleRow.AddManipulator(new ContextualMenuManipulator((evt) => BuildAttributeFieldContextualMenu(evt.menu, fieldElement))); - - if (fieldElement.GetFieldStatusIndicator() != null) - { - fieldElement.GetFieldStatusIndicator().populateMenuItems = - (menu) => BuildAttributeFieldContextualMenu(menu, fieldElement); - } - - m_Inspector.UpdateFieldStatus(fieldElement, null); - return styleRow; - } - - string GetRemapAttributeNameToCSProperty(string attributeName) - { - if (currentVisualElement is ObjectField && attributeName == "type") - return "objectType"; - else if (attributeName == "readonly") - return "isReadOnly"; - - var camel = BuilderNameUtilities.ConvertDashToCamel(attributeName); - return camel; - } - - object GetCustomValueAbstract(string attributeName) - { - if (currentVisualElement is ScrollView scrollView) - { - if (attributeName == "show-horizontal-scroller") - { - return scrollView.horizontalScrollerVisibility != ScrollerVisibility.Hidden; - } - else if (attributeName == "show-vertical-scroller") - { - return scrollView.verticalScrollerVisibility != ScrollerVisibility.Hidden; - } - else if (attributeName == "touch-scroll-type") - { - return scrollView.touchScrollBehavior; - } - } - else if (currentVisualElement is ListView listView) - { - if (attributeName == "horizontal-scrolling") - return listView.horizontalScrollingEnabled; - } - - return null; - } - - void RefreshAttributeField(BindableElement fieldElement) - { - var styleRow = - fieldElement.GetProperty(BuilderConstants.InspectorLinkedStyleRowVEPropertyName) as VisualElement; - var attribute = - fieldElement.GetProperty(BuilderConstants.InspectorLinkedAttributeDescriptionVEPropertyName) as - UxmlAttributeDescription; - - var veType = currentVisualElement.GetType(); - var csPropertyName = GetRemapAttributeNameToCSProperty(attribute.name); - var fieldInfo = veType.GetProperty(csPropertyName, - BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase); - - object veValueAbstract = null; - if (fieldInfo == null) - { - veValueAbstract = GetCustomValueAbstract(attribute.name); - } - else - { - veValueAbstract = fieldInfo.GetValue(currentVisualElement, null); - } - - var attributeType = attribute.GetType(); - var vea = currentVisualElement.GetVisualElementAsset(); - var isAssetAttribute = attributeType.IsGenericType && attributeType.GetGenericTypeDefinition() == typeof(UxmlAssetAttributeDescription<>); - - if (veValueAbstract == null) - { - if (currentVisualElement is EnumField defaultEnumField && - attribute.name == "value") - { - if (defaultEnumField.type == null) - { - fieldElement.SetEnabled(false); - } - else - { - ((EnumField)fieldElement).PopulateDataFromType(defaultEnumField.type); - fieldElement.SetEnabled(true); - } - } - else if (currentVisualElement is EnumFlagsField defaultEnumFlagsField && - attribute.name == "value") - { - if (defaultEnumFlagsField.type == null) - { - fieldElement.SetEnabled(false); - } - else - { - ((EnumFlagsField)fieldElement).PopulateDataFromType(defaultEnumFlagsField.type); - fieldElement.SetEnabled(true); - } - } - else if (isAssetAttribute && fieldElement is ObjectField objectField) - { - if (vea != null && attribute.TryGetValueFromBagAsString(vea, CreationContext.Default, out var value)) - { - // Asset wasn't loaded correctly, most likely due to an invalid path. Show the missing reference. - var asset = m_Inspector.visualTreeAsset.GetAsset(value, objectField.objectType); - objectField.SetValueWithoutNotify(asset); - - styleRow.EnableInClassList(BuilderConstants.InspectorLocalStyleOverrideClassName, true); - m_Inspector.UpdateFieldStatus(fieldElement, null); - } - } - - return; - } - - if (attribute is UxmlStringAttributeDescription && - attribute.name == "value" && - currentVisualElement is EnumField enumField - && fieldElement is EnumField inputEnumField) - { - var hasValue = enumField.value != null; - if (hasValue) - inputEnumField.Init(enumField.value, enumField.includeObsoleteValues); - else - inputEnumField.SetValueWithoutNotify(null); - inputEnumField.SetEnabled(hasValue); - } - else if (attribute is UxmlStringAttributeDescription && - attribute.name == "value" && - currentVisualElement is TagField tagField - && fieldElement is TagField inputTagField) - { - inputTagField.SetValueWithoutNotify(tagField.value); - } - else if (attribute is UxmlIntAttributeDescription && - attribute.name == "value" && - currentVisualElement is LayerField layerField - && fieldElement is LayerField inputLayerField) - { - inputLayerField.SetValueWithoutNotify(layerField.value); - } - else if (attribute is UxmlIntAttributeDescription && - attribute.name == "value" && - currentVisualElement is LayerMaskField layerMaskField - && fieldElement is LayerMaskField inputLayerMaskField) - { - inputLayerMaskField.SetValueWithoutNotify(layerMaskField.value); - } - else if (attribute is UxmlStringAttributeDescription && - attribute.name == "value" && - currentVisualElement is EnumFlagsField enumFlagsField - && fieldElement is EnumFlagsField inputEnumFlagsField) - { - var hasValue = enumFlagsField.value != null; - if (hasValue) - inputEnumFlagsField.Init(enumFlagsField.value, enumFlagsField.includeObsoleteValues); - else - inputEnumFlagsField.SetValueWithoutNotify(null); - inputEnumFlagsField.SetEnabled(hasValue); - } - else if (attribute is UxmlStringAttributeDescription && fieldElement is TextField textField) - { - textField.SetValueWithoutNotify(GetAttributeStringValue(veValueAbstract)); - } - else if (attribute is UxmlFloatAttributeDescription && fieldElement is FloatField floatField) - { - floatField.SetValueWithoutNotify((float) veValueAbstract); - } - else if (attribute is UxmlDoubleAttributeDescription && fieldElement is DoubleField doubleField) - { - doubleField.SetValueWithoutNotify((double) veValueAbstract); - } - else if (attribute is UxmlIntAttributeDescription && fieldElement is IntegerField integerField) - { - if (veValueAbstract is int) - integerField.SetValueWithoutNotify((int) veValueAbstract); - else if (veValueAbstract is float) - integerField.SetValueWithoutNotify(Convert.ToInt32(veValueAbstract)); - } - else if (attribute is UxmlLongAttributeDescription && fieldElement is LongField longField) - { - longField.SetValueWithoutNotify((long) veValueAbstract); - } - else if (attribute is UxmlBoolAttributeDescription && fieldElement is Toggle toggle) - { - toggle.SetValueWithoutNotify((bool) veValueAbstract); - } - else if (attribute is UxmlColorAttributeDescription && fieldElement is ColorField colorField) - { - colorField.SetValueWithoutNotify((Color) veValueAbstract); - } - else if (isAssetAttribute && fieldElement is ObjectField objectField) - { - objectField.SetValueWithoutNotify((Object)veValueAbstract); - } - else if (attributeType.IsGenericType && - !attributeType.GetGenericArguments()[0].IsEnum && - attributeType.GetGenericArguments()[0] is Type && - fieldElement is TextField typeTextField && - veValueAbstract is Type veTypeValue) - { - var fullTypeName = veTypeValue.AssemblyQualifiedName; - var fullTypeNameSplit = fullTypeName.Split(','); - typeTextField.SetValueWithoutNotify($"{fullTypeNameSplit[0]},{fullTypeNameSplit[1]}"); - } - else if (attributeType.IsGenericType && - attributeType.GetGenericArguments()[0].IsEnum && - fieldElement is BaseField baseEnumField) - { - var propInfo = attributeType.GetProperty("defaultValue"); - var defaultEnumValue = propInfo.GetValue(attribute, null) as Enum; - - string enumAttributeValueStr; - if (m_Selection.selectionType == BuilderSelectionType.ElementInTemplateInstance) - { - // Special case for template children and usageHints that is not set in the visual element when set in the builder - var parentTemplate = BuilderAssetUtilities.GetVisualElementRootTemplate(currentVisualElement); - var parentTemplateAsset = parentTemplate.GetVisualElementAsset() as TemplateAsset; - var fieldAttributeOverride = parentTemplateAsset.attributeOverrides.FirstOrDefault(x => - x.m_AttributeName == attribute.name && x.m_ElementName == currentVisualElement.name); - - if (fieldAttributeOverride.m_ElementName == currentVisualElement.name) - { - enumAttributeValueStr = fieldAttributeOverride.m_Value; - } - else - { - enumAttributeValueStr = veValueAbstract.ToString(); - } - } - else - { - enumAttributeValueStr = vea?.GetAttributeValue(attribute.name); - } - - // Set the value from the UXML attribute. - if (!string.IsNullOrEmpty(enumAttributeValueStr)) - { - try - { - var parsedValue = Enum.Parse(defaultEnumValue.GetType(), enumAttributeValueStr, true) as Enum; - baseEnumField.SetValueWithoutNotify(parsedValue); - } - catch (ArgumentException exception) - { - Debug.LogException(exception); - // use default if anything went wrong - baseEnumField.SetValueWithoutNotify(defaultEnumValue); - } - catch (OverflowException exception) - { - Debug.LogException(exception); - // use default if anything went wrong - baseEnumField.SetValueWithoutNotify(defaultEnumValue); - } - } - } - else if (fieldElement is TextField defaultTextField) - { - defaultTextField.SetValueWithoutNotify(veValueAbstract.ToString()); - } - - styleRow.EnableInClassList(BuilderConstants.InspectorLocalStyleOverrideClassName, IsAttributeOverriden(attribute)); - m_Inspector.UpdateFieldStatus(fieldElement, null); - } - - string GetAttributeStringValue(object attributeValue) - { - string value; - if (attributeValue is Enum @enum) - value = @enum.ToString(); - else if (attributeValue is IEnumerable list) - { - value = string.Join(",", list.ToArray()); - } - else - { - value = attributeValue.ToString(); - } - - return value; - } - - bool IsAttributeOverriden(UxmlAttributeDescription attribute) - { - return IsAttributeOverriden(currentVisualElement, attribute); - } - - public static bool IsAttributeOverriden(VisualElement currentVisualElement, UxmlAttributeDescription attribute) - { - var vea = currentVisualElement.GetVisualElementAsset(); - if (vea != null && attribute.name == "picking-mode") - { - var veaAttributeValue = vea.GetAttributeValue(attribute.name); - if (veaAttributeValue != null && - veaAttributeValue.ToLower() != attribute.defaultValueAsString.ToLower()) - return true; - } - else if (attribute.name == "name") - { - if (!string.IsNullOrEmpty(currentVisualElement.name)) - return true; - } - else if (vea != null && vea.HasAttribute(attribute.name)) - { - return true; - } - else if (BuilderAssetUtilities.HasAttributeOverrideInRootTemplate(currentVisualElement, attribute.name)) - { - return true; - } - - return false; - } - - void ResetAttributeFieldToDefault(BindableElement fieldElement) - { - var styleRow = - fieldElement.GetProperty(BuilderConstants.InspectorLinkedStyleRowVEPropertyName) as VisualElement; - var attribute = - fieldElement.GetProperty(BuilderConstants.InspectorLinkedAttributeDescriptionVEPropertyName) as - UxmlAttributeDescription; - - var attributeType = attribute.GetType(); - - if (attribute is UxmlStringAttributeDescription stringAttribute && fieldElement is TextField textField) - { - textField.SetValueWithoutNotify(stringAttribute.defaultValue); - } - else if (attribute is UxmlStringAttributeDescription && fieldElement is EnumField enumField && - currentVisualElement is EnumField) - { - if (null == enumField.type) - enumField.SetValueWithoutNotify(null); - else - enumField.SetValueWithoutNotify((Enum)Enum.ToObject(enumField.type, 0)); - } - else if (attribute is UxmlStringAttributeDescription && fieldElement is EnumFlagsField enumFlagsField && - currentVisualElement is EnumFlagsField) - { - if (null == enumFlagsField.type) - enumFlagsField.SetValueWithoutNotify(null); - else - enumFlagsField.SetValueWithoutNotify((Enum)Enum.ToObject(enumFlagsField.type, 0)); - } - else if (attribute is UxmlFloatAttributeDescription floatAttribute && fieldElement is FloatField floatField) - { - floatField.SetValueWithoutNotify(floatAttribute.defaultValue); - } - else if (attribute is UxmlDoubleAttributeDescription doubleAttribute && fieldElement is DoubleField doubleField) - { - doubleField.SetValueWithoutNotify(doubleAttribute.defaultValue); - } - else if (attribute is UxmlIntAttributeDescription intAttribute && fieldElement is IntegerField intField) - { - intField.SetValueWithoutNotify(intAttribute.defaultValue); - } - else if (attribute is UxmlLongAttributeDescription longAttribute && fieldElement is LongField longField) - { - longField.SetValueWithoutNotify(longAttribute.defaultValue); - } - else if (attribute is UxmlBoolAttributeDescription boolAttribute && fieldElement is Toggle toggle) - { - toggle.SetValueWithoutNotify(boolAttribute.defaultValue); - } - else if (attribute is UxmlColorAttributeDescription colorAttribute && fieldElement is ColorField colorField) - { - colorField.SetValueWithoutNotify(colorAttribute.defaultValue); - } - else if (attributeType.IsGenericType && attributeType.GetGenericTypeDefinition() == typeof(UxmlAssetAttributeDescription<>) && - fieldElement is ObjectField objectField) - { - objectField.SetValueWithoutNotify(default); - } - else if (attributeType.IsGenericType && - !attributeType.GetGenericArguments()[0].IsEnum && - attributeType.GetGenericArguments()[0] is Type && - fieldElement is TextField typeTextField) - { - var a = attribute as TypedUxmlAttributeDescription; - if (a.defaultValue == null) - typeTextField.SetValueWithoutNotify(string.Empty); - else - typeTextField.SetValueWithoutNotify(a.defaultValue.ToString()); - } - else if (attributeType.IsGenericType && - attributeType.GetGenericArguments()[0].IsEnum && - fieldElement is BaseField baseEnumField) - { - var propInfo = attributeType.GetProperty("defaultValue"); - var defaultEnumValue = propInfo.GetValue(attribute, null) as Enum; - - baseEnumField.SetValueWithoutNotify(defaultEnumValue); - } - else if (fieldElement is TextField defaultTextField) - { - defaultTextField.SetValueWithoutNotify(string.Empty); - } - - // Clear override. - styleRow.RemoveFromClassList(BuilderConstants.InspectorLocalStyleOverrideClassName); - var styleFields = styleRow.Query().ToList(); - foreach (var styleField in styleFields) - { - styleField.RemoveFromClassList(BuilderConstants.InspectorLocalStyleResetClassName); - styleField.RemoveFromClassList(BuilderConstants.InspectorLocalStyleOverrideClassName); - } - } - - void BuildAttributeFieldContextualMenu(ContextualMenuPopulateEvent evt) - { - BuildAttributeFieldContextualMenu(evt.menu, evt.elementTarget); - } - - void BuildAttributeFieldContextualMenu(DropdownMenu menu, VisualElement fieldElement) - { - // if the context menu is already populated by the field (text field) then ignore - if (menu.MenuItems() != null & menu.MenuItems().Count > 0) - return; - - menu.AppendAction( - BuilderConstants.ContextMenuUnsetMessage, - UnsetAttributeProperty, - action => - { - var fieldElement = action.userData as BindableElement; - if (fieldElement == null) - return DropdownMenuAction.Status.Disabled; - - var attributeName = fieldElement.bindingPath; - var vea = currentVisualElement.GetVisualElementAsset(); - var isAttributeOverrideAttribute = - m_Selection.selectionType == BuilderSelectionType.ElementInTemplateInstance - && BuilderAssetUtilities.HasAttributeOverrideInRootTemplate(currentVisualElement, - attributeName); - - return (vea != null && vea.HasAttribute(attributeName)) || isAttributeOverrideAttribute - ? DropdownMenuAction.Status.Normal - : DropdownMenuAction.Status.Disabled; - }, - fieldElement); - - menu.AppendAction( - BuilderConstants.ContextMenuUnsetAllMessage, - (action) => UnsetAllAttributes(), - action => - { - var attributeList = currentVisualElement.GetAttributeDescriptions(); - foreach (var attribute in attributeList) - { - if (attribute?.name == null) - continue; - - if (m_Selection.selectionType == BuilderSelectionType.ElementInTemplateInstance - && attribute.name == "name") - { - continue; - } - - if (IsAttributeOverriden(attribute)) - return DropdownMenuAction.Status.Normal; - } - - return DropdownMenuAction.Status.Disabled; - }, - fieldElement); - } - - internal void UnsetAllAttributes() - { - // Undo/Redo - Undo.RegisterCompleteObjectUndo(m_Inspector.visualTreeAsset, - BuilderConstants.ChangeAttributeValueUndoMessage); - - if (m_Selection.selectionType == BuilderSelectionType.ElementInTemplateInstance) - { - var parentTemplate = BuilderAssetUtilities.GetVisualElementRootTemplate(currentVisualElement); - var parentTemplateAsset = parentTemplate.GetVisualElementAsset() as TemplateAsset; - var attributeOverrides = - new List(parentTemplateAsset.attributeOverrides); - - foreach (var attributeOverride in attributeOverrides) - { - if (attributeOverride.m_ElementName == currentVisualElement.name) - { - parentTemplateAsset.RemoveAttributeOverride(attributeOverride.m_ElementName, - attributeOverride.m_AttributeName); - } - } - - var builder = Builder.ActiveWindow; - var hierarchyView = builder.hierarchy.elementHierarchyView; - var selectionId = hierarchyView.GetSelectedItemId(); - - builder.OnEnableAfterAllSerialization(); - - hierarchyView.SelectItemById(selectionId); - } - else - { - var attributeList = currentVisualElement.GetAttributeDescriptions(); - var vea = currentVisualElement.GetVisualElementAsset(); - - foreach (var attribute in attributeList) - { - if (attribute?.name == null) - continue; - - // Unset value in asset. - vea.RemoveAttribute(attribute.name); - } - - var fields = m_AttributesSection.Query() - .Where(e => !string.IsNullOrEmpty(e.bindingPath)).ToList(); - foreach (var fieldElement in fields) - { - // Reset UI value. - ResetAttributeFieldToDefault(fieldElement); - m_Inspector.UpdateFieldStatus(fieldElement, null); - } - - // Call Init(); - m_Inspector.CallInitOnElement(); - - // Notify of changes. - m_Selection.NotifyOfHierarchyChange(m_Inspector); - } - } - - void UnsetAttributeProperty(DropdownMenuAction action) - { - var fieldElement = action.userData as BindableElement; - UnsetAttributeProperty(fieldElement); - - // When unsetting the type value for an enum field, we also need to clear the value field as well. - if (currentVisualElement is EnumField && fieldElement.bindingPath == "type") - { - // If the current value is not defined in the new enum type, we need to clear the property because - // it will otherwise throw an exception. - var valueField = root.Query().Where(f => f.label == "Value").First(); - UnsetAttributeProperty(valueField); - } - if (currentVisualElement is EnumFlagsField && fieldElement.bindingPath == "type") - { - // If the current value is not defined in the new enum type, we need to clear the property because - // it will otherwise throw an exception. - var valueField = root.Query().Where(f => f.label == "Value").First(); - UnsetAttributeProperty(valueField); - } - } - - public void UnsetAttributeProperty(BindableElement fieldElement) - { - var attributeName = fieldElement.bindingPath; - // Undo/Redo - Undo.RegisterCompleteObjectUndo(m_Inspector.visualTreeAsset, - BuilderConstants.ChangeAttributeValueUndoMessage); - - // Unset value in asset. - var vea = currentVisualElement.GetVisualElementAsset(); - - if (m_Selection.selectionType == BuilderSelectionType.ElementInTemplateInstance) - { - var templateContainer = BuilderAssetUtilities.GetVisualElementRootTemplate(currentVisualElement); - var templateAsset = templateContainer.GetVisualElementAsset() as TemplateAsset; - - if (templateAsset != null) - { - var builder = Builder.ActiveWindow; - var hierarchyView = builder.hierarchy.elementHierarchyView; - var selectionId = hierarchyView.GetSelectedItemId(); - - templateAsset.RemoveAttributeOverride(currentVisualElement.name, attributeName); - - builder.OnEnableAfterAllSerialization(); - - hierarchyView.SelectItemById(selectionId); - } - } - else - { - vea.RemoveAttribute(attributeName); - - // Reset UI value. - ResetAttributeFieldToDefault(fieldElement); - - // Call Init(); - m_Inspector.CallInitOnElement(); - - // Notify of changes. - m_Selection.NotifyOfHierarchyChange(m_Inspector); - - m_Inspector.UpdateFieldStatus(fieldElement, null); - Refresh(); - } - } - - void OnAttributeValueChange(ChangeEvent evt) - { - var field = evt.target as TextField; - PostAttributeValueChange(field, evt.newValue); - } - - void OnValidatedTypeAttributeChange(ChangeEvent evt, Type desiredType) - { - var field = evt.target as TextField; - var typeName = evt.newValue; - var fullTypeName = typeName; - - Type type = null; - if (!string.IsNullOrEmpty(typeName)) - { - type = Type.GetType(fullTypeName, false); - - // Try some auto-fixes. - if (type == null) - { - fullTypeName = typeName + ", UnityEngine.CoreModule"; - type = Type.GetType(fullTypeName, false); - } - - if (type == null) - { - fullTypeName = typeName + ", UnityEditor"; - type = Type.GetType(fullTypeName, false); - } - - if (type == null && typeName.Contains(".")) - { - var split = typeName.Split('.'); - fullTypeName = typeName + $", {split[0]}.{split[1]}Module"; - type = Type.GetType(fullTypeName, false); - } - - if (type == null) - { - Builder.ShowWarning(string.Format(BuilderConstants.TypeAttributeInvalidTypeMessage, field.label)); - evt.StopPropagation(); - return; - } - else if (!desiredType.IsAssignableFrom(type)) - { - Builder.ShowWarning(string.Format(BuilderConstants.TypeAttributeMustDeriveFromMessage, field.label, - desiredType.FullName)); - evt.StopPropagation(); - return; - } - } - - if (currentVisualElement is EnumField) - { - // If the current value is not defined in the new enum type, we need to clear the property because - // it will otherwise throw an exception. - var valueField = root.Query().Where(f => f.label == "Value").First(); - UnsetAttributeProperty(valueField); - } - else if (currentVisualElement is EnumFlagsField) - { - // If the current value is not defined in the new enum type, we need to clear the property because - // it will otherwise throw an exception. - var valueField = root.Query().Where(f => f.label == "Value").First(); - UnsetAttributeProperty(valueField); - } - - field.value = fullTypeName; - PostAttributeValueChange(field, fullTypeName); - - Refresh(); - } - - internal void OnValidatedAttributeValueChange(ChangeEvent evt, Regex regex, string message) - { - var field = evt.target as TextField; - if (!string.IsNullOrEmpty(evt.newValue) && !regex.IsMatch(evt.newValue)) - { - Builder.ShowWarning(string.Format(message, field.label)); - field.SetValueWithoutNotify(evt.previousValue); - evt.StopPropagation(); - return; - } - - OnAttributeValueChange(evt); - } - - void OnAttributeValueChange(ChangeEvent evt) - { - var field = evt.target as FloatField; - PostAttributeValueChange(field, evt.newValue.ToString(CultureInfo.InvariantCulture.NumberFormat)); - } - - void OnAttributeValueChange(ChangeEvent evt) - { - var field = evt.target as DoubleField; - PostAttributeValueChange(field, evt.newValue.ToString(CultureInfo.InvariantCulture.NumberFormat)); - } - - void OnAttributeValueChange(ChangeEvent evt) - { - var field = evt.target as BaseField; - PostAttributeValueChange(field, evt.newValue.ToString()); - } - - void OnAttributeValueChange(ChangeEvent evt) - { - var field = evt.target as LongField; - PostAttributeValueChange(field, evt.newValue.ToString()); - } - - void OnAttributeValueChange(ChangeEvent evt) - { - var field = evt.target as Toggle; - PostAttributeValueChange(field, evt.newValue.ToString().ToLower()); - } - - void OnAttributeValueChange(ChangeEvent evt) - { - var field = evt.target as ColorField; - PostAttributeValueChange(field, "#" + ColorUtility.ToHtmlStringRGBA(evt.newValue)); - } - - void OnAssetAttributeValueChange(ChangeEvent evt) - { - var field = evt.target as ObjectField; - - var assetPath = AssetDatabase.GetAssetPath(evt.newValue); - if (BuilderAssetUtilities.IsBuiltinPath(assetPath)) - { - Builder.ShowWarning(BuilderConstants.BuiltInAssetPathsNotSupportedMessageUxml); - - // Revert the change. - field.SetValueWithoutNotify(evt.previousValue); - return; - } - - var vta = m_Inspector.visualTreeAsset; - var uri = URIHelpers.MakeAssetUri(evt.newValue); - - if (!string.IsNullOrEmpty(uri) && !vta.AssetEntryExists(uri, field.objectType)) - { - vta.RegisterAssetEntry(uri, field.objectType, evt.newValue); - } - - PostAttributeValueChange(field, uri); - } - - void OnAttributeValueChange(ChangeEvent evt) - { - var field = evt.target as BaseField; - PostAttributeValueChange(field, evt.newValue.ToString()); - } - - void PostAttributeValueChange(BindableElement field, string value) - { - // Undo/Redo - Undo.RegisterCompleteObjectUndo(m_Inspector.visualTreeAsset, - BuilderConstants.ChangeAttributeValueUndoMessage); - - // Set value in asset. - var vea = currentVisualElement.GetVisualElementAsset(); - var vta = currentVisualElement.GetVisualTreeAsset(); - - if (m_Selection.selectionType == BuilderSelectionType.ElementInTemplateInstance) - { - TemplateContainer templateContainerParent = - BuilderAssetUtilities.GetVisualElementRootTemplate(currentVisualElement); - - if (templateContainerParent != null) - { - var templateAsset = templateContainerParent.GetVisualElementAsset() as TemplateAsset; - var currentVisualElementName = currentVisualElement.name; - - if (!string.IsNullOrEmpty(currentVisualElementName)) - { - templateAsset.SetAttributeOverride(currentVisualElementName, field.bindingPath, value); - - var document = Builder.ActiveWindow.document; - var rootElement = Builder.ActiveWindow.viewport.documentRootElement; - - var elementsToChange = templateContainerParent.Query(currentVisualElementName); - elementsToChange.ForEach(x => - { - var templateVea = - x.GetProperty(VisualTreeAsset.LinkedVEAInTemplatePropertyName) as VisualElementAsset; - var attributeOverrides = - BuilderAssetUtilities.GetAccumulatedAttributeOverrides(currentVisualElement); - m_Inspector.CallInitOnTemplateChild(x, templateVea, attributeOverrides); - }); - } - } - } - else - { - vea.SetAttribute(field.bindingPath, value); - - // Call Init(); - m_Inspector.CallInitOnElement(); - } - - // Mark field as overridden. - var styleRow = field.GetProperty(BuilderConstants.InspectorLinkedStyleRowVEPropertyName) as BuilderStyleRow; - - if (styleRow != null) - { - styleRow.AddToClassList(BuilderConstants.InspectorLocalStyleOverrideClassName); - - var styleFields = styleRow.Query().ToList(); - - foreach (var styleField in styleFields) - { - styleField.RemoveFromClassList(BuilderConstants.InspectorLocalStyleResetClassName); - if (field.bindingPath == styleField.bindingPath) - { - styleField.AddToClassList(BuilderConstants.InspectorLocalStyleOverrideClassName); - } - else if (!string.IsNullOrEmpty(styleField.bindingPath) && - field.bindingPath != styleField.bindingPath && - !styleField.ClassListContains(BuilderConstants.InspectorLocalStyleOverrideClassName)) - { - styleField.AddToClassList(BuilderConstants.InspectorLocalStyleResetClassName); - } - } - } - - // Notify of changes. - m_Selection.NotifyOfHierarchyChange(m_Inspector); - - if (styleRow != null) - { - m_Inspector.UpdateFieldStatus(field, null); - } - } - } -} diff --git a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Viewport/BuilderInPlaceTextEditingUtilities.cs b/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Viewport/BuilderInPlaceTextEditingUtilities.cs deleted file mode 100644 index 475c009842..0000000000 --- a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Builder/Viewport/BuilderInPlaceTextEditingUtilities.cs +++ /dev/null @@ -1,258 +0,0 @@ -using System; -using UnityEditor; -using UnityEngine; -using UnityEngine.UIElements; -using Object = UnityEngine.Object; - -namespace Unity.UI.Builder -{ - static class BuilderInPlaceTextEditingUtilities - { - static readonly string s_DummyText = " "; - static readonly string s_TextAttributeName = "text"; - - static BuilderViewport s_Viewport; - - static UxmlAttributeDescription s_EditedTextAttribute; - static VisualElement s_EditedElement; - static TextElement s_EditedTextElement; - - internal static void GetAlignmentFromTextAnchor(TextAnchor anchor, out Align align, out Justify justifyContent) - { - align = Align.FlexStart; - justifyContent = Justify.FlexStart; - - switch (anchor) - { - case TextAnchor.UpperCenter: - case TextAnchor.MiddleCenter: - case TextAnchor.LowerCenter: - align = Align.Center; - break; - case TextAnchor.UpperRight: - case TextAnchor.MiddleRight: - case TextAnchor.LowerRight: - align = Align.FlexEnd; - break; - } - - switch (anchor) - { - case TextAnchor.MiddleLeft: - case TextAnchor.MiddleCenter: - case TextAnchor.MiddleRight: - justifyContent = Justify.Center; - break; - case TextAnchor.LowerLeft: - case TextAnchor.LowerCenter: - case TextAnchor.LowerRight: - justifyContent = Justify.FlexEnd; - break; - } - } - - static void GetAttributeToEdit(VisualElement editedElement, Vector2 pos, out TextElement textElement, out string attributeName) - { - TextElement labelElement = null; - - textElement = null; - attributeName = null; - - switch (editedElement) - { - case BaseField boolField: labelElement = boolField.labelElement; break; - case BaseField inField: labelElement = inField.labelElement; break; - case BaseField floatField: labelElement = floatField.labelElement; break; - case BaseField longField: labelElement = longField.labelElement; break; - case BaseField strField: labelElement = strField.labelElement; break; - case BaseField vec2Field: labelElement = vec2Field.labelElement; break; - case BaseField vec3Field: labelElement = vec3Field.labelElement; break; - case BaseField vec4Field: labelElement = vec4Field.labelElement; break; - case BaseField rectField: labelElement = rectField.labelElement; break; - case BaseField colorField: labelElement = colorField.labelElement; break; - case BaseField gradField: labelElement = gradField.labelElement; break; - case BaseField objField: labelElement = objField.labelElement; break; - case BaseField boundsField: labelElement = boundsField.labelElement; break; - case BaseField animCurveField: labelElement = animCurveField.labelElement; break; - case BaseField enumField: labelElement = enumField.labelElement; break; - } - - if (labelElement != null) - { - textElement = labelElement; - attributeName = "label"; - } - else if (editedElement is TextElement editorTextElement) - { - textElement = editorTextElement; - attributeName = s_TextAttributeName; - } - } - - public static void OpenEditor(VisualElement element, Vector2 pos) - { - BuilderViewport viewport = element.GetFirstAncestorOfType(); - var editorLayer = viewport.editorLayer; - var textEditor = viewport.textEditor; - - GetAttributeToEdit(element, pos, out var textElement, out var attributeName); - - if (textElement == null || string.IsNullOrEmpty(attributeName)) - return; - - var attributeList = element.GetAttributeDescriptions(); - - foreach (var attribute in attributeList) - { - if (attribute?.name != null && attribute.name.Equals(attributeName)) - { - s_EditedTextAttribute = attribute; - break; - } - } - - if (s_EditedTextAttribute == null) - return; - - s_Viewport = viewport; - s_EditedElement = element; - s_EditedTextElement = textElement; - - var value = s_EditedElement.GetValueByReflection(s_EditedTextAttribute.name) as string; - - // To ensure the text element is visible - if (string.IsNullOrEmpty(value)) - { - s_EditedElement.SetValueByReflection(s_EditedTextAttribute.name, s_DummyText); - } - - editorLayer.RemoveFromClassList(BuilderConstants.HiddenStyleClassName); - - var textInput = textEditor.Q(TextField.textInputUssName); - textEditor.value = value; - textInput.style.unityTextAlign = textElement.resolvedStyle.unityTextAlign; - textInput.style.fontSize = textElement.resolvedStyle.fontSize; - textInput.style.unityFontStyleAndWeight = textElement.resolvedStyle.unityFontStyleAndWeight; - textInput.style.whiteSpace = s_EditedTextElement.resolvedStyle.whiteSpace; - textInput.style.width = s_EditedTextElement.resolvedStyle.width; - textInput.style.height = s_EditedTextElement.resolvedStyle.height; - - textEditor.multiline = true; - - GetAlignmentFromTextAnchor(textElement.resolvedStyle.unityTextAlign, out var alignItems, out var justifyContent); - - var textEditorContainer = textEditor.parent; - textEditorContainer.style.paddingLeft = s_EditedTextElement.resolvedStyle.paddingLeft; - textEditorContainer.style.paddingTop = s_EditedTextElement.resolvedStyle.paddingTop; - textEditorContainer.style.paddingRight = s_EditedTextElement.resolvedStyle.paddingRight; - textEditorContainer.style.paddingBottom = s_EditedTextElement.resolvedStyle.paddingBottom; - textEditorContainer.style.alignItems = alignItems; - textEditorContainer.style.justifyContent = justifyContent; - UpdateTextEditorGeometry(); - textElement.RegisterCallback(OnTextElementGeometryChanged); - - textEditor.schedule.Execute(a => textEditor.Focus()); - textEditor.textSelection.SelectAll(); - - textInput.RegisterCallback(OnFocusOutEvent, TrickleDown.TrickleDown); - textEditor.RegisterCallback>(OnTextChanged); - } - - static void OnTextElementGeometryChanged(GeometryChangedEvent evt) - { - UpdateTextEditorGeometry(); - } - - static void UpdateTextEditorGeometry() - { - // The text element may have been removed from its hierarchy if the text is empty. This is the case with Fields. - if (s_EditedTextElement.panel == null) - return; - - var textElementPos = - s_EditedTextElement.parent.ChangeCoordinatesTo(s_Viewport.documentRootElement, new Vector2(s_EditedTextElement.layout.x, s_EditedTextElement.layout.y)); - - var textEditorContainer = s_Viewport.textEditor.parent; - - // Note: Set the font here it because the font maybe still null of empty label of field in OpenEditor() - s_Viewport.textEditor.Q(TextField.textInputUssName).style.unityFont = s_EditedTextElement.resolvedStyle.unityFont; - - textEditorContainer.style.left = textElementPos.x; - textEditorContainer.style.top = textElementPos.y; - var textInput = textEditorContainer.Q(TextField.textInputUssName); - textInput.style.width = s_EditedTextElement.layout.width; - textInput.style.height = s_EditedTextElement.layout.height; - } - - public static void CloseEditor() - { - if (s_Viewport == null) - return; - - s_Viewport.textEditor.UnregisterCallback(OnFocusOutEvent, TrickleDown.TrickleDown); - s_Viewport.textEditor.UnregisterCallback>(OnTextChanged); - s_Viewport.editorLayer.AddToClassList(BuilderConstants.HiddenStyleClassName); - s_EditedTextElement.UnregisterCallback(OnTextElementGeometryChanged); - s_Viewport = null; - s_EditedElement = null; - s_EditedTextAttribute = null; - } - - static void OnTextChanged(ChangeEvent evt) - { - s_EditedElement.SetValueByReflection(s_EditedTextAttribute.name, string.IsNullOrEmpty(evt.newValue) ? s_DummyText : evt.newValue); - } - - static void OnFocusOutEvent(FocusOutEvent evt) - { - OnEditTextFinished(); - } - - static void OnEditTextFinished() - { - if (s_EditedElement == null) - return; - - // Undo/Redo - Undo.RegisterCompleteObjectUndo(s_Viewport.paneWindow.document.visualTreeAsset, BuilderConstants.ChangeAttributeValueUndoMessage); - - var newValue = s_Viewport.textEditor.value; - var type = s_EditedElement.GetType(); - var vea = s_EditedElement.GetVisualElementAsset(); - var oldValue = vea.GetAttributeValue(s_EditedTextAttribute.name); - - if (newValue != oldValue) - { - // Set value in asset. - vea.SetAttribute(s_EditedTextAttribute.name, newValue); - - var fullTypeName = type.ToString(); - - if (VisualElementFactoryRegistry.TryGetValue(fullTypeName, out var factoryList)) - { - var traits = factoryList[0].GetTraits(); - - if (traits == null) - { - CloseEditor(); - return; - } - - var context = new CreationContext(); - - try - { - traits.Init(s_EditedElement, vea, context); - } - catch - { - // HACK: This throws in 2019.3.0a4 because usageHints property throws when set after the element has already been added to the panel. - } - } - - s_Viewport.selection.NotifyOfHierarchyChange(s_Viewport); - } - CloseEditor(); - } - } -} diff --git a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Utilities/SampleDocument/BuilderAttributesTestElement.cs b/External/MirroredPackageSources/com.unity.ui.builder/Editor/Utilities/SampleDocument/BuilderAttributesTestElement.cs deleted file mode 100644 index 5421fc7ba7..0000000000 --- a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Utilities/SampleDocument/BuilderAttributesTestElement.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Collections.Generic; -using UnityEditor; -using UnityEditor.UIElements; -using UnityEngine; -using UnityEngine.UIElements; - -namespace Unity.UI.Builder -{ - internal class BuilderAttributesTestElement : VisualElement - { - public enum Existance - { - None, - Good, - Bad - } - - public new class UxmlFactory : UxmlFactory { } - - public new class UxmlTraits : VisualElement.UxmlTraits - { - UxmlStringAttributeDescription m_String = new UxmlStringAttributeDescription { name = "string-attr", defaultValue = "default_value" }; - UxmlFloatAttributeDescription m_Float = new UxmlFloatAttributeDescription { name = "float-attr", defaultValue = 0.1f }; - UxmlDoubleAttributeDescription m_Double = new UxmlDoubleAttributeDescription { name = "double-attr", defaultValue = 0.1 }; - UxmlIntAttributeDescription m_Int = new UxmlIntAttributeDescription { name = "int-attr", defaultValue = 2 }; - UxmlLongAttributeDescription m_Long = new UxmlLongAttributeDescription { name = "long-attr", defaultValue = 3 }; - UxmlBoolAttributeDescription m_Bool = new UxmlBoolAttributeDescription { name = "bool-attr", defaultValue = false }; - UxmlColorAttributeDescription m_Color = new UxmlColorAttributeDescription { name = "color-attr", defaultValue = Color.red }; - UxmlEnumAttributeDescription m_Enum = new UxmlEnumAttributeDescription { name = "enum-attr", defaultValue = Existance.Bad }; - - public override IEnumerable uxmlChildElementsDescription - { - get { yield break; } - } - - public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc) - { - base.Init(ve, bag, cc); - var ate = ve as BuilderAttributesTestElement; - - ate.Clear(); - - ate.stringAttr = m_String.GetValueFromBag(bag, cc); - ate.Add(new TextField("String") { value = ate.stringAttr }); - - ate.floatAttr = m_Float.GetValueFromBag(bag, cc); - ate.Add(new FloatField("Float") { value = ate.floatAttr }); - - ate.doubleAttr = m_Double.GetValueFromBag(bag, cc); - ate.Add(new DoubleField("Double") { value = ate.doubleAttr }); - - ate.intAttr = m_Int.GetValueFromBag(bag, cc); - ate.Add(new IntegerField("Integer") { value = ate.intAttr }); - - ate.longAttr = m_Long.GetValueFromBag(bag, cc); - ate.Add(new LongField("Long") { value = ate.longAttr }); - - ate.boolAttr = m_Bool.GetValueFromBag(bag, cc); - ate.Add(new Toggle("Toggle") { value = ate.boolAttr }); - - ate.colorAttr = m_Color.GetValueFromBag(bag, cc); - ate.Add(new ColorField("Color") { value = ate.colorAttr }); - - ate.enumAttr = m_Enum.GetValueFromBag(bag, cc); - var en = new EnumField("Enum"); - en.Init(m_Enum.defaultValue); - en.value = ate.enumAttr; - ate.Add(en); - } - } - - public string stringAttr { get; set; } - public float floatAttr { get; set; } - public double doubleAttr { get; set; } - public int intAttr { get; set; } - public long longAttr { get; set; } - public bool boolAttr { get; set; } - public Color colorAttr { get; set; } - public Existance enumAttr { get; set; } - } -} diff --git a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Utilities/Transitions/TransitionPropertyDropdownContent.cs b/External/MirroredPackageSources/com.unity.ui.builder/Editor/Utilities/Transitions/TransitionPropertyDropdownContent.cs deleted file mode 100644 index ece2d791a4..0000000000 --- a/External/MirroredPackageSources/com.unity.ui.builder/Editor/Utilities/Transitions/TransitionPropertyDropdownContent.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Unity.UI.Builder -{ - static class TransitionPropertyDropdownContent - { - public static CategoryDropdownContent Content; - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/BaselineFunction.cs b/External/Yoga/csharp/Facebook.Yoga/BaselineFunction.cs deleted file mode 100644 index 15ac81abdd..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/BaselineFunction.cs +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal delegate float BaselineFunction(YogaNode node, float width, float height); -} diff --git a/External/Yoga/csharp/Facebook.Yoga/Logger.cs b/External/Yoga/csharp/Facebook.Yoga/Logger.cs deleted file mode 100644 index 24691f74d0..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/Logger.cs +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal delegate void Logger( - YogaConfig config, - YogaNode node, - YogaLogLevel level, - string message); -} diff --git a/External/Yoga/csharp/Facebook.Yoga/MeasureFunction.cs b/External/Yoga/csharp/Facebook.Yoga/MeasureFunction.cs deleted file mode 100644 index c31fdbc6e6..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/MeasureFunction.cs +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal delegate YogaSize MeasureFunction( - YogaNode node, - float width, - YogaMeasureMode widthMode, - float height, - YogaMeasureMode heightMode); -} diff --git a/External/Yoga/csharp/Facebook.Yoga/MeasureOutput.cs b/External/Yoga/csharp/Facebook.Yoga/MeasureOutput.cs deleted file mode 100644 index 47efd12042..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/MeasureOutput.cs +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal class MeasureOutput - { - public static YogaSize Make(float width, float height) - { - return new YogaSize { width = width, height = height}; - } - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YGConfigHandle.cs b/External/Yoga/csharp/Facebook.Yoga/YGConfigHandle.cs deleted file mode 100644 index 77870be7c1..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YGConfigHandle.cs +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -using System; -using System.Runtime.InteropServices; - -namespace UnityEngine.Yoga -{ -// BEGIN_UNITY @jonathanma Remove usage of YGConfigHandle (less allocation) -//END_UNITY -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YGNodeHandle.cs b/External/Yoga/csharp/Facebook.Yoga/YGNodeHandle.cs deleted file mode 100644 index 120738a7e1..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YGNodeHandle.cs +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -using System; -using System.Runtime.InteropServices; - -namespace UnityEngine.Yoga -{ - // BEGIN_UNITY @jonathanma Remove usage of YGNodeHandle (less allocation) -// END_UNITY -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaAlign.cs b/External/Yoga/csharp/Facebook.Yoga/YogaAlign.cs deleted file mode 100644 index c242f75bad..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaAlign.cs +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal enum YogaAlign - { - Auto, - FlexStart, - Center, - FlexEnd, - Stretch, - Baseline, - SpaceBetween, - SpaceAround, - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaBaselineFunc.cs b/External/Yoga/csharp/Facebook.Yoga/YogaBaselineFunc.cs deleted file mode 100644 index 0199019d0a..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaBaselineFunc.cs +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -using System; -using System.Runtime.InteropServices; - -namespace UnityEngine.Yoga -{ - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate float YogaBaselineFunc(IntPtr unmanagedNodePtr, float width, float height); -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaConfig.cs b/External/Yoga/csharp/Facebook.Yoga/YogaConfig.cs deleted file mode 100644 index 3b422c563f..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaConfig.cs +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -using System; -using System.Runtime.InteropServices; - - -// BEGIN_UNITY @jonathanma -// The facebook implementation use the YGConfig context to keep a mapping between the native and managed object. -// However, since we don't really use YogaConfig this has been removed to avoid pinning the object. -// END_UNITY -namespace UnityEngine.Yoga -{ - internal class YogaConfig - { - internal static readonly YogaConfig Default = new YogaConfig(Native.YGConfigGetDefault()); - - private IntPtr _ygConfig; - private Logger _logger; - - private YogaConfig(IntPtr ygConfig) - { - _ygConfig = ygConfig; - if (_ygConfig == IntPtr.Zero) - { - throw new InvalidOperationException("Failed to allocate native memory"); - } - - // BEGIN_UNITY @jonathanma RemoveContext - //_ygConfig.SetContext(this); - // END_UNITY - - // BEGIN_UNITY @jonathanma DisableLogger - //if (_ygConfig == YGConfigHandle.Default) - //{ - //_managedLogger = LoggerInternal; - //Native.YGInteropSetLogger(_managedLogger); - //} - // END_UNITY - } - - public YogaConfig() - : this(Native.YGConfigNew()) - { - } - - ~YogaConfig() - { - if (this.Handle != Default.Handle) - { - Native.YGConfigFree(this.Handle); - } - } - - internal IntPtr Handle - { - get { - return _ygConfig; - } - } - - // BEGIN_UNITY @jonathanma Do not support C# logger - // END_UNITY - - public Logger Logger - { - get { - return _logger; - } - - set { - _logger = value; - } - } - - public void SetExperimentalFeatureEnabled( - YogaExperimentalFeature feature, - bool enabled) - { - Native.YGConfigSetExperimentalFeatureEnabled(_ygConfig, feature, enabled); - } - - public bool IsExperimentalFeatureEnabled(YogaExperimentalFeature feature) - { - return Native.YGConfigIsExperimentalFeatureEnabled(_ygConfig, feature); - } - - public bool UseWebDefaults - { - get - { - return Native.YGConfigGetUseWebDefaults(_ygConfig); - } - - set - { - Native.YGConfigSetUseWebDefaults(_ygConfig, value); - } - } - - public float PointScaleFactor - { - get - { - return Native.YGConfigGetPointScaleFactor(_ygConfig); - } - set - { - Native.YGConfigSetPointScaleFactor(_ygConfig, value); - } - } - - public static int GetInstanceCount() - { - return Native.YGConfigGetInstanceCount(); - } - - public static void SetDefaultLogger(Logger logger) - { - Default.Logger = logger; - } - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaConstants.cs b/External/Yoga/csharp/Facebook.Yoga/YogaConstants.cs deleted file mode 100644 index 3cce0c4c30..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaConstants.cs +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal static class YogaConstants - { - public const float Undefined = float.NaN; - - public static bool IsUndefined(float value) - { - return float.IsNaN(value); - } - - public static bool IsUndefined(YogaValue value) - { - return value.Unit == YogaUnit.Undefined; - } - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaDimension.cs b/External/Yoga/csharp/Facebook.Yoga/YogaDimension.cs deleted file mode 100644 index 3f74ee613d..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaDimension.cs +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal enum YogaDimension - { - Width, - Height, - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaDirection.cs b/External/Yoga/csharp/Facebook.Yoga/YogaDirection.cs deleted file mode 100644 index 258bbdb9be..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaDirection.cs +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal enum YogaDirection - { - Inherit, - LTR, - RTL, - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaDisplay.cs b/External/Yoga/csharp/Facebook.Yoga/YogaDisplay.cs deleted file mode 100644 index 5f5787322b..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaDisplay.cs +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal enum YogaDisplay - { - Flex, - None, - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaEdge.cs b/External/Yoga/csharp/Facebook.Yoga/YogaEdge.cs deleted file mode 100644 index 4098b06253..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaEdge.cs +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal enum YogaEdge - { - Left, - Top, - Right, - Bottom, - Start, - End, - Horizontal, - Vertical, - All, - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaExperimentalFeature.cs b/External/Yoga/csharp/Facebook.Yoga/YogaExperimentalFeature.cs deleted file mode 100644 index 45faffcfa4..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaExperimentalFeature.cs +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal enum YogaExperimentalFeature - { - WebFlexBasis, - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaFlexDirection.cs b/External/Yoga/csharp/Facebook.Yoga/YogaFlexDirection.cs deleted file mode 100644 index 5a52d849b4..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaFlexDirection.cs +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal enum YogaFlexDirection - { - Column, - ColumnReverse, - Row, - RowReverse, - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaJustify.cs b/External/Yoga/csharp/Facebook.Yoga/YogaJustify.cs deleted file mode 100644 index bd47854655..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaJustify.cs +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal enum YogaJustify - { - FlexStart, - Center, - FlexEnd, - SpaceBetween, - SpaceAround, - SpaceEvenly - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaLogLevel.cs b/External/Yoga/csharp/Facebook.Yoga/YogaLogLevel.cs deleted file mode 100644 index c66dc9a66f..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaLogLevel.cs +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal enum YogaLogLevel - { - Error, - Warn, - Info, - Debug, - Verbose, - Fatal, - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaLogger.cs b/External/Yoga/csharp/Facebook.Yoga/YogaLogger.cs deleted file mode 100644 index d2047d327e..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaLogger.cs +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -using System; -using System.Runtime.InteropServices; - -namespace UnityEngine.Yoga -{ - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate void YogaLogger( - IntPtr unmanagedConfigPtr, - IntPtr unmanagedNotePtr, - YogaLogLevel level, - string message); -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaMeasureFunc.cs b/External/Yoga/csharp/Facebook.Yoga/YogaMeasureFunc.cs deleted file mode 100644 index 1f8ca502e7..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaMeasureFunc.cs +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -using System; -using System.Runtime.InteropServices; - -namespace UnityEngine.Yoga -{ - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate YogaSize YogaMeasureFunc( - IntPtr unmanagedNodePtr, - float width, - YogaMeasureMode widthMode, - float height, - YogaMeasureMode heightMode); -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaMeasureMode.cs b/External/Yoga/csharp/Facebook.Yoga/YogaMeasureMode.cs deleted file mode 100644 index 6fc6677776..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaMeasureMode.cs +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal enum YogaMeasureMode - { - Undefined, - Exactly, - AtMost, - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaNative.bindings.cs b/External/Yoga/csharp/Facebook.Yoga/YogaNative.bindings.cs deleted file mode 100644 index ae4e01cd99..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaNative.bindings.cs +++ /dev/null @@ -1,411 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -// BEGIN_UNITY @jonathanma This file is heavily modified to use Unity bindings system - -using System; -using UnityEngine.Scripting; -using UnityEngine.Bindings; - -namespace UnityEngine.Yoga -{ - using YogaValueType = YogaValue; - - [NativeHeader("ModuleOverrides/com.unity.ui/Core/Native/YogaNative.bindings.h")] - internal static partial class Native - { - // We don't support setting custom C# logger (only use a default one in native code). - //[FreeFunction] - //public static extern void YGInteropSetLogger(YogaLogger logger); - - [FreeFunction] - static extern IntPtr YGNodeNew(); - - [FreeFunction] - public static extern IntPtr YGNodeNewWithConfig(IntPtr config); - - public static void YGNodeFree(IntPtr ygNode) - { - if (ygNode == IntPtr.Zero) - return; - - YGNodeFreeInternal(ygNode); - } - - [FreeFunction(Name = "YGNodeFree", IsThreadSafe = true)] - private static extern void YGNodeFreeInternal(IntPtr ygNode); - - [FreeFunction] - public static extern void YGNodeReset(IntPtr node); - - // BEGIN_UNITY @jonathanma Added this function to store a reference in native code to the managed object - // This is used by callback functions. - [FreeFunction] - public static extern void YGSetManagedObject(IntPtr ygNode, YogaNode node); - // END_UNITY - - [FreeFunction] - public static extern void YGNodeSetConfig(IntPtr ygNode, IntPtr config); - - [FreeFunction(IsThreadSafe = true)] - public static extern IntPtr YGConfigGetDefault(); - - [FreeFunction] - public static extern IntPtr YGConfigNew(); - - public static void YGConfigFree(IntPtr config) - { - if (config == IntPtr.Zero) - return; - - YGConfigFreeInternal(config); - } - - [FreeFunction(Name = "YGConfigFree", IsThreadSafe = true)] - static extern void YGConfigFreeInternal(IntPtr config); - - [FreeFunction] - public static extern int YGNodeGetInstanceCount(); - - [FreeFunction] - public static extern int YGConfigGetInstanceCount(); - - [FreeFunction] - public static extern void YGConfigSetExperimentalFeatureEnabled( - IntPtr config, - YogaExperimentalFeature feature, - bool enabled); - - [FreeFunction] - public static extern bool YGConfigIsExperimentalFeatureEnabled( - IntPtr config, - YogaExperimentalFeature feature); - - [FreeFunction] - public static extern void YGConfigSetUseWebDefaults( - IntPtr config, - bool useWebDefaults); - - [FreeFunction] - public static extern bool YGConfigGetUseWebDefaults(IntPtr config); - - [FreeFunction] - public static extern void YGConfigSetPointScaleFactor( - IntPtr config, - float pixelsInPoint); - - [FreeFunction] - public static extern float YGConfigGetPointScaleFactor( - IntPtr config); - - [FreeFunction] - public static extern void YGNodeInsertChild( - IntPtr node, - IntPtr child, - uint index); - - [FreeFunction] - public static extern void YGNodeRemoveChild(IntPtr node, IntPtr child); - - [FreeFunction] - public static extern void YGNodeCalculateLayout( - IntPtr node, - float availableWidth, - float availableHeight, - YogaDirection parentDirection); - - [FreeFunction] - public static extern void YGNodeMarkDirty(IntPtr node); - - [FreeFunction] - public static extern bool YGNodeIsDirty(IntPtr node); - - [FreeFunction] - public static extern void YGNodePrint(IntPtr node, YogaPrintOptions options); - - [FreeFunction] - public static extern void YGNodeCopyStyle(IntPtr dstNode, IntPtr srcNode); - -#region YG_NODE_PROPERTY - - [FreeFunction(Name = "YogaCallback::SetMeasureFunc")] - public static extern void YGNodeSetMeasureFunc(IntPtr node); - - [FreeFunction(Name = "YogaCallback::RemoveMeasureFunc")] - public static extern void YGNodeRemoveMeasureFunc(IntPtr node); - - [RequiredByNativeCode] - unsafe public static void YGNodeMeasureInvoke(YogaNode node, float width, YogaMeasureMode widthMode, float height, YogaMeasureMode heightMode, IntPtr returnValueAddress) - { - (*(YogaSize*)returnValueAddress) = YogaNode.MeasureInternal(node, width, widthMode, height, heightMode); - } - - [FreeFunction(Name = "YogaCallback::SetBaselineFunc")] - public static extern void YGNodeSetBaselineFunc(IntPtr node); - - [FreeFunction(Name = "YogaCallback::RemoveBaselineFunc")] - public static extern void YGNodeRemoveBaselineFunc(IntPtr node); - - [RequiredByNativeCode] - unsafe public static void YGNodeBaselineInvoke(YogaNode node, float width, float height, IntPtr returnValueAddress) - { - (*(float*)returnValueAddress) = YogaNode.BaselineInternal(node, width, height); - } - - [FreeFunction] - public static extern void YGNodeSetHasNewLayout(IntPtr node, bool hasNewLayout); - - [FreeFunction] - public static extern bool YGNodeGetHasNewLayout(IntPtr node); - -#endregion - -#region YG_NODE_STYLE_PROPERTY - - [FreeFunction] - public static extern void YGNodeStyleSetDirection(IntPtr node, YogaDirection direction); - - [FreeFunction] - public static extern YogaDirection YGNodeStyleGetDirection(IntPtr node); - - [FreeFunction] - public static extern void YGNodeStyleSetFlexDirection(IntPtr node, YogaFlexDirection flexDirection); - - [FreeFunction] - public static extern YogaFlexDirection YGNodeStyleGetFlexDirection(IntPtr node); - - [FreeFunction] - public static extern void YGNodeStyleSetJustifyContent(IntPtr node, YogaJustify justifyContent); - - [FreeFunction] - public static extern YogaJustify YGNodeStyleGetJustifyContent(IntPtr node); - - [FreeFunction] - public static extern void YGNodeStyleSetAlignContent(IntPtr node, YogaAlign alignContent); - - [FreeFunction] - public static extern YogaAlign YGNodeStyleGetAlignContent(IntPtr node); - - [FreeFunction] - public static extern void YGNodeStyleSetAlignItems(IntPtr node, YogaAlign alignItems); - - [FreeFunction] - public static extern YogaAlign YGNodeStyleGetAlignItems(IntPtr node); - - [FreeFunction] - public static extern void YGNodeStyleSetAlignSelf(IntPtr node, YogaAlign alignSelf); - - [FreeFunction] - public static extern YogaAlign YGNodeStyleGetAlignSelf(IntPtr node); - - [FreeFunction] - public static extern void YGNodeStyleSetPositionType(IntPtr node, YogaPositionType positionType); - - [FreeFunction] - public static extern YogaPositionType YGNodeStyleGetPositionType(IntPtr node); - - [FreeFunction] - public static extern void YGNodeStyleSetFlexWrap(IntPtr node, YogaWrap flexWrap); - - [FreeFunction] - public static extern YogaWrap YGNodeStyleGetFlexWrap(IntPtr node); - - [FreeFunction] - public static extern void YGNodeStyleSetOverflow(IntPtr node, YogaOverflow flexWrap); - - [FreeFunction] - public static extern YogaOverflow YGNodeStyleGetOverflow(IntPtr node); - - [FreeFunction] - public static extern void YGNodeStyleSetDisplay(IntPtr node, YogaDisplay display); - - [FreeFunction] - public static extern YogaDisplay YGNodeStyleGetDisplay(IntPtr node); - - [FreeFunction] - public static extern void YGNodeStyleSetFlex(IntPtr node, float flex); - - [FreeFunction] - public static extern void YGNodeStyleSetFlexGrow(IntPtr node, float flexGrow); - - [FreeFunction] - public static extern float YGNodeStyleGetFlexGrow(IntPtr node); - - [FreeFunction] - public static extern void YGNodeStyleSetFlexShrink(IntPtr node, float flexShrink); - - [FreeFunction] - public static extern float YGNodeStyleGetFlexShrink(IntPtr node); - - [FreeFunction] - public static extern void YGNodeStyleSetFlexBasis(IntPtr node, float flexBasis); - - [FreeFunction] - public static extern void YGNodeStyleSetFlexBasisPercent(IntPtr node, float flexBasis); - - [FreeFunction] - public static extern void YGNodeStyleSetFlexBasisAuto(IntPtr node); - - [FreeFunction] - public static extern YogaValueType YGNodeStyleGetFlexBasis(IntPtr node); - - [FreeFunction] - public static extern float YGNodeGetComputedFlexBasis(IntPtr node); - - [FreeFunction] - public static extern void YGNodeStyleSetWidth(IntPtr node, float width); - - [FreeFunction] - public static extern void YGNodeStyleSetWidthPercent(IntPtr node, float width); - - [FreeFunction] - public static extern void YGNodeStyleSetWidthAuto(IntPtr node); - - [FreeFunction] - public static extern YogaValueType YGNodeStyleGetWidth(IntPtr node); - - [FreeFunction] - public static extern void YGNodeStyleSetHeight(IntPtr node, float height); - - [FreeFunction] - public static extern void YGNodeStyleSetHeightPercent(IntPtr node, float height); - - [FreeFunction] - public static extern void YGNodeStyleSetHeightAuto(IntPtr node); - - [FreeFunction] - public static extern YogaValueType YGNodeStyleGetHeight(IntPtr node); - - [FreeFunction] - public static extern void YGNodeStyleSetMinWidth(IntPtr node, float minWidth); - - [FreeFunction] - public static extern void YGNodeStyleSetMinWidthPercent(IntPtr node, float minWidth); - - [FreeFunction] - public static extern YogaValueType YGNodeStyleGetMinWidth(IntPtr node); - - [FreeFunction] - public static extern void YGNodeStyleSetMinHeight(IntPtr node, float minHeight); - - [FreeFunction] - public static extern void YGNodeStyleSetMinHeightPercent(IntPtr node, float minHeight); - - [FreeFunction] - public static extern YogaValueType YGNodeStyleGetMinHeight(IntPtr node); - - [FreeFunction] - public static extern void YGNodeStyleSetMaxWidth(IntPtr node, float maxWidth); - - [FreeFunction] - public static extern void YGNodeStyleSetMaxWidthPercent(IntPtr node, float maxWidth); - - [FreeFunction] - public static extern YogaValueType YGNodeStyleGetMaxWidth(IntPtr node); - - [FreeFunction] - public static extern void YGNodeStyleSetMaxHeight(IntPtr node, float maxHeight); - - [FreeFunction] - public static extern void YGNodeStyleSetMaxHeightPercent(IntPtr node, float maxHeight); - - [FreeFunction] - public static extern YogaValueType YGNodeStyleGetMaxHeight(IntPtr node); - - [FreeFunction] - public static extern void YGNodeStyleSetAspectRatio(IntPtr node, float aspectRatio); - - [FreeFunction] - public static extern float YGNodeStyleGetAspectRatio(IntPtr node); - -#endregion - -#region YG_NODE_STYLE_EDGE_PROPERTY - - [FreeFunction] - public static extern void YGNodeStyleSetPosition(IntPtr node, YogaEdge edge, float position); - - [FreeFunction] - public static extern void YGNodeStyleSetPositionPercent(IntPtr node, YogaEdge edge, float position); - - [FreeFunction] - public static extern YogaValueType YGNodeStyleGetPosition(IntPtr node, YogaEdge edge); - - [FreeFunction] - public static extern void YGNodeStyleSetMargin(IntPtr node, YogaEdge edge, float margin); - - [FreeFunction] - public static extern void YGNodeStyleSetMarginPercent(IntPtr node, YogaEdge edge, float margin); - - [FreeFunction] - public static extern void YGNodeStyleSetMarginAuto(IntPtr node, YogaEdge edge); - - [FreeFunction] - public static extern YogaValueType YGNodeStyleGetMargin(IntPtr node, YogaEdge edge); - - [FreeFunction] - public static extern void YGNodeStyleSetPadding(IntPtr node, YogaEdge edge, float padding); - - [FreeFunction] - public static extern void YGNodeStyleSetPaddingPercent(IntPtr node, YogaEdge edge, float padding); - - [FreeFunction] - public static extern YogaValueType YGNodeStyleGetPadding(IntPtr node, YogaEdge edge); - - [FreeFunction] - public static extern void YGNodeStyleSetBorder(IntPtr node, YogaEdge edge, float border); - - [FreeFunction] - public static extern float YGNodeStyleGetBorder(IntPtr node, YogaEdge edge); - -#endregion - -#region YG_NODE_LAYOUT_PROPERTY - - [FreeFunction] - public static extern float YGNodeLayoutGetLeft(IntPtr node); - - [FreeFunction] - public static extern float YGNodeLayoutGetTop(IntPtr node); - - [FreeFunction] - public static extern float YGNodeLayoutGetRight(IntPtr node); - - [FreeFunction] - public static extern float YGNodeLayoutGetBottom(IntPtr node); - - [FreeFunction] - public static extern float YGNodeLayoutGetWidth(IntPtr node); - - [FreeFunction] - public static extern float YGNodeLayoutGetHeight(IntPtr node); - - [FreeFunction] - public static extern float YGNodeLayoutGetMargin(IntPtr node, YogaEdge edge); - - [FreeFunction] - public static extern float YGNodeLayoutGetPadding(IntPtr node, YogaEdge edge); - - [FreeFunction] - public static extern float YGNodeLayoutGetBorder(IntPtr node, YogaEdge edge); - - [FreeFunction] - public static extern YogaDirection YGNodeLayoutGetDirection(IntPtr node); - - #endregion - - #region Context - -// BEGIN_UNITY @jonathanma Remove YGNode and YGConfig context to avoid pinning GC objects -// END_UNITY - -#endregion - } -} -// END_UNITY diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaNode.Spacing.cs b/External/Yoga/csharp/Facebook.Yoga/YogaNode.Spacing.cs deleted file mode 100644 index ef72e56b2f..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaNode.Spacing.cs +++ /dev/null @@ -1,593 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal partial class YogaNode - { - public YogaValue Left - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetPosition(_ygNode, YogaEdge.Left)); - } - - set - { - SetStylePosition(YogaEdge.Left, value); - } - } - - public YogaValue Top - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetPosition(_ygNode, YogaEdge.Top)); - } - - set - { - SetStylePosition(YogaEdge.Top, value); - } - } - - public YogaValue Right - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetPosition(_ygNode, YogaEdge.Right)); - } - - set - { - SetStylePosition(YogaEdge.Right, value); - } - } - - public YogaValue Bottom - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetPosition(_ygNode, YogaEdge.Bottom)); - } - - set - { - SetStylePosition(YogaEdge.Bottom, value); - } - } - - public YogaValue Start - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetPosition(_ygNode, YogaEdge.Start)); - } - - set - { - SetStylePosition(YogaEdge.Start, value); - } - } - - public YogaValue End - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetPosition(_ygNode, YogaEdge.End)); - } - - set - { - SetStylePosition(YogaEdge.End, value); - } - } - - private void SetStylePosition(YogaEdge edge, YogaValue value) - { - if (value.Unit == YogaUnit.Percent) - { - Native.YGNodeStyleSetPositionPercent(_ygNode, edge, value.Value); - } - else - { - Native.YGNodeStyleSetPosition(_ygNode, edge, value.Value); - } - } - - public YogaValue MarginLeft - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetMargin(_ygNode, YogaEdge.Left)); - } - - set - { - SetStyleMargin(YogaEdge.Left, value); - } - } - - public YogaValue MarginTop - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetMargin(_ygNode, YogaEdge.Top)); - } - - set - { - SetStyleMargin(YogaEdge.Top, value); - } - } - - public YogaValue MarginRight - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetMargin(_ygNode, YogaEdge.Right)); - } - - set - { - SetStyleMargin(YogaEdge.Right, value); - } - } - - public YogaValue MarginBottom - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetMargin(_ygNode, YogaEdge.Bottom)); - } - - set - { - SetStyleMargin(YogaEdge.Bottom, value); - } - } - - public YogaValue MarginStart - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetMargin(_ygNode, YogaEdge.Start)); - } - - set - { - SetStyleMargin(YogaEdge.Start, value); - } - } - - public YogaValue MarginEnd - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetMargin(_ygNode, YogaEdge.End)); - } - - set - { - SetStyleMargin(YogaEdge.End, value); - } - } - - public YogaValue MarginHorizontal - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetMargin(_ygNode, YogaEdge.Horizontal)); - } - - set - { - SetStyleMargin(YogaEdge.Horizontal, value); - } - } - - public YogaValue MarginVertical - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetMargin(_ygNode, YogaEdge.Vertical)); - } - - set - { - SetStyleMargin(YogaEdge.Vertical, value); - } - } - - public YogaValue Margin - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetMargin(_ygNode, YogaEdge.All)); - } - - set - { - SetStyleMargin(YogaEdge.All, value); - } - } - - private void SetStyleMargin(YogaEdge edge, YogaValue value) - { - if (value.Unit == YogaUnit.Percent) - { - Native.YGNodeStyleSetMarginPercent(_ygNode, edge, value.Value); - } - else if (value.Unit == YogaUnit.Auto) - { - Native.YGNodeStyleSetMarginAuto(_ygNode, edge); - } - else - { - Native.YGNodeStyleSetMargin(_ygNode, edge, value.Value); - } - } - - public YogaValue PaddingLeft - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetPadding(_ygNode, YogaEdge.Left)); - } - - set - { - SetStylePadding(YogaEdge.Left, value); - } - } - - public YogaValue PaddingTop - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetPadding(_ygNode, YogaEdge.Top)); - } - - set - { - SetStylePadding(YogaEdge.Top, value); - } - } - - public YogaValue PaddingRight - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetPadding(_ygNode, YogaEdge.Right)); - } - - set - { - SetStylePadding(YogaEdge.Right, value); - } - } - - public YogaValue PaddingBottom - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetPadding(_ygNode, YogaEdge.Bottom)); - } - - set - { - SetStylePadding(YogaEdge.Bottom, value); - } - } - - public YogaValue PaddingStart - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetPadding(_ygNode, YogaEdge.Start)); - } - - set - { - SetStylePadding(YogaEdge.Start, value); - } - } - - public YogaValue PaddingEnd - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetPadding(_ygNode, YogaEdge.End)); - } - - set - { - SetStylePadding(YogaEdge.End, value); - } - } - - public YogaValue PaddingHorizontal - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetPadding(_ygNode, YogaEdge.Horizontal)); - } - - set - { - SetStylePadding(YogaEdge.Horizontal, value); - } - } - - public YogaValue PaddingVertical - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetPadding(_ygNode, YogaEdge.Vertical)); - } - - set - { - SetStylePadding(YogaEdge.Vertical, value); - } - } - - public YogaValue Padding - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetPadding(_ygNode, YogaEdge.All)); - } - - set - { - SetStylePadding(YogaEdge.All, value); - } - } - - private void SetStylePadding(YogaEdge edge, YogaValue value) - { - if (value.Unit == YogaUnit.Percent) - { - Native.YGNodeStyleSetPaddingPercent(_ygNode, edge, value.Value); - } - else - { - Native.YGNodeStyleSetPadding(_ygNode, edge, value.Value); - } - } - - public float BorderLeftWidth - { - get - { - return Native.YGNodeStyleGetBorder(_ygNode, YogaEdge.Left); - } - - set - { - Native.YGNodeStyleSetBorder(_ygNode, YogaEdge.Left, value); - } - } - - public float BorderTopWidth - { - get - { - return Native.YGNodeStyleGetBorder(_ygNode, YogaEdge.Top); - } - - set - { - Native.YGNodeStyleSetBorder(_ygNode, YogaEdge.Top, value); - } - } - - public float BorderRightWidth - { - get - { - return Native.YGNodeStyleGetBorder(_ygNode, YogaEdge.Right); - } - - set - { - Native.YGNodeStyleSetBorder(_ygNode, YogaEdge.Right, value); - } - } - - public float BorderBottomWidth - { - get - { - return Native.YGNodeStyleGetBorder(_ygNode, YogaEdge.Bottom); - } - - set - { - Native.YGNodeStyleSetBorder(_ygNode, YogaEdge.Bottom, value); - } - } - - public float BorderStartWidth - { - get - { - return Native.YGNodeStyleGetBorder(_ygNode, YogaEdge.Start); - } - - set - { - Native.YGNodeStyleSetBorder(_ygNode, YogaEdge.Start, value); - } - } - - public float BorderEndWidth - { - get - { - return Native.YGNodeStyleGetBorder(_ygNode, YogaEdge.End); - } - - set - { - Native.YGNodeStyleSetBorder(_ygNode, YogaEdge.End, value); - } - } - - public float BorderWidth - { - get - { - return Native.YGNodeStyleGetBorder(_ygNode, YogaEdge.All); - } - - set - { - Native.YGNodeStyleSetBorder(_ygNode, YogaEdge.All, value); - } - } - - public float LayoutMarginLeft - { - get - { - return Native.YGNodeLayoutGetMargin(_ygNode, YogaEdge.Left); - } - } - - public float LayoutMarginTop - { - get - { - return Native.YGNodeLayoutGetMargin(_ygNode, YogaEdge.Top); - } - } - - public float LayoutMarginRight - { - get - { - return Native.YGNodeLayoutGetMargin(_ygNode, YogaEdge.Right); - } - } - - public float LayoutMarginBottom - { - get - { - return Native.YGNodeLayoutGetMargin(_ygNode, YogaEdge.Bottom); - } - } - - public float LayoutMarginStart - { - get - { - return Native.YGNodeLayoutGetMargin(_ygNode, YogaEdge.Start); - } - } - - public float LayoutMarginEnd - { - get - { - return Native.YGNodeLayoutGetMargin(_ygNode, YogaEdge.End); - } - } - - public float LayoutPaddingLeft - { - get - { - return Native.YGNodeLayoutGetPadding(_ygNode, YogaEdge.Left); - } - } - - public float LayoutPaddingTop - { - get - { - return Native.YGNodeLayoutGetPadding(_ygNode, YogaEdge.Top); - } - } - - public float LayoutPaddingRight - { - get - { - return Native.YGNodeLayoutGetPadding(_ygNode, YogaEdge.Right); - } - } - - public float LayoutPaddingBottom - { - get - { - return Native.YGNodeLayoutGetPadding(_ygNode, YogaEdge.Bottom); - } - } - - public float LayoutBorderLeft - { - get - { - return Native.YGNodeLayoutGetBorder(_ygNode, YogaEdge.Left); - } - } - - public float LayoutBorderTop - { - get - { - return Native.YGNodeLayoutGetBorder(_ygNode, YogaEdge.Top); - } - } - - public float LayoutBorderRight - { - get - { - return Native.YGNodeLayoutGetBorder(_ygNode, YogaEdge.Right); - } - } - - public float LayoutBorderBottom - { - get - { - return Native.YGNodeLayoutGetBorder(_ygNode, YogaEdge.Bottom); - } - } - - public float LayoutPaddingStart - { - get - { - return Native.YGNodeLayoutGetPadding(_ygNode, YogaEdge.Start); - } - } - - public float LayoutPaddingEnd - { - get - { - return Native.YGNodeLayoutGetPadding(_ygNode, YogaEdge.End); - } - } - - public float ComputedFlexBasis - { - get - { - return Native.YGNodeGetComputedFlexBasis(_ygNode); - } - } - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaNode.cs b/External/Yoga/csharp/Facebook.Yoga/YogaNode.cs deleted file mode 100644 index 690eabf5d9..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaNode.cs +++ /dev/null @@ -1,733 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Text; - - -// BEGIN_UNITY @jonathanma -// This file is modified to set a managed object reference on the native side when a callback is set on a node. -// The facebook implementation use the YGNode context to create a mapping between IntPtr and managed objects (YGNodeHandle SetContext and GetManaged). -// However, this require pinning the managed objects in the GC and since there might be quite a few -// nodes alive at the same time we avoid that and use a weak GC handle in the native code instead. -// END_UNITY -namespace UnityEngine.Yoga -{ - internal partial class YogaNode : IEnumerable - { - // BEGIN_UNITY @jonathanma _ygNode needs to be accessible on UWP (see YogaNodeIntPtrMarshaler) - //private readonly IntPtr _ygNode; - internal IntPtr _ygNode; - // END_UNITY - private YogaConfig _config; //@mathieum not readonly anymore - private WeakReference _parent; - private List _children; - private MeasureFunction _measureFunction; - private BaselineFunction _baselineFunction; - private object _data; - - public YogaNode(YogaConfig config = null) - { - _config = config == null ? YogaConfig.Default : config; - _ygNode = Native.YGNodeNewWithConfig(_config.Handle); - if (_ygNode == IntPtr.Zero) - { - throw new InvalidOperationException("Failed to allocate native memory"); - } - } - - public YogaNode(YogaNode srcNode) - : this(srcNode._config) - { - CopyStyle(srcNode); - } - - ~YogaNode() - { - Native.YGNodeFree(_ygNode); - } - - public void Reset() - { - _measureFunction = null; - _baselineFunction = null; - _data = null; - -// BEGIN_UNITY @jonathanma Remove Context usage and YGNodeHandle, native YGNode has a reference of the managed object - //_ygNode.ReleaseManaged(); - Native.YGSetManagedObject(_ygNode, null); - Native.YGNodeReset(_ygNode); - //_ygNode.SetContext(this); -// END_UNITY - } - -// BEGIN_UNITY @mathieum Needed for dpi awareness - internal YogaConfig Config - { - get { return _config; } - set - { - _config = value ?? YogaConfig.Default; - Native.YGNodeSetConfig(_ygNode, _config.Handle); - - } - } -// END_UNITY - - public bool IsDirty - { - get - { - return Native.YGNodeIsDirty(_ygNode); - } - } - - public virtual void MarkDirty() - { - Native.YGNodeMarkDirty(_ygNode); - } - - public bool HasNewLayout - { - get - { - return Native.YGNodeGetHasNewLayout(_ygNode); - } - } - - public void MarkHasNewLayout() - { - Native.YGNodeSetHasNewLayout(_ygNode, true); - } - - public YogaNode Parent - { - get - { - return _parent != null ? _parent.Target as YogaNode : null; - } - } - - public bool IsMeasureDefined - { - get - { - return _measureFunction != null; - } - } - - public bool IsBaselineDefined - { - get - { - return _baselineFunction != null; - } - } - - public void CopyStyle(YogaNode srcNode) - { - Native.YGNodeCopyStyle(_ygNode, srcNode._ygNode); - } - - public YogaDirection StyleDirection - { - get - { - return Native.YGNodeStyleGetDirection(_ygNode); - } - - set - { - Native.YGNodeStyleSetDirection(_ygNode, value); - } - } - - public YogaFlexDirection FlexDirection - { - get - { - return Native.YGNodeStyleGetFlexDirection(_ygNode); - } - - set - { - Native.YGNodeStyleSetFlexDirection(_ygNode, value); - } - } - - public YogaJustify JustifyContent - { - get - { - return Native.YGNodeStyleGetJustifyContent(_ygNode); - } - - set - { - Native.YGNodeStyleSetJustifyContent(_ygNode, value); - } - } - - public YogaDisplay Display - { - get - { - return Native.YGNodeStyleGetDisplay(_ygNode); - } - - set - { - Native.YGNodeStyleSetDisplay(_ygNode, value); - } - } - - public YogaAlign AlignItems - { - get - { - return Native.YGNodeStyleGetAlignItems(_ygNode); - } - - set - { - Native.YGNodeStyleSetAlignItems(_ygNode, value); - } - } - - public YogaAlign AlignSelf - { - get - { - return Native.YGNodeStyleGetAlignSelf(_ygNode); - } - - set - { - Native.YGNodeStyleSetAlignSelf(_ygNode, value); - } - } - - public YogaAlign AlignContent - { - get - { - return Native.YGNodeStyleGetAlignContent(_ygNode); - } - - set - { - Native.YGNodeStyleSetAlignContent(_ygNode, value); - } - } - - public YogaPositionType PositionType - { - get - { - return Native.YGNodeStyleGetPositionType(_ygNode); - } - - set - { - Native.YGNodeStyleSetPositionType(_ygNode, value); - } - } - - public YogaWrap Wrap - { - get - { - return Native.YGNodeStyleGetFlexWrap(_ygNode); - } - - set - { - Native.YGNodeStyleSetFlexWrap(_ygNode, value); - } - } - - public float Flex - { - set - { - Native.YGNodeStyleSetFlex(_ygNode, value); - } - } - - public float FlexGrow - { - get - { - return Native.YGNodeStyleGetFlexGrow(_ygNode); - } - - set - { - Native.YGNodeStyleSetFlexGrow(_ygNode, value); - } - } - - public float FlexShrink - { - get - { - return Native.YGNodeStyleGetFlexShrink(_ygNode); - } - - set - { - Native.YGNodeStyleSetFlexShrink(_ygNode, value); - } - } - - public YogaValue FlexBasis - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetFlexBasis(_ygNode)); - } - - set - { - if (value.Unit == YogaUnit.Percent) - { - Native.YGNodeStyleSetFlexBasisPercent(_ygNode, value.Value); - } - else if (value.Unit == YogaUnit.Auto) - { - Native.YGNodeStyleSetFlexBasisAuto(_ygNode); - } - else - { - Native.YGNodeStyleSetFlexBasis(_ygNode, value.Value); - } - } - } - - public YogaValue Width - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetWidth(_ygNode)); - } - - set - { - if (value.Unit == YogaUnit.Percent) - { - Native.YGNodeStyleSetWidthPercent(_ygNode, value.Value); - } - else if (value.Unit == YogaUnit.Auto) - { - Native.YGNodeStyleSetWidthAuto(_ygNode); - } - else - { - Native.YGNodeStyleSetWidth(_ygNode, value.Value); - } - } - } - - public YogaValue Height - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetHeight(_ygNode)); - } - - set - { - if (value.Unit == YogaUnit.Percent) - { - Native.YGNodeStyleSetHeightPercent(_ygNode, value.Value); - } - else if (value.Unit == YogaUnit.Auto) - { - Native.YGNodeStyleSetHeightAuto(_ygNode); - } - else - { - Native.YGNodeStyleSetHeight(_ygNode, value.Value); - } - } - } - - public YogaValue MaxWidth - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetMaxWidth(_ygNode)); - } - - set - { - if (value.Unit == YogaUnit.Percent) - { - Native.YGNodeStyleSetMaxWidthPercent(_ygNode, value.Value); - } - else - { - Native.YGNodeStyleSetMaxWidth(_ygNode, value.Value); - } - } - } - - public YogaValue MaxHeight - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetMaxHeight(_ygNode)); - } - - set - { - if (value.Unit == YogaUnit.Percent) - { - Native.YGNodeStyleSetMaxHeightPercent(_ygNode, value.Value); - } - else - { - Native.YGNodeStyleSetMaxHeight(_ygNode, value.Value); - } - } - } - - public YogaValue MinWidth - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetMinWidth(_ygNode)); - } - - set - { - if (value.Unit == YogaUnit.Percent) - { - Native.YGNodeStyleSetMinWidthPercent(_ygNode, value.Value); - } - else - { - Native.YGNodeStyleSetMinWidth(_ygNode, value.Value); - } - } - } - - public YogaValue MinHeight - { - get - { - return YogaValue.MarshalValue(Native.YGNodeStyleGetMinHeight(_ygNode)); - } - - set - { - if (value.Unit == YogaUnit.Percent) - { - Native.YGNodeStyleSetMinHeightPercent(_ygNode, value.Value); - } - else - { - Native.YGNodeStyleSetMinHeight(_ygNode, value.Value); - } - } - } - - public float AspectRatio - { - get - { - return Native.YGNodeStyleGetAspectRatio(_ygNode); - } - - set - { - Native.YGNodeStyleSetAspectRatio(_ygNode, value); - } - } - - public float LayoutX - { - get - { - return Native.YGNodeLayoutGetLeft(_ygNode); - } - } - - public float LayoutY - { - get - { - return Native.YGNodeLayoutGetTop(_ygNode); - } - } - - public float LayoutRight - { - get - { - return Native.YGNodeLayoutGetRight(_ygNode); - } - } - - public float LayoutBottom - { - get - { - return Native.YGNodeLayoutGetBottom(_ygNode); - } - } - - public float LayoutWidth - { - get - { - return Native.YGNodeLayoutGetWidth(_ygNode); - } - } - - public float LayoutHeight - { - get - { - return Native.YGNodeLayoutGetHeight(_ygNode); - } - } - - public YogaDirection LayoutDirection - { - get - { - return Native.YGNodeLayoutGetDirection(_ygNode); - } - } - - public YogaOverflow Overflow - { - get - { - return Native.YGNodeStyleGetOverflow(_ygNode); - } - - set - { - Native.YGNodeStyleSetOverflow(_ygNode, value); - } - } - - public object Data - { - get - { - return _data; - } - - set - { - _data = value; - } - } - - public YogaNode this[int index] - { - get - { - return _children[index]; - } - } - - public int Count - { - get - { - return _children != null ? _children.Count : 0; - } - } - - public void MarkLayoutSeen() - { - Native.YGNodeSetHasNewLayout(_ygNode, false); - } - - public bool ValuesEqual(float f1, float f2) - { - if (float.IsNaN(f1) || float.IsNaN(f2)) - { - return float.IsNaN(f1) && float.IsNaN(f2); - } - - return Math.Abs(f2 - f1) < float.Epsilon; - } - - public void Insert(int index, YogaNode node) - { - if (_children == null) - { - _children = new List(4); - } - _children.Insert(index, node); - node._parent = new WeakReference(this); - Native.YGNodeInsertChild(_ygNode, node._ygNode, (uint)index); - } - - public void RemoveAt(int index) - { - var child = _children[index]; - child._parent = null; - _children.RemoveAt(index); - Native.YGNodeRemoveChild(_ygNode, child._ygNode); - } - - public void AddChild(YogaNode child) - { - Insert(Count, child); - } - - public void RemoveChild(YogaNode child) - { - int index = IndexOf(child); - if (index >= 0) - { - RemoveAt(index); - } - } - - public void Clear() - { - if (_children != null) - { - while (_children.Count > 0) - { - RemoveAt(_children.Count-1); - } - } - } - - public int IndexOf(YogaNode node) - { - return _children != null ? _children.IndexOf(node) : -1; - } - - public void SetMeasureFunction(MeasureFunction measureFunction) - { - _measureFunction = measureFunction; - if (measureFunction == null) - { - if (!IsBaselineDefined) - { - Native.YGSetManagedObject(_ygNode, null); - } - Native.YGNodeRemoveMeasureFunc(_ygNode); - } - else - { - Native.YGSetManagedObject(_ygNode, this); - Native.YGNodeSetMeasureFunc(_ygNode); - } - } - - public void SetBaselineFunction(BaselineFunction baselineFunction) - { - _baselineFunction = baselineFunction; - if (baselineFunction == null) - { - if (!IsMeasureDefined) - { - Native.YGSetManagedObject(_ygNode, null); - } - Native.YGNodeRemoveBaselineFunc(_ygNode); - } - else - { - Native.YGSetManagedObject(_ygNode, this); - Native.YGNodeSetBaselineFunc(_ygNode); - } - } - - public void CalculateLayout( - float width = YogaConstants.Undefined, - float height = YogaConstants.Undefined) - { - Native.YGNodeCalculateLayout( - _ygNode, - width, - height, - Native.YGNodeStyleGetDirection(_ygNode)); - } -// BEGIN_UNITY @jonathanma Required for Unity package -//#if (UNITY_IOS && !UNITY_EDITOR) || ENABLE_IL2CPP || __IOS__ - //[MonoPInvokeCallback(typeof(YogaMeasureFunc))] -//#endif - //private static YogaSize MeasureInternal( - //IntPtr unmanagedNodePtr, -// END_UNITY - public static YogaSize MeasureInternal( - YogaNode node, - float width, - YogaMeasureMode widthMode, - float height, - YogaMeasureMode heightMode) - { - if (node == null || node._measureFunction == null) - { - throw new InvalidOperationException("Measure function is not defined."); - } - return node._measureFunction(node, width, widthMode, height, heightMode); - } - - // BEGIN_UNITY @jonathanma Required for Unity package - //#if (UNITY_IOS && !UNITY_EDITOR) || ENABLE_IL2CPP || __IOS__ - //[MonoPInvokeCallback(typeof(YogaMeasureFunc))] - //#endif - //private static float BaselineInternal( - //IntPtr unmanagedNodePtr, - // END_UNITY - public static float BaselineInternal( - YogaNode node, - float width, - float height) - { - if (node == null || node._baselineFunction == null) - { - throw new InvalidOperationException("Baseline function is not defined."); - } - return node._baselineFunction(node, width, height); - } - - public string Print(YogaPrintOptions options = - YogaPrintOptions.Layout|YogaPrintOptions.Style|YogaPrintOptions.Children) - { - StringBuilder sb = new StringBuilder(); - Logger orig = _config.Logger; - _config.Logger = (config, node, level, message) => {sb.Append(message);}; - Native.YGNodePrint(_ygNode, options); - _config.Logger = orig; - return sb.ToString(); - } - - public IEnumerator GetEnumerator() - { - return _children != null ? ((IEnumerable)_children).GetEnumerator() : - System.Linq.Enumerable.Empty().GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return _children != null ? ((IEnumerable)_children).GetEnumerator() : - System.Linq.Enumerable.Empty().GetEnumerator(); - } - - public static int GetInstanceCount() - { - return Native.YGNodeGetInstanceCount(); - } - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaNodeType.cs b/External/Yoga/csharp/Facebook.Yoga/YogaNodeType.cs deleted file mode 100644 index e76be6c314..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaNodeType.cs +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal enum YogaNodeType - { - Default, - Text, - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaOverflow.cs b/External/Yoga/csharp/Facebook.Yoga/YogaOverflow.cs deleted file mode 100644 index 963a930fb4..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaOverflow.cs +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal enum YogaOverflow - { - Visible, - Hidden, - Scroll, - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaPositionType.cs b/External/Yoga/csharp/Facebook.Yoga/YogaPositionType.cs deleted file mode 100644 index 39a0411bbc..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaPositionType.cs +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal enum YogaPositionType - { - Relative, - Absolute, - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaPrintOptions.cs b/External/Yoga/csharp/Facebook.Yoga/YogaPrintOptions.cs deleted file mode 100644 index 2d92b01b65..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaPrintOptions.cs +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - [System.Flags] - internal enum YogaPrintOptions - { - Layout = 1, - Style = 2, - Children = 4, - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaSize.cs b/External/Yoga/csharp/Facebook.Yoga/YogaSize.cs deleted file mode 100644 index f4803e0893..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaSize.cs +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -using System.Runtime.InteropServices; - -namespace UnityEngine.Yoga -{ - [StructLayout(LayoutKind.Sequential)] - internal struct YogaSize - { - public float width; - public float height; - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaUnit.cs b/External/Yoga/csharp/Facebook.Yoga/YogaUnit.cs deleted file mode 100644 index 2bc091a37b..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaUnit.cs +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal enum YogaUnit - { - Undefined, - Point, - Percent, - Auto, - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaValue.cs b/External/Yoga/csharp/Facebook.Yoga/YogaValue.cs deleted file mode 100644 index 5140334816..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaValue.cs +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -using System; -using System.Runtime.InteropServices; - -namespace UnityEngine.Yoga -{ - [StructLayout(LayoutKind.Sequential)] - internal struct YogaValue - { - private float value; - private YogaUnit unit; - - public YogaUnit Unit - { - get - { - return unit; - } - } - - public float Value - { - get - { - return value; - } - } - - public static YogaValue Point(float value) - { - return new YogaValue - { - value = value, - unit = YogaConstants.IsUndefined(value) ? YogaUnit.Undefined : YogaUnit.Point - }; - } - - public bool Equals(YogaValue other) - { - return Unit == other.Unit && (Value.Equals(other.Value) || Unit == YogaUnit.Undefined); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - return obj is YogaValue && Equals((YogaValue) obj); - } - - public override int GetHashCode() - { - unchecked - { - return (Value.GetHashCode() * 397) ^ (int) Unit; - } - } - - public static YogaValue Undefined() - { - return new YogaValue - { - value = YogaConstants.Undefined, - unit = YogaUnit.Undefined - }; - } - - public static YogaValue Auto() - { - return new YogaValue - { - // BEGIN_UNITY @jonathanma For some styles Auto has to be NaN - value = float.NaN, - // END_UNITY - unit = YogaUnit.Auto - }; - } - - public static YogaValue Percent(float value) - { - return new YogaValue - { - value = value, - unit = YogaConstants.IsUndefined(value) ? YogaUnit.Undefined : YogaUnit.Percent - }; - } - - public static implicit operator YogaValue(float pointValue) - { - return Point(pointValue); - } - - internal static YogaValue MarshalValue(YogaValue value) - { - return value; - } - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaValueExtensions.cs b/External/Yoga/csharp/Facebook.Yoga/YogaValueExtensions.cs deleted file mode 100644 index ba2440d378..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaValueExtensions.cs +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal static class YogaValueExtensions - { - public static YogaValue Percent(this float value) - { - return YogaValue.Percent(value); - } - - public static YogaValue Pt(this float value) - { - return YogaValue.Point(value); - } - - public static YogaValue Percent(this int value) - { - return YogaValue.Percent(value); - } - - public static YogaValue Pt(this int value) - { - return YogaValue.Point(value); - } - } -} diff --git a/External/Yoga/csharp/Facebook.Yoga/YogaWrap.cs b/External/Yoga/csharp/Facebook.Yoga/YogaWrap.cs deleted file mode 100644 index e35e6a3dd2..0000000000 --- a/External/Yoga/csharp/Facebook.Yoga/YogaWrap.cs +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -namespace UnityEngine.Yoga -{ - internal enum YogaWrap - { - NoWrap, - Wrap, - WrapReverse, - } -} diff --git a/External/unitytls/builds/CSharp/BindingsUnity/TLSAgent.gen.bindings.cs b/External/unitytls/builds/CSharp/BindingsUnity/TLSAgent.gen.bindings.cs index 7e9358afa9..e9d6371044 100644 --- a/External/unitytls/builds/CSharp/BindingsUnity/TLSAgent.gen.bindings.cs +++ b/External/unitytls/builds/CSharp/BindingsUnity/TLSAgent.gen.bindings.cs @@ -79,9 +79,31 @@ internal static unsafe partial class Binding public const int UNITYTLS_SSL_HANDSHAKE_CLIENT_FINISHED = 11; public const int UNITYTLS_SSL_HANDSHAKE_SERVER_CHANGE_CIPHER_SPEC = 12; public const int UNITYTLS_SSL_HANDSHAKE_SERVER_FINISHED = 13; + public const int UNITYTLS_SSL_HANDSHAKE_FLUSH_BUFFERS = 14; + public const int UNITYTLS_SSL_HANDSHAKE_WRAPUP = 15; + public const int UNITYTLS_SSL_HANDSHAKE_OVER = 16; + public const int UNITYTLS_SSL_HANDSHAKE_SERVER_NEW_SESSION_TICKET = 17; + public const int UNITYTLS_SSL_HANDSHAKE_HELLO_VERIFY_REQUIRED = 18; + /// Number of constant definitions + public const int UNITYTLS_SSL_HANDSHAKE_COUNT = 19; + /// Deprecated: for backward compatibility only + public const int UNITYTLS_SSL_HANDSHAKE_BEGIN = 0; + /// Deprecated: for backward compatibility only + public const int UNITYTLS_SSL_HANDSHAKE_DONE = 16; + /// Deprecated: for backward compatibility only public const int UNITYTLS_SSL_HANDSHAKE_HANDSHAKE_FLUSH_BUFFERS = 14; + /// Deprecated: for backward compatibility only public const int UNITYTLS_SSL_HANDSHAKE_HANDSHAKE_WRAPUP = 15; + /// Deprecated: for backward compatibility only public const int UNITYTLS_SSL_HANDSHAKE_HANDSHAKE_OVER = 16; + [StructLayout(LayoutKind.Sequential)] + public struct unitytls_errorstate + { + public uint magic; + public uint code; + /// Implementation specific error code/handle. + public ulong reserved; + } /// struct that encapsulates a data pointer and data length. [StructLayout(LayoutKind.Sequential)] public struct unitytls_dataRef @@ -89,12 +111,12 @@ public struct unitytls_dataRef public byte* dataPtr; public size_t dataLen; } - public const int UnityTLSRole_None = 0; - public const int UnityTLSRole_Server = 1; - public const int UnityTLSRole_Client = 2; public const int UnityTLSClientAuth_None = 0; public const int UnityTLSClientAuth_Optional = 1; public const int UnityTLSClientAuth_Required = 2; + public const int UnityTLSRole_None = 0; + public const int UnityTLSRole_Server = 1; + public const int UnityTLSRole_Client = 2; public const int UnityTLSTransportProtocol_Stream = 0; public const int UnityTLSTransportProtocol_Datagram = 1; public const int UnityTLSClientState_None = 0; @@ -197,7 +219,7 @@ public struct unitytls_client_config /// server's hostname's name public byte* hostname; /// one of unitytls_log_level_t above. - public int tracelevel; + public uint tracelevel; /// callback for logging statements. // Delegates are managed types, putting it here directly would prevent us from creating pointers to this struct. // Use System.Runtime.InteropServices.GetFunctionPointerForDelegate to generate IntPtr from the original delegate. diff --git a/ModuleOverrides/com.unity.ui/Core/BackgroundPropertyHelper.cs b/ModuleOverrides/com.unity.ui/Core/BackgroundPropertyHelper.cs index 325676f781..290b39c27b 100644 --- a/ModuleOverrides/com.unity.ui/Core/BackgroundPropertyHelper.cs +++ b/ModuleOverrides/com.unity.ui/Core/BackgroundPropertyHelper.cs @@ -34,13 +34,15 @@ public static BackgroundSize ConvertScaleModeToBackgroundSize(ScaleMode scaleMod } public static ScaleMode ResolveUnityBackgroundScaleMode(BackgroundPosition backgroundPositionX, BackgroundPosition backgroundPositionY, - BackgroundRepeat backgroundRepeat, BackgroundSize backgroundSize) + BackgroundRepeat backgroundRepeat, BackgroundSize backgroundSize, out bool valid) { + if (backgroundPositionX == BackgroundPropertyHelper.ConvertScaleModeToBackgroundPosition(ScaleMode.ScaleAndCrop) && backgroundPositionY == BackgroundPropertyHelper.ConvertScaleModeToBackgroundPosition(ScaleMode.ScaleAndCrop) && backgroundRepeat == BackgroundPropertyHelper.ConvertScaleModeToBackgroundRepeat(ScaleMode.ScaleAndCrop) && backgroundSize == BackgroundPropertyHelper.ConvertScaleModeToBackgroundSize(ScaleMode.ScaleAndCrop)) { + valid = true; return ScaleMode.ScaleAndCrop; } else if (backgroundPositionX == BackgroundPropertyHelper.ConvertScaleModeToBackgroundPosition(ScaleMode.ScaleToFit) && @@ -48,10 +50,20 @@ public static ScaleMode ResolveUnityBackgroundScaleMode(BackgroundPosition backg backgroundRepeat == BackgroundPropertyHelper.ConvertScaleModeToBackgroundRepeat(ScaleMode.ScaleToFit) && backgroundSize == BackgroundPropertyHelper.ConvertScaleModeToBackgroundSize(ScaleMode.ScaleToFit)) { + valid = true; return ScaleMode.ScaleToFit; } + else if (backgroundPositionX == BackgroundPropertyHelper.ConvertScaleModeToBackgroundPosition(ScaleMode.StretchToFill) && + backgroundPositionY == BackgroundPropertyHelper.ConvertScaleModeToBackgroundPosition(ScaleMode.StretchToFill) && + backgroundRepeat == BackgroundPropertyHelper.ConvertScaleModeToBackgroundRepeat(ScaleMode.StretchToFill) && + backgroundSize == BackgroundPropertyHelper.ConvertScaleModeToBackgroundSize(ScaleMode.StretchToFill)) + { + valid = true; + return ScaleMode.StretchToFill; + } else { + valid = false; return ScaleMode.StretchToFill; } } diff --git a/ModuleOverrides/com.unity.ui/Core/ClampedDragger.cs b/ModuleOverrides/com.unity.ui/Core/ClampedDragger.cs index a0086599ba..402f87f3ba 100644 --- a/ModuleOverrides/com.unity.ui/Core/ClampedDragger.cs +++ b/ModuleOverrides/com.unity.ui/Core/ClampedDragger.cs @@ -44,6 +44,9 @@ protected override void ProcessDownEvent(EventBase evt, Vector2 localPosition, i startMousePosition = localPosition; dragDirection = DragDirection.None; base.ProcessDownEvent(evt, localPosition, pointerId); + + // First click should behave like dragging immediately & ensure that the cursor is clamped on first touch + dragging?.Invoke(); } protected override void ProcessMoveEvent(EventBase evt, Vector2 localPosition) diff --git a/ModuleOverrides/com.unity.ui/Core/ClickDetector.cs b/ModuleOverrides/com.unity.ui/Core/ClickDetector.cs index 5046414ea1..c8f430c470 100644 --- a/ModuleOverrides/com.unity.ui/Core/ClickDetector.cs +++ b/ModuleOverrides/com.unity.ui/Core/ClickDetector.cs @@ -170,5 +170,18 @@ private static bool ContainsPointer(VisualElement element, Vector2 position) var elementUnderPointer = element.panel.Pick(position); return element == elementUnderPointer || element.Contains(elementUnderPointer); } + + //Called when a visual element is removed from a panel to clear all reference to the visual element + internal void Cleanup(List elements) + { + foreach (var status in m_ClickStatus) + { + if( status.m_Target == null) + continue; + + if(elements.Contains(status.m_Target)) + status.Reset(); + } + } } } diff --git a/ModuleOverrides/com.unity.ui/Core/Clickable.cs b/ModuleOverrides/com.unity.ui/Core/Clickable.cs index e168880747..0f65febb29 100644 --- a/ModuleOverrides/com.unity.ui/Core/Clickable.cs +++ b/ModuleOverrides/com.unity.ui/Core/Clickable.cs @@ -17,10 +17,32 @@ public class Clickable : PointerManipulator /// /// Callback triggered when the target element is clicked, including event data. /// + /// + /// Encapsulates a method that has an parameter and does not return a value. + /// public event System.Action clickedWithEventInfo; + /// /// Callback triggered when the target element is clicked. /// + /// + /// Encapsulates a method that has no parameters and does not return a value. + /// + /// + /// + /// + /// { + /// Debug.Log("Button was pressed!"); + /// }; + /// return button; + /// } + /// ]]> + /// + /// public event System.Action clicked; private readonly long m_Delay; // in milliseconds @@ -61,8 +83,8 @@ internal bool acceptClicksIfDisabled /// /// Constructor. /// - /// Determines when the event begins. Applies if delay > 0. - /// Determines the time delta between event repetition. Applies if interval > 0. + /// Determines when the event begins. Value is defined in milliseconds. Applies if delay > 0. + /// Determines the time delta between event repetition. Value is defined in milliseconds. Applies if interval > 0. public Clickable(System.Action handler, long delay, long interval) : this(handler) { m_Delay = delay; @@ -96,7 +118,7 @@ private void OnTimer(TimerState timerState) { if ((clicked != null || clickedWithEventInfo != null) && IsRepeatable()) { - if (ContainsPointer(m_ActivePointerId)) + if (ContainsPointer(m_ActivePointerId) && (target.enabledInHierarchy || acceptClicksIfDisabled)) { Invoke(null); target.pseudoStates |= PseudoStates.Active; @@ -310,7 +332,7 @@ protected virtual void ProcessDownEvent(EventBase evt, Vector2 localPosition, in if (IsRepeatable()) { // Repeatable button clicks are performed on the MouseDown and at timer events - if (ContainsPointer(pointerId)) + if (ContainsPointer(pointerId) && (target.enabledInHierarchy || acceptClicksIfDisabled)) { Invoke(evt); } @@ -370,7 +392,7 @@ protected virtual void ProcessUpEvent(EventBase evt, Vector2 localPosition, int else { // Non repeatable button clicks are performed on the MouseUp - if (ContainsPointer(pointerId) && target.enabledInHierarchy) + if (ContainsPointer(pointerId) && (target.enabledInHierarchy || acceptClicksIfDisabled)) { Invoke(evt); } diff --git a/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/BaseListViewController.cs b/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/BaseListViewController.cs index f9c92555dd..365ac6e87f 100644 --- a/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/BaseListViewController.cs +++ b/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/BaseListViewController.cs @@ -49,6 +49,7 @@ internal void PostInitRegistration(ReusableListViewItem listItem) listItem.bindableElement.style.flexBasis = StyleKeyword.Initial; listItem.bindableElement.style.marginTop = 0f; listItem.bindableElement.style.marginBottom = 0f; + listItem.bindableElement.style.paddingTop = 0f; listItem.bindableElement.style.flexGrow = 0f; listItem.bindableElement.style.flexShrink = 0f; } @@ -99,10 +100,25 @@ public virtual void AddItems(int itemCount) } else { - for (var i = 0; i < itemCount; i++) + var sourceType = itemsSource.GetType(); + bool IsGenericList(Type t) => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IList<>); + var listType = sourceType.GetInterfaces().FirstOrDefault(IsGenericList); + if (listType != null && listType.GetGenericArguments()[0].IsValueType) { - indices.Add(previousCount + i); - itemsSource.Add(default); + var elementValueType = listType.GetGenericArguments()[0]; + for (var i = 0; i < itemCount; i++) + { + indices.Add(previousCount + i); + itemsSource.Add(Activator.CreateInstance(elementValueType)); + } + } + else + { + for (var i = 0; i < itemCount; i++) + { + indices.Add(previousCount + i); + itemsSource.Add(default); + } } } @@ -126,6 +142,9 @@ public virtual void Move(int index, int newIndex) if (itemsSource == null) return; + if (index == newIndex) + return; + var minIndex = Mathf.Min(index, newIndex); var maxIndex = Mathf.Max(index, newIndex); @@ -150,16 +169,11 @@ public virtual void Move(int index, int newIndex) /// The item index. public virtual void RemoveItem(int index) { - var indices = ListPool.Get(); - try + using (ListPool.Get(out var indices)) { indices.Add(index); RemoveItems(indices); } - finally - { - ListPool.Release(indices); - } } /// @@ -297,9 +311,7 @@ static Array RemoveFromArray(Array source, List indicesToRemove) void Swap(int lhs, int rhs) { - var current = itemsSource[lhs]; - itemsSource[lhs] = itemsSource[rhs]; - itemsSource[rhs] = current; + (itemsSource[lhs], itemsSource[rhs]) = (itemsSource[rhs], itemsSource[lhs]); } void EnsureItemSourceCanBeResized() diff --git a/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/BaseTreeViewController.cs b/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/BaseTreeViewController.cs index 9e7bb9973c..8395d817d4 100644 --- a/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/BaseTreeViewController.cs +++ b/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/BaseTreeViewController.cs @@ -6,6 +6,8 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using Unity.Profiling; +using UnityEngine.Pool; namespace UnityEngine.UIElements { @@ -14,10 +16,11 @@ namespace UnityEngine.UIElements /// public abstract class BaseTreeViewController : CollectionViewController { - Dictionary m_TreeItems = new Dictionary(); - List m_RootIndices = new List(); - List m_ItemWrappers = new List(); - List m_WrapperInsertionList = new List(); + Dictionary m_TreeItems = new(); + List m_RootIndices = new(); + List m_ItemWrappers = new(); + HashSet m_TreeItemIdsWithItemWrappers = new(); + List m_WrapperInsertionList = new(); /// /// View for this controller, cast as a . @@ -56,37 +59,37 @@ public void RebuildTree() } /// - /// Returns the root items of the tree, by ids. + /// Returns the root items of the tree, by IDs. /// - /// The root item ids. + /// The root item IDs. public IEnumerable GetRootItemIds() { return m_RootIndices; } /// - /// Returns all item ids that can be found in the tree, optionally specifying root ids from where to start. + /// Returns all item IDs that can be found in the tree, optionally specifying root IDs from where to start. /// - /// Root ids to start from. If null, will use the tree root ids. - /// All items ids in the tree, starting from the specified ids. + /// Root IDs to start from. If null, will use the tree root ids. + /// All items IDs in the tree, starting from the specified IDs. public abstract IEnumerable GetAllItemIds(IEnumerable rootIds = null); /// - /// Returns the parent id of an item, by id. + /// Returns the parent ID of an item, by ID. /// - /// The id of the item to fetch the parent from. - /// The parent id, or -1 if the item is at the root of the tree. + /// The ID of the item to fetch the parent from. + /// The parent ID, or -1 if the item is at the root of the tree. public abstract int GetParentId(int id); /// - /// Get all children of a specific id in the tree. + /// Get all children of a specific ID in the tree. /// - /// The item id. - /// The children ids. + /// The item ID. + /// The children IDs. public abstract IEnumerable GetChildrenIds(int id); /// - /// Moves an item by id, to a new parent and child index. + /// Moves an item by ID, to a new parent and child index. /// - /// The id of the item to move. - /// The new parent id. -1 if moved at the root. + /// The ID of the item to move. + /// The new parent ID. -1 if moved at the root. /// The child index to insert at under the parent. -1 will add as the last child. /// Whether we need to rebuild tree data. Set to false when doing multiple operations. public abstract void Move(int id, int newParentId, int childIndex = -1, bool rebuildTree = true); @@ -94,8 +97,8 @@ public IEnumerable GetRootItemIds() /// Removes an item by id. /// /// The item id. - /// Whether we need to rebuild tree data. Set to false when doing multiple operations. - /// Whether or not the item was successfully found and removed. + /// Whether we need to rebuild tree data. Set to false when doing multiple operations and call . + /// Whether the item was successfully found and removed. public abstract bool TryRemoveItem(int id, bool rebuildTree = true); internal override void InvokeMakeItem(ReusableCollectionItem reusableItem) @@ -111,7 +114,7 @@ internal override void InvokeBindItem(ReusableCollectionItem reusableItem, int i { if (reusableItem is ReusableTreeViewItem treeItem) { - treeItem.Indent(GetIndentationDepth(index)); + treeItem.Indent(GetIndentationDepthByIndex(index)); treeItem.SetExpandedWithoutNotify(IsExpandedByIndex(index)); treeItem.SetToggleVisibility(HasChildrenByIndex(index)); } @@ -208,18 +211,21 @@ public virtual int GetTreeItemsCount() } /// - /// Returns the index in the source of the item, by id. + /// Returns the index in the source of the item, by ID. /// - /// The id of the item to look for. + /// The ID of the item to look for. /// The index of the item in the expanded items source. Returns -1 if the item is not visible. public override int GetIndexForId(int id) { - for (var index = 0; index < m_ItemWrappers.Count; index++) + if (m_TreeItemIdsWithItemWrappers.Contains(id)) { - var wrapper = m_ItemWrappers[index]; - if (wrapper.id == id) + for (var index = 0; index < m_ItemWrappers.Count; index++) { - return index; + var wrapper = m_ItemWrappers[index]; + if (wrapper.id == id) + { + return index; + } } } @@ -227,7 +233,7 @@ public override int GetIndexForId(int id) } /// - /// Returns the id for a specified index in the visible items source. + /// Returns the ID for a specified index in the visible items source. /// /// /// @@ -237,10 +243,10 @@ public override int GetIdForIndex(int index) } /// - /// Returns whether or not the item with the specified id has one or more child. + /// Return whether the item with the specified ID has one or more child. /// /// The item id. - /// Whether or not the item with the specified id has one or more child. + /// Whether the item with the specified ID has one or more child. public virtual bool HasChildren(int id) { if (m_TreeItems.TryGetValue(id, out var item)) @@ -250,29 +256,39 @@ public virtual bool HasChildren(int id) } /// - /// Returns whether or not the item with the specified index has one or more child. + /// Checks if an ID exists within this tree. + /// + /// The id to look for. + /// Whether an item with this id exists in the tree. + internal bool Exists(int id) + { + return m_TreeItems.ContainsKey(id); + } + + /// + /// Return whether the item with the specified index has one or more child. /// /// The item index. - /// Whether or not the item with the specified id has one or more child. + /// Whether the item with the specified ID has one or more child. public bool HasChildrenByIndex(int index) { return IsIndexValid(index) && m_ItemWrappers[index].hasChildren; } /// - /// Gets the children ids of the item with the specified index. + /// Gets the children IDs of the item with the specified index. /// /// The item index. - /// The children ids. + /// The children IDs. public IEnumerable GetChildrenIdsByIndex(int index) { return IsIndexValid(index) ? m_ItemWrappers[index].childrenIds : null; } /// - /// Gets the child index under the parent of the item with the specified id. + /// Gets the child index under the parent of the item with the specified ID. /// - /// The item id. + /// The item ID. /// The child index under the parent. Returns -1 if the item has no parent or doesn't exist in the tree. public int GetChildIndexForId(int id) { @@ -292,26 +308,58 @@ public int GetChildIndexForId(int id) return -1; } - int GetIndentationDepth(int index) + /// + /// Returns the depth of the element at that ID. + /// + /// The item ID. + /// The depth of the element. + internal int GetIndentationDepth(int id) { - return IsIndexValid(index) ? m_ItemWrappers[index].depth : 0; + var depth = 0; + var parentId = GetParentId(id); + while (parentId != -1) + { + parentId = GetParentId(parentId); + depth++; + } + + return depth; } /// - /// Returns whether or not the item with the specified id is expanded in the tree. + /// Return the depth of the element at that index. /// - /// The item id - /// Whether or not the item with the specified id is expanded in the tree. + /// The item index. + /// The depth of the element. + internal int GetIndentationDepthByIndex(int index) + { + var id = GetIdForIndex(index); + return GetIndentationDepth(id); + } + + /// + /// Determines whether the item with the specified ID can be expanded or collapsed. + /// + internal virtual bool CanChangeExpandedState(int id) + { + return true; + } + + /// + /// Return whether the item with the specified ID is expanded in the tree. + /// + /// The item ID + /// Whether the item with the specified ID is expanded in the tree. public bool IsExpanded(int id) { return baseTreeView.expandedItemIds.Contains(id); } /// - /// Returns whether or not the item with the specified index is expanded in the tree. + /// Return whether the item with the specified index is expanded in the tree. /// /// The item index - /// Whether or not the item with the specified id is expanded in the tree. Will return false if the index is not valid. + /// Whether the item with the specified index is expanded in the tree. Will return false if the index is not valid. public bool IsExpandedByIndex(int index) { if (!IsIndexValid(index)) @@ -320,28 +368,34 @@ public bool IsExpandedByIndex(int index) return IsExpanded(m_ItemWrappers[index].id); } + static readonly ProfilerMarker K_ExpandItemByIndex = new ProfilerMarker(ProfilerCategory.Scripts, "BaseTreeViewController.ExpandItemByIndex"); /// /// Expands the item with the specified index, making his children visible. Allows to expand the whole hierarchy under that item. /// /// The item index. - /// Whether or not to expand the whole hierarchy under that item. + /// Whether the whole hierarchy under that item will be expanded. /// Whether to refresh items or not. Set to false when doing multiple operations on the tree, to only do one RefreshItems once all operations are done. public void ExpandItemByIndex(int index, bool expandAllChildren, bool refresh = true) { + using var marker = K_ExpandItemByIndex.Auto(); if (!HasChildrenByIndex(index)) return; - if (!baseTreeView.expandedItemIds.Contains(GetIdForIndex(index)) || expandAllChildren) + var id = GetIdForIndex(index); + if (!CanChangeExpandedState(id)) + return; + + if (!baseTreeView.expandedItemIds.Contains(id) || expandAllChildren) { var childrenIds = GetChildrenIdsByIndex(index); var childrenIdsList = new List(); foreach (var childId in childrenIds) { - if (m_ItemWrappers.All(x => x.id != childId)) + if (!m_TreeItemIdsWithItemWrappers.Contains(childId)) childrenIdsList.Add(childId); } - CreateWrappers(childrenIdsList, GetIndentationDepth(index) + 1, + CreateWrappers(childrenIdsList, GetIndentationDepth(id) + 1, ref m_WrapperInsertionList); m_ItemWrappers.InsertRange(index + 1, m_WrapperInsertionList); if (!baseTreeView.expandedItemIds.Contains(m_ItemWrappers[index].id)) @@ -351,7 +405,6 @@ public void ExpandItemByIndex(int index, bool expandAllChildren, bool refresh = if (expandAllChildren) { - var id = GetIdForIndex(index); var childrenIds = GetChildrenIds(id); foreach (var childId in GetAllItemIds(childrenIds)) if (!baseTreeView.expandedItemIds.Contains(childId)) @@ -363,14 +416,14 @@ public void ExpandItemByIndex(int index, bool expandAllChildren, bool refresh = } /// - /// Expands the item with the specified id, making its children visible. Allows to expand the whole hierarchy under that item. + /// Expands the item with the specified ID, making its children visible. Allows to expand the whole hierarchy under that item. /// - /// The item id. - /// Whether or not to expand the whole hierarchy under that item. + /// The item ID. + /// Whether the whole hierarchy under that item will be expanded. /// Whether to refresh items or not. Set to false when doing multiple operations on the tree, to only do one RefreshItems once all operations are done. public void ExpandItem(int id, bool expandAllChildren, bool refresh = true) { - if (!HasChildren(id)) + if (!HasChildren(id) || !CanChangeExpandedState(id)) return; // Try to find it in the currently visible list. @@ -392,51 +445,66 @@ public void ExpandItem(int id, bool expandAllChildren, bool refresh = true) /// Collapses the item with the specified index, hiding its children. Allows to collapse the whole hierarchy under that item. /// /// The item index. - /// Whether or not to collapse the whole hierarchy under that item. + /// Whether the whole hierarchy under that item will be collapsed. public void CollapseItemByIndex(int index, bool collapseAllChildren) { if (!HasChildrenByIndex(index)) return; + var id = GetIdForIndex(index); + if (!CanChangeExpandedState(id)) + return; + if (collapseAllChildren) { - var id = GetIdForIndex(index); var childrenIds = GetChildrenIds(id); foreach (var childId in GetAllItemIds(childrenIds)) baseTreeView.expandedItemIds.Remove(childId); } - baseTreeView.expandedItemIds.Remove(GetIdForIndex(index)); + baseTreeView.expandedItemIds.Remove(id); var recursiveChildCount = 0; var currentIndex = index + 1; - var currentDepth = GetIndentationDepth(index); - while (currentIndex < m_ItemWrappers.Count && GetIndentationDepth(currentIndex) > currentDepth) + var currentDepth = GetIndentationDepthByIndex(index); + while (currentIndex < m_ItemWrappers.Count && GetIndentationDepthByIndex(currentIndex) > currentDepth) { recursiveChildCount++; currentIndex++; } + var end = index + 1 + recursiveChildCount; + for (int i = index + 1; i < end; i++) + { + m_TreeItemIdsWithItemWrappers.Remove(m_ItemWrappers[i].id); + } m_ItemWrappers.RemoveRange(index + 1, recursiveChildCount); - baseTreeView.RefreshItems(); } /// - /// Collapses the item with the specified id, hiding its children. Allows to collapse the whole hierarchy under that item. + /// Collapses the item with the specified ID, hiding its children. Allows to collapse the whole hierarchy under that item. /// - /// The item id. - /// Whether or not to collapse the whole hierarchy under that item. + /// The item ID. + /// Whether the whole hierarchy under that item will be collapsed. public void CollapseItem(int id, bool collapseAllChildren) { + if (!CanChangeExpandedState(id)) + return; + // Try to find it in the currently visible list. for (var i = 0; i < m_ItemWrappers.Count; ++i) + { if (m_ItemWrappers[i].id == id) + { if (IsExpandedByIndex(i)) { CollapseItemByIndex(i, collapseAllChildren); return; } + break; + } + } if (!baseTreeView.expandedItemIds.Contains(id)) return; @@ -450,8 +518,13 @@ public void CollapseItem(int id, bool collapseAllChildren) public void ExpandAll() { foreach (var itemId in GetAllItemIds()) + { + if (!CanChangeExpandedState(itemId)) + continue; + if (!baseTreeView.expandedItemIds.Contains(itemId)) baseTreeView.expandedItemIds.Add(itemId); + } RegenerateWrappers(); baseTreeView.RefreshItems(); @@ -465,7 +538,20 @@ public void CollapseAll() if (baseTreeView.expandedItemIds.Count == 0) return; - baseTreeView.expandedItemIds.Clear(); + using (ListPool.Get(out var list)) + { + foreach (var itemId in baseTreeView.expandedItemIds) + { + if (!CanChangeExpandedState(itemId)) + { + list.Add(itemId); + } + } + + baseTreeView.expandedItemIds.Clear(); + baseTreeView.expandedItemIds.AddRange(list); + } + RegenerateWrappers(); baseTreeView.RefreshItems(); } @@ -473,6 +559,7 @@ public void CollapseAll() internal void RegenerateWrappers() { m_ItemWrappers.Clear(); + m_TreeItemIdsWithItemWrappers.Clear(); var rootItemIds = GetRootItemIds(); if (rootItemIds == null) @@ -482,15 +569,21 @@ internal void RegenerateWrappers() SetItemsSourceWithoutNotify(m_ItemWrappers); } + static readonly ProfilerMarker k_CreateWrappers = new ProfilerMarker("BaseTreeViewController.CreateWrappers"); void CreateWrappers(IEnumerable treeViewItemIds, int depth, ref List wrappers) { - if (treeViewItemIds == null || wrappers == null) + using var marker = k_CreateWrappers.Auto(); + if (treeViewItemIds == null || wrappers == null || m_TreeItemIdsWithItemWrappers == null) return; foreach (var id in treeViewItemIds) { - var wrapper = new TreeViewItemWrapper(m_TreeItems[id], depth); + if (!m_TreeItems.TryGetValue(id, out var treeItem)) + continue; + + var wrapper = new TreeViewItemWrapper(treeItem, depth); wrappers.Add(wrapper); + m_TreeItemIdsWithItemWrappers.Add(id); if (baseTreeView?.expandedItemIds == null) continue; @@ -504,5 +597,10 @@ bool IsIndexValid(int index) { return index >= 0 && index < m_ItemWrappers.Count; } + + internal void RaiseItemParentChanged(int id, int newParentId) + { + RaiseItemIndexChanged(id, newParentId); + } } } diff --git a/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/CollectionViewController.cs b/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/CollectionViewController.cs index 3d96ea70fb..018a1dea27 100644 --- a/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/CollectionViewController.cs +++ b/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/CollectionViewController.cs @@ -38,7 +38,19 @@ public virtual IList itemsSource { if (m_ItemsSource == value) return; + + var hadValidDataAndBindings = m_View.HasValidDataAndBindings(); + m_ItemsSource = value; + + // We need to make sure to RefreshItems if itemsSource was set last, but only when we're not using + // ListViewBindings, that does the refresh already when the SerializedProperty is ready. + if (!hadValidDataAndBindings && m_View.HasValidDataAndBindings() && + m_View.GetProperty(BaseVerticalCollectionView.internalBindingKey) == null) + { + m_View.RefreshItems(); + } + RaiseItemsSourceChanged(); } } @@ -116,11 +128,11 @@ public virtual int GetIndexForId(int id) /// For example, the index will be different from the id in a tree. public virtual int GetIdForIndex(int index) { - return m_View.getItemId?.Invoke(index) ?? index; + return index; } /// - /// Returns the item for the specified index. + /// Returns the item with the specified index. /// /// The item index. /// The object in the source at this index. @@ -135,6 +147,23 @@ public virtual object GetItemForIndex(int index) return m_ItemsSource[index]; } + /// + /// Returns the item with the specified ID. + /// + /// The item ID. + /// The object in the source with this ID. + internal virtual object GetItemForId(int id) + { + if (m_ItemsSource == null) + return null; + + var index = GetIndexForId(id); + if (index < 0 || index >= m_ItemsSource.Count) + return null; + + return m_ItemsSource[index]; + } + internal virtual void InvokeMakeItem(ReusableCollectionItem reusableItem) { reusableItem.Init(MakeItem()); diff --git a/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/DefaultMultiColumnTreeViewController.cs b/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/DefaultMultiColumnTreeViewController.cs new file mode 100644 index 0000000000..bc6ec84a42 --- /dev/null +++ b/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/DefaultMultiColumnTreeViewController.cs @@ -0,0 +1,194 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace UnityEngine.UIElements +{ + /// + /// Default implementation of a . + /// + /// The data type. + internal class DefaultMultiColumnTreeViewController : MultiColumnTreeViewController, IDefaultTreeViewController + { + TreeDataController m_TreeDataController; + TreeDataController treeDataController => m_TreeDataController ??= new TreeDataController(); + + /// + /// The constructor for DefaultMultiColumnTreeViewController. + /// + /// The columns data used to initialize the header. + /// The sort data used to initialize the header. + /// The sorted columns for the view. + public DefaultMultiColumnTreeViewController(Columns columns, SortColumnDescriptions sortDescriptions, List sortedColumns) + : base(columns, sortDescriptions, sortedColumns) {} + + /// + public override IList itemsSource + { + get => base.itemsSource; + set + { + if (value == null) + { + SetRootItems(null); + } + else if (value is IList> dataList) + { + SetRootItems(dataList); + } + else + { + Debug.LogError($"Type does not match this tree view controller's data type ({typeof(T)})."); + } + } + } + + /// + /// Sets the root items. + /// + /// + /// Root items can include their children directly. + /// + /// The TreeView root items. + public void SetRootItems(IList> items) + { + if (items == base.itemsSource) + return; + + treeDataController.SetRootItems(items); + RebuildTree(); + RaiseItemsSourceChanged(); + } + + /// + /// Adds an item to the tree. + /// + /// Item to add. + /// The parent id for the item. + /// The child index in the parent's children list. + /// Whether the tree data should be rebuilt right away. Call when false. + public virtual void AddItem(in TreeViewItemData item, int parentId, int childIndex, bool rebuildTree = true) + { + treeDataController.AddItem(item, parentId, childIndex); + + if (rebuildTree) + RebuildTree(); + } + + /// + /// Gets the tree item data for the specified TreeView item ID. + /// + /// The TreeView item ID. + /// Type of the data inside TreeViewItemData. + /// The tree item data. + public virtual TreeViewItemData GetTreeViewItemDataForId(int id) + { + return treeDataController.GetTreeItemDataForId(id); + } + + /// + /// Gets the tree item data for the specified TreeView item index. + /// + /// The TreeView item index. + /// Type of the data inside TreeViewItemData. + /// The tree item data. + public virtual TreeViewItemData GetTreeViewItemDataForIndex(int index) + { + var itemId = GetIdForIndex(index); + return treeDataController.GetTreeItemDataForId(itemId); + } + + /// + public override bool TryRemoveItem(int id, bool rebuildTree = true) + { + if (treeDataController.TryRemoveItem(id)) + { + if (rebuildTree) + RebuildTree(); + + return true; + } + + return false; + } + + /// + /// Gets data for the specified TreeView item ID. + /// + /// The TreeView item ID. + /// Type of the data inside TreeViewItemData. + /// The data. + public T GetDataForId(int id) + { + return treeDataController.GetDataForId(id); + } + + /// + /// Gets data for the specified TreeView item index. + /// + /// The TreeView item index. + /// Type of the data inside TreeViewItemData. + /// The data. + public T GetDataForIndex(int index) + { + return treeDataController.GetDataForId(GetIdForIndex(index)); + } + + /// + public override object GetItemForIndex(int index) + { + return treeDataController.GetDataForId(GetIdForIndex(index)); + } + + /// + public override int GetParentId(int id) + { + return treeDataController.GetParentId(id); + } + + /// + public override bool HasChildren(int id) + { + return treeDataController.HasChildren(id); + } + + /// + public override IEnumerable GetChildrenIds(int id) + { + return treeDataController.GetChildrenIds(id); + } + + /// + public override void Move(int id, int newParentId, int childIndex = -1, bool rebuildTree = true) + { + if (id == newParentId) + return; + + if (IsChildOf(newParentId, id)) + return; + + treeDataController.Move(id, newParentId, childIndex); + + if (rebuildTree) + { + RebuildTree(); + RaiseItemIndexChanged(id, newParentId); + } + } + + bool IsChildOf(int childId, int id) + { + return treeDataController.IsChildOf(childId, id); + } + + /// + public override IEnumerable GetAllItemIds(IEnumerable rootIds = null) + { + return treeDataController.GetAllItemIds(rootIds); + } + } +} diff --git a/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/DefaultTreeViewController.cs b/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/DefaultTreeViewController.cs new file mode 100644 index 0000000000..f8eca77355 --- /dev/null +++ b/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/DefaultTreeViewController.cs @@ -0,0 +1,196 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace UnityEngine.UIElements +{ + /// + /// Default implementation of a . + /// + /// The data type. + internal class DefaultTreeViewController : TreeViewController, IDefaultTreeViewController, IDefaultTreeViewController + { + TreeDataController m_TreeDataController; + TreeDataController treeDataController => m_TreeDataController ??= new TreeDataController(); + + /// + public override IList itemsSource + { + get => base.itemsSource; + set + { + if (value == null) + { + SetRootItems(null); + } + else if (value is IList> dataList) + { + SetRootItems(dataList); + } + else + { + Debug.LogError($"Type does not match this tree view controller's data type ({typeof(T)})."); + } + } + } + + /// + /// Sets the root items. + /// + /// + /// Root items can include their children directly. + /// + /// The TreeView root items. + public void SetRootItems(IList> items) + { + if (items == base.itemsSource) + return; + + treeDataController.SetRootItems(items); + RebuildTree(); + RaiseItemsSourceChanged(); + } + + /// + /// Adds an item to the tree. + /// + /// Item to add. + /// The parent id for the item. + /// The child index in the parent's children list. + /// Whether the tree data should be rebuilt right away. Call when false. + public virtual void AddItem(in TreeViewItemData item, int parentId, int childIndex, bool rebuildTree = true) + { + treeDataController.AddItem(item, parentId, childIndex); + + if (rebuildTree) + RebuildTree(); + } + + /// + public override bool TryRemoveItem(int id, bool rebuildTree = true) + { + if (treeDataController.TryRemoveItem(id)) + { + if (rebuildTree) + RebuildTree(); + + return true; + } + + return false; + } + + /// + /// Gets the tree item data for the specified TreeView item ID. + /// + /// The TreeView item ID. + /// Type of the data inside TreeViewItemData. + /// The tree item data. + public virtual object GetItemDataForId(int id) + { + return treeDataController.GetTreeItemDataForId(id).data; + } + + /// + /// Gets the tree item data for the specified TreeView item ID. + /// + /// The TreeView item ID. + /// Type of the data inside TreeViewItemData. + /// The tree item data. + public virtual TreeViewItemData GetTreeViewItemDataForId(int id) + { + return treeDataController.GetTreeItemDataForId(id); + } + + /// + /// Gets the tree item data for the specified TreeView item index. + /// + /// The TreeView item index. + /// Type of the data inside TreeViewItemData. + /// The tree item data. + public virtual TreeViewItemData GetTreeViewItemDataForIndex(int index) + { + var itemId = GetIdForIndex(index); + return treeDataController.GetTreeItemDataForId(itemId); + } + + /// + /// Gets data for the specified TreeView item ID. + /// + /// The TreeView item ID. + /// Type of the data inside TreeViewItemData. + /// The data. + public virtual T GetDataForId(int id) + { + return treeDataController.GetDataForId(id); + } + + /// + /// Gets data for the specified TreeView item index. + /// + /// The TreeView item index. + /// Type of the data inside TreeViewItemData. + /// The data. + public virtual T GetDataForIndex(int index) + { + return treeDataController.GetDataForId(GetIdForIndex(index)); + } + + /// + public override object GetItemForIndex(int index) + { + return treeDataController.GetDataForId(GetIdForIndex(index)); + } + + /// + public override int GetParentId(int id) + { + return treeDataController.GetParentId(id); + } + + /// + public override bool HasChildren(int id) + { + return treeDataController.HasChildren(id); + } + + /// + public override IEnumerable GetChildrenIds(int id) + { + return treeDataController.GetChildrenIds(id); + } + + /// + public override void Move(int id, int newParentId, int childIndex = -1, bool rebuildTree = true) + { + if (id == newParentId) + return; + + if (IsChildOf(newParentId, id)) + return; + + treeDataController.Move(id, newParentId, childIndex); + + if (rebuildTree) + { + RebuildTree(); + RaiseItemParentChanged(id, newParentId); + } + } + + bool IsChildOf(int childId, int id) + { + return treeDataController.IsChildOf(childId, id); + } + + /// + public override IEnumerable GetAllItemIds(IEnumerable rootIds = null) + { + return treeDataController.GetAllItemIds(rootIds); + } + } +} diff --git a/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/IDefaultTreeViewController.cs b/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/IDefaultTreeViewController.cs index 85f99e8f5c..f613574a30 100644 --- a/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/IDefaultTreeViewController.cs +++ b/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/IDefaultTreeViewController.cs @@ -6,6 +6,11 @@ namespace UnityEngine.UIElements { + internal interface IDefaultTreeViewController + { + object GetItemDataForId(int id); + } + internal interface IDefaultTreeViewController { void SetRootItems(IList> items); diff --git a/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/MultiColumnTreeViewController.cs b/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/MultiColumnTreeViewController.cs index 76a649c647..80e42f3411 100644 --- a/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/MultiColumnTreeViewController.cs +++ b/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/MultiColumnTreeViewController.cs @@ -3,7 +3,6 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; -using System.Collections; using System.Collections.Generic; using UnityEngine.UIElements.Internal; @@ -87,131 +86,4 @@ public override void Dispose() base.Dispose(); } } - - class DefaultMultiColumnTreeViewController : MultiColumnTreeViewController, IDefaultTreeViewController - { - TreeDataController m_TreeDataController; - TreeDataController treeDataController => m_TreeDataController ??= new TreeDataController(); - - public DefaultMultiColumnTreeViewController(Columns columns, SortColumnDescriptions sortDescriptions, List sortedColumns) - : base(columns, sortDescriptions, sortedColumns) {} - - public override IList itemsSource - { - get => base.itemsSource; - set - { - if (value == null) - { - SetRootItems(null); - } - else if (value is IList> dataList) - { - SetRootItems(dataList); - } - else - { - Debug.LogError($"Type does not match this tree view controller's data type ({typeof(T)})."); - } - } - } - - public void SetRootItems(IList> items) - { - if (items == base.itemsSource) - return; - - treeDataController.SetRootItems(items); - RebuildTree(); - RaiseItemsSourceChanged(); - } - - public void AddItem(in TreeViewItemData item, int parentId, int childIndex, bool rebuildTree = true) - { - treeDataController.AddItem(item, parentId, childIndex); - - if (rebuildTree) - RebuildTree(); - } - - public TreeViewItemData GetTreeViewItemDataForId(int id) - { - return treeDataController.GetTreeItemDataForId(id); - } - - public TreeViewItemData GetTreeViewItemDataForIndex(int index) - { - var itemId = GetIdForIndex(index); - return treeDataController.GetTreeItemDataForId(itemId); - } - - public override bool TryRemoveItem(int id, bool rebuildTree = true) - { - if (treeDataController.TryRemoveItem(id)) - { - if (rebuildTree) - RebuildTree(); - - return true; - } - - return false; - } - - public T GetDataForId(int id) - { - return treeDataController.GetDataForId(id); - } - - public T GetDataForIndex(int index) - { - return treeDataController.GetDataForId(GetIdForIndex(index)); - } - - public override object GetItemForIndex(int index) - { - return treeDataController.GetDataForId(GetIdForIndex(index)); - } - - public override int GetParentId(int id) - { - return treeDataController.GetParentId(id); - } - - public override bool HasChildren(int id) - { - return treeDataController.HasChildren(id); - } - - public override IEnumerable GetChildrenIds(int id) - { - return treeDataController.GetChildrenIds(id); - } - - public override void Move(int id, int newParentId, int childIndex = -1, bool rebuildTree = true) - { - if (id == newParentId) - return; - - if (IsChildOf(newParentId, id)) - return; - - treeDataController.Move(id, newParentId, childIndex); - - if (rebuildTree) - RebuildTree(); - - RaiseItemIndexChanged(id, newParentId); - } - - bool IsChildOf(int childId, int id) - { - return treeDataController.IsChildOf(childId, id); - } - - public override IEnumerable GetAllItemIds(IEnumerable rootIds = null) - { - return treeDataController.GetAllItemIds(rootIds); - } - } } diff --git a/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/TreeViewController.cs b/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/TreeViewController.cs index 5ff485d061..f94509890f 100644 --- a/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/TreeViewController.cs +++ b/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/TreeViewController.cs @@ -3,8 +3,6 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; -using System.Collections; -using System.Collections.Generic; using System.Linq; namespace UnityEngine.UIElements @@ -59,129 +57,13 @@ protected override void DestroyItem(VisualElement element) { treeView.destroyItem?.Invoke(element); } - } - - internal sealed class DefaultTreeViewController : TreeViewController, IDefaultTreeViewController - { - TreeDataController m_TreeDataController; - TreeDataController treeDataController => m_TreeDataController ??= new TreeDataController(); - - public override IList itemsSource - { - get => base.itemsSource; - set - { - if (value == null) - { - SetRootItems(null); - } - else if (value is IList> dataList) - { - SetRootItems(dataList); - } - else - { - Debug.LogError($"Type does not match this tree view controller's data type ({typeof(T)})."); - } - } - } - - public void SetRootItems(IList> items) - { - if (items == base.itemsSource) - return; - - treeDataController.SetRootItems(items); - RebuildTree(); - RaiseItemsSourceChanged(); - } - - public void AddItem(in TreeViewItemData item, int parentId, int childIndex, bool rebuildTree = true) - { - treeDataController.AddItem(item, parentId, childIndex); - if (rebuildTree) - RebuildTree(); - } - - public override bool TryRemoveItem(int id, bool rebuildTree = true) + internal override object GetItemForId(int id) { - if (treeDataController.TryRemoveItem(id)) - { - if (rebuildTree) - RebuildTree(); - - return true; - } - - return false; - } + if (this is IDefaultTreeViewController defaultController) + return defaultController.GetItemDataForId(id); - public TreeViewItemData GetTreeViewItemDataForId(int id) - { - return treeDataController.GetTreeItemDataForId(id); - } - - public TreeViewItemData GetTreeViewItemDataForIndex(int index) - { - var itemId = GetIdForIndex(index); - return treeDataController.GetTreeItemDataForId(itemId); - } - - public T GetDataForId(int id) - { - return treeDataController.GetDataForId(id); - } - - public T GetDataForIndex(int index) - { - return treeDataController.GetDataForId(GetIdForIndex(index)); - } - - public override object GetItemForIndex(int index) - { - return treeDataController.GetDataForId(GetIdForIndex(index)); - } - - public override int GetParentId(int id) - { - return treeDataController.GetParentId(id); - } - - public override bool HasChildren(int id) - { - return treeDataController.HasChildren(id); - } - - public override IEnumerable GetChildrenIds(int id) - { - return treeDataController.GetChildrenIds(id); - } - - public override void Move(int id, int newParentId, int childIndex = -1, bool rebuildTree = true) - { - if (id == newParentId) - return; - - if (IsChildOf(newParentId, id)) - return; - - treeDataController.Move(id, newParentId, childIndex); - - if (rebuildTree) - RebuildTree(); - - RaiseItemIndexChanged(id, newParentId); - } - - bool IsChildOf(int childId, int id) - { - return treeDataController.IsChildOf(childId, id); - } - - public override IEnumerable GetAllItemIds(IEnumerable rootIds = null) - { - return treeDataController.GetAllItemIds(rootIds); + return base.GetItemForId(id); } } } diff --git a/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/CollectionVirtualizationController.cs b/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/CollectionVirtualizationController.cs index d03205647d..1824a4d2e3 100644 --- a/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/CollectionVirtualizationController.cs +++ b/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/CollectionVirtualizationController.cs @@ -20,7 +20,7 @@ protected CollectionVirtualizationController(ScrollView scrollView) public abstract void Refresh(bool rebuild); public abstract void ScrollToItem(int id); - public abstract void Resize(Vector2 size, int layoutPass); + public abstract void Resize(Vector2 size); public abstract void OnScroll(Vector2 offset); public abstract int GetIndexFromPosition(Vector2 position); public abstract float GetExpectedItemHeight(int index); @@ -30,6 +30,8 @@ protected CollectionVirtualizationController(ScrollView scrollView) public abstract void UpdateBackground(); public abstract IEnumerable activeItems { get; } - public abstract void ReplaceActiveItem(int index); + + internal abstract void StartDragItem(ReusableCollectionItem item); + internal abstract void EndDrag(int dropIndex); } } diff --git a/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/DynamicHeightVirtualizationController.cs b/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/DynamicHeightVirtualizationController.cs index 6faa54f01f..668e462059 100644 --- a/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/DynamicHeightVirtualizationController.cs +++ b/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/DynamicHeightVirtualizationController.cs @@ -22,11 +22,10 @@ public ContentHeightCacheInfo(float sum, int count) } } - const int k_AdditionalItems = 5; int m_HighestCachedIndex = -1; - readonly Dictionary m_ItemHeightCache = new Dictionary(32); - readonly Dictionary m_ContentHeightCache = new Dictionary(32); - readonly HashSet m_WaitingCache = new HashSet(32); + readonly Dictionary m_ItemHeightCache = new (32); + readonly Dictionary m_ContentHeightCache = new (32); + readonly HashSet m_WaitingCache = new (32); // Internal for tests. internal IReadOnlyDictionary itemHeightCache => m_ItemHeightCache; @@ -34,69 +33,159 @@ public ContentHeightCacheInfo(float sum, int count) int m_ForcedFirstVisibleItem = -1; int m_ForcedLastVisibleItem = -1; bool m_StickToBottom; + VirtualizationChange m_LastChange; + ScrollDirection m_ScrollDirection; + Vector2 m_DelayedScrollOffset = Vector2.negativeInfinity; + + enum VirtualizationChange + { + /// + /// We're back in an idle state. + /// + None, + /// + /// Viewport was resized. + /// + Resize, + /// + /// Regular scroll changes, through mouse wheel or scroller changes. + /// + Scroll, + /// + /// ScrollToItem was called. + /// + ForcedScroll, + } + + enum ScrollDirection + { + Idle, + Up, + Down, + } - float m_AverageHeight = BaseVerticalCollectionView.s_DefaultItemHeight; float m_AccumulatedHeight; + float m_MinimumItemHeight = -1; + + float defaultExpectedHeight + { + get + { + if (m_MinimumItemHeight > 0) + return m_MinimumItemHeight; + + if (m_CollectionView.m_ItemHeightIsInline && m_CollectionView.fixedItemHeight > 0) + return m_CollectionView.fixedItemHeight; + + return BaseVerticalCollectionView.s_DefaultItemHeight; + } + } + + float contentPadding + { + get => serializedData.contentPadding; + set + { + m_CollectionView.scrollView.contentContainer.style.paddingTop = value; + serializedData.contentPadding = value; + m_CollectionView.SaveViewData(); + } + } + + float contentHeight + { + get => serializedData.contentHeight; + set + { + m_CollectionView.scrollView.contentContainer.style.height = value; + serializedData.contentHeight = value; + m_CollectionView.SaveViewData(); + } + } + + int anchoredIndex + { + get => serializedData.anchoredItemIndex; + set + { + serializedData.anchoredItemIndex = value; + m_CollectionView.SaveViewData(); + } + } - float storedPadding + float anchorOffset { - get => m_CollectionView.serializedVirtualizationData.storedPadding; + get => serializedData.anchorOffset; set { - m_CollectionView.serializedVirtualizationData.storedPadding = value; + serializedData.anchorOffset = value; m_CollectionView.SaveViewData(); } } + float viewportMaxOffset => serializedData.scrollOffset.y + m_ScrollView.contentViewport.layout.height; + Action m_FillCallback; + Action m_ScrollCallback; + Action m_ScrollResetCallback; Action m_GeometryChangedCallback; IVisualElementScheduledItem m_ScheduledItem; - IVisualElementScheduledItem m_ScrollbarsScheduledItem; - Action m_UpdateScrollbarsCallback; + IVisualElementScheduledItem m_ScrollScheduledItem; + IVisualElementScheduledItem m_ScrollResetScheduledItem; Predicate m_IndexOutOfBoundsPredicate; + // Dynamic height virtualization handles the refresh binding with the scheduled Fill call. + protected override bool alwaysRebindOnRefresh => false; + public DynamicHeightVirtualizationController(BaseVerticalCollectionView collectionView) : base(collectionView) { m_FillCallback = Fill; + m_ScrollCallback = OnScrollUpdate; m_GeometryChangedCallback = OnRecycledItemGeometryChanged; m_IndexOutOfBoundsPredicate = IsIndexOutOfBounds; - m_UpdateScrollbarsCallback = UpdateScrollViewScrollers; + m_ScrollResetCallback = ResetScroll; } public override void Refresh(bool rebuild) { CleanItemHeightCache(); + var previousActiveItemsCount = m_ActiveItems.Count; + var needsApply = false; + if (rebuild) { m_WaitingCache.Clear(); } else { - m_WaitingCache.RemoveWhere(m_IndexOutOfBoundsPredicate); - - // Update item height of active items to support index changes (reordering). - foreach (var item in m_ActiveItems) - { - if (IsIndexOutOfBounds(item.index) || item.rootElement.style.display == DisplayStyle.None) - continue; - - UpdateRegisteredHeight(item); - } + needsApply |= m_WaitingCache.RemoveWhere(m_IndexOutOfBoundsPredicate) > 0; } base.Refresh(rebuild); + m_ScrollDirection = ScrollDirection.Idle; + m_LastChange = VirtualizationChange.None; + if (m_CollectionView.HasValidDataAndBindings()) { - m_ScheduledItem ??= m_CollectionView.schedule.Execute(m_FillCallback); + if (needsApply || previousActiveItemsCount != m_ActiveItems.Count) + { + contentHeight = GetExpectedContentHeight(); + var scrollableHeight = Mathf.Max(0, contentHeight - m_ScrollView.contentViewport.layout.height); + m_ScrollView.verticalScroller.slider.SetHighValueWithoutNotify(scrollableHeight); + m_ScrollView.verticalScroller.value = serializedData.scrollOffset.y; + serializedData.scrollOffset.y = m_ScrollView.verticalScroller.value; + } + + ScheduleFill(); } } public override void ScrollToItem(int index) { - if (visibleItemCount == 0 || index < -1) + if (index < ReusableCollectionItem.UndefinedIndex) return; var currentContentHeight = m_ScrollView.contentContainer.layout.height; @@ -106,207 +195,285 @@ public override void ScrollToItem(int index) { // Scroll to last item m_ForcedLastVisibleItem = itemsCount - 1; + m_ForcedFirstVisibleItem = ReusableCollectionItem.UndefinedIndex; m_StickToBottom = true; m_ScrollView.scrollOffset = new Vector2(0, viewportHeight >= currentContentHeight ? 0 : currentContentHeight); } else if (firstVisibleIndex >= index) { m_ForcedFirstVisibleItem = index; + m_ForcedLastVisibleItem = ReusableCollectionItem.UndefinedIndex; m_ScrollView.scrollOffset = new Vector2(0, GetContentHeightForIndex(index - 1)); } else // index > first { var itemOffset = GetContentHeightForIndex(index); - if (itemOffset < storedPadding + viewportHeight) + if (itemOffset < contentPadding + viewportHeight) return; - m_ForcedLastVisibleItem = index; - var yScrollOffset = itemOffset - viewportHeight + BaseVerticalCollectionView.s_DefaultItemHeight; - + m_ForcedLastVisibleItem = index; + m_ForcedFirstVisibleItem = ReusableCollectionItem.UndefinedIndex; m_ScrollView.scrollOffset = new Vector2(0, yScrollOffset); } } - public override void Resize(Vector2 size, int layoutPass) + public override void Resize(Vector2 size) { - var contentHeight = GetExpectedContentHeight(); - m_ScrollView.contentContainer.style.height = contentHeight; - - // Recalculate offset - var firstItemPadding = GetContentHeightForIndex(firstVisibleIndex - 1); - - var previousOffset = m_CollectionView.serializedVirtualizationData.scrollOffset.y; - var previousPadding = storedPadding; - var deltaOffset = previousOffset - previousPadding; - var offset = firstItemPadding + deltaOffset; + var expectedContentHeight = GetExpectedContentHeight(); + contentHeight = Mathf.Max(expectedContentHeight, contentHeight); // Restore scroll offset and preemptively update the highValue // in case this is the initial restore from persistent data and // the ScrollView's OnGeometryChanged() didn't update the low // and highValues. - var scrollableHeight = Mathf.Max(0, contentHeight - m_ScrollView.contentViewport.layout.height); - var scrollOffset = Mathf.Min(offset, scrollableHeight); + var viewportHeight = m_ScrollView.contentViewport.layout.height; + var scrollableHeight = Mathf.Max(0, contentHeight - viewportHeight); + var scrollOffset = Mathf.Min(serializedData.scrollOffset.y, scrollableHeight); m_ScrollView.verticalScroller.slider.SetHighValueWithoutNotify(scrollableHeight); - m_ScrollView.verticalScroller.value = scrollOffset; - m_CollectionView.serializedVirtualizationData.scrollOffset.y = m_ScrollView.verticalScroller.value; - m_ScrollView.contentContainer.style.paddingTop = firstItemPadding; - storedPadding = firstItemPadding; + m_ScrollView.verticalScroller.slider.SetValueWithoutNotify(scrollOffset); + serializedData.scrollOffset.y = m_ScrollView.verticalScroller.value; + + // We virtualize a number of items based on the smallest expected item height. + var resolvedViewportHeight = m_CollectionView.ResolveItemHeight(size.y); + var itemCountFromHeight = Mathf.CeilToInt(resolvedViewportHeight / defaultExpectedHeight); + var expectedItemCount = itemCountFromHeight; + if (expectedItemCount <= 0) + return; - if (layoutPass == 0) - { - Fill(); - OnScroll(new Vector2(0, scrollOffset)); - } - else if (m_ScheduledItem == null) + expectedItemCount += k_ExtraVisibleItems; + + var itemCount = Mathf.Min(expectedItemCount, itemsCount); + + if (m_ActiveItems.Count != itemCount) { - m_ScheduledItem = m_CollectionView.schedule.Execute(m_FillCallback); + var initialItemCount = m_ActiveItems.Count; + if (initialItemCount > itemCount) + { + // Shrink + var removeCount = initialItemCount - itemCount; + for (var i = 0; i < removeCount; i++) + { + var lastIndex = m_ActiveItems.Count - 1; + ReleaseItem(lastIndex); + } + } + else + { + // Grow + var addCount = itemCount - m_ActiveItems.Count; + for (var i = 0; i < addCount; i++) + { + var index = i + firstVisibleIndex + initialItemCount; + var recycledItem = GetOrMakeItemAtIndex(); + + if (IsIndexOutOfBounds(index)) + { + HideItem(m_ActiveItems.Count - 1); + continue; + } + + Setup(recycledItem, index); + MarkWaitingForLayout(recycledItem); + } + } } + + ScheduleFill(); + ScheduleScrollDirectionReset(); + + m_LastChange = VirtualizationChange.Resize; } public override void OnScroll(Vector2 scrollOffset) { - if (float.IsNaN(lastHeight) || float.IsNaN(scrollOffset.y)) + if (m_DelayedScrollOffset == scrollOffset) return; - var offset = scrollOffset.y; - var contentHeight = GetExpectedContentHeight(); - var maxOffset = Mathf.Max(0, contentHeight - m_ScrollView.contentViewport.layout.height); - var scrollableHeight = m_ScrollView.contentContainer.boundingBox.height - m_ScrollView.contentViewport.layout.height; - m_CollectionView.serializedVirtualizationData.scrollOffset.y = Mathf.Min(offset, maxOffset); + m_DelayedScrollOffset = scrollOffset; - if (scrollOffset.y == 0) + // ScrollToItem forced a new offset, let's scroll right away, it will be our new saved state. + if (m_ForcedFirstVisibleItem != -1 || m_ForcedLastVisibleItem != -1) { - m_ForcedFirstVisibleItem = 0; + OnScrollUpdate(); + m_LastChange = VirtualizationChange.ForcedScroll; + return; } - else + + // As we resize, the geometry changed event of the ScrollView tries to change the scroll offset as it + // updates the scrollers. We want to keep our cached values, so we reassign them and early out until we're + // in an idle state where we receive true user input. + if (m_LastChange is VirtualizationChange.Resize or VirtualizationChange.ForcedScroll) + { + var viewportHeight = m_ScrollView.contentViewport.layout.height; + var scrollableHeight = Mathf.Max(0, contentHeight - viewportHeight); + var offset = Mathf.Min(serializedData.scrollOffset.y, scrollableHeight); + m_ScrollView.verticalScroller.slider.SetHighValueWithoutNotify(scrollableHeight); + m_ScrollView.verticalScroller.slider.SetValueWithoutNotify(offset); + serializedData.scrollOffset.y = m_ScrollView.verticalScroller.value; + return; + } + + // Schedule later to allow receiving multiple scroll events in one frame and potentially avoid a few expensive rebind. + ScheduleScroll(); + } + + void OnScrollUpdate() + { + var scrollOffset = float.IsNegativeInfinity(m_DelayedScrollOffset.y) ? serializedData.scrollOffset : m_DelayedScrollOffset; + + if (float.IsNaN(m_ScrollView.contentViewport.layout.height) || float.IsNaN(scrollOffset.y)) + return; + + m_LastChange = VirtualizationChange.Scroll; + var expectedContentHeight = GetExpectedContentHeight(); + + // Keep the serialized value if new content is smaller. Adjustment will come later in Fill or Apply. + contentHeight = Mathf.Max(expectedContentHeight, contentHeight); + m_ScrollDirection = scrollOffset.y < serializedData.scrollOffset.y ? ScrollDirection.Up : ScrollDirection.Down; + var scrollableHeight = Mathf.Max(0, contentHeight - m_ScrollView.contentViewport.layout.height); + + if (scrollOffset.y <= 0) { - m_StickToBottom = scrollableHeight > 0 && Math.Abs(scrollOffset.y - m_ScrollView.verticalScroller.highValue) < float.Epsilon; + m_ForcedFirstVisibleItem = 0; } - var firstIndex = m_ForcedFirstVisibleItem != -1 ? m_ForcedFirstVisibleItem : GetFirstVisibleItem(m_CollectionView.serializedVirtualizationData.scrollOffset.y); + m_StickToBottom = scrollableHeight > 0 && Math.Abs(scrollOffset.y - m_ScrollView.verticalScroller.highValue) < float.Epsilon; + serializedData.scrollOffset = scrollOffset; + m_CollectionView.SaveViewData(); + + var firstIndex = m_ForcedFirstVisibleItem != -1 ? m_ForcedFirstVisibleItem : GetFirstVisibleItem(serializedData.scrollOffset.y); var firstVisiblePadding = GetContentHeightForIndex(firstIndex - 1); + contentPadding = firstVisiblePadding; m_ForcedFirstVisibleItem = -1; if (firstIndex != firstVisibleIndex) { - firstVisibleIndex = firstIndex; + CycleItems(firstIndex); + } + else + { + Fill(); + } - if (m_ActiveItems.Count > 0) - { - var currentFirstVisibleItem = firstVisibleItem; - if (m_StickToBottom || currentFirstVisibleItem == null) - { - // Skip the item swapping. - } - else if (firstVisibleIndex < currentFirstVisibleItem.index) //we're scrolling up - { - //How many do we have to swap back - var count = currentFirstVisibleItem.index - firstVisibleIndex; + ScheduleScrollDirectionReset(); - var inserting = m_ScrollInsertionList; + m_DelayedScrollOffset = Vector2.negativeInfinity; + } - for (var i = 0; i < count && m_ActiveItems.Count > 0; ++i) - { - var last = m_ActiveItems[^1]; - if (last.rootElement.layout.y < m_CollectionView.serializedVirtualizationData.scrollOffset.y + m_ScrollView.contentViewport.layout.height) - break; + void CycleItems(int firstIndex) + { + if (firstIndex == firstVisibleIndex) + return; - inserting.Add(last); - m_ActiveItems.RemoveAt(m_ActiveItems.Count - 1); //we remove from the end + var currentFirstVisibleItem = firstVisibleItem; + contentPadding = GetContentHeightForIndex(firstIndex - 1); + firstVisibleIndex = firstIndex; - last.rootElement.SendToBack(); //We send the element to the top of the list (back in z-order) - } + if (m_ActiveItems.Count > 0) + { + if (currentFirstVisibleItem == null || m_ActiveItems.Count <= Mathf.Abs(firstVisibleIndex - currentFirstVisibleItem.index)) + { + // We're scrolling for more items than the number of visible items, let's just rebind everything + // without cycling them in the scroll view / active items. + } + else if (firstVisibleIndex < currentFirstVisibleItem.index) // we're scrolling up. + { + //How many do we have to swap back. + var count = currentFirstVisibleItem.index - firstVisibleIndex; + var inserting = m_ScrollInsertionList; - m_ActiveItems.InsertRange(0, inserting); - m_ScrollInsertionList.Clear(); - } - else // down + for (var i = 0; i < count; ++i) { - var currentLastVisibleItem = lastVisibleItem; - if (firstVisibleIndex < currentLastVisibleItem.index && !IsIndexOutOfBounds(currentLastVisibleItem.index)) - { - var inserting = m_ScrollInsertionList; - - var checkIndex = 0; - while (firstVisibleIndex > m_ActiveItems[checkIndex].index) - { - var first = m_ActiveItems[checkIndex]; - inserting.Add(first); - checkIndex++; + var last = m_ActiveItems[^1]; + inserting.Insert(0, last); + m_ActiveItems.RemoveAt(m_ActiveItems.Count - 1); // we remove from the end. - first.rootElement.BringToFront(); //We send the element to the bottom of the list (front in z-order) - } - - m_ActiveItems.RemoveRange(0, checkIndex); //we remove them all at once - m_ActiveItems.AddRange(inserting); // add them back to the end - m_ScrollInsertionList.Clear(); - } + last.rootElement.SendToBack(); //We send the element to the top of the list (back in z-order) } - var itemContentOffset = firstVisiblePadding; + m_ActiveItems.InsertRange(0, inserting); + m_ScrollInsertionList.Clear(); + } + else // we're scrolling down + { + var inserting = m_ScrollInsertionList; - //Let's rebind everything - for (var i = 0; i < m_ActiveItems.Count; i++) + var checkIndex = 0; + while (firstVisibleIndex > m_ActiveItems[checkIndex].index) { - var recycledItem = m_ActiveItems[i]; - var index = firstVisibleIndex + i; - var previousIndex = recycledItem.index; - m_WaitingCache.Remove(previousIndex); + var first = m_ActiveItems[checkIndex]; + inserting.Add(first); + checkIndex++; - if (IsIndexOutOfBounds(index)) - { - if (recycledItem.rootElement.style.display == DisplayStyle.Flex) - { - m_StickToBottom = true; - m_ForcedLastVisibleItem = -1; - HideItem(i); - } - - m_WaitingCache.Remove(index); - continue; - } + first.rootElement.BringToFront(); //We send the element to the bottom of the list (front in z-order) + } - var isItemInViewport = itemContentOffset - m_CollectionView.serializedVirtualizationData.scrollOffset.y <= m_ScrollView.contentViewport.layout.height; + m_ActiveItems.RemoveRange(0, checkIndex); //we remove them all at once + m_ActiveItems.AddRange(inserting); // add them back to the end + m_ScrollInsertionList.Clear(); + } - Setup(recycledItem, index); + var itemContentOffset = contentPadding; - if (isItemInViewport) - { - if (index != previousIndex) - { - m_WaitingCache.Add(index); - } - } - else - { - HideItem(i); - } + //Let's rebind everything + for (var i = 0; i < m_ActiveItems.Count; i++) + { + var recycledItem = m_ActiveItems[i]; + var index = firstVisibleIndex + i; + var previousIndex = recycledItem.index; + var wasVisible = recycledItem.rootElement.style.display == DisplayStyle.Flex; + m_WaitingCache.Remove(previousIndex); + + if (IsIndexOutOfBounds(index)) + { + HideItem(i); + continue; + } + + Setup(recycledItem, index); - itemContentOffset += GetExpectedItemHeight(index); + var isItemOutsideViewport = itemContentOffset > viewportMaxOffset; + if (isItemOutsideViewport) + { + HideItem(i); + } + else if (index != previousIndex || !wasVisible) + { + MarkWaitingForLayout(recycledItem); } + + itemContentOffset += GetExpectedItemHeight(index); } } - storedPadding = firstVisiblePadding; - m_ScrollView.contentContainer.style.paddingTop = firstVisiblePadding; - m_ScheduledItem ??= m_CollectionView.schedule.Execute(m_FillCallback); + // Save anchored item. + if (m_LastChange != VirtualizationChange.Resize) + { + UpdateAnchor(); + } + + ScheduleFill(); } bool NeedsFill() { + // No need to fill until we get to a stable state. + if (m_LastChange != VirtualizationChange.None || anchoredIndex < 0) + return false; + var lastItemIndex = lastVisibleItem?.index ?? -1; - var padding = storedPadding; + var contentOffset = contentPadding; - if (padding > m_CollectionView.serializedVirtualizationData.scrollOffset.y) + if (contentOffset > serializedData.scrollOffset.y) return true; for (var i = firstVisibleIndex; i < itemsCount; i++) { - if (padding - m_CollectionView.serializedVirtualizationData.scrollOffset.y > m_ScrollView.contentViewport.layout.height) + if (contentOffset >= viewportMaxOffset) break; - padding += GetExpectedItemHeight(i); + contentOffset += GetExpectedItemHeight(i); if (i > lastItemIndex) return true; @@ -320,209 +487,312 @@ void Fill() if (!m_CollectionView.HasValidDataAndBindings()) return; - // Wait for view to scroll first. - var contentHeight = GetExpectedContentHeight(); - if (storedPadding > contentHeight) + if (m_ActiveItems.Count == 0) + { + // Reset the saved content height. + contentHeight = 0; + contentPadding = 0; return; + } - var itemsToAdd = 0; - var lastItemIndex = lastVisibleItem?.index ?? -1; - var firstVisiblePadding = storedPadding; - var padding = firstVisiblePadding; + // Let the UI stabilize. + if (anchoredIndex < 0) + return; - if (m_CollectionView.dragger is ListViewDraggerAnimated dragger && dragger.draggedItem != null) + // Wait for view to scroll first. + if (contentPadding > contentHeight) { - padding -= dragger.draggedItem.rootElement.style.height.value.value; + OnScrollUpdate(); + return; } - // Find how many items we should add to fill the viewport. + var firstVisiblePadding = contentPadding; + var contentOffset = contentPadding; + var activeIndex = 0; + + // Change the visibility of items under the current content to fill the viewport below. for (var i = firstVisibleIndex; i < itemsCount; i++) { - if (padding - m_CollectionView.serializedVirtualizationData.scrollOffset.y > m_ScrollView.contentViewport.layout.height) + if (contentOffset >= viewportMaxOffset) break; + + contentOffset += GetExpectedItemHeight(i); - padding += GetExpectedItemHeight(i); + var item = m_ActiveItems[activeIndex++]; + if (item.index != i || item.rootElement.style.display == DisplayStyle.None) + { + Setup(item, i); + MarkWaitingForLayout(item); + } - if (i > lastItemIndex) - itemsToAdd++; + if (activeIndex >= m_ActiveItems.Count) + { + break; + } } - // Give some leverage to the layout system to avoid coming back here too often. - if (itemsToAdd > 0 && !m_StickToBottom && m_ActiveItems.Count - visibleItemCount < itemsToAdd) - itemsToAdd += k_AdditionalItems; - - // Grow down - var initialVisibleCount = visibleItemCount; - for (var i = 0; i < itemsToAdd; i++) + // Bring back items in front of the current content to fill the viewport above. + if (firstVisibleIndex > 0 && contentPadding > serializedData.scrollOffset.y) { - var index = i + firstVisibleIndex + initialVisibleCount; - if (index >= itemsCount) - break; + var inserting = m_ScrollInsertionList; - var recycledItem = GetOrMakeItemAtIndex(); - m_WaitingCache.Add(index); - Setup(recycledItem, index); - } + for (var i = m_ActiveItems.Count - 1; i >= activeIndex; --i) + { + if (firstVisibleIndex == 0) + break; - // Grow upwards - while (firstVisiblePadding > m_CollectionView.serializedVirtualizationData.scrollOffset.y) - { - var index = firstVisibleIndex - 1; + var last = m_ActiveItems[i]; - if (index < 0) - break; + inserting.Insert(0, last); + m_ActiveItems.RemoveAt(m_ActiveItems.Count - 1); // we remove from the end + last.rootElement.SendToBack(); - var recycledItem = GetOrMakeItemAtIndex(0, 0); - m_WaitingCache.Add(index); - Setup(recycledItem, index); + var newIndex = --firstVisibleIndex; + Setup(last, newIndex); + MarkWaitingForLayout(last); - firstVisiblePadding -= GetExpectedItemHeight(index); - firstVisibleIndex = index; + firstVisiblePadding -= GetExpectedItemHeight(newIndex); + if (firstVisiblePadding < serializedData.scrollOffset.y) + break; + } + + m_ActiveItems.InsertRange(0, inserting); + m_ScrollInsertionList.Clear(); } - m_ScrollView.contentContainer.style.paddingTop = firstVisiblePadding; - storedPadding = firstVisiblePadding; - m_ScheduledItem = null; - } + contentPadding = firstVisiblePadding; + contentHeight = GetExpectedContentHeight(); - public override int GetIndexFromPosition(Vector2 position) - { - var index = 0; - var traversedHeight = 0f; - while (traversedHeight < position.y) + // During resize, we must not update the anchored item, as it may take a while for the UI to stabilize. + if (m_LastChange != VirtualizationChange.Resize) { - traversedHeight += GetExpectedItemHeight(index++); + UpdateAnchor(); } - return index - 1; - } - - float defaultExpectedHeight => m_CollectionView.m_ItemHeightIsInline ? m_CollectionView.fixedItemHeight : m_AverageHeight; - - public override float GetExpectedItemHeight(int index) - { - return m_ItemHeightCache.TryGetValue(index, out var height) ? height : defaultExpectedHeight; - } - - int GetFirstVisibleItem(float offset) - { - if (offset <= 0) - return 0; - - var index = -1; - while (offset > 0) + // After a fill, we want to reapply the dimensions correctly if anything changed. + if (m_WaitingCache.Count == 0) { - index++; - var height = GetExpectedItemHeight(index); - offset -= height; + ResetScroll(); + ApplyScrollViewUpdate(true); } - - return index; } - void UpdateScrollViewContainer(int index, float previousHeight, float newHeight) + void UpdateScrollViewContainer(float previousHeight, float newHeight) { - var previousOffset = m_CollectionView.serializedVirtualizationData.scrollOffset.y; - var previousPadding = storedPadding; - - storedPadding = GetContentHeightForIndex(firstVisibleIndex - 1); - if (m_StickToBottom) return; - if (newHeight < previousHeight) - { - foreach (var item in m_ActiveItems) - { - if (item.index == itemsCount - 1 && item.rootElement.style.display == DisplayStyle.Flex) - { - m_StickToBottom = true; - return; - } - } - } - if (m_ForcedLastVisibleItem >= 0) { var lastItemHeight = GetContentHeightForIndex(m_ForcedLastVisibleItem); - m_CollectionView.serializedVirtualizationData.scrollOffset.y = lastItemHeight + BaseVerticalCollectionView.s_DefaultItemHeight - m_ScrollView.contentViewport.layout.height; + serializedData.scrollOffset.y = lastItemHeight + BaseVerticalCollectionView.s_DefaultItemHeight - m_ScrollView.contentViewport.layout.height; } else { - var offset = previousOffset - previousPadding; - - // We need to adjust the scroll offset relative to the new item height. - if (index == firstVisibleIndex && offset != 0) + if (m_ScrollDirection == ScrollDirection.Up) { - offset += newHeight - previousHeight; + serializedData.scrollOffset.y += newHeight - previousHeight; } - - m_CollectionView.serializedVirtualizationData.scrollOffset.y = storedPadding + offset; } } - void ApplyScrollViewUpdate() + void ApplyScrollViewUpdate(bool dimensionsOnly = false) { - var contentHeight = GetExpectedContentHeight(); - storedPadding = GetContentHeightForIndex(firstVisibleIndex - 1); + var previousPadding = contentPadding; + var previousScrollOffset = serializedData.scrollOffset.y; + var itemOffset = previousScrollOffset - previousPadding; - m_ScrollView.contentContainer.style.paddingTop = storedPadding; - m_ScrollView.contentContainer.style.height = contentHeight; + if (anchoredIndex >= 0) + { + // Force the anchored item to the top. + if (firstVisibleIndex != anchoredIndex) + { + CycleItems(anchoredIndex); + ScheduleFill(); + } + + firstVisibleIndex = anchoredIndex; + itemOffset = anchorOffset; + } - var scrollableHeight = Mathf.Max(0, contentHeight - m_ScrollView.contentViewport.layout.height); + var expectedContentHeight = GetExpectedContentHeight(); + contentHeight = expectedContentHeight; + contentPadding = GetContentHeightForIndex(firstVisibleIndex - 1); - if (m_StickToBottom) + var scrollableHeight = Mathf.Max(0, expectedContentHeight - m_ScrollView.contentViewport.layout.height); + var scrollOffset = Mathf.Min(contentPadding + itemOffset, scrollableHeight); + + // Stick to the end of the viewport. + if (m_StickToBottom && scrollableHeight > 0) { - m_CollectionView.serializedVirtualizationData.scrollOffset.y = scrollableHeight; + scrollOffset = scrollableHeight; + } + else if (m_ForcedLastVisibleItem != -1) + { + var lastItemHeight = GetContentHeightForIndex(m_ForcedLastVisibleItem); + var lastItemViewportOffset = lastItemHeight + BaseVerticalCollectionView.s_DefaultItemHeight - m_ScrollView.contentViewport.layout.height; + scrollOffset = lastItemViewportOffset; } + // Don't notify to avoid coming back in the scroll update for no reason. m_ScrollView.verticalScroller.slider.SetHighValueWithoutNotify(scrollableHeight); - m_ScrollView.verticalScroller.slider.SetValueWithoutNotify(m_CollectionView.serializedVirtualizationData.scrollOffset.y); - m_CollectionView.serializedVirtualizationData.scrollOffset.y = m_ScrollView.verticalScroller.slider.value; + m_ScrollView.verticalScroller.slider.SetValueWithoutNotify(scrollOffset); + serializedData.scrollOffset.y = m_ScrollView.verticalScroller.slider.value; - if (!NeedsFill()) + if (dimensionsOnly || m_LastChange == VirtualizationChange.Resize) + { + ScheduleScrollDirectionReset(); + return; + } + + if (NeedsFill()) + { + Fill(); + } + else { // Clear extra items. - var itemContentOffset = storedPadding; + var itemContentOffset = contentPadding; var previousFirstVisibleIndex = firstVisibleIndex; + var inserting = m_ScrollInsertionList; + var bumpCount = 0; for (var i = 0; i < m_ActiveItems.Count; i++) { - var index = firstVisibleIndex + i; - var isItemInViewport = itemContentOffset - m_CollectionView.serializedVirtualizationData.scrollOffset.y < m_ScrollView.contentViewport.layout.height; + var item = m_ActiveItems[i]; + var index = item.index; - if (!isItemInViewport && m_ActiveItems[i].rootElement.style.display == DisplayStyle.Flex) - { - HideItem(i); + if (index < 0) + break; - if (firstVisibleIndex == index) - firstVisibleIndex = index + 1; + var itemHeight = GetExpectedItemHeight(index); + + // Hide items outside viewport's top/bottom. + if (m_ActiveItems[i].rootElement.style.display == DisplayStyle.Flex) + { + // Items above the viewport bounds need to be sent back to the back of active items. + if (itemContentOffset + itemHeight <= serializedData.scrollOffset.y) + { + item.rootElement.BringToFront(); // We send the element to the bottom of the list (front in z-order) + HideItem(i); + inserting.Add(item); + bumpCount++; + firstVisibleIndex++; + } + else if (itemContentOffset > viewportMaxOffset) + { + // We can just hide items below the bounds of the viewport. + HideItem(i); + } } itemContentOffset += GetExpectedItemHeight(index); } + m_ActiveItems.RemoveRange(0, bumpCount); //we remove them all at once + m_ActiveItems.AddRange(inserting); // add them back to the end + m_ScrollInsertionList.Clear(); + if (firstVisibleIndex != previousFirstVisibleIndex) { - storedPadding = GetContentHeightForIndex(firstVisibleIndex - 1); + contentPadding = GetContentHeightForIndex(firstVisibleIndex - 1); + UpdateAnchor(); } - m_StickToBottom = false; + ScheduleScrollDirectionReset(); m_ForcedLastVisibleItem = -1; - m_ScheduledItem?.Pause(); - m_ScheduledItem = null; m_CollectionView.SaveViewData(); } - else + } + + void UpdateAnchor() + { + anchoredIndex = firstVisibleIndex; + anchorOffset = serializedData.scrollOffset.y - contentPadding; + } + + void ScheduleFill() + { + if (m_ScheduledItem == null) { - Fill(); + m_ScheduledItem = m_CollectionView.schedule.Execute(m_FillCallback); + return; + } + + m_ScheduledItem.Pause(); + m_ScheduledItem.Resume(); + } + + void ScheduleScroll() + { + if (m_ScrollScheduledItem == null) + { + m_ScrollScheduledItem = m_CollectionView.schedule.Execute(m_ScrollCallback); + return; + } + + m_ScrollScheduledItem.Pause(); + m_ScrollScheduledItem.Resume(); + } + + void ScheduleScrollDirectionReset() + { + if (m_ScrollResetScheduledItem == null) + { + m_ScrollResetScheduledItem = m_CollectionView.schedule.Execute(m_ScrollResetCallback); + return; + } + + m_ScrollResetScheduledItem.Pause(); + m_ScrollResetScheduledItem.Resume(); + } + + void ResetScroll() + { + m_ScrollDirection = ScrollDirection.Idle; + m_LastChange = VirtualizationChange.None; + m_ScrollView.UpdateContentViewTransform(); + UpdateAnchor(); + m_CollectionView.SaveViewData(); + } + + public override int GetIndexFromPosition(Vector2 position) + { + var index = 0; + var traversedHeight = 0f; + while (traversedHeight < position.y) + { + traversedHeight += GetExpectedItemHeight(index++); } + + return index - 1; } - void UpdateScrollViewScrollers() + public override float GetExpectedItemHeight(int index) { - m_ScrollView.UpdateScrollers(m_ScrollView.needsHorizontal, m_ScrollView.needsVertical); - m_ScrollbarsScheduledItem = null; + var draggedIndex = GetDraggedIndex(); + if (draggedIndex >= 0 && index == draggedIndex) + { + return 0; + } + + return m_ItemHeightCache.TryGetValue(index, out var height) ? height : defaultExpectedHeight; + } + + int GetFirstVisibleItem(float offset) + { + if (offset <= 0) + return 0; + + var index = -1; + while (offset > 0) + { + index++; + var height = GetExpectedItemHeight(index); + offset -= height; + } + + return index; } public override float GetExpectedContentHeight() @@ -535,9 +805,16 @@ float GetContentHeightForIndex(int lastIndex) if (lastIndex < 0) return 0; - if (m_ContentHeightCache.TryGetValue(lastIndex, out var contentHeight)) + if (m_ContentHeightCache.TryGetValue(lastIndex, out var height)) { - return contentHeight.sum + (lastIndex - contentHeight.count + 1) * defaultExpectedHeight; + // Make sure we don't include the dragged item height. + var draggedIndex = GetDraggedIndex(); + if (draggedIndex >= 0 && lastIndex >= draggedIndex) + { + return height.sum + (lastIndex - height.count + 1) * defaultExpectedHeight - m_DraggedItem.rootElement.layout.height; + } + + return height.sum + (lastIndex - height.count + 1) * defaultExpectedHeight; } return GetContentHeightForIndex(lastIndex - 1) + GetExpectedItemHeight(lastIndex); @@ -545,13 +822,15 @@ float GetContentHeightForIndex(int lastIndex) ContentHeightCacheInfo GetCachedContentHeight(int index) { - if (index < 0) - return default; + while (index >= 0) + { + if (m_ContentHeightCache.TryGetValue(index, out var content)) + return content; - if (m_ContentHeightCache.TryGetValue(index, out var content)) - return content; + index -= 1; + } - return GetCachedContentHeight(index - 1); + return default; } void RegisterItemHeight(int index, float height) @@ -565,8 +844,6 @@ void RegisterItemHeight(int index, float height) m_AccumulatedHeight -= value; m_AccumulatedHeight += resolvedHeight; - var count = m_ItemHeightCache.Count; - m_AverageHeight = m_CollectionView.ResolveItemHeight(count > 0 ? m_AccumulatedHeight / count : m_AccumulatedHeight); m_ItemHeightCache[index] = resolvedHeight; if (index > m_HighestCachedIndex) @@ -613,16 +890,6 @@ void UnregisterItemHeight(int index) } m_HighestCachedIndex = highestIndex; - - var count = m_ItemHeightCache.Count; - if (m_AccumulatedHeight <= 0) - { - m_AverageHeight = BaseVerticalCollectionView.s_DefaultItemHeight; - } - else - { - m_AverageHeight = m_CollectionView.ResolveItemHeight(count > 0 ? m_AccumulatedHeight / count : m_AccumulatedHeight); - } } void CleanItemHeightCache() @@ -651,15 +918,15 @@ void CleanItemHeightCache() { ListPool.Release(unregisterList); } + + m_MinimumItemHeight = -1; } void OnRecycledItemGeometryChanged(ReusableCollectionItem item) { - if (item.index == ReusableCollectionItem.UndefinedIndex || float.IsNaN(item.rootElement.layout.height) || item.rootElement.layout.height == 0) + if (item.index == ReusableCollectionItem.UndefinedIndex || item.isDragGhost || float.IsNaN(item.rootElement.layout.height) || item.rootElement.layout.height == 0) return; - m_ScrollbarsScheduledItem ??= m_ScrollView.contentContainer.schedule.Execute(m_UpdateScrollbarsCallback); - if (UpdateRegisteredHeight(item)) { ApplyScrollViewUpdate(); @@ -668,17 +935,40 @@ void OnRecycledItemGeometryChanged(ReusableCollectionItem item) bool UpdateRegisteredHeight(ReusableCollectionItem item) { - if (item.index == ReusableCollectionItem.UndefinedIndex || float.IsNaN(item.rootElement.layout.height) || item.rootElement.layout.height == 0) + if (item.index == ReusableCollectionItem.UndefinedIndex || item.isDragGhost || float.IsNaN(item.rootElement.layout.height) || item.rootElement.layout.height == 0) return false; - var targetHeight = item.rootElement.layout.height - item.rootElement.style.paddingTop.value.value; + if (item.rootElement.layout.height < defaultExpectedHeight) + { + m_MinimumItemHeight = item.rootElement.layout.height; + Resize(m_ScrollView.layout.size); + } + + var targetHeight = item.rootElement.layout.height - item.rootElement.resolvedStyle.paddingTop; var wasCached = m_ItemHeightCache.TryGetValue(item.index, out var height); - var previousHeight = wasCached ? GetExpectedItemHeight(item.index) : targetHeight; + var previousHeight = wasCached ? GetExpectedItemHeight(item.index) : defaultExpectedHeight; - if (!wasCached || !targetHeight.Equals(height)) + // Update the m_StickToBottom variable. + if (m_WaitingCache.Count == 0) + { + // When the size increases on an item that wasn't waiting for layout, we are certainly not sticking + // to the bottom. Otherwise, we should check if we should. + if (targetHeight > previousHeight) + { + m_StickToBottom = false; + } + else + { + var deltaHeight = targetHeight - previousHeight; + var scrollableHeight = Mathf.Max(0, contentHeight - m_ScrollView.contentViewport.layout.height); + m_StickToBottom = scrollableHeight > 0 && serializedData.scrollOffset.y >= m_ScrollView.verticalScroller.highValue + deltaHeight; + } + } + + if (!wasCached || !Mathf.Approximately(targetHeight, height)) { RegisterItemHeight(item.index, targetHeight); - UpdateScrollViewContainer(item.index, previousHeight, targetHeight); + UpdateScrollViewContainer(previousHeight, targetHeight); if (m_WaitingCache.Count == 0) { @@ -691,47 +981,64 @@ bool UpdateRegisteredHeight(ReusableCollectionItem item) internal override T GetOrMakeItemAtIndex(int activeItemIndex = -1, int scrollViewIndex = -1) { - // Reuse hidden items first. - foreach (var i in m_ActiveItems) - { - if (i.rootElement.style.display == DisplayStyle.None) - { - if (activeItemIndex != -1) - { - m_ActiveItems.Remove(i); - m_ActiveItems.Insert(activeItemIndex, i); - } - - if (scrollViewIndex != -1) - { - m_ScrollView.Insert(scrollViewIndex, i.rootElement); - } - - return i; - } - } - var item = base.GetOrMakeItemAtIndex(activeItemIndex, scrollViewIndex); item.onGeometryChanged += m_GeometryChangedCallback; return item; } - public override void ReplaceActiveItem(int index) - { - base.ReplaceActiveItem(index); - m_WaitingCache.Remove(index); - } - internal override void ReleaseItem(int activeItemsIndex) { var item = m_ActiveItems[activeItemsIndex]; item.onGeometryChanged -= m_GeometryChangedCallback; var index = item.index; + UnregisterItemHeight(index); base.ReleaseItem(activeItemsIndex); m_WaitingCache.Remove(index); } + internal override void StartDragItem(ReusableCollectionItem item) + { + m_WaitingCache.Remove(item.index); + base.StartDragItem(item); + + // We don't need to track geometry changes for the dragged item + m_DraggedItem.onGeometryChanged -= m_GeometryChangedCallback; + } + + internal override void EndDrag(int dropIndex) + { + // Update item height of active items to support index changes (reordering). + var draggingDown = m_DraggedItem.index < dropIndex; + var startIndex = m_DraggedItem.index; + var increment = draggingDown ? 1 : -1; + var startItemHeight = GetExpectedItemHeight(startIndex); + for (var i = startIndex; i != dropIndex; i += increment) + { + var height = GetExpectedItemHeight(i); + var nextHeight = GetExpectedItemHeight(i + increment); + if (Mathf.Approximately(height, nextHeight)) + continue; + + RegisterItemHeight(i, nextHeight); + } + + RegisterItemHeight(draggingDown ? dropIndex - 1 : dropIndex, startItemHeight); + + // With the new updated height, we need to grab anchors on the new first element if needed. + if (firstVisibleIndex > m_DraggedItem.index) + { + firstVisibleIndex = GetFirstVisibleItem(serializedData.scrollOffset.y); + UpdateAnchor(); + } + + // Restore the geometry changed event. + m_DraggedItem.onGeometryChanged += m_GeometryChangedCallback; + + // Clear the dragged item reference. + base.EndDrag(dropIndex); + } + void HideItem(int activeItemsIndex) { var item = m_ActiveItems[activeItemsIndex]; @@ -739,6 +1046,21 @@ void HideItem(int activeItemsIndex) m_WaitingCache.Remove(item.index); } + void MarkWaitingForLayout(T item) + { + // We don't wait for the layout of the dragged item, it will stay hidden. + if (item.isDragGhost) + return; + + m_WaitingCache.Add(item.index); + + // This will ensure the GeometryChangedEvent is triggered no matter what. + // We depend on it to know when all items are laid out to update the contentContainer size, + // so we need to make sure it is going to be called when we track it. + item.rootElement.lastLayout = Rect.zero; + item.rootElement.MarkDirtyRepaint(); + } + bool IsIndexOutOfBounds(int i) { return m_CollectionView.itemsSource == null || i >= itemsCount; diff --git a/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/FixedHeightVirtualizationController.cs b/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/FixedHeightVirtualizationController.cs index 5bbf26963f..00eba51d20 100644 --- a/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/FixedHeightVirtualizationController.cs +++ b/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/FixedHeightVirtualizationController.cs @@ -64,7 +64,7 @@ public override void ScrollToItem(int index) } } - public override void Resize(Vector2 size, int layoutPass) + public override void Resize(Vector2 size) { var pixelAlignedItemHeight = resolvedItemHeight; var contentHeight = GetExpectedContentHeight(); @@ -75,7 +75,7 @@ public override void Resize(Vector2 size, int layoutPass) // the ScrollView's OnGeometryChanged() didn't update the low // and highValues. var scrollableHeight = Mathf.Max(0, contentHeight - m_ScrollView.contentViewport.layout.height); - var scrollOffset = Mathf.Min(m_CollectionView.serializedVirtualizationData.scrollOffset.y, scrollableHeight); + var scrollOffset = Mathf.Min(serializedData.scrollOffset.y, scrollableHeight); m_ScrollView.verticalScroller.slider.SetHighValueWithoutNotify(scrollableHeight); m_ScrollView.verticalScroller.slider.SetValueWithoutNotify(scrollOffset); @@ -118,13 +118,13 @@ public override void Resize(Vector2 size, int layoutPass) public override void OnScroll(Vector2 scrollOffset) { - var offset = scrollOffset.y; + var clampedOffset = Mathf.Max(0, scrollOffset.y); var pixelAlignedItemHeight = resolvedItemHeight; - var firstVisibleItemIndex = (int)(offset / pixelAlignedItemHeight); + var firstVisibleItemIndex = (int)(clampedOffset / pixelAlignedItemHeight); m_ScrollView.contentContainer.style.paddingTop = firstVisibleItemIndex * pixelAlignedItemHeight; m_ScrollView.contentContainer.style.height = itemsCount * pixelAlignedItemHeight; - m_CollectionView.serializedVirtualizationData.scrollOffset.y = scrollOffset.y; + serializedData.scrollOffset.y = scrollOffset.y; if (firstVisibleItemIndex != firstVisibleIndex) { @@ -141,7 +141,7 @@ public override void OnScroll(Vector2 scrollOffset) for (int i = 0; i < count && m_ActiveItems.Count > 0; ++i) { - var last = m_ActiveItems[m_ActiveItems.Count - 1]; + var last = m_ActiveItems[^1]; inserting.Add(last); m_ActiveItems.RemoveAt(m_ActiveItems.Count - 1); //we remove from the end @@ -153,7 +153,7 @@ public override void OnScroll(Vector2 scrollOffset) } else //down { - if (firstVisibleIndex < m_ActiveItems[m_ActiveItems.Count - 1].index) + if (firstVisibleIndex < m_ActiveItems[^1].index) { var inserting = m_ScrollInsertionList; @@ -189,5 +189,17 @@ internal override T GetOrMakeItemAtIndex(int activeItemIndex = -1, int scrollVie item.rootElement.style.height = resolvedItemHeight; return item; } + + internal override void EndDrag(int dropIndex) + { + m_DraggedItem.rootElement.style.height = resolvedItemHeight; + + if (firstVisibleIndex > m_DraggedItem.index) + { + m_ScrollView.verticalScroller.value = serializedData.scrollOffset.y - resolvedItemHeight; + } + + base.EndDrag(dropIndex); + } } } diff --git a/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/ReusableCollectionItem.cs b/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/ReusableCollectionItem.cs index a040ba74b5..28dacea9f7 100644 --- a/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/ReusableCollectionItem.cs +++ b/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/ReusableCollectionItem.cs @@ -18,6 +18,9 @@ class ReusableCollectionItem public int index { get; set; } public int id { get; set; } + // Identifies an item as an invisible duplicate of the dragged item. + internal bool isDragGhost { get; private set; } + public event Action onGeometryChanged; protected EventCallback m_GeometryChangedEventCallback; @@ -46,6 +49,7 @@ public virtual void DetachElement() rootElement?.RemoveFromHierarchy(); SetSelected(false); + SetDragGhost(false); index = id = UndefinedIndex; } @@ -63,6 +67,13 @@ public virtual void SetSelected(bool selected) } } + public virtual void SetDragGhost(bool dragGhost) + { + isDragGhost = dragGhost; + rootElement.style.maxHeight = isDragGhost ? 0 : StyleKeyword.Initial; + bindableElement.style.display = isDragGhost ? DisplayStyle.None : DisplayStyle.Flex; + } + protected void OnGeometryChanged(GeometryChangedEvent evt) { onGeometryChanged?.Invoke(this); diff --git a/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/ReusableListViewItem.cs b/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/ReusableListViewItem.cs index ffbb35ff2f..292048ee16 100644 --- a/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/ReusableListViewItem.cs +++ b/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/ReusableListViewItem.cs @@ -90,5 +90,14 @@ public override void DetachElement() base.DetachElement(); rootElement.RemoveFromClassList(BaseListView.itemUssClassName); } + + public override void SetDragGhost(bool dragGhost) + { + base.SetDragGhost(dragGhost); + if (m_DragHandle != null) + { + m_DragHandle.style.display = isDragGhost ? DisplayStyle.None : DisplayStyle.Flex; + } + } } } diff --git a/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/ReusableTreeViewItem.cs b/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/ReusableTreeViewItem.cs index a565f1c7ac..3f8339c4cd 100644 --- a/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/ReusableTreeViewItem.cs +++ b/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/ReusableTreeViewItem.cs @@ -10,28 +10,29 @@ class ReusableTreeViewItem : ReusableCollectionItem { Toggle m_Toggle; VisualElement m_Container; - VisualElement m_IndentContainer; + VisualElement m_IndentElement; VisualElement m_BindableContainer; + VisualElement m_Checkmark; public override VisualElement rootElement => m_Container ?? bindableElement; public event Action onPointerUp; public event Action> onToggleValueChanged; - static Pool.ObjectPool s_IndentPool = new Pool.ObjectPool( - () => - { - var indentElement = new VisualElement(); - indentElement.AddToClassList(BaseTreeView.itemIndentUssClassName); - return indentElement; - }); + int m_Depth; + float m_IndentWidth; + + // Internal for tests. + internal float indentWidth => m_IndentWidth; EventCallback m_PointerUpCallback; EventCallback> m_ToggleValueChangedCallback; + EventCallback m_ToggleGeometryChangedCallback; public ReusableTreeViewItem() { m_PointerUpCallback = OnPointerUp; m_ToggleValueChangedCallback = OnToggleValueChanged; + m_ToggleGeometryChangedCallback = OnToggleGeometryChanged; } public override void Init(VisualElement item) @@ -49,20 +50,23 @@ protected void InitExpandHierarchy(VisualElement root, VisualElement item) m_Container = root; m_Container.style.flexDirection = FlexDirection.Row; - m_IndentContainer = new VisualElement() + m_IndentElement = new VisualElement() { - name = BaseTreeView.itemIndentsContainerUssClassName, + name = BaseTreeView.itemIndentUssClassName, style = { flexDirection = FlexDirection.Row }, }; - m_IndentContainer.AddToClassList(BaseTreeView.itemIndentsContainerUssClassName); - m_Container.hierarchy.Add(m_IndentContainer); + m_Container.hierarchy.Add(m_IndentElement); - m_Toggle = new Toggle { name = BaseTreeView.itemToggleUssClassName }; - m_Toggle.userData = this; + m_Toggle = new Toggle + { + name = BaseTreeView.itemToggleUssClassName, + userData = this + }; m_Toggle.AddToClassList(Foldout.toggleUssClassName); m_Toggle.AddToClassList(BaseTreeView.itemToggleUssClassName); m_Toggle.visualInput.AddToClassList(Foldout.inputUssClassName); - m_Toggle.visualInput.Q(className: Toggle.checkmarkUssClassName).AddToClassList(Foldout.checkmarkUssClassName); + m_Checkmark = m_Toggle.visualInput.Q(className: Toggle.checkmarkUssClassName); + m_Checkmark.AddToClassList(Foldout.checkmarkUssClassName); m_Container.hierarchy.Add(m_Toggle); m_BindableContainer = new VisualElement() @@ -81,6 +85,7 @@ public override void PreAttachElement() base.PreAttachElement(); rootElement.AddToClassList(BaseTreeView.itemUssClassName); m_Container?.RegisterCallback(m_PointerUpCallback); + m_Toggle?.visualInput.Q(className: Toggle.checkmarkUssClassName).RegisterCallback(m_ToggleGeometryChangedCallback); m_Toggle?.RegisterValueChangedCallback(m_ToggleValueChangedCallback); } @@ -89,28 +94,17 @@ public override void DetachElement() base.DetachElement(); rootElement.RemoveFromClassList(BaseTreeView.itemUssClassName); m_Container?.UnregisterCallback(m_PointerUpCallback); + m_Toggle?.visualInput.Q(className: Toggle.checkmarkUssClassName).UnregisterCallback(m_ToggleGeometryChangedCallback); m_Toggle?.UnregisterValueChangedCallback(m_ToggleValueChangedCallback); } public void Indent(int depth) { - if (m_IndentContainer == null) + if (m_IndentElement == null) return; - for (var i = 0; i < m_IndentContainer.childCount; i++) - { - s_IndentPool.Release(m_IndentContainer[i]); - } - - m_IndentContainer.Clear(); - - for (var i = 0; i < depth; ++i) - { - var indentElement = s_IndentPool.Get(); - m_IndentContainer.Add(indentElement); - } - - m_IndentContainer.EnableInClassList(BaseTreeView.itemIndentsContainerUssClassName, depth > 0); + m_Depth = depth; + UpdateIndentLayout(); } public void SetExpandedWithoutNotify(bool expanded) @@ -124,6 +118,22 @@ public void SetToggleVisibility(bool visible) m_Toggle.visible = visible; } + void OnToggleGeometryChanged(GeometryChangedEvent evt) + { + var width = m_Checkmark.resolvedStyle.width + m_Checkmark.resolvedStyle.marginLeft + m_Checkmark.resolvedStyle.marginRight; + if (Math.Abs(width - m_IndentWidth) < float.Epsilon) + return; + + m_IndentWidth = width; + UpdateIndentLayout(); + } + + void UpdateIndentLayout() + { + m_IndentElement.style.width = m_IndentWidth * m_Depth; + m_IndentElement.EnableInClassList(BaseTreeView.itemIndentUssClassName, m_Depth > 0); + } + void OnPointerUp(PointerUpEvent evt) { onPointerUp?.Invoke(evt); diff --git a/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/VerticalVirtualizationController.cs b/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/VerticalVirtualizationController.cs index 36732fa91d..63f2189982 100644 --- a/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/VerticalVirtualizationController.cs +++ b/ModuleOverrides/com.unity.ui/Core/Collections/Virtualization/VerticalVirtualizationController.cs @@ -11,13 +11,14 @@ namespace UnityEngine.UIElements // TODO [GR] Could move some of that stuff to a base CollectionVirtualizationController class (pool, active items, visible items, etc.) abstract class VerticalVirtualizationController : CollectionVirtualizationController where T : ReusableCollectionItem, new() { + readonly UnityEngine.Pool.ObjectPool m_Pool = new (() => new T(), null, i => i.DetachElement()); + protected BaseVerticalCollectionView m_CollectionView; protected const int k_ExtraVisibleItems = 2; - - protected readonly UnityEngine.Pool.ObjectPool m_Pool = new UnityEngine.Pool.ObjectPool(() => new T(), null, i => i.DetachElement()); protected List m_ActiveItems; + protected T m_DraggedItem; - public override IEnumerable activeItems => m_ActiveItems as IEnumerable; + public override IEnumerable activeItems => m_ActiveItems; int m_LastFocusedElementIndex = -1; List m_LastFocusedElementTreeChildIndexes = new List(); @@ -29,32 +30,28 @@ namespace UnityEngine.UIElements ? m_CollectionView.itemsSource.Count - 1 : m_CollectionView.itemsSource.Count; - protected virtual bool VisibleItemPredicate(T i) - { - var isBeingDragged = false; - if (m_CollectionView.dragger is ListViewDraggerAnimated dragger) - isBeingDragged = dragger.isDragging && i.index == dragger.draggedItem.index; - - return i.rootElement.style.display == DisplayStyle.Flex && !isBeingDragged; - } + protected virtual bool VisibleItemPredicate(T i)=> i.rootElement.style.display == DisplayStyle.Flex; internal T firstVisibleItem => m_ActiveItems.FirstOrDefault(m_VisibleItemPredicateDelegate); internal T lastVisibleItem => m_ActiveItems.LastOrDefault(m_VisibleItemPredicateDelegate); public override int visibleItemCount => m_ActiveItems.Count(m_VisibleItemPredicateDelegate); + protected SerializedVirtualizationData serializedData => m_CollectionView.serializedVirtualizationData; + public override int firstVisibleIndex { - get => Mathf.Min(m_CollectionView.serializedVirtualizationData.firstVisibleIndex, m_CollectionView.viewController.GetItemsCount() - 1); - protected set => m_CollectionView.serializedVirtualizationData.firstVisibleIndex = value; + get => Mathf.Min(serializedData.firstVisibleIndex, m_CollectionView.viewController.GetItemsCount() - 1); + protected set => serializedData.firstVisibleIndex = value; } // we keep this list in order to minimize temporary gc allocs - protected List m_ScrollInsertionList = new List(); + protected List m_ScrollInsertionList = new (); VisualElement m_EmptyRows; protected float lastHeight => m_CollectionView.lastHeight; + protected virtual bool alwaysRebindOnRefresh => true; protected VerticalVirtualizationController(BaseVerticalCollectionView collectionView) : base(collectionView.scrollView) @@ -92,9 +89,17 @@ public override void Refresh(bool rebuild) if (m_CollectionView.itemsSource != null && index >= 0 && index < itemsCount) { - if (hasValidBindings && isVisible) + if (!hasValidBindings) + continue; + + // Rebind visible items. + if (isVisible || alwaysRebindOnRefresh) { - m_CollectionView.viewController.InvokeUnbindItem(recycledItem, recycledItem.index); + if (recycledItem.index != ReusableCollectionItem.UndefinedIndex) + { + m_CollectionView.viewController.InvokeUnbindItem(recycledItem, recycledItem.index); + } + recycledItem.index = ReusableCollectionItem.UndefinedIndex; Setup(recycledItem, index); } @@ -116,9 +121,23 @@ public override void Refresh(bool rebuild) protected void Setup(T recycledItem, int newIndex) { // We want to skip the item that is being reordered with the animated dragger. - if (m_CollectionView.dragger is ListViewDraggerAnimated dragger) - if (dragger.isDragging && (dragger.draggedItem.index == newIndex || dragger.draggedItem == recycledItem)) - return; + var wasGhostItem = recycledItem.isDragGhost; + if (GetDraggedIndex() == newIndex) + { + if (recycledItem.index != ReusableCollectionItem.UndefinedIndex) + m_CollectionView.viewController.InvokeUnbindItem(recycledItem, recycledItem.index); + + recycledItem.SetDragGhost(true); + recycledItem.index = m_DraggedItem.index; + recycledItem.rootElement.style.display = DisplayStyle.Flex; + return; + } + + // Restore the state of the item if it was hidden by a drag. + if (wasGhostItem) + { + recycledItem.SetDragGhost(false); + } if (newIndex >= itemsCount) { @@ -132,13 +151,15 @@ protected void Setup(T recycledItem, int newIndex) } recycledItem.rootElement.style.display = DisplayStyle.Flex; - if (recycledItem.index == newIndex) return; + + var newId = m_CollectionView.viewController.GetIdForIndex(newIndex); + if (recycledItem.index == newIndex && recycledItem.id == newId) + return; var useAlternateUss = m_CollectionView.showAlternatingRowBackgrounds != AlternatingRowBackground.None && newIndex % 2 == 1; recycledItem.rootElement.EnableInClassList(BaseVerticalCollectionView.itemAlternativeBackgroundUssClassName, useAlternateUss); var previousIndex = recycledItem.index; - var newId = m_CollectionView.viewController.GetIdForIndex(newIndex); if (recycledItem.index != ReusableCollectionItem.UndefinedIndex) m_CollectionView.viewController.InvokeUnbindItem(recycledItem, recycledItem.index); @@ -231,8 +252,10 @@ public override void UpdateBackground() if (m_EmptyRows == null) { - m_EmptyRows = new VisualElement(); - m_EmptyRows.AddToClassList(BaseVerticalCollectionView.backgroundFillUssClassName); + m_EmptyRows = new VisualElement() + { + classList = {BaseVerticalCollectionView.backgroundFillUssClassName} + }; } if (m_EmptyRows.parent == null) @@ -240,6 +263,7 @@ public override void UpdateBackground() var pixelAlignedItemHeight = GetExpectedItemHeight(-1); var itemCount = Mathf.FloorToInt(backgroundFillHeight / pixelAlignedItemHeight) + 1; + if (itemCount > m_EmptyRows.childCount) { var itemsToAdd = itemCount - m_EmptyRows.childCount; @@ -265,28 +289,61 @@ public override void UpdateBackground() } } - public override void ReplaceActiveItem(int index) + internal override void StartDragItem(ReusableCollectionItem item) + { + m_DraggedItem = item as T; + + // Remove the active item from the list to prevent recycling it. + var activeIndex = m_ActiveItems.IndexOf(m_DraggedItem); + m_ActiveItems.RemoveAt(activeIndex); + + // Create a replacement item. Flag it as being dragged, so that we know it needs to stay hidden during item cycling. + var replacementItem = GetOrMakeItemAtIndex(activeIndex, activeIndex); + Setup(replacementItem, m_DraggedItem.index); + } + + internal override void EndDrag(int dropIndex) { - var i = 0; - foreach (var item in m_ActiveItems) + // Reinsert the dragged item. + var item = m_CollectionView.GetRecycledItemFromIndex(dropIndex); + var activeItemIndex = item != null ? m_ScrollView.IndexOf(item.rootElement) : m_ActiveItems.Count; + m_ScrollView.Insert(activeItemIndex, m_DraggedItem.rootElement); + m_ActiveItems.Insert(activeItemIndex, m_DraggedItem); + + // Release the ghost items. + for (var i = 0; i < m_ActiveItems.Count; i++) { - if (item.index == index) + var activeItem = m_ActiveItems[i]; + if (activeItem.isDragGhost) { - // Detach the old one - var scrollViewIndex = m_ScrollView.IndexOf(item.rootElement); - m_CollectionView.viewController.InvokeUnbindItem(item, index); - m_CollectionView.viewController.InvokeDestroyItem(item); - item.DetachElement(); - m_ActiveItems.Remove(item); - - // Attach and setup new one. - var recycledItem = GetOrMakeItemAtIndex(i, scrollViewIndex); - Setup(recycledItem, index); - break; + // Clear index so that it doesn't get unbound since it was never bound, then restore and release it. + activeItem.index = ReusableCollectionItem.UndefinedIndex; + ReleaseItem(i); + i--; } + } + + // We want to avoid releasing items in Refresh, that happens when an item is out of bounds and visible, + // so we set the last one invisible and let the virtualization display it if necessary. + if (Math.Min(dropIndex, itemsCount - 1) != m_DraggedItem.index) + { + if (lastVisibleItem != null) + lastVisibleItem.rootElement.style.display = DisplayStyle.None; - i++; + // We unbind in order. + if (m_DraggedItem.index < dropIndex) + { + m_CollectionView.viewController.InvokeUnbindItem(m_DraggedItem, m_DraggedItem.index); + m_DraggedItem.index = ReusableCollectionItem.UndefinedIndex; + } + else if (item != null) + { + m_CollectionView.viewController.InvokeUnbindItem(item, item.index); + item.index = ReusableCollectionItem.UndefinedIndex; + } } + + m_DraggedItem = null; } internal virtual T GetOrMakeItemAtIndex(int activeItemIndex = -1, int scrollViewIndex = -1) @@ -324,15 +381,21 @@ internal virtual T GetOrMakeItemAtIndex(int activeItemIndex = -1, int scrollView internal virtual void ReleaseItem(int activeItemsIndex) { var item = m_ActiveItems[activeItemsIndex]; - var index = item.index; - - if (index != ReusableCollectionItem.UndefinedIndex) + if (item.index != ReusableCollectionItem.UndefinedIndex) { - m_CollectionView.viewController.InvokeUnbindItem(item, index); + m_CollectionView.viewController.InvokeUnbindItem(item, item.index); } m_Pool.Release(item); - m_ActiveItems.RemoveAt(activeItemsIndex); + m_ActiveItems.Remove(item); + } + + protected int GetDraggedIndex() + { + if (m_CollectionView.dragger is ListViewDraggerAnimated { isDragging: true } dragger) + return dragger.draggedItem.index; + + return ReusableCollectionItem.UndefinedIndex; } } } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/BaseBoolField.cs b/ModuleOverrides/com.unity.ui/Core/Controls/BaseBoolField.cs index 3992330ea3..fa8a500b44 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/BaseBoolField.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/BaseBoolField.cs @@ -2,18 +2,25 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using Unity.Properties; + namespace UnityEngine.UIElements { /// - /// A is a clickable element that represents a boolean value. + /// A BaseBoolField is a clickable element that represents a boolean value. /// public abstract class BaseBoolField : BaseField { + internal static readonly DataBindingProperty textProperty = nameof(text); + protected Label m_Label; protected readonly VisualElement m_CheckMark; internal Clickable m_Clickable; + // Needed by the UIBuilder for authoring in the viewport + internal Label boolFieldLabelElement => m_Label; + /// /// Creates a with a Label and a default manipulator. /// @@ -52,11 +59,15 @@ private void OnNavigationSubmit(NavigationSubmitEvent evt) /// /// Unity creates a automatically if one does not exist. /// + [CreateProperty] public string text { get { return m_Label?.text; } set { + if (string.CompareOrdinal(m_Label?.text, value) == 0) + return; + if (!string.IsNullOrEmpty(value)) { // Lazy allocation of label if needed... @@ -72,6 +83,7 @@ public string text m_Label.RemoveFromHierarchy(); m_Label = null; } + NotifyPropertyChanged(textProperty); } } @@ -169,5 +181,18 @@ protected override void UpdateMixedValueContent() text = m_OriginalText; } } + + internal override void RegisterEditingCallbacks() + { + RegisterCallback(StartEditing); + RegisterCallback(EndEditing); + } + + internal override void UnregisterEditingCallbacks() + { + RegisterCallback(StartEditing); + RegisterCallback(EndEditing); + } + } } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/BaseCompositeField.cs b/ModuleOverrides/com.unity.ui/Core/Controls/BaseCompositeField.cs index ff073810d0..fa22ef23df 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/BaseCompositeField.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/BaseCompositeField.cs @@ -235,7 +235,7 @@ private void UpdateDisplay() FieldDescription[] fieldDescriptions = DescribeFields(); foreach (var fd in fieldDescriptions) { - m_Fields[i].value = (fd.read(rawValue)); + m_Fields[i].SetValueWithoutNotify(fd.read(rawValue)); i++; } } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/BaseListView.cs b/ModuleOverrides/com.unity.ui/Core/Controls/BaseListView.cs index cccb02068d..5d8ed32b84 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/BaseListView.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/BaseListView.cs @@ -6,11 +6,12 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using Unity.Properties; namespace UnityEngine.UIElements { /// - /// Options to change the drag and drop mode for items in the ListView. + /// Options to change the drag-and-drop mode for items in the ListView. /// /// /// Using @@Animated@@ will affect the layout of the ListView, by adding drag handles before every item. @@ -33,6 +34,12 @@ public enum ListViewReorderMode /// public abstract class BaseListView : BaseVerticalCollectionView { + internal static readonly DataBindingProperty showBoundCollectionSizeProperty = nameof(showBoundCollectionSize); + internal static readonly DataBindingProperty showFoldoutHeaderProperty = nameof(showFoldoutHeader); + internal static readonly DataBindingProperty headerTitleProperty = nameof(headerTitle); + internal static readonly DataBindingProperty showAddRemoveFooterProperty = nameof(showAddRemoveFooter); + internal static readonly DataBindingProperty reorderModeProperty = nameof(reorderMode); + /// /// Defines for the . /// @@ -72,6 +79,16 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext view.showAddRemoveFooter = m_ShowAddRemoveFooter.GetValueFromBag(bag, cc); view.showBoundCollectionSize = m_ShowBoundCollectionSize.GetValueFromBag(bag, cc); } + + /// + /// Constructor. + /// + protected UxmlTraits() + { + // Ignore by default, because the ListView content can have empty space when using footer for example. + // PointerEvents are registered on the ScrollView, which is pickingMode = PickingMode.Position. + m_PickingMode.defaultValue = PickingMode.Ignore; + } } bool m_ShowBoundCollectionSize = true; @@ -89,6 +106,7 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext /// linked correctly. In production, the collection size rarely displays as a line item in a ListView. /// > /// + [CreateProperty] public bool showBoundCollectionSize { get => m_ShowBoundCollectionSize; @@ -100,6 +118,7 @@ public bool showBoundCollectionSize m_ShowBoundCollectionSize = value; SetupArraySizeField(); + NotifyPropertyChanged(showBoundCollectionSizeProperty); } } @@ -118,6 +137,7 @@ public bool showBoundCollectionSize /// If is set to true, the header includes a TextField to control /// the array size, instead of using the field as part of the list. /// > + [CreateProperty] public bool showFoldoutHeader { get => m_ShowFoldoutHeader; @@ -130,30 +150,41 @@ public bool showFoldoutHeader EnableInClassList(listViewWithHeaderUssClassName, value); - if (m_ShowFoldoutHeader) + try { - if (m_Foldout != null) - return; - - m_Foldout = new Foldout() { name = foldoutHeaderUssClassName, text = m_HeaderTitle }; - m_Foldout.AddToClassList(foldoutHeaderUssClassName); - m_Foldout.tabIndex = 1; - hierarchy.Add(m_Foldout); - m_Foldout.Add(scrollView); + if (m_ShowFoldoutHeader) + { + if (m_Foldout != null) + return; + + m_Foldout = new Foldout() {name = foldoutHeaderUssClassName, text = m_HeaderTitle}; + + var foldoutToggle = m_Foldout.Q(className: Foldout.toggleUssClassName); + foldoutToggle.m_Clickable.acceptClicksIfDisabled = true; + + m_Foldout.AddToClassList(foldoutHeaderUssClassName); + m_Foldout.tabIndex = 1; + hierarchy.Add(m_Foldout); + m_Foldout.Add(scrollView); + } + else if (m_Foldout != null) + { + m_Foldout?.RemoveFromHierarchy(); + m_Foldout = null; + hierarchy.Add(scrollView); + } + + SetupArraySizeField(); + UpdateListViewLabel(); + + if (showAddRemoveFooter) + { + EnableFooter(true); + } } - else if (m_Foldout != null) + finally { - m_Foldout?.RemoveFromHierarchy(); - m_Foldout = null; - hierarchy.Add(scrollView); - } - - SetupArraySizeField(); - UpdateListViewLabel(); - - if (showAddRemoveFooter) - { - EnableFooter(true); + NotifyPropertyChanged(showFoldoutHeaderProperty); } } } @@ -182,15 +213,21 @@ void SetupArraySizeField() /// /// This property controls the text of the foldout header when using . /// + [CreateProperty] public string headerTitle { get => m_HeaderTitle; set { + var previous = m_HeaderTitle; + m_HeaderTitle = value; if (m_Foldout != null) m_Foldout.text = m_HeaderTitle; + + if (string.CompareOrdinal(previous, m_HeaderTitle) != 0) + NotifyPropertyChanged(headerTitleProperty); } } @@ -204,10 +241,18 @@ public string headerTitle /// A "+" button. When clicked, it adds a single item at the end of the list view. /// A "-" button. When clicked, it removes all selected items, or the last item if none are selected. /// + [CreateProperty] public bool showAddRemoveFooter { get => m_Footer != null; - set => EnableFooter(value); + set + { + var previous = showAddRemoveFooter; + EnableFooter(value); + + if (previous != showFoldoutHeader) + NotifyPropertyChanged(showAddRemoveFooterProperty); + } } internal Foldout headerFoldout => m_Foldout; @@ -248,15 +293,25 @@ void EnableFooter(bool enabled) } /// - /// This event is called for every item added to the itemsSource. Includes the item index. + /// This event is called for every item added to the ::ref::itemsSource. Includes the item index. /// + /// + /// Note: This event is only called when items are added through the ::ref::viewController, such as when calling ::ref::AddItems. + /// Adding items directly to the ::ref::itemsSource will not trigger this event. + /// public event Action> itemsAdded; /// - /// This event is called for every item added to the itemsSource. Includes the item index. + /// This event is called for every item removed from the ::ref::itemsSource. Includes the item index. /// + /// + /// Note: This event is only called when items are removed through the ::ref::viewController, such as when + /// calling ::ref::RemoveItems or . + /// public event Action> itemsRemoved; + internal event Action itemsSourceSizeChanged; + private void AddItems(int itemCount) { viewController.AddItems(itemCount); @@ -463,8 +518,12 @@ void OnItemsRemoved(IEnumerable indices) void OnItemsSourceSizeChanged() { // When bound, the ListViewBinding class takes care of refreshing when the array size is updated. - if (!(binding is IInternalListViewBinding)) + if (GetProperty(internalBindingKey) == null) + { RefreshItems(); + } + + itemsSourceSizeChanged?.Invoke(); } ListViewReorderMode m_ReorderMode; @@ -479,6 +538,7 @@ void OnItemsSourceSizeChanged() /// drop manipulation pushes items with an animation when the reordering happens. /// Multiple item reordering is only supported with the Simple drag mode. /// + [CreateProperty] public ListViewReorderMode reorderMode { get => m_ReorderMode; @@ -490,6 +550,7 @@ public ListViewReorderMode reorderMode InitializeDragAndDropController(reorderable); reorderModeChanged?.Invoke(); Rebuild(); + NotifyPropertyChanged(reorderModeProperty); } } } @@ -621,13 +682,24 @@ internal override ListViewDragger CreateDragger() /// The USS class name for scroll view when add/remove footer is enabled. /// /// - /// Unity adds this USS class to ListView's scroll view when is set to true. + /// Unity adds this USS class scroll view of when is set to true. /// Any styling applied to this class affects every list located beside, or below the stylesheet in the visual tree. /// public static readonly string scrollViewWithFooterUssClassName = ussClassName + "__scroll-view--with-footer"; - - internal static readonly string footerAddButtonName = ussClassName + "__add-button"; - internal static readonly string footerRemoveButtonName = ussClassName + "__remove-button"; + /// + /// The name of the add button element in the footer. + /// + /// + /// Unity uses this name of add button when is set to true. + /// + public static readonly string footerAddButtonName = ussClassName + "__add-button"; + /// + /// The name of the remove button element in the footer. + /// + /// + /// Unity uses this name of remove button when is set to true. + /// + public static readonly string footerRemoveButtonName = ussClassName + "__remove-button"; string m_MaxMultiEditStr; static readonly string k_EmptyListStr = "List is empty"; @@ -639,6 +711,7 @@ internal override ListViewDragger CreateDragger() public BaseListView() { AddToClassList(ussClassName); + pickingMode = PickingMode.Ignore; } /// @@ -650,6 +723,7 @@ public BaseListView(IList itemsSource, float itemHeight = ItemHeightUnset) : base(itemsSource, itemHeight) { AddToClassList(ussClassName); + pickingMode = PickingMode.Ignore; } private protected override void PostRefresh() diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/BasePopupField.cs b/ModuleOverrides/com.unity.ui/Core/Controls/BasePopupField.cs index fccab7f7b8..44036abd66 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/BasePopupField.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/BasePopupField.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; -using UnityEngine; +using Unity.Properties; using UnityEngine.Scripting.APIUpdating; namespace UnityEngine.UIElements @@ -19,6 +19,9 @@ namespace UnityEngine.UIElements [MovedFrom(true, UpgradeConstants.EditorNamespace, UpgradeConstants.EditorAssembly)] public abstract class BasePopupField : BaseField { + internal static readonly DataBindingProperty choicesProperty = nameof(choices); + internal static readonly DataBindingProperty textProperty = nameof(text); + internal List m_Choices; TextElement m_TextElement; VisualElement m_ArrowElement; @@ -48,6 +51,7 @@ protected TextElement textElement /// /// The list of choices to display in the popup menu. /// + [CreateProperty] public virtual List choices { get { return m_Choices; } @@ -60,6 +64,7 @@ public virtual List choices // Make sure to update the text displayed SetValueWithoutNotify(rawValue); + NotifyPropertyChanged(choicesProperty); } } @@ -76,6 +81,7 @@ public override void SetValueWithoutNotify(TValueType newValue) /// /// This is the text displayed to the user for the current selection of the popup. /// + [CreateProperty(ReadOnly = true)] public string text { get { return m_TextElement.text; } @@ -213,6 +219,7 @@ protected override void UpdateMixedValueContent() { if (showMixedValue) { + value = default; textElement.text = mixedValueString; } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/BaseSlider.cs b/ModuleOverrides/com.unity.ui/Core/Controls/BaseSlider.cs index 05871f49b7..42cbc528e4 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/BaseSlider.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/BaseSlider.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using Unity.Properties; namespace UnityEngine.UIElements { @@ -29,23 +30,39 @@ public enum SliderDirection public abstract class BaseSlider : BaseField, IValueField where TValueType : System.IComparable { + internal static readonly DataBindingProperty lowValueProperty = nameof(lowValue); + internal static readonly DataBindingProperty highValueProperty = nameof(highValue); + internal static readonly DataBindingProperty rangeProperty = nameof(range); + internal static readonly DataBindingProperty pageSizeProperty = nameof(pageSize); + internal static readonly DataBindingProperty showInputFieldProperty = nameof(showInputField); + internal static readonly DataBindingProperty directionProperty = nameof(direction); + internal static readonly DataBindingProperty invertedProperty = nameof(inverted); + internal VisualElement dragContainer { get; private set; } internal VisualElement dragElement { get; private set; } internal VisualElement trackElement { get; private set; } internal VisualElement dragBorderElement { get; private set; } internal TextField inputTextField { get; private set; } - [SerializeField] + bool m_IsEditingTextField; + + [SerializeField, DontCreateProperty] private TValueType m_LowValue; + [Obsolete($"Use the generic BaseSlider.UxmlTraits class", true)] + public new class UxmlTraits : BaseField.UxmlTraits {} + /// /// Defines for the . + /// + /// This class must be used instead of the non-generic inherited UxmlTraits equivalent. /// /// /// This class defines the properties of a BaseSlider element that you can /// use in a UXML asset. /// - public new class UxmlTraits : BaseField.UxmlTraits + public class UxmlTraits : BaseFieldTraits + where TValueUxmlAttributeType : TypedUxmlAttributeDescription, new() { /// /// Constructor. @@ -59,6 +76,7 @@ public UxmlTraits() /// /// This is the minimum value that the slider encodes. /// + [CreateProperty] public TValueType lowValue { get { return m_LowValue; } @@ -70,16 +88,18 @@ public TValueType lowValue ClampValue(); UpdateDragElementPosition(); SaveViewData(); + NotifyPropertyChanged(lowValueProperty); } } } - [SerializeField] + [SerializeField, DontCreateProperty] private TValueType m_HighValue; /// /// This is the maximum value that the slider encodes. /// + [CreateProperty] public TValueType highValue { get { return m_HighValue; } @@ -91,6 +111,7 @@ public TValueType highValue ClampValue(); UpdateDragElementPosition(); SaveViewData(); + NotifyPropertyChanged(highValueProperty); } } } @@ -107,6 +128,7 @@ internal void SetHighValueWithoutNotify(TValueType newHighValue) /// /// This is the range from the minimum value to the maximum value of the slider. /// + [CreateProperty(ReadOnly = true)] public TValueType range { get { return SliderRange(); } @@ -115,12 +137,19 @@ public TValueType range private float m_PageSize; /// - /// This is a generic page size used to change the value when clicking in the slider. + /// Represents the value that should be applied to the calculated scroll offset while scrolling the slider, such as when clicking within the track or clicking the slider arrows. /// + [CreateProperty] public virtual float pageSize { get { return m_PageSize; } - set { m_PageSize = value; } + set + { + if (m_PageSize == value) + return; + m_PageSize = value; + NotifyPropertyChanged(pageSizeProperty); + } } private bool m_ShowInputField = false; @@ -132,6 +161,7 @@ public virtual float pageSize /// Set this property to true to display a numerical text field that provides another way to /// edit the slider value. /// + [CreateProperty] public virtual bool showInputField { get { return m_ShowInputField; } @@ -141,6 +171,7 @@ public virtual bool showInputField { m_ShowInputField = value; UpdateTextFieldVisibility(); + NotifyPropertyChanged(showInputFieldProperty); } } } @@ -224,11 +255,14 @@ public override void SetValueWithoutNotify(TValueType newValue) /// /// This is the actual property to contain the direction of the slider. /// + [CreateProperty] public SliderDirection direction { get { return m_Direction; } set { + var previous = m_Direction; + m_Direction = value; if (m_Direction == SliderDirection.Horizontal) { @@ -240,6 +274,8 @@ public SliderDirection direction RemoveFromClassList(horizontalVariantUssClassName); AddToClassList(verticalVariantUssClassName); } + if (previous != m_Direction) + NotifyPropertyChanged(directionProperty); } } @@ -250,6 +286,7 @@ public SliderDirection direction /// For an inverted horizontal slider, high value is located to the left, low value is located to the right /// For an inverted vertical slider, high value is located to the bottom, low value is located to the top. /// + [CreateProperty] public bool inverted { get { return m_Inverted; } @@ -259,6 +296,7 @@ public bool inverted { m_Inverted = value; UpdateDragElementPosition(); + NotifyPropertyChanged(invertedProperty); } } } @@ -381,7 +419,7 @@ private void ClampValue() internal abstract TValueType SliderLerpUnclamped(TValueType a, TValueType b, float interpolant); internal abstract float SliderNormalizeValue(TValueType currentValue, TValueType lowerValue, TValueType higherValue); internal abstract TValueType SliderRange(); - internal abstract TValueType ParseStringToValue(string stringValue); + internal abstract TValueType ParseStringToValue(string previousValue, string newValue); internal abstract void ComputeValueFromKey(SliderKey sliderKey, bool isShift); internal enum SliderKey @@ -430,7 +468,13 @@ void ComputeValueAndDirectionFromDrag(float sliderLength, float dragElementLengt if (Mathf.Abs(totalRange) < UIRUtility.k_Epsilon) return; - float normalizedDragElementPosition = Mathf.Max(0f, Mathf.Min(dragElementPos, totalRange)) / totalRange; + float normalizedDragElementPosition; + + if (clamped) + normalizedDragElementPosition = Mathf.Max(0f, Mathf.Min(dragElementPos, totalRange)) / totalRange; + else + normalizedDragElementPosition = dragElementPos / totalRange; + value = SliderLerpDirectionalUnclamped(lowValue, highValue, normalizedDragElementPosition); } @@ -690,7 +734,9 @@ private void UpdateTextFieldVisibility() { inputTextField = new TextField() { name = "unity-text-field" }; inputTextField.AddToClassList(textFieldClassName); + inputTextField.RegisterCallback(OnInputNavigationMoveEvent, TrickleDown.TrickleDown); inputTextField.RegisterValueChangedCallback(OnTextFieldValueChange); + inputTextField.RegisterCallback(OnTextFieldFocusIn); inputTextField.RegisterCallback(OnTextFieldFocusOut); visualInput.Add(inputTextField); UpdateTextFieldValue(); @@ -701,7 +747,9 @@ private void UpdateTextFieldVisibility() if (inputTextField.panel != null) inputTextField.RemoveFromHierarchy(); + inputTextField.UnregisterCallback(OnInputNavigationMoveEvent); inputTextField.UnregisterValueChangedCallback(OnTextFieldValueChange); + inputTextField.UnregisterCallback(OnTextFieldFocusIn); inputTextField.UnregisterCallback(OnTextFieldFocusOut); inputTextField = null; } @@ -709,20 +757,32 @@ private void UpdateTextFieldVisibility() private void UpdateTextFieldValue() { - if (inputTextField == null) + if (inputTextField == null || m_IsEditingTextField) return; inputTextField.SetValueWithoutNotify(String.Format(CultureInfo.InvariantCulture, "{0:g7}", value)); } + private void OnTextFieldFocusIn(FocusInEvent evt) + { + m_IsEditingTextField = true; + } + private void OnTextFieldFocusOut(FocusOutEvent evt) { + m_IsEditingTextField = false; UpdateTextFieldValue(); } + private void OnInputNavigationMoveEvent(NavigationMoveEvent evt) + { + // The input field should not do any navigation when using the arrow keys. + evt.StopPropagation(); + } + void OnTextFieldValueChange(ChangeEvent evt) { - var newValue = GetClampedValue(ParseStringToValue(evt.newValue)); + var newValue = GetClampedValue(ParseStringToValue(evt.previousValue, evt.newValue)); if (!EqualityComparer.Default.Equals(newValue, value)) { value = newValue; @@ -741,8 +801,24 @@ protected override void UpdateMixedValueContent() } else { - visualInput.Add(dragElement); + dragContainer.Add(dragElement); } } + + internal override void RegisterEditingCallbacks() + { + labelElement.RegisterCallback(StartEditing, TrickleDown.TrickleDown); + dragContainer.RegisterCallback(StartEditing, TrickleDown.TrickleDown); + + dragContainer.RegisterCallback(EndEditing); + } + + internal override void UnregisterEditingCallbacks() + { + labelElement.UnregisterCallback(StartEditing, TrickleDown.TrickleDown); + dragContainer.RegisterCallback(StartEditing, TrickleDown.TrickleDown); + + dragContainer.RegisterCallback(EndEditing); + } } } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/BaseTreeView.cs b/ModuleOverrides/com.unity.ui/Core/Controls/BaseTreeView.cs index 3e32a16268..e758e3245e 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/BaseTreeView.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/BaseTreeView.cs @@ -6,6 +6,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using Unity.Properties; namespace UnityEngine.UIElements { @@ -14,6 +15,8 @@ namespace UnityEngine.UIElements /// public abstract class BaseTreeView : BaseVerticalCollectionView { + internal static readonly DataBindingProperty autoExpandProperty = nameof(autoExpand); + /// /// The USS class name for TreeView elements. /// @@ -47,7 +50,7 @@ public abstract class BaseTreeView : BaseVerticalCollectionView /// public static readonly string itemIndentsContainerUssClassName = ussClassName + "__item-indents"; /// - /// The USS class name for TreeView indent elements. + /// The USS class name for TreeView indent element. /// /// /// Unity adds this USS class to every indent element of the TreeView. Any styling applied to @@ -92,9 +95,10 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext /// /// To set the items source, use instead, which allows fully typed items. /// + [CreateProperty(ReadOnly = true)] public new IList itemsSource { - get => viewController.itemsSource; + get => viewController?.itemsSource; internal set => GetOrCreateViewController().itemsSource = value; } @@ -174,18 +178,23 @@ void OnItemIndexChanged(int srcIndex, int dstIndex) /// /// When true, items are automatically expanded when added to the TreeView. /// + [CreateProperty] public bool autoExpand { get => m_AutoExpand; set { + if (m_AutoExpand == value) + return; + m_AutoExpand = value; viewController?.RegenerateWrappers(); RefreshItems(); + NotifyPropertyChanged(autoExpandProperty); } } - [SerializeField] + [SerializeField, DontCreateProperty] private List m_ExpandedItemIds; internal List expandedItemIds @@ -212,13 +221,9 @@ public BaseTreeView() : this((int)ItemHeightUnset) {} public BaseTreeView(int itemHeight) : base(null, itemHeight) { m_ExpandedItemIds = new List(); - - viewDataKey = ussClassName; AddToClassList(ussClassName); - scrollView.contentContainer.RegisterCallback(OnScrollViewNavigationMove); - - RegisterCallback(OnTreeViewMouseUp, TrickleDown.TrickleDown); + RegisterCallback(OnTreeViewPointerUp, TrickleDown.TrickleDown); } /// @@ -334,54 +339,58 @@ internal override void OnViewDataReady() } } - private void OnScrollViewKeyDown(KeyDownEvent evt) + private protected override bool HandleItemNavigation(bool moveIn, bool altPressed) { var index = selectedIndex; + var hasChildren = viewController.HasChildrenByIndex(index); - bool shouldStopPropagation = true; - - switch (evt.keyCode) + var selectionIncrement = 1; + if (moveIn) { - case KeyCode.RightArrow: - if (evt.altKey || !IsExpandedByIndex(index)) - ExpandItemByIndex(index, evt.altKey); - break; - case KeyCode.LeftArrow: - if (evt.altKey || IsExpandedByIndex(index)) - CollapseItemByIndex(index, evt.altKey); - break; - default: - shouldStopPropagation = false; - break; + if (hasChildren && !IsExpandedByIndex(index)) + { + ExpandItemByIndex(index, altPressed); + return true; + } } + else + { + if (hasChildren && IsExpandedByIndex(index)) + { + CollapseItemByIndex(index, altPressed); + return true; + } - if (shouldStopPropagation) - evt.StopPropagation(); - } + // Find the nearest ancestor with children in the tree and select it. + // If no ancestor is found, find the closest item with children before the current one. + var id = viewController.GetIdForIndex(index); + var ancestorId = viewController.GetParentId(id); + if (ancestorId != ReusableCollectionItem.UndefinedIndex) + { + SetSelectionById(ancestorId); + ScrollToItemById(ancestorId); + return true; + } - private void OnScrollViewNavigationMove(NavigationMoveEvent evt) - { - var index = selectedIndex; + selectionIncrement = -1; + } - bool shouldStopPropagation = true; + // Find the next item with children in the tree and select it. + var selectionIndex = index; + do + { + selectionIndex += selectionIncrement; + hasChildren = viewController.HasChildrenByIndex(selectionIndex); + } while (!hasChildren && selectionIndex >= 0 && selectionIndex < itemsSource.Count); - switch (evt.direction) + if (hasChildren) { - case NavigationMoveEvent.Direction.Right: - if (evt.altKey || !IsExpandedByIndex(index)) - ExpandItemByIndex(index, evt.altKey); - break; - case NavigationMoveEvent.Direction.Left: - if (evt.altKey || IsExpandedByIndex(index)) - CollapseItemByIndex(index, evt.altKey); - break; - default: - shouldStopPropagation = false; - break; + SetSelection(selectionIndex); + ScrollToItem(selectionIndex); + return true; } - if (shouldStopPropagation) - evt.StopPropagation(); + return false; } /// @@ -558,7 +567,7 @@ public void CollapseAll() viewController.CollapseAll(); } - private void OnTreeViewMouseUp(MouseUpEvent evt) + private void OnTreeViewPointerUp(PointerUpEvent evt) { scrollView.contentContainer.Focus(); } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/BaseVerticalCollectionView.cs b/ModuleOverrides/com.unity.ui/Core/Controls/BaseVerticalCollectionView.cs index 635da8c1b5..3a6077237f 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/BaseVerticalCollectionView.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/BaseVerticalCollectionView.cs @@ -7,6 +7,8 @@ using System.Collections.Generic; using System.Linq; using Unity.Profiling; +using UnityEngine.Pool; +using Unity.Properties; namespace UnityEngine.UIElements { @@ -53,7 +55,10 @@ class SerializedVirtualizationData { public Vector2 scrollOffset; public int firstVisibleIndex; - public float storedPadding; + public float contentPadding; + public float contentHeight; + public int anchoredItemIndex; + public float anchorOffset; } /// @@ -61,6 +66,23 @@ class SerializedVirtualizationData /// public abstract class BaseVerticalCollectionView : BindableElement, ISerializationCallbackReceiver { + internal static readonly DataBindingProperty itemsSourceProperty = nameof(itemsSource); + internal static readonly DataBindingProperty selectionTypeProperty = nameof(selectionType); + internal static readonly DataBindingProperty selectedItemProperty = nameof(selectedItem); + internal static readonly DataBindingProperty selectedItemsProperty = nameof(selectedItems); + internal static readonly DataBindingProperty selectedIndexProperty = nameof(selectedIndex); + internal static readonly DataBindingProperty selectedIndicesProperty = nameof(selectedIndices); + internal static readonly DataBindingProperty showBorderProperty = nameof(showBorder); + internal static readonly DataBindingProperty reorderableProperty = nameof(reorderable); + internal static readonly DataBindingProperty horizontalScrollingEnabledProperty = nameof(horizontalScrollingEnabled); + internal static readonly DataBindingProperty showAlternatingRowBackgroundsProperty = nameof(showAlternatingRowBackgrounds); + internal static readonly DataBindingProperty virtualizationMethodProperty = nameof(virtualizationMethod); + internal static readonly DataBindingProperty fixedItemHeightProperty = nameof(fixedItemHeight); + + internal const string internalBindingKey = "__unity-collection-view-internal-binding"; + static readonly ProfilerMarker k_RefreshMarker = new ("BaseVerticalCollectionView.RefreshItems"); + static readonly ProfilerMarker k_RebuildMarker = new ("BaseVerticalCollectionView.Rebuild"); + /// /// Defines for the . /// @@ -69,8 +91,8 @@ public abstract class BaseVerticalCollectionView : BindableElement, ISerializati /// public new class UxmlTraits : BindableElement.UxmlTraits { - private readonly UxmlIntAttributeDescription m_FixedItemHeight = new UxmlIntAttributeDescription { name = "fixed-item-height", obsoleteNames = new[] { "itemHeight, item-height" }, defaultValue = s_DefaultItemHeight }; private readonly UxmlEnumAttributeDescription m_VirtualizationMethod = new UxmlEnumAttributeDescription { name = "virtualization-method", defaultValue = CollectionVirtualizationMethod.FixedHeight }; + private readonly UxmlIntAttributeDescription m_FixedItemHeight = new UxmlIntAttributeDescription { name = "fixed-item-height", obsoleteNames = new[] { "itemHeight, item-height" }, defaultValue = s_DefaultItemHeight }; private readonly UxmlBoolAttributeDescription m_ShowBorder = new UxmlBoolAttributeDescription { name = "show-border", defaultValue = false }; private readonly UxmlEnumAttributeDescription m_SelectionType = new UxmlEnumAttributeDescription { name = "selection-type", defaultValue = SelectionType.Single }; private readonly UxmlEnumAttributeDescription m_ShowAlternatingRowBackgrounds = new UxmlEnumAttributeDescription { name = "show-alternating-row-backgrounds", defaultValue = AlternatingRowBackground.None }; @@ -123,7 +145,7 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext } /// - /// Callback triggered when the user acts on a selection of one or more items, for example by double-clicking or pressing Enter. + /// Obsolete. Use instead. /// /// /// This callback receives an enumerable that contains the item or items chosen. @@ -140,11 +162,12 @@ public event Action> onItemsChosen /// /// /// This callback receives an enumerable that contains the item or items chosen. + /// __Note__: A single-click only changes the selection. Use a double-click to submit the selection. /// public event Action> itemsChosen; /// - /// Callback triggered when the selection changes. + /// Obsolete. Use instead. /// /// /// This callback receives an enumerable that contains the item or items selected. @@ -165,7 +188,7 @@ public event Action> onSelectionChange public event Action> selectionChanged; /// - /// Callback triggered when the selection changes. + /// Obsolete. Use instead. /// /// /// This callback receives an enumerable that contains the item index or item indices selected. @@ -199,17 +222,48 @@ public event Action> onSelectedIndicesChange /// public event Action itemsSourceChanged; - // [GR] We can get rid of this once the InternalTreeView is removed. - private Func m_GetItemId; + internal event Action selectionNotChanged; + + /// + /// Called when a drag operation wants to start in this collection view. + /// + internal event Func canStartDrag; - internal Func getItemId + internal bool HasCanStartDrag() => canStartDrag != null; + + internal bool RaiseCanStartDrag(ReusableCollectionItem item, IEnumerable ids) { - get => m_GetItemId; - set - { - m_GetItemId = value; - RefreshItems(); - } + return canStartDrag?.Invoke(new CanStartDragArgs(item?.rootElement, item?.id ?? TreeItem.invalidId, ids)) ?? true; + } + + /// + /// Called when a drag operation starts in this collection view. + /// + internal event Func setupDragAndDrop; + + internal StartDragArgs RaiseSetupDragAndDrop(ReusableCollectionItem item, IEnumerable ids, StartDragArgs args) + { + return setupDragAndDrop?.Invoke(new SetupDragAndDropArgs(item?.rootElement, ids, args)) ?? args; + } + + /// + /// Called when a drag operation updates in this collection view. + /// + internal event Func dragAndDropUpdate; + + internal DragVisualMode RaiseHandleDragAndDrop(Vector2 pointerPosition, DragAndDropArgs dragAndDropArgs) + { + return dragAndDropUpdate?.Invoke(new HandleDragAndDropArgs(pointerPosition, dragAndDropArgs)) ?? DragVisualMode.None; + } + + /// + /// Called when a drag operation is released in this collection view. + /// + internal event Func handleDrop; + + internal DragVisualMode RaiseDrop(Vector2 pointerPosition, DragAndDropArgs dragAndDropArgs) + { + return handleDrop?.Invoke(new HandleDragAndDropArgs(pointerPosition, dragAndDropArgs)) ?? DragVisualMode.None; } /// @@ -218,16 +272,25 @@ internal Func getItemId /// /// This list contains the items that the displays. /// + [CreateProperty] public IList itemsSource { get => viewController?.itemsSource; - set => GetOrCreateViewController().itemsSource = value; + set + { + var previous = itemsSource; + GetOrCreateViewController().itemsSource = value; + if (previous != itemsSource) + { + NotifyPropertyChanged(itemsSourceProperty); + } + } } internal virtual bool sourceIncludesArraySize => false; /// - /// Callback for constructing the VisualElement that is the template for each recycled and re-bound element in the list. + /// Obsolete. Use or instead. /// [Obsolete("makeItem has been moved to ListView and TreeView. Use these ones instead.")] public Func makeItem @@ -237,7 +300,7 @@ public Func makeItem } /// - /// Callback for binding a data item to the visual element. + /// Obsolete. Use or instead. /// [Obsolete("bindItem has been moved to ListView and TreeView. Use these ones instead.")] public Action bindItem @@ -247,7 +310,7 @@ public Action bindItem } /// - /// Callback for unbinding a data item from the VisualElement. + /// Obsolete. Use or instead. /// [Obsolete("unbindItem has been moved to ListView and TreeView. Use these ones instead.")] public Action unbindItem @@ -257,7 +320,7 @@ public Action unbindItem } /// - /// Callback invoked when a created via is no longer needed and will be destroyed. + /// Obsolete. Use or instead. /// [Obsolete("destroyItem has been moved to ListView and TreeView. Use these ones instead.")] public Action destroyItem @@ -281,11 +344,13 @@ public Action destroyItem /// The default value is . /// When you set the collection view to disable selections, any current selection is cleared. /// + [CreateProperty] public SelectionType selectionType { get { return m_SelectionType; } set { + var previous = m_SelectionType; m_SelectionType = value; if (m_SelectionType == SelectionType.None) { @@ -298,39 +363,52 @@ public SelectionType selectionType SetSelection(m_SelectedIndices.First()); } } + + if (previous != m_SelectionType) + NotifyPropertyChanged(selectionTypeProperty); } } /// /// Returns the selected item from the data source. If multiple items are selected, returns the first selected item. /// + [CreateProperty(ReadOnly = true)] public object selectedItem => m_SelectedItems.Count == 0 ? null : m_SelectedItems.First(); /// /// Returns the selected items from the data source. Always returns an enumerable, even if no item is selected, or a single /// item is selected. /// + [CreateProperty(ReadOnly = true)] public IEnumerable selectedItems => m_SelectedItems; /// /// Returns or sets the selected item's index in the data source. If multiple items are selected, returns the /// first selected item's index. If multiple items are provided, sets them all as selected. /// + [CreateProperty] public int selectedIndex { get { return m_SelectedIndices.Count == 0 ? -1 : m_SelectedIndices.First(); } - set { SetSelection(value); } + set + { + var previous = selectedIndex; + SetSelection(value); + if (previous != selectedIndex) + NotifyPropertyChanged(selectedIndexProperty); + } } /// /// Returns the indices of selected items in the data source. Always returns an enumerable, even if no item is selected, or a /// single item is selected. /// + [CreateProperty(ReadOnly = true)] public IEnumerable selectedIndices => m_SelectedIndices; - internal List currentSelectionIds => m_SelectedIds; + internal IEnumerable selectedIds => m_SelectedIds; - static readonly List k_EmptyItems = new List(); + static readonly List k_EmptyItems = new(); internal IEnumerable activeItems => m_VirtualizationController?.activeItems ?? k_EmptyItems; internal ScrollView scrollView => m_ScrollView; internal ListViewDragger dragger => m_Dragger; @@ -342,7 +420,7 @@ public int selectedIndex public CollectionViewController viewController => m_ViewController; /// - /// The computed pixel-aligned height for the list elements. + /// Obsolete, will be removed from the API. /// /// /// This value changes depending on the current panel's DPI scaling. @@ -364,41 +442,49 @@ internal float ResolveItemHeight(float height = -1) /// /// If set to true, a border appears around the ScrollView that the collection view uses internally. /// + [CreateProperty] public bool showBorder { get => m_ScrollView.ClassListContains(borderUssClassName); - set => m_ScrollView.EnableInClassList(borderUssClassName, value); + set + { + var previous = showBorder; + m_ScrollView.EnableInClassList(borderUssClassName, value); + + if (previous != showBorder) + NotifyPropertyChanged(showBorderProperty); + } } /// /// Gets or sets a value that indicates whether the user can drag list items to reorder them. /// /// - /// The default value is false. - /// Set this value to true to allow the user to drag and drop the items in the list. The collection view - /// provides a default controller to allow standard behavior. It also automatically handles reordering - /// the items in the data source. + /// The default value is false which allows the user to drag items to and from other views + /// when you implement , , , and . + /// Set this value to true to allow the user to reorder items in the list. /// + [CreateProperty] public bool reorderable { get => m_Dragger?.dragAndDropController?.enableReordering ?? false; set { - if (m_Dragger?.dragAndDropController == null) + var previous = reorderable; + + try { - if (value) + var controller = m_Dragger.dragAndDropController; + if (controller != null && controller.enableReordering != value) { - InitializeDragAndDropController(true); + controller.enableReordering = value; + Rebuild(); } - - return; } - - var controller = m_Dragger.dragAndDropController; - if (controller != null && controller.enableReordering != value) + finally { - controller.enableReordering = value; - Rebuild(); + if (previous != reorderable) + NotifyPropertyChanged(reorderableProperty); } } } @@ -409,26 +495,30 @@ public bool reorderable /// This property controls whether the collection view shows a horizontal scroll bar when its content /// does not fit in the visible area. /// + [CreateProperty] public bool horizontalScrollingEnabled { - get { return m_HorizontalScrollingEnabled; } + get => m_HorizontalScrollingEnabled; set { if (m_HorizontalScrollingEnabled == value) return; m_HorizontalScrollingEnabled = value; - m_ScrollView.mode = (value ? ScrollViewMode.VerticalAndHorizontal : ScrollViewMode.Vertical); + m_ScrollView.horizontalScrollerVisibility = value ? ScrollerVisibility.Auto : ScrollerVisibility.Hidden; + m_ScrollView.mode = value ? ScrollViewMode.VerticalAndHorizontal : ScrollViewMode.Vertical; + NotifyPropertyChanged(horizontalScrollingEnabledProperty); } } - [SerializeField] + [SerializeField, DontCreateProperty] private AlternatingRowBackground m_ShowAlternatingRowBackgrounds = AlternatingRowBackground.None; /// /// This property controls whether the background colors of collection view rows alternate. /// Takes a value from the enum. /// + [CreateProperty] public AlternatingRowBackground showAlternatingRowBackgrounds { get { return m_ShowAlternatingRowBackgrounds; } @@ -439,9 +529,11 @@ public AlternatingRowBackground showAlternatingRowBackgrounds m_ShowAlternatingRowBackgrounds = value; RefreshItems(); + NotifyPropertyChanged(showAlternatingRowBackgroundsProperty); } } + internal static readonly string k_InvalidTemplateError = "Template Not Found"; // If we ever change the default item height, we should consider changing the default max height of the view when // used in property fields. The rule to look for is ".unity-property-field > .unity-collection-view" internal static readonly int s_DefaultItemHeight = 22; @@ -460,23 +552,23 @@ public AlternatingRowBackground showAlternatingRowBackgrounds /// When using DynamicHeight, the collection will wait for the actual height to be computed. /// Dynamic height is more flexible but less performant. /// + [CreateProperty] public CollectionVirtualizationMethod virtualizationMethod { get => m_VirtualizationMethod; set { - var oldValue = m_VirtualizationMethod; + if (m_VirtualizationMethod == value) + return; m_VirtualizationMethod = value; - if (oldValue != value) - { - CreateVirtualizationController(); - Rebuild(); - } + CreateVirtualizationController(); + Rebuild(); + NotifyPropertyChanged(virtualizationMethodProperty); } } /// - /// The height of a single item in the list, in pixels. + /// Obsolete. Use instead. /// /// /// This property must be set when using the is set to FixedHeight, for the collection view to function. @@ -493,12 +585,17 @@ public int itemHeight /// /// /// This property must be set when using the is set to FixedHeight, for the collection view to function. + /// If set when is DynamicHeight, it serves as the default height to help calculate the + /// number of items necessary and the scrollable area, before items are laid out. It should be set to the minimum expected height of an item. /// + [CreateProperty] public float fixedItemHeight { get => m_FixedItemHeight; set { + var previous = fixedItemHeight; + if (value < 0) throw new ArgumentOutOfRangeException(nameof(fixedItemHeight), "Value needs to be positive for virtualization."); @@ -507,6 +604,7 @@ public float fixedItemHeight { m_FixedItemHeight = value; RefreshItems(); + NotifyPropertyChanged(fixedItemHeightProperty); } } } @@ -516,11 +614,11 @@ public float fixedItemHeight CollectionVirtualizationController m_VirtualizationController; KeyboardNavigationManipulator m_NavigationManipulator; - [SerializeField] + [SerializeField, DontCreateProperty] internal SerializedVirtualizationData serializedVirtualizationData = new SerializedVirtualizationData(); // Persisted. It's why this can't be a HashSet(). :( - [SerializeField] + [SerializeField, DontCreateProperty] private readonly List m_SelectedIds = new List(); // Not persisted! Just used for fast lookups of selected indices and object references. @@ -627,6 +725,9 @@ internal void InitializeDragAndDropController(bool enableReordering) m_Dragger = CreateDragger(); m_Dragger.dragAndDropController = CreateDragAndDropController(); + if (m_Dragger.dragAndDropController == null) + return; + m_Dragger.dragAndDropController.enableReordering = enableReordering; } @@ -667,18 +768,24 @@ internal void SetDragAndDropController(ICollectionDragAndDropController dragAndD /// The USS class name of the drag hover bar. /// /// - /// Unity adds this USS class to the bar that appears when an item element is dragged. The - /// property must be true in order for items to be dragged. + /// Unity adds this USS class to the bar that appears when the user drags an item in the list. /// Any styling applied to this class affects every BaseVerticalCollectionView located beside, or below the stylesheet in the /// visual tree. /// public static readonly string dragHoverBarUssClassName = ussClassName + "__drag-hover-bar"; /// + /// The USS class name of the drag hover circular marker used to indicate depth. + /// + /// + /// Unity adds this USS class to the bar that appears when the user drags an item in the list. Any styling applied to this class affects + /// every BaseVerticalCollectionView located beside, or below the stylesheet in the visual tree. + /// + public static readonly string dragHoverMarkerUssClassName = ussClassName + "__drag-hover-marker"; + /// /// The USS class name applied to an item element on drag hover. /// /// - /// Unity adds this USS class to the list element that is dragged. The - /// property must be set to true for items to be draggable. Any styling applied to this class affects + /// Unity adds this USS class to the item element when it's being dragged. Any styling applied to this class affects /// every BaseVerticalCollectionView item located beside, or below the stylesheet in the visual tree. /// public static readonly string itemDragHoverUssClassName = itemUssClassName + "--drag-hover"; @@ -696,10 +803,10 @@ internal void SetDragAndDropController(ICollectionDragAndDropController dragAndD /// /// /// Unity adds this USS class to every odd-numbered item in the BaseVerticalCollectionView when the - /// property is set to ContentOnly or All. - /// When the showAlternatingRowBackground property is set to either of those values, odd-numbered items + /// property is set to ContentOnly or All. + /// When the showAlternatingRowBackgrounds property is set to either of those values, odd-numbered items /// are displayed with a different background color than even-numbered items. This USS class is used to differentiate - /// odd-numbered items from even-numbered items. When the showAlternatingRowBackground property is set to + /// odd-numbered items from even-numbered items. When the showAlternatingRowBackgrounds property is set to /// None, the USS class is not added, and any styling or behavior that relies on it's invalidated. /// public static readonly string itemAlternativeBackgroundUssClassName = itemUssClassName + "--alternative-background"; @@ -712,11 +819,11 @@ internal void SetDragAndDropController(ICollectionDragAndDropController dragAndD /// public static readonly string listScrollViewUssClassName = ussClassName + "__scroll-view"; - internal static readonly string backgroundFillUssClassName = ussClassName + "__background"; + internal static readonly string backgroundFillUssClassName = ussClassName + "__background-fill"; /// /// Creates a with all default properties. - /// The must all be set for the BaseVerticalCollectionView to function properly. + /// The must all be set for the BaseVerticalCollectionView to function properly. /// public BaseVerticalCollectionView() { @@ -725,11 +832,10 @@ public BaseVerticalCollectionView() selectionType = SelectionType.Single; m_ScrollView = new ScrollView(); - m_ScrollView.viewDataKey = "list-view__scroll-view"; m_ScrollView.AddToClassList(listScrollViewUssClassName); m_ScrollView.verticalScroller.valueChanged += v => OnScroll(new Vector2(0, v)); - RegisterCallback(OnSizeChanged); + m_ScrollView.RegisterCallback(OnSizeChanged); RegisterCallback(OnCustomStyleResolved); m_ScrollView.contentContainer.RegisterCallback(OnAttachToPanel); @@ -746,6 +852,8 @@ public BaseVerticalCollectionView() m_ItemIndexChangedCallback = OnItemIndexChanged; m_ItemsSourceChangedCallback = OnItemsSourceChanged; + + InitializeDragAndDropController(false); } /// @@ -769,7 +877,7 @@ public BaseVerticalCollectionView(IList itemsSource, float itemHeight = ItemHeig } /// - /// Constructs a , with all required properties provided. + /// Obsolete. Use or constructor directly. /// /// The list of items to use as a data source. /// The height of each item, in pixels. For FixedHeight virtualization only. @@ -791,17 +899,21 @@ public BaseVerticalCollectionView(IList itemsSource, float itemHeight = ItemHeig } /// - /// Gets the root element the specified TreeView item. + /// Gets the root element of the specified collection view item. /// - /// The TreeView item identifier. - /// The TreeView item's root element. + /// The item identifier. + /// The item's root element. + /// + /// This method provides an entry point to re-style elements added by Unity over the user-driven content. + /// Ex. the drag handle in a ListView, or the Toggle in a TreeView. + /// public VisualElement GetRootElementForId(int id) { return activeItems.FirstOrDefault(t => t.id == id)?.rootElement; } /// - /// Gets the root element the specified collection view item. + /// Gets the root element of the specified collection view item. /// /// The item index. /// The item's root element. @@ -822,10 +934,7 @@ internal virtual bool HasValidDataAndBindings() void OnItemIndexChanged(int srcIndex, int dstIndex) { itemIndexChanged?.Invoke(srcIndex, dstIndex); - if (!(binding is IInternalListViewBinding)) - RefreshItems(); - else - schedule.Execute(RefreshItems).ExecuteLater(100); + RefreshItems(); } void OnItemsSourceChanged() @@ -857,7 +966,7 @@ public void RefreshItem(int index) /// public void RefreshItems() { - using (new ProfilerMarker("BaseVerticalCollectionView.RefreshItems").Auto()) + using (k_RefreshMarker.Auto()) { if (m_ViewController == null) return; @@ -868,6 +977,9 @@ public void RefreshItems() } } + /// + /// Obsolete. Use instead. + /// [Obsolete("Refresh() has been deprecated. Use Rebuild() instead. (UnityUpgradable) -> Rebuild()", false)] public void Refresh() { @@ -882,7 +994,7 @@ public void Refresh() /// public void Rebuild() { - using (new ProfilerMarker("BaseVerticalCollectionView.Rebuild").Auto()) + using (k_RebuildMarker.Auto()) { if (m_ViewController == null) return; @@ -952,7 +1064,7 @@ public void ScrollToItem(int index) } /// - /// Scrolls to a specific item id and makes it visible. + /// Obsolete. Use instead. /// /// Item id to scroll to. [Obsolete("ScrollToId() has been deprecated. Use ScrollToItemById() instead. (UnityUpgradable) -> ScrollToItemById(*)", false)] @@ -982,9 +1094,9 @@ private void OnScroll(Vector2 offset) virtualizationController.OnScroll(offset); } - private void Resize(Vector2 size, int layoutPass = -1) + private void Resize(Vector2 size) { - virtualizationController.Resize(size, layoutPass); + virtualizationController.Resize(size); m_LastHeight = size.y; virtualizationController.UpdateBackground(); } @@ -1021,7 +1133,7 @@ public void OnKeyDown(KeyDownEvent evt) m_NavigationManipulator.OnKeyDown(evt); } - private bool Apply(KeyboardNavigationOperation op, bool shiftKey) + private bool Apply(KeyboardNavigationOperation op, bool shiftKey, bool altKey) { if (selectionType == SelectionType.None || !HasValidDataAndBindings()) { @@ -1091,6 +1203,20 @@ void HandleSelectionAndScroll(int index) HandleSelectionAndScroll(Mathf.Max(0, selectionUp - (virtualizationController.visibleItemCount - 1))); } return true; + case KeyboardNavigationOperation.MoveRight: + if (m_SelectedIndices.Count > 0) + { + return HandleItemNavigation(true, altKey); + } + break; + case KeyboardNavigationOperation.MoveLeft: + if (m_SelectedIndices.Count > 0) + { + return HandleItemNavigation(false, altKey); + } + break; + default: + throw new ArgumentOutOfRangeException(nameof(op), op, null); } return false; @@ -1098,15 +1224,21 @@ void HandleSelectionAndScroll(int index) private void Apply(KeyboardNavigationOperation op, EventBase sourceEvent) { - var shiftKey = sourceEvent is KeyDownEvent kde && kde.shiftKey || - sourceEvent is INavigationEvent ne && ne.shiftKey; - if (Apply(op, shiftKey)) + var shiftKey = sourceEvent is KeyDownEvent { shiftKey: true } or INavigationEvent { shiftKey: true }; + var altKey = sourceEvent is KeyDownEvent { altKey: true } or INavigationEvent { altKey: true }; + if (Apply(op, shiftKey, altKey)) { sourceEvent.StopPropagation(); sourceEvent.PreventDefault(); } } + private protected virtual bool HandleItemNavigation(bool moveIn, bool altKey) + { + // We could possibly want to expand child items here. + return false; + } + private void OnPointerMove(PointerMoveEvent evt) { // Support cases where PointerMove corresponds to a MouseDown or MouseUp event with multiple buttons. @@ -1249,21 +1381,41 @@ private void DoSelect(Vector2 localPosition, int clickCount, bool actionKey, boo } else if (selectionType == SelectionType.Multiple && m_SelectedIndices.Contains(clickedIndex)) { - // Do noting, selection will be processed OnPointerUp. + // Do nothing, selection will be processed OnPointerUp. // If drag and drop will be started ListViewDragger will capture the mouse and ListView will not receive the mouse up event. + selectionNotChanged?.Invoke(); } else // single { + if (selectionType == SelectionType.Single && m_SelectedIndices.Contains(clickedIndex)) + { + selectionNotChanged?.Invoke(); + } + SetSelection(clickedIndex); } break; case 2: - if (itemsChosen != null) + if (itemsChosen == null) + return; + + var wasClickedIndexInSelection = false; + foreach (var index in selectedIndices) { - ProcessSingleClick(clickedIndex); + if (clickedIndex == index) + { + wasClickedIndexInSelection = true; + break; + } } + ProcessSingleClick(clickedIndex); + + // Only invoke itemsChosen if we're clicking on the same entry. Case UUM-42450. + if (!wasClickedIndexInSelection) + return; + itemsChosen?.Invoke(m_SelectedItems); break; } @@ -1437,6 +1589,9 @@ internal void SetSelectionInternal(IEnumerable indices, bool sendNotificati if (!HasValidDataAndBindings() || indices == null) return; + if (MatchesExistingSelection(indices)) + return; + ClearSelectionWithoutValidation(); foreach (var index in indices) AddToSelectionWithoutValidation(index); @@ -1447,6 +1602,28 @@ internal void SetSelectionInternal(IEnumerable indices, bool sendNotificati SaveViewData(); } + private bool MatchesExistingSelection(IEnumerable indices) + { + var pooled = ListPool.Get(); + try + { + pooled.AddRange(indices); + if (pooled.Count != m_SelectedIndices.Count) + return false; + for (var i = 0; i < pooled.Count; ++i) + { + if (pooled[i] != m_SelectedIndices[i]) + return false; + } + + return true; + } + finally + { + ListPool.Release(pooled); + } + } + private void NotifyOfSelectionChange() { if (!HasValidDataAndBindings()) @@ -1484,6 +1661,7 @@ internal override void OnViewDataReady() var key = GetFullHierarchicalViewDataKey(); OverwriteFromViewData(this, key); + m_ScrollView.UpdateContentViewTransform(); } [EventInterest(typeof(PointerUpEvent), typeof(FocusEvent), typeof(NavigationSubmitEvent), typeof(BlurEvent))] @@ -1528,7 +1706,7 @@ private void OnSizeChanged(GeometryChangedEvent evt) Mathf.Approximately(evt.newRect.height, evt.oldRect.height)) return; - Resize(evt.newRect.size, evt.layoutPass); + Resize(evt.newRect.size); } private void OnCustomStyleResolved(CustomStyleResolvedEvent e) diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/Binding.cs b/ModuleOverrides/com.unity.ui/Core/Controls/Binding.cs index 106e77fa2f..86e5c3f8f9 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/Binding.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/Binding.cs @@ -53,6 +53,4 @@ public static bool IsBound(this IBindable control) return control?.binding != null; } } - - internal interface IInternalListViewBinding { } } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/Box.cs b/ModuleOverrides/com.unity.ui/Core/Controls/Box.cs index ba3306d431..8b483c8854 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/Box.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/Box.cs @@ -5,7 +5,7 @@ namespace UnityEngine.UIElements { /// - /// Styled visual element to match the IMGUI Box Style. + /// Styled visual element to match the IMGUI Box Style. For more information, refer to [[wiki:UIE-uxml-element-box|UXML element Box]]. /// public class Box : VisualElement { diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/Button.cs b/ModuleOverrides/com.unity.ui/Core/Controls/Button.cs index a3e7df66f9..4f38f448e2 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/Button.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/Button.cs @@ -10,10 +10,10 @@ namespace UnityEngine.UIElements /// This is a clickable button. /// /// - /// A consists of a text label element that can respond to pointer and mouse events. - /// You can replace or add to the content of the button by adding elements to its hierarchy. - /// For example, to use a separate image as an icon for the button, you can make an - /// element a child of the button. + /// A Button has a text label element that can respond to pointer and mouse events. + /// You can customize a button by adding child elements to its hierarchy. + /// For example, to use a separate image as an icon for the button, you can add an + /// element as a child of the button. /// /// By default, a single left mouse click activates the Button's property button. /// To remove this activator, or add more activators, modify the clickable.activators property. @@ -21,6 +21,8 @@ namespace UnityEngine.UIElements /// /// To bind a Button's text value to the contents of a variable, set the binding-path property in the /// UXML file, or the bindingPath property in the C# code, to a string that contains the variable name. + /// + /// For more information, refer to [[wiki:UIE-uxml-element-Button|UXML element Button]]. /// public class Button : TextElement { @@ -120,6 +122,10 @@ public event Action onClick /// /// This is a shortcut for modifying . It is provided as a convenience. When you add or remove actions from clicked, it adds or removes them from Clickable.clicked automatically. /// + /// + /// The following example shows how to use the clicked event to print a message to the console when the button is clicked. + /// + /// public event Action clicked { add diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/CompoundFields.cs b/ModuleOverrides/com.unity.ui/Core/Controls/CompoundFields.cs index 231f562fa2..018c6b7a68 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/CompoundFields.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/CompoundFields.cs @@ -169,7 +169,7 @@ public RectIntField(string label) } /// - /// A field. + /// A field. For more information, refer to [[wiki:UIE-uxm-element-Vector2Field|UXML element Vector2Field]]. /// [MovedFrom(true, UpgradeConstants.EditorNamespace, UpgradeConstants.EditorAssembly)] public class Vector2Field : BaseCompositeField @@ -244,7 +244,7 @@ public Vector2Field(string label) } /// - /// A field. + /// A field. For more information, refer to [[wiki:UIE-uxm-element-Vector3Field|UXML element Vector3Field]]. /// [MovedFrom(true, UpgradeConstants.EditorNamespace, UpgradeConstants.EditorAssembly)] public class Vector3Field : BaseCompositeField @@ -265,7 +265,7 @@ internal override FieldDescription[] DescribeFields() public new class UxmlFactory : UxmlFactory {} /// - /// Defines for the . + /// Defines for the . /// public new class UxmlTraits : BaseCompositeField.UxmlTraits { @@ -322,7 +322,7 @@ public Vector3Field(string label) /// - /// A field. + /// A field. For more information, refer to [[wiki:UIE-uxm-element-Vector4Field|UXML element Vector4Field]]. /// [MovedFrom(true, UpgradeConstants.EditorNamespace, UpgradeConstants.EditorAssembly)] public class Vector4Field : BaseCompositeField @@ -401,7 +401,7 @@ public Vector4Field(string label) /// - /// A field. + /// A field. For more information, refer to [[wiki:UIE-uxm-element-Vector2IntField|UXML element Vector2IntField]]. /// [MovedFrom(true, UpgradeConstants.EditorNamespace, UpgradeConstants.EditorAssembly)] public class Vector2IntField : BaseCompositeField @@ -476,7 +476,7 @@ public Vector2IntField(string label) } /// - /// A field. + /// A field. For more information, refer to [[wiki:UIE-uxm-element-Vector3IntField|UXML element Vector3IntField]]. /// [MovedFrom(true, UpgradeConstants.EditorNamespace, UpgradeConstants.EditorAssembly)] public class Vector3IntField : BaseCompositeField diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/DoubleField.cs b/ModuleOverrides/com.unity.ui/Core/Controls/DoubleField.cs index 26a9d610ba..af9539756a 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/DoubleField.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/DoubleField.cs @@ -43,7 +43,8 @@ protected override string ValueToString(double v) /// The double parsed from the string. protected override double StringToValue(string str) { - return UINumericFieldsUtils.StringToDouble(str, out var v) ? v : rawValue; + var success = UINumericFieldsUtils.TryConvertStringToDouble(str, textInputBase.originalText, out var v); + return success ? v : rawValue; } /// @@ -87,7 +88,7 @@ public DoubleField(string label, int maxLength = kMaxLengthNone) internal override bool CanTryParse(string textString) => double.TryParse(textString, out _); /// - /// Modify the value using a 3D delta and a speed, typically coming from an input device. + /// Applies the values of a 3D delta and a speed from an input device. /// /// A vector used to compute the value change. /// A multiplier for the value change. @@ -132,8 +133,7 @@ protected override string ValueToString(double v) protected override double StringToValue(string str) { - double v; - UINumericFieldsUtils.StringToDouble(str, out v); + UINumericFieldsUtils.TryConvertStringToDouble(str, originalText, out var v); return v; } } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/DropdownField.cs b/ModuleOverrides/com.unity.ui/Core/Controls/DropdownField.cs index 84fa9ded39..f2489825e1 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/DropdownField.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/DropdownField.cs @@ -8,7 +8,7 @@ namespace UnityEngine.UIElements { /// - /// A control that allows the user to pick a choice from a list of options. + /// A control that allows the user to pick a choice from a list of options. For more information, refer to [[wiki:UIE-uxml-element-dropdown|UXML element Dropdown]]. /// public class DropdownField : PopupField { @@ -30,7 +30,7 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext base.Init(ve, bag, cc); var f = (DropdownField)ve; - var choices = ParseChoiceList(m_Choices.GetValueFromBag(bag, cc)); + var choices = UxmlUtility.ParseStringListAttribute(m_Choices.GetValueFromBag(bag, cc)); if (choices != null) f.choices = choices; f.index = m_Index.GetValueFromBag(bag, cc); diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/EnumField.cs b/ModuleOverrides/com.unity.ui/Core/Controls/EnumField.cs index 7c980a7270..f2c3d00aa2 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/EnumField.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/EnumField.cs @@ -3,8 +3,10 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; +using Unity.Properties; using UnityEngine; using UnityEngine.Scripting.APIUpdating; +using static UnityEngine.EnumDataUtility; namespace UnityEngine.UIElements { @@ -41,11 +43,13 @@ internal static bool ExtractValue(IUxmlAttributes bag, CreationContext cc, out T } /// - /// Makes a dropdown for switching between enum values. + /// Makes a dropdown for switching between enum values. For more information, refer to [[wiki:|UIE-uxml-element EnumField|UXML element EnumField]]. /// [MovedFrom(true, UpgradeConstants.EditorNamespace, UpgradeConstants.EditorAssembly)] public class EnumField : BaseField { + internal static readonly DataBindingProperty textProperty = nameof(text); + /// /// Instantiates an using the data read from a UXML file. /// @@ -112,6 +116,7 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext /// /// Return the text value of the currently selected enum. /// + [CreateProperty(ReadOnly = true)] public string text { get { return m_TextElement.text; } @@ -214,34 +219,44 @@ public void Init(Enum defaultValue, bool includeObsoleteValues) m_IncludeObsoleteValues = includeObsoleteValues; PopulateDataFromType(defaultValue.GetType()); - SetValueWithoutNotify(defaultValue); + // If the value is the same then we just need to ensure that the value label is + // updated as m_EnumType may have been null when the value was set. (UUM-28904) + if (!Enum.Equals(rawValue, defaultValue)) + SetValueWithoutNotify(defaultValue); + else + UpdateValueLabel(defaultValue); } internal void PopulateDataFromType(Type enumType) { m_EnumType = enumType; - m_EnumData = EnumDataUtility.GetCachedEnumData(m_EnumType, !includeObsoleteValues); + m_EnumData = GetCachedEnumData(m_EnumType, includeObsoleteValues ? CachedType.IncludeObsoleteExceptErrors : CachedType.ExcludeObsolete); } public override void SetValueWithoutNotify(Enum newValue) { - if (rawValue != newValue) + if (!Enum.Equals(rawValue, newValue)) { base.SetValueWithoutNotify(newValue); if (m_EnumType == null) return; - int idx = Array.IndexOf(m_EnumData.values, newValue); + UpdateValueLabel(newValue); + } + } - if (idx >= 0 & idx < m_EnumData.values.Length) - { - m_TextElement.text = m_EnumData.displayNames[idx]; - } - else - { - m_TextElement.text = string.Empty; - } + void UpdateValueLabel(Enum value) + { + int idx = Array.IndexOf(m_EnumData.values, value); + + if (idx >= 0 & idx < m_EnumData.values.Length) + { + m_TextElement.text = m_EnumData.displayNames[idx]; + } + else + { + m_TextElement.text = string.Empty; } } @@ -323,6 +338,10 @@ protected override void UpdateMixedValueContent() { m_TextElement.text = mixedValueString; } + else + { + UpdateValueLabel(value); + } m_TextElement.EnableInClassList(labelUssClassName, showMixedValue); m_TextElement.EnableInClassList(mixedValueLabelUssClassName, showMixedValue); diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/FloatField.cs b/ModuleOverrides/com.unity.ui/Core/Controls/FloatField.cs index 554786172c..10e8d17266 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/FloatField.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/FloatField.cs @@ -8,7 +8,7 @@ namespace UnityEngine.UIElements { /// - /// Makes a text field for entering a float. + /// Makes a text field for entering a float. For more information, refer to [[wiki:UIE-uxml-element-FloatField|UXML element FloatField]]. /// [MovedFrom(true, UpgradeConstants.EditorNamespace, UpgradeConstants.EditorAssembly)] public class FloatField : TextValueField @@ -42,7 +42,8 @@ protected override string ValueToString(float v) /// The float parsed from the string. protected override float StringToValue(string str) { - return UINumericFieldsUtils.StringToDouble(str, out var v) ? Mathf.ClampToFloat(v) : rawValue; + var success = UINumericFieldsUtils.TryConvertStringToFloat(str, textInputBase.originalText, out var v); + return success ? v : rawValue; } /// @@ -86,7 +87,7 @@ public FloatField(string label, int maxLength = kMaxLengthNone) internal override bool CanTryParse(string textString) => float.TryParse(textString, out _); /// - /// Modify the value using a 3D delta and a speed, typically coming from an input device. + /// Applies the values of a 3D delta and a speed from an input device. /// /// A vector used to compute the value change. /// A multiplier for the value change. @@ -131,9 +132,8 @@ protected override string ValueToString(float v) protected override float StringToValue(string str) { - double v; - UINumericFieldsUtils.StringToDouble(str, out v); - return Mathf.ClampToFloat(v); + UINumericFieldsUtils.TryConvertStringToFloat(str, originalText, out var v); + return v; } } } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/Foldout.cs b/ModuleOverrides/com.unity.ui/Core/Controls/Foldout.cs index e4e0febe41..048bde6eca 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/Foldout.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/Foldout.cs @@ -2,6 +2,8 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using Unity.Properties; + namespace UnityEngine.UIElements { /// @@ -15,6 +17,9 @@ namespace UnityEngine.UIElements /// public class Foldout : BindableElement, INotifyValueChanged { + internal static readonly DataBindingProperty textProperty = nameof(text); + internal static readonly DataBindingProperty valueProperty = nameof(value); + /// /// Instantiates a using the data from a UXML file. /// @@ -54,10 +59,10 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext } Toggle m_Toggle; - VisualElement m_Container; - internal Toggle toggle => m_Toggle; + VisualElement m_Container; + /// /// This element contains the elements that are shown or hidden when you toggle the . /// @@ -66,23 +71,28 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext /// /// This is the text of the toggle's label. /// + [CreateProperty] public string text { get => m_Toggle.text; set { + var previous = text; m_Toggle.text = value; m_Toggle.visualInput.Q(className: Toggle.textUssClassName)?.AddToClassList(textUssClassName); + if (string.CompareOrdinal(previous, text) != 0) + NotifyPropertyChanged(textProperty); } } - [SerializeField] + [SerializeField, DontCreateProperty] private bool m_Value; /// /// This is the state of the Foldout's toggle. It is true if the is open and its contents are /// visible, and false if the Foldout is closed, and its contents are hidden. /// + [CreateProperty] public bool value { get => m_Value; @@ -97,6 +107,7 @@ public bool value SetValueWithoutNotify(value); SendEvent(evt); SaveViewData(); + NotifyPropertyChanged(valueProperty); } } } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/GenericDropdownMenu.cs b/ModuleOverrides/com.unity.ui/Core/Controls/GenericDropdownMenu.cs index 00ec090236..3a02ee6639 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/GenericDropdownMenu.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/GenericDropdownMenu.cs @@ -129,9 +129,6 @@ void OnDetachFromPanel(DetachFromPanelEvent evt) if (evt.originPanel == null) return; - m_MenuContainer.UnregisterCallback(OnAttachToPanel); - m_MenuContainer.UnregisterCallback(OnDetachFromPanel); - contentContainer.RemoveManipulator(m_NavigationManipulator); m_MenuContainer.UnregisterCallback(OnPointerDown); m_MenuContainer.UnregisterCallback(OnPointerMove); @@ -148,6 +145,7 @@ void Hide(bool giveFocusBack = false) if (m_TargetElement != null) { + m_TargetElement.UnregisterCallback(OnTargetElementDetachFromPanel); m_TargetElement.pseudoStates ^= PseudoStates.Active; if (giveFocusBack) m_TargetElement.Focus(); diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/GroupBox.cs b/ModuleOverrides/com.unity.ui/Core/Controls/GroupBox.cs index 3dd682525c..2266417c64 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/GroupBox.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/GroupBox.cs @@ -3,6 +3,7 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; +using Unity.Properties; namespace UnityEngine.UIElements { @@ -14,6 +15,8 @@ namespace UnityEngine.UIElements /// public class GroupBox : BindableElement, IGroupBox { + internal static readonly DataBindingProperty textProperty = nameof(text); + /// /// Instantiates a using data from a UXML file. /// @@ -57,14 +60,20 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext Label m_TitleLabel; + // Needed by the UIBuilder for authoring in the viewport + internal Label titleLabel => m_TitleLabel; + /// /// The title text of the box. /// + [CreateProperty] public string text { get => m_TitleLabel?.text; set { + var previous = text; + if (!string.IsNullOrEmpty(value)) { // Lazy allocation of label if needed... @@ -82,6 +91,9 @@ public string text m_TitleLabel.RemoveFromHierarchy(); m_TitleLabel = null; } + + if (string.CompareOrdinal(previous, text) != 0) + NotifyPropertyChanged(textProperty); } } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/HelpBox.cs b/ModuleOverrides/com.unity.ui/Core/Controls/HelpBox.cs index 9c4b36ae69..075a6f6b21 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/HelpBox.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/HelpBox.cs @@ -2,6 +2,8 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using Unity.Properties; + namespace UnityEngine.UIElements { /// @@ -28,7 +30,7 @@ public enum HelpBoxMessageType } /// - /// Makes a help box with a message to the user. + /// Makes a help box with a message to the user. For more information, refer to [[wiki:UIE-uxml-element-HelpBox|UXML element HelpBox]]. /// /// /// @@ -50,6 +52,9 @@ public enum HelpBoxMessageType /// public class HelpBox : VisualElement { + internal static readonly DataBindingProperty textProperty = nameof(text); + internal static readonly DataBindingProperty messageTypeProperty = nameof(messageType); + /// /// The USS class name for Elements of this type. /// @@ -112,15 +117,24 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext /// /// The message text. /// + [CreateProperty] public string text { get { return m_Label.text; } - set { m_Label.text = value; } + set + { + var previous = text; + m_Label.text = value; + + if (string.CompareOrdinal(previous, text) != 0) + NotifyPropertyChanged(textProperty); + } } /// /// The type of message. /// + [CreateProperty] public HelpBoxMessageType messageType { get { return m_HelpBoxMessageType; } @@ -130,6 +144,7 @@ public HelpBoxMessageType messageType { m_HelpBoxMessageType = value; UpdateIcon(value); + NotifyPropertyChanged(messageTypeProperty); } } } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/Image.cs b/ModuleOverrides/com.unity.ui/Core/Controls/Image.cs index f9e3f41007..2a36978182 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/Image.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/Image.cs @@ -2,17 +2,28 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License -using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Unity.Properties; using UnityEngine.UIElements.StyleSheets; namespace UnityEngine.UIElements { /// /// A representing a source texture. + /// + /// **Note**: This is not related to the `UnityEngine.UI.Image` uGUI control. This is the Image control for the UI Toolkit framework. /// public class Image : VisualElement { + internal static readonly DataBindingProperty imageProperty = nameof(image); + internal static readonly DataBindingProperty spriteProperty = nameof(sprite); + internal static readonly DataBindingProperty vectorImageProperty = nameof(vectorImage); + internal static readonly DataBindingProperty sourceRectProperty = nameof(sourceRect); + internal static readonly DataBindingProperty uvProperty = nameof(uv); + internal static readonly DataBindingProperty scaleModeProperty = nameof(scaleMode); + internal static readonly DataBindingProperty tintColorProperty = nameof(tintColor); + /// /// Instantiates an using the data read from a UXML file. /// @@ -39,126 +50,111 @@ public override IEnumerable uxmlChildElementsDescri private Rect m_UV; private Color m_TintColor; - private bool m_ImageIsInline; + // Internal for tests + internal bool m_ImageIsInline; private bool m_ScaleModeIsInline; private bool m_TintColorIsInline; - /// - /// The texture to display in this image. + /// The texture to display in this image. If you assign a `Texture` or `Texture2D`, the Image element will resize and show the assigned texture. /// + [CreateProperty] public Texture image { - get { return m_Image; } + get => m_Image; set { - if (value != null && (m_Sprite != null || m_VectorImage != null)) - { - var unsetProp = m_Sprite != null ? "sprite" : "vector image"; - Debug.LogWarning($"Image object already has a background, removing {unsetProp}"); - m_Sprite = null; - m_VectorImage = null; - } + if (m_Image == value && m_ImageIsInline) + return; + m_ImageIsInline = value != null; - if (m_Image != value) - { - m_Image = value; - IncrementVersion(VersionChangeType.Layout | VersionChangeType.Repaint); - if (m_Image == null) - { - m_UV = new Rect(0, 0, 1, 1); - } - } + SetProperty(value, ref m_Image, ref m_Sprite, ref m_VectorImage, imageProperty); } } /// /// The sprite to display in this image. /// + [CreateProperty] public Sprite sprite { - get { return m_Sprite; } + get => m_Sprite; set { - if (value != null && (m_Image != null || m_VectorImage != null)) - { - var unsetProp = m_Image != null ? "texture" : "vector image"; - Debug.LogWarning($"Image object already has a background, removing {unsetProp}"); - m_Image = null; - m_VectorImage = null; - } + if (m_Sprite == value && m_ImageIsInline) + return; + m_ImageIsInline = value != null; - if (m_Sprite != value) - { - m_Sprite = value; - IncrementVersion(VersionChangeType.Layout | VersionChangeType.Repaint); - } + SetProperty(value, ref m_Sprite, ref m_Image, ref m_VectorImage, spriteProperty); } } - /// /// The to display in this image. /// + [CreateProperty] public VectorImage vectorImage { - get { return m_VectorImage; } + get => m_VectorImage; set { - if (value != null && (m_Image != null || m_Sprite != null)) - { - var unsetProp = m_Image != null ? "texture" : "sprite"; - Debug.LogWarning($"Image object already has a background, removing {unsetProp}"); - m_Image = null; - m_Sprite = null; - } + if (m_VectorImage == value && m_ImageIsInline) + return; + m_ImageIsInline = value != null; - if (m_VectorImage != value) - { - m_VectorImage = value; - IncrementVersion(VersionChangeType.Layout | VersionChangeType.Repaint); - if (m_VectorImage == null) - { - m_UV = new Rect(0, 0, 1, 1); - } - } + SetProperty(value, ref m_VectorImage, ref m_Image, ref m_Sprite, vectorImageProperty); } } /// /// The source rectangle inside the texture relative to the top left corner. /// + [CreateProperty] public Rect sourceRect { - get { return GetSourceRect(); } + get => GetSourceRect(); set { + if (GetSourceRect() == value) + return; + if (sprite != null) { Debug.LogError("Cannot set sourceRect on a sprite image"); return; } CalculateUV(value); + NotifyPropertyChanged(sourceRectProperty); } } /// /// The base texture coordinates of the Image relative to the bottom left corner. /// + [CreateProperty] public Rect uv { - get { return m_UV; } - set { m_UV = value; } + get => m_UV; + set + { + if (m_UV == value) + return; + m_UV = value; + NotifyPropertyChanged(uvProperty); + } } /// /// ScaleMode used to display the Image. /// + [CreateProperty] public ScaleMode scaleMode { - get { return m_ScaleMode; } + get => m_ScaleMode; set { + if (m_ScaleMode == value && m_ScaleModeIsInline) + return; m_ScaleModeIsInline = true; SetScaleMode(value); } @@ -167,20 +163,16 @@ public ScaleMode scaleMode /// /// Tinting color for this Image. /// + [CreateProperty] public Color tintColor { - get - { - return m_TintColor; - } + get => m_TintColor; set { + if (m_TintColor == value && m_TintColorIsInline) + return; m_TintColorIsInline = true; - if (m_TintColor != value) - { - m_TintColor = value; - IncrementVersion(VersionChangeType.Repaint); - } + SetTintColor(value); } } @@ -251,9 +243,10 @@ protected internal override Vector2 DoMeasure(float desiredWidth, MeasureMode wi // covers the MeasureMode.Exactly case Rect rect = sourceRect; - bool hasImagePosition = rect != Rect.zero; - measuredWidth = hasImagePosition ? rect.width : sourceSize.x; - measuredHeight = hasImagePosition ? rect.height : sourceSize.y; + bool hasRect = rect != Rect.zero; + // UUM-17229: rect width/height can be negative (e.g. when the UVs are flipped) + measuredWidth = hasRect ? Mathf.Abs(rect.width) : sourceSize.x; + measuredHeight = hasRect ? Mathf.Abs(rect.height) : sourceSize.y; if (widthMode == MeasureMode.AtMost) { @@ -281,7 +274,7 @@ private void OnGenerateVisualContent(MeshGenerationContext mgc) else if (sprite != null) { var slices = Vector4.zero; - rectParams = UIR.MeshGenerator.RectangleParams.MakeSprite(alignedRect, sprite, scaleMode, panel.contextType, false, ref slices); + rectParams = UIR.MeshGenerator.RectangleParams.MakeSprite(alignedRect, uv, sprite, scaleMode, panel.contextType, false, ref slices); } else if (vectorImage != null) rectParams = UIR.MeshGenerator.RectangleParams.MakeVectorTextured(alignedRect, uv, vectorImage, scaleMode, panel.contextType); @@ -298,41 +291,76 @@ private void OnGenerateVisualContent(MeshGenerationContext mgc) private void OnCustomStyleResolved(CustomStyleResolvedEvent e) { // We should consider not exposing image as a style at all, since it's intimately tied to uv/sourceRect - Texture2D textureValue = null; - Sprite spriteValue = null; - VectorImage vectorImageValue = null; - string scaleModeValue; - Color tintValue = Color.white; - ICustomStyle customStyle = e.customStyle; - if (!m_ImageIsInline && customStyle.TryGetValue(s_ImageProperty, out textureValue)) + ReadCustomProperties(e.customStyle); + } + + private void ReadCustomProperties(ICustomStyle customStyleProvider) + { + if (!m_ImageIsInline) { - m_Image = textureValue; - m_Sprite = null; - m_VectorImage = null; + if (customStyleProvider.TryGetValue(s_ImageProperty, out var textureValue)) + { + SetProperty(textureValue, ref m_Image, ref m_Sprite, ref m_VectorImage, imageProperty); + } + else if (customStyleProvider.TryGetValue(s_SpriteProperty, out var spriteValue)) + { + SetProperty(spriteValue, ref m_Sprite, ref m_Image, ref m_VectorImage, spriteProperty); + } + else if (customStyleProvider.TryGetValue(s_VectorImageProperty, out var vectorImageValue)) + { + SetProperty(vectorImageValue, ref m_VectorImage, ref m_Image, ref m_Sprite, vectorImageProperty); + } + // If the value is not inline and none of the custom style properties are resolved, unset the value. + else + { + ClearProperty(); + } } - if (!m_ImageIsInline && customStyle.TryGetValue(s_SpriteProperty, out spriteValue)) + if (!m_ScaleModeIsInline && customStyleProvider.TryGetValue(s_ScaleModeProperty, out var scaleModeValue)) { - m_Image = null; - m_Sprite = spriteValue; - m_VectorImage = null; + StylePropertyUtil.TryGetEnumIntValue(StyleEnumType.ScaleMode, scaleModeValue, out var intValue); + SetScaleMode((ScaleMode)intValue); } - if (!m_ImageIsInline && customStyle.TryGetValue(s_VectorImageProperty, out vectorImageValue)) + if (!m_TintColorIsInline && customStyleProvider.TryGetValue(s_TintColorProperty, out var tintValue)) { - m_Image = null; - m_Sprite = null; - m_VectorImage = vectorImageValue; + SetTintColor(tintValue); } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetProperty(T0 src, ref T0 dst, ref T1 alt0, ref T2 alt1, DataBindingProperty binding) + where T0 : Object where T1 : Object where T2 : Object + { + if (src == dst) + return; - if (!m_ScaleModeIsInline && customStyle.TryGetValue(s_ScaleModeProperty, out scaleModeValue)) + dst = src; + + if (dst != null) { - StylePropertyUtil.TryGetEnumIntValue(StyleEnumType.ScaleMode, scaleModeValue, out var intValue); - SetScaleMode((ScaleMode)intValue); + alt0 = null; + alt1 = null; + } + + if (dst == null) + { + uv = new Rect(0, 0, 1, 1); + ReadCustomProperties(customStyle); } - if (!m_TintColorIsInline && customStyle.TryGetValue(s_TintColorProperty, out tintValue)) - m_TintColor = tintValue; + IncrementVersion(VersionChangeType.Layout | VersionChangeType.Repaint); + NotifyPropertyChanged(binding); + } + + private void ClearProperty() + { + if (m_ImageIsInline) + return; + image = null; + sprite = null; + vectorImage = null; } private void SetScaleMode(ScaleMode mode) @@ -341,6 +369,17 @@ private void SetScaleMode(ScaleMode mode) { m_ScaleMode = mode; IncrementVersion(VersionChangeType.Repaint); + NotifyPropertyChanged(scaleModeProperty); + } + } + + private void SetTintColor(Color color) + { + if (m_TintColor != color) + { + m_TintColor = color; + IncrementVersion(VersionChangeType.Repaint); + NotifyPropertyChanged(tintColorProperty); } } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/InputField/BaseField.cs b/ModuleOverrides/com.unity.ui/Core/Controls/InputField/BaseField.cs index 5d279f3f78..15b3db2366 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/InputField/BaseField.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/InputField/BaseField.cs @@ -4,19 +4,43 @@ using System; using System.Collections.Generic; +using Unity.Properties; namespace UnityEngine.UIElements { internal interface IPrefixLabel { string label { get; } + Label labelElement { get; } } /// - /// Abstract base class for controls. + /// Interface for all editable elements. /// - public abstract class BaseField : BindableElement, INotifyValueChanged, IMixedValueSupport, IPrefixLabel + internal interface IEditableElement { + internal Action editingStarted { get; set; } + internal Action editingEnded { get; set; } + } + + /// + /// Abstract base class for controls. + /// A BaseField is a base class for field elements like and . + /// To align a BaseField element automatically with other fields in an Inspector window, + /// use the @@.unity-base-field__aligned@@ USS class. This style class is designed for use with + /// Inspector elements like , which has the style class by default. + /// However, if you manually add a child BaseField element to a PropertyField, you must add + /// the style class manually. + /// When the style class is present, the field automatically calculates the label width + /// to align with other fields in the Inspector window. If there are IMGUI fields present, + /// UI Toolkit fields are aligned with them for consistency and compatibility. + /// + public abstract class BaseField : BindableElement, INotifyValueChanged, IMixedValueSupport, IPrefixLabel, IEditableElement + { + internal static readonly DataBindingProperty valueProperty = nameof(value); + internal static readonly DataBindingProperty labelProperty = nameof(label); + internal static readonly DataBindingProperty showMixedValueProperty = nameof(showMixedValue); + /// /// Defines for the . /// @@ -38,26 +62,6 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext base.Init(ve, bag, cc); ((BaseField)ve).label = m_Label.GetValueFromBag(bag, cc); } - - internal static List ParseChoiceList(string choicesFromBag) - { - if (string.IsNullOrEmpty(choicesFromBag.Trim())) - return null; - - // Here the choices is comma separated in the string... - var choices = choicesFromBag.Split(','); - - if (choices.Length != 0) - { - var listOfChoices = new List(); - foreach (var choice in choices) - { - listOfChoices.Add(choice.Trim()); - } - return listOfChoices; - } - return null; - } } /// @@ -138,7 +142,7 @@ internal VisualElement visualInput } } - [SerializeField] + [SerializeField, DontCreateProperty] TValueType m_Value; /// @@ -155,6 +159,7 @@ protected TValueType rawValue /// /// The value associated with the field. /// + [CreateProperty] public virtual TValueType value { get @@ -175,6 +180,7 @@ public virtual TValueType value evt.elementTarget = this; SendEvent(evt); } + NotifyPropertyChanged(valueProperty); } else { @@ -191,6 +197,7 @@ public virtual TValueType value /// /// The string representing the label that will appear beside the field. /// + [CreateProperty] public string label { get @@ -202,7 +209,6 @@ public string label if (labelElement.text != value) { labelElement.text = value; - if (string.IsNullOrEmpty(labelElement.text)) { AddToClassList(noLabelVariantUssClassName); @@ -212,10 +218,12 @@ public string label { if (!Contains(labelElement)) { - Insert(0, labelElement); + hierarchy.Insert(0, labelElement); RemoveFromClassList(noLabelVariantUssClassName); } } + + NotifyPropertyChanged(labelProperty); } } } @@ -225,6 +233,7 @@ public string label /// /// When set to true, gives the field the appearance of editing multiple different values. /// + [CreateProperty] public bool showMixedValue { get => m_ShowMixedValue; @@ -236,6 +245,8 @@ public bool showMixedValue // Once value has been set, update the field's appearance UpdateMixedValueContent(); + + NotifyPropertyChanged(showMixedValueProperty); } } @@ -262,6 +273,9 @@ protected Label mixedValueLabel private VisualElement m_CachedContextWidthElement; private VisualElement m_CachedInspectorElement; + Action IEditableElement.editingStarted { get; set; } + Action IEditableElement.editingEnded { get; set; } + internal BaseField(string label) { isCompositeRoot = true; @@ -297,6 +311,9 @@ protected BaseField(string label, VisualElement visualInput) private void OnAttachToPanel(AttachToPanelEvent e) { + // indicates the start and end of the field value being edited + RegisterEditingCallbacks(); + if (e.destinationPanel == null) { return; @@ -347,9 +364,33 @@ private void OnAttachToPanel(AttachToPanelEvent e) private void OnDetachFromPanel(DetachFromPanelEvent e) { + UnregisterEditingCallbacks(); + onValidateValue = null; } + internal virtual void RegisterEditingCallbacks() + { + RegisterCallback(StartEditing); + RegisterCallback(EndEditing); + } + + internal virtual void UnregisterEditingCallbacks() + { + UnregisterCallback(StartEditing); + UnregisterCallback(EndEditing); + } + + internal void StartEditing(EventBase e) + { + ((IEditableElement)this).editingStarted?.Invoke(); + } + + internal void EndEditing(EventBase e) + { + ((IEditableElement)this).editingEnded?.Invoke(); + } + private void OnCustomStyleResolved(CustomStyleResolvedEvent evt) { if (evt.customStyle.TryGetValue(s_LabelWidthRatioProperty, out var labelWidthRatio)) diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/InputField/KeyboardTextEditor.cs b/ModuleOverrides/com.unity.ui/Core/Controls/InputField/KeyboardTextEditor.cs index f306392c3b..07336a2b63 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/InputField/KeyboardTextEditor.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/InputField/KeyboardTextEditor.cs @@ -94,11 +94,13 @@ void OnKeyDown(KeyDownEvent evt) return; // We rely on KeyCode.Tab for both navigation and inserting tabulation. - if (c == '\t') + // On Linux Platform regular tab event occupies both keycode and character fields + if (c == '\t' && evt.keyCode == KeyCode.None && evt.modifiers == EventModifiers.None) return; // Ignore tab in single-line text fields, modifier+tab in multiline text fields - if (evt.keyCode == KeyCode.Tab) + // On Linux Platform modifier+tab case will be represented as keycode=None and character='\t' + if (evt.keyCode == KeyCode.Tab || (evt.keyCode == KeyCode.Tab && evt.character == '\t' && evt.modifiers == EventModifiers.Shift)) { if(!textElement.edition.multiline || evt.shiftKey) { @@ -145,7 +147,7 @@ void OnKeyDown(KeyDownEvent evt) if (!textElement.edition.AcceptCharacter(c)) return; - if (c >= k_Space || evt.keyCode == KeyCode.Tab || c == '\n' || c == '\r' || c == k_LineFeed) + if (c >= k_Space || evt.keyCode == KeyCode.Tab || (textElement.edition.multiline && !evt.altKey && (c == '\n' || c == '\r' || c == k_LineFeed))) { editingUtilities.Insert(c); m_Changed = true; diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/InputField/TextField.cs b/ModuleOverrides/com.unity.ui/Core/Controls/InputField/TextField.cs index 278a284c34..9217b76c98 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/InputField/TextField.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/InputField/TextField.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using Unity.Properties; namespace UnityEngine.UIElements { @@ -12,6 +13,8 @@ namespace UnityEngine.UIElements /// public class TextField : TextInputBaseField { + internal static readonly DataBindingProperty multilineProperty = nameof(multiline); + // This property to alleviate the fact we have to cast all the time TextInput textInput => (TextInput)textInputBase; @@ -44,7 +47,6 @@ public class TextField : TextInputBaseField public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc) { TextField field = ((TextField)ve); - field.multiline = m_Multiline.GetValueFromBag(bag, cc); base.Init(ve, bag, cc); // Re-defining the value to account for the "obsolete" text property. @@ -55,16 +57,33 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext { field.SetValueWithoutNotify(value); } + + field.multiline = m_Multiline.GetValueFromBag(bag, cc); + + // \n characters in the string are only removed when multiline goes from true to false. + // If the field value in the bag contains \n and multiline is false in the bag, we remove any \n here. + if (!field.multiline && !string.IsNullOrEmpty(field.value) && field.value.Contains("\n")) + { + field.SetValueWithoutNotify(field.value.Replace("\n","")); + } } } /// /// Set this to true to allow multiple lines in the textfield and false if otherwise. /// + [CreateProperty] public bool multiline { get { return textInput.multiline; } - set { textInput.multiline = value; } + set + { + var previous = multiline; + textInput.multiline = value; + + if (previous != multiline) + NotifyPropertyChanged(multilineProperty); + } } /// @@ -125,9 +144,6 @@ public TextField(string label, int maxLength, bool multiline, bool isPasswordFie /// /// The string currently being exposed by the field. /// - /// - /// Note that changing this will not trigger a change event to be sent. - /// public override string value { get { return base.value; } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/InputField/TextInputFieldBase.cs b/ModuleOverrides/com.unity.ui/Core/Controls/InputField/TextInputFieldBase.cs index ab10eb8b63..09f23ff7c1 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/InputField/TextInputFieldBase.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/InputField/TextInputFieldBase.cs @@ -3,6 +3,7 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; +using Unity.Properties; namespace UnityEngine.UIElements { @@ -11,6 +12,27 @@ namespace UnityEngine.UIElements /// public abstract class TextInputBaseField : BaseField { + internal static readonly DataBindingProperty autoCorrectionProperty = nameof(autoCorrection); + internal static readonly DataBindingProperty hideMobileInputProperty = nameof(hideMobileInput); + internal static readonly DataBindingProperty keyboardTypeProperty = nameof(keyboardType); + internal static readonly DataBindingProperty isReadOnlyProperty = nameof(isReadOnly); + internal static readonly DataBindingProperty isPasswordFieldProperty = nameof(isPasswordField); + internal static readonly DataBindingProperty textSelectionProperty = nameof(textSelection); + internal static readonly DataBindingProperty textEditionProperty = nameof(textEdition); + internal static readonly DataBindingProperty selectionColorProperty = nameof(selectionColor); + internal static readonly DataBindingProperty cursorColorProperty = nameof(cursorColor); + internal static readonly DataBindingProperty cursorIndexProperty = nameof(cursorIndex); + internal static readonly DataBindingProperty cursorPositionProperty = nameof(cursorPosition); + internal static readonly DataBindingProperty selectIndexProperty = nameof(selectIndex); + internal static readonly DataBindingProperty selectAllOnFocusProperty = nameof(selectAllOnFocus); + internal static readonly DataBindingProperty selectAllOnMouseUpProperty = nameof(selectAllOnMouseUp); + internal static readonly DataBindingProperty maxLengthProperty = nameof(maxLength); + internal static readonly DataBindingProperty doubleClickSelectsWordProperty = nameof(doubleClickSelectsWord); + internal static readonly DataBindingProperty tripleClickSelectsLineProperty = nameof(tripleClickSelectsLine); + internal static readonly DataBindingProperty isDelayedProperty = nameof(isDelayed); + internal static readonly DataBindingProperty maskCharProperty = nameof(maskChar); + internal static readonly DataBindingProperty verticalScrollerVisibilityProperty = nameof(verticalScrollerVisibility); + static CustomStyleProperty s_SelectionColorProperty = new CustomStyleProperty("--unity-selection-color"); static CustomStyleProperty s_CursorColorProperty = new CustomStyleProperty("--unity-cursor-color"); internal const int kMaxLengthNone = -1; @@ -212,11 +234,13 @@ protected TextInputBaseField(string label, int maxLength, char maskChar, TextInp /// /// Retrieves this Field's TextElement ITextSelection /// + [CreateProperty(ReadOnly = true)] public ITextSelection textSelection => m_TextInputBase.textElement.selection; /// /// Retrieves this Field's TextElement ITextEdition /// + [CreateProperty(ReadOnly = true)] public ITextEdition textEdition => m_TextInputBase.textElement.edition; #region TextEdition @@ -232,16 +256,24 @@ protected Action onIsReadOnlyChanged /// /// Returns true if the field is read only. /// + [CreateProperty] public bool isReadOnly { get => textEdition.isReadOnly; - set => textEdition.isReadOnly = value; + set + { + if (textEdition.isReadOnly == value) + return; + textEdition.isReadOnly = value; + NotifyPropertyChanged(isReadOnlyProperty); + } } // Password field (indirectly lossy behaviour when activated via multiline) /// /// Returns true if the field is used to edit a password. /// + [CreateProperty] public bool isPasswordField { get => textEdition.isPassword; @@ -252,34 +284,56 @@ public bool isPasswordField textEdition.isPassword = value; m_TextInputBase.IncrementVersion(VersionChangeType.Repaint); + NotifyPropertyChanged(isPasswordFieldProperty); } } /// /// Determines if the touch screen keyboard auto correction is turned on or off. /// + [CreateProperty] public bool autoCorrection { get => textEdition.autoCorrection; - set => textEdition.autoCorrection = value; + set + { + if (textEdition.autoCorrection == value) + return; + textEdition.autoCorrection = value; + NotifyPropertyChanged(autoCorrectionProperty); + } } /// /// Hides or shows the mobile input field. /// + [CreateProperty] public bool hideMobileInput { get => textEdition.hideMobileInput; - set => textEdition.hideMobileInput = value; + set + { + if (textEdition.hideMobileInput == value) + return; + textEdition.hideMobileInput = value; + NotifyPropertyChanged(hideMobileInputProperty); + } } /// /// The type of mobile keyboard that will be used. /// + [CreateProperty] public TouchScreenKeyboardType keyboardType { get => textEdition.keyboardType; - set => textEdition.keyboardType = value; + set + { + if (textEdition.keyboardType == value) + return; + textEdition.keyboardType = value; + NotifyPropertyChanged(keyboardTypeProperty); + } } /// @@ -293,28 +347,49 @@ public TouchScreenKeyboard touchScreenKeyboard /// /// Maximum number of characters for the field. /// + [CreateProperty] public int maxLength { get => textEdition.maxLength; - set => textEdition.maxLength = value; + set + { + if (textEdition.maxLength == value) + return; + textEdition.maxLength = value; + NotifyPropertyChanged(maxLengthProperty); + } } /// /// If set to true, the value property isn't updated until either the user presses Enter or the text field loses focus. /// + [CreateProperty] public bool isDelayed { get => textEdition.isDelayed; - set => textEdition.isDelayed = value; + set + { + if (textEdition.isDelayed == value) + return; + textEdition.isDelayed = value; + NotifyPropertyChanged(isDelayedProperty); + } } /// /// The character used for masking in a password field. /// + [CreateProperty] public char maskChar { get => textEdition.maskChar; - set => textEdition.maskChar = value; + set + { + if (textEdition.maskChar == value) + return; + textEdition.maskChar = value; + NotifyPropertyChanged(maskCharProperty); + } } #endregion @@ -323,24 +398,34 @@ public char maskChar /// /// Background color of selected text. /// + [CreateProperty(ReadOnly = true)] public Color selectionColor => textSelection.selectionColor; /// /// Color of the cursor. /// + [CreateProperty(ReadOnly = true)] public Color cursorColor => textSelection.cursorColor; /// /// This is the cursor index in the text presented. /// + [CreateProperty] public int cursorIndex { get => textSelection.cursorIndex; - set => textSelection.cursorIndex = value; + set + { + if (textSelection.cursorIndex == value) + return; + textSelection.cursorIndex = value; + NotifyPropertyChanged(cursorIndexProperty); + } } /// /// The position of the text cursor inside the element. /// + [CreateProperty(ReadOnly = true)] public Vector2 cursorPosition { get => textSelection.cursorPosition; @@ -349,10 +434,17 @@ public Vector2 cursorPosition /// /// This is the selection index in the text presented. /// + [CreateProperty] public int selectIndex { get => textSelection.selectIndex; - set => textSelection.selectIndex = value; + set + { + if (textSelection.selectIndex == value) + return; + textSelection.selectIndex = value; + NotifyPropertyChanged(selectIndexProperty); + } } /// @@ -382,36 +474,65 @@ public void SelectRange(int cursorIndex, int selectionIndex) /// /// Controls whether the element's content is selected upon receiving focus. /// + [CreateProperty] public bool selectAllOnFocus { get => textSelection.selectAllOnFocus; - set => textSelection.selectAllOnFocus = value; + set + { + if (textSelection.selectAllOnFocus == value) + return; + textSelection.selectAllOnFocus = value; + NotifyPropertyChanged(selectAllOnFocusProperty); + } } /// /// Controls whether the element's content is selected when you mouse up for the first time. /// + [CreateProperty] public bool selectAllOnMouseUp { get => textSelection.selectAllOnMouseUp; - set => textSelection.selectAllOnMouseUp = value; + set + { + if (textSelection.selectAllOnMouseUp == value) + return; + textSelection.selectAllOnMouseUp = value; + NotifyPropertyChanged(selectAllOnMouseUpProperty); + } } /// /// Controls whether double clicking selects the word under the mouse pointer or not. /// + [CreateProperty] public bool doubleClickSelectsWord { get => textSelection.doubleClickSelectsWord; - set => textSelection.doubleClickSelectsWord = value; + set + { + if (textSelection.doubleClickSelectsWord == value) + return; + textSelection.doubleClickSelectsWord = value; + NotifyPropertyChanged(doubleClickSelectsWordProperty); + } } + /// /// Controls whether triple clicking selects the entire line under the mouse pointer or not. /// + [CreateProperty] public bool tripleClickSelectsLine { get => textSelection.tripleClickSelectsLine; - set => textSelection.tripleClickSelectsLine = value; + set + { + if (textSelection.tripleClickSelectsLine == value) + return; + textSelection.tripleClickSelectsLine = value; + NotifyPropertyChanged(tripleClickSelectsLineProperty); + } } /// @@ -435,10 +556,17 @@ public string text /// /// Option for controlling the visibility of the vertical scroll bar in the . /// + [CreateProperty] public ScrollerVisibility verticalScrollerVisibility { get => textInputBase.verticalScrollerVisibility; - set => textInputBase.SetVerticalScrollerVisibility(value); + set + { + if (textInputBase.verticalScrollerVisibility == value) + return; + textInputBase.SetVerticalScrollerVisibility(value); + NotifyPropertyChanged(verticalScrollerVisibilityProperty); + } } /// @@ -686,6 +814,11 @@ public string text } } + /// + /// The initial value of the input field before being edited. + /// + internal string originalText => textElement.originalText; + /// /// Converts a string to a value type. /// @@ -792,10 +925,7 @@ void ScrollViewOnGeometryChangedEvent(GeometryChangedEvent e) { if (e.oldRect.size == e.newRect.size) return; - - // The ScrollView is updating its Scroller values the next frame after the GeometryChangedEvent. - // We need to make sure the new ScrollOffset value is not clamped. - scrollView.OnUpdateScrollers += UpdateScrollOffset; + UpdateScrollOffset(); } void TextElementOnGeometryChangedEvent(GeometryChangedEvent e) @@ -803,21 +933,20 @@ void TextElementOnGeometryChangedEvent(GeometryChangedEvent e) if (e.oldRect.size == e.newRect.size) return; - UpdateScrollOffset(); + var widthChanged = Math.Abs(e.oldRect.size.x - e.newRect.size.x) > UIRUtility.k_Epsilon; + + UpdateScrollOffset(isBackspace: false, widthChanged); } internal void OnInputCustomStyleResolved(CustomStyleResolvedEvent e) { - Color selectionValue = Color.clear; - Color cursorValue = Color.clear; - // These don't quite follow the inline style behavior // (aka setting the value via code should always overrides the one from styleSheets) ICustomStyle customStyle = e.customStyle; - if (customStyle.TryGetValue(s_SelectionColorProperty, out selectionValue)) + if (customStyle.TryGetValue(s_SelectionColorProperty, out Color selectionValue)) textSelection.selectionColor = selectionValue; - if (customStyle.TryGetValue(s_CursorColorProperty, out cursorValue)) + if (customStyle.TryGetValue(s_CursorColorProperty, out Color cursorValue)) textSelection.cursorColor = cursorValue; SetScrollViewMode(); @@ -835,28 +964,32 @@ internal virtual bool AcceptCharacter(char c) return !textEdition.isReadOnly && enabledInHierarchy; } + internal void UpdateScrollOffset(bool isBackspace = false) + { + UpdateScrollOffset(isBackspace, widthChanged: false); + } + // scrollOffset is used in automated tests internal Vector2 scrollOffset = Vector2.zero; bool m_ScrollViewWasClamped; - internal void UpdateScrollOffset(bool isBackspace = false) + internal void UpdateScrollOffset(bool isBackspace, bool widthChanged) { var selection = textSelection; - if (selection.cursorIndex < 0) + if (selection.cursorIndex < 0 || (selection.cursorIndex <= 0 && selection.selectIndex <= 0 && scrollOffset == Vector2.zero)) return; if (scrollView != null) { - scrollOffset = GetScrollOffset(scrollView.scrollOffset.x, scrollView.scrollOffset.y, scrollView.contentViewport.layout.width, isBackspace); + scrollOffset = GetScrollOffset(scrollView.scrollOffset.x, scrollView.scrollOffset.y, scrollView.contentViewport.layout.width, isBackspace, widthChanged); scrollView.scrollOffset = scrollOffset; m_ScrollViewWasClamped = scrollOffset.x > scrollView.scrollOffset.x || scrollOffset.y > scrollView.scrollOffset.y; - scrollView.OnUpdateScrollers -= UpdateScrollOffset; } else { var t = textElement.transform.position; - scrollOffset = GetScrollOffset(scrollOffset.x, scrollOffset.y, contentRect.width, isBackspace); + scrollOffset = GetScrollOffset(scrollOffset.x, scrollOffset.y, contentRect.width, isBackspace, widthChanged); t.y = -Mathf.Min(scrollOffset.y, Math.Abs(textElement.contentRect.height - contentRect.height)); t.x = -scrollOffset.x; @@ -867,7 +1000,7 @@ internal void UpdateScrollOffset(bool isBackspace = false) } Vector2 lastCursorPos = Vector2.zero; - Vector2 GetScrollOffset(float xOffset, float yOffset, float contentViewportWidth, bool isBackspace = false) + Vector2 GetScrollOffset(float xOffset, float yOffset, float contentViewportWidth, bool isBackspace, bool widthChanged) { var cursorPos = textSelection.cursorPosition; var cursorWidth = textSelection.cursorWidth; @@ -884,14 +1017,21 @@ Vector2 GetScrollOffset(float xOffset, float yOffset, float contentViewportWidth // { // newXOffset = xOffset + cursorPos.x - lastCursorPos.x; // } - if (Math.Abs(lastCursorPos.x - cursorPos.x) > epsilon || m_ScrollViewWasClamped) + + if (Math.Abs(lastCursorPos.x - cursorPos.x) > epsilon || m_ScrollViewWasClamped || widthChanged) { - // Update scrollOffset when cursor moves right. - if (cursorPos.x > xOffset + contentViewportWidth - cursorWidth) - newXOffset = cursorPos.x + cursorWidth - contentViewportWidth; + // Update scrollOffset when cursor moves right or when the offset is not needed anymore. + if (cursorPos.x > xOffset + contentViewportWidth - cursorWidth + || xOffset > 0 && widthChanged) + { + var roundedValue = Mathf.Ceil(cursorPos.x + cursorWidth - contentViewportWidth); + newXOffset = Mathf.Max(roundedValue, 0); + } // Update scrollOffset when cursor moves left. else if (cursorPos.x < xOffset + leftScrollOffsetPadding) + { newXOffset = Mathf.Max(cursorPos.x - leftScrollOffsetPadding, 0); + } } if (textEdition.multiline && (Math.Abs(lastCursorPos.y - cursorPos.y) > epsilon || m_ScrollViewWasClamped)) @@ -906,8 +1046,10 @@ Vector2 GetScrollOffset(float xOffset, float yOffset, float contentViewportWidth lastCursorPos = cursorPos; - if (xOffset != newXOffset || yOffset != newYOffset) + if (Math.Abs(xOffset - newXOffset) > epsilon || Math.Abs(yOffset - newYOffset) > epsilon) + { return new Vector2(newXOffset, newYOffset); + } return scrollView != null ? scrollView.scrollOffset : scrollOffset; } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/InputField/TouchScreenTextEditor.cs b/ModuleOverrides/com.unity.ui/Core/Controls/InputField/TouchScreenTextEditor.cs index ffce234074..748456e61f 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/InputField/TouchScreenTextEditor.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/InputField/TouchScreenTextEditor.cs @@ -138,6 +138,7 @@ private void CloseTouchScreenKeyboard() textElement.m_TouchScreenKeyboard.active = false; textElement.m_TouchScreenKeyboard = null; m_TouchKeyboardPoller.Pause(); + TouchScreenKeyboard.hideInput = true; } } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/IntegerField.cs b/ModuleOverrides/com.unity.ui/Core/Controls/IntegerField.cs index ce7895687f..6940c1a1ca 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/IntegerField.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/IntegerField.cs @@ -43,7 +43,8 @@ protected override string ValueToString(int v) /// The integer parsed from the string. protected override int StringToValue(string str) { - return UINumericFieldsUtils.StringToLong(str, out var v) ? Mathf.ClampToInt(v) : rawValue; + var success = UINumericFieldsUtils.TryConvertStringToInt(str, textInputBase.originalText, out var v); + return success ? v : rawValue; } /// @@ -88,7 +89,7 @@ public IntegerField(string label, int maxLength = kMaxLengthNone) internal override bool CanTryParse(string textString) => int.TryParse(textString, out _); /// - /// Modify the value using a 3D delta and a speed, typically coming from an input device. + /// Applies the values of a 3D delta and a speed from an input device. /// /// A vector used to compute the value change. /// A multiplier for the value change. @@ -132,9 +133,8 @@ protected override string ValueToString(int v) protected override int StringToValue(string str) { - long v; - UINumericFieldsUtils.StringToLong(str, out v); - return Mathf.ClampToInt(v); + UINumericFieldsUtils.TryConvertStringToInt(str, originalText, out var v); + return v; } } } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/Label.cs b/ModuleOverrides/com.unity.ui/Core/Controls/Label.cs index 53af804715..3001daba75 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/Label.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/Label.cs @@ -7,7 +7,7 @@ namespace UnityEngine.UIElements { /// - /// Provides an Element displaying text. + /// Provides an Element displaying text. For more information, refer to [[wiki:UIE-uxml-element-label|UXML element Label]]. /// public class Label : TextElement { diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/ListView.cs b/ModuleOverrides/com.unity.ui/Core/Controls/ListView.cs index 6543122da2..0b2a90a0a3 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/ListView.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/ListView.cs @@ -4,6 +4,7 @@ using System; using System.Collections; +using Unity.Properties; namespace UnityEngine.UIElements { @@ -20,8 +21,10 @@ namespace UnityEngine.UIElements /// itemsSource. /// /// It's also recommended to supply the following properties for more complex items:\\ - /// - makeItem \\ - /// - bindItem \\ + /// - makeItem \\ + /// - bindItem \\ + /// - unbindItem \\ + /// - destroyItem \\ /// - fixedItemHeight when using \\ /// /// The ListView creates multiple objects for the visible items. As the user scrolls, the ListView @@ -35,7 +38,7 @@ namespace UnityEngine.UIElements /// \\ /// By default, the user can select one element in the list at a time. To change the default selection /// use the selection-type property in UXML or the property in C#. - /// To allow the user to select more than one element simultaneously, set the property to Selection.Multiple. + /// To allow the user to select more than one element simultaneously, set the property to Selection.Multiple. /// To prevent the user from selecting items, set the property to Selection.None.\\ /// \\ /// By default, all rows in the ListView have same background color. To make the row background colors @@ -55,57 +58,20 @@ namespace UnityEngine.UIElements /// To enable horizontal scrolling when the displayed element is wider than the visible area, set the /// horizontal-scrolling-enabled property in UXML or the /// to true. + /// + /// For more information, refer to [[wiki:UIE-uxml-element-ListView|ListView]]. /// /// - /// - /// ().Show(); - /// } - /// - /// public void OnEnable() - /// { - /// // Create a list of data. In this case, numbers from 1 to 1000. - /// const int itemCount = 1000; - /// var items = new List(itemCount); - /// for (int i = 1; i <= itemCount; i++) - /// items.Add(i.ToString()); - /// - /// // The "makeItem" function is called when the - /// // ListView needs more items to render. - /// Func makeItem = () => new Label(); - /// - /// // As the user scrolls through the list, the ListView object - /// // recycles elements created by the "makeItem" function, - /// // and invoke the "bindItem" callback to associate - /// // the element with the matching data item (specified as an index in the list). - /// Action bindItem = (e, i) => (e as Label).text = items[i]; - /// - /// // Provide the list view with an explicit height for every row - /// // so it can calculate how many items to actually display - /// const int itemHeight = 16; - /// - /// var listView = new ListView(items, itemHeight, makeItem, bindItem); - /// - /// listView.selectionType = SelectionType.Multiple; - /// - /// listView.itemsChosen += objects => Debug.Log(objects); - /// listView.selectionChanged += objects => Debug.Log(objects); - /// - /// listView.style.flexGrow = 1.0f; - /// - /// rootVisualElement.Add(listView); - /// } - /// } - /// ]]> - /// + /// The following example creates an editor window with a list view of a thousand items. + /// /// public class ListView : BaseListView { + internal static readonly DataBindingProperty makeItemProperty = nameof(makeItem); + internal static readonly DataBindingProperty bindItemProperty = nameof(bindItem); + internal static readonly DataBindingProperty unbindItemProperty = nameof(unbindItem); + internal static readonly DataBindingProperty destroyItemProperty = nameof(destroyItem); + /// /// Instantiates a using data from a UXML file. /// @@ -137,6 +103,7 @@ public class ListView : BaseListView /// If this property and are not set, Unity will either create a PropertyField if bound /// to a SerializedProperty, or create an empty label for any other case. /// + [CreateProperty] public new Func makeItem { get => m_MakeItem; @@ -146,6 +113,7 @@ public class ListView : BaseListView { m_MakeItem = value; Rebuild(); + NotifyPropertyChanged(makeItemProperty); } } } @@ -166,7 +134,11 @@ internal void SetMakeItemWithoutNotify(Func func) /// /// If this property and are not set, Unity will try to bind to a SerializedProperty if /// bound, or simply set text in the created Label. + /// + /// **Note:**: Setting this callback without also setting might result in unexpected behavior. + /// This is because the default implementation of unbindItem expects the default implementation of bindItem. /// + [CreateProperty] public new Action bindItem { get => m_BindItem; @@ -176,6 +148,7 @@ internal void SetMakeItemWithoutNotify(Func func) { m_BindItem = value; RefreshItems(); + NotifyPropertyChanged(bindItemProperty); } } } @@ -185,14 +158,31 @@ internal void SetBindItemWithoutNotify(Action callback) m_BindItem = callback; } + private Action m_UnbindItem; /// /// Callback for unbinding a data item from the VisualElement. /// /// /// The method called by this callback receives the VisualElement to unbind, and the index of the /// element to unbind it from. + /// + /// **Note:**: Setting this callback without also setting might cause unexpected behavior. + /// This is because the default implementation of bindItem expects the default implementation of unbindItem. /// - public new Action unbindItem { get; set; } + [CreateProperty] + public new Action unbindItem + { + get => m_UnbindItem; + set + { + if (value == m_UnbindItem) + return; + m_UnbindItem = value; + NotifyPropertyChanged(unbindItemProperty); + } + } + + private Action m_DestroyItem; /// /// Callback invoked when a created via is no longer needed and will be destroyed. @@ -200,7 +190,18 @@ internal void SetBindItemWithoutNotify(Action callback) /// /// The method called by this callback receives the VisualElement that will be destroyed from the pool. /// - public new Action destroyItem { get; set; } + [CreateProperty] + public new Action destroyItem + { + get => m_DestroyItem; + set + { + if (value == m_DestroyItem) + return; + m_DestroyItem = value; + NotifyPropertyChanged(destroyItemProperty); + } + } internal override bool HasValidDataAndBindings() { diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/LongField.cs b/ModuleOverrides/com.unity.ui/Core/Controls/LongField.cs index 55936c74e2..72c4dcc22d 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/LongField.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/LongField.cs @@ -43,7 +43,8 @@ protected override string ValueToString(long v) /// The long integer parsed from the string. protected override long StringToValue(string str) { - return UINumericFieldsUtils.StringToLong(str, out var v) ? v : rawValue; + var success = UINumericFieldsUtils.TryConvertStringToLong(str, textInputBase.originalText, out var v); + return success ? v : rawValue; } /// @@ -88,7 +89,7 @@ public LongField(string label, int maxLength = kMaxLengthNone) internal override bool CanTryParse(string textString) => long.TryParse(textString, out _); /// - /// Modify the value using a 3D delta and a speed, typically coming from an input device. + /// Applies the values of a 3D delta and a speed from an input device. /// /// A vector used to compute the value change. /// A multiplier for the value change. @@ -115,9 +116,12 @@ protected override string allowedCharacters public override void ApplyInputDeviceDelta(Vector3 delta, DeltaSpeed speed, long startValue) { double sensitivity = NumericFieldDraggerUtility.CalculateIntDragSensitivity(startValue); - float acceleration = NumericFieldDraggerUtility.Acceleration(speed == DeltaSpeed.Fast, speed == DeltaSpeed.Slow); - long v = StringToValue(text); - v += (long)Math.Round(NumericFieldDraggerUtility.NiceDelta(delta, acceleration) * sensitivity); + var acceleration = NumericFieldDraggerUtility.Acceleration(speed == DeltaSpeed.Fast, speed == DeltaSpeed.Slow); + var v = StringToValue(text); + var niceDelta = (long)Math.Round(NumericFieldDraggerUtility.NiceDelta(delta, acceleration) * sensitivity); + + v = ClampMinMaxLongValue(niceDelta, v); + if (parentLongField.isDelayed) { text = ValueToString(v); @@ -128,6 +132,27 @@ public override void ApplyInputDeviceDelta(Vector3 delta, DeltaSpeed speed, long } } + private long ClampMinMaxLongValue(long niceDelta, long value) + { + var niceDeltaAbs = Math.Abs(niceDelta); + if (niceDelta > 0) + { + if (value > 0 && niceDeltaAbs > long.MaxValue - value) + { + return long.MaxValue; + } + + return value + niceDelta; + } + + if (value < 0 && value < long.MinValue + niceDeltaAbs) + { + return long.MinValue; + } + + return value - niceDeltaAbs; + } + protected override string ValueToString(long v) { return v.ToString(formatString); @@ -135,8 +160,7 @@ protected override string ValueToString(long v) protected override long StringToValue(string str) { - long v; - UINumericFieldsUtils.StringToLong(str, out v); + UINumericFieldsUtils.TryConvertStringToLong(str, originalText, out var v); return v; } } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/MinMaxSlider.cs b/ModuleOverrides/com.unity.ui/Core/Controls/MinMaxSlider.cs index 167435b0f8..cf57bb619d 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/MinMaxSlider.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/MinMaxSlider.cs @@ -3,14 +3,21 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; +using Unity.Properties; namespace UnityEngine.UIElements { /// - /// A min/max slider containing a representation of a range. + /// A min/max slider containing a representation of a range. For more information, refer to [[wiki:UIE-uxml-element-MinMaxSlider|UXML element MinMaxSlider]]. /// public class MinMaxSlider : BaseField { + internal static readonly DataBindingProperty minValueProperty = nameof(minValue); + internal static readonly DataBindingProperty maxValueProperty = nameof(maxValue); + internal static readonly DataBindingProperty rangeProperty = nameof(range); + internal static readonly DataBindingProperty lowLimitProperty = nameof(lowLimit); + internal static readonly DataBindingProperty highLimitProperty = nameof(highLimit); + /// /// Instantiates a using the data read from a UXML file. /// @@ -77,12 +84,17 @@ private enum DragState /// /// This is the low value of the range represented on the slider. /// + [CreateProperty] public float minValue { get { return value.x; } set { + var previous = minValue; base.value = ClampValues(new Vector2(value, rawValue.y)); + + if (!Mathf.Approximately(previous, minValue)) + NotifyPropertyChanged(minValueProperty); } } @@ -90,12 +102,17 @@ public float minValue /// /// This is the high value of the range represented on the slider. /// + [CreateProperty] public float maxValue { get { return value.y; } set { + var previous = maxValue; base.value = ClampValues(new Vector2(rawValue.x, value)); + + if (!Mathf.Approximately(previous, maxValue)) + NotifyPropertyChanged(maxValueProperty); } } @@ -122,6 +139,7 @@ public override void SetValueWithoutNotify(Vector2 newValue) /// /// Returns the range of the low/high limits of the slider. /// + [CreateProperty(ReadOnly = true)] public float range { get { return Math.Abs(highLimit - lowLimit); } @@ -134,6 +152,7 @@ public float range /// /// This is the low limit of the slider. /// + [CreateProperty] public float lowLimit { get { return m_MinLimit; } @@ -152,6 +171,7 @@ public float lowLimit if (!string.IsNullOrEmpty(viewDataKey)) SaveViewData(); + NotifyPropertyChanged(lowLimitProperty); } } } @@ -160,6 +180,7 @@ public float lowLimit /// /// This is the high limit of the slider. /// + [CreateProperty] public float highLimit { get { return m_MaxLimit; } @@ -178,6 +199,7 @@ public float highLimit if (!string.IsNullOrEmpty(viewDataKey)) SaveViewData(); + NotifyPropertyChanged(highLimitProperty); } } } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/Columns.cs b/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/Columns.cs index 32fe2bba5c..0383efbded 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/Columns.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/Columns.cs @@ -111,6 +111,8 @@ public override void Init(ref T obj, IUxmlAttributes bag, CreationContext cc) bool m_ResizePreview; string m_PrimaryColumnName; + internal IList columns => m_Columns; + /// /// Indicates the column that needs to be considered as primary column, by id. /// diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/MultiColumnCollectionHeader.cs b/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/MultiColumnCollectionHeader.cs index bb4bb0cbc3..e43fba336b 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/MultiColumnCollectionHeader.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/MultiColumnCollectionHeader.cs @@ -17,8 +17,9 @@ class MultiColumnCollectionHeader : VisualElement, IDisposable const int kMaxStableLayoutPassCount = 2; // Beyond this threshold, DoLayout must be performed in the next frame; otherwise, this may lead to Layout instabilities. This is caused by the dependencies between the geometries of the header, the viewport and the content. [Serializable] - class ViewState : ISerializationCallbackReceiver + class ViewState { + [SerializeField] bool m_HasPersistedData; /// @@ -117,16 +118,6 @@ internal void Apply(MultiColumnCollectionHeader header) header.sortDescriptions.Add(sortDesc); } } - - public void OnBeforeSerialize() - { - m_HasPersistedData = true; - } - - public void OnAfterDeserialize() - { - m_HasPersistedData = true; - } } internal class ColumnData @@ -169,12 +160,15 @@ public SortedColumnState(SortColumnDescription desc, SortDirection dir) bool m_SortingEnabled; List m_SortedColumns; + SortColumnDescriptions m_SortDescriptions; List m_OldSortedColumnStates = new List(); bool m_SortingUpdatesTemporarilyDisabled; ViewState m_ViewState; bool m_ApplyingViewState; + internal bool isApplyingViewState => m_ApplyingViewState; + bool m_DoLayoutScheduled; /// @@ -205,7 +199,16 @@ public SortedColumnState(SortColumnDescription desc, SortDirection dir) /// /// The descriptions of sorted columns. /// - public SortColumnDescriptions sortDescriptions { get; } + public SortColumnDescriptions sortDescriptions + { + get => m_SortDescriptions; + protected internal set + { + m_SortDescriptions = value; + m_SortDescriptions.changed += UpdateSortedColumns; + UpdateSortedColumns(); + } + } /// /// The list of columns. @@ -243,6 +246,11 @@ public bool sortingEnabled /// public event Action contextMenuPopulateEvent; + /// + /// Sent whenever a ContextMenuPopulate event sent allowing user code to add its own actions to the context menu. + /// + internal event Action viewDataRestored; + /// /// Default constructor. /// @@ -262,7 +270,6 @@ public MultiColumnCollectionHeader(Columns columns, SortColumnDescriptions sortD this.columns = columns; m_SortedColumns = sortedColumns; this.sortDescriptions = sortDescriptions; - this.sortDescriptions.changed += UpdateSortedColumns; columnContainer = new VisualElement() { @@ -416,25 +423,31 @@ void OnColumnAdded(Column column) return; var columnElement = new MultiColumnHeaderColumn(column); + var resizeHandle = new MultiColumnHeaderColumnResizeHandle(); columnElement.RegisterCallback(OnColumnControlGeometryChanged); columnElement.clickable.clickedWithEventInfo += OnColumnClicked; // Prevent cursor change when hovering handles while drag reordering columns columnElement.mover.activeChanged += OnMoveManipulatorActivated; - columnContainer.Insert(column.visibleIndex, columnElement); - - var resHandle = new MultiColumnHeaderColumnResizeHandle(); - - resizeHandleContainer.Insert(column.visibleIndex, resHandle); - resHandle.dragArea.AddManipulator(new ColumnResizer(column)); + resizeHandle.dragArea.AddManipulator(new ColumnResizer(column)); columnDataMap[column] = new ColumnData() { control = columnElement, - resizeHandle = resHandle + resizeHandle = resizeHandle }; + if (column.visible) + { + columnContainer.Insert(column.visibleIndex, columnElement); + resizeHandleContainer.Insert(column.visibleIndex, resizeHandle); + } + else + { + OnColumnRemoved(column); + } + UpdateColumnControls(); SaveViewState(); } @@ -476,6 +489,8 @@ void OnColumnChanged(Column column, ColumnDataType type) OnColumnAdded(column); else OnColumnRemoved(column); + + ApplyColumnSorting(); } UpdateColumnControls(); @@ -492,7 +507,7 @@ void OnColumnChanged(Column column, ColumnDataType type) /// void OnColumnReordered(Column column, int from, int to) { - if (!column.visible) + if (!column.visible || from == to) return; if (columnDataMap.TryGetValue(column, out var columnData)) @@ -741,7 +756,8 @@ void RaiseColumnResized(int columnIndex) void RaiseColumnSortingChanged() { ApplyColumnSorting(); - columnSortingChanged?.Invoke(); + if (!m_ApplyingViewState) + columnSortingChanged?.Invoke(); } void ApplyColumnSorting() @@ -826,6 +842,8 @@ internal override void OnViewDataReady() m_ViewState = GetOrCreateViewData(m_ViewState, key); m_ViewState.Apply(this); + + viewDataRestored?.Invoke(); } finally { diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/MultiColumnController.cs b/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/MultiColumnController.cs index fc3e54e46c..f60888e0fd 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/MultiColumnController.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/MultiColumnController.cs @@ -18,6 +18,7 @@ public class MultiColumnController : IDisposable internal static readonly PropertyName bindableElementPropertyName = "__unity-multi-column-bindable-element"; internal static readonly string baseUssClassName = "unity-multi-column-view"; + static readonly string k_HeaderContainerViewDataKey = "unity-multi-column-header-container"; /// /// The USS class name for the header container inside a multi column view. @@ -48,6 +49,7 @@ public class MultiColumnController : IDisposable public event Action headerContextMenuPopulateEvent; BaseVerticalCollectionView m_View; + VisualElement m_HeaderContainer; MultiColumnCollectionHeader m_MultiColumnHeader; internal MultiColumnCollectionHeader header => m_MultiColumnHeader; @@ -64,6 +66,7 @@ public MultiColumnController(Columns columns, SortColumnDescriptions sortDescrip m_MultiColumnHeader.columnSortingChanged += OnColumnSortingChanged; m_MultiColumnHeader.contextMenuPopulateEvent += OnContextMenuPopulateEvent; m_MultiColumnHeader.columnResized += OnColumnResized; + m_MultiColumnHeader.viewDataRestored += OnViewDataRestored; m_MultiColumnHeader.columns.columnAdded += OnColumnAdded; m_MultiColumnHeader.columns.columnRemoved += OnColumnRemoved; @@ -203,8 +206,12 @@ public void PrepareView(BaseVerticalCollectionView collectionView) m_View = collectionView; - // Insert header to the view. - collectionView.Insert(0, m_MultiColumnHeader); + // Insert header to the multi column view. + m_HeaderContainer = new VisualElement { name = headerContainerUssClassName }; + m_HeaderContainer.AddToClassList(headerContainerUssClassName); + m_HeaderContainer.viewDataKey = k_HeaderContainerViewDataKey; + collectionView.scrollView.hierarchy.Insert(0, m_HeaderContainer); + m_HeaderContainer.Add(m_MultiColumnHeader); // Handle horizontal scrolling m_View.scrollView.horizontalScroller.valueChanged += OnHorizontalScrollerValueChanged; @@ -228,6 +235,7 @@ public void Dispose() m_MultiColumnHeader.columnSortingChanged -= OnColumnSortingChanged; m_MultiColumnHeader.contextMenuPopulateEvent -= OnContextMenuPopulateEvent; m_MultiColumnHeader.columnResized -= OnColumnResized; + m_MultiColumnHeader.viewDataRestored -= OnViewDataRestored; m_MultiColumnHeader.columns.columnAdded -= OnColumnAdded; m_MultiColumnHeader.columns.columnRemoved -= OnColumnRemoved; m_MultiColumnHeader.columns.columnReordered -= OnColumnReordered; @@ -236,6 +244,9 @@ public void Dispose() m_MultiColumnHeader.RemoveFromHierarchy(); m_MultiColumnHeader.Dispose(); m_MultiColumnHeader = null; + + m_HeaderContainer.RemoveFromHierarchy(); + m_HeaderContainer = null; } void OnHorizontalScrollerValueChanged(float v) @@ -288,17 +299,31 @@ void OnColumnRemoved(Column column) void OnColumnReordered(Column column, int from, int to) { + if (m_MultiColumnHeader.isApplyingViewState) + return; + m_View.Rebuild(); } void OnColumnsChanged(Column column, ColumnDataType type) { + if (m_MultiColumnHeader.isApplyingViewState) + return; + if (type == ColumnDataType.Visibility) m_View.Rebuild(); } void OnColumnChanged(ColumnsDataType type) { + if (m_MultiColumnHeader.isApplyingViewState) + return; + if (type == ColumnsDataType.PrimaryColumn) m_View.Rebuild(); } + + void OnViewDataRestored() + { + m_View.Rebuild(); + } } } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/MultiColumnListView.cs b/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/MultiColumnListView.cs index 44bb897825..26c1a8a704 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/MultiColumnListView.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/MultiColumnListView.cs @@ -49,10 +49,7 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext } } - static readonly string k_HeaderContainerViewDataKey = "HeaderContainer"; - Columns m_Columns; - VisualElement m_HeaderContainer; bool m_SortingEnabled; SortColumnDescriptions m_SortColumnDescriptions = new SortColumnDescriptions(); @@ -63,11 +60,6 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext /// public new MultiColumnListViewController viewController => base.viewController as MultiColumnListViewController; - /// - /// The content container. - /// - public override VisualElement contentContainer => m_HeaderContainer; - /// /// If a column is clicked to change sorting, this event is raised to allow users to sort the tree view items. /// @@ -122,6 +114,12 @@ private set } m_SortColumnDescriptions = value; + + if (viewController != null) + { + viewController.columnController.header.sortDescriptions = value; + RaiseColumnSortingChanged(); + } } } @@ -154,10 +152,14 @@ public MultiColumnListView() /// Column definitions used to initialize the header. public MultiColumnListView(Columns columns) { - m_HeaderContainer = new VisualElement() { name = MultiColumnController.headerContainerUssClassName }; - m_HeaderContainer.AddToClassList(MultiColumnController.headerContainerUssClassName); - m_HeaderContainer.viewDataKey = k_HeaderContainerViewDataKey; - scrollView.hierarchy.Insert(0, m_HeaderContainer); + // Setting the view data key on the ScrollView to get view data persistence on the header which + // is inside the contentViewport. + // Disabling view data persistence on the vertical and horizontal scrollers to make sure we keep + // the previous behavior on the scrollOffset. + scrollView.viewDataKey = "unity-multi-column-scroll-view"; + scrollView.verticalScroller.viewDataKey = null; + scrollView.horizontalScroller.viewDataKey = null; + this.columns = columns ?? new Columns(); } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/MultiColumnTreeView.cs b/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/MultiColumnTreeView.cs index 4c19c1ebf0..1dbd9ba988 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/MultiColumnTreeView.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/MultiColumnTreeView.cs @@ -49,10 +49,7 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext } } - static readonly string k_HeaderContainerViewDataKey = "HeaderContainer"; - Columns m_Columns; - VisualElement m_HeaderContainer; bool m_SortingEnabled; SortColumnDescriptions m_SortColumnDescriptions = new SortColumnDescriptions(); @@ -63,11 +60,6 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext /// public new MultiColumnTreeViewController viewController => base.viewController as MultiColumnTreeViewController; - /// - /// The content container. - /// - public override VisualElement contentContainer => m_HeaderContainer; - /// /// If a column is clicked to change sorting, this event is raised to allow users to sort the tree view items. /// @@ -122,6 +114,12 @@ private set } m_SortColumnDescriptions = value; + + if (viewController != null) + { + viewController.columnController.header.sortDescriptions = value; + RaiseColumnSortingChanged(); + } } } @@ -153,10 +151,14 @@ public MultiColumnTreeView() /// Column definitions used to initialize the header. public MultiColumnTreeView(Columns columns) { - m_HeaderContainer = new VisualElement() { name = MultiColumnController.headerContainerUssClassName }; - m_HeaderContainer.AddToClassList(MultiColumnController.headerContainerUssClassName); - m_HeaderContainer.viewDataKey = k_HeaderContainerViewDataKey; - scrollView.hierarchy.Insert(0, m_HeaderContainer); + // Setting the view data key on the ScrollView to get view data persistence on the header which + // is inside the contentViewport. + // Disabling view data persistence on the vertical and horizontal scrollers to make sure we keep + // the previous behavior on the scrollOffset. + scrollView.viewDataKey = "unity-multi-column-scroll-view"; + scrollView.verticalScroller.viewDataKey = null; + scrollView.horizontalScroller.viewDataKey = null; + this.columns = columns ?? new Columns(); } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/SortColumnDescription.cs b/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/SortColumnDescription.cs index 4bfaa6ddff..8550449e0b 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/SortColumnDescription.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/MultiColumn/SortColumnDescription.cs @@ -81,7 +81,7 @@ public string columnName } /// - /// The index of the column. + /// The index of the column to be used to find the column only if the columnName isn't set. /// public int columnIndex { diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/PopupField.cs b/ModuleOverrides/com.unity.ui/Core/Controls/PopupField.cs index c4a6007e73..f239cdcb06 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/PopupField.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/PopupField.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using Unity.Properties; using UnityEngine.Scripting.APIUpdating; namespace UnityEngine.UIElements @@ -14,6 +15,10 @@ namespace UnityEngine.UIElements [MovedFrom(true, UpgradeConstants.EditorNamespace, UpgradeConstants.EditorAssembly)] public class PopupField : BasePopupField { + internal static readonly DataBindingProperty indexProperty = nameof(index); + + internal static List s_Modifiers = new List() { '&', '%', '^', '#', '_' }; + /// /// Callback that provides a string representation used to display the selected value. /// @@ -40,7 +45,20 @@ internal override string GetValueToDisplay() { if (m_FormatSelectedValueCallback != null) return m_FormatSelectedValueCallback(value); - return (value != null) ? value.ToString() : string.Empty; + + Func defaultFormatSelectedValue = (value) => { + string displayValue = value.TrimEnd(); + int pos = displayValue.LastIndexOf(' '); + if (pos != -1 && displayValue.Length > pos + 1 && s_Modifiers.Contains(displayValue[pos + 1])) + displayValue = displayValue.Substring(0, pos).TrimEnd(); + + return displayValue; + }; + + if (value != null) + return defaultFormatSelectedValue(value.ToString()); + + return string.Empty; } internal override string GetListItemToDisplay(T value) @@ -58,8 +76,11 @@ public override T value get { return base.value; } set { - m_Index = m_Choices?.IndexOf(value) ?? -1; + var newIndex = m_Choices?.IndexOf(value) ?? -1; + m_Index = newIndex; base.value = value; + if (m_Index != newIndex) + NotifyPropertyChanged(indexProperty); } } @@ -73,7 +94,9 @@ public override void SetValueWithoutNotify(T newValue) private int m_Index = kPopupFieldDefaultIndex; /// /// The currently selected index in the popup menu. + /// Setting the index will update the ::ref::value field and send a property change notification. /// + [CreateProperty] public int index { get { return m_Index; } @@ -86,6 +109,7 @@ public int index this.value = m_Choices[m_Index]; else this.value = default(T); + NotifyPropertyChanged(indexProperty); } } } @@ -188,6 +212,7 @@ internal override void AddMenuItems(IGenericMenu menu) private void ChangeValueFromMenu(T menuItem) { + showMixedValue = false; value = menuItem; } } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/PopupWindow.cs b/ModuleOverrides/com.unity.ui/Core/Controls/PopupWindow.cs index 30c3fcbc7c..c950e39dc4 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/PopupWindow.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/PopupWindow.cs @@ -8,8 +8,14 @@ namespace UnityEngine.UIElements { /// - /// Styled visual element that matches the EditorGUILayout.Popup IMGUI element. + /// Styled visual text element. This element doesn't have any functionality. It's just a container with a border and a title, rather than a window or popup. /// + /// + /// + /// var popupWindow = new UnityEngine.UIElements.PopupWindow() { text = "Title" }; + /// popupWindow.Add(new Button()); + /// + /// public class PopupWindow : TextElement { /// diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/ProgressBar.cs b/ModuleOverrides/com.unity.ui/Core/Controls/ProgressBar.cs index 2772ef5d8b..b0ecf67fed 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/ProgressBar.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/ProgressBar.cs @@ -3,6 +3,7 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System.Collections.Generic; +using Unity.Properties; using UnityEngine; using UnityEngine.Scripting.APIUpdating; @@ -13,6 +14,11 @@ namespace UnityEngine.UIElements /// public abstract class AbstractProgressBar : BindableElement, INotifyValueChanged { + internal static readonly DataBindingProperty titleProperty = nameof(title); + internal static readonly DataBindingProperty lowValueProperty = nameof(lowValue); + internal static readonly DataBindingProperty highValueProperty = nameof(highValue); + internal static readonly DataBindingProperty valueProperty = nameof(value); + /// /// USS Class Name used to style the . /// @@ -44,7 +50,7 @@ public abstract class AbstractProgressBar : BindableElement, INotifyValueChanged UxmlFloatAttributeDescription m_LowValue = new UxmlFloatAttributeDescription { name = "low-value", defaultValue = 0 }; UxmlFloatAttributeDescription m_HighValue = new UxmlFloatAttributeDescription { name = "high-value", defaultValue = 100 }; UxmlFloatAttributeDescription m_Value = new UxmlFloatAttributeDescription { name = "value", defaultValue = 0 }; - UxmlStringAttributeDescription m_Title = new UxmlStringAttributeDescription() { name = "title" }; + UxmlStringAttributeDescription m_Title = new UxmlStringAttributeDescription() { name = "title", defaultValue = string.Empty }; public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc) { @@ -53,8 +59,7 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext bar.lowValue = m_LowValue.GetValueFromBag(bag, cc); bar.highValue = m_HighValue.GetValueFromBag(bag, cc); bar.value = m_Value.GetValueFromBag(bag, cc); - var title = m_Title.GetValueFromBag(bag, cc); - bar.title = (string.IsNullOrEmpty(title)) ? string.Empty : title; + bar.title = m_Title.GetValueFromBag(bag, cc); } } @@ -67,35 +72,54 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext /// /// Sets the title of the ProgressBar that displays in the center of the control. /// + [CreateProperty] public string title { get => m_Title.text; - set => m_Title.text = value; + set + { + var previous = title; + m_Title.text = value; + + if (string.CompareOrdinal(previous, title) != 0) + NotifyPropertyChanged(titleProperty); + } } /// /// Sets the minimum value of the ProgressBar. /// + [CreateProperty] public float lowValue { get => m_LowValue; set { + var previous = lowValue; m_LowValue = value; SetProgress(m_Value); + + if (!Mathf.Approximately(previous, lowValue)) + NotifyPropertyChanged(lowValueProperty); } } /// /// Sets the maximum value of the ProgressBar. /// + [CreateProperty] public float highValue { get => m_HighValue; set { + var previous = highValue; + m_HighValue = value; SetProgress(m_Value); + + if (!Mathf.Approximately(previous, highValue)) + NotifyPropertyChanged(highValueProperty); } } @@ -138,6 +162,7 @@ void OnGeometryChanged(GeometryChangedEvent e) /// /// Sets the progress value. If the value has changed, dispatches an of type float. /// + [CreateProperty] public virtual float value { get { return m_Value; } @@ -152,6 +177,7 @@ public virtual float value evt.elementTarget = this; SetValueWithoutNotify(value); SendEvent(evt); + NotifyPropertyChanged(valueProperty); } } else @@ -214,10 +240,32 @@ float CalculateProgressWidth(float width) } } - /// /// A control that displays the progress between a lower and upper bound value. /// + /// + /// + /// + /// { + /// progressBar.value += 2f; + /// }).Every(75).Until(() => progressBar.value >= 100f); + /// + /// return progressBar; + /// } + /// ]]> + /// + /// [MovedFrom(true, UpgradeConstants.EditorNamespace, UpgradeConstants.EditorAssembly)] public class ProgressBar : AbstractProgressBar { diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/RadioButton.cs b/ModuleOverrides/com.unity.ui/Core/Controls/RadioButton.cs index db8367aa51..44c7276be8 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/RadioButton.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/RadioButton.cs @@ -7,7 +7,7 @@ namespace UnityEngine.UIElements { /// - /// A control that allows users to select a single option inside a + /// A control that allows users to select a single option inside a . For more information, refer to [[wiki:UIE-uxml-element-RadioButton|UXML element RadioButton]]. /// public class RadioButton : BaseBoolField, IGroupBoxOption { diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/RadioButtonGroup.cs b/ModuleOverrides/com.unity.ui/Core/Controls/RadioButtonGroup.cs index de230ff0fd..7e0e7258d9 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/RadioButtonGroup.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/RadioButtonGroup.cs @@ -4,14 +4,17 @@ using System; using System.Collections.Generic; +using Unity.Properties; namespace UnityEngine.UIElements { /// - /// A control that allows single selection out of a logical group of elements. Selecting one will deselect the others. + /// A control that allows single selection out of a logical group of elements. Selecting one will deselect the others. For more information, refer to [[wiki:UIE-uxml-element-RadioButtonGroup|UXML element RadioButtonGroup]]. /// public class RadioButtonGroup : BaseField, IGroupBox { + internal static readonly DataBindingProperty choicesProperty = nameof(choices); + /// /// Instantiates a using data from a UXML file. /// @@ -35,7 +38,7 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext base.Init(ve, bag, cc); var f = (RadioButtonGroup)ve; - f.choices = ParseChoiceList(m_Choices.GetValueFromBag(bag, cc)); + f.choices = UxmlUtility.ParseStringListAttribute(m_Choices.GetValueFromBag(bag, cc)); } } @@ -62,6 +65,7 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext /// Writing to this property removes existing elements and /// re-creates them to display the new list. /// + [CreateProperty] public IEnumerable choices { get @@ -73,9 +77,23 @@ public IEnumerable choices } set { - if (value == null) + if (!value.HasValues()) { m_RadioButtonContainer.Clear(); + + // Only need to clear radio buttons if we're attached to a panel, because we already + // do the cleaning on detach to panel otherwise. + if (panel != null) + { + return; + } + + foreach (var radioButton in m_RadioButtons) + { + radioButton.UnregisterValueChangedCallback(m_RadioButtonValueChangedCallback); + } + m_RadioButtons.Clear(); + return; } @@ -107,11 +125,14 @@ public IEnumerable choices } UpdateRadioButtons(); + NotifyPropertyChanged(choicesProperty); } } VisualElement m_RadioButtonContainer; + public override VisualElement contentContainer => m_RadioButtonContainer ?? this; + /// /// Initializes and returns an instance of RadioButtonGroup. /// diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/RepeatButton.cs b/ModuleOverrides/com.unity.ui/Core/Controls/RepeatButton.cs index c70932d640..afcd974d8a 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/RepeatButton.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/RepeatButton.cs @@ -7,7 +7,7 @@ namespace UnityEngine.UIElements { /// - /// A button that executes an action repeatedly while it is pressed. + /// A button that executes an action repeatedly while it is pressed. For more information, refer to [[wiki:UIE-uxml-element-RepeatButton|UXML element RepeatButton]]. /// public class RepeatButton : TextElement { @@ -58,8 +58,8 @@ public RepeatButton() /// Constructor. /// /// The action to execute when the button is pressed. - /// The initial delay before the action is executed for the first time. - /// The interval between each execution of the action. + /// The initial delay before the action is executed for the first time. Value is defined in milliseconds. + /// The interval between each execution of the action. Value is defined in milliseconds. public RepeatButton(System.Action clickEvent, long delay, long interval) : this() { SetAction(clickEvent, delay, interval); @@ -69,8 +69,8 @@ public RepeatButton(System.Action clickEvent, long delay, long interval) : this( /// Set the action that should be executed when the button is pressed. /// /// The action to execute. - /// The initial delay before the action is executed for the first time. - /// The interval between each execution of the action. + /// The initial delay before the action is executed for the first time. Value is defined in milliseconds. + /// The interval between each execution of the action. Value is defined in milliseconds. public void SetAction(System.Action clickEvent, long delay, long interval) { this.RemoveManipulator(m_Clickable); diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/ScrollView.cs b/ModuleOverrides/com.unity.ui/Core/Controls/ScrollView.cs index 9aaf19363c..33b32cdeb2 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/ScrollView.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/ScrollView.cs @@ -3,6 +3,8 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; +using System.Collections.Generic; +using Unity.Properties; namespace UnityEngine.UIElements { @@ -43,6 +45,8 @@ namespace UnityEngine.UIElements /// /// /// The default is . + /// + /// For more information, refer to [[wiki:UIE-uxml-element-ScrollView|UXML element ScrollView]]. /// public enum ScrollViewMode { @@ -95,10 +99,23 @@ public enum ScrollerVisibility } /// - /// Displays its contents inside a scrollable frame. + /// Displays its contents inside a scrollable frame. For more information, see [[wiki:UIE-uxml-element-ScrollView|ScrollView]]. /// public class ScrollView : VisualElement { + internal static readonly DataBindingProperty horizontalScrollerVisibilityProperty = nameof(horizontalScrollerVisibility); + internal static readonly DataBindingProperty verticalScrollerVisibilityProperty = nameof(verticalScrollerVisibility); + internal static readonly DataBindingProperty scrollOffsetProperty = nameof(scrollOffset); + internal static readonly DataBindingProperty horizontalPageSizeProperty = nameof(horizontalPageSize); + internal static readonly DataBindingProperty verticalPageSizeProperty = nameof(verticalPageSize); + internal static readonly DataBindingProperty mouseWheelScrollSizeProperty = nameof(mouseWheelScrollSize); + internal static readonly DataBindingProperty scrollDecelerationRateProperty = nameof(scrollDecelerationRate); + internal static readonly DataBindingProperty elasticityProperty = nameof(elasticity); + internal static readonly DataBindingProperty touchScrollBehaviorProperty = nameof(touchScrollBehavior); + internal static readonly DataBindingProperty nestedInteractionKindProperty = nameof(nestedInteractionKind); + internal static readonly DataBindingProperty modeProperty = nameof(mode); + internal static readonly DataBindingProperty elasticAnimationIntervalMsProperty = nameof(elasticAnimationIntervalMs); + /// /// Instantiates a using the data read from a UXML file. /// @@ -135,6 +152,9 @@ public class ScrollView : VisualElement UxmlFloatAttributeDescription m_VerticalPageSize = new UxmlFloatAttributeDescription { name = "vertical-page-size", defaultValue = k_UnsetPageSizeValue }; + UxmlFloatAttributeDescription m_MouseWheelScrollSize = new UxmlFloatAttributeDescription + { name = "mouse-wheel-scroll-size", defaultValue = k_MouseWheelScrollSizeDefaultValue }; + UxmlEnumAttributeDescription m_TouchScrollBehavior = new UxmlEnumAttributeDescription { name = "touch-scroll-type", defaultValue = TouchScrollBehavior.Clamped }; @@ -144,6 +164,8 @@ public class ScrollView : VisualElement UxmlFloatAttributeDescription m_Elasticity = new UxmlFloatAttributeDescription { name = "elasticity", defaultValue = k_DefaultElasticity }; + UxmlLongAttributeDescription m_ElasticAnimationIntervalMs = new UxmlLongAttributeDescription + { name = "elastic-animation-interval-ms", defaultValue = k_DefaultElasticAnimationInterval }; /// /// Initialize properties using values from the attribute bag. @@ -176,24 +198,36 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext scrollView.nestedInteractionKind = m_NestedInteractionKind.GetValueFromBag(bag, cc); scrollView.horizontalPageSize = m_HorizontalPageSize.GetValueFromBag(bag, cc); scrollView.verticalPageSize = m_VerticalPageSize.GetValueFromBag(bag, cc); + scrollView.mouseWheelScrollSize = m_MouseWheelScrollSize.GetValueFromBag(bag, cc); scrollView.scrollDecelerationRate = m_ScrollDecelerationRate.GetValueFromBag(bag, cc); scrollView.touchScrollBehavior = m_TouchScrollBehavior.GetValueFromBag(bag, cc); scrollView.elasticity = m_Elasticity.GetValueFromBag(bag, cc); + scrollView.elasticAnimationIntervalMs = m_ElasticAnimationIntervalMs.GetValueFromBag(bag, cc); } } + // ScrollViews can take more than 3 passes to stabilize. This can be the case when a scrollview contains elements with height bound to their width (e.g label with wrapped text). + // Beyond 5 passes, we assume that the layout may never be stabilized then we stop updating the visibility of the scrollers. + private const int k_MaxLocalLayoutPassCount = 5; + private int m_FirstLayoutPass = -1; // The layout pass when the first geometry changed occurred. It may not be layoutPass = 0, which could occur when you have nested ScrollViews. + ScrollerVisibility m_HorizontalScrollerVisibility; /// /// Specifies whether the horizontal scroll bar is visible. /// + [CreateProperty] public ScrollerVisibility horizontalScrollerVisibility { get { return m_HorizontalScrollerVisibility; } set { + var previous = m_HorizontalScrollerVisibility; m_HorizontalScrollerVisibility = value; UpdateScrollers(needsHorizontal, needsVertical); + + if (previous != m_HorizontalScrollerVisibility) + NotifyPropertyChanged(horizontalScrollerVisibilityProperty); } } @@ -202,13 +236,17 @@ public ScrollerVisibility horizontalScrollerVisibility /// /// Specifies whether the vertical scroll bar is visible. /// + [CreateProperty] public ScrollerVisibility verticalScrollerVisibility { get { return m_VerticalScrollerVisibility; } set { + var previous = m_VerticalScrollerVisibility; m_VerticalScrollerVisibility = value; UpdateScrollers(needsHorizontal, needsVertical); + if (previous != m_VerticalScrollerVisibility) + NotifyPropertyChanged(verticalScrollerVisibilityProperty); } } @@ -242,24 +280,15 @@ public bool showVertical const float k_ScrollPageOverlapFactor = 0.1f; internal const float k_UnsetPageSizeValue = -1.0f; - private IVisualElementScheduledItem m_UpdateScrollersScheduledItem; - internal Action OnUpdateScrollers; + internal const float k_MouseWheelScrollSizeDefaultValue = 18.0f; + internal const float k_MouseWheelScrollSizeUnset = -1.0f; + internal bool m_MouseWheelScrollSizeIsInline; - internal bool needsHorizontal - { - get - { - return horizontalScrollerVisibility == ScrollerVisibility.AlwaysVisible || (horizontalScrollerVisibility == ScrollerVisibility.Auto && scrollableWidth > k_SizeThreshold); - } - } + internal bool needsHorizontal => mode != ScrollViewMode.Vertical && horizontalScrollerVisibility == ScrollerVisibility.AlwaysVisible || (horizontalScrollerVisibility == ScrollerVisibility.Auto && scrollableWidth > k_SizeThreshold); - internal bool needsVertical - { - get - { - return verticalScrollerVisibility == ScrollerVisibility.AlwaysVisible || (verticalScrollerVisibility == ScrollerVisibility.Auto && scrollableHeight > k_SizeThreshold); - } - } + internal bool needsVertical => mode != ScrollViewMode.Horizontal && + verticalScrollerVisibility == ScrollerVisibility.AlwaysVisible || + (verticalScrollerVisibility == ScrollerVisibility.Auto && scrollableHeight > k_SizeThreshold); internal bool isVerticalScrollDisplayed { @@ -280,6 +309,7 @@ internal bool isHorizontalScrollDisplayed /// /// The current scrolling position. /// + [CreateProperty] public Vector2 scrollOffset { get { return new Vector2(horizontalScroller.value, verticalScroller.value); } @@ -289,7 +319,14 @@ public Vector2 scrollOffset { horizontalScroller.value = value.x; verticalScroller.value = value.y; - UpdateContentViewTransform(); + + if (panel != null) + { + UpdateScrollers(needsHorizontal, needsVertical); + UpdateContentViewTransform(); + } + + NotifyPropertyChanged(scrollOffsetProperty); } } } @@ -297,30 +334,67 @@ public Vector2 scrollOffset private float m_HorizontalPageSize; /// - /// This property is controlling the scrolling speed of the horizontal scroller. + /// This property controls the speed of the horizontal scrolling when using a keyboard or the on-screen scrollbar buttons (arrows and handle), based on the size of the page. /// + /// + /// When scrolling the page size will be applied to the offset for each scroll step, so the final offset will be the page size multiplied by the number of steps. + /// SA: BaseSlider_1::ref::pageSize. + /// + [CreateProperty] public float horizontalPageSize { get { return m_HorizontalPageSize; } set { + var previous = m_HorizontalPageSize; m_HorizontalPageSize = value; UpdateHorizontalSliderPageSize(); + if (!Mathf.Approximately(previous, m_HorizontalPageSize)) + NotifyPropertyChanged(horizontalPageSizeProperty); } } private float m_VerticalPageSize; /// - /// This property is controlling the scrolling speed of the vertical scroller. + /// This property controls the speed of the vertical scrolling when using a keyboard or the on-screen scrollbar buttons (arrows and handle), based on the size of the page. /// + /// + /// When scrolling the page size will be applied to the offset for each scroll step, so the final offset will be the page size multiplied by the number of steps. + /// SA: BaseSlider_1::ref::pageSize. + /// + [CreateProperty] public float verticalPageSize { get { return m_VerticalPageSize; } set { + var previous = m_VerticalPageSize; m_VerticalPageSize = value; UpdateVerticalSliderPageSize(); + if (!Mathf.Approximately(previous, m_VerticalPageSize)) + NotifyPropertyChanged(verticalPageSizeProperty); + } + } + + private float m_MouseWheelScrollSize = k_MouseWheelScrollSizeDefaultValue; + + /// + /// This property controls the scrolling speed only when using a mouse scroll wheel, based on the size of the page. It takes precedence over the --unity-metrics-single_line-height USS variable. + /// + [CreateProperty] + public float mouseWheelScrollSize + { + get { return m_MouseWheelScrollSize; } + set + { + var previous = m_MouseWheelScrollSize; + if (Math.Abs(m_MouseWheelScrollSize - value) > float.Epsilon) + { + m_MouseWheelScrollSizeIsInline = true; + m_MouseWheelScrollSize = value; + NotifyPropertyChanged(mouseWheelScrollSizeProperty); + } } } @@ -344,10 +418,17 @@ internal float scrollableHeight /// /// The deceleration rate is the speed reduction per second. A value of 0.5 halves the speed each second. A value of 0 stops the scrolling immediately. /// + [CreateProperty] public float scrollDecelerationRate { get { return m_ScrollDecelerationRate; } - set { m_ScrollDecelerationRate = Mathf.Max(0f, value); } + set + { + var previous = m_ScrollDecelerationRate; + m_ScrollDecelerationRate = Mathf.Max(0f, value); + if (!Mathf.Approximately(previous, m_ScrollDecelerationRate)) + NotifyPropertyChanged(scrollDecelerationRateProperty); + } } // For elastic behavior: how long it takes to go back to original position. @@ -359,10 +440,17 @@ public float scrollDecelerationRate /// /// Elasticity is only used when is set to Elastic. /// + [CreateProperty] public float elasticity { get { return m_Elasticity;} - set { m_Elasticity = Mathf.Max(0f, value); } + set + { + var previous = m_Elasticity; + m_Elasticity = Mathf.Max(0f, value); + if (!Mathf.Approximately(previous, m_Elasticity)) + NotifyPropertyChanged(elasticityProperty); + } } /// @@ -388,11 +476,13 @@ public enum TouchScrollBehavior /// /// The behavior to use when a user tries to scroll past the boundaries of the ScrollView content using a touch interaction. /// + [CreateProperty] public TouchScrollBehavior touchScrollBehavior { get { return m_TouchScrollBehavior; } set { + var previous = m_TouchScrollBehavior; m_TouchScrollBehavior = value; if (m_TouchScrollBehavior == TouchScrollBehavior.Clamped) { @@ -404,6 +494,8 @@ public TouchScrollBehavior touchScrollBehavior horizontalScroller.slider.clamped = false; verticalScroller.slider.clamped = false; } + if (previous != m_TouchScrollBehavior) + NotifyPropertyChanged(touchScrollBehaviorProperty); } } @@ -433,10 +525,39 @@ public enum NestedInteractionKind /// /// The behavior to use when scrolling reaches limits of a nested . /// + [CreateProperty] public NestedInteractionKind nestedInteractionKind { get => m_NestedInteractionKind; - set => m_NestedInteractionKind = value; + set + { + var previous = m_NestedInteractionKind; + m_NestedInteractionKind = value; + if (previous != m_NestedInteractionKind) + NotifyPropertyChanged(nestedInteractionKindProperty); + } + } + + static readonly long k_DefaultElasticAnimationInterval = 16; + long m_ElasticAnimationIntervalMs = k_DefaultElasticAnimationInterval; + + /// + /// Specifies the minimum amount of time in milliseconds between each elastic spring animation execution. + /// + [CreateProperty] + public long elasticAnimationIntervalMs + { + get { return m_ElasticAnimationIntervalMs; } + set + { + var previous = m_ElasticAnimationIntervalMs; + m_ElasticAnimationIntervalMs = value; + if (previous != m_ElasticAnimationIntervalMs) + { + NotifyPropertyChanged(elasticAnimationIntervalMsProperty); + m_PostPointerUpAnimation = schedule.Execute(PostPointerUpAnimation).Every(m_ElasticAnimationIntervalMs); + } + } } void OnHorizontalScrollDragElementChanged(GeometryChangedEvent evt) @@ -499,7 +620,7 @@ void UpdateVerticalSliderPageSize() } } - void UpdateContentViewTransform() + internal void UpdateContentViewTransform() { // Adjust contentContainer's position var t = contentContainer.transform.position; @@ -520,6 +641,10 @@ void UpdateContentViewTransform() /// Scroll to a specific child element. /// /// The child to scroll to. + /// + /// This example creates a ScrollView that contains multiple labels. A Button is added that can be used to scroll to a selected label. + /// + /// public void ScrollTo(VisualElement child) { if (child == null) @@ -528,6 +653,7 @@ public void ScrollTo(VisualElement child) if (!contentContainer.Contains(child)) throw new ArgumentException("Cannot scroll to a VisualElement that's not a child of the ScrollView content-container."); + m_Velocity = Vector2.zero; float yDeltaOffset = 0, xDeltaOffset = 0; if (scrollableHeight > 0) @@ -611,16 +737,16 @@ private float GetDeltaDistance(float viewMin, float viewMax, float childBoundary /// /// Represents the visible part of contentContainer. /// - public VisualElement contentViewport { get; private set; } // Represents the visible part of contentContainer + public VisualElement contentViewport { get; } // Represents the visible part of contentContainer /// /// Horizontal scrollbar. /// - public Scroller horizontalScroller { get; private set; } + public Scroller horizontalScroller { get; } /// /// Vertical Scrollbar. /// - public Scroller verticalScroller { get; private set; } + public Scroller verticalScroller { get; } private VisualElement m_ContentContainer; private VisualElement m_ContentAndVerticalScrollContainer; @@ -751,6 +877,7 @@ public ScrollView(ScrollViewMode scrollViewMode) (value) => { scrollOffset = new Vector2(value, scrollOffset.y); + UpdateElasticBehaviour(); UpdateContentViewTransform(); }, SliderDirection.Horizontal) { viewDataKey = "HorizontalScroller" }; @@ -762,6 +889,7 @@ public ScrollView(ScrollViewMode scrollViewMode) (value) => { scrollOffset = new Vector2(scrollOffset.x, value); + UpdateElasticBehaviour(); UpdateContentViewTransform(); }, SliderDirection.Vertical) { viewDataKey = "VerticalScroller" }; @@ -778,6 +906,7 @@ public ScrollView(ScrollViewMode scrollViewMode) horizontalPageSize = k_UnsetPageSizeValue; verticalPageSize = k_UnsetPageSizeValue; + horizontalScroller.slider.dragElement.RegisterCallback(OnHorizontalScrollDragElementChanged); verticalScroller.slider.dragElement.RegisterCallback(OnVerticalScrollDragElementChanged); @@ -798,6 +927,7 @@ public ScrollView(ScrollViewMode scrollViewMode) /// . When the value changes, the class list matching the old value is removed and /// the class list matching the new value is added. /// + [CreateProperty] public ScrollViewMode mode { get => m_Mode; @@ -806,6 +936,7 @@ public ScrollViewMode mode if (m_Mode == value) return; SetScrollViewMode(value); + NotifyPropertyChanged(modeProperty); } } @@ -862,11 +993,11 @@ private void OnAttachToPanel(AttachToPanelEvent evt) if (evt.destinationPanel.contextType == ContextType.Player) { - m_ContentAndVerticalScrollContainer.RegisterCallback(OnPointerDown, TrickleDown.TrickleDown); m_ContentAndVerticalScrollContainer.RegisterCallback(OnPointerMove); - m_ContentAndVerticalScrollContainer.RegisterCallback(OnPointerCancel); - m_ContentAndVerticalScrollContainer.RegisterCallback(OnPointerUp, TrickleDown.TrickleDown); + contentContainer.RegisterCallback(OnPointerDown, TrickleDown.TrickleDown); + contentContainer.RegisterCallback(OnPointerCancel); + contentContainer.RegisterCallback(OnPointerUp, TrickleDown.TrickleDown); contentContainer.RegisterCallback(OnPointerCapture); contentContainer.RegisterCallback(OnPointerCaptureOut); } @@ -874,6 +1005,9 @@ private void OnAttachToPanel(AttachToPanelEvent evt) private void OnDetachFromPanel(DetachFromPanelEvent evt) { + m_ScheduledLayoutPassResetItem?.Pause(); + ResetLayoutPass(); + if (evt.originPanel == null) { return; @@ -925,7 +1059,48 @@ private void OnGeometryChanged(GeometryChangedEvent evt) { return; } - DelayUpdateScrollers(); + + // Get the initial information on the necessity of the scrollbars + bool needsVerticalCached = needsVertical; + bool needsHorizontalCached = needsHorizontal; + + if (m_FirstLayoutPass == -1) + m_FirstLayoutPass = evt.layoutPass; + else + { + // Here, we update the visibility of the scrollbars for only few layout pass. + // Exceeding this limit could suggest that the layout will never be stabilized if we keep showing/hiding the scrollbars. + if ((evt.layoutPass - m_FirstLayoutPass) > k_MaxLocalLayoutPassCount) + { + needsVerticalCached = needsVerticalCached || isVerticalScrollDisplayed; + needsHorizontalCached = needsHorizontalCached || isHorizontalScrollDisplayed; + } + } + + UpdateScrollers(needsHorizontalCached, needsVerticalCached); + UpdateContentViewTransform(); + ScheduleResetLayoutPass(); + } + + private IVisualElementScheduledItem m_ScheduledLayoutPassResetItem; + + void ScheduleResetLayoutPass() + { + // Reset the cached layout pass information in the next frame. + if (m_ScheduledLayoutPassResetItem == null) + { + m_ScheduledLayoutPassResetItem = schedule.Execute(ResetLayoutPass); + } + else + { + m_ScheduledLayoutPassResetItem.Pause(); + m_ScheduledLayoutPassResetItem.Resume(); + } + } + + void ResetLayoutPass() + { + m_FirstLayoutPass = -1; } private int m_ScrollingPointerId = PointerId.invalidPointerId; @@ -943,7 +1118,9 @@ private void OnGeometryChanged(GeometryChangedEvent evt) VisualElement m_CapturedTarget; EventCallback m_CapturedTargetPointerMoveCallback; EventCallback m_CapturedTargetPointerUpCallback; - private IVisualElementScheduledItem m_PostPointerUpAnimation; + + // Internal for tests + internal IVisualElementScheduledItem m_PostPointerUpAnimation; // Compute the new scroll view offset from a pointer delta, taking elasticity into account. // Low and high limits are the values beyond which the scrollview starts to show resistance to scrolling (elasticity). @@ -1127,7 +1304,8 @@ private void SpringBack() scrollOffset = newOffset; } - private void ApplyScrollInertia() + // Internal for tests. + internal void ApplyScrollInertia() { if (hasInertia && m_Velocity != Vector2.zero) { @@ -1371,7 +1549,7 @@ void ExecuteElasticSpringAnimation() if (m_PostPointerUpAnimation == null) { - m_PostPointerUpAnimation = schedule.Execute(PostPointerUpAnimation).Every(30); + m_PostPointerUpAnimation = schedule.Execute(PostPointerUpAnimation).Every(m_ElasticAnimationIntervalMs); } else { @@ -1387,38 +1565,6 @@ void AdjustScrollers() horizontalScroller.Adjust(horizontalFactor); verticalScroller.Adjust(verticalFactor); } - - void DelayUpdateScrollers() - { - if (m_UpdateScrollersScheduledItem == null) - { - m_UpdateScrollersScheduledItem = schedule.Execute(UpdateScrollers); - } - else - { - // Restart the scheduled update of the scrollers - m_UpdateScrollersScheduledItem.Pause(); - m_UpdateScrollersScheduledItem.Resume(); - } - } - - void UpdateScrollers() - { - UpdateScrollers(needsHorizontal, needsVertical); - UpdateContentViewTransform(); - // Cancel the scheduled update if this method is not called from the scheduler - m_UpdateScrollersScheduledItem?.Pause(); - OnUpdateScrollers?.Invoke(false); - } - - void UpdateViewportContentMargins(bool displayHorizontal, bool displayVertical) - { - // Expand content if scrollbars are hidden - contentViewport.style.marginRight = displayVertical ? verticalScroller.layout.width : 0; - m_ContentAndVerticalScrollContainer.style.marginBottom = - displayHorizontal ? horizontalScroller.layout.height : 0; - } - internal void UpdateScrollers(bool displayHorizontal, bool displayVertical) { AdjustScrollers(); @@ -1427,8 +1573,6 @@ internal void UpdateScrollers(bool displayHorizontal, bool displayVertical) horizontalScroller.SetEnabled(contentContainer.boundingBox.width - contentViewport.layout.width > 0); verticalScroller.SetEnabled(contentContainer.boundingBox.height - contentViewport.layout.height > 0); - UpdateViewportContentMargins(displayHorizontal, displayVertical); - var newShowHorizontal = displayHorizontal && m_HorizontalScrollerVisibility != ScrollerVisibility.Hidden; var newShowVertical = displayVertical && m_VerticalScrollerVisibility != ScrollerVisibility.Hidden; var newHorizontalDisplay = newShowHorizontal ? DisplayStyle.Flex : DisplayStyle.None; @@ -1449,16 +1593,6 @@ internal void UpdateScrollers(bool displayHorizontal, bool displayVertical) verticalScroller.highValue = scrollableHeight; horizontalScroller.lowValue = 0f; horizontalScroller.highValue = scrollableWidth; - - if (scrollableHeight <= 0f) - { - verticalScroller.value = 0f; - } - - if (scrollableWidth <= 0f) - { - horizontalScroller.value = 0f; - } } private void OnScrollersGeometryChanged(GeometryChangedEvent evt) @@ -1467,17 +1601,16 @@ private void OnScrollersGeometryChanged(GeometryChangedEvent evt) { return; } - var newShowVertical = needsVertical && m_VerticalScrollerVisibility != ScrollerVisibility.Hidden; + var newShowHorizontal = needsHorizontal && m_HorizontalScrollerVisibility != ScrollerVisibility.Hidden; - // Add some space if both scrollers are visible + // Align the right side of the horizontal scroller with the left side of the vertical scroller. if (newShowHorizontal) { horizontalScroller.style.marginRight = verticalScroller.layout.width; } AdjustScrollers(); - UpdateViewportContentMargins(newShowHorizontal, newShowVertical); } // TODO: Same behaviour as IMGUI Scroll view @@ -1487,11 +1620,12 @@ void OnScrollWheel(WheelEvent evt) var canUseVerticalScroll = mode != ScrollViewMode.Horizontal && contentContainer.boundingBox.height - layout.height > 0; var canUseHorizontalScroll = mode != ScrollViewMode.Vertical && contentContainer.boundingBox.width - layout.width > 0; var horizontalScrollDelta = canUseHorizontalScroll && !canUseVerticalScroll ? evt.delta.y : evt.delta.x; + var mouseScrollFactor = m_MouseWheelScrollSizeIsInline ? mouseWheelScrollSize : m_SingleLineHeight; if (canUseVerticalScroll) { var oldVerticalValue = verticalScroller.value; - verticalScroller.value += evt.delta.y * (verticalScroller.lowValue < verticalScroller.highValue ? 1f : -1f) * m_SingleLineHeight; + verticalScroller.value += evt.delta.y * (verticalScroller.lowValue < verticalScroller.highValue ? 1f : -1f) * mouseScrollFactor; if (nestedInteractionKind == NestedInteractionKind.StopScrolling || !Mathf.Approximately(verticalScroller.value, oldVerticalValue)) { @@ -1503,7 +1637,7 @@ void OnScrollWheel(WheelEvent evt) if (canUseHorizontalScroll) { var oldHorizontalValue = horizontalScroller.value; - horizontalScroller.value += horizontalScrollDelta * (horizontalScroller.lowValue < horizontalScroller.highValue ? 1f : -1f) * m_SingleLineHeight; + horizontalScroller.value += horizontalScrollDelta * (horizontalScroller.lowValue < horizontalScroller.highValue ? 1f : -1f) * mouseScrollFactor; if (nestedInteractionKind == NestedInteractionKind.StopScrolling || !Mathf.Approximately(horizontalScroller.value, oldHorizontalValue)) { @@ -1514,19 +1648,7 @@ void OnScrollWheel(WheelEvent evt) if (updateContentViewTransform) { - // Update elastic behavior - if (touchScrollBehavior == TouchScrollBehavior.Elastic) - { - m_LowBounds = new Vector2( - Mathf.Min(horizontalScroller.lowValue, horizontalScroller.highValue), - Mathf.Min(verticalScroller.lowValue, verticalScroller.highValue)); - m_HighBounds = new Vector2( - Mathf.Max(horizontalScroller.lowValue, horizontalScroller.highValue), - Mathf.Max(verticalScroller.lowValue, verticalScroller.highValue)); - - ExecuteElasticSpringAnimation(); - } - + UpdateElasticBehaviour(); UpdateContentViewTransform(); } } @@ -1551,5 +1673,20 @@ void ReadSingleLineHeight() m_SingleLineHeight = UIElementsUtility.singleLineHeight; } } + + void UpdateElasticBehaviour() + { + if (touchScrollBehavior == TouchScrollBehavior.Elastic) + { + m_LowBounds = new Vector2( + Mathf.Min(horizontalScroller.lowValue, horizontalScroller.highValue), + Mathf.Min(verticalScroller.lowValue, verticalScroller.highValue)); + m_HighBounds = new Vector2( + Mathf.Max(horizontalScroller.lowValue, horizontalScroller.highValue), + Mathf.Max(verticalScroller.lowValue, verticalScroller.highValue)); + + ExecuteElasticSpringAnimation(); + } + } } } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/Scroller.cs b/ModuleOverrides/com.unity.ui/Core/Controls/Scroller.cs index 0fa1e28b48..9b58aae62c 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/Scroller.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/Scroller.cs @@ -4,14 +4,20 @@ using System; using System.Collections.Generic; +using Unity.Properties; namespace UnityEngine.UIElements { /// - /// A vertical or horizontal scrollbar. + /// A vertical or horizontal scrollbar. For more information, refer to [[wiki:UIE-uxml-element-scroller|UXML element Scroller]]. /// public class Scroller : VisualElement { + internal static readonly DataBindingProperty valueProperty = nameof(value); + internal static readonly DataBindingProperty lowValueProperty = nameof(lowValue); + internal static readonly DataBindingProperty highValueProperty = nameof(highValue); + internal static readonly DataBindingProperty directionProperty = nameof(direction); + class ScrollerSlider : Slider { public ScrollerSlider(float start, float end, @@ -78,51 +84,78 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext /// /// The slider used by this scroller. /// - public Slider slider { get; private set; } + public Slider slider { get; } + /// /// Bottom or left scroll button. /// - public RepeatButton lowButton { get; private set; } + public RepeatButton lowButton { get; } + /// /// Top or right scroll button. /// - public RepeatButton highButton { get; private set; } + public RepeatButton highButton { get; } /// /// Value that defines the slider position. It lies between and . /// + [CreateProperty] public float value { get { return slider.value; } - set { slider.value = value; } + set + { + var previous = slider.value; + slider.value = value; + if (!Mathf.Approximately(previous, slider.value)) + NotifyPropertyChanged(valueProperty); + } } /// /// Minimum value. /// + [CreateProperty] public float lowValue { get { return slider.lowValue; } - set { slider.lowValue = value; } + set + { + var previous = slider.lowValue; + slider.lowValue = value; + + if (!Mathf.Approximately(previous, slider.lowValue)) + NotifyPropertyChanged(lowValueProperty); + } } /// /// Maximum value. /// + [CreateProperty] public float highValue { get { return slider.highValue; } - set { slider.highValue = value; } + set + { + var previous = slider.highValue; + slider.highValue = value; + + if (!Mathf.Approximately(previous, slider.highValue)) + NotifyPropertyChanged(highValueProperty); + } } /// /// Direction of this scrollbar. /// + [CreateProperty] public SliderDirection direction { get { return resolvedStyle.flexDirection == FlexDirection.Row ? SliderDirection.Horizontal : SliderDirection.Vertical; } set { + var previous = slider.direction; slider.direction = value; // We want default behavior for vertical scrollers to be lowValue at the top and highValue at the bottom, // instead of the default Slider behavior. @@ -139,6 +172,8 @@ public SliderDirection direction AddToClassList(verticalVariantUssClassName); RemoveFromClassList(horizontalVariantUssClassName); } + if (previous != slider.direction) + NotifyPropertyChanged(directionProperty); } } @@ -200,7 +235,7 @@ public Scroller(float lowValue, float highValue, System.Action valueChang } /// - /// Updates the slider element size as a ratio of total range. A value greater than 1 will disable the Scroller. + /// Updates the slider element size as a ratio of total range. A value greater than or equal to 1 will disable the Scroller. /// /// Slider size ratio. public void Adjust(float factor) diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/Slider.cs b/ModuleOverrides/com.unity.ui/Core/Controls/Slider.cs index ed05ce4404..7bc17786ec 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/Slider.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/Slider.cs @@ -8,7 +8,7 @@ namespace UnityEngine.UIElements { /// - /// A slider containing floating point values. + /// A slider containing floating point values. For more information, refer to [[wiki:UIE-uxml-element-slider|UXML element Slider]]. /// public class Slider : BaseSlider { @@ -20,7 +20,7 @@ public class Slider : BaseSlider /// /// Defines for the . /// - public new class UxmlTraits : BaseSlider.UxmlTraits + public new class UxmlTraits : UxmlTraits { UxmlFloatAttributeDescription m_LowValue = new UxmlFloatAttributeDescription { name = "low-value" }; UxmlFloatAttributeDescription m_HighValue = new UxmlFloatAttributeDescription { name = "high-value", defaultValue = kDefaultHighValue }; @@ -66,20 +66,29 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext public new static readonly string inputUssClassName = ussClassName + "__input"; /// - /// Constructor. + /// Creates a new instance of a Slider. /// public Slider() : this((string)null, 0, kDefaultHighValue) {} /// - /// Constructor. + /// Creates a new instance of a Slider. /// + /// The minimum value that the slider encodes. + /// The maximum value that the slider encodes. + /// The direction of the slider (Horizontal or Vertical). + /// A generic page size used to change the value when clicking in the slider. public Slider(float start, float end, SliderDirection direction = SliderDirection.Horizontal, float pageSize = kDefaultPageSize) : this(null, start, end, direction, pageSize) {} /// - /// Constructor. + /// Creates a new instance of a Slider. /// + /// The string representing the label that will appear beside the field. + /// The minimum value that the slider encodes. + /// The maximum value that the slider encodes. + /// The direction of the slider (Horizontal or Vertical). + /// A generic page size used to change the value when clicking in the slider. public Slider(string label, float start = 0, float end = kDefaultHighValue, SliderDirection direction = SliderDirection.Horizontal, float pageSize = kDefaultPageSize) : base(label, start, end, direction, pageSize) { @@ -123,18 +132,11 @@ internal override float SliderRange() return Math.Abs(highValue - lowValue); } - internal override float ParseStringToValue(string stringValue) + internal override float ParseStringToValue(string previousValue, string newValue) { - float result; - - if (float.TryParse(stringValue.Replace(",", "."), NumberStyles.Float, CultureInfo.InvariantCulture, out result)) - { - return result; - } - else - { - return 0f; - } + if (UINumericFieldsUtils.TryConvertStringToFloat(newValue, previousValue, out var value)) + return value; + return 0; } internal override void ComputeValueFromKey(SliderKey sliderKey, bool isShift) diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/SliderInt.cs b/ModuleOverrides/com.unity.ui/Core/Controls/SliderInt.cs index 248ccce3da..065b92b832 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/SliderInt.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/SliderInt.cs @@ -8,7 +8,7 @@ namespace UnityEngine.UIElements { /// - /// A slider containing Integer discrete values. + /// A slider containing Integer discrete values. For more information, refer to [[wiki:UIE-uxml-element-sliderInt|UXML element SliderInt]]. /// public class SliderInt : BaseSlider { @@ -20,7 +20,7 @@ public class SliderInt : BaseSlider /// /// Defines for the . /// - public new class UxmlTraits : BaseSlider.UxmlTraits + public new class UxmlTraits : UxmlTraits { UxmlIntAttributeDescription m_LowValue = new UxmlIntAttributeDescription { name = "low-value" }; UxmlIntAttributeDescription m_HighValue = new UxmlIntAttributeDescription { name = "high-value", defaultValue = kDefaultHighValue }; @@ -134,17 +134,11 @@ internal override int SliderRange() return Math.Abs(highValue - lowValue); } - internal override int ParseStringToValue(string stringValue) + internal override int ParseStringToValue(string previousValue, string newValue) { - int result; - if (int.TryParse(stringValue, out result)) - { - return result; - } - else - { - return 0; - } + if (UINumericFieldsUtils.TryConvertStringToInt(newValue, previousValue, out var value)) + return value; + return 0; } internal override void ComputeValueAndDirectionFromClick(float sliderLength, float dragElementLength, float dragElementPos, float dragElementLastPos) diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/TextValueField.cs b/ModuleOverrides/com.unity.ui/Core/Controls/TextValueField.cs index bfe387187d..9598559014 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/TextValueField.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/TextValueField.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using Unity.Properties; using UnityEngine.Scripting.APIUpdating; namespace UnityEngine.UIElements @@ -40,7 +41,7 @@ public interface IValueField T value { get; set; } /// - /// Modify the value using a 3D delta and a speed, typically coming from an input device. + /// Applies the values of a 3D delta and a speed from an input device. /// /// A vector used to compute the value change. /// A multiplier for the value change. @@ -62,6 +63,8 @@ public interface IValueField [MovedFrom(true, UpgradeConstants.EditorNamespace, UpgradeConstants.EditorAssembly)] public abstract class TextValueField : TextInputBaseField, IValueField { + internal static readonly DataBindingProperty formatStringProperty = nameof(formatString); + // This property to alleviate the fact we have to cast all the time TextValueInput textValueInput => (TextValueInput)textInputBase; @@ -72,6 +75,7 @@ public abstract class TextValueField : TextInputBaseField /// The format string for the value. /// + [CreateProperty] public string formatString { get => textValueInput.formatString; @@ -81,6 +85,7 @@ public string formatString { textValueInput.formatString = value; textEdition.UpdateText(ValueToString(rawValue)); + NotifyPropertyChanged(formatStringProperty); } } } @@ -97,7 +102,7 @@ protected TextValueField(string label, int maxLength, TextValueInput textValueIn } /// - /// Modify the value using a 3D delta and a speed, typically coming from an input device. + /// Applies the values of a 3D delta and a speed from an input device. /// /// A vector used to compute the value change. /// A multiplier for the value change. @@ -124,15 +129,6 @@ public void StopDragging() textValueInput.StopDragging(); } - /// - /// This is the value of the field. - /// - public override TValueType value - { - get => base.value; - set => base.value = value; - } - internal override void UpdateValueFromText() { // Prevent text from changing when the value change @@ -244,6 +240,20 @@ internal override void OnViewDataReady() base.OnViewDataReady(); } + internal override void RegisterEditingCallbacks() + { + base.RegisterEditingCallbacks(); + labelElement.RegisterCallback(StartEditing, TrickleDown.TrickleDown); + labelElement.RegisterCallback(EndEditing); + } + + internal override void UnregisterEditingCallbacks() + { + base.UnregisterEditingCallbacks(); + labelElement.RegisterCallback(StartEditing, TrickleDown.TrickleDown); + labelElement.RegisterCallback(EndEditing); + } + // Implements a control with a value of type T backed by a text. /// /// This is the inner representation of the Text input. diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/Toggle.cs b/ModuleOverrides/com.unity.ui/Core/Controls/Toggle.cs index 9cc3ba8a53..91b89e913b 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/Toggle.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/Toggle.cs @@ -7,7 +7,7 @@ namespace UnityEngine.UIElements { /// - /// A is a clickable element that represents a boolean value. + /// A Toggle is a clickable element that represents a boolean value. /// /// /// A Toggle control consists of a label and an input field. The input field contains a sprite for the control. By default, @@ -20,6 +20,8 @@ namespace UnityEngine.UIElements /// /// To bind the Toggle's state to a boolean variable, set the`binding-path` property in a UI Document (.uxml file), or /// the C# `bindingPath` to the variable name. + /// + /// For more information, refer to [[wiki:UIE-uxml-element-toggle|UXML element Toggle]]. /// public class Toggle : BaseBoolField { diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/TreeView.cs b/ModuleOverrides/com.unity.ui/Core/Controls/TreeView.cs index c758a3d26e..fc491d8323 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/TreeView.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/TreeView.cs @@ -3,9 +3,8 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; -using System.Collections; using System.Collections.Generic; -using System.Linq; +using Unity.Properties; namespace UnityEngine.UIElements { @@ -31,9 +30,16 @@ namespace UnityEngine.UIElements /// /// The TreeView creates VisualElements for the visible items, and supports binding many more. As the user scrolls, the TreeView /// recycles VisualElements and re-binds them to new data items. + /// + /// For more information, refer to [[wiki:UIE-uxml-element-TreeView|UXML element TreeView]]. /// public class TreeView : BaseTreeView { + internal static readonly DataBindingProperty makeItemProperty = nameof(makeItem); + internal static readonly DataBindingProperty bindItemProperty = nameof(bindItem); + internal static readonly DataBindingProperty unbindItemProperty = nameof(unbindItem); + internal static readonly DataBindingProperty destroyItemProperty = nameof(destroyItem); + /// /// Instantiates a using data from a UXML file. /// @@ -65,6 +71,7 @@ public class TreeView : BaseTreeView /// If this property and are not set, Unity will either create a PropertyField if bound /// to a SerializedProperty, or create an empty label for any other case. /// + [CreateProperty] public new Func makeItem { get => m_MakeItem; @@ -74,6 +81,7 @@ public class TreeView : BaseTreeView { m_MakeItem = value; Rebuild(); + NotifyPropertyChanged(makeItemProperty); } } } @@ -89,7 +97,11 @@ public class TreeView : BaseTreeView /// /// If this property and are not set, Unity will try to bind to a SerializedProperty if /// bound, or simply set text in the created Label. + /// + /// **Note:**: Setting this callback without also setting might result in unexpected behavior. + /// This is because the default implementation of unbindItem expects the default implementation of bindItem. /// + [CreateProperty] public new Action bindItem { get => m_BindItem; @@ -99,19 +111,38 @@ public class TreeView : BaseTreeView { m_BindItem = value; RefreshItems(); + NotifyPropertyChanged(bindItemProperty); } - } } + private Action m_UnbindItem; + /// /// Callback for unbinding a data item from the VisualElement. /// /// /// The method called by this callback receives the VisualElement to unbind, and the index of the /// element to unbind it from. + /// + /// **Note:**: Setting this callback without also setting might cause unexpected behavior. + /// This is because the default implementation of bindItem expects the default implementation of unbindItem. /// - public new Action unbindItem { get; set; } + [CreateProperty] + public new Action unbindItem + { + get => m_UnbindItem; + set + { + if (value != m_UnbindItem) + { + m_UnbindItem = value; + NotifyPropertyChanged(unbindItemProperty); + } + } + } + + private Action m_DestroyItem; /// /// Callback invoked when a created via is no longer needed and will be destroyed. @@ -119,7 +150,20 @@ public class TreeView : BaseTreeView /// /// The method called by this callback receives the VisualElement that will be destroyed from the pool. /// - public new Action destroyItem { get; set; } + [CreateProperty] + public new Action destroyItem + { + get => m_DestroyItem; + set + { + if (value != m_DestroyItem) + { + m_DestroyItem = value; + NotifyPropertyChanged(destroyItemProperty); + } + } + } + /// /// Sets the root items. diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/TreeViewItem.cs b/ModuleOverrides/com.unity.ui/Core/Controls/TreeViewItem.cs index 5c0c42d09a..191156e065 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/TreeViewItem.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/TreeViewItem.cs @@ -86,7 +86,7 @@ public void Move(int id, int newParentId, int childIndex) else { var rootItemIdsPositionForId = m_RootItemIds.IndexOf(id); - if (rootItemIdsPositionForId < childIndex) + if (newParentId == ReusableCollectionItem.UndefinedIndex && rootItemIdsPositionForId < childIndex) childIndex--; m_RootItemIds.Remove(id); diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/TwoPaneSplitView.cs b/ModuleOverrides/com.unity.ui/Core/Controls/TwoPaneSplitView.cs index 68b90589a0..5e456fe5f9 100644 --- a/ModuleOverrides/com.unity.ui/Core/Controls/TwoPaneSplitView.cs +++ b/ModuleOverrides/com.unity.ui/Core/Controls/TwoPaneSplitView.cs @@ -3,14 +3,19 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System.Collections.Generic; +using Unity.Properties; namespace UnityEngine.UIElements { /// - /// A SplitView that contains two resizable panes. One pane is fixed-size while the other pane has flex-grow style set to 1 to take all remaining space. The border between he panes is draggable to resize both panes. Both horizontal and vertical modes are supported. Requires _exactly_ two child elements to operate. + /// A SplitView that contains two resizable panes. One pane is fixed-size while the other pane has flex-grow style set to 1 to take all remaining space. The border between the panes is draggable to resize both panes. Both horizontal and vertical modes are supported. Requires exactly two child elements to operate. /// public class TwoPaneSplitView : VisualElement { + internal static readonly DataBindingProperty fixedPaneIndexProperty = nameof(fixedPaneIndex); + internal static readonly DataBindingProperty fixedPaneInitialDimensionProperty = nameof(fixedPaneInitialDimension); + internal static readonly DataBindingProperty orientationProperty = nameof(orientation); + static readonly string s_UssClassName = "unity-two-pane-split-view"; static readonly string s_ContentContainerClassName = "unity-two-pane-split-view__content-container"; static readonly string s_HandleDragLineClassName = "unity-two-pane-split-view__dragline"; @@ -58,7 +63,7 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext VisualElement m_FixedPane; VisualElement m_FlexedPane; - [SerializeField] float m_FixedPaneDimension = -1; + [SerializeField, DontCreateProperty] float m_FixedPaneDimension = -1; /// /// The child element that is set as the fixed size pane. @@ -83,6 +88,7 @@ public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext /// /// 0 for setting first child as the fixed pane, 1 for the second child element. /// + [CreateProperty] public int fixedPaneIndex { get => m_FixedPaneIndex; @@ -92,12 +98,14 @@ public int fixedPaneIndex return; Init(value, m_FixedPaneInitialDimension, m_Orientation); + NotifyPropertyChanged(fixedPaneIndexProperty); } } /// /// The initial width or height for the fixed pane. /// + [CreateProperty] public float fixedPaneInitialDimension { get => m_FixedPaneInitialDimension; @@ -107,12 +115,14 @@ public float fixedPaneInitialDimension return; Init(m_FixedPaneIndex, value, m_Orientation); + NotifyPropertyChanged(fixedPaneInitialDimensionProperty); } } /// /// Orientation of the split view. /// + [CreateProperty] public TwoPaneSplitViewOrientation orientation { get => m_Orientation; @@ -122,6 +132,7 @@ public TwoPaneSplitViewOrientation orientation return; Init(m_FixedPaneIndex, m_FixedPaneInitialDimension, value); + NotifyPropertyChanged(orientationProperty); } } diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/UnsignedIntegerField.cs b/ModuleOverrides/com.unity.ui/Core/Controls/UnsignedIntegerField.cs new file mode 100644 index 0000000000..ab71a056d7 --- /dev/null +++ b/ModuleOverrides/com.unity.ui/Core/Controls/UnsignedIntegerField.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 System; +using System.Globalization; + +namespace UnityEngine.UIElements +{ + /// + /// Makes a text field for entering an unsigned integer. + /// + public class UnsignedIntegerField : TextValueField + { + // This property to alleviate the fact we have to cast all the time + UnsignedIntegerInput integerInput => (UnsignedIntegerInput)textInputBase; + + /// + /// Instantiates an using the data read from a UXML file. + /// + public new class UxmlFactory : UxmlFactory {} + /// + /// Defines for the . + /// + public new class UxmlTraits : TextValueFieldTraits {} + + /// + /// Converts the given unsigned integer to a string. + /// + /// The unsigned integer to be converted to string. + /// The unsigned integer as string. + protected override string ValueToString(uint v) + { + return v.ToString(formatString, CultureInfo.InvariantCulture.NumberFormat); + } + + /// + /// Converts a string to an unsigned integer. + /// + /// The string to convert. + /// The unsigned integer parsed from the string. + protected override uint StringToValue(string str) + { + var success = UINumericFieldsUtils.TryConvertStringToUInt(str, textInputBase.originalText, out var v); + return success ? v : rawValue; + } + + /// + /// USS class name of elements of this type. + /// + public new static readonly string ussClassName = "unity-unsigned-integer-field"; + /// + /// USS class name of labels in elements of this type. + /// + public new static readonly string labelUssClassName = ussClassName + "__label"; + /// + /// USS class name of input elements in elements of this type. + /// + public new static readonly string inputUssClassName = ussClassName + "__input"; + + /// + /// Constructor. + /// + public UnsignedIntegerField() + : this((string)null) {} + + /// + /// Constructor. + /// + /// Maximum number of characters the field can take. + public UnsignedIntegerField(int maxLength) + : this(null, maxLength) {} + + /// + /// Constructor. + /// + /// Maximum number of characters the field can take. + public UnsignedIntegerField(string label, int maxLength = kMaxLengthNone) + : base(label, maxLength, new UnsignedIntegerInput()) + { + AddToClassList(ussClassName); + labelElement.AddToClassList(labelUssClassName); + visualInput.AddToClassList(inputUssClassName); + AddLabelDragger(); + } + + internal override bool CanTryParse(string textString) => uint.TryParse(textString, out _); + + /// + /// Applies the values of a 3D delta and a speed from an input device. + /// + /// A vector used to compute the value change. + /// A multiplier for the value change. + /// The start value. + public override void ApplyInputDeviceDelta(Vector3 delta, DeltaSpeed speed, uint startValue) + { + integerInput.ApplyInputDeviceDelta(delta, speed, startValue); + } + + class UnsignedIntegerInput : TextValueInput + { + UnsignedIntegerField parentUnsignedIntegerField => (UnsignedIntegerField)parent; + + internal UnsignedIntegerInput() + { + formatString = UINumericFieldsUtils.k_IntFieldFormatString; + } + + protected override string allowedCharacters => UINumericFieldsUtils.k_AllowedCharactersForInt; + + public override void ApplyInputDeviceDelta(Vector3 delta, DeltaSpeed speed, uint startValue) + { + double sensitivity = NumericFieldDraggerUtility.CalculateIntDragSensitivity(startValue); + var acceleration = NumericFieldDraggerUtility.Acceleration(speed == DeltaSpeed.Fast, speed == DeltaSpeed.Slow); + long v = StringToValue(text); + v += (long)Math.Round(NumericFieldDraggerUtility.NiceDelta(delta, acceleration) * sensitivity); + + if (parentUnsignedIntegerField.isDelayed) + { + text = ValueToString(Mathf.ClampToUInt(v)); + } + else + { + parentUnsignedIntegerField.value = Mathf.ClampToUInt(v); + } + } + + protected override string ValueToString(uint v) + { + return v.ToString(formatString); + } + + protected override uint StringToValue(string str) + { + UINumericFieldsUtils.TryConvertStringToUInt(str, originalText, out var v); + return v; + } + } + } +} diff --git a/ModuleOverrides/com.unity.ui/Core/Controls/UnsignedLongField.cs b/ModuleOverrides/com.unity.ui/Core/Controls/UnsignedLongField.cs new file mode 100644 index 0000000000..74448aa186 --- /dev/null +++ b/ModuleOverrides/com.unity.ui/Core/Controls/UnsignedLongField.cs @@ -0,0 +1,164 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using System.Globalization; + +namespace UnityEngine.UIElements +{ + /// + /// Makes a text field for entering unsigned long integers. + /// + public class UnsignedLongField : TextValueField + { + // This property to alleviate the fact we have to cast all the time + UnsignedLongInput unsignedLongInput => (UnsignedLongInput)textInputBase; + + /// + /// Instantiates a using the data read from a UXML file. + /// + public new class UxmlFactory : UxmlFactory {} + /// + /// Defines for the . + /// + public new class UxmlTraits : TextValueFieldTraits {} + + /// + /// Converts the given unsigned long integer to a string. + /// + /// The unsigned long integer to be converted to string. + /// The ulong integer as string. + protected override string ValueToString(ulong v) + { + return v.ToString(formatString, CultureInfo.InvariantCulture.NumberFormat); + } + + /// + /// Converts a string to a unsigned long integer. + /// + /// The string to convert. + /// The unsigned long integer parsed from the string. + protected override ulong StringToValue(string str) + { + var success = UINumericFieldsUtils.TryConvertStringToULong(str, textInputBase.originalText, out var v); + return success ? v : rawValue; + } + + /// + /// USS class name of elements of this type. + /// + public new static readonly string ussClassName = "unity-unsigned-long-field"; + /// + /// USS class name of labels in elements of this type. + /// + public new static readonly string labelUssClassName = ussClassName + "__label"; + /// + /// USS class name of input elements in elements of this type. + /// + public new static readonly string inputUssClassName = ussClassName + "__input"; + + /// + /// Constructor. + /// + public UnsignedLongField() + : this((string)null) {} + + /// + /// Constructor. + /// + /// Maximum number of characters the field can take. + public UnsignedLongField(int maxLength) + : this(null, maxLength) {} + + /// + /// Constructor. + /// + /// Maximum number of characters the field can take. + public UnsignedLongField(string label, int maxLength = kMaxLengthNone) + : base(label, maxLength, new UnsignedLongInput()) + { + AddToClassList(ussClassName); + labelElement.AddToClassList(labelUssClassName); + visualInput.AddToClassList(inputUssClassName); + AddLabelDragger(); + } + + internal override bool CanTryParse(string textString) => ulong.TryParse(textString, out _); + + /// + /// Applies the values of a 3D delta and a speed from an input device. + /// + /// A vector used to compute the value change. + /// A multiplier for the value change. + /// The start value. + public override void ApplyInputDeviceDelta(Vector3 delta, DeltaSpeed speed, ulong startValue) + { + unsignedLongInput.ApplyInputDeviceDelta(delta, speed, startValue); + } + + class UnsignedLongInput : TextValueInput + { + UnsignedLongField parentUnsignedLongField => (UnsignedLongField)parent; + + internal UnsignedLongInput() + { + formatString = UINumericFieldsUtils.k_IntFieldFormatString; + } + + protected override string allowedCharacters => UINumericFieldsUtils.k_AllowedCharactersForInt; + + public override void ApplyInputDeviceDelta(Vector3 delta, DeltaSpeed speed, ulong startValue) + { + double sensitivity = NumericFieldDraggerUtility.CalculateIntDragSensitivity(startValue); + var acceleration = NumericFieldDraggerUtility.Acceleration(speed == DeltaSpeed.Fast, speed == DeltaSpeed.Slow); + var v = StringToValue(text); + var niceDelta = (long)Math.Round(NumericFieldDraggerUtility.NiceDelta(delta, acceleration) * sensitivity); + + v = ClampToMinMaxULongValue(niceDelta, v); + + if (parentUnsignedLongField.isDelayed) + { + text = ValueToString(v); + } + else + { + parentUnsignedLongField.value = v; + } + } + + private ulong ClampToMinMaxULongValue(long niceDelta, ulong value) + { + var niceDeltaAbs = (ulong)Math.Abs(niceDelta); + + if (niceDelta > 0) + { + if (niceDeltaAbs > ulong.MaxValue - value) + { + return ulong.MaxValue; + } + + return value + niceDeltaAbs; + } + + if (niceDeltaAbs > value) + { + return ulong.MinValue; + } + + return value - niceDeltaAbs; + } + + protected override string ValueToString(ulong v) + { + return v.ToString(formatString); + } + + protected override ulong StringToValue(string str) + { + UINumericFieldsUtils.TryConvertStringToULong(str, originalText, out var v); + return v; + } + } + } +} diff --git a/ModuleOverrides/com.unity.ui/Core/Conversions/UIConversion.cs b/ModuleOverrides/com.unity.ui/Core/Conversions/UIConversion.cs index 7bd1faa454..6169b94d3f 100644 --- a/ModuleOverrides/com.unity.ui/Core/Conversions/UIConversion.cs +++ b/ModuleOverrides/com.unity.ui/Core/Conversions/UIConversion.cs @@ -16,6 +16,9 @@ internal static class UIConversion static readonly ConversionRegistry s_GlobalUIConverters = ConversionRegistry.Create(); static readonly ConversionRegistry s_PrimitiveConverters = ConversionRegistry.Create(); + internal static ConversionRegistry globalUIConverters => s_GlobalUIConverters; + internal static ConversionRegistry primitiveConverters => s_PrimitiveConverters; + static UIConversion() { RegisterPrimitivesConverter(); @@ -175,6 +178,7 @@ static void RegisterPrimitivesConverter() RegisterDoubleConverters(); RegisterBooleanConverters(); RegisterCharConverters(); + RegisterColorConverters(); } static void RegisterInt8Converters() @@ -467,5 +471,11 @@ static void RegisterCharConverters() s_PrimitiveConverters.Register(typeof(string), typeof(char), (TypeConverter) ((ref string v) => !string.IsNullOrEmpty(v) ? v[0] : '\0')); } + + static void RegisterColorConverters() + { + s_PrimitiveConverters.Register(typeof(Color), typeof(Color32), (TypeConverter) ((ref Color v) => v)); + s_PrimitiveConverters.Register(typeof(Color32), typeof(Color), (TypeConverter) ((ref Color32 v) => v)); + } } } diff --git a/ModuleOverrides/com.unity.ui/Core/DefaultEventSystem.cs b/ModuleOverrides/com.unity.ui/Core/DefaultEventSystem.cs index 1e7e2af1b6..9f6a9f068a 100644 --- a/ModuleOverrides/com.unity.ui/Core/DefaultEventSystem.cs +++ b/ModuleOverrides/com.unity.ui/Core/DefaultEventSystem.cs @@ -166,6 +166,22 @@ void SendIMGUIEvents() SendFocusBasedEvent(self => UIElementsRuntimeUtility.CreateEvent(self.m_Event), this); ProcessTabEvent(m_Event, m_CurrentModifiers); } + else if (m_Event.type == EventType.ScrollWheel) + { + // There's different scrollDelta rates between Input Manager, New Input and IMGUI. UITK events use + // IMGUI conventions. Factors can vary between platforms (they come from PlatformDependent code). + // For example, InputEvent::InputEvent (InputEventWin.cpp) and NewInput::OnMessage (NewInput.cpp) + // read data differently from the WM_MOUSEWHEEL message. + // Since we want to rely as little as possible on IMGUI event position for multiple display support, + // we use the mouse position from input and combine it with the scroll delta from IMGUI. + var position = UIElementsRuntimeUtility.MultiDisplayBottomLeftToPanelPosition(input.mousePosition, out var targetDisplay); + var delta = position - m_LastMousePosition; + var scrollDelta = m_Event.delta; + + SendPositionBasedEvent(position, delta, PointerId.mousePointerId, targetDisplay, + (panelPosition, _, t) => WheelEvent.GetPooled(t.scrollDelta, panelPosition, t.modifiers), + (modifiers: m_CurrentModifiers, scrollDelta)); + } else if (!m_SendingTouchEvents && !m_SendingPenEvent && m_Event.pointerType != UnityEngine.PointerType.Mouse || m_Event.type == EventType.MouseEnterWindow || m_Event.type == EventType.MouseLeaveWindow) { @@ -198,7 +214,6 @@ private void ProcessMouseEvents() var position = UIElementsRuntimeUtility.MultiDisplayBottomLeftToPanelPosition(input.mousePosition, out var targetDisplay); var delta = position - m_LastMousePosition; - var scrollDelta = input.mouseScrollDelta; if (!m_MouseProcessedAtLeastOnce) { @@ -217,13 +232,6 @@ private void ProcessMouseEvents() (panelPosition, panelDelta, self) => PointerMoveEvent.GetPooled(EventType.MouseMove, panelPosition, panelDelta, -1, 0, self.m_CurrentModifiers), this); } - - if (!Mathf.Approximately(scrollDelta.x, 0f) || !Mathf.Approximately(scrollDelta.y, 0f)) - { - SendPositionBasedEvent(position, delta, PointerId.mousePointerId, targetDisplay, - (panelPosition, _, t) => WheelEvent.GetPooled(panelPosition, -1, 0, t.scrollDelta, t.modifiers), - (modifiers: m_CurrentModifiers, scrollDelta)); - } } int mouseButtonCount = input.mouseButtonCount; @@ -231,15 +239,16 @@ private void ProcessMouseEvents() { if (input.GetMouseButtonDown(button)) { - if (m_LastMousePressButton != button || Time.unscaledTime >= m_NextMousePressTime) + if (m_LastMousePressButton != button || input.unscaledTime >= m_NextMousePressTime) { m_LastMousePressButton = button; m_LastMouseClickCount = 0; - m_NextMousePressTime = Time.unscaledTime + Event.GetDoubleClickTime(); } var clickCount = ++m_LastMouseClickCount; + m_NextMousePressTime = input.unscaledTime + input.doubleClickTime; + SendPositionBasedEvent(position, delta, PointerId.mousePointerId, targetDisplay, (panelPosition, panelDelta, t) => PointerEventHelper.GetPooled(EventType.MouseDown, panelPosition, panelDelta, t.button, t.clickCount, t.modifiers), @@ -537,7 +546,7 @@ private Vector2 GetRawMoveVector() private bool ShouldSendMoveFromInput() { - float time = Time.unscaledTime; + float time = input.unscaledTime; Vector2 movement = GetRawMoveVector(); if (Mathf.Approximately(movement.x, 0f) && Mathf.Approximately(movement.y, 0f)) @@ -612,6 +621,8 @@ internal interface IInput Vector2 mouseScrollDelta { get; } int mouseButtonCount { get; } bool anyKey { get; } + float unscaledTime { get; } // overriden in unit tests + float doubleClickTime { get; } } private class Input : IInput @@ -632,6 +643,8 @@ private class Input : IInput public Vector2 mouseScrollDelta => UnityEngine.Input.mouseScrollDelta; public int mouseButtonCount => 3; public bool anyKey => UnityEngine.Input.anyKey; + public float unscaledTime => Time.unscaledTime; + public float doubleClickTime => Event.GetDoubleClickTime() * 0.001f; } private class NoInput : IInput @@ -652,6 +665,8 @@ public void ClearLastPenContactEvent() { } public Vector2 mouseScrollDelta => default; public int mouseButtonCount => 0; public bool anyKey => false; + public float unscaledTime => 0; + public float doubleClickTime => Mathf.Infinity; } } } diff --git a/ModuleOverrides/com.unity.ui/Core/DragAndDrop/BaseReorderableDragAndDropController.cs b/ModuleOverrides/com.unity.ui/Core/DragAndDrop/BaseReorderableDragAndDropController.cs index 896465e063..380fb58a77 100644 --- a/ModuleOverrides/com.unity.ui/Core/DragAndDrop/BaseReorderableDragAndDropController.cs +++ b/ModuleOverrides/com.unity.ui/Core/DragAndDrop/BaseReorderableDragAndDropController.cs @@ -11,40 +11,41 @@ internal abstract class BaseReorderableDragAndDropController : ICollectionDragAn { protected readonly BaseVerticalCollectionView m_View; - protected List m_SelectedIndices; + protected List m_SortedSelectedIds = new (); - public BaseReorderableDragAndDropController(BaseVerticalCollectionView view) + // Sorted by index in the source. + public IEnumerable GetSortedSelectedIds() => m_SortedSelectedIds; + + protected BaseReorderableDragAndDropController(BaseVerticalCollectionView view) { m_View = view; - enableReordering = true; } - public bool enableReordering { get; set; } + public virtual bool enableReordering { get; set; } = true; - public virtual bool CanStartDrag(IEnumerable itemIndices) + public virtual bool CanStartDrag(IEnumerable itemIds) { return enableReordering; } - public virtual StartDragArgs SetupDragAndDrop(IEnumerable itemIndices, bool skipText = false) + public virtual StartDragArgs SetupDragAndDrop(IEnumerable itemIds, bool skipText = false) { - m_SelectedIndices ??= new List(); - m_SelectedIndices.Clear(); + m_SortedSelectedIds.Clear(); var title = string.Empty; - if (itemIndices != null) + if (itemIds != null) { - foreach (var index in itemIndices) + foreach (var id in itemIds) { - m_SelectedIndices.Add(index); + m_SortedSelectedIds.Add(id); if (skipText) continue; if (string.IsNullOrEmpty(title)) { - var label = m_View.GetRecycledItemFromIndex(index)?.rootElement.Q