diff --git a/Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/BeeBuildProgramCommon.Data.gen.csproj b/Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/BeeBuildProgramCommon.Data.gen.csproj index 2388bd88fa..793953d705 100644 --- a/Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/BeeBuildProgramCommon.Data.gen.csproj +++ b/Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/BeeBuildProgramCommon.Data.gen.csproj @@ -18,6 +18,10 @@ false false + + $(HOME) + $(USERPROFILE) + diff --git a/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/Data.cs b/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/Data.cs index 48b52443eb..02db58f293 100644 --- a/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/Data.cs +++ b/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/Data.cs @@ -106,6 +106,7 @@ public class Il2CppConfig public string RelativeDataPath; public bool GenerateUsymFile; public string UsymtoolPath; + public BuildProgramLTOMode LtoMode; } public class Services @@ -129,4 +130,12 @@ public enum ScriptingBackend IL2CPP, CoreCLR, } + + // Keep in sync with bee LTO modes + public enum BuildProgramLTOMode + { + None = 0, + Thin = 1, + Full = 2, + } } diff --git a/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/PlayerBuildProgramLibrary.Data.gen.csproj b/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/PlayerBuildProgramLibrary.Data.gen.csproj index 515e2921e1..0a93d19a25 100644 --- a/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/PlayerBuildProgramLibrary.Data.gen.csproj +++ b/Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/PlayerBuildProgramLibrary.Data.gen.csproj @@ -19,6 +19,10 @@ false PlayerBuildProgramLibrary.Data + + $(HOME) + $(USERPROFILE) + diff --git a/Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/ScriptCompilationBuildProgram.Data.gen.csproj b/Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/ScriptCompilationBuildProgram.Data.gen.csproj index 79fabee04c..3e0fa83fc7 100644 --- a/Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/ScriptCompilationBuildProgram.Data.gen.csproj +++ b/Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/ScriptCompilationBuildProgram.Data.gen.csproj @@ -18,6 +18,10 @@ false false + + $(HOME) + $(USERPROFILE) + diff --git a/Editor/Mono/2D/SpriteAtlas/SpriteAtlasImporter.bindings.cs b/Editor/Mono/2D/SpriteAtlas/SpriteAtlasImporter.bindings.cs index 12a8178c4f..a295a8d200 100644 --- a/Editor/Mono/2D/SpriteAtlas/SpriteAtlasImporter.bindings.cs +++ b/Editor/Mono/2D/SpriteAtlas/SpriteAtlasImporter.bindings.cs @@ -15,7 +15,7 @@ namespace UnityEditor.U2D { // SpriteAtlas Importer lets you modify [[SpriteAtlas]] - [HelpURL("https://docs.unity3d.com/6000.2/Documentation/Manual/sprite/atlas/v2/sprite-atlas-v2.html")] + [HelpURL("sprite/atlas/sprite-atlas-reference")] [NativeHeader("Editor/Src/2D/SpriteAtlas/SpriteAtlasImporter.h")] public sealed partial class SpriteAtlasImporter : AssetImporter { diff --git a/Editor/Mono/2D/SpriteAtlas/SpriteAtlasImporterInspector.cs b/Editor/Mono/2D/SpriteAtlas/SpriteAtlasImporterInspector.cs index 9fde0c7d30..2d61e1d87a 100644 --- a/Editor/Mono/2D/SpriteAtlas/SpriteAtlasImporterInspector.cs +++ b/Editor/Mono/2D/SpriteAtlas/SpriteAtlasImporterInspector.cs @@ -317,6 +317,8 @@ private SerializedObject GetSerializedAssetObject() public override void OnEnable() { base.OnEnable(); + if (!AreImporterTargetsValid()) // asset gone: base already logged and bailed + return; m_FilterMode = serializedObject.FindProperty("m_TextureSettings.filterMode"); m_AnisoLevel = serializedObject.FindProperty("m_TextureSettings.anisoLevel"); diff --git a/Editor/Mono/AI/EditorAIAssistantAnalytics.cs b/Editor/Mono/AI/EditorAIAssistantAnalytics.cs new file mode 100644 index 0000000000..efe8c15dff --- /dev/null +++ b/Editor/Mono/AI/EditorAIAssistantAnalytics.cs @@ -0,0 +1,58 @@ +// 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.Analytics; + +namespace UnityEditor.Toolbars; + +enum UITriggerLocalEventSubType +{ + AIDropdownOpened, + AIInstallAccepted, +} + +internal static class EditorAIAssistantAnalytics +{ + const string k_EventName = "AIAssistantUITriggerLocalEvent"; + const string k_VendorKey = "unity.ai.assistant"; + + [Serializable] + class UITriggerLocalEventData : IAnalytic.IData + { + public UITriggerLocalEventData(UITriggerLocalEventSubType subType) => SubType = subType.ToString(); + + public string SubType; + } + + [AnalyticInfo(eventName: k_EventName, vendorKey: k_VendorKey)] + class UITriggerLocalEvent : IAnalytic + { + readonly UITriggerLocalEventData m_Data; + + public UITriggerLocalEvent(UITriggerLocalEventData data) => m_Data = data; + + public bool TryGatherData(out IAnalytic.IData data, out Exception error) + { + error = null; + data = m_Data; + return true; + } + } + + static void ReportUITriggerLocalEvent(UITriggerLocalEventData data) + { + EditorAnalytics.SendAnalytic(new UITriggerLocalEvent(data)); + } + + internal static void ReportAIDropdownOpenedEvent() + { + ReportUITriggerLocalEvent(new UITriggerLocalEventData(UITriggerLocalEventSubType.AIDropdownOpened)); + } + + internal static void ReportAIInstallAcceptedEvent() + { + ReportUITriggerLocalEvent(new UITriggerLocalEventData(UITriggerLocalEventSubType.AIInstallAccepted)); + } +} diff --git a/Editor/Mono/Animation/AnimationMode.bindings.cs b/Editor/Mono/Animation/AnimationMode.bindings.cs index bdcb21eeed..60e5bb053c 100644 --- a/Editor/Mono/Animation/AnimationMode.bindings.cs +++ b/Editor/Mono/Animation/AnimationMode.bindings.cs @@ -54,7 +54,6 @@ public class AnimationMode static private AnimationModeDriver s_DummyDriver; - static internal AnimationModeDriver GetDriver() => Internal_GetDriver() as AnimationModeDriver; static private AnimationModeDriver DummyDriver() { if (s_DummyDriver == null) @@ -234,7 +233,5 @@ public static void EndSampling() [NativeMethod(ThrowsException = true)] extern private static void Internal_StartCandidateRecording(Object driver); - - extern internal static Object Internal_GetDriver(); } } diff --git a/Editor/Mono/Animation/AnimationUtility.bindings.cs b/Editor/Mono/Animation/AnimationUtility.bindings.cs index 0eb3e14223..24c7e6bd43 100644 --- a/Editor/Mono/Animation/AnimationUtility.bindings.cs +++ b/Editor/Mono/Animation/AnimationUtility.bindings.cs @@ -36,7 +36,7 @@ public class AnimationClipCurveData // This is only used internally for deleting curves internal int classID; - internal int scriptInstanceID; + internal EntityId scriptInstanceID; public AnimationClipCurveData() { diff --git a/Editor/Mono/Animation/AnimationWindow/AnimEditor.cs b/Editor/Mono/Animation/AnimationWindow/AnimEditor.cs index da21ea42d1..640deed5cf 100644 --- a/Editor/Mono/Animation/AnimationWindow/AnimEditor.cs +++ b/Editor/Mono/Animation/AnimationWindow/AnimEditor.cs @@ -20,7 +20,7 @@ class AnimEditor : ScriptableObject [SerializeReference] private AnimationWindowState m_State; [SerializeReference] private DopeSheetEditor m_DopeSheet; [SerializeReference] private CurveEditor m_CurveEditor; - [SerializeField] private AnimationWindowHierarchy m_Hierarchy; + private AnimationWindowHierarchy m_Hierarchy; [SerializeField] private UnityEditor.AnimationWindowBuiltin.AnimationEventTimeLine m_Events; [SerializeField] private EditorWindow m_OwnerWindow; @@ -59,6 +59,8 @@ private bool triggerFraming internal string eventToolTipText => m_Events.tooltipText; internal Vector2 eventToolTipPosition => m_Events.tooltipPosition; + internal bool initialized => m_Initialized; + internal void MainContentOnGUI(Rect contentLayoutRect) { // Bail out if the hierarchy in animator is optimized. @@ -251,6 +253,9 @@ internal void EventLineOnGUI(Rect eventsRect) internal void HierarchyOnGUI(Rect hierarchyLayoutRect) { + if (m_State.disabled) + return; + if (!m_State.showReadOnly && m_State.selection.isReadOnly) { Vector2 labelSize = GUI.skin.label.CalcSize(AnimationWindowStyles.readOnlyPropertiesLabel); @@ -275,8 +280,7 @@ internal void HierarchyOnGUI(Rect hierarchyLayoutRect) return; } - if (!m_State.disabled) - m_Hierarchy.OnGUI(hierarchyLayoutRect); + m_Hierarchy.OnGUI(hierarchyLayoutRect); } internal void DopeSheetOnGUI(Rect position) diff --git a/Editor/Mono/Animation/AnimationWindow/AnimationWindow.cs b/Editor/Mono/Animation/AnimationWindow/AnimationWindow.cs index 2fd5613b12..2ae304cd06 100644 --- a/Editor/Mono/Animation/AnimationWindow/AnimationWindow.cs +++ b/Editor/Mono/Animation/AnimationWindow/AnimationWindow.cs @@ -4,6 +4,7 @@ using System; using UnityEngine; +using UnityEngine.Bindings; using System.Collections.Generic; using UnityEditor.Callbacks; using UnityObject = UnityEngine.Object; @@ -80,6 +81,7 @@ public AnimationClip animationClip set => clip = new UnityEditor.AnimationWindowBuiltin.AnimationWindowClip(value, state.activeRootGameObject); } + [VisibleToOtherModules("UnityEditor.UIToolkitAuthoringModule")] internal IAnimationWindowSelectionItem selection { get => state?.selection; @@ -171,6 +173,8 @@ internal void RefreshClip() void OnEnable() { + titleContent = GetLocalizedTitleContent(); + if (m_AnimEditor == null) { m_AnimEditor = CreateInstance(); @@ -184,6 +188,7 @@ void OnEnable() Undo.undoRedoEvent += UndoRedoPerformed; EditorApplication.playModeStateChanged += OnPlayModeStateChanged; + AssemblyReloadEvents.beforeAssemblyReload += PurgeSelection; } void OnDisable() @@ -193,6 +198,7 @@ void OnDisable() Undo.undoRedoEvent -= UndoRedoPerformed; EditorApplication.playModeStateChanged -= OnPlayModeStateChanged; + AssemblyReloadEvents.beforeAssemblyReload -= PurgeSelection; } void OnDestroy() @@ -202,10 +208,15 @@ void OnDestroy() void Update() { - state?.Update(); + // AnimationWindowState has some data that still depends on IMGUI + // evaluation and is initialized just in time during rendering. + // Make sure AnimEditor has been initialized before updating + // AnimationWindowState and AnimEditor. + if (m_AnimEditor == null || !m_AnimEditor.initialized) + return; - if (m_AnimEditor != null) - m_AnimEditor.Update(); + state?.Update(); + m_AnimEditor.Update(); hasUnsavedChanges = selection?.hasUnsavedChanges ?? false; @@ -415,6 +426,7 @@ void ShowButton(Rect r) private void UndoRedoPerformed(in UndoRedoInfo info) { + OnSelectionChangeInternal(false); Repaint(); } @@ -428,18 +440,41 @@ private void OnPlayModeStateChanged(PlayModeStateChange state) } else { - selection?.OnPlayModeStateChanged(state); + // Purge selection before domain reload to prevent stale GameObject references + PurgeSelection(); } } else { - selection?.OnPlayModeStateChanged(state); + // Purge selection before domain reload on ExitingPlayMode + if (state == PlayModeStateChange.ExitingPlayMode) + { + PurgeSelection(); + } if (state == PlayModeStateChange.EnteredEditMode) { - // Reload selection + // Reload selection when exiting play mode OnSelectionChangeInternal(false); } + + if (state == PlayModeStateChange.EnteredPlayMode) + { + // Reload selection when entering play mode + OnSelectionChangeInternal(false); + } + } + } + + private void PurgeSelection() + { + // Clear selection to prevent stale GameObject references during domain reload. + // Note: Dispose() will also stop preview by disposing the controller. + // This matches the behavior during assembly reload (script compilation). + if (state != null) + { + state.linkedWithSequencer = false; + state.selection = new FallbackSelectionItem(); } } diff --git a/Editor/Mono/Animation/AnimationWindow/AnimationWindowCurve.cs b/Editor/Mono/Animation/AnimationWindow/AnimationWindowCurve.cs index 64b64abac4..114ec86dcd 100644 --- a/Editor/Mono/Animation/AnimationWindow/AnimationWindowCurve.cs +++ b/Editor/Mono/Animation/AnimationWindow/AnimationWindowCurve.cs @@ -35,8 +35,9 @@ class AnimationWindowCurve : private bool m_IsPhantom; public EditorCurveBinding binding { get { return m_Binding; } } - public bool isPPtrCurve { get { return m_Binding.isPPtrCurve; } } - public bool isDiscreteCurve { get { return m_Binding.isDiscreteCurve; } } + public bool isPPtrCurve => m_Binding.isPPtrCurve; + public bool isDiscreteCurve => m_Binding.isDiscreteCurve; + public bool isFloatCurve => !m_Binding.isDiscreteCurve && !m_Binding.isPPtrCurve; public bool isSerializeReferenceCurve { get {return m_Binding.isSerializeReferenceCurve;}} public bool isPhantom { get { return m_IsPhantom; } set { m_IsPhantom = value; } } public InheritanceState inheritanceState { get; set; } diff --git a/Editor/Mono/Animation/AnimationWindow/AnimationWindowHierarchyGUI.cs b/Editor/Mono/Animation/AnimationWindow/AnimationWindowHierarchyGUI.cs index 48a8eb193e..5e9d4224fe 100644 --- a/Editor/Mono/Animation/AnimationWindow/AnimationWindowHierarchyGUI.cs +++ b/Editor/Mono/Animation/AnimationWindow/AnimationWindowHierarchyGUI.cs @@ -43,6 +43,9 @@ internal class AnimationWindowHierarchyGUI : TreeViewGUI private const float k_ValueFieldDragWidth = 15; private const float k_ValueFieldWidth = 80; private const float k_ValueFieldOffsetFromRightSide = 100; + private const float k_ObjectFieldAdditionalWidth = 30; + private const float k_ObjectFieldAdditionalOffset = k_ObjectFieldAdditionalWidth + 30; + private const float k_ObjectFieldMaxHeight = 18; private const float k_ColorIndicatorTopMargin = 3; public static readonly float k_DopeSheetRowHeight = EditorGUI.kSingleLineHeight; public static readonly float k_DopeSheetRowHeightTall = k_DopeSheetRowHeight * 2f; @@ -156,7 +159,7 @@ public override void BeginRowGUI() for (int i = 0; i < rowCount; ++i) { var propertyNode = m_TreeView.data.GetItem(i) as AnimationWindowHierarchyPropertyNode; - if (propertyNode != null && !propertyNode.isPPtrNode) + if (propertyNode != null) m_HierarchyItemValueControlIDs[i] = GUIUtility.GetControlID(FocusType.Keyboard); else m_HierarchyItemValueControlIDs[i] = 0; // not needed. @@ -320,6 +323,8 @@ private void DoIconAndName(Rect rect, AnimationWindowHierarchyNode node, bool se GUI.Label(rect, Styles.content, lineStyle); SetStyleTextColor(lineStyle, oldColor); + + GUIView.current?.MarkHotRegion(GUIClip.UnclipToWindow(rect)); } if (IsRenaming(node.id) && Event.current.type != EventType.Layout) @@ -349,86 +354,98 @@ private void DoValueField(Rect rect, AnimationWindowHierarchyNode node, int row) AnimationWindowCurve curve = curves[0]; object value = CurveBindingUtility.GetCurrentValue(state, curve); - if (!curve.isPPtrCurve) - { - Rect valueFieldDragRect = new Rect(rect.xMax - k_ValueFieldOffsetFromRightSide - k_ValueFieldDragWidth, rect.y, k_ValueFieldDragWidth, rect.height); - Rect valueFieldRect = new Rect(rect.xMax - k_ValueFieldOffsetFromRightSide, rect.y, k_ValueFieldWidth, rect.height); + int id = m_HierarchyItemValueControlIDs[row]; - if (Event.current.type == EventType.MouseMove && valueFieldRect.Contains(Event.current.mousePosition)) - s_WasInsideValueRectFrame = Time.frameCount; + Rect valueFieldDragRect = new Rect(rect.xMax - k_ValueFieldOffsetFromRightSide - k_ValueFieldDragWidth, rect.y, k_ValueFieldDragWidth, rect.height); + Rect valueFieldRect = new Rect(rect.xMax - k_ValueFieldOffsetFromRightSide, rect.y, k_ValueFieldWidth, rect.height); - EditorGUI.BeginChangeCheck(); + if (Event.current.type == EventType.MouseMove && valueFieldRect.Contains(Event.current.mousePosition)) + s_WasInsideValueRectFrame = Time.frameCount; + + EditorGUI.BeginChangeCheck(); + + if (curve.isPPtrCurve) + { + var objType = curve.valueType; - if (curve.valueType == typeof(bool)) + if (typeof(UnityEngine.Object).IsAssignableFrom(objType)) { - value = GUI.Toggle(valueFieldRect, m_HierarchyItemValueControlIDs[row], Convert.ToSingle(value) != 0f, GUIContent.none, EditorStyles.toggle) ? 1f : 0f; + var height = Mathf.Min(k_ObjectFieldMaxHeight, valueFieldRect.height); + var yOffset = (valueFieldRect.height - height) * 0.5f; + valueFieldRect = new Rect(valueFieldRect.x - k_ObjectFieldAdditionalOffset, valueFieldRect.y + yOffset, valueFieldRect.width + k_ObjectFieldAdditionalWidth, height); + + value = EditorGUI.DoObjectField(valueFieldRect, valueFieldRect, id, value as UnityEngine.Object, null, objType, null, false); } - else + } + else if (curve.valueType == typeof(bool)) + { + value = GUI.Toggle(valueFieldRect, id, Convert.ToSingle(value) != 0f, GUIContent.none, EditorStyles.toggle) ? 1f : 0f; + } + else + { + bool enterInTextField = (EditorGUIUtility.keyboardControl == id + && EditorGUIUtility.editingTextField + && Event.current.type == EventType.KeyDown + && (Event.current.character == '\n' || (int)Event.current.character == 3)); + + // Force back keyboard focus to float field editor when editing it since the TreeView forces keyboard focus on itself at mouse down. + // The focus will be reclaimed after the TreeViewController.OnGUI call. + if (EditorGUI.s_RecycledEditor.controlID == id && Event.current.type == EventType.MouseDown && valueFieldRect.Contains(Event.current.mousePosition)) { - int id = m_HierarchyItemValueControlIDs[row]; - bool enterInTextField = (EditorGUIUtility.keyboardControl == id - && EditorGUIUtility.editingTextField - && Event.current.type == EventType.KeyDown - && (Event.current.character == '\n' || (int)Event.current.character == 3)); - - // Force back keyboard focus to float field editor when editing it since the TreeView forces keyboard focus on itself at mouse down. - // The focus will be reclaimed after the TreeViewController.OnGUI call. - if (EditorGUI.s_RecycledEditor.controlID == id && Event.current.type == EventType.MouseDown && valueFieldRect.Contains(Event.current.mousePosition)) - { - m_NeedsToReclaimFieldFocus = true; - m_FieldToReclaimFocus = id; - } + m_NeedsToReclaimFieldFocus = true; + m_FieldToReclaimFocus = id; + } - if (curve.isDiscreteCurve) + if (curve.isDiscreteCurve) + { + value = EditorGUI.DoIntField(EditorGUI.s_RecycledEditor, + valueFieldRect, + valueFieldDragRect, + id, + Convert.ToInt32(value), + EditorGUI.kIntFieldFormatString, + m_AnimationSelectionTextField, + true, + 0); + if (enterInTextField) { - value = EditorGUI.DoIntField(EditorGUI.s_RecycledEditor, - valueFieldRect, - valueFieldDragRect, - id, - Convert.ToInt32(value), - EditorGUI.kIntFieldFormatString, - m_AnimationSelectionTextField, - true, - 0); - if (enterInTextField) - { - GUI.changed = true; - Event.current.Use(); - } + GUI.changed = true; + Event.current.Use(); } - else + } + else + { + value = EditorGUI.DoFloatField(EditorGUI.s_RecycledEditor, + valueFieldRect, + valueFieldDragRect, + id, + Convert.ToSingle(value), + "g5", + m_AnimationSelectionTextField, + true); + if (enterInTextField) { - value = EditorGUI.DoFloatField(EditorGUI.s_RecycledEditor, - valueFieldRect, - valueFieldDragRect, - id, - Convert.ToSingle(value), - "g5", - m_AnimationSelectionTextField, - true); - if (enterInTextField) - { - GUI.changed = true; - Event.current.Use(); - } - - var floatValue = Convert.ToSingle(value); - if (float.IsInfinity(floatValue) || float.IsNaN(floatValue)) - value = 0f; + GUI.changed = true; + Event.current.Use(); } + + var floatValue = Convert.ToSingle(value); + if (float.IsInfinity(floatValue) || float.IsNaN(floatValue)) + value = 0f; } + } - if (EditorGUI.EndChangeCheck()) - { - string undoLabel = "Edit Key"; + if (EditorGUI.EndChangeCheck()) + { + string undoLabel = "Edit Key"; - AnimationKeyTime newAnimationKeyTime = AnimationKeyTime.Time(state.currentTime, curve.clip.frameRate); - AnimationWindowUtility.AddKeyframeToCurve(curve, value, curve.valueType, newAnimationKeyTime); + AnimationKeyTime newAnimationKeyTime = AnimationKeyTime.Time(state.currentTime, curve.clip.frameRate); + AnimationWindowUtility.AddKeyframeToCurve(curve, value, curve.valueType, newAnimationKeyTime); - state.SaveCurve(curve.clip, curve, undoLabel); - curvesChanged = true; - } + state.SaveCurve(curve.clip, curve, undoLabel); + curvesChanged = true; } + } if (curvesChanged) @@ -458,6 +475,7 @@ private bool DoTreeViewButton(int id, Rect position, GUIContent content, GUIStyl { case EventType.Repaint: style.Draw(position, content, id, false, position.Contains(evt.mousePosition)); + GUIView.current?.MarkHotRegion(GUIClip.UnclipToWindow(position)); break; case EventType.MouseDown: if (position.Contains(evt.mousePosition) && evt.button == 0) diff --git a/Editor/Mono/Animation/AnimationWindow/AnimationWindowKeyframe.cs b/Editor/Mono/Animation/AnimationWindow/AnimationWindowKeyframe.cs index 134bc4fac4..97295ebd4c 100644 --- a/Editor/Mono/Animation/AnimationWindow/AnimationWindowKeyframe.cs +++ b/Editor/Mono/Animation/AnimationWindow/AnimationWindowKeyframe.cs @@ -101,8 +101,9 @@ public AnimationWindowCurve curve } } - public bool isPPtrCurve { get { return curve.isPPtrCurve; } } - public bool isDiscreteCurve { get { return curve.isDiscreteCurve; } } + public bool isPPtrCurve => curve.isPPtrCurve; + public bool isDiscreteCurve => curve.isDiscreteCurve; + public bool isFloatCurve => curve.isFloatCurve; public AnimationWindowKeyframe() { diff --git a/Editor/Mono/Animation/AnimationWindow/AnimationWindowState.cs b/Editor/Mono/Animation/AnimationWindow/AnimationWindowState.cs index bed8e50ef2..3bc7d1e0fd 100644 --- a/Editor/Mono/Animation/AnimationWindow/AnimationWindowState.cs +++ b/Editor/Mono/Animation/AnimationWindow/AnimationWindowState.cs @@ -7,7 +7,7 @@ using UnityEngine; using UnityEditor; using System.Collections.Generic; -using UnityEditor.IMGUI.Controls; +using Unity.Collections; using Object = UnityEngine.Object; using TreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem; using static UnityEditor.AnimationUtility; @@ -74,7 +74,7 @@ public enum SnapMode private EditorCurveBinding? m_lastAddedCurveBinding; // Hash of all the things that require animationWindow to refresh if they change - private int m_PreviousRefreshHash; + [NonSerialized] private int m_PreviousRefreshHash; // Changing m_Refresh means you are ordering a refresh at the next Update (). // CurvesOnly means that there is no need to refresh the hierarchy, since only the keyframe data changed. @@ -334,16 +334,9 @@ public void RefreshCurve(EditorCurveBinding binding) } } - private void PurgeSelection() - { - linkedWithSequencer = false; - m_Selection = new FallbackSelectionItem(); - } - public void OnEnable() { Undo.undoRedoEvent += UndoRedoPerformed; - AssemblyReloadEvents.beforeAssemblyReload += PurgeSelection; AnimationUtility.onCurveWasModified += CurveWasModified; // NoOps... @@ -358,7 +351,6 @@ public void OnEnable() public void OnDisable() { Undo.undoRedoEvent -= UndoRedoPerformed; - AssemblyReloadEvents.beforeAssemblyReload -= PurgeSelection; AnimationUtility.onCurveWasModified -= CurveWasModified; previewing = false; @@ -609,9 +601,7 @@ public bool ShouldShowCurve(AnimationWindowCurve curve) Transform t = activeRootGameObject.transform.Find(curve.path); if (t != null) { -#pragma warning disable UA2001 // The Banned API Analyzer produces compile errors for any new Linq code. This pre-existing usage has been suppressed, but should be rewritten if possible. if (!m_SelectionFilter.Contains(t.gameObject.GetEntityId())) -#pragma warning restore UA2001 return false; } else diff --git a/Editor/Mono/Animation/AnimationWindow/AnimationWindowUtility.cs b/Editor/Mono/Animation/AnimationWindow/AnimationWindowUtility.cs index 1826e9f50a..9ceae02a7e 100644 --- a/Editor/Mono/Animation/AnimationWindow/AnimationWindowUtility.cs +++ b/Editor/Mono/Animation/AnimationWindow/AnimationWindowUtility.cs @@ -314,7 +314,7 @@ public static bool ContainsFloatKeyframes(List keyframe foreach (var key in keyframes) { - if (!key.isPPtrCurve) + if (key.isFloatCurve) return true; } diff --git a/Editor/Mono/Animation/AnimationWindow/Builtin/AnimationClipSelectionItem.cs b/Editor/Mono/Animation/AnimationWindow/Builtin/AnimationClipSelectionItem.cs index dedcbc0b8e..dff9e4a52c 100644 --- a/Editor/Mono/Animation/AnimationWindow/Builtin/AnimationClipSelectionItem.cs +++ b/Editor/Mono/Animation/AnimationWindow/Builtin/AnimationClipSelectionItem.cs @@ -16,6 +16,9 @@ internal class AnimationClipSelectionItem : AnimationWindowSelectionItem { } + public static AnimationClipSelectionItem Create(AnimationClip animationClip, Object sourceObject = null) => + Create(null, animationClip, sourceObject); + public static AnimationClipSelectionItem Create(AnimationWindow window, AnimationClip animationClip, Object sourceObject = null) { var selectionItem = new AnimationClipSelectionItem(window); diff --git a/Editor/Mono/Animation/AnimationWindow/Builtin/AnimationWindowControl.cs b/Editor/Mono/Animation/AnimationWindow/Builtin/AnimationWindowControl.cs index 5608260b19..2e2cadfb13 100644 --- a/Editor/Mono/Animation/AnimationWindow/Builtin/AnimationWindowControl.cs +++ b/Editor/Mono/Animation/AnimationWindow/Builtin/AnimationWindowControl.cs @@ -19,14 +19,6 @@ namespace UnityEditor.AnimationWindowBuiltin [Serializable] class AnimationWindowControl : IAnimationWindowController, IAnimationContextualResponder { - public AnimationWindowControl() - { - if (AnimationMode.GetDriver() is MainDriver mainDriver) - m_Driver = mainDriver; - } - - class MainDriver: AnimationModeDriver {} - class CandidateDriver: AnimationModeDriver {} class CandidateRecordingState : IAnimationRecordingState { public GameObject activeGameObject { get; private set; } @@ -174,7 +166,9 @@ public void Dispose() { StopPreview(); if (m_Driver != null) + { ScriptableObject.DestroyImmediate(m_Driver); + } } public void OnSelectionChanged() @@ -186,15 +180,6 @@ public void OnSelectionChanged() StopPreview(); } - public void OnPlayModeStateChanged(PlayModeStateChange state) - { - if (state == PlayModeStateChange.ExitingPlayMode || - state == PlayModeStateChange.ExitingEditMode) - { - StopPreview(); - } - } - public float time { get => m_Time.time; @@ -361,8 +346,11 @@ public bool canPreview if (state.activeAnimationPlayer is Animator{ isOptimizable:true, hasTransformHierarchy:false}) return false; + var driver = GetAnimationModeDriverNoAlloc(); - return (driver != null && AnimationMode.InAnimationMode(driver)) || !AnimationMode.InAnimationMode(); + + return !AnimationMode.InAnimationMode() // no one is in mode + || (driver != null && AnimationMode.InAnimationMode(driver)); } } @@ -696,7 +684,7 @@ private AnimationModeDriver GetAnimationModeDriver() { if (m_Driver == null) { - m_Driver = ScriptableObject.CreateInstance(); + m_Driver = ScriptableObject.CreateInstance(); m_Driver.hideFlags = HideFlags.HideAndDontSave; m_Driver.name = "AnimationWindowDriver"; m_Driver.isKeyCallback += (Object target, string propertyPath) => @@ -726,7 +714,7 @@ private AnimationModeDriver GetCandidateDriver() { if (m_CandidateDriver == null) { - m_CandidateDriver = ScriptableObject.CreateInstance(); + m_CandidateDriver = ScriptableObject.CreateInstance(); m_CandidateDriver.name = "AnimationWindowCandidateDriver"; } diff --git a/Editor/Mono/Animation/AnimationWindow/Builtin/AnimationWindowEventInspector.cs b/Editor/Mono/Animation/AnimationWindow/Builtin/AnimationWindowEventInspector.cs index abf968e86b..0a97e3aca4 100644 --- a/Editor/Mono/Animation/AnimationWindow/Builtin/AnimationWindowEventInspector.cs +++ b/Editor/Mono/Animation/AnimationWindow/Builtin/AnimationWindowEventInspector.cs @@ -16,7 +16,8 @@ namespace UnityEditor.AnimationWindowBuiltin internal class AnimationWindowEventInspector : UnityEditor.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!"); + 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!"); + public static GUIContent s_RequireMethod = EditorGUIUtility.TrTextContent("Require Receiver", "When enabled, an error will be emitted if there is no matching method on the GameObject."); const string kNotSupportedPostFix = " (Function Not Supported)"; const string kNoneSelected = "(No Function Selected)"; @@ -56,6 +57,7 @@ public static void OnEditAnimationEvents(AnimationWindowEvent[] awEvents, Animat AnimationEvent firstEvent = data.selectedEvents[0]; bool singleFunctionName = Array.TrueForAll(data.selectedEvents, evt => evt.functionName == firstEvent.functionName); + bool singleMessageOptions = Array.TrueForAll(data.selectedEvents, evt => evt.messageOptions == firstEvent.messageOptions); EditorGUI.BeginChangeCheck(); @@ -102,17 +104,38 @@ public static void OnEditAnimationEvents(AnimationWindowEvent[] awEvents, Animat EditorGUIUtility.labelWidth = 130; EditorGUI.showMixedValue = !singleFunctionName; - int wasSelected = singleFunctionName ? selected : -1; + + EditorGUI.BeginChangeCheck(); selected = EditorGUILayout.Popup("Function: ", selected, methodsFormatted); - if (wasSelected != selected && selected != -1 && selected != notSupportedIndex) + if (EditorGUI.EndChangeCheck()) + { + if (selected >= 0 && selected < notSupportedIndex) + { + foreach (var evt in data.selectedEvents) + { + evt.functionName = supportedMethods[selected].Name; + evt.stringParameter = string.Empty; + } + } + } + + EditorGUI.showMixedValue = false; + + EditorGUI.indentLevel++; + EditorGUI.showMixedValue = !singleMessageOptions; + bool requireReceiver = EditorGUILayout.Toggle( + s_RequireMethod, + firstEvent.messageOptions == SendMessageOptions.RequireReceiver); + var sendMessageOptions = requireReceiver ? SendMessageOptions.RequireReceiver : SendMessageOptions.DontRequireReceiver; + if (sendMessageOptions != firstEvent.messageOptions) { foreach (var evt in data.selectedEvents) { - evt.functionName = supportedMethods[selected].Name; - evt.stringParameter = string.Empty; + evt.messageOptions = sendMessageOptions; } } EditorGUI.showMixedValue = false; + EditorGUI.indentLevel--; var selectedParameter = supportedMethods[selected].parameterType; @@ -150,6 +173,7 @@ public static void OnEditAnimationEvents(AnimationWindowEvent[] awEvents, Animat GUILayout.Label(duplicatedFunctionDetails, EditorStyles.helpBox); } } + } else { @@ -165,6 +189,21 @@ public static void OnEditAnimationEvents(AnimationWindowEvent[] awEvents, Animat } EditorGUI.showMixedValue = false; + EditorGUI.indentLevel++; + EditorGUI.showMixedValue = !singleMessageOptions; + bool wasRequiringReceiver = firstEvent.messageOptions == SendMessageOptions.RequireReceiver; + bool nowRequireReceiver = EditorGUILayout.Toggle(s_RequireMethod, wasRequiringReceiver); + var sendMessageOptions = nowRequireReceiver ? SendMessageOptions.RequireReceiver : SendMessageOptions.DontRequireReceiver; + if (wasRequiringReceiver != nowRequireReceiver) + { + foreach (var evt in data.selectedEvents) + { + evt.messageOptions = sendMessageOptions; + } + } + EditorGUI.showMixedValue = false; + EditorGUI.indentLevel--; + if (singleFunctionName) { DoEditRegularParameters(data.selectedEvents, typeof(AnimationEvent)); @@ -228,6 +267,9 @@ public static void OnDisabledAnimationEvent() using (new EditorGUI.DisabledScope(true)) { dummyEvent.functionName = EditorGUILayout.TextField(EditorGUIUtility.TrTextContent("Function"), dummyEvent.functionName); + EditorGUI.indentLevel++; + dummyEvent.m_MessageOptions = EditorGUILayout.Toggle(s_RequireMethod, dummyEvent.messageOptions == SendMessageOptions.RequireReceiver) ? 0 : 1; + EditorGUI.indentLevel--; DoEditRegularParameters(new AnimationEvent[] { dummyEvent }, typeof(AnimationEvent)); } } diff --git a/Editor/Mono/Animation/AnimationWindow/Builtin/AnimationWindowSelectionItem.cs b/Editor/Mono/Animation/AnimationWindow/Builtin/AnimationWindowSelectionItem.cs index d9fac4d217..855d4f632b 100644 --- a/Editor/Mono/Animation/AnimationWindow/Builtin/AnimationWindowSelectionItem.cs +++ b/Editor/Mono/Animation/AnimationWindow/Builtin/AnimationWindowSelectionItem.cs @@ -5,10 +5,12 @@ using System; using UnityEditorInternal; using UnityEngine; +using UnityEngine.Bindings; namespace UnityEditor.AnimationWindowBuiltin { [Serializable] + [VisibleToOtherModules("UnityEditor.UIToolkitAuthoringModule")] abstract class AnimationWindowSelectionItem : System.IEquatable, IAnimationWindowSelectionItem { [SerializeField] protected AnimationWindow m_Window; @@ -293,11 +295,6 @@ public void DiscardChanges() throw new NotImplementedException(); } - public virtual void OnPlayModeStateChanged(PlayModeStateChange state) - { - controller.OnPlayModeStateChanged(state); - } - // When curve is modified, we never trigger refresh right away. We order a refresh at later time by setting refresh to appropriate value. public virtual void CurveWasModified(AnimationClip clip, EditorCurveBinding binding, AnimationUtility.CurveModifiedType type) { diff --git a/Editor/Mono/Animation/AnimationWindow/Builtin/GameObjectSelectionItem.cs b/Editor/Mono/Animation/AnimationWindow/Builtin/GameObjectSelectionItem.cs index 25df3002c7..03207e4d85 100644 --- a/Editor/Mono/Animation/AnimationWindow/Builtin/GameObjectSelectionItem.cs +++ b/Editor/Mono/Animation/AnimationWindow/Builtin/GameObjectSelectionItem.cs @@ -5,10 +5,12 @@ using System; using UnityEngine; using UnityEditorInternal; +using UnityEngine.Bindings; namespace UnityEditor.AnimationWindowBuiltin { [Serializable] + [VisibleToOtherModules("UnityEditor.UIToolkitAuthoringModule")] class GameObjectSelectionItem : AnimationWindowSelectionItem { protected GameObjectSelectionItem(AnimationWindow window) : base(window) diff --git a/Editor/Mono/Animation/AnimationWindow/Builtin/RotationCurveInterpolation.cs b/Editor/Mono/Animation/AnimationWindow/Builtin/RotationCurveInterpolation.cs index 271f2c8ecb..8847bc24d2 100644 --- a/Editor/Mono/Animation/AnimationWindow/Builtin/RotationCurveInterpolation.cs +++ b/Editor/Mono/Animation/AnimationWindow/Builtin/RotationCurveInterpolation.cs @@ -11,7 +11,7 @@ namespace UnityEditor.AnimationWindowBuiltin { - class RotationCurveInterpolation + static class RotationCurveInterpolation { public struct State { @@ -36,7 +36,7 @@ internal static EditorCurveBinding[] GenerateTransformCurveBindingArray(string p return bindings; } - static public EditorCurveBinding[] RemapAnimationBindingForAddKey(EditorCurveBinding binding, AnimationClip clip) + public static EditorCurveBinding[] RemapAnimationBindingForAddKey(EditorCurveBinding binding, AnimationClip clip) { if (!AnimationWindowUtility.IsTransformType(binding.type)) { @@ -83,7 +83,7 @@ static EditorCurveBinding[] SelectRotationBindingForAddKey(EditorCurveBinding bi } } - static public EditorCurveBinding RemapAnimationBindingForRotationCurves(EditorCurveBinding curveBinding, AnimationClip clip) + public static EditorCurveBinding RemapAnimationBindingForRotationCurves(EditorCurveBinding curveBinding, AnimationClip clip) { if (!AnimationWindowUtility.IsTransformType(curveBinding.type)) return curveBinding; diff --git a/Editor/Mono/Animation/AnimationWindow/CurveEditorSettings.cs b/Editor/Mono/Animation/AnimationWindow/CurveEditorSettings.cs index dbe8c45ece..a9b8945adb 100644 --- a/Editor/Mono/Animation/AnimationWindow/CurveEditorSettings.cs +++ b/Editor/Mono/Animation/AnimationWindow/CurveEditorSettings.cs @@ -2,6 +2,7 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using System; using UnityEngine; namespace UnityEditor @@ -46,6 +47,7 @@ public bool hasUnboundedRanges // Offset to move the labels along the horizontal axis to make room for the overlaid scrollbar in the // curve editor popup. public float hTickLabelOffset = 0; + [NonSerialized] public EditorGUIUtility.SkinnedColor wrapColor = new EditorGUIUtility.SkinnedColor(new Color(1.0f, 1.0f, 1.0f, 0.5f), new Color(.65f, .65f, .65f, 0.5f)); public bool useFocusColors = false; public bool showAxisLabels = true; diff --git a/Editor/Mono/Animation/AnimationWindow/DefaultAnimationWindowController.cs b/Editor/Mono/Animation/AnimationWindow/DefaultAnimationWindowController.cs index 88b1161a2d..7ced4114fd 100644 --- a/Editor/Mono/Animation/AnimationWindow/DefaultAnimationWindowController.cs +++ b/Editor/Mono/Animation/AnimationWindow/DefaultAnimationWindowController.cs @@ -16,7 +16,6 @@ class DefaultAnimationWindowController : IAnimationWindowController public void Dispose() {} public void OnSelectionChanged() {} - public void OnPlayModeStateChanged(PlayModeStateChange state) { } public float time { diff --git a/Editor/Mono/Animation/AnimationWindow/DopeSheetEditor.cs b/Editor/Mono/Animation/AnimationWindow/DopeSheetEditor.cs index daf0aee36a..f9d445e143 100644 --- a/Editor/Mono/Animation/AnimationWindow/DopeSheetEditor.cs +++ b/Editor/Mono/Animation/AnimationWindow/DopeSheetEditor.cs @@ -255,7 +255,7 @@ public void OnGUI(Rect position, Vector2 scrollPosition) Init(); // drag'n'drops outside any dopelines - HandleDragAndDropToEmptyArea(); + HandleDragAndDropToEmptyArea(position); GUIClip.Push(position, scrollPosition, Vector2.zero, false); @@ -589,7 +589,7 @@ private GenericMenu GenerateMenu(DopeLine dopeline) Hashtable editorCurves = new Hashtable(); foreach (AnimationWindowKeyframe key in state.selectedKeys) { - if (key.isDiscreteCurve) + if (!key.isFloatCurve) continue; int index = key.curve.GetKeyframeIndex(AnimationKeyTime.Time(key.time, state.frameRate)); @@ -754,13 +754,17 @@ private void HandleSelectionRect(Rect rect) } // Handles drag and drop into empty area outside dopelines - private void HandleDragAndDropToEmptyArea() + private void HandleDragAndDropToEmptyArea(Rect dopeSheetArea) { Event evt = Event.current; if (evt.type != EventType.DragPerform && evt.type != EventType.DragUpdated) return; + // Only handle drag and drop if the mouse is inside the DopeSheet area + if (!dopeSheetArea.Contains(evt.mousePosition)) + return; + if (!ValidateDragAndDropObjects()) return; @@ -1342,7 +1346,7 @@ internal class DopeSheetSelectionRect enum SelectionType { Normal, Additive, Subtractive } public readonly GUIStyle createRect = "U2D.createRect"; - static int s_RectSelectionID = GUIUtility.GetPermanentControlID(); + readonly int m_RectSelectionID = GUIUtility.GetPermanentControlID(); public DopeSheetSelectionRect(DopeSheetEditor owner) { @@ -1353,7 +1357,7 @@ public void OnGUI(Rect position) { Event evt = Event.current; Vector2 mousePos = evt.mousePosition; - int id = s_RectSelectionID; + int id = m_RectSelectionID; switch (evt.GetTypeForControl(id)) { case EventType.MouseDown: diff --git a/Editor/Mono/Animation/AnimationWindow/FallbackSelectionItem.cs b/Editor/Mono/Animation/AnimationWindow/FallbackSelectionItem.cs index 0d922e35f6..8cde0bb53c 100644 --- a/Editor/Mono/Animation/AnimationWindow/FallbackSelectionItem.cs +++ b/Editor/Mono/Animation/AnimationWindow/FallbackSelectionItem.cs @@ -79,10 +79,6 @@ public Type GetValueType(EditorCurveBinding _) return null; } - public void OnPlayModeStateChanged(PlayModeStateChange state) - { - } - public bool isImported => false; public bool hasUnsavedChanges => false; public void SaveChanges() diff --git a/Editor/Mono/Animation/AnimationWindow/IAnimationWindowClip.cs b/Editor/Mono/Animation/AnimationWindow/IAnimationWindowClip.cs index f9f67978d3..f79200831d 100644 --- a/Editor/Mono/Animation/AnimationWindow/IAnimationWindowClip.cs +++ b/Editor/Mono/Animation/AnimationWindow/IAnimationWindowClip.cs @@ -6,12 +6,14 @@ using System.Collections.Generic; using UnityEditorInternal; using UnityEngine; +using UnityEngine.Bindings; namespace UnityEditor { /// /// Use this interface to control how an animation clip is authored in the AnimationWindow. /// + [VisibleToOtherModules("UnityEditor.UIToolkitAuthoringModule")] interface IAnimationWindowClip : IEquatable { string name { get; } diff --git a/Editor/Mono/Animation/AnimationWindow/IAnimationWindowControl.cs b/Editor/Mono/Animation/AnimationWindow/IAnimationWindowControl.cs index a0b2b49002..b95ff6fde8 100644 --- a/Editor/Mono/Animation/AnimationWindow/IAnimationWindowControl.cs +++ b/Editor/Mono/Animation/AnimationWindow/IAnimationWindowControl.cs @@ -21,10 +21,6 @@ public virtual void OnEnable() public abstract void OnSelectionChanged(); - public void OnPlayModeStateChanged(PlayModeStateChange state) - { - } - public void Init(AnimationWindowState state) { m_State = state; diff --git a/Editor/Mono/Animation/AnimationWindow/IAnimationWindowController.cs b/Editor/Mono/Animation/AnimationWindow/IAnimationWindowController.cs index 730865cd50..5859e47c2e 100644 --- a/Editor/Mono/Animation/AnimationWindow/IAnimationWindowController.cs +++ b/Editor/Mono/Animation/AnimationWindow/IAnimationWindowController.cs @@ -13,8 +13,6 @@ interface IAnimationWindowController : IDisposable { void OnSelectionChanged(); - void OnPlayModeStateChanged(PlayModeStateChange state); - float time { get; set; } int frame { get; set; } diff --git a/Editor/Mono/Animation/AnimationWindow/IAnimationWindowResponder.cs b/Editor/Mono/Animation/AnimationWindow/IAnimationWindowResponder.cs index e5d8cd0184..4261bfca87 100644 --- a/Editor/Mono/Animation/AnimationWindow/IAnimationWindowResponder.cs +++ b/Editor/Mono/Animation/AnimationWindow/IAnimationWindowResponder.cs @@ -2,6 +2,8 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using UnityEngine.Bindings; + namespace UnityEditor { /// @@ -9,6 +11,7 @@ namespace UnityEditor /// This allow any custom component to provide its own [IAnimationWindowSelectionItem] to the AnimationWindow /// and control how animation is authored and evaluated. /// + [VisibleToOtherModules("UnityEditor.UIToolkitAuthoringModule")] interface IAnimationWindowResponder { bool OnSelectionChange(AnimationWindow window, UnityEngine.Object selectedObject, out IAnimationWindowSelectionItem newSelection); diff --git a/Editor/Mono/Animation/AnimationWindow/IAnimationWindowSelectionItem.cs b/Editor/Mono/Animation/AnimationWindow/IAnimationWindowSelectionItem.cs index 78c39215f6..305aac5f23 100644 --- a/Editor/Mono/Animation/AnimationWindow/IAnimationWindowSelectionItem.cs +++ b/Editor/Mono/Animation/AnimationWindow/IAnimationWindowSelectionItem.cs @@ -4,6 +4,7 @@ using System; using UnityEngine; +using UnityEngine.Bindings; namespace UnityEditor { @@ -12,6 +13,7 @@ namespace UnityEditor /// This allows to control how clips are created and managed in the AnimationWindow. /// Also, this gives the ability to customize how animation is authored. /// + [VisibleToOtherModules("UnityEditor.UIToolkitAuthoringModule")] interface IAnimationWindowSelectionItem : ISelectionBinding, IDisposable { GameObject gameObject { get; } @@ -34,7 +36,6 @@ interface IAnimationWindowSelectionItem : ISelectionBinding, IDisposable IAnimationWindowClip[] GetClips(); IAnimationWindowClip CreateNewClip(); bool InitializeSelection(); - void OnPlayModeStateChanged(PlayModeStateChange state); bool IsCompatibleWith(UnityEngine.Object selectedObject); EditorCurveBinding[] GetAnimatableBindings(GameObject gameObject); diff --git a/Editor/Mono/Animation/AnimationWindow/RotationCurveInterpolation.cs b/Editor/Mono/Animation/AnimationWindow/RotationCurveInterpolation.cs index ece0fffa22..26f6527822 100644 --- a/Editor/Mono/Animation/AnimationWindow/RotationCurveInterpolation.cs +++ b/Editor/Mono/Animation/AnimationWindow/RotationCurveInterpolation.cs @@ -3,11 +3,13 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; using UnityEditorInternal; namespace UnityEditor { - partial class RotationCurveInterpolation + static partial class RotationCurveInterpolation { public enum Mode { Baked, NonBaked, RawQuaternions, RawEuler, Undefined } @@ -42,27 +44,38 @@ public static string GetPrefixForInterpolation(Mode newInterpolationMode) return null; } + static List s_BindingsCache; + const string s_PropertyWithSuffixRegex = @"(?\.[xyz])$"; internal static EditorCurveBinding[] ConvertRotationPropertiesToInterpolationType(ReadOnlySpan selection, Mode newInterpolationMode) { - if (selection.Length != 4) - return selection.ToArray(); + s_BindingsCache ??= new List(4); + if (s_BindingsCache.Capacity < selection.Length) + s_BindingsCache.Capacity = selection.Length; - if (GetModeFromCurveData(selection[0]) == Mode.RawQuaternions) + s_BindingsCache.Clear(); + for (int i = 0; i < selection.Length; ++i) { - EditorCurveBinding[] newCurves = new EditorCurveBinding[3]; - newCurves[0] = selection[0]; - newCurves[1] = selection[1]; - newCurves[2] = selection[2]; + if (GetModeFromCurveData(selection[i]) == Mode.RawQuaternions) + { + // Process x, y, z rotation bindings. Drop w channel. + var match = Regex.Match(selection[i].propertyName, s_PropertyWithSuffixRegex); + if (match.Success) + { + string prefix = GetPrefixForInterpolation(newInterpolationMode); - string prefix = GetPrefixForInterpolation(newInterpolationMode); - newCurves[0].propertyName = prefix + ".x"; - newCurves[1].propertyName = prefix + ".y"; - newCurves[2].propertyName = prefix + ".z"; + var newBinding = selection[i]; + newBinding.propertyName = prefix + match.Groups["suffix"]; - return newCurves; + s_BindingsCache.Add(newBinding); + } + } + else + { + s_BindingsCache.Add(selection[i]); + } } - else - return selection.ToArray(); + + return s_BindingsCache.ToArray(); } } } diff --git a/Editor/Mono/Animation/AnimationWindow/TimelineCompatibilityFunctions.cs b/Editor/Mono/Animation/AnimationWindow/TimelineCompatibilityFunctions.cs index 68ec18ad67..ccdb8568df 100644 --- a/Editor/Mono/Animation/AnimationWindow/TimelineCompatibilityFunctions.cs +++ b/Editor/Mono/Animation/AnimationWindow/TimelineCompatibilityFunctions.cs @@ -10,7 +10,7 @@ // Compatibility functions to support Timeline's use of the AnimationWindow internal API namespace UnityEditor { - partial class RotationCurveInterpolation + static partial class RotationCurveInterpolation { public static EditorCurveBinding RemapAnimationBindingForRotationCurves(EditorCurveBinding curveBinding, AnimationClip clip) => AnimationWindowBuiltin.RotationCurveInterpolation.RemapAnimationBindingForRotationCurves(curveBinding, clip); diff --git a/Editor/Mono/Animation/AnimationWindow/Widgets/ClipDropdownField.cs b/Editor/Mono/Animation/AnimationWindow/Widgets/ClipDropdownField.cs index d8ae89a452..9a8267c7f4 100644 --- a/Editor/Mono/Animation/AnimationWindow/Widgets/ClipDropdownField.cs +++ b/Editor/Mono/Animation/AnimationWindow/Widgets/ClipDropdownField.cs @@ -55,14 +55,18 @@ internal override void AddMenuItems(AbstractGenericMenu menu) choices = GetOrderedClipList(); foreach (var menuItem in choices) { - var isSelected = (menuItem == value) && !showMixedValue; + var isSelected = menuItem.Equals(value) && !showMixedValue; menu.AddItem( GetListItemToDisplay(menuItem), isSelected, () => ChangeValueFromMenu(menuItem)); } - menu.AddSeparator(String.Empty); - menu.AddItem(s_CreateNewClip, false, CreateNewClipFromMenu); + + if (m_State.selection.canChangeClip) + { + menu.AddSeparator(String.Empty); + menu.AddItem(s_CreateNewClip, false, CreateNewClipFromMenu); + } } new string GetListItemToDisplay(IAnimationWindowClip clip) => diff --git a/Editor/Mono/Animation/AnimationWindow/Widgets/Layout.cs b/Editor/Mono/Animation/AnimationWindow/Widgets/Layout.cs index 578cf5ae6b..918fe91192 100644 --- a/Editor/Mono/Animation/AnimationWindow/Widgets/Layout.cs +++ b/Editor/Mono/Animation/AnimationWindow/Widgets/Layout.cs @@ -13,6 +13,7 @@ using UnityEditor.Animations.AnimationWindow.TimelineFoundation; using UnityEditor.AnimationWindowBuiltin; using UnityEditor.Experimental; +using UnityEditor.ShortcutManagement; using UnityEditor.UIElements; using UnityEngine.Playables; @@ -27,6 +28,8 @@ class Layout : VisualElement, IDisposable const string k_AnimationPropertyHeader = "animation-propertyHeader"; const string k_AnimationControls = "animation-controls"; const string k_AnimationTimeArea = "animation-timeArea"; + const string k_AnimationTimeAreaLeftOverlap = "animation-timeArea-leftOverlap"; + const string k_AnimationTimeAreaRightOverlap = "animation-timeArea-rightOverlap"; const string k_AnimationContentOverlay = "animation-contentsOverlay"; @@ -41,6 +44,7 @@ class Layout : VisualElement, IDisposable const string k_AnimationAddPropertyButton = "animation-addPropertyButton"; const string k_AnimationAddKeyframeButton = "animation-addKeyframeButton"; const string k_AnimationAddEventButton = "animation-addEventButton"; + const string k_AnimationModeRippleToggle = "animation-modeRippleToggle"; const string k_AnimationFilterBySelectionToggle = "animation-filterBySelectionToggle"; const string k_AnimationApplyButton = "animation-applyButton"; @@ -66,10 +70,11 @@ class Layout : VisualElement, IDisposable static string s_RevertContentTooltip = L10n.Tr("Discard changes made to imported animation."); static string s_ApplyContentTooltip = L10n.Tr("Apply changes made to imported animation."); - static string s_AddKeyframeContentTooltip = L10n.Tr("Add keyframe."); + static string s_AddKeyframeContentTooltip = L10n.Tr("Add keyframe ({0})."); static string s_AddEventContentTooltip = L10n.Tr("Add event."); static string s_FilterBySelectionContentTooltip = L10n.Tr("Filter by selection."); static string s_SequencerLinkContentTooltip = L10n.Tr("Animation Window is linked to Timeline Editor. Press to Unlink."); + static string s_ModeRippleContentTooltip = L10n.Tr("Ripple mode ({0})."); const float k_LeftMargin = 40f; const float k_RightMargin = 40f; @@ -77,11 +82,13 @@ class Layout : VisualElement, IDisposable class DopesheetButton : IToggleButtonItem { public string Name => L10n.Tr("Dopesheet"); + public string Tooltip => L10n.Tr($"Show Dopesheet ({ShortcutManager.instance.GetShortcutBinding("Animation/Show Curves")})"); } class CurveEditorButton : IToggleButtonItem { public string Name => L10n.Tr("Curves"); + public string Tooltip => L10n.Tr($"Show Curves ({ShortcutManager.instance.GetShortcutBinding("Animation/Show Curves")})"); } AnimEditor m_AnimEditor; @@ -97,6 +104,8 @@ class CurveEditorButton : IToggleButtonItem HierarchyElement m_HierarchyElement; AnimationEventTimelineElement m_AnimationEventTimeline; TimelineFoundation.TimeArea m_TimeArea; + VisualElement m_TimeAreaLeftOverlap; + VisualElement m_TimeAreaRightOverlap; VisualElement m_OnboardingPanel; Label m_OnboardingPanelLabel; @@ -107,6 +116,7 @@ class CurveEditorButton : IToggleButtonItem Button m_AddPropertyButton; Button m_AddKeyframeButton; Button m_AddEventButton; + ToolbarToggle m_ModeRippleToggle; ToolbarToggle m_FilterBySelectionToggle; ToggleButtonStrip m_ContentSwitcherButtons; @@ -128,9 +138,7 @@ class CurveEditorButton : IToggleButtonItem HeaderResizeManipulator m_HeaderResizeManipulator; PlayHeadOverlay m_PlayHeadOverlay; - PlayHeadOverlay m_DurationOverlay; TimeDragManipulator m_PlayHeadDragManipulator; - TimeDragManipulator m_DurationDragManipulator; TimeDragManipulator m_TimeAreaDragManipulator; EventsOverlay m_EventsOverlay; @@ -184,14 +192,6 @@ void InitPlayHead() m_Canvas = new CanvasManager(m_CanvasOverlayManager, state); m_CanvasOverlayManager.canvas = m_Canvas; - m_DurationOverlay = new PlayHeadOverlay(); - m_DurationOverlay.name = "preview-duration-overlay"; - m_CanvasOverlayManager.AddOverlay(m_DurationOverlay); - - m_DurationDragManipulator = new TimeDragManipulator(m_Canvas); - SetupTimeDragManipulator(m_DurationDragManipulator); - m_DurationOverlay.AddManipulator(m_DurationDragManipulator); - m_PlayHeadOverlay = new PlayHeadOverlay(PickingMode.Position); m_CanvasOverlayManager.AddOverlay(m_PlayHeadOverlay); @@ -241,6 +241,8 @@ void InitAnimationContent() m_AnimationControls = this.Q(className: k_AnimationControls); m_OnboardingPanel = this.Q(className: k_AnimationOnboarding); + m_OnboardingPanel.EnableInClassList(k_AnimationOnboarding + "__hidden", true); + m_OnboardingPanelLabel = m_OnboardingPanel.Q