diff options
Diffstat (limited to 'Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview')
78 files changed, 7473 insertions, 0 deletions
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/AnimationTrackKeyDataSource.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/AnimationTrackKeyDataSource.cs new file mode 100644 index 0000000..38c2f81 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/AnimationTrackKeyDataSource.cs @@ -0,0 +1,25 @@ +using UnityEngine; +using UnityEngine.Timeline; + +// Data sources for key overlays +namespace UnityEditor.Timeline +{ + // Used for key overlays manipulators + class AnimationTrackKeyDataSource : BasePropertyKeyDataSource + { + readonly float m_TrackOffset; + + protected override AnimationClip animationClip { get; } + + public AnimationTrackKeyDataSource(AnimationTrack track) + { + animationClip = track != null ? track.infiniteClip : null; + m_TrackOffset = track != null ? (float)track.infiniteClipTimeOffset : 0.0f; + } + + protected override float TransformKeyTime(float keyTime) + { + return keyTime + m_TrackOffset; + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/AnimationTrackKeyDataSource.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/AnimationTrackKeyDataSource.cs.meta new file mode 100644 index 0000000..3c81758 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/AnimationTrackKeyDataSource.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a0b56195e00682b4594dfaeef9d5fa78 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Control.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Control.cs new file mode 100644 index 0000000..8788058 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Control.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +namespace UnityEditor.Timeline +{ + class Control + { + readonly List<Manipulator> m_Manipulators = new List<Manipulator>(); + + public bool HandleManipulatorsEvents(WindowState state) + { + var isHandled = false; + + foreach (var manipulator in m_Manipulators) + { + isHandled = manipulator.HandleEvent(state); + if (isHandled) + break; + } + + return isHandled; + } + + public void AddManipulator(Manipulator m) + { + m_Manipulators.Add(m); + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Control.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Control.cs.meta new file mode 100644 index 0000000..71c8d33 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Control.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 077690d334440b044bdd51b26b3e9413 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers.meta new file mode 100644 index 0000000..a7ad23a --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d40a0edbdcdcf9747a420f3bbe0f18db +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/AnimationTrackDrawer.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/AnimationTrackDrawer.cs new file mode 100644 index 0000000..0e9ef89 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/AnimationTrackDrawer.cs @@ -0,0 +1,86 @@ +using System; +using System.ComponentModel; +using System.Linq; +using JetBrains.Annotations; +using UnityEngine; +using UnityEditor; +using UnityEngine.Timeline; + +namespace UnityEditor.Timeline +{ + [CustomTrackDrawer(typeof(AnimationTrack)), UsedImplicitly] + class AnimationTrackDrawer : TrackDrawer + { + internal static class Styles + { + public static readonly GUIContent AnimationButtonOnTooltip = EditorGUIUtility.TrTextContent("", "Avatar Mask enabled\nClick to disable"); + public static readonly GUIContent AnimationButtonOffTooltip = EditorGUIUtility.TrTextContent("", "Avatar Mask disabled\nClick to enable"); + } + + public override bool DrawTrackHeaderButton(Rect rect, TrackAsset track, WindowState state) + { + var animTrack = track as AnimationTrack; + bool hasAvatarMask = animTrack != null && animTrack.avatarMask != null; + if (hasAvatarMask) + { + var style = animTrack.applyAvatarMask + ? DirectorStyles.Instance.avatarMaskOn + : DirectorStyles.Instance.avatarMaskOff; + var tooltip = animTrack.applyAvatarMask + ? Styles.AnimationButtonOnTooltip + : Styles.AnimationButtonOffTooltip; + if (GUI.Button(rect, tooltip, style)) + { + animTrack.applyAvatarMask = !animTrack.applyAvatarMask; + if (state != null) + state.rebuildGraph = true; + } + } + return hasAvatarMask; + } + + public override void DrawRecordingBackground(Rect trackRect, TrackAsset trackAsset, Vector2 visibleTime, WindowState state) + { + base.DrawRecordingBackground(trackRect, trackAsset, visibleTime, state); + DrawBorderOfAddedRecordingClip(trackRect, trackAsset, visibleTime, (WindowState)state); + } + + static void DrawBorderOfAddedRecordingClip(Rect trackRect, TrackAsset trackAsset, Vector2 visibleTime, WindowState state) + { + if (!state.IsArmedForRecord(trackAsset)) + return; + + AnimationTrack animTrack = trackAsset as AnimationTrack; + if (animTrack == null || !animTrack.inClipMode) + return; + + // make sure there is no clip but we can add one + TimelineClip clip = null; + if (trackAsset.FindRecordingClipAtTime(state.editSequence.time, out clip) || clip != null) + return; + + float yMax = trackRect.yMax; + float yMin = trackRect.yMin; + + double startGap = 0; + double endGap = 0; + + trackAsset.GetGapAtTime(state.editSequence.time, out startGap, out endGap); + if (double.IsInfinity(endGap)) + endGap = visibleTime.y; + + if (startGap > visibleTime.y || endGap < visibleTime.x) + return; + + + startGap = Math.Max(startGap, visibleTime.x); + endGap = Math.Min(endGap, visibleTime.y); + + float xMin = state.TimeToPixel(startGap); + float xMax = state.TimeToPixel(endGap); + + var r = Rect.MinMaxRect(xMin, yMin, xMax, yMax); + ClipDrawer.DrawClipSelectionBorder(r, ClipBorder.Recording(), ClipBlends.kNone); + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/AnimationTrackDrawer.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/AnimationTrackDrawer.cs.meta new file mode 100644 index 0000000..20fed76 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/AnimationTrackDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cb281723220c9964094e6c52e0ece792 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/ClipDrawer.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/ClipDrawer.cs new file mode 100644 index 0000000..266e465 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/ClipDrawer.cs @@ -0,0 +1,678 @@ +using System; +using UnityEngine.Timeline; +using UnityEngine; +using System.Linq; +using System.Collections.Generic; + +namespace UnityEditor.Timeline +{ + enum BlendKind + { + None, + Ease, + Mix + } + + enum BlendAngle + { + Descending, + Ascending + } + + struct IconData + { + public enum Side { Left = -1, Right = 1 } + + public Texture2D icon; + public Color tint; + + public float width { get { return 16; } } + public float height { get { return 16; } } + + public IconData(Texture2D icon) + { + this.icon = icon; + tint = Color.white; + } + } + + class ClipBorder + { + public readonly Color color; + public readonly float thickness; + + ClipBorder(Color color, float thickness) + { + this.color = color; + this.thickness = thickness; + } + + const float k_ClipSelectionBorder = 1.0f; + const float k_ClipRecordingBorder = 2.0f; + + public static ClipBorder Recording() + { + return new ClipBorder(DirectorStyles.Instance.customSkin.colorRecordingClipOutline, k_ClipRecordingBorder); + } + + public static ClipBorder Selection() + { + return new ClipBorder(Color.white, k_ClipSelectionBorder); + } + + public static ClipBorder Default() + { + return new ClipBorder(DirectorStyles.Instance.customSkin.clipBorderColor, k_ClipSelectionBorder); + } + } + + struct ClipBlends + { + public readonly BlendKind inKind; + public readonly Rect inRect; + + public readonly BlendKind outKind; + public readonly Rect outRect; + + public ClipBlends(BlendKind inKind, Rect inRect, BlendKind outKind, Rect outRect) + { + this.inKind = inKind; + this.inRect = inRect; + this.outKind = outKind; + this.outRect = outRect; + } + + public static readonly ClipBlends kNone = new ClipBlends(BlendKind.None, Rect.zero, BlendKind.None, Rect.zero); + } + + struct ClipDrawData + { + public TimelineClip clip; // clip being drawn + public Rect targetRect; // rectangle to draw to + public Rect unclippedRect; // the clip's unclipped rect + public Rect clippedRect; // the clip's clipped rect to the visible time area + public Rect clipCenterSection; // clip center section + public string title; // clip title + public bool selected; // is the clip selected + public bool inlineCurvesSelected; // is the inline curve of the clip selected + public double localVisibleStartTime; + public double localVisibleEndTime; + public IconData[] leftIcons; + public IconData[] rightIcons; + public TimelineClip previousClip; + public bool previousClipSelected; + public bool supportsLooping; + public int minLoopIndex; + public List<Rect> loopRects; + public ClipBlends clipBlends; + public ClipDrawOptions ClipDrawOptions; + public ClipEditor clipEditor; + } + + static class ClipDrawer + { + public static class Styles + { + public static readonly Texture2D iconWarn = EditorGUIUtility.LoadIconRequired("console.warnicon.inactive.sml"); + public static readonly string HoldText = LocalizationDatabase.GetLocalizedString("HOLD"); + public static readonly Texture2D s_IconNoRecord = EditorGUIUtility.LoadIcon("console.erroricon.sml"); + public static readonly GUIContent s_ClipNotRecorable = EditorGUIUtility.TrTextContent("", "This clip is not recordable"); + public static readonly GUIContent s_ClipNoRecordInBlend = EditorGUIUtility.TrTextContent("", "Recording in blends in prohibited"); + } + + const float k_ClipSwatchLineThickness = 4.0f; + const float k_MinClipWidth = 7.0f; + const float k_ClipInOutMinWidth = 15.0f; + const float k_ClipLoopsMinWidth = 20.0f; + const float k_ClipLabelPadding = 6.0f; + const float k_ClipLabelMinWidth = 10.0f; + const float k_IconsPadding = 1.0f; + const float k_ClipInlineWidth = 1.0f; + + static readonly GUIContent s_TitleContent = new GUIContent(); + static readonly IconData[] k_ClipErrorIcons = { new IconData {icon = Styles.iconWarn, tint = DirectorStyles.kClipErrorColor} }; + static readonly Dictionary<int, string> s_LoopStringCache = new Dictionary<int, string>(100); + + // caches the loopstring to avoid allocation from string concats + static string GetLoopString(int loopIndex) + { + string loopString = null; + if (!s_LoopStringCache.TryGetValue(loopIndex, out loopString)) + { + loopString = "L" + loopIndex; + s_LoopStringCache[loopIndex] = loopString; + } + return loopString; + } + + static void DrawLoops(ClipDrawData drawData) + { + if (drawData.loopRects == null || drawData.loopRects.Count == 0) + return; + + var oldColor = GUI.color; + + int loopIndex = drawData.minLoopIndex; + for (int l = 0; l < drawData.loopRects.Count; l++) + { + Rect theRect = drawData.loopRects[l]; + theRect.x -= drawData.unclippedRect.x; + theRect.x += 1; + theRect.width -= 2.0f; + theRect.y = 5.0f; + theRect.height -= 4.0f; + theRect.xMin -= 4f; + + if (theRect.width >= 5f) + { + using (new GUIViewportScope(drawData.clipCenterSection)) + { + GUI.color = new Color(0.0f, 0.0f, 0.0f, 0.2f); + GUI.Label(theRect, GUIContent.none, DirectorStyles.Instance.displayBackground); + + if (theRect.width > 36.0f) + { + var style = DirectorStyles.Instance.fontClipLoop; + GUI.color = new Color(0.0f, 0.0f, 0.0f, 0.3f); + var loopContent = new GUIContent(drawData.supportsLooping ? GetLoopString(loopIndex) : Styles.HoldText); + GUI.Label(theRect, loopContent, style); + } + } + } + + loopIndex++; + + if (!drawData.supportsLooping) + break; + } + + GUI.color = oldColor; + } + + static void DrawClipBorder(ClipDrawData drawData) + { + var animTrack = drawData.clip.parentTrack as AnimationTrack; + var selectionBorder = ClipBorder.Selection(); + + if (TimelineWindow.instance.state.recording && animTrack == null && drawData.clip.parentTrack.IsRecordingToClip(drawData.clip)) + { + DrawClipSelectionBorder(drawData.clipCenterSection, selectionBorder, drawData.clipBlends); + return; + } + + DrawClipDefaultBorder(drawData.clipCenterSection, ClipBorder.Default(), drawData.clipBlends); + + if (drawData.selected) + DrawClipSelectionBorder(drawData.clipCenterSection, selectionBorder, drawData.clipBlends); + + if (drawData.previousClip != null && drawData.previousClipSelected) + DrawClipBlendSelectionBorder(drawData.clipCenterSection, selectionBorder, drawData.clipBlends); + } + + public static void DrawClipSelectionBorder(Rect clipRect, ClipBorder border, ClipBlends blends) + { + var thickness = border.thickness; + var color = border.color; + var min = clipRect.min; + var max = clipRect.max; + + //Left line + if (blends.inKind == BlendKind.None) + EditorGUI.DrawRect(new Rect(min.x, min.y, thickness, max.y - min.y), color); + else + DrawBlendLine(blends.inRect, blends.inKind == BlendKind.Mix ? BlendAngle.Descending : BlendAngle.Ascending, thickness, color); + + //Right line + if (blends.outKind == BlendKind.None) + EditorGUI.DrawRect(new Rect(max.x - thickness, min.y, thickness, max.y - min.y), color); + else + DrawBlendLine(blends.outRect, BlendAngle.Descending, thickness, color); + + //Top line + var xTop1 = blends.inKind == BlendKind.Mix ? blends.inRect.xMin : min.x; + var xTop2 = max.x; + EditorGUI.DrawRect(new Rect(xTop1, min.y, xTop2 - xTop1, thickness), color); + + //Bottom line + var xBottom1 = blends.inKind == BlendKind.Ease ? blends.inRect.xMin : min.x; + var xBottom2 = blends.outKind == BlendKind.None ? max.x : blends.outRect.xMax; + EditorGUI.DrawRect(new Rect(xBottom1, max.y - thickness, xBottom2 - xBottom1, thickness), color); + } + + static Vector3[] s_BlendLines = new Vector3[4]; + static void DrawBlendLine(Rect rect, BlendAngle blendAngle, float width, Color color) + { + var halfWidth = width / 2.0f; + Vector2 p0, p1; + var inverse = 1.0f; + if (blendAngle == BlendAngle.Descending) + { + p0 = rect.min; + p1 = rect.max; + } + else + { + p0 = new Vector2(rect.xMax, rect.yMin); + p1 = new Vector2(rect.xMin, rect.yMax); + inverse = -1.0f; + } + s_BlendLines[0] = new Vector3(p0.x - halfWidth, p0.y + halfWidth * inverse); + s_BlendLines[1] = new Vector3(p1.x - halfWidth, p1.y + halfWidth * inverse); + s_BlendLines[2] = new Vector3(p1.x + halfWidth, p1.y - halfWidth * inverse); + s_BlendLines[3] = new Vector3(p0.x + halfWidth, p0.y - halfWidth * inverse); + Graphics.DrawPolygonAA(color, s_BlendLines); + } + + static void DrawClipBlendSelectionBorder(Rect clipRect, ClipBorder border, ClipBlends blends) + { + var color = border.color; + var thickness = border.thickness; + if (blends.inKind == BlendKind.Mix) + { + DrawBlendLine(blends.inRect, BlendAngle.Descending, thickness, color); + var xBottom1 = blends.inRect.xMin; + var xBottom2 = blends.inRect.xMax; + EditorGUI.DrawRect(new Rect(xBottom1, clipRect.max.y - thickness, xBottom2 - xBottom1, thickness), color); + } + } + + static void DrawClipDefaultBorder(Rect clipRect, ClipBorder border, ClipBlends blends) + { + var color = border.color; + var thickness = border.thickness; + + // Draw vertical lines at the edges of the clip + EditorGUI.DrawRect(new Rect(clipRect.xMin, clipRect.y, thickness, clipRect.height), color); //left + //only draw the right one when no out mix blend + if (blends.outKind != BlendKind.Mix) + EditorGUI.DrawRect(new Rect(clipRect.xMax - thickness, clipRect.y, thickness, clipRect.height), color); //right + //draw a vertical line for the previous clip + if (blends.inKind == BlendKind.Mix) + EditorGUI.DrawRect(new Rect(blends.inRect.xMin, blends.inRect.y, thickness, blends.inRect.height), color); //left + + //Draw blend line + if (blends.inKind == BlendKind.Mix) + DrawBlendLine(blends.inRect, BlendAngle.Descending, thickness, color); + } + + static void DrawClipTimescale(Rect targetRect, Rect clippedRect, double timeScale) + { + if (timeScale != 1.0) + { + const float xOffset = 4.0f; + const float yOffset = 6.0f; + var segmentLength = timeScale > 1.0f ? 5.0f : 15.0f; + + // clamp to the visible region to reduce the line count (case 1213189), but adject the start segment to match the visuals of drawing targetRect + var startX = clippedRect.min.x - ((clippedRect.min.x - targetRect.min.x) % (segmentLength*2)); + var endX = clippedRect.max.x; + + var start = new Vector3(startX + xOffset, targetRect.min.y + yOffset, 0.0f); + var end = new Vector3(endX - xOffset, targetRect.min.y + yOffset, 0.0f); + + Graphics.DrawDottedLine(start, end, segmentLength, DirectorStyles.Instance.customSkin.colorClipFont); + Graphics.DrawDottedLine(start + new Vector3(0.0f, 1.0f, 0.0f), end + new Vector3(0.0f, 1.0f, 0.0f), segmentLength, DirectorStyles.Instance.customSkin.colorClipFont); + } + } + + static void DrawClipInOut(Rect targetRect, TimelineClip clip) + { + var assetDuration = TimelineHelpers.GetClipAssetEndTime(clip); + + bool drawClipOut = assetDuration<double.MaxValue && + assetDuration - clip.end> TimeUtility.kTimeEpsilon; + + bool drawClipIn = clip.clipIn > 0.0; + + if (!drawClipIn && !drawClipOut) + return; + + var rect = targetRect; + + if (drawClipOut) + { + var icon = DirectorStyles.Instance.clipOut; + var iconRect = new Rect(rect.xMax - icon.fixedWidth - 2.0f, + rect.yMin + (rect.height - icon.fixedHeight) * 0.5f, + icon.fixedWidth, icon.fixedHeight); + + GUI.Label(iconRect, GUIContent.none, icon); + } + + if (drawClipIn) + { + var icon = DirectorStyles.Instance.clipIn; + var iconRect = new Rect(2.0f + rect.xMin, + rect.yMin + (rect.height - icon.fixedHeight) * 0.5f, + icon.fixedWidth, icon.fixedHeight); + + GUI.Label(iconRect, GUIContent.none, icon); + } + } + + static void DrawClipLabel(ClipDrawData data, Rect availableRect, Color color) + { + var errorText = data.ClipDrawOptions.errorText; + var hasError = !string.IsNullOrEmpty(errorText); + var textColor = hasError ? DirectorStyles.kClipErrorColor : color; + var tooltip = hasError ? errorText : data.ClipDrawOptions.tooltip; + + if (hasError) + DrawClipLabel(data.title, availableRect, textColor, k_ClipErrorIcons, null, tooltip); + else + DrawClipLabel(data.title, availableRect, textColor, data.leftIcons, data.rightIcons, tooltip); + } + + static void DrawClipLabel(string title, Rect availableRect, Color color, string errorText = "") + { + var hasError = !string.IsNullOrEmpty(errorText); + var textColor = hasError ? DirectorStyles.kClipErrorColor : color; + + if (hasError) + DrawClipLabel(title, availableRect, textColor, k_ClipErrorIcons, null, errorText); + else + DrawClipLabel(title, availableRect, textColor, null, null, errorText); + } + + static void DrawClipLabel(string title, Rect availableRect, Color textColor, IconData[] leftIcons, IconData[] rightIcons, string tooltipMessage = "") + { + s_TitleContent.text = title; + var neededTextWidth = DirectorStyles.Instance.fontClip.CalcSize(s_TitleContent).x; + var neededIconWidthLeft = 0.0f; + var neededIconWidthRight = 0.0f; + + if (leftIcons != null) + for (int i = 0, n = leftIcons.Length; i < n; ++i) + neededIconWidthLeft += leftIcons[i].width + k_IconsPadding; + + if (rightIcons != null) + for (int i = 0, n = rightIcons.Length; i < n; ++i) + neededIconWidthRight += rightIcons[i].width + k_IconsPadding; + + var neededIconWidth = Mathf.Max(neededIconWidthLeft, neededIconWidthRight); + + // Atomic operation: We either show all icons or no icons at all + var showIcons = neededTextWidth / 2.0f + neededIconWidth < availableRect.width / 2.0f; + + if (showIcons) + { + if (leftIcons != null) + DrawClipIcons(leftIcons, IconData.Side.Left, neededTextWidth, availableRect); + + if (rightIcons != null) + DrawClipIcons(rightIcons, IconData.Side.Right, neededTextWidth, availableRect); + } + + if (neededTextWidth > availableRect.width) + s_TitleContent.text = DirectorStyles.Elipsify(title, availableRect.width, neededTextWidth); + + s_TitleContent.tooltip = tooltipMessage; + DrawClipName(availableRect, s_TitleContent, textColor); + } + + static void DrawClipIcons(IconData[] icons, IconData.Side side, float labelWidth, Rect availableRect) + { + var halfText = labelWidth / 2.0f; + var offset = halfText + k_IconsPadding; + + foreach (var iconData in icons) + { + offset += iconData.width / 2.0f + k_IconsPadding; + + var iconRect = + new Rect(0.0f, 0.0f, iconData.width, iconData.height) + { + center = new Vector2(availableRect.center.x + offset * (int)side, availableRect.center.y) + }; + + DrawIcon(iconRect, iconData.tint, iconData.icon); + + offset += iconData.width / 2.0f; + } + } + + static void DrawClipName(Rect rect, GUIContent content, Color textColor) + { + Graphics.ShadowLabel(rect, content, DirectorStyles.Instance.fontClip, textColor, Color.black); + } + + static void DrawIcon(Rect imageRect, Color color, Texture2D icon) + { + GUI.DrawTexture(imageRect, icon, ScaleMode.ScaleAndCrop, true, 0, color, 0, 0); + } + + static void DrawClipBackground(Rect clipCenterSection, bool selected) + { + if (Event.current.type != EventType.Repaint) + return; + + var color = selected ? DirectorStyles.Instance.customSkin.clipSelectedBckg : DirectorStyles.Instance.customSkin.clipBckg; + EditorGUI.DrawRect(clipCenterSection, color); + } + + static Vector3[] s_BlendVertices = new Vector3[3]; + static void DrawClipBlends(ClipBlends blends, Color inColor, Color outColor, Color backgroundColor) + { + switch (blends.inKind) + { + case BlendKind.Ease: + // 2 + // / | + // / | + // 0---1 + EditorGUI.DrawRect(blends.inRect, backgroundColor); + s_BlendVertices[0] = new Vector3(blends.inRect.xMin, blends.inRect.yMax); + s_BlendVertices[1] = new Vector3(blends.inRect.xMax, blends.inRect.yMax); + s_BlendVertices[2] = new Vector3(blends.inRect.xMax, blends.inRect.yMin); + Graphics.DrawPolygonAA(inColor, s_BlendVertices); + break; + case BlendKind.Mix: + // 0---2 + // \ | + // \ | + // 1 + s_BlendVertices[0] = new Vector3(blends.inRect.xMin, blends.inRect.yMin); + s_BlendVertices[1] = new Vector3(blends.inRect.xMax, blends.inRect.yMax); + s_BlendVertices[2] = new Vector3(blends.inRect.xMax, blends.inRect.yMin); + Graphics.DrawPolygonAA(inColor, s_BlendVertices); + break; + } + + if (blends.outKind != BlendKind.None) + { + if (blends.outKind == BlendKind.Ease) + EditorGUI.DrawRect(blends.outRect, backgroundColor); + // 0 + // | \ + // | \ + // 1---2 + s_BlendVertices[0] = new Vector3(blends.outRect.xMin, blends.outRect.yMin); + s_BlendVertices[1] = new Vector3(blends.outRect.xMin, blends.outRect.yMax); + s_BlendVertices[2] = new Vector3(blends.outRect.xMax, blends.outRect.yMax); + Graphics.DrawPolygonAA(outColor, s_BlendVertices); + } + } + + static void DrawClipSwatch(Rect targetRect, Color swatchColor) + { + // Draw Colored Line at the bottom. + var colorRect = targetRect; + colorRect.yMin = colorRect.yMax - k_ClipSwatchLineThickness; + EditorGUI.DrawRect(colorRect, swatchColor); + } + + public static void DrawSimpleClip(TimelineClip clip, Rect targetRect, ClipBorder border, Color overlay, ClipDrawOptions drawOptions) + { + GUI.BeginClip(targetRect); + var clipRect = new Rect(0.0f, 0.0f, targetRect.width, targetRect.height); + + var orgColor = GUI.color; + GUI.color = overlay; + + DrawClipBackground(clipRect, false); + GUI.color = orgColor; + + if (clipRect.width <= k_MinClipWidth) + { + clipRect.width = k_MinClipWidth; + } + + DrawClipSwatch(targetRect, drawOptions.highlightColor * overlay); + + if (targetRect.width >= k_ClipInOutMinWidth) + DrawClipInOut(clipRect, clip); + + var textRect = clipRect; + + textRect.xMin += k_ClipLabelPadding; + textRect.xMax -= k_ClipLabelPadding; + + if (textRect.width > k_ClipLabelMinWidth) + DrawClipLabel(clip.displayName, textRect, Color.white, drawOptions.errorText); + + if (border != null) + DrawClipSelectionBorder(clipRect, border, ClipBlends.kNone); + + GUI.EndClip(); + } + + public static void DrawDefaultClip(ClipDrawData drawData) + { + var customSkin = DirectorStyles.Instance.customSkin; + var blendInColor = drawData.selected ? customSkin.clipBlendInSelected : customSkin.clipBlendIn; + var blendOutColor = drawData.selected ? customSkin.clipBlendOutSelected : customSkin.clipBlendOut; + var easeBackgroundColor = customSkin.clipEaseBckgColor; + + DrawClipBlends(drawData.clipBlends, blendInColor, blendOutColor, easeBackgroundColor); + DrawClipBackground(drawData.clipCenterSection, drawData.selected); + + if (drawData.targetRect.width > k_MinClipWidth) + { + DrawClipEditorBackground(drawData); + } + else + { + drawData.targetRect.width = k_MinClipWidth; + drawData.clipCenterSection.width = k_MinClipWidth; + } + + DrawClipTimescale(drawData.targetRect, drawData.clippedRect, drawData.clip.timeScale); + + if (drawData.targetRect.width >= k_ClipInOutMinWidth) + DrawClipInOut(drawData.targetRect, drawData.clip); + + var labelRect = drawData.clipCenterSection; + + if (drawData.targetRect.width >= k_ClipLoopsMinWidth) + { + bool selected = drawData.selected || drawData.inlineCurvesSelected; + + if (selected) + { + if (drawData.loopRects != null && drawData.loopRects.Any()) + { + DrawLoops(drawData); + + var l = drawData.loopRects[0]; + labelRect.xMax = Math.Min(labelRect.xMax, l.x - drawData.unclippedRect.x); + } + } + } + + labelRect.xMin += k_ClipLabelPadding; + labelRect.xMax -= k_ClipLabelPadding; + + if (labelRect.width > k_ClipLabelMinWidth) + { + DrawClipLabel(drawData, labelRect, Color.white); + } + + DrawClipSwatch(drawData.targetRect, drawData.ClipDrawOptions.highlightColor); + DrawClipBorder(drawData); + } + + static void DrawClipEditorBackground(ClipDrawData drawData) + { + var isRepaint = (Event.current.type == EventType.Repaint); + if (isRepaint && drawData.clipEditor != null) + { + var customBodyRect = drawData.clippedRect; + customBodyRect.yMin += k_ClipInlineWidth; + customBodyRect.yMax -= k_ClipSwatchLineThickness; + var region = new ClipBackgroundRegion(customBodyRect, drawData.localVisibleStartTime, drawData.localVisibleEndTime); + try + { + drawData.clipEditor.DrawBackground(drawData.clip, region); + } + catch (Exception e) + { + Debug.LogException(e); + } + } + } + + public static void DrawAnimationRecordBorder(ClipDrawData drawData) + { + if (!drawData.clip.parentTrack.IsRecordingToClip(drawData.clip)) + return; + + var time = new DiscreteTime(TimelineWindow.instance.state.editSequence.time); + var start = new DiscreteTime(drawData.clip.start + drawData.clip.mixInDuration); + var end = new DiscreteTime(drawData.clip.end - drawData.clip.mixOutDuration); + + if (time < start || time >= end) + return; + + DrawClipSelectionBorder(drawData.clipCenterSection, ClipBorder.Recording(), ClipBlends.kNone); + } + + public static void DrawRecordProhibited(ClipDrawData drawData) + { + DrawRecordInvalidClip(drawData); + DrawRecordOnBlend(drawData); + } + + public static void DrawRecordOnBlend(ClipDrawData drawData) + { + double time = TimelineWindow.instance.state.editSequence.time; + if (time >= drawData.clip.start && time < drawData.clip.start + drawData.clip.mixInDuration) + { + Rect r = Rect.MinMaxRect(drawData.clippedRect.xMin, drawData.clippedRect.yMin, drawData.clipCenterSection.xMin, drawData.clippedRect.yMax); + DrawInvalidRecordIcon(r, Styles.s_ClipNoRecordInBlend); + } + + if (time <= drawData.clip.end && time > drawData.clip.end - drawData.clip.mixOutDuration) + { + Rect r = Rect.MinMaxRect(drawData.clipCenterSection.xMax, drawData.clippedRect.yMin, drawData.clippedRect.xMax, drawData.clippedRect.yMax); + DrawInvalidRecordIcon(r, Styles.s_ClipNoRecordInBlend); + } + } + + public static void DrawRecordInvalidClip(ClipDrawData drawData) + { + if (drawData.clip.recordable) + return; + + double time = TimelineWindow.instance.state.editSequence.time; + if (time < drawData.clip.start + drawData.clip.mixInDuration || time > drawData.clip.end - drawData.clip.mixOutDuration) + return; + + DrawInvalidRecordIcon(drawData.clipCenterSection, Styles.s_ClipNotRecorable); + } + + public static void DrawInvalidRecordIcon(Rect rect, GUIContent helpText) + { + EditorGUI.DrawRect(rect, new Color(0, 0, 0, 0.30f)); + + var icon = Styles.s_IconNoRecord; + if (rect.width < icon.width || rect.height < icon.height) + return; + + float x = rect.x + (rect.width - icon.width) * 0.5f; + float y = rect.y + (rect.height - icon.height) * 0.5f; + Rect r = new Rect(x, y, icon.width, icon.height); + GUI.Label(r, helpText); + GUI.DrawTexture(r, icon, ScaleMode.ScaleAndCrop, true, 0, Color.white, 0, 0); + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/ClipDrawer.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/ClipDrawer.cs.meta new file mode 100644 index 0000000..e1493e6 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/ClipDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 63118a0c9ee42ac46b7f30e793177a76 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/InfiniteTrackDrawer.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/InfiniteTrackDrawer.cs new file mode 100644 index 0000000..b2c5d7f --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/InfiniteTrackDrawer.cs @@ -0,0 +1,86 @@ +using UnityEngine; +using UnityEngine.Timeline; + +namespace UnityEditor.Timeline +{ + class InfiniteTrackDrawer : TrackDrawer + { + readonly IPropertyKeyDataSource m_DataSource; + Rect m_TrackRect; + + public InfiniteTrackDrawer(IPropertyKeyDataSource dataSource) + { + m_DataSource = dataSource; + } + + public bool CanDraw(TrackAsset track, WindowState state) + { + var keys = m_DataSource.GetKeys(); + var isTrackEmpty = track.clips.Length == 0; + + return keys != null || (state.IsArmedForRecord(track) && isTrackEmpty); + } + + static void DrawRecordBackground(Rect trackRect) + { + var styles = DirectorStyles.Instance; + + EditorGUI.DrawRect(trackRect, styles.customSkin.colorInfiniteTrackBackgroundRecording); + + Graphics.ShadowLabel(trackRect, + DirectorStyles.Elipsify(DirectorStyles.recordingLabel.text, trackRect, styles.fontClip), + styles.fontClip, Color.white, Color.black); + } + + public override bool DrawTrack(Rect trackRect, TrackAsset trackAsset, Vector2 visibleTime, WindowState state) + { + m_TrackRect = trackRect; + + if (!CanDraw(trackAsset, state)) + return true; + + if (state.recording && state.IsArmedForRecord(trackAsset)) + DrawRecordBackground(trackRect); + + GUI.Box(trackRect, GUIContent.none, DirectorStyles.Instance.infiniteTrack); + + var shadowRect = trackRect; + shadowRect.yMin = shadowRect.yMax; + shadowRect.height = 15.0f; + if (Event.current.type == EventType.Repaint) + DirectorStyles.Instance.bottomShadow.Draw(shadowRect, false, false, false, false); + + var keys = m_DataSource.GetKeys(); + if (keys != null && keys.Length > 0) + { + foreach (var k in keys) + DrawKeyFrame(k, state); + } + + return true; + } + + void DrawKeyFrame(float key, WindowState state) + { + var x = state.TimeToPixel(key); + var bounds = new Rect(x, m_TrackRect.yMin + 3.0f, 1.0f, m_TrackRect.height - 6.0f); + + if (!m_TrackRect.Overlaps(bounds)) + return; + + var iconWidth = DirectorStyles.Instance.keyframe.fixedWidth; + var iconHeight = DirectorStyles.Instance.keyframe.fixedHeight; + + var keyframeRect = bounds; + keyframeRect.width = iconWidth; + keyframeRect.height = iconHeight; + keyframeRect.xMin -= iconWidth / 2.0f; + keyframeRect.yMin = m_TrackRect.yMin + ((m_TrackRect.height - iconHeight) / 2.0f); + + // case 890650 : Make sure to use GUI.Label and not GUI.Box since the number of key frames can vary while dragging keys in the inline curves causing hotControls to be desynchronized + GUI.Label(keyframeRect, GUIContent.none, DirectorStyles.Instance.keyframe); + + EditorGUI.DrawRect(bounds, DirectorStyles.Instance.customSkin.colorInfiniteClipLine); + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/InfiniteTrackDrawer.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/InfiniteTrackDrawer.cs.meta new file mode 100644 index 0000000..fd7c0b5 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/InfiniteTrackDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5ea6a8a826704f743b3b0ce3e9d3c9a9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/Layers.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/Layers.meta new file mode 100644 index 0000000..920cf3a --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/Layers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 16388ae022a89264b84107f0c1b44680 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/Layers/ClipsLayer.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/Layers/ClipsLayer.cs new file mode 100644 index 0000000..910123e --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/Layers/ClipsLayer.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.Timeline; + +namespace UnityEditor.Timeline +{ + class ClipsLayer : ItemsLayer + { + static readonly GUIStyle k_ConnectorIcon = DirectorStyles.Instance.connector; + + public ClipsLayer(Layer layerOrder, IRowGUI parent) : base(layerOrder) + { + var track = parent.asset; + track.SortClips(); + TimelineClipGUI previousClipGUI = null; + + foreach (var clip in track.clips) + { + var oldClipGUI = ItemToItemGui.GetGuiForClip(clip); + var isInvalid = oldClipGUI != null && oldClipGUI.isInvalid; // HACK Make sure to carry invalidy state when refereshing the cache. + + var currentClipGUI = new TimelineClipGUI(clip, parent, this) {isInvalid = isInvalid}; + if (previousClipGUI != null) previousClipGUI.nextClip = currentClipGUI; + currentClipGUI.previousClip = previousClipGUI; + AddItem(currentClipGUI); + previousClipGUI = currentClipGUI; + } + } + + public override void Draw(Rect rect, WindowState state) + { + base.Draw(rect, state); //draw clips + DrawConnector(items.OfType<TimelineClipGUI>()); + } + + static void DrawConnector(IEnumerable<TimelineClipGUI> clips) + { + if (Event.current.type != EventType.Repaint) + return; + + foreach (var clip in clips) + { + if (clip.previousClip != null && clip.visible && clip.treeViewRect.width > 14 && + (DiscreteTime)clip.start == (DiscreteTime)clip.previousClip.end) + { + // draw little connector widget + var localRect = clip.treeViewRect; + localRect.x -= Mathf.Floor(k_ConnectorIcon.fixedWidth / 2.0f); + localRect.width = k_ConnectorIcon.fixedWidth; + localRect.height = k_ConnectorIcon.fixedHeight; + GUI.Label(localRect, GUIContent.none, k_ConnectorIcon); + } + } + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/Layers/ClipsLayer.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/Layers/ClipsLayer.cs.meta new file mode 100644 index 0000000..58ef809 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/Layers/ClipsLayer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a809a4b50addbf44b9023b5e7f9fd4d2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/Layers/ItemsLayer.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/Layers/ItemsLayer.cs new file mode 100644 index 0000000..7446d59 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/Layers/ItemsLayer.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.Timeline; + +namespace UnityEditor.Timeline +{ + enum Layer : byte + { + Clips, + ClipHandles, + Markers, + MarkerHeaderTrack, + MarkersOnHeader + } + + struct LayerZOrder : IComparable<LayerZOrder> + { + Layer m_Layer; + int m_ZOrder; + + public LayerZOrder(Layer layer, int zOrder) + { + m_Layer = layer; + m_ZOrder = zOrder; + } + + public int CompareTo(LayerZOrder other) + { + if (m_Layer == other.m_Layer) + return m_ZOrder.CompareTo(other.m_ZOrder); + return m_Layer.CompareTo(other.m_Layer); + } + + public static LayerZOrder operator++(LayerZOrder x) + { + return new LayerZOrder(x.m_Layer, x.m_ZOrder + 1); + } + + public LayerZOrder ChangeLayer(Layer layer) + { + return new LayerZOrder(layer, m_ZOrder); + } + } + + interface ILayerable + { + LayerZOrder zOrder { get; } + } + + interface IZOrderProvider + { + LayerZOrder Next(); + } + + abstract class ItemsLayer : IZOrderProvider + { + // provide a buffer for time-based culling to allow for UI that extends slightly beyong the time (e.g. markers) + // prevents popping of marker visibility. + private const int kVisibilityBufferInPixels = 10; + + int m_PreviousLayerStateHash = -1; + LayerZOrder m_LastZOrder; + + public LayerZOrder Next() + { + return m_LastZOrder++; + } + + readonly List<TimelineItemGUI> m_Items = new List<TimelineItemGUI>(); + bool m_NeedSort = true; + + public virtual void Draw(Rect rect, WindowState state) + { + if (!m_Items.Any()) return; + + Sort(); + + // buffer to prevent flickering of markers at boundaries + var onePixelTime = state.PixelDeltaToDeltaTime(kVisibilityBufferInPixels); + var visibleTime = state.timeAreaShownRange + new Vector2(-onePixelTime, onePixelTime); + var layerViewStateHasChanged = GetLayerViewStateChanged(rect, state); + + foreach (var item in m_Items) + { + item.visible = item.end > visibleTime.x && item.start < visibleTime.y; + if (!item.visible) + continue; + + item.Draw(rect, layerViewStateHasChanged, state); + } + } + + public IEnumerable<TimelineItemGUI> items + { + get + { + return m_Items; + } + } + + protected void AddItem(TimelineItemGUI item) + { + m_Items.Add(item); + m_NeedSort = true; + } + + protected ItemsLayer(Layer layerOrder) + { + m_LastZOrder = new LayerZOrder(layerOrder, 0); + } + + void Sort() + { + if (!m_NeedSort) + return; + + m_Items.Sort((a, b) => a.zOrder.CompareTo(b.zOrder)); + m_NeedSort = false; + } + + bool GetLayerViewStateChanged(Rect rect, WindowState state) + { + var layerStateHash = rect.GetHashCode().CombineHash(state.viewStateHash); + var layerViewStateHasChanged = layerStateHash != m_PreviousLayerStateHash; + + if (Event.current.type == EventType.Layout && layerViewStateHasChanged) + m_PreviousLayerStateHash = layerStateHash; + + return layerViewStateHasChanged; + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/Layers/ItemsLayer.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/Layers/ItemsLayer.cs.meta new file mode 100644 index 0000000..5ebff18 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/Layers/ItemsLayer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ef97f39912c138b4cabdccedfb24093b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/Layers/MarkersLayer.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/Layers/MarkersLayer.cs new file mode 100644 index 0000000..50d1432 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/Layers/MarkersLayer.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine.Timeline; + +namespace UnityEditor.Timeline +{ + class MarkersLayer : ItemsLayer + { + public MarkersLayer(Layer layerOrder, IRowGUI parent) : base(layerOrder) + { + CreateLists(parent); + } + + void CreateLists(IRowGUI parent) + { + var markerCount = parent.asset.GetMarkerCount(); + if (markerCount == 0) return; + + var accumulator = new List<IMarker>(); + var sortedMarkers = new List<IMarker>(parent.asset.GetMarkers()); + var vm = TimelineWindowViewPrefs.GetTrackViewModelData(parent.asset); + + sortedMarkers.Sort((lhs, rhs) => + { + // Sort by time first + var timeComparison = lhs.time.CompareTo(rhs.time); + if (timeComparison != 0) + return timeComparison; + + // If there's a collision, sort by edit timestamp + var lhsObject = lhs as object; + var rhsObject = rhs as object; + + if (lhsObject.Equals(null) || rhsObject.Equals(null)) + return 0; + + var lhsHash = lhsObject.GetHashCode(); + var rhsHash = rhsObject.GetHashCode(); + + if (vm.markerTimeStamps.ContainsKey(lhsHash) && vm.markerTimeStamps.ContainsKey(rhsHash)) + return vm.markerTimeStamps[lhsHash].CompareTo(vm.markerTimeStamps[rhsHash]); + + return 0; + }); + + foreach (var current in sortedMarkers) + { + // TODO: Take zoom factor into account? + if (accumulator.Count > 0 && Math.Abs(current.time - accumulator[accumulator.Count - 1].time) > TimeUtility.kTimeEpsilon) + ProcessAccumulator(accumulator, parent); + + accumulator.Add(current); + } + + ProcessAccumulator(accumulator, parent); + } + + void ProcessAccumulator(List<IMarker> accumulator, IRowGUI parent) + { + if (accumulator.Count == 0) return; + + if (accumulator.Count == 1) + { + AddItem(new TimelineMarkerGUI(accumulator[0], parent, this)); + } + else + { + // Ensure that the cluster is always considered *below* the markers it contains. + var clusterZOrder = Next(); + AddItem( + new TimelineMarkerClusterGUI( + accumulator.Select(m => new TimelineMarkerGUI(m, parent, this)).ToList(), + parent, this, clusterZOrder)); + } + + accumulator.Clear(); + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/Layers/MarkersLayer.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/Layers/MarkersLayer.cs.meta new file mode 100644 index 0000000..32b0787 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/Layers/MarkersLayer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bea62e1faac8f9a48a4cb919ea05cb6a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/TrackDrawer.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/TrackDrawer.cs new file mode 100644 index 0000000..a29fefa --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/TrackDrawer.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.Playables; +using UnityEngine.Timeline; +namespace UnityEditor.Timeline +{ + class TrackDrawer : GUIDrawer + { + internal WindowState sequencerState { get; set; } + + + public static TrackDrawer CreateInstance(TrackAsset trackAsset) + { + if (trackAsset == null) + return Activator.CreateInstance<TrackDrawer>(); + + TrackDrawer drawer; + + try + { + drawer = (TrackDrawer)Activator.CreateInstance(TimelineHelpers.GetCustomDrawer(trackAsset.GetType())); + } + catch (Exception) + { + drawer = Activator.CreateInstance<TrackDrawer>(); + } + + drawer.track = trackAsset; + return drawer; + } + + protected TrackAsset track { get; private set; } + + public virtual bool DrawTrackHeaderButton(Rect rect, TrackAsset track, WindowState state) + { + return false; + } + + public virtual bool DrawTrack(Rect trackRect, TrackAsset trackAsset, Vector2 visibleTime, WindowState state) + { + return false; + } + + public virtual void DrawRecordingBackground(Rect trackRect, TrackAsset trackAsset, Vector2 visibleTime, WindowState state) + { + EditorGUI.DrawRect(trackRect, DirectorStyles.Instance.customSkin.colorTrackBackgroundRecording); + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/TrackDrawer.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/TrackDrawer.cs.meta new file mode 100644 index 0000000..9588043 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/TrackDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9a0f991b6c2f45b44b92e163f9969e8e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/TrackItemsDrawer.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/TrackItemsDrawer.cs new file mode 100644 index 0000000..677c2fe --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/TrackItemsDrawer.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace UnityEditor.Timeline +{ + struct TrackItemsDrawer + { + List<ItemsLayer> m_Layers; + ClipsLayer m_ClipsLayer; + + public IEnumerable<TimelineClipGUI> clips + { + get { return m_ClipsLayer.items.Cast<TimelineClipGUI>(); } + } + + public TrackItemsDrawer(IRowGUI parent) + { + m_Layers = null; + m_ClipsLayer = null; + BuildGUICache(parent); + } + + void BuildGUICache(IRowGUI parent) + { + m_ClipsLayer = new ClipsLayer(Layer.Clips, parent); + m_Layers = new List<ItemsLayer> + { + m_ClipsLayer, + new MarkersLayer(Layer.Markers, parent) + }; + } + + public void Draw(Rect rect, WindowState state) + { + foreach (var layer in m_Layers) + { + layer.Draw(rect, state); + } + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/TrackItemsDrawer.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/TrackItemsDrawer.cs.meta new file mode 100644 index 0000000..d41923c --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Drawers/TrackItemsDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c7137daaeb11e8647bf1ade9b7e9aa97 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/IPropertyKeyDataSource.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/IPropertyKeyDataSource.cs new file mode 100644 index 0000000..a26deb6 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/IPropertyKeyDataSource.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEditorInternal; +using UnityEngine; + +namespace UnityEditor.Timeline +{ + interface IPropertyKeyDataSource + { + float[] GetKeys(); // Get the keys + Dictionary<float, string> GetDescriptions(); // Caches for descriptions + } + + abstract class BasePropertyKeyDataSource : IPropertyKeyDataSource + { + static readonly StringBuilder k_StringBuilder = new StringBuilder(); + + protected abstract AnimationClip animationClip { get; } + + public virtual float[] GetKeys() + { + if (animationClip == null) + return null; + + var info = AnimationClipCurveCache.Instance.GetCurveInfo(animationClip); + return info.keyTimes.Select(TransformKeyTime).ToArray(); + } + + public virtual Dictionary<float, string> GetDescriptions() + { + var map = new Dictionary<float, string>(); + var info = AnimationClipCurveCache.Instance.GetCurveInfo(animationClip); + var processed = new HashSet<string>(); + + foreach (var b in info.bindings) + { + var groupID = b.GetGroupID(); + if (processed.Contains(groupID)) + continue; + + var group = info.GetGroupBinding(groupID); + var prefix = AnimationWindowUtility.GetNicePropertyGroupDisplayName(b.type, b.propertyName); + + foreach (var t in info.keyTimes) + { + k_StringBuilder.Length = 0; + + var key = TransformKeyTime(t); + if (map.ContainsKey(key)) + k_StringBuilder.Append(map[key]) + .Append('\n'); + + k_StringBuilder.Append(prefix) + .Append(" : ") + .Append(group.GetDescription(key)); + + map[key] = k_StringBuilder.ToString(); + } + processed.Add(groupID); + } + + return map; + } + + protected virtual float TransformKeyTime(float keyTime) + { + return keyTime; + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/IPropertyKeyDataSource.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/IPropertyKeyDataSource.cs.meta new file mode 100644 index 0000000..4c8993c --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/IPropertyKeyDataSource.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ee83d5e1ad2ac4c9aa39e3a6f256062e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/IRowGUI.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/IRowGUI.cs new file mode 100644 index 0000000..0745c6a --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/IRowGUI.cs @@ -0,0 +1,16 @@ +using UnityEngine; +using UnityEngine.Timeline; + +namespace UnityEditor.Timeline +{ + interface IRowGUI + { + TrackAsset asset { get; } + Rect boundingRect { get; } + bool locked { get; } + bool showMarkers { get; } + bool muted { get; } + + Rect ToWindowSpace(Rect treeViewRect); + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/IRowGUI.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/IRowGUI.cs.meta new file mode 100644 index 0000000..26dcaed --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/IRowGUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5ecec8cdc3ff99e4a9c65635d7e6b043 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/InlineCurveEditor.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/InlineCurveEditor.cs new file mode 100644 index 0000000..0bff23f --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/InlineCurveEditor.cs @@ -0,0 +1,299 @@ +using System.Linq; +using JetBrains.Annotations; +using UnityEngine; +using UnityEngine.Timeline; + +namespace UnityEditor.Timeline +{ + interface IClipCurveEditorOwner + { + ClipCurveEditor clipCurveEditor { get; } + bool inlineCurvesSelected { get; set; } + bool showLoops { get; } + TrackAsset owner { get; } + } + + class InlineCurveResizeHandle : IBounds + { + public Rect boundingRect { get; private set; } + + public TimelineTrackGUI trackGUI { get; } + + public InlineCurveResizeHandle(TimelineTrackGUI trackGUI) + { + this.trackGUI = trackGUI; + } + + public void Draw(Rect headerRect, Rect trackRect, WindowState state) + { + var rect = new Rect(headerRect.xMax + 4, headerRect.yMax - 5.0f, trackRect.width - 4, 5.0f); + + var handleColor = Handles.color; + Handles.color = Color.black; + Handles.DrawAAPolyLine(1.0f, + new Vector3(rect.x, rect.yMax, 0.0f), + new Vector3(rect.xMax, rect.yMax, 0.0f)); + Handles.color = handleColor; + + EditorGUIUtility.AddCursorRect(rect, MouseCursor.SplitResizeUpDown); + + boundingRect = trackGUI.ToWindowSpace(rect); + + if (Event.current.type == EventType.Repaint) + { + state.spacePartitioner.AddBounds(this); + + var dragStyle = new GUIStyle("RL DragHandle"); + dragStyle.Draw(rect, GUIContent.none, false, false, false, false); + } + } + } + + class InlineCurveEditor : IBounds + { + Rect m_TrackRect; + Rect m_HeaderRect; + readonly TimelineTrackGUI m_TrackGUI; + readonly InlineCurveResizeHandle m_ResizeHandle; + + bool m_LastSelectionWasClip; + TimelineClipGUI m_LastSelectedClipGUI; + + Rect IBounds.boundingRect { get { return m_TrackGUI.ToWindowSpace(m_TrackRect); } } + + [UsedImplicitly] // Used in tests + public TimelineClipGUI currentClipGui + { + get { return m_LastSelectedClipGUI; } + } + + public IClipCurveEditorOwner currentCurveEditor + { + get { return m_LastSelectionWasClip ? (IClipCurveEditorOwner)m_LastSelectedClipGUI : (IClipCurveEditorOwner)m_TrackGUI; } + } + + public InlineCurveEditor(TimelineTrackGUI trackGUI) + { + m_TrackGUI = trackGUI; + m_ResizeHandle = new InlineCurveResizeHandle(trackGUI); + } + + static bool MouseOverTrackArea(Rect curveRect, Rect trackRect) + { + curveRect.y = trackRect.y; + curveRect.height = trackRect.height; + + // clamp the curve editor to the track. this allows the menu to scroll properly + curveRect.xMin = Mathf.Max(curveRect.xMin, trackRect.xMin); + curveRect.xMax = trackRect.xMax; + + return curveRect.Contains(Event.current.mousePosition); + } + + static bool MouseOverHeaderArea(Rect headerRect, Rect trackRect) + { + headerRect.y = trackRect.y; + headerRect.height = trackRect.height; + + return headerRect.Contains(Event.current.mousePosition); + } + + static void DrawCurveEditor(IClipCurveEditorOwner clipCurveEditorOwner, WindowState state, Rect headerRect, Rect trackRect, Vector2 activeRange, bool locked) + { + ClipCurveEditor clipCurveEditor = clipCurveEditorOwner.clipCurveEditor; + CurveDataSource dataSource = clipCurveEditor.dataSource; + Rect curveRect = dataSource.GetBackgroundRect(state); + + bool newlySelected = false; + + if (Event.current.type == EventType.MouseDown || Event.current.type == EventType.ContextClick) + newlySelected = MouseOverTrackArea(curveRect, trackRect) || MouseOverHeaderArea(headerRect, trackRect); + + // make sure to not use any event before drawing the curve. + bool prevEnabledState = GUI.enabled; + GUI.enabled = true; + clipCurveEditorOwner.clipCurveEditor.DrawHeader(headerRect); + GUI.enabled = prevEnabledState; + + bool displayAsSelected = !locked && (clipCurveEditorOwner.inlineCurvesSelected || newlySelected); + + using (new EditorGUI.DisabledScope(locked)) + clipCurveEditor.DrawCurveEditor(trackRect, state, activeRange, clipCurveEditorOwner.showLoops, displayAsSelected); + + if (newlySelected && !locked) + { + clipCurveEditorOwner.inlineCurvesSelected = true; + HandleCurrentEvent(); + } + } + + public void Draw(Rect headerRect, Rect trackRect, WindowState state) + { + m_TrackRect = trackRect; + m_TrackRect.height -= 5.0f; + + if (Event.current.type == EventType.Repaint) + state.spacePartitioner.AddBounds(this); + + // Remove the indentation of this track to render it properly, otherwise every GUI elements will be offsetted. + headerRect.x -= DirectorStyles.kBaseIndent; + headerRect.width += DirectorStyles.kBaseIndent; + + // Remove the width of the color swatch. + headerRect.x += 4.0f; + headerRect.width -= 4.0f; + + m_HeaderRect = headerRect; + + EditorGUI.DrawRect(m_HeaderRect, DirectorStyles.Instance.customSkin.colorAnimEditorBinding); + + if (ShouldShowClipCurves(state)) + { + DrawCurveEditorsForClipsOnTrack(m_HeaderRect, m_TrackRect, state); + } + else if (ShouldShowTrackCurves()) + { + DrawCurveEditorForTrack(m_HeaderRect, m_TrackRect, state); + } + else + { + DrawCurvesEditorForNothingSelected(m_HeaderRect, m_TrackRect, state); + } + + m_ResizeHandle.Draw(headerRect, trackRect, state); + + // If MouseDown or ContextClick are not consumed by the curves, use the event to prevent it from going deeper into the treeview. + if (Event.current.type == EventType.ContextClick) + { + var r = Rect.MinMaxRect(m_HeaderRect.xMin, m_HeaderRect.yMin, m_TrackRect.xMax, m_TrackRect.yMax); + if (r.Contains(Event.current.mousePosition)) + Event.current.Use(); + } + + UpdateViewModel(); + } + + public void Refresh() + { + if (m_LastSelectionWasClip) + RefreshInlineCurves(m_LastSelectedClipGUI); + else + RefreshInlineCurves(m_TrackGUI); + } + + static void RefreshInlineCurves(IClipCurveEditorOwner guiItem) + { + if (guiItem.clipCurveEditor != null && guiItem.clipCurveEditor.dataSource != null) + guiItem.clipCurveEditor.dataSource.RebuildCurves(); + } + + void DrawCurveEditorForTrack(Rect headerRect, Rect trackRect, WindowState state) + { + if (m_TrackGUI.clipCurveEditor == null) + return; + + var activeRange = new Vector2(state.TimeToPixel(0.0d), state.TimeToPixel(state.editSequence.duration)); + DrawCurveEditor(m_TrackGUI, state, headerRect, trackRect, activeRange, m_TrackGUI.locked); + m_LastSelectionWasClip = false; + } + + void DrawCurveEditorsForClipsOnTrack(Rect headerRect, Rect trackRect, WindowState state) + { + if (m_TrackGUI.clips.Count == 0) + return; + + if (Event.current.type == EventType.Layout) + { + TimelineClipGUI selectedClip = SelectionManager.SelectedClipGUI().FirstOrDefault(x => x.parent == m_TrackGUI); + if (selectedClip != null) + { + m_LastSelectedClipGUI = selectedClip; + } + else if (state.recording && state.IsArmedForRecord(m_TrackGUI.track)) + { + if (m_LastSelectedClipGUI == null || !m_TrackGUI.track.IsRecordingToClip(m_LastSelectedClipGUI.clip)) + { + var clip = m_TrackGUI.clips.FirstOrDefault(x => m_TrackGUI.track.IsRecordingToClip(x.clip)); + if (clip != null) + m_LastSelectedClipGUI = clip; + } + } + + if (m_LastSelectedClipGUI == null) + m_LastSelectedClipGUI = m_TrackGUI.clips[0]; + } + + if (m_LastSelectedClipGUI == null || m_LastSelectedClipGUI.clipCurveEditor == null || m_LastSelectedClipGUI.isInvalid) + return; + + var activeRange = new Vector2(state.TimeToPixel(m_LastSelectedClipGUI.clip.start), state.TimeToPixel(m_LastSelectedClipGUI.clip.end)); + DrawCurveEditor(m_LastSelectedClipGUI, state, headerRect, trackRect, activeRange, m_TrackGUI.locked); + m_LastSelectionWasClip = true; + } + + void DrawCurvesEditorForNothingSelected(Rect headerRect, Rect trackRect, WindowState state) + { + if (m_LastSelectionWasClip || !TrackHasCurvesToShow() && m_TrackGUI.clips.Count > 0) + { + DrawCurveEditorsForClipsOnTrack(headerRect, trackRect, state); + } + else + { + DrawCurveEditorForTrack(headerRect, trackRect, state); + } + } + + bool ShouldShowClipCurves(WindowState state) + { + if (m_TrackGUI.clips.Count == 0) + return false; + + // Is a clip selected or being recorded to? + return SelectionManager.SelectedClipGUI().FirstOrDefault(x => x.parent == m_TrackGUI) != null || + state.recording && state.IsArmedForRecord(m_TrackGUI.track) && m_TrackGUI.clips.FirstOrDefault(x => m_TrackGUI.track.IsRecordingToClip(x.clip)) != null; + } + + bool ShouldShowTrackCurves() + { + if (m_TrackGUI == null) + return false; + + var isTrackSelected = SelectionManager.SelectedTrackGUI().FirstOrDefault(x => x == m_TrackGUI) != null; + + if (!isTrackSelected) + return false; + + return TrackHasCurvesToShow(); + } + + bool TrackHasCurvesToShow() + { + var animTrack = m_TrackGUI.track as AnimationTrack; + if (animTrack != null && !animTrack.inClipMode) + return true; + + return m_TrackGUI.track.HasAnyAnimatableParameters(); + } + + void UpdateViewModel() + { + var curveEditor = currentCurveEditor.clipCurveEditor; + if (curveEditor == null || curveEditor.bindingHierarchy.treeViewController == null) + return; + + var vm = TimelineWindowViewPrefs.GetTrackViewModelData(m_TrackGUI.track); + vm.inlineCurvesState = curveEditor.bindingHierarchy.treeViewController.state; + vm.inlineCurvesShownAreaInsideMargins = curveEditor.shownAreaInsideMargins; + vm.lastInlineCurveDataID = curveEditor.dataSource.id; + } + + static void HandleCurrentEvent() + { +#if UNITY_EDITOR_OSX + Event.current.type = EventType.Ignore; +#else + Event.current.Use(); +#endif + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/InlineCurveEditor.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/InlineCurveEditor.cs.meta new file mode 100644 index 0000000..cbc0b18 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/InlineCurveEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e1f640f1769d6274194cf97b0e24602c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui.meta new file mode 100644 index 0000000..f19c2d2 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9d816a6ab06c6834480f5f45f440e287 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/ISelectable.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/ISelectable.cs new file mode 100644 index 0000000..5f312c1 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/ISelectable.cs @@ -0,0 +1,11 @@ +using System; + +namespace UnityEditor.Timeline +{ + interface ISelectable : ILayerable + { + void Select(); + bool IsSelected(); + void Deselect(); + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/ISelectable.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/ISelectable.cs.meta new file mode 100644 index 0000000..970ecaa --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/ISelectable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4acbfc0398bab674f922f693e58f4afc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineClipGUI.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineClipGUI.cs new file mode 100644 index 0000000..7d00228 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineClipGUI.cs @@ -0,0 +1,760 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.Playables; +using UnityEngine.Timeline; + +namespace UnityEditor.Timeline +{ + class TimelineClipGUI : TimelineItemGUI, IClipCurveEditorOwner, ISnappable, IAttractable + { + EditorClip m_EditorItem; + + Rect m_ClipCenterSection; + readonly List<Rect> m_LoopRects = new List<Rect>(); + + ClipDrawData m_ClipDrawData; + Rect m_MixOutRect = new Rect(); + Rect m_MixInRect = new Rect(); + int m_MinLoopIndex = 1; + + // clip dirty detection + int m_LastDirtyIndex = Int32.MinValue; + bool m_ClipViewDirty = true; + + bool supportResize { get; } + public ClipCurveEditor clipCurveEditor { get; set; } + public TimelineClipGUI previousClip { get; set; } + public TimelineClipGUI nextClip { get; set; } + + static readonly float k_MinMixWidth = 2; + static readonly float k_MaxHandleWidth = 10f; + static readonly float k_MinHandleWidth = 1f; + + bool? m_ShowDrillIcon; + ClipEditor m_ClipEditor; + + static List<PlayableDirector> s_TempSubDirectors = new List<PlayableDirector>(); + + static readonly IconData k_DiggableClipIcon = new IconData(DirectorStyles.LoadIcon("TimelineDigIn")); + + string name + { + get + { + if (string.IsNullOrEmpty(clip.displayName)) + return "(Empty)"; + + return clip.displayName; + } + } + + public bool inlineCurvesSelected + { + get { return SelectionManager.IsCurveEditorFocused(this); } + set + { + if (!value && SelectionManager.IsCurveEditorFocused(this)) + SelectionManager.SelectInlineCurveEditor(null); + else + SelectionManager.SelectInlineCurveEditor(this); + } + } + + public Rect mixOutRect + { + get + { + var percent = clip.mixOutPercentage; + var x = Mathf.Round(treeViewRect.width * (1 - percent)); + var width = Mathf.Round(treeViewRect.width * percent); + m_MixOutRect.Set(x, 0.0f, width, treeViewRect.height); + return m_MixOutRect; + } + } + + public Rect mixInRect + { + get + { + var width = Mathf.Round(treeViewRect.width * clip.mixInPercentage); + m_MixInRect.Set(0.0f, 0.0f, width, treeViewRect.height); + return m_MixInRect; + } + } + + public ClipBlends GetClipBlends() + { + var _mixInRect = mixInRect; + var _mixOutRect = mixOutRect; + + var blendInKind = BlendKind.None; + if (_mixInRect.width > k_MinMixWidth && clip.hasBlendIn) + blendInKind = BlendKind.Mix; + else if (_mixInRect.width > k_MinMixWidth) + blendInKind = BlendKind.Ease; + + var blendOutKind = BlendKind.None; + if (_mixOutRect.width > k_MinMixWidth && clip.hasBlendOut) + blendOutKind = BlendKind.Mix; + else if (_mixOutRect.width > k_MinMixWidth) + blendOutKind = BlendKind.Ease; + + return new ClipBlends(blendInKind, _mixInRect, blendOutKind, _mixOutRect); + } + + public override double start + { + get { return clip.start; } + } + + public override double end + { + get { return clip.end; } + } + + public bool supportsLooping + { + get { return clip.SupportsLooping(); } + } + + // for the inline curve editor, only show loops if we recorded the asset + bool IClipCurveEditorOwner.showLoops + { + get { return clip.SupportsLooping() && (clip.asset is AnimationPlayableAsset); } + } + + TrackAsset IClipCurveEditorOwner.owner + { + get { return clip.parentTrack; } + } + + public bool supportsSubTimelines + { + get { return m_ClipEditor.supportsSubTimelines; } + } + + + public int minLoopIndex + { + get { return m_MinLoopIndex; } + } + + public TrackDrawer drawer + { + get { return ((TimelineTrackGUI)parent).drawer; } + } + + public Rect clippedRect { get; private set; } + + public override void Select() + { + zOrder = zOrderProvider.Next(); + SelectionManager.Add(clip); + } + + public override bool IsSelected() + { + return SelectionManager.Contains(clip); + } + + public override void Deselect() + { + SelectionManager.Remove(clip); + } + + public override ITimelineItem item + { + get { return ItemsUtils.ToItem(clip); } + } + + IZOrderProvider zOrderProvider { get; } + + public TimelineClipHandle leftHandle { get; } + public TimelineClipHandle rightHandle { get; } + + public TimelineClipGUI(TimelineClip clip, IRowGUI parent, IZOrderProvider provider) : base(parent) + { + zOrderProvider = provider; + zOrder = provider.Next(); + + m_EditorItem = EditorClipFactory.GetEditorClip(clip); + m_ClipEditor = CustomTimelineEditorCache.GetClipEditor(clip); + + supportResize = true; + + leftHandle = new TimelineClipHandle(this, TrimEdge.Start); + rightHandle = new TimelineClipHandle(this, TrimEdge.End); + + ItemToItemGui.Add(clip, this); + } + + void CreateInlineCurveEditor(WindowState state) + { + if (clipCurveEditor != null) + return; + + var animationClip = clip.animationClip; + + if (animationClip != null && animationClip.empty) + animationClip = null; + + // prune out clips coming from FBX + if (animationClip != null && !clip.recordable) + return; // don't show, even if there are curves + + if (animationClip == null && !clip.HasAnyAnimatableParameters()) + return; // nothing to show + + state.AddEndFrameDelegate((istate, currentEvent) => + { + clipCurveEditor = new ClipCurveEditor(CurveDataSource.Create(this), TimelineWindow.instance, clip.parentTrack); + return true; + }); + } + + public TimelineClip clip + { + get { return m_EditorItem.clip; } + } + + // Draw the actual clip. Defers to the track drawer for customization + void UpdateDrawData(WindowState state, Rect drawRect, string title, bool selected, bool previousClipSelected, float rectXOffset) + { + m_ClipDrawData.clip = clip; + m_ClipDrawData.targetRect = drawRect; + m_ClipDrawData.clipCenterSection = m_ClipCenterSection; + m_ClipDrawData.unclippedRect = treeViewRect; + m_ClipDrawData.title = title; + m_ClipDrawData.selected = selected; + m_ClipDrawData.inlineCurvesSelected = inlineCurvesSelected; + m_ClipDrawData.previousClip = previousClip != null ? previousClip.clip : null; + m_ClipDrawData.previousClipSelected = previousClipSelected; + + Vector3 shownAreaTime = state.timeAreaShownRange; + m_ClipDrawData.localVisibleStartTime = clip.ToLocalTimeUnbound(Math.Max(clip.start, shownAreaTime.x)); + m_ClipDrawData.localVisibleEndTime = clip.ToLocalTimeUnbound(Math.Min(clip.end, shownAreaTime.y)); + + m_ClipDrawData.clippedRect = new Rect(clippedRect.x - rectXOffset, 0.0f, clippedRect.width, clippedRect.height); + + m_ClipDrawData.minLoopIndex = minLoopIndex; + m_ClipDrawData.loopRects = m_LoopRects; + m_ClipDrawData.supportsLooping = supportsLooping; + m_ClipDrawData.clipBlends = GetClipBlends(); + m_ClipDrawData.clipEditor = m_ClipEditor; + m_ClipDrawData.ClipDrawOptions = UpdateClipDrawOptions(m_ClipEditor, clip); + + UpdateClipIcons(state); + } + + void UpdateClipIcons(WindowState state) + { + // Pass 1 - gather size + int required = 0; + bool requiresDigIn = ShowDrillIcon(state.editSequence.director); + if (requiresDigIn) + required++; + + var icons = m_ClipDrawData.ClipDrawOptions.icons; + foreach (var icon in icons) + { + if (icon != null) + required++; + } + + // Pass 2 - copy icon data + if (required == 0) + { + m_ClipDrawData.rightIcons = null; + return; + } + + if (m_ClipDrawData.rightIcons == null || m_ClipDrawData.rightIcons.Length != required) + m_ClipDrawData.rightIcons = new IconData[required]; + + int index = 0; + if (requiresDigIn) + m_ClipDrawData.rightIcons[index++] = k_DiggableClipIcon; + + foreach (var icon in icons) + { + if (icon != null) + m_ClipDrawData.rightIcons[index++] = new IconData(icon); + } + } + + static ClipDrawOptions UpdateClipDrawOptions(ClipEditor clipEditor, TimelineClip clip) + { + try + { + return clipEditor.GetClipOptions(clip); + } + catch (Exception e) + { + Debug.LogException(e); + } + + return CustomTimelineEditorCache.GetDefaultClipEditor().GetClipOptions(clip); + } + + static void DrawClip(ClipDrawData drawData) + { + ClipDrawer.DrawDefaultClip(drawData); + + if (drawData.clip.asset is AnimationPlayableAsset) + { + var state = TimelineWindow.instance.state; + if (state.recording && state.IsArmedForRecord(drawData.clip.parentTrack)) + { + ClipDrawer.DrawAnimationRecordBorder(drawData); + ClipDrawer.DrawRecordProhibited(drawData); + } + } + } + + public void DrawGhostClip(Rect targetRect) + { + DrawSimpleClip(targetRect, ClipBorder.Selection(), new Color(1.0f, 1.0f, 1.0f, 0.5f)); + } + + public void DrawInvalidClip(Rect targetRect) + { + DrawSimpleClip(targetRect, ClipBorder.Selection(), DirectorStyles.Instance.customSkin.colorInvalidClipOverlay); + } + + void DrawSimpleClip(Rect targetRect, ClipBorder border, Color overlay) + { + var drawOptions = UpdateClipDrawOptions(CustomTimelineEditorCache.GetClipEditor(clip), clip); + ClipDrawer.DrawSimpleClip(clip, targetRect, border, overlay, drawOptions); + } + + void DrawInto(Rect drawRect, WindowState state) + { + if (Event.current.type != EventType.Repaint) + return; + + // create the inline curve editor if not already created + CreateInlineCurveEditor(state); + + // @todo optimization, most of the calculations (rect, offsets, colors, etc.) could be cached + // and rebuilt when the hash of the clip changes. + + if (isInvalid) + { + DrawInvalidClip(treeViewRect); + return; + } + + GUI.BeginClip(drawRect); + + var originRect = new Rect(0.0f, 0.0f, drawRect.width, drawRect.height); + string clipLabel = name; + var selected = SelectionManager.Contains(clip); + var previousClipSelected = previousClip != null && SelectionManager.Contains(previousClip.clip); + + if (selected && 1.0 != clip.timeScale) + clipLabel += " " + clip.timeScale.ToString("F2") + "x"; + + UpdateDrawData(state, originRect, clipLabel, selected, previousClipSelected, drawRect.x); + DrawClip(m_ClipDrawData); + + GUI.EndClip(); + + if (clip.parentTrack != null && !clip.parentTrack.lockedInHierarchy) + { + if (selected && supportResize) + { + var cursorRect = rect; + cursorRect.xMin += leftHandle.boundingRect.width; + cursorRect.xMax -= rightHandle.boundingRect.width; + EditorGUIUtility.AddCursorRect(cursorRect, MouseCursor.MoveArrow); + } + + if (supportResize) + { + var handleWidth = Mathf.Clamp(drawRect.width * 0.3f, k_MinHandleWidth, k_MaxHandleWidth); + + leftHandle.Draw(drawRect, handleWidth, state); + rightHandle.Draw(drawRect, handleWidth, state); + } + } + } + + void CalculateClipRectangle(Rect trackRect, WindowState state) + { + if (m_ClipViewDirty) + { + var clipRect = RectToTimeline(trackRect, state); + treeViewRect = clipRect; + + // calculate clipped rect + clipRect.xMin = Mathf.Max(clipRect.xMin, trackRect.xMin); + clipRect.xMax = Mathf.Min(clipRect.xMax, trackRect.xMax); + + if (clipRect.width > 0 && clipRect.width < 2) + { + clipRect.width = 5.0f; + } + + clippedRect = clipRect; + } + } + + void AddToSpacePartitioner(WindowState state) + { + if (Event.current.type == EventType.Repaint && !parent.locked) + state.spacePartitioner.AddBounds(this, rect); + } + + void CalculateBlendRect() + { + m_ClipCenterSection = treeViewRect; + m_ClipCenterSection.x = 0; + m_ClipCenterSection.y = 0; + + m_ClipCenterSection.xMin = Mathf.Round(treeViewRect.width * clip.mixInPercentage); + m_ClipCenterSection.width = Mathf.Round(treeViewRect.width); + m_ClipCenterSection.xMax -= Mathf.Round(mixOutRect.width + treeViewRect.width * clip.mixInPercentage); + } + + // Entry point to the Clip Drawing... + public override void Draw(Rect trackRect, bool trackRectChanged, WindowState state) + { + // if the clip has changed, fire the appropriate callback + DetectClipChanged(trackRectChanged); + + // update the clip projected rectangle on the timeline + CalculateClipRectangle(trackRect, state); + + AddToSpacePartitioner(state); + + // update the blend rects (when clip overlaps with others) + CalculateBlendRect(); + + // update the loop rects (when clip loops) + CalculateLoopRects(trackRect, state); + + DrawExtrapolation(trackRect, treeViewRect); + + DrawInto(treeViewRect, state); + + ResetClipChanged(); + } + + void DetectClipChanged(bool trackRectChanged) + { + if (Event.current.type == EventType.Layout) + { + if (clip.DirtyIndex != m_LastDirtyIndex) + { + m_ClipViewDirty = true; + + try + { + m_ClipEditor.OnClipChanged(clip); + } + catch (Exception e) + { + Debug.LogException(e); + } + + m_LastDirtyIndex = clip.DirtyIndex; + } + m_ClipViewDirty |= trackRectChanged; + } + } + + void ResetClipChanged() + { + if (Event.current.type == EventType.Repaint) + m_ClipViewDirty = false; + } + + GUIStyle GetExtrapolationIcon(TimelineClip.ClipExtrapolation mode) + { + GUIStyle extrapolationIcon = null; + + switch (mode) + { + case TimelineClip.ClipExtrapolation.None: return null; + case TimelineClip.ClipExtrapolation.Hold: extrapolationIcon = m_Styles.extrapolationHold; break; + case TimelineClip.ClipExtrapolation.Loop: extrapolationIcon = m_Styles.extrapolationLoop; break; + case TimelineClip.ClipExtrapolation.PingPong: extrapolationIcon = m_Styles.extrapolationPingPong; break; + case TimelineClip.ClipExtrapolation.Continue: extrapolationIcon = m_Styles.extrapolationContinue; break; + } + + return extrapolationIcon; + } + + Rect GetPreExtrapolationBounds(Rect trackRect, Rect clipRect, GUIStyle icon) + { + float x = clipRect.xMin - (icon.fixedWidth + 10.0f); + float y = trackRect.yMin + (trackRect.height - icon.fixedHeight) / 2.0f; + + if (previousClip != null) + { + float distance = Mathf.Abs(treeViewRect.xMin - previousClip.treeViewRect.xMax); + + if (distance < icon.fixedWidth) + return new Rect(0.0f, 0.0f, 0.0f, 0.0f); + + if (distance < icon.fixedWidth + 20.0f) + { + float delta = (distance - icon.fixedWidth) / 2.0f; + x = clipRect.xMin - (icon.fixedWidth + delta); + } + } + + return new Rect(x, y, icon.fixedWidth, icon.fixedHeight); + } + + Rect GetPostExtrapolationBounds(Rect trackRect, Rect clipRect, GUIStyle icon) + { + float x = clipRect.xMax + 10.0f; + float y = trackRect.yMin + (trackRect.height - icon.fixedHeight) / 2.0f; + + if (nextClip != null) + { + float distance = Mathf.Abs(nextClip.treeViewRect.xMin - treeViewRect.xMax); + + if (distance < icon.fixedWidth) + return new Rect(0.0f, 0.0f, 0.0f, 0.0f); + + if (distance < icon.fixedWidth + 20.0f) + { + float delta = (distance - icon.fixedWidth) / 2.0f; + x = clipRect.xMax + delta; + } + } + + return new Rect(x, y, icon.fixedWidth, icon.fixedHeight); + } + + static void DrawExtrapolationIcon(Rect rect, GUIStyle icon) + { + GUI.Label(rect, GUIContent.none, icon); + } + + void DrawExtrapolation(Rect trackRect, Rect clipRect) + { + if (clip.hasPreExtrapolation) + { + GUIStyle icon = GetExtrapolationIcon(clip.preExtrapolationMode); + + if (icon != null) + { + Rect iconBounds = GetPreExtrapolationBounds(trackRect, clipRect, icon); + + if (iconBounds.width > 1 && iconBounds.height > 1) + DrawExtrapolationIcon(iconBounds, icon); + } + } + + if (clip.hasPostExtrapolation) + { + GUIStyle icon = GetExtrapolationIcon(clip.postExtrapolationMode); + + if (icon != null) + { + Rect iconBounds = GetPostExtrapolationBounds(trackRect, clipRect, icon); + + if (iconBounds.width > 1 && iconBounds.height > 1) + DrawExtrapolationIcon(iconBounds, icon); + } + } + } + + static Rect ProjectRectOnTimeline(Rect rect, Rect trackRect, WindowState state) + { + Rect newRect = rect; + // transform clipRect into pixel-space + newRect.x *= state.timeAreaScale.x; + newRect.width *= state.timeAreaScale.x; + + newRect.x += state.timeAreaTranslation.x + trackRect.xMin; + + // adjust clipRect height and vertical centering + const int clipPadding = 2; + newRect.y = trackRect.y + clipPadding; + newRect.height = trackRect.height - (2 * clipPadding); + return newRect; + } + + void CalculateLoopRects(Rect trackRect, WindowState state) + { + if (!m_ClipViewDirty) + return; + + m_LoopRects.Clear(); + if (clip.duration < WindowState.kTimeEpsilon) + return; + + var times = TimelineHelpers.GetLoopTimes(clip); + var loopDuration = TimelineHelpers.GetLoopDuration(clip); + m_MinLoopIndex = -1; + + // we have a hold, no need to compute all loops + if (!supportsLooping) + { + if (times.Length > 1) + { + var t = times[1]; + float loopTime = (float)(clip.duration - t); + m_LoopRects.Add(ProjectRectOnTimeline(new Rect((float)(t + clip.start), 0, loopTime, 0), trackRect, state)); + } + return; + } + + var range = state.timeAreaShownRange; + var visibleStartTime = range.x - clip.start; + var visibleEndTime = range.y - clip.start; + + for (int i = 1; i < times.Length; i++) + { + var t = times[i]; + + // don't draw off screen loops + if (t > visibleEndTime) + break; + + float loopTime = Mathf.Min((float)(clip.duration - t), (float)loopDuration); + var loopEnd = t + loopTime; + + if (loopEnd < visibleStartTime) + continue; + + m_LoopRects.Add(ProjectRectOnTimeline(new Rect((float)(t + clip.start), 0, loopTime, 0), trackRect, state)); + + if (m_MinLoopIndex == -1) + m_MinLoopIndex = i; + } + } + + public override Rect RectToTimeline(Rect trackRect, WindowState state) + { + var offsetFromTimeSpaceToPixelSpace = state.timeAreaTranslation.x + trackRect.xMin; + + var start = (float)(DiscreteTime)clip.start; + var end = (float)(DiscreteTime)clip.end; + + return Rect.MinMaxRect( + Mathf.Round(start * state.timeAreaScale.x + offsetFromTimeSpaceToPixelSpace), Mathf.Round(trackRect.yMin), + Mathf.Round(end * state.timeAreaScale.x + offsetFromTimeSpaceToPixelSpace), Mathf.Round(trackRect.yMax) + ); + } + + public IEnumerable<Edge> SnappableEdgesFor(IAttractable attractable, ManipulateEdges manipulateEdges) + { + var edges = new List<Edge>(); + + bool canAddEdges = !parent.muted; + + if (canAddEdges) + { + // Hack: Trim Start in Ripple mode should not have any snap point added + if (EditMode.editType == EditMode.EditType.Ripple && manipulateEdges == ManipulateEdges.Left) + return edges; + + if (attractable != this) + { + if (EditMode.editType == EditMode.EditType.Ripple) + { + bool skip = false; + + // Hack: Since Trim End and Move in Ripple mode causes other snap point to move on the same track (which is not supported), disable snapping for this special cases... + // TODO Find a proper way to have different snap edges for each edit mode. + if (manipulateEdges == ManipulateEdges.Right) + { + var otherClipGUI = attractable as TimelineClipGUI; + skip = otherClipGUI != null && otherClipGUI.parent == parent; + } + else if (manipulateEdges == ManipulateEdges.Both) + { + var moveHandler = attractable as MoveItemHandler; + skip = moveHandler != null && moveHandler.movingItems.Any(clips => clips.targetTrack == clip.parentTrack && clip.start >= clips.start); + } + + if (skip) + return edges; + } + + AddEdge(edges, clip.start); + AddEdge(edges, clip.end); + } + else + { + if (manipulateEdges == ManipulateEdges.Right) + { + var d = TimelineHelpers.GetClipAssetEndTime(clip); + + if (d < double.MaxValue) + { + if (clip.SupportsLooping()) + { + var l = TimelineHelpers.GetLoopDuration(clip); + + var shownTime = TimelineWindow.instance.state.timeAreaShownRange; + do + { + AddEdge(edges, d, false); + d += l; + } + while (d < shownTime.y); + } + else + { + AddEdge(edges, d, false); + } + } + } + + if (manipulateEdges == ManipulateEdges.Left) + { + var clipInfo = AnimationClipCurveCache.Instance.GetCurveInfo(clip.animationClip); + if (clipInfo != null && clipInfo.keyTimes.Any()) + AddEdge(edges, clip.FromLocalTimeUnbound(clipInfo.keyTimes.Min()), false); + } + } + } + return edges; + } + + public bool ShouldSnapTo(ISnappable snappable) + { + return true; + } + + bool ShowDrillIcon(PlayableDirector resolver) + { + if (!m_ShowDrillIcon.HasValue || TimelineWindow.instance.hierarchyChangedThisFrame) + { + var nestable = m_ClipEditor.supportsSubTimelines; + m_ShowDrillIcon = nestable && resolver != null; + if (m_ShowDrillIcon.Value) + { + s_TempSubDirectors.Clear(); + try + { + m_ClipEditor.GetSubTimelines(clip, resolver, s_TempSubDirectors); + } + catch (Exception e) + { + Debug.LogException(e); + } + + m_ShowDrillIcon &= s_TempSubDirectors.Count > 0; + } + } + + return m_ShowDrillIcon.Value; + } + + static void AddEdge(List<Edge> edges, double time, bool showEdgeHint = true) + { + var shownTime = TimelineWindow.instance.state.timeAreaShownRange; + if (time >= shownTime.x && time <= shownTime.y) + edges.Add(new Edge(time, showEdgeHint)); + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineClipGUI.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineClipGUI.cs.meta new file mode 100644 index 0000000..4e94156 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineClipGUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3c01b61b3a6887c49a15276fd38be918 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineItemGUI.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineItemGUI.cs new file mode 100644 index 0000000..522b35e --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineItemGUI.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using UnityEngine.Timeline; +using UnityEngine; + +namespace UnityEditor.Timeline +{ + static class ItemToItemGui + { + static Dictionary<object, TimelineItemGUI> s_ItemToItemGUI = + new Dictionary<object, TimelineItemGUI>(); + + public static void Add(TimelineClip clip, TimelineItemGUI gui) + { + s_ItemToItemGUI[clip] = gui; + } + + public static void Add(IMarker marker, TimelineItemGUI gui) + { + s_ItemToItemGUI[marker] = gui; + } + + public static TimelineClipGUI GetGuiForClip(TimelineClip clip) + { + return GetGuiForItem(clip) as TimelineClipGUI; + } + + public static TimelineMarkerGUI GetGuiForMarker(IMarker marker) + { + return GetGuiForItem(marker) as TimelineMarkerGUI; + } + + static TimelineItemGUI GetGuiForItem(object item) + { + if (item == null) + return null; + + TimelineItemGUI gui; + s_ItemToItemGUI.TryGetValue(item, out gui); + return gui; + } + } + + abstract class TimelineItemGUI : ISelectable + { + protected readonly DirectorStyles m_Styles; + + public abstract ITimelineItem item { get; } + public abstract double start { get; } + public abstract double end { get; } + public abstract void Draw(Rect rect, bool rectChanged, WindowState state); + public abstract Rect RectToTimeline(Rect trackRect, WindowState state); + + public virtual void Select() {} + public virtual bool IsSelected() { return false; } + public virtual void Deselect() {} + + public virtual void StartDrag() {} + public virtual void StopDrag() {} + + public LayerZOrder zOrder { get; set; } + + public bool visible { get; set; } + public bool isInvalid { get; set; } + + public IRowGUI parent { get; } + + public Rect rect + { + get { return parent.ToWindowSpace(treeViewRect); } + } + + public Rect treeViewRect + { + get { return m_TreeViewRect; } + protected set + { + m_TreeViewRect = value; + if (value.width < 0.0f) + m_TreeViewRect.width = 1.0f; + } + } + + Rect m_TreeViewRect; + + protected TimelineItemGUI(IRowGUI parent) + { + this.parent = parent; + m_Styles = DirectorStyles.Instance; + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineItemGUI.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineItemGUI.cs.meta new file mode 100644 index 0000000..a38143d --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineItemGUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6f515f8ecd3b6a546b90abaae2553f99 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineMarkerClusterGUI.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineMarkerClusterGUI.cs new file mode 100644 index 0000000..b0ffeac --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineMarkerClusterGUI.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +using UnityObject = UnityEngine.Object; + +namespace UnityEditor.Timeline +{ + class TimelineMarkerClusterGUI : TimelineItemGUI + { + readonly List<TimelineMarkerGUI> m_MarkerGUIs; + readonly IZOrderProvider m_ZOrderProvider; + + public TimelineMarkerGUI topMarker + { + get { return m_MarkerGUIs.LastOrDefault(); } + } + + TimelineMarkerGUI m_ManipulatedMarker; + + public TimelineMarkerClusterGUI(List<TimelineMarkerGUI> guis, IRowGUI parent, + IZOrderProvider zOrderProvider, LayerZOrder layerZOrder) + : base(parent) + { + m_MarkerGUIs = guis; + m_ZOrderProvider = zOrderProvider; + zOrder = layerZOrder; + SortMarkers(); + topMarker.onStartDrag += OnDragTopMarker; + } + + public override double start + { + get { return topMarker.start; } + } + + public override double end + { + get { return topMarker.end; } + } + + public override ITimelineItem item + { + get { return topMarker.item; } + } + + public override void Select() + { + foreach (var marker in m_MarkerGUIs) + { + if (!marker.IsSelected()) + marker.Select(); + } + } + + public override void Deselect() + { + foreach (var marker in m_MarkerGUIs) + { + if (marker.IsSelected()) + marker.Deselect(); + } + } + + public override void Draw(Rect trackRect, bool trackRectChanged, WindowState state) + { + RegisterRect(state); + + topMarker.Draw(trackRect, trackRectChanged, state); + + if (m_MarkerGUIs.Count > 1) + GUI.Box(treeViewRect, String.Empty, DirectorStyles.Instance.markerMultiOverlay); + + if (m_ManipulatedMarker != null) + m_ManipulatedMarker.Draw(trackRect, trackRectChanged, state); + } + + public override Rect RectToTimeline(Rect trackRect, WindowState state) + { + return topMarker.RectToTimeline(trackRect, state); + } + + public void CycleTop() + { + if (m_MarkerGUIs.Count < 2) + return; + + topMarker.onStartDrag -= OnDragTopMarker; + + var last = topMarker; + for (int i = 0; i < m_MarkerGUIs.Count; ++i) + { + var next = m_MarkerGUIs[i]; + m_MarkerGUIs[i] = last; + last = next; + } + + topMarker.zOrder = m_ZOrderProvider.Next(); + + topMarker.onStartDrag += OnDragTopMarker; + } + + void OnDragTopMarker() + { + m_ManipulatedMarker = topMarker; + m_ManipulatedMarker.onStartDrag -= OnDragTopMarker; + m_MarkerGUIs.RemoveAt(m_MarkerGUIs.Count - 1); + } + + void SortMarkers() + { + m_MarkerGUIs.Sort((lhs, rhs) => lhs.zOrder.CompareTo(rhs.zOrder)); + } + + void RegisterRect(WindowState state) + { + treeViewRect = topMarker.treeViewRect; + + if (Event.current.type == EventType.Repaint && !parent.locked) + state.spacePartitioner.AddBounds(this, rect); + } + + public static bool CanCycleMarkers() + { + if (!SelectionManager.SelectedMarkers().Any()) + return false; + + var cluster = PickerUtils.PickedLayerableOfType<TimelineMarkerClusterGUI>(); + + if (cluster == null) + return false; + + // Only cycle if the marker is selected and nothing else is selected + return cluster.topMarker.IsSelected() && SelectionManager.Count() == 1; + } + + public static void CycleMarkers() + { + var cluster = PickerUtils.PickedLayerableOfType<TimelineMarkerClusterGUI>(); + + if (cluster == null) + return; + + cluster.topMarker.Deselect(); + cluster.CycleTop(); + cluster.topMarker.Select(); + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineMarkerClusterGUI.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineMarkerClusterGUI.cs.meta new file mode 100644 index 0000000..692661f --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineMarkerClusterGUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2c03ae9aa36a4fd44a983831f44654be +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineMarkerGUI.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineMarkerGUI.cs new file mode 100644 index 0000000..5f054f7 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineMarkerGUI.cs @@ -0,0 +1,193 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Timeline; +using UnityObject = UnityEngine.Object; + +namespace UnityEditor.Timeline +{ + class TimelineMarkerGUI : TimelineItemGUI, ISnappable, IAttractable + { + public event Action onStartDrag; + + int m_ProjectedClipHash; + int m_MarkerHash; + bool m_Selectable; + + MarkerDrawOptions m_MarkerDrawOptions; + MarkerEditor m_Editor; + + IMarker marker { get; } + + bool selectable + { + get { return m_Selectable; } + } + + public double time + { + get { return marker.time; } + } + + public override double start + { + get { return time; } + } + + public override double end + { + get { return time; } + } + + public override void Select() + { + zOrder = zOrderProvider.Next(); + SelectionManager.Add(marker); + TimelineWindowViewPrefs.GetTrackViewModelData(parent.asset).markerTimeStamps[m_MarkerHash] = DateTime.UtcNow.Ticks; + } + + public override bool IsSelected() + { + return SelectionManager.Contains(marker); + } + + public override void Deselect() + { + SelectionManager.Remove(marker); + } + + public override ITimelineItem item + { + get { return ItemsUtils.ToItem(marker); } + } + + IZOrderProvider zOrderProvider { get; } + + public TimelineMarkerGUI(IMarker theMarker, IRowGUI parent, IZOrderProvider provider) : base(parent) + { + marker = theMarker; + m_Selectable = marker.GetType().IsSubclassOf(typeof(UnityObject)); + + m_MarkerHash = 0; + var o = marker as object; + if (!o.Equals(null)) + m_MarkerHash = o.GetHashCode(); + + zOrderProvider = provider; + zOrder = zOrderProvider.Next(); + ItemToItemGui.Add(marker, this); + m_Editor = CustomTimelineEditorCache.GetMarkerEditor(theMarker); + } + + int ComputeDirtyHash() + { + return time.GetHashCode(); + } + + static void DrawMarker(Rect drawRect, Type type, bool isSelected, bool isCollapsed, MarkerDrawOptions options) + { + if (Event.current.type == EventType.Repaint) + { + bool hasError = !string.IsNullOrEmpty(options.errorText); + + var style = StyleManager.UssStyleForType(type); + style.Draw(drawRect, GUIContent.none, false, false, !isCollapsed, isSelected); + + // case1141836: Use of GUI.Box instead of GUI.Label causes desync in UI controlID + if (hasError) + GUI.Label(drawRect, String.Empty, DirectorStyles.Instance.markerWarning); + + var tooltip = hasError ? options.errorText : options.tooltip; + if (!string.IsNullOrEmpty(tooltip) && drawRect.Contains(Event.current.mousePosition)) + { + GUIStyle.SetMouseTooltip(tooltip, drawRect); + } + } + } + + void UpdateDrawData() + { + if (Event.current.type == EventType.Layout) + { + try + { + m_MarkerDrawOptions = m_Editor.GetMarkerOptions(marker); + } + catch (Exception e) + { + Debug.LogException(e); + m_MarkerDrawOptions = CustomTimelineEditorCache.GetDefaultMarkerEditor().GetMarkerOptions(marker); + } + } + } + + public override void Draw(Rect trackRect, bool trackRectChanged, WindowState state) + { + UpdateDrawData(); + + // compute marker hash + var currentMarkerHash = ComputeDirtyHash(); + + // update the clip projected rectangle on the timeline + CalculateClipRectangle(trackRect, state, currentMarkerHash, trackRectChanged); + + var isSelected = selectable && SelectionManager.Contains(marker); + var showMarkers = parent.showMarkers; + + QueueOverlay(treeViewRect, isSelected, !showMarkers); + DrawMarker(treeViewRect, marker.GetType(), isSelected, !showMarkers, m_MarkerDrawOptions); + + if (Event.current.type == EventType.Repaint && showMarkers && !parent.locked) + state.spacePartitioner.AddBounds(this, rect); + } + + public void QueueOverlay(Rect rect, bool isSelected, bool isCollapsed) + { + if (Event.current.type == EventType.Repaint && m_Editor.supportsDrawOverlay) + { + rect = GUIClip.Unclip(rect); + TimelineWindow.instance.AddUserOverlay(marker, rect, m_Editor, isCollapsed, isSelected); + } + } + + public override void StartDrag() + { + if (onStartDrag != null) + onStartDrag.Invoke(); + } + + void CalculateClipRectangle(Rect trackRect, WindowState state, int projectedClipHash, bool trackRectChanged) + { + if (m_ProjectedClipHash == projectedClipHash && !trackRectChanged) + return; + + m_ProjectedClipHash = projectedClipHash; + treeViewRect = RectToTimeline(trackRect, state); + } + + public override Rect RectToTimeline(Rect trackRect, WindowState state) + { + var style = StyleManager.UssStyleForType(marker.GetType()); + var width = style.fixedWidth; + var height = style.fixedHeight; + var x = ((float)marker.time * state.timeAreaScale.x) + state.timeAreaTranslation.x + trackRect.xMin; + x -= 0.5f * width; + return new Rect(x, trackRect.y, width, height); + } + + public IEnumerable<Edge> SnappableEdgesFor(IAttractable attractable, ManipulateEdges manipulateEdges) + { + var edges = new List<Edge>(); + var attractableGUI = attractable as TimelineMarkerGUI; + var canAddEdges = !(attractableGUI != null && attractableGUI.parent == parent); + if (canAddEdges) + edges.Add(new Edge(time)); + return edges; + } + + public bool ShouldSnapTo(ISnappable snappable) + { + return snappable != this; + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineMarkerGUI.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineMarkerGUI.cs.meta new file mode 100644 index 0000000..ecfff45 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ItemGui/TimelineMarkerGUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8d34348f8b97a334291f5cf31adc5d67 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ManipulationsClips.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ManipulationsClips.cs new file mode 100644 index 0000000..9a51bc6 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ManipulationsClips.cs @@ -0,0 +1,77 @@ +using System.Linq; +using UnityEngine; +using UnityEngine.Timeline; + +namespace UnityEditor.Timeline +{ + class ItemActionShortcutManipulator : Manipulator + { + protected override bool ExecuteCommand(Event evt, WindowState state) + { + if (state.IsEditingASubItem()) + return false; + + var consumed = false; + var clips = SelectionManager.SelectedClips(); + foreach (var clip in clips) + consumed |= ItemAction<TimelineClip>.HandleShortcut(state, evt, clip); + + var markers = SelectionManager.SelectedMarkers(); + foreach (var marker in markers) + consumed |= ItemAction<IMarker>.HandleShortcut(state, evt, marker); + + return consumed; + } + } + + class DrillIntoClip : Manipulator + { + protected override bool DoubleClick(Event evt, WindowState state) + { + if (evt.button != 0) + return false; + + var guiClip = PickerUtils.PickedLayerableOfType<TimelineClipGUI>(); + + if (guiClip == null) + return false; + + if (!TimelineWindow.instance.state.editSequence.isReadOnly && (guiClip.clip.curves != null || guiClip.clip.animationClip != null)) + ItemAction<TimelineClip>.Invoke<EditClipInAnimationWindow>(state, guiClip.clip); + + if (guiClip.supportsSubTimelines) + ItemAction<TimelineClip>.Invoke<EditSubTimeline>(state, guiClip.clip); + + return true; + } + } + + class ContextMenuManipulator : Manipulator + { + protected override bool MouseDown(Event evt, WindowState state) + { + if (evt.button == 1) + ItemSelection.HandleSingleSelection(evt); + + return false; + } + + protected override bool ContextClick(Event evt, WindowState state) + { + if (evt.alt) + return false; + + var selectable = PickerUtils.PickedLayerableOfType<ISelectable>(); + + if (selectable != null && selectable.IsSelected()) + { + SequencerContextMenu.ShowItemContextMenu(evt.mousePosition, + SelectionManager.SelectedClips().ToArray(), + SelectionManager.SelectedMarkers().ToArray()); + return true; + } + + return false; + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ManipulationsClips.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ManipulationsClips.cs.meta new file mode 100644 index 0000000..c230e78 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ManipulationsClips.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b222f61a1f7253e4d8e8cc82bfde9e42 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ManipulationsTimeline.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ManipulationsTimeline.cs new file mode 100644 index 0000000..a3fcac8 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ManipulationsTimeline.cs @@ -0,0 +1,287 @@ +using System; +using System.Linq; +using UnityEditor.ShortcutManagement; +using UnityEngine; + +namespace UnityEditor.Timeline +{ + class TimelinePanManipulator : Manipulator + { + const float k_MaxPanSpeed = 50.0f; + bool m_Active; + + protected override bool MouseDown(Event evt, WindowState state) + { + if ((evt.button == 2 && evt.modifiers == EventModifiers.None) || + (evt.button == 0 && evt.modifiers == EventModifiers.Alt)) + { + TimelineCursors.SetCursor(TimelineCursors.CursorType.Pan); + + m_Active = true; + return true; + } + + return false; + } + + protected override bool MouseUp(Event evt, WindowState state) + { + if (m_Active) + { + TimelineCursors.ClearCursor(); + state.editorWindow.Repaint(); + } + + return false; + } + + protected override bool MouseDrag(Event evt, WindowState state) + { + // Note: Do not rely on evt.button here as some 3rd party automation + // software does not properly set the button data during drag. + + if (!m_Active) + return false; + + return Pan(evt, state); + } + + protected override bool MouseWheel(Event evt, WindowState state) + { + if (Math.Abs(evt.delta.x) < 1e-5 || Math.Abs(evt.delta.x) <= Math.Abs(evt.delta.y)) + return false; + + TimelineZoomManipulator.InvalidateWheelZoom(); + + var panEvent = new Event(evt); + panEvent.delta = new Vector2(panEvent.delta.x * k_MaxPanSpeed * -1.0f, 0.0f); + + return Pan(panEvent, state); + } + + static bool Pan(Event evt, WindowState state) + { + var cursorRect = TimelineWindow.instance.sequenceContentRect; + cursorRect.xMax = TimelineWindow.instance.position.xMax; + cursorRect.yMax = TimelineWindow.instance.position.yMax; + + if (state.GetWindow() != null && state.GetWindow().treeView != null) + { + var scroll = state.GetWindow().treeView.scrollPosition; + scroll.y -= evt.delta.y; + state.GetWindow().treeView.scrollPosition = scroll; + state.OffsetTimeArea((int)evt.delta.x); + return true; + } + + return false; + } + } + + + class TimelineZoomManipulator : Manipulator + { + Vector2 m_MouseDownPos = Vector2.zero; + Vector2 m_InitialShownRange = Vector2.zero; + float m_FocalTime; + float m_LastMouseMoveX = -1; + float m_ZoomFactor = 1; + bool m_WheelUsedLast; + + TimelineZoomManipulator() {} + + public static readonly TimelineZoomManipulator Instance = new TimelineZoomManipulator(); + + internal void DoZoom(float zoomFactor, WindowState state) + { + var refRange = state.timeAreaShownRange; + DoZoom(zoomFactor, state, refRange, (refRange.x + refRange.y) / 2); + // Force resetting the reference zoom after a Framing operation + InvalidateWheelZoom(); + } + + static void DoZoom(float zoomFactor, WindowState state, Vector2 refRange, float focalTime) + { + const float kMinRange = 0.05f; // matches zoomable area. + + var s = zoomFactor; + if (s <= 0) return; + + var t = Mathf.Max(focalTime, refRange.x); + var x = (refRange.x + t * (s - 1)) / s; + var y = (refRange.y + t * (s - 1)) / s; + + // don't set it if we reach the limit or panning happens + if (Math.Abs(x - y) > kMinRange) + { + // Zoomable area does not protect 100% against crazy values + state.SetTimeAreaShownRange( + Math.Max(x, -WindowConstants.timeAreaShownRangePadding), + Math.Min(y, WindowState.kMaxShownTime)); + } + } + + internal static void InvalidateWheelZoom() + { + Instance.m_WheelUsedLast = false; + } + + protected override bool MouseDown(Event evt, WindowState state) + { + m_MouseDownPos = evt.mousePosition; + m_FocalTime = state.PixelToTime(m_MouseDownPos.x); + m_InitialShownRange = state.timeAreaShownRange; + return false; + } + + protected override bool MouseWheel(Event evt, WindowState state) + { + if (Math.Abs(evt.delta.y) < 1e-5) + return false; + + var zoomRect = TimelineWindow.instance.sequenceContentRect; + zoomRect.yMax += TimelineWindow.instance.horizontalScrollbarHeight; + + if (!zoomRect.Contains(evt.mousePosition)) + return false; + + if (!m_WheelUsedLast || Mathf.Abs(m_LastMouseMoveX - evt.mousePosition.x) > 1.0f) + { + m_LastMouseMoveX = evt.mousePosition.x; + m_FocalTime = state.PixelToTime(m_LastMouseMoveX); + m_InitialShownRange = state.timeAreaShownRange; + m_ZoomFactor = 1; + } + + var newZoom = m_ZoomFactor * (-evt.delta.y * 0.02f + 1); + newZoom = Mathf.Clamp(newZoom, 1e-7f, 1e7f); + + var lastRange = state.timeAreaShownRange; + DoZoom(newZoom, state, m_InitialShownRange, m_FocalTime); + + // if we hit a limit, don't change the zoom + // this prevents accumulating when zoom doesn't change + if (lastRange != state.timeAreaShownRange) + m_ZoomFactor = newZoom; + + m_WheelUsedLast = true; + return true; + } + + protected override bool MouseDrag(Event evt, WindowState state) + { + // Fast zoom... + if (evt.modifiers != EventModifiers.Alt || evt.button != 1) return false; + + var mouseMoveLength = Event.current.mousePosition - m_MouseDownPos; + var delta = Math.Abs(mouseMoveLength.x) > Math.Abs(mouseMoveLength.y) + ? mouseMoveLength.x + : -mouseMoveLength.y; + m_ZoomFactor = PixelToZoom(delta); + DoZoom(m_ZoomFactor, state, m_InitialShownRange, m_FocalTime); + + m_WheelUsedLast = false; + return true; + } + + static float PixelToZoom(float x) + { + const float pixel2Zoom = 1 / 300.0f; + x *= pixel2Zoom; + if (x < -0.75) + { + // Rational function that behaves like 1+x on [-0.75,inf) and asymptotically reaches zero on (-inf,-0.75] + // The coefficients were obtained by the following constraints: + //1) f(-0.75) = 0.25 + //2) f'(-0.75) = 1 C1 continuity + //3) f(-3) = 0.001 (asymptotically zero) + return 1 / (98.6667f + 268.444f * x + 189.63f * x * x); + } + return 1 + x; + } + } + + class TimelineShortcutManipulator : Manipulator + { + protected override bool ValidateCommand(Event evt, WindowState state) + { + return evt.commandName == EventCommandNames.Copy || + evt.commandName == EventCommandNames.Paste || + evt.commandName == EventCommandNames.Duplicate || + evt.commandName == EventCommandNames.SelectAll || + evt.commandName == EventCommandNames.Delete || + evt.commandName == EventCommandNames.SoftDelete || + evt.commandName == EventCommandNames.FrameSelected; + } + + protected override bool ExecuteCommand(Event evt, WindowState state) + { + if (state.IsCurrentEditingASequencerTextField()) + return false; + + if (evt.commandName == EventCommandNames.SelectAll) + { + TimelineAction.Invoke<SelectAllAction>(state); + return true; + } + + if (evt.commandName == EventCommandNames.SoftDelete) + { + TimelineAction.Invoke<DeleteAction>(state); + return true; + } + + if (evt.commandName == EventCommandNames.FrameSelected) + { + TimelineAction.Invoke<FrameSelectedAction>(state); + return true; + } + + return TimelineAction.HandleShortcut(state, evt); + } + } + + class InlineCurvesShortcutManipulator : Manipulator + { + protected override bool ExecuteCommand(Event evt, WindowState state) + { + if (state.IsCurrentEditingASequencerTextField()) + return false; + + var inlineCurveEditor = SelectionManager.GetCurrentInlineEditorCurve(); + if (inlineCurveEditor == null || !inlineCurveEditor.inlineCurvesSelected) + return false; + + if (evt.commandName != EventCommandNames.FrameSelected) + return false; + + TimelineAction.Invoke<FrameSelectedAction>(state); + return true; + } + + // CurveEditor uses an hardcoded shortcut to execute the FrameAll action, preventing the ShortcutManager from + // ever picking it up. We have to hijack it to ensure our code is being run when framing inline curves. + protected override bool KeyDown(Event evt, WindowState state) + { + var inlineCurveEditor = SelectionManager.GetCurrentInlineEditorCurve(); + if (inlineCurveEditor == null || !inlineCurveEditor.inlineCurvesSelected) + return false; + + // Not conflicting with the hardcoded value + if (evt.keyCode != KeyCode.A) + return false; + + var combination = ShortcutManager.instance.GetShortcutBinding(Shortcuts.Timeline.frameAll) + .keyCombinationSequence.ToList(); + + var shortcutCombination = combination.First(); + var currentCombination = KeyCombination.FromKeyboardInput(evt); + + // User is not actually pressing the correct key combination for FrameAll + if (combination.Count == 1 && shortcutCombination.Equals(currentCombination)) + TimelineAction.Invoke<FrameAllAction>(state); + + return true; + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ManipulationsTimeline.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ManipulationsTimeline.cs.meta new file mode 100644 index 0000000..5612ca4 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ManipulationsTimeline.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d029640460cf8ff47bbbfe69f49ddf29 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ManipulationsTracks.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ManipulationsTracks.cs new file mode 100644 index 0000000..3bd2a1c --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ManipulationsTracks.cs @@ -0,0 +1,103 @@ +using System.Linq; +using UnityEngine; +using UnityEngine.Timeline; + +namespace UnityEditor.Timeline +{ + class InlineCurveResize : Manipulator + { + bool m_Captured; + + float m_CapturedHeight; + float m_CaptureMouseYPos; + + InlineCurveResizeHandle m_Target; + + protected override bool MouseDown(Event evt, WindowState state) + { + m_Target = PickerUtils.PickedInlineCurveResizer(); + if (m_Target == null) + return false; + + m_Captured = true; + m_CapturedHeight = TimelineWindowViewPrefs.GetInlineCurveHeight(m_Target.trackGUI.track); + m_CaptureMouseYPos = GUIUtility.GUIToScreenPoint(Event.current.mousePosition).y; + state.AddCaptured(this); + + return true; + } + + protected override bool MouseDrag(Event evt, WindowState state) + { + if (!m_Captured || m_Target == null) + return false; + + var trackGUI = m_Target.trackGUI; + + float inlineTrackHeight = m_CapturedHeight + + (GUIUtility.GUIToScreenPoint(Event.current.mousePosition).y - m_CaptureMouseYPos); + + TimelineWindowViewPrefs.SetInlineCurveHeight(trackGUI.track, Mathf.Max(inlineTrackHeight, 60.0f)); + + state.GetWindow().treeView.CalculateRowRects(); + + return true; + } + + protected override bool MouseUp(Event evt, WindowState state) + { + if (!m_Captured) + return false; + + state.RemoveCaptured(this); + m_Captured = false; + + return true; + } + } + + class TrackDoubleClick : Manipulator + { + protected override bool DoubleClick(Event evt, WindowState state) + { + if (evt.button != 0) + return false; + + var trackGUI = PickerUtils.PickedTrackBaseGUI(); + + if (trackGUI == null) + return false; + + // Double-click is only available for AnimationTracks: it conflicts with selection mechanics on other tracks + if ((trackGUI.track as AnimationTrack) == null) + return false; + + return EditTrackInAnimationWindow.Do(state, trackGUI.track); + } + } + + class TrackShortcutManipulator : Manipulator + { + protected override bool ExecuteCommand(Event evt, WindowState state) + { + if (state.IsCurrentEditingASequencerTextField()) + return false; + + var tracks = SelectionManager.SelectedTracks().ToList(); + + var itemGUIs = SelectionManager.SelectedClipGUI(); + + foreach (var itemGUI in itemGUIs) + { + var trackGUI = itemGUI.parent == null ? null : itemGUI.parent as TimelineTrackBaseGUI; + if (trackGUI == null) + continue; + + if (!tracks.Contains(trackGUI.track)) + tracks.Add(trackGUI.track); + } + + return TrackAction.HandleShortcut(state, evt, tracks.ToArray()); + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ManipulationsTracks.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ManipulationsTracks.cs.meta new file mode 100644 index 0000000..cfbbb31 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/ManipulationsTracks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5151708d47edd4344ba8e7bd469d966e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Manipulator.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Manipulator.cs new file mode 100644 index 0000000..d0aac15 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Manipulator.cs @@ -0,0 +1,91 @@ +using UnityEngine; + +namespace UnityEditor.Timeline +{ + abstract class Manipulator + { + int m_Id; + + protected virtual bool MouseDown(Event evt, WindowState state) { return false; } + protected virtual bool MouseDrag(Event evt, WindowState state) { return false; } + protected virtual bool MouseWheel(Event evt, WindowState state) { return false; } + protected virtual bool MouseUp(Event evt, WindowState state) { return false; } + protected virtual bool DoubleClick(Event evt, WindowState state) { return false; } + protected virtual bool KeyDown(Event evt, WindowState state) { return false; } + protected virtual bool KeyUp(Event evt, WindowState state) { return false; } + protected virtual bool ContextClick(Event evt, WindowState state) { return false; } + protected virtual bool ValidateCommand(Event evt, WindowState state) { return false; } + protected virtual bool ExecuteCommand(Event evt, WindowState state) { return false; } + + public virtual void Overlay(Event evt, WindowState state) {} + + public bool HandleEvent(WindowState state) + { + if (m_Id == 0) + m_Id = GUIUtility.GetPermanentControlID(); + + bool isHandled = false; + var evt = Event.current; + + switch (evt.GetTypeForControl(m_Id)) + { + case EventType.ScrollWheel: + isHandled = MouseWheel(evt, state); + break; + + case EventType.MouseUp: + { + if (GUIUtility.hotControl == m_Id) + { + isHandled = MouseUp(evt, state); + + GUIUtility.hotControl = 0; + evt.Use(); + } + } + break; + + case EventType.MouseDown: + { + isHandled = evt.clickCount < 2 ? MouseDown(evt, state) : DoubleClick(evt, state); + + if (isHandled) + GUIUtility.hotControl = m_Id; + } + break; + + case EventType.MouseDrag: + { + if (GUIUtility.hotControl == m_Id) + isHandled = MouseDrag(evt, state); + } + break; + + case EventType.KeyDown: + isHandled = KeyDown(evt, state); + break; + + case EventType.KeyUp: + isHandled = KeyUp(evt, state); + break; + + case EventType.ContextClick: + isHandled = ContextClick(evt, state); + break; + + case EventType.ValidateCommand: + isHandled = ValidateCommand(evt, state); + break; + + case EventType.ExecuteCommand: + isHandled = ExecuteCommand(evt, state); + break; + } + + if (isHandled) + evt.Use(); + + return isHandled; + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Manipulator.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Manipulator.cs.meta new file mode 100644 index 0000000..9676bc0 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Manipulator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2bf68308f6cdff141ab98cd34174e4f1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/PickerUtils.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/PickerUtils.cs new file mode 100644 index 0000000..50d0382 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/PickerUtils.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace UnityEditor.Timeline +{ + static class PickerUtils + { + public static List<object> pickedElements { get; private set; } + + public static void DoPick(WindowState state, Vector2 mousePosition) + { + if (state.GetWindow().sequenceContentRect.Contains(mousePosition)) + { + pickedElements = state.spacePartitioner.GetItemsAtPosition<object>(mousePosition).ToList(); + } + else + { + if (pickedElements != null) + pickedElements.Clear(); + else + pickedElements = new List<object>(); + } + } + + public static T PickedLayerableOfType<T>() where T : class, ILayerable + { + return pickedElements.OfType<ILayerable>().OrderBy(x => x.zOrder).LastOrDefault() as T; + } + + public static InlineCurveResizeHandle PickedInlineCurveResizer() + { + return pickedElements.FirstOrDefault(e => e is InlineCurveResizeHandle) as InlineCurveResizeHandle; + } + + public static TimelineTrackBaseGUI PickedTrackBaseGUI() + { + return pickedElements.FirstOrDefault(e => e is TimelineTrackBaseGUI) as TimelineTrackBaseGUI; + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/PickerUtils.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/PickerUtils.cs.meta new file mode 100644 index 0000000..829ef0a --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/PickerUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 16950289b516d6747868e0f7bf7b37a0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Snapping.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Snapping.meta new file mode 100644 index 0000000..bb822d5 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Snapping.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 06ae1baf5524b314fa65b173b9eca869 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Snapping/IAttractable.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Snapping/IAttractable.cs new file mode 100644 index 0000000..772809d --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Snapping/IAttractable.cs @@ -0,0 +1,21 @@ +namespace UnityEditor.Timeline +{ + enum AttractedEdge + { + None, + Left, + Right + } + + interface IAttractable + { + bool ShouldSnapTo(ISnappable snappable); + double start { get; } + double end { get; } + } + + interface IAttractionHandler + { + void OnAttractedEdge(IAttractable attractable, ManipulateEdges manipulateEdges, AttractedEdge edge, double time); + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Snapping/IAttractable.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Snapping/IAttractable.cs.meta new file mode 100644 index 0000000..570a8b9 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Snapping/IAttractable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6bfec54ce89b0b642a65d44def023b99 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Snapping/ISnappable.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Snapping/ISnappable.cs new file mode 100644 index 0000000..52a17a6 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Snapping/ISnappable.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace UnityEditor.Timeline +{ + struct Edge + { + public double time { get; set; } + + public bool showSnapHint { get; set; } + + public Edge(double edgeTime, bool snapHint = true) : this() + { + time = edgeTime; + showSnapHint = snapHint; + } + } + + interface ISnappable + { + IEnumerable<Edge> SnappableEdgesFor(IAttractable attractable, ManipulateEdges manipulateEdges); + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Snapping/ISnappable.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Snapping/ISnappable.cs.meta new file mode 100644 index 0000000..dccc3e2 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Snapping/ISnappable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3664a2ade19bb7848a4d2c96ac9148e3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Snapping/SnapEngine.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Snapping/SnapEngine.cs new file mode 100644 index 0000000..8aca3d4 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Snapping/SnapEngine.cs @@ -0,0 +1,288 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.Timeline; + +namespace UnityEditor.Timeline +{ + enum ManipulateEdges + { + Left, + Right, + Both + } + + class SnapEngine + { + static readonly float k_MagnetInfluenceInPixels = 10.0f; + + class SnapInfo + { + public double time { get; set; } + + public bool showSnapHint { get; set; } + + public bool IsInInfluenceZone(double currentTime, WindowState state) + { + var pos = state.TimeToPixel(currentTime); + var magnetPos = state.TimeToPixel(time); + + return Math.Abs(pos - magnetPos) < k_MagnetInfluenceInPixels; + } + } + + struct TimeBoundaries + { + public TimeBoundaries(double l, double r) + { + left = l; + right = r; + } + + public readonly double left; + public readonly double right; + + public TimeBoundaries Translate(double d) + { + return new TimeBoundaries(left + d, right + d); + } + } + + public static bool displayDebugLayout; + + readonly IAttractable m_Attractable; + readonly IAttractionHandler m_AttractionHandler; + readonly ManipulateEdges m_ManipulateEdges; + + readonly WindowState m_State; + + double m_GrabbedTime; + TimeBoundaries m_GrabbedTimes; + + TimeBoundaries m_CurrentTimes; + + readonly List<SnapInfo> m_Magnets = new List<SnapInfo>(); + + bool m_SnapEnabled; + + public SnapEngine(IAttractable attractable, IAttractionHandler attractionHandler, ManipulateEdges manipulateEdges, WindowState state, + Vector2 mousePosition, IEnumerable<ISnappable> snappables = null) + { + m_Attractable = attractable; + m_ManipulateEdges = manipulateEdges; + + m_AttractionHandler = attractionHandler; + m_State = state; + + m_CurrentTimes = m_GrabbedTimes = new TimeBoundaries(m_Attractable.start, m_Attractable.end); + m_GrabbedTime = m_State.PixelToTime(mousePosition.x); + + // Add Time zero as Magnet + AddMagnet(0.0, true, state); + + // Add current Time as Magnet + // case1157280 only add current time as magnet if visible + if (TimelineWindow.instance.currentMode.ShouldShowTimeCursor(m_State)) + AddMagnet(state.editSequence.time, true, state); + + if (state.IsEditingASubTimeline()) + { + // Add start and end of evaluable range as Magnets + // This includes the case where the master timeline has a fixed length + var range = state.editSequence.GetEvaluableRange(); + AddMagnet(range.start, true, state); + AddMagnet(range.end, true, state); + } + else if (state.masterSequence.asset.durationMode == TimelineAsset.DurationMode.FixedLength) + { + // Add end sequence Time as Magnet + AddMagnet(state.masterSequence.asset.duration, true, state); + } + + + if (snappables == null) + snappables = GetVisibleSnappables(m_State); + + foreach (var snappable in snappables) + { + if (!attractable.ShouldSnapTo(snappable)) + continue; + + var edges = snappable.SnappableEdgesFor(attractable, manipulateEdges); + foreach (var edge in edges) + AddMagnet(edge.time, edge.showSnapHint, state); + } + } + + public static IEnumerable<ISnappable> GetVisibleSnappables(WindowState state) + { + Rect rect = TimelineWindow.instance.state.timeAreaRect; + rect.height = float.MaxValue; + return state.spacePartitioner.GetItemsInArea<ISnappable>(rect).ToArray(); + } + + void AddMagnet(double magnetTime, bool showSnapHint, WindowState state) + { + var magnet = m_Magnets.FirstOrDefault(m => m.time.Equals(magnetTime)); + if (magnet == null) + { + if (IsMagnetInShownArea(magnetTime, state)) + m_Magnets.Add(new SnapInfo { time = magnetTime, showSnapHint = showSnapHint }); + } + else + { + magnet.showSnapHint |= showSnapHint; + } + } + + static bool IsMagnetInShownArea(double time, WindowState state) + { + var shownArea = state.timeAreaShownRange; + return time >= shownArea.x && time <= shownArea.y; + } + + SnapInfo GetMagnetAt(double time) + { + return m_Magnets.FirstOrDefault(m => m.time.Equals(time)); + } + + SnapInfo ClosestMagnet(double time) + { + SnapInfo candidate = null; + var min = double.MaxValue; + foreach (var magnetInfo in m_Magnets) + { + var m = Math.Abs(magnetInfo.time - time); + if (m < min) + { + candidate = magnetInfo; + min = m; + } + } + + if (candidate != null && candidate.IsInInfluenceZone(time, m_State)) + return candidate; + + return null; + } + + public void Snap(Vector2 currentMousePosition, EventModifiers modifiers) + { + var d = m_State.PixelToTime(currentMousePosition.x) - m_GrabbedTime; + + m_CurrentTimes = m_GrabbedTimes.Translate(d); + + bool isLeft = m_ManipulateEdges == ManipulateEdges.Left || m_ManipulateEdges == ManipulateEdges.Both; + bool isRight = m_ManipulateEdges == ManipulateEdges.Right || m_ManipulateEdges == ManipulateEdges.Both; + + bool attracted = false; + + m_SnapEnabled = modifiers == ManipulatorsUtils.actionModifier ? !m_State.edgeSnaps : m_State.edgeSnaps; + + if (m_SnapEnabled) + { + SnapInfo leftActiveMagnet = null; + SnapInfo rightActiveMagnet = null; + + if (isLeft) + leftActiveMagnet = ClosestMagnet(m_CurrentTimes.left); + + if (isRight) + rightActiveMagnet = ClosestMagnet(m_CurrentTimes.right); + + if (leftActiveMagnet != null || rightActiveMagnet != null) + { + attracted = true; + + bool leftAttraction = false; + + if (rightActiveMagnet == null) + { + // Attracted by a left magnet only. + leftAttraction = true; + } + else + { + if (leftActiveMagnet != null) + { + // Attracted by both magnets, choose the closest one. + var leftDistance = Math.Abs(leftActiveMagnet.time - m_CurrentTimes.left); + var rightDistance = Math.Abs(rightActiveMagnet.time - m_CurrentTimes.right); + + leftAttraction = leftDistance <= rightDistance; + } + // else, Attracted by right magnet only + } + + if (leftAttraction) + { + m_AttractionHandler.OnAttractedEdge(m_Attractable, m_ManipulateEdges, AttractedEdge.Left, leftActiveMagnet.time); + } + else + { + m_AttractionHandler.OnAttractedEdge(m_Attractable, m_ManipulateEdges, AttractedEdge.Right, rightActiveMagnet.time); + } + } + } + + if (!attracted) + { + var time = isLeft ? m_CurrentTimes.left : m_CurrentTimes.right; + + time = m_State.SnapToFrameIfRequired(time); + + m_AttractionHandler.OnAttractedEdge(m_Attractable, m_ManipulateEdges, AttractedEdge.None, time); + } + } + + public void OnGUI(bool showLeft = true, bool showRight = true) + { + if (displayDebugLayout) + { + // Display Magnet influence zone + foreach (var m in m_Magnets) + { + var window = TimelineWindow.instance; + var rect = new Rect(m_State.TimeToPixel(m.time) - k_MagnetInfluenceInPixels, window.state.timeAreaRect.yMax, 2f * k_MagnetInfluenceInPixels, m_State.windowHeight); + EditorGUI.DrawRect(rect, new Color(1f, 0f, 0f, 0.4f)); + } + + // Display Cursor position + var mousePos = Event.current.mousePosition; + var time = m_State.PixelToTime(mousePos.x); + var p = new Vector2(m_State.TimeToPixel(time), TimelineWindow.instance.state.timeAreaRect.yMax); + var s = new Vector2(1f, m_State.windowHeight); + EditorGUI.DrawRect(new Rect(p, s), Color.blue); + + p = new Vector2(m_State.TimeToPixel(m_GrabbedTime), TimelineWindow.instance.state.timeAreaRect.yMax); + s = new Vector2(1f, m_State.windowHeight); + EditorGUI.DrawRect(new Rect(p, s), Color.red); + + p = new Vector2(m_State.TimeToPixel(m_CurrentTimes.left), TimelineWindow.instance.state.timeAreaRect.yMax); + s = new Vector2(1f, m_State.windowHeight); + EditorGUI.DrawRect(new Rect(p, s), Color.yellow); + + p = new Vector2(m_State.TimeToPixel(m_CurrentTimes.right), TimelineWindow.instance.state.timeAreaRect.yMax); + EditorGUI.DrawRect(new Rect(p, s), Color.yellow); + } + + if (m_SnapEnabled) + { + if (showLeft) + DrawMagnetLineAt(m_Attractable.start); + + if (showRight) + DrawMagnetLineAt(m_Attractable.end); + } + } + + void DrawMagnetLineAt(double time) + { + var magnet = GetMagnetAt(time); + + if (magnet != null && magnet.showSnapHint) + Graphics.DrawLineAtTime(m_State, magnet.time, Color.white); + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Snapping/SnapEngine.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Snapping/SnapEngine.cs.meta new file mode 100644 index 0000000..a2ce57d --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/Snapping/SnapEngine.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 48967a2d5427ac2489cc0ea61da5f1a0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineClipHandle.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineClipHandle.cs new file mode 100644 index 0000000..6bd2238 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineClipHandle.cs @@ -0,0 +1,53 @@ +using UnityEngine; + +namespace UnityEditor.Timeline +{ + class TimelineClipHandle : ILayerable + { + Rect m_Rect; + readonly TimelineClipGUI m_ClipGUI; + readonly TrimEdge m_TrimDirection; + readonly LayerZOrder m_ZOrder; + + public Rect boundingRect + { + get { return m_ClipGUI.parent.ToWindowSpace(m_Rect); } + } + + public TrimEdge trimDirection + { + get { return m_TrimDirection; } + } + + public TimelineClipGUI clipGUI + { + get { return m_ClipGUI; } + } + + public LayerZOrder zOrder + { + get { return m_ZOrder; } + } + + public TimelineClipHandle(TimelineClipGUI theClipGUI, TrimEdge trimDirection) + { + m_TrimDirection = trimDirection; + m_ClipGUI = theClipGUI; + m_ZOrder = theClipGUI.zOrder.ChangeLayer(Layer.ClipHandles); + } + + public void Draw(Rect clientRect, float width, WindowState state) + { + var handleRect = clientRect; + handleRect.width = width; + + if (m_TrimDirection == TrimEdge.End) + handleRect.x = clientRect.xMax - width; + + m_Rect = handleRect; + if (!TimelineWindow.instance.state.editSequence.isReadOnly) + EditorGUIUtility.AddCursorRect(handleRect, MouseCursor.SplitResizeLeftRight); + state.spacePartitioner.AddBounds(this, boundingRect); + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineClipHandle.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineClipHandle.cs.meta new file mode 100644 index 0000000..df0e041 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineClipHandle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3e29cdd1646803545b8fb18908666ec4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineClipUnion.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineClipUnion.cs new file mode 100644 index 0000000..4288992 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineClipUnion.cs @@ -0,0 +1,126 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEditorInternal; +using UnityEngine; + +namespace UnityEditor.Timeline +{ + class TimelineClipUnion + { + List<TimelineClipGUI> m_Members = new List<TimelineClipGUI>(); + Rect m_BoundingRect; + Rect m_Union; + double m_Start; + double m_Duration; + bool m_InitUnionRect = true; + + void Add(TimelineClipGUI clip) + { + m_Members.Add(clip); + if (m_Members.Count == 1) + { + m_BoundingRect = clip.clippedRect; + } + else + { + m_BoundingRect = Encompass(m_BoundingRect, clip.rect); + } + } + + public void Draw(Rect parentRect, WindowState state) + { + if (m_InitUnionRect) + { + m_Start = m_Members.OrderBy(c => c.clip.start).First().clip.start; + m_Duration = m_Members.Sum(c => c.clip.duration); + m_InitUnionRect = false; + } + + m_Union = new Rect((float)(m_Start) * state.timeAreaScale.x, 0, (float)m_Duration * state.timeAreaScale.x, 0); + + // transform clipRect into pixel-space + m_Union.xMin += state.timeAreaTranslation.x + parentRect.x; + m_Union.xMax += state.timeAreaTranslation.x + parentRect.x; + m_Union.y = parentRect.y + 4.0f; + m_Union.height = parentRect.height - 8.0f; + + // calculate clipped rect + if (m_Union.x < parentRect.xMin) + { + var overflow = parentRect.xMin - m_Union.x; + m_Union.x = parentRect.xMin; + m_Union.width -= overflow; + } + + // bail out if completely clipped + if (m_Union.xMax < parentRect.xMin) + return; + if (m_Union.xMin > parentRect.xMax) + return; + + EditorGUI.DrawRect(m_Union, DirectorStyles.Instance.customSkin.colorClipUnion); + } + + public static List<TimelineClipUnion> Build(List<TimelineClipGUI> clips) + { + var unions = new List<TimelineClipUnion>(); + if (clips == null) + return unions; + + TimelineClipUnion currentUnion = null; + foreach (var c in clips) + { + if (currentUnion == null) + { + currentUnion = new TimelineClipUnion(); + currentUnion.Add(c); + unions.Add(currentUnion); + } + else + { + Rect result; + if (Intersection(c.rect, currentUnion.m_BoundingRect, out result)) + { + currentUnion.Add(c); + } + else + { + currentUnion = new TimelineClipUnion(); + currentUnion.Add(c); + unions.Add(currentUnion); + } + } + } + + return unions; + } + + public static Rect Encompass(Rect a, Rect b) + { + Rect newRect = a; + newRect.xMin = Mathf.Min(a.xMin, b.xMin); + newRect.yMin = Mathf.Min(a.yMin, b.yMin); + newRect.xMax = Mathf.Max(a.xMax, b.xMax); + newRect.yMax = Mathf.Max(a.yMax, b.yMax); + return newRect; + } + + public static bool Intersection(Rect r1, Rect r2, out Rect intersection) + { + if (!r1.Overlaps(r2) && !r2.Overlaps(r1)) + { + intersection = new Rect(0, 0, 0, 0); + return false; + } + + float left = Mathf.Max(r1.xMin, r2.xMin); + float top = Mathf.Max(r1.yMin, r2.yMin); + + float right = Mathf.Min(r1.xMax, r2.xMax); + float bottom = Mathf.Min(r1.yMax, r2.yMax); + + intersection = new Rect(left, top, right - left, bottom - top); + return true; + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineClipUnion.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineClipUnion.cs.meta new file mode 100644 index 0000000..86b5654 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineClipUnion.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dc99dfeec967f2644b25dd6bace59b7e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineDataSource.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineDataSource.cs new file mode 100644 index 0000000..2337392 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineDataSource.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor.IMGUI.Controls; +using UnityEngine; +using UnityEngine.Playables; +using UnityEngine.Timeline; + +namespace UnityEditor.Timeline +{ + class TimelineDataSource : TreeViewDataSource + { + readonly TimelineWindow m_TimelineWindow; + readonly TimelineTreeViewGUI m_ParentGUI; + + public List<TimelineTrackBaseGUI> allTrackGuis { get; private set; } + + TreeViewItem treeroot + { + get { return m_RootItem; } + } + + public TimelineDataSource(TimelineTreeViewGUI parentGUI, TreeViewController treeView, TimelineWindow sequencerWindow) + : base(treeView) + { + m_TreeView.useExpansionAnimation = false; + m_TimelineWindow = sequencerWindow; + m_ParentGUI = parentGUI; + FetchData(); + } + + public override bool IsExpanded(TreeViewItem item) + { + if (!IsExpandable(item)) + return true; + + return IsExpanded(item.id); + } + + public override bool IsExpandable(TreeViewItem item) + { + var expandable = false; + + var track = item as TimelineTrackBaseGUI; + + if (track != null) + expandable = track.expandable; + + return expandable && item.hasChildren; + } + + public sealed override void FetchData() + { + // create root item + m_RootItem = new TimelineGroupGUI(m_TreeView, m_ParentGUI, 1, 0, null, "root", null, true); + + var tree = new Dictionary<TrackAsset, TimelineTrackBaseGUI>(); + + var filteredView = m_TimelineWindow.state.editSequence.asset.trackObjects; + allTrackGuis = new List<TimelineTrackBaseGUI>(filteredView.Count()); + + foreach (var t in filteredView) + { + CreateItem(t, ref tree, filteredView.OfType<TrackAsset>(), m_RootItem); + } + + m_NeedRefreshRows = true; + + SetExpanded(m_RootItem, true); + } + + TimelineTrackBaseGUI CreateItem(ScriptableObject scriptableObject, ref Dictionary<TrackAsset, TimelineTrackBaseGUI> tree, IEnumerable<TrackAsset> selectedRows, TreeViewItem parentTreeViewItem) + { + // if a script doesn't load correctly, the trackAsset will be NULL, but the scriptableObject __should_ be intact (but == null will be true) + var trackAsset = scriptableObject as TrackAsset; + + if (tree == null) + throw new ArgumentNullException("tree"); + + if (selectedRows == null) + throw new ArgumentNullException("selectedRows"); + + if (trackAsset != null && tree.ContainsKey(trackAsset)) + return tree[trackAsset]; + + TimelineTrackBaseGUI parentItem = parentTreeViewItem as TimelineTrackBaseGUI; + + // should we create the parent? + TrackAsset parentTrack = trackAsset != null ? (trackAsset.parent as TrackAsset) : null; + if (trackAsset != null && parentTrack != null && selectedRows.Contains(parentTrack)) + { + parentItem = CreateItem(parentTrack, ref tree, selectedRows, parentTreeViewItem); + } + + int theDepth = -1; + if (parentItem != null) + theDepth = parentItem.depth; + theDepth++; + + TimelineTrackBaseGUI newItem; + if (trackAsset == null) + { + PlayableAsset parent = m_TimelineWindow.state.editSequence.asset; + if (parentItem != null && parentItem.track != null) + parent = parentItem.track; + + newItem = new TimelineTrackErrorGUI(m_TreeView, m_ParentGUI, 0, theDepth, parentItem, "ERROR", scriptableObject, parent); + } + else if (trackAsset.GetType() != typeof(GroupTrack)) + { + newItem = new TimelineTrackGUI(m_TreeView, m_ParentGUI, trackAsset.GetInstanceID(), theDepth, parentItem, trackAsset.name, trackAsset); + } + else + { + newItem = new TimelineGroupGUI(m_TreeView, m_ParentGUI, trackAsset.GetInstanceID(), theDepth, parentItem, trackAsset.name, trackAsset, false); + } + + allTrackGuis.Add(newItem); + + if (parentItem != null) + { + if (parentItem.children == null) + parentItem.children = new List<TreeViewItem>(); + parentItem.children.Add(newItem); + } + else + { + m_RootItem = newItem; + SetExpanded(m_RootItem, true); + } + + if (trackAsset != null) + tree[trackAsset] = newItem; + + var actorAsAnimTrack = newItem.track as AnimationTrack; + bool isEditableInfiniteClip = actorAsAnimTrack != null && actorAsAnimTrack.ShouldShowInfiniteClipEditor(); + if (isEditableInfiniteClip) + { + if (newItem.children == null) + newItem.children = new List<TreeViewItem>(); + } + else if (trackAsset != null) + { + // check if clips on this track have animation, if so we inline a animationEditorTrack + bool clipHasAnimatableAnimationCurves = false; + + for (var i = 0; i != newItem.track.clips.Length; ++i) + { + var curveClip = newItem.track.clips[i].curves; + var animationClip = newItem.track.clips[i].animationClip; + + // prune out clip with zero curves + if (curveClip != null && curveClip.empty) + curveClip = null; + + if (animationClip != null && animationClip.empty) + animationClip = null; + + // prune out clips coming from FBX + if (animationClip != null && ((animationClip.hideFlags & HideFlags.NotEditable) != 0)) + animationClip = null; + + if (!newItem.track.clips[i].recordable) + animationClip = null; + + clipHasAnimatableAnimationCurves = (curveClip != null) || (animationClip != null); + if (clipHasAnimatableAnimationCurves) + break; + } + + if (clipHasAnimatableAnimationCurves) + { + if (newItem.children == null) + newItem.children = new List<TreeViewItem>(); + } + } + + if (trackAsset != null) + { + // Here we are using the internal subTrackObject so we can properly handle tracks whose script + // can't load (via ScriptableObject) + foreach (var subTrack in trackAsset.subTracksObjects) + { + CreateItem(subTrack, ref tree, selectedRows, newItem); + } + } + return newItem; + } + + public override bool CanBeParent(TreeViewItem item) + { + // will prevent track becoming subtracks via dragging + TimelineTrackGUI track = item as TimelineTrackGUI; + if (track != null) + return false; + + return true; + } + + public void ExpandItems(TreeViewItem item) + { + if (treeroot == item) + { + SetExpanded(treeroot, true); + } + + TimelineGroupGUI gui = item as TimelineGroupGUI; + if (gui != null && gui.track != null) + { + SetExpanded(item, !gui.track.GetCollapsed()); + } + + if (item.children != null) + { + for (int c = 0; c < item.children.Count; c++) + { + ExpandItems(item.children[c]); + } + } + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineDataSource.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineDataSource.cs.meta new file mode 100644 index 0000000..5d40d2d --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineDataSource.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e8c30efa89029e447b0dc0efc75b294c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineDragging.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineDragging.cs new file mode 100644 index 0000000..8162c06 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineDragging.cs @@ -0,0 +1,683 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor.IMGUI.Controls; +using UnityEditor.Timeline; +using UnityEngine; +using UnityEngine.Timeline; +using UnityEngine.Playables; +using UnityObject = UnityEngine.Object; + +namespace UnityEditor +{ + class TimelineDragging : TreeViewDragging + { + public delegate bool TypeResolver(IEnumerable<Type> types, Action<Type> onComplete, string format); + + private static readonly string k_SelectTrackWithBinding = LocalizationDatabase.GetLocalizedString("Add {0}"); + private static readonly string k_SelectTrackWithClip = LocalizationDatabase.GetLocalizedString("Add Clip With {0}"); + private static readonly string k_SelectClip = LocalizationDatabase.GetLocalizedString("Add {0}"); + + + const string k_GenericDragId = "TimelineDragging"; + readonly int kDragSensitivity = 2; + readonly TimelineAsset m_Timeline; + readonly TimelineWindow m_Window; + + class TimelineDragData + { + public TimelineDragData(List<TreeViewItem> draggedItems) + { + this.draggedItems = draggedItems; + } + + public readonly List<TreeViewItem> draggedItems; + } + + public TimelineDragging(TreeViewController treeView, TimelineWindow window, TimelineAsset data) + : base(treeView) + { + m_Timeline = data; + m_Window = window; + } + + public override bool CanStartDrag(TreeViewItem targetItem, List<int> draggedItemIDs, Vector2 mouseDownPosition) + { + if (Event.current.modifiers != EventModifiers.None) + return false; + + // Can only drag when starting in the track header area + if (mouseDownPosition.x > m_Window.sequenceHeaderRect.xMax) + return false; + + var trackBaseGUI = targetItem as TimelineTrackBaseGUI; + + if (trackBaseGUI == null || trackBaseGUI.track == null) + return false; + + if (trackBaseGUI.track.lockedInHierarchy) + return false; + + if (Event.current.type == EventType.MouseDrag && Mathf.Abs(Event.current.delta.y) < kDragSensitivity) + return false; + + // Make sure dragged items are selected + // TODO Use similar system than the SceneHierarchyWindow in order to handle selection between treeView and tracks. + SelectionManager.Clear(); + var draggedTrackGUIs = m_Window.allTracks.Where(t => draggedItemIDs.Contains(t.id)); + foreach (var trackGUI in draggedTrackGUIs) + SelectionManager.Add(trackGUI.track); + + return true; + } + + public override void StartDrag(TreeViewItem draggedNode, List<int> draggedItemIDs) + { + DragAndDrop.PrepareStartDrag(); + var tvItems = SelectionManager.SelectedTrackGUI().Cast<TreeViewItem>().ToList(); + DragAndDrop.SetGenericData(k_GenericDragId, new TimelineDragData(tvItems)); + DragAndDrop.objectReferences = new UnityObject[] {}; // this IS required for dragging to work + + string title = draggedItemIDs.Count + (draggedItemIDs.Count > 1 ? "s" : ""); // title is only shown on OSX (at the cursor) + + TimelineGroupGUI groupGui = draggedNode as TimelineGroupGUI; + if (groupGui != null) + { + title = groupGui.displayName; + } + DragAndDrop.StartDrag(title); + } + + public static bool IsDraggingEvent() + { + return Event.current.type == EventType.DragUpdated || + Event.current.type == EventType.DragExited || + Event.current.type == EventType.DragPerform; + } + + public static bool ResolveType(IEnumerable<System.Type> types, Action<Type> onComplete, string formatString) + { + if (!types.Any() || onComplete == null) + return false; + + if (types.Count() == 1) + { + onComplete(types.First()); + return true; + } + + var menu = new GenericMenu(); + + var builtInTypes = types.Where(TypeUtility.IsBuiltIn).OrderBy(TypeUtility.GetDisplayName).ToArray(); + var customTypes = types.Where(x => !TypeUtility.IsBuiltIn(x)).OrderBy(TypeUtility.GetDisplayName).ToArray(); + + foreach (var t in builtInTypes) + { + menu.AddItem(new GUIContent(string.Format(formatString, TypeUtility.GetDisplayName(t))), false, s => onComplete((System.Type)s), t); + } + + if (builtInTypes.Length != 0 && customTypes.Length != 0) + menu.AddSeparator(string.Empty); + + foreach (var t in customTypes) + { + menu.AddItem(new GUIContent(string.Format(formatString, TypeUtility.GetDisplayName(t))), false, s => onComplete((System.Type)s), t); + } + + menu.ShowAsContext(); + return true; + } + + public override bool DragElement(TreeViewItem targetItem, Rect targetItemRect, int row) + { + if (TimelineWindow.instance.state.editSequence.isReadOnly) + return false; + // the drop rect contains the row rect plus additional spacing. The base drag element overlaps 1/2 the height of the next track + // which interferes with track bindings + var targetTrack = targetItem as TimelineGroupGUI; + if (row > 0 && targetTrack != null && !targetTrack.dropRect.Contains(Event.current.mousePosition)) + return false; + + return base.DragElement(targetItem, targetItemRect, row); + } + + TreeViewItem GetNextItem(TreeViewItem item) + { + if (item == null) + return null; + + if (item.parent == null) + { + int row = m_Window.treeView.data.GetRow(item.id); + var items = m_Window.treeView.data.GetRows(); + if (items.Count > row + 1) + return items[row + 1]; + return null; + } + + var children = item.parent.children; + if (children == null) + return null; + + for (int i = 0; i < children.Count - 1; i++) + { + if (children[i] == item) + return children[i + 1]; + } + return null; + } + + private static TrackAsset GetTrack(TreeViewItem item) + { + TimelineTrackBaseGUI baseGui = item as TimelineTrackBaseGUI; + if (baseGui == null) + return null; + return baseGui.track; + } + + // The drag and drop may be over an expanded group but might be between tracks + private void HandleNestedItemGUI(ref TreeViewItem parentItem, ref TreeViewItem targetItem, ref TreeViewItem insertBefore) + { + const float kTopPad = 5; + const float kBottomPad = 5; + + insertBefore = null; + + if (!ShouldUseHierarchyDragAndDrop()) + return; + + var targetTrack = targetItem as TimelineGroupGUI; + if (targetTrack == null) + return; + + var mousePosition = Event.current.mousePosition; + + var dropBefore = targetTrack.rowRect.yMin + kTopPad > mousePosition.y; + var dropAfter = !(targetTrack.track is GroupTrack) && (targetTrack.rowRect.yMax - kBottomPad < mousePosition.y); + + targetTrack.drawInsertionMarkerBefore = dropBefore; + targetTrack.drawInsertionMarkerAfter = dropAfter; + + if (dropBefore) + { + targetItem = parentItem; + parentItem = targetItem != null ? targetItem.parent : null; + insertBefore = targetTrack; + } + else if (dropAfter) + { + targetItem = parentItem; + parentItem = targetItem != null ? targetItem.parent : null; + insertBefore = GetNextItem(targetTrack); + } + else if (targetTrack.track is GroupTrack) + { + targetTrack.isDropTarget = true; + } + } + + public override DragAndDropVisualMode DoDrag(TreeViewItem parentItem, TreeViewItem targetItem, bool perform, DropPosition dropPos) + { + m_Window.isDragging = false; + + var retMode = DragAndDropVisualMode.None; + + var trackDragData = DragAndDrop.GetGenericData(k_GenericDragId) as TimelineDragData; + + if (trackDragData != null) + { + retMode = HandleTrackDrop(parentItem, targetItem, perform, dropPos); + if (retMode == DragAndDropVisualMode.Copy && targetItem != null && Event.current.type == EventType.DragUpdated) + { + var targetActor = targetItem as TimelineGroupGUI; + if (targetActor != null) + targetActor.isDropTarget = true; + } + } + else if (DragAndDrop.objectReferences.Any()) + { + var objectsBeingDropped = DragAndDrop.objectReferences.OfType<UnityObject>(); + var director = m_Window.state.editSequence.director; + + if (ShouldUseHierarchyDragAndDrop()) + { + // for object drawing + var originalTarget = targetItem; + TreeViewItem insertBeforeItem = null; + HandleNestedItemGUI(ref parentItem, ref targetItem, ref insertBeforeItem); + var track = GetTrack(targetItem); + var parent = GetTrack(parentItem); + var insertBefore = GetTrack(insertBeforeItem); + retMode = HandleHierarchyPaneDragAndDrop(objectsBeingDropped, track, perform, m_Timeline, director, ResolveType, insertBefore); + + // fallback to old clip behaviour + if (retMode == DragAndDropVisualMode.None) + { + retMode = HandleClipPaneObjectDragAndDrop(objectsBeingDropped, track, perform, m_Timeline, parent, director, m_Window.state.timeAreaShownRange.x, ResolveType, insertBefore); + } + + // if we are rejected, clear any drop markers + if (retMode == DragAndDropVisualMode.Rejected && targetItem != null) + { + ClearInsertionMarkers(originalTarget); + ClearInsertionMarkers(targetItem); + ClearInsertionMarkers(parentItem); + ClearInsertionMarkers(insertBeforeItem); + } + } + else + { + var candidateTime = TimelineHelpers.GetCandidateTime(m_Window.state, Event.current.mousePosition); + retMode = HandleClipPaneObjectDragAndDrop(objectsBeingDropped, GetTrack(targetItem), perform, m_Timeline, GetTrack(parentItem), director, candidateTime, ResolveType); + } + } + + m_Window.isDragging = false; + + return retMode; + } + + void ClearInsertionMarkers(TreeViewItem item) + { + var trackGUI = item as TimelineTrackBaseGUI; + if (trackGUI != null) + { + trackGUI.drawInsertionMarkerAfter = false; + trackGUI.drawInsertionMarkerBefore = false; + trackGUI.isDropTarget = false; + } + } + + bool ShouldUseHierarchyDragAndDrop() + { + return m_Window.state.IsEditingAnEmptyTimeline() || m_Window.state.sequencerHeaderWidth > Event.current.mousePosition.x; + } + + public static DragAndDropVisualMode HandleHierarchyPaneDragAndDrop(IEnumerable<UnityObject> objectsBeingDropped, TrackAsset targetTrack, bool perform, TimelineAsset timeline, PlayableDirector director, TypeResolver typeResolver, TrackAsset insertBefore = null) + { + if (timeline == null) + return DragAndDropVisualMode.Rejected; + + // if we are over a target track, defer to track binding system (implemented in TrackGUIs), unless we are a groupTrack + if (targetTrack != null && (targetTrack as GroupTrack) == null) + return DragAndDropVisualMode.None; + + if (targetTrack != null && targetTrack.lockedInHierarchy) + return DragAndDropVisualMode.Rejected; + + var tracksWithBinding = objectsBeingDropped.SelectMany(TypeUtility.GetTracksCreatableFromObject).Distinct(); + if (!tracksWithBinding.Any()) + return DragAndDropVisualMode.None; + + if (perform) + { + System.Action<Type> onResolve = trackType => + { + foreach (var obj in objectsBeingDropped) + { + if (!obj.IsPrefab() && TypeUtility.IsTrackCreatableFromObject(obj, trackType)) + { + var newTrack = TimelineHelpers.CreateTrack(timeline, trackType, targetTrack, string.Empty); + if (insertBefore != null) + { + if (targetTrack != null) + targetTrack.MoveLastTrackBefore(insertBefore); + else + timeline.MoveLastTrackBefore(insertBefore); + } + + TimelineHelpers.Bind(newTrack, obj, director); + } + } + TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved); + }; + typeResolver(tracksWithBinding, onResolve, k_SelectTrackWithBinding); + } + + return DragAndDropVisualMode.Copy; + } + + public static DragAndDropVisualMode HandleClipPaneObjectDragAndDrop(IEnumerable<UnityObject> objectsBeingDropped, TrackAsset targetTrack, bool perform, TimelineAsset timeline, TrackAsset parent, PlayableDirector director, double candidateTime, TypeResolver typeResolver, TrackAsset insertBefore = null) + { + if (timeline == null) + return DragAndDropVisualMode.Rejected; + + // locked tracks always reject + if (targetTrack != null && targetTrack.lockedInHierarchy) + return DragAndDropVisualMode.Rejected; + + // treat group tracks as having no track + if (targetTrack is GroupTrack) + { + parent = targetTrack; + targetTrack = null; + } + + // Special case for monoscripts, since they describe the type + if (objectsBeingDropped.Any(o => o is MonoScript)) + return HandleClipPaneMonoScriptDragAndDrop(objectsBeingDropped.OfType<MonoScript>(), targetTrack, perform, timeline, parent, director, candidateTime); + + // no unity objects, or explicit exceptions + if (!objectsBeingDropped.Any() || objectsBeingDropped.Any(o => !ValidateObjectDrop(o))) + return DragAndDropVisualMode.Rejected; + + // reject scene references if we have no context + if (director == null && objectsBeingDropped.Any(o => o.IsSceneObject())) + return DragAndDropVisualMode.Rejected; + + var validTrackTypes = objectsBeingDropped.SelectMany(o => TypeUtility.GetTrackTypesForObject(o)).Distinct().ToList(); + // special case for playable assets + if (objectsBeingDropped.Any(o => TypeUtility.IsConcretePlayableAsset(o.GetType()))) + { + var playableAssets = objectsBeingDropped.OfType<IPlayableAsset>().Where(o => TypeUtility.IsConcretePlayableAsset(o.GetType())); + return HandleClipPanePlayableAssetDragAndDrop(playableAssets, targetTrack, perform, timeline, parent, director, candidateTime, typeResolver); + } + + var markerTypes = objectsBeingDropped.SelectMany(o => TypeUtility.MarkerTypesWithFieldForObject(o)).Distinct(); + + // Markers support all tracks + if (!markerTypes.Any()) + { + // No tracks support this object + if (!validTrackTypes.Any()) + return DragAndDropVisualMode.Rejected; + + // no tracks for this object + if (targetTrack != null && !validTrackTypes.Contains(targetTrack.GetType())) + return DragAndDropVisualMode.Rejected; + } + + // there is no target track, dropping to empty space, or onto a group + if (perform) + { + // choose track and then clip + if (targetTrack == null) + { + var createdTrack = HandleTrackAndItemCreation(objectsBeingDropped, candidateTime, typeResolver, timeline, parent, validTrackTypes, insertBefore); + if (!createdTrack) + { + timeline.CreateMarkerTrack(); + HandleItemCreation(objectsBeingDropped, timeline.markerTrack, candidateTime, typeResolver, true); // menu is always popped if ambiguous choice + } + } + // just choose clip/marker + else + { + HandleItemCreation(objectsBeingDropped, targetTrack, candidateTime, typeResolver, true); // menu is always popped if ambiguous choice + } + } + + return DragAndDropVisualMode.Copy; + } + + static bool HandleTrackAndItemCreation(IEnumerable<UnityEngine.Object> objectsBeingDropped, double candidateTime, TypeResolver typeResolver, TimelineAsset timeline, TrackAsset parent, IEnumerable<Type> validTrackTypes, TrackAsset insertBefore = null) + { + Action<Type> onResolved = t => + { + var newTrack = TimelineHelpers.CreateTrack(timeline, t, parent, string.Empty); + if (insertBefore != null) + { + if (parent != null) + parent.MoveLastTrackBefore(insertBefore); + else + timeline.MoveLastTrackBefore(insertBefore); + } + HandleItemCreation(objectsBeingDropped, newTrack, candidateTime, typeResolver, validTrackTypes.Count() == 1); // menu is popped if ambiguous clip choice and unambiguous track choice + }; + return typeResolver(validTrackTypes, t => onResolved(t), k_SelectTrackWithClip); // Did it create a track + } + + static void HandleItemCreation(IEnumerable<UnityEngine.Object> objectsBeingDropped, TrackAsset targetTrack, double candidateTime, TypeResolver typeResolver, bool allowMenu) + { + var assetTypes = objectsBeingDropped.Select(o => + TypeUtility.GetAssetTypesForObject(targetTrack.GetType(), o) + .Union(TypeUtility.MarkerTypesWithFieldForObject(o))).ToList(); + Action<Type> onCreateItem = assetType => + { + if (typeof(PlayableAsset).IsAssignableFrom(assetType)) + { + TimelineHelpers.CreateClipsFromObjects(assetType, targetTrack, candidateTime, + objectsBeingDropped); + } + else + { + TimelineHelpers.CreateMarkersFromObjects(assetType, targetTrack, candidateTime, objectsBeingDropped); + } + }; + + var flatAssetTypes = assetTypes.SelectMany(x => x).Distinct(); + // If there is a one to one mapping between assets and timeline types, no need to go through the type resolution, not ambiguous. + if (assetTypes.All(x => x.Count() <= 1)) + { + foreach (var type in flatAssetTypes) + { + onCreateItem(type); + } + } + else + { + if (!allowMenu) // If we already popped a menu, and are presented with an ambiguous choice, take the first entry + { + flatAssetTypes = new[] {flatAssetTypes.First()}; + } + + typeResolver(flatAssetTypes, onCreateItem, k_SelectClip); + } + } + + /// Handles drag and drop of a mono script. + public static DragAndDropVisualMode HandleClipPaneMonoScriptDragAndDrop(IEnumerable<MonoScript> scriptsBeingDropped, TrackAsset targetTrack, bool perform, TimelineAsset timeline, TrackAsset parent, PlayableDirector director, double candidateTime) + { + var playableAssetTypes = scriptsBeingDropped.Select(s => s.GetClass()).Where(TypeUtility.IsConcretePlayableAsset).Distinct(); + if (!playableAssetTypes.Any()) + return DragAndDropVisualMode.Rejected; + + var targetTrackType = typeof(PlayableTrack); + if (targetTrack != null) + targetTrackType = targetTrack.GetType(); + + var trackAssetsTypes = TypeUtility.GetPlayableAssetsHandledByTrack(targetTrackType); + var supportedTypes = trackAssetsTypes.Intersect(playableAssetTypes); + if (!supportedTypes.Any()) + return DragAndDropVisualMode.Rejected; + + if (perform) + { + if (targetTrack == null) + targetTrack = TimelineHelpers.CreateTrack(timeline, targetTrackType, parent, string.Empty); + TimelineHelpers.CreateClipsFromTypes(supportedTypes, targetTrack, candidateTime); + } + + return DragAndDropVisualMode.Copy; + } + + public static DragAndDropVisualMode HandleClipPanePlayableAssetDragAndDrop(IEnumerable<IPlayableAsset> assetsBeingDropped, TrackAsset targetTrack, bool perform, TimelineAsset timeline, TrackAsset parent, PlayableDirector director, double candidateTime, TypeResolver typeResolver) + { + // get the list of supported track types + var assetTypes = assetsBeingDropped.Select(x => x.GetType()).Distinct(); + IEnumerable<Type> supportedTypes = null; + if (targetTrack == null) + { + supportedTypes = TypeUtility.AllTrackTypes().Where(t => TypeUtility.GetPlayableAssetsHandledByTrack(t).Intersect(assetTypes).Any()).ToList(); + } + else + { + supportedTypes = Enumerable.Empty<Type>(); + var trackAssetTypes = TypeUtility.GetPlayableAssetsHandledByTrack(targetTrack.GetType()); + if (trackAssetTypes.Intersect(assetTypes).Any()) + supportedTypes = new[] {targetTrack.GetType()}; + } + + if (!supportedTypes.Any()) + return DragAndDropVisualMode.Rejected; + + if (perform) + { + Action<Type> onResolved = (t) => + { + if (targetTrack == null) + targetTrack = TimelineHelpers.CreateTrack(timeline, t, parent, string.Empty); + + var clipTypes = TypeUtility.GetPlayableAssetsHandledByTrack(targetTrack.GetType()); + foreach (var asset in assetsBeingDropped) + { + if (clipTypes.Contains(asset.GetType())) + TimelineHelpers.CreateClipOnTrackFromPlayableAsset(asset, targetTrack, candidateTime); + } + }; + + typeResolver(supportedTypes, onResolved, k_SelectTrackWithClip); + } + + + return DragAndDropVisualMode.Copy; + } + + static bool ValidateObjectDrop(UnityObject obj) + { + // legacy animation clips are not supported at all + AnimationClip clip = obj as AnimationClip; + if (clip != null && clip.legacy) + return false; + + return !(obj is TimelineAsset); + } + + public DragAndDropVisualMode HandleTrackDrop(TreeViewItem parentItem, TreeViewItem targetItem, bool perform, DropPosition dropPos) + { + ((TimelineTreeView)m_Window.treeView.gui).showInsertionMarker = false; + var trackDragData = (TimelineDragData)DragAndDrop.GetGenericData(k_GenericDragId); + bool validDrag = ValidDrag(targetItem, trackDragData.draggedItems); + if (!validDrag) + return DragAndDropVisualMode.None; + + + var draggedTracks = trackDragData.draggedItems.OfType<TimelineGroupGUI>().Select(x => x.track).ToList(); + if (draggedTracks.Count == 0) + return DragAndDropVisualMode.None; + + if (parentItem != null) + { + var parentActor = parentItem as TimelineGroupGUI; + if (parentActor != null && parentActor.track != null) + { + if (parentActor.track.lockedInHierarchy) + return DragAndDropVisualMode.Rejected; + + if (draggedTracks.Any(x => !TimelineCreateUtilities.ValidateParentTrack(parentActor.track, x.GetType()))) + return DragAndDropVisualMode.Rejected; + } + } + + var insertAfterItem = targetItem as TimelineGroupGUI; + if (insertAfterItem != null && insertAfterItem.track != null) + { + ((TimelineTreeView)m_Window.treeView.gui).showInsertionMarker = true; + } + + if (dropPos == DropPosition.Upon) + { + var groupGUI = targetItem as TimelineGroupGUI; + if (groupGUI != null) + groupGUI.isDropTarget = true; + } + + if (perform) + { + PlayableAsset targetParent = m_Timeline; + var parentActor = parentItem as TimelineGroupGUI; + + if (parentActor != null && parentActor.track != null) + targetParent = parentActor.track; + + TrackAsset siblingTrack = insertAfterItem != null ? insertAfterItem.track : null; + + // where the user drops after the last track, make sure to place it after all the tracks + if (targetParent == m_Timeline && dropPos == DropPosition.Below && siblingTrack == null) + { + siblingTrack = m_Timeline.GetRootTracks().LastOrDefault(x => !draggedTracks.Contains(x)); + } + + if (TrackExtensions.ReparentTracks(TrackExtensions.FilterTracks(draggedTracks).ToList(), targetParent, siblingTrack, dropPos == DropPosition.Above)) + { + m_Window.state.Refresh(); + } + } + + return DragAndDropVisualMode.Move; + } + + public static void HandleBindingDragAndDrop(TrackAsset dropTarget, Type requiredBindingType) + { + var objectBeingDragged = DragAndDrop.objectReferences[0]; + + var action = BindingUtility.GetBindingAction(requiredBindingType, objectBeingDragged); + DragAndDrop.visualMode = action == BindingAction.DoNotBind + ? DragAndDropVisualMode.Rejected + : DragAndDropVisualMode.Link; + + if (action == BindingAction.DoNotBind || Event.current.type != EventType.DragPerform) + return; + + var director = TimelineEditor.inspectedDirector; + + switch (action) + { + case BindingAction.BindDirectly: + { + BindingUtility.Bind(director, dropTarget, objectBeingDragged); + break; + } + case BindingAction.BindToExistingComponent: + { + var gameObjectBeingDragged = objectBeingDragged as GameObject; + Debug.Assert(gameObjectBeingDragged != null, "The object being dragged was detected as being a GameObject"); + + BindingUtility.Bind(director, dropTarget, gameObjectBeingDragged.GetComponent(requiredBindingType)); + break; + } + case BindingAction.BindToMissingComponent: + { + var gameObjectBeingDragged = objectBeingDragged as GameObject; + Debug.Assert(gameObjectBeingDragged != null, "The object being dragged was detected as being a GameObject"); + + var typeNameOfComponent = requiredBindingType.ToString().Split(".".ToCharArray()).Last(); + var bindMenu = new GenericMenu(); + + bindMenu.AddItem( + EditorGUIUtility.TextContent("Create " + typeNameOfComponent + " on " + gameObjectBeingDragged.name), + false, + nullParam => BindingUtility.Bind(director, dropTarget, Undo.AddComponent(gameObjectBeingDragged, requiredBindingType)), + null); + + bindMenu.AddSeparator(""); + bindMenu.AddItem(EditorGUIUtility.TrTextContent("Cancel"), false, userData => {}, null); + bindMenu.ShowAsContext(); + + break; + } + default: + { + //no-op + return; + } + } + + DragAndDrop.AcceptDrag(); + } + + static bool ValidDrag(TreeViewItem target, List<TreeViewItem> draggedItems) + { + TreeViewItem currentParent = target; + while (currentParent != null) + { + if (draggedItems.Contains(currentParent)) + return false; + currentParent = currentParent.parent; + } + + // dragging into the sequence itself + return true; + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineDragging.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineDragging.cs.meta new file mode 100644 index 0000000..4aff0f8 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineDragging.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fcd66bf74cbeb264aa99679c7df84427 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineGroupGUI.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineGroupGUI.cs new file mode 100644 index 0000000..8341b8b --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineGroupGUI.cs @@ -0,0 +1,310 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor.IMGUI.Controls; +using UnityEngine; +using UnityEngine.Timeline; + +namespace UnityEditor.Timeline +{ + class TimelineGroupGUI : TimelineTrackBaseGUI + { + protected DirectorStyles m_Styles; + protected Rect m_TreeViewRect = new Rect(0, 0, 0, 0); + protected GUIContent m_ProblemIcon = new GUIContent(); + + bool m_MustRecomputeUnions = true; + int m_GroupDepth; + readonly bool m_IsReferencedTrack; + readonly List<TimelineClipUnion> m_Unions = new List<TimelineClipUnion>(); + + public override Rect boundingRect + { + get { return ToWindowSpace(m_TreeViewRect); } + } + + public Rect ToWindowSpace(Rect localRect) + { + localRect.position += treeViewToWindowTransformation; + return localRect; + } + + public override bool expandable + { + get { return !m_IsRoot; } + } + + // The expanded rectangle (contains children) as calculated by the the tree gui + public Rect expandedRect { get; set; } + // The row rectangle (header only) as calculated by the tree gui + public Rect rowRect { get; set; } + // the drop rectangle as set by the tree gui when targetted by a drag and drop + public Rect dropRect { get; set; } + + public TimelineGroupGUI(TreeViewController treeview, TimelineTreeViewGUI treeviewGUI, int id, int depth, TreeViewItem parent, string displayName, TrackAsset trackAsset, bool isRoot) + : base(id, depth, parent, displayName, trackAsset, treeview, treeviewGUI) + { + m_Styles = DirectorStyles.Instance; + m_IsRoot = isRoot; + + var trackPath = AssetDatabase.GetAssetPath(trackAsset); + var sequencePath = AssetDatabase.GetAssetPath(treeviewGUI.TimelineWindow.state.editSequence.asset); + if (trackPath != sequencePath) + m_IsReferencedTrack = true; + + m_GroupDepth = CalculateGroupDepth(parent); + } + + public virtual float GetHeight(WindowState state) + { + // group tracks don't scale in height + return TrackEditor.DefaultTrackHeight; + } + + public override void OnGraphRebuilt() {} + + static int CalculateGroupDepth(TreeViewItem parent) + { + int depth = 0; + + bool done = false; + do + { + var gui = parent as TimelineGroupGUI; + if (gui == null || gui.track == null) + done = true; + else + { + if (gui.track is GroupTrack) + depth++; + + parent = parent.parent; + } + } + while (!done); + + return depth; + } + + internal static float Spaced(float width) + { + return width > 0 ? width + WindowConstants.trackHeaderButtonSpacing : 0; + } + + void DrawTrackButtons(Rect headerRect, WindowState state) + { + const float buttonSize = WindowConstants.trackHeaderButtonSize; + const float padding = WindowConstants.trackHeaderButtonPadding; + + var buttonRect = new Rect(headerRect.xMax - buttonSize - padding, headerRect.y + ((headerRect.height - buttonSize) / 2f), buttonSize, buttonSize); + + if (GUI.Button(buttonRect, EditorGUIUtility.IconContent("CreateAddNew"), m_Styles.trackGroupAddButton)) + { + // the drop down will apply to all selected tracks + if (!SelectionManager.Contains(track)) + { + SelectionManager.Clear(); + SelectionManager.Add(track); + } + SequencerContextMenu.ShowNewTracksContextMenu(SelectionManager.SelectedTracks().ToArray(), TimelineWindow.state, buttonRect); + } + buttonRect.x -= buttonSize; + buttonRect.x -= Spaced(DrawMuteButton(buttonRect, state)); + buttonRect.x -= Spaced(DrawLockButton(buttonRect, state)); + } + + public void SetExpanded(bool expanded) + { + var collapseChanged = expanded != isExpanded; + isExpanded = expanded; + if (collapseChanged) + { + track.SetCollapsed(!expanded); + m_MustRecomputeUnions = true; + } + } + + public override void Draw(Rect headerRect, Rect contentRect, WindowState state) + { + if (track == null || m_IsRoot) + return; + + if (m_MustRecomputeUnions) + RecomputeRectUnions(); + + if (depth == 1) + Graphics.DrawBackgroundRect(state, headerRect); + + var background = headerRect; + background.height = expandedRect.height; + + var groupColor = TrackResourceCache.GetTrackColor(track); + + m_TreeViewRect = contentRect; + + var col = groupColor; + + var isSelected = SelectionManager.Contains(track); + + if (isSelected) + col = DirectorStyles.Instance.customSkin.colorSelection; + else if (isDropTarget) + col = DirectorStyles.Instance.customSkin.colorDropTarget; + else + { + if (m_GroupDepth % 2 == 1) + { + float h, s, v; + Color.RGBToHSV(col, out h, out s, out v); + v += 0.06f; + col = Color.HSVToRGB(h, s, v); + } + } + + if (background.width > 0) + { + using (new GUIColorOverride(col)) + GUI.Box(background, GUIContent.none, m_Styles.groupBackground); + } + + var trackRectBackground = headerRect; + trackRectBackground.xMin += background.width; + trackRectBackground.width = contentRect.width; + trackRectBackground.height = background.height; + + if (isSelected) + { + col = state.IsEditingASubTimeline() + ? m_Styles.customSkin.colorTrackSubSequenceBackgroundSelected + : m_Styles.customSkin.colorTrackBackgroundSelected; + } + else + { + col = m_Styles.customSkin.colorGroupTrackBackground; + } + + EditorGUI.DrawRect(trackRectBackground, col); + if (!isExpanded && children != null && children.Count > 0) + { + var collapsedTrackRect = contentRect; + + foreach (var u in m_Unions) + u.Draw(collapsedTrackRect, state); + } + + using (new GUIGroupScope(headerRect)) + { + var groupRect = new Rect(0, 0, headerRect.width, headerRect.height); + DrawName(groupRect, isSelected); + DrawTrackButtons(groupRect, state); + } + + if (IsTrackRecording(state)) + { + using (new GUIColorOverride(DirectorStyles.Instance.customSkin.colorTrackBackgroundRecording)) + GUI.Label(background, GUIContent.none, m_Styles.displayBackground); + } + + // is this a referenced track? + if (m_IsReferencedTrack) + { + var refRect = contentRect; + refRect.x = state.timeAreaRect.xMax - 20.0f; + refRect.y += 5.0f; + refRect.width = 30.0f; + GUI.Label(refRect, DirectorStyles.referenceTrackLabel, EditorStyles.label); + } + + var bgRect = contentRect; + if (track as GroupTrack != null || AllChildrenMuted(this)) + bgRect.height = expandedRect.height; + DrawTrackState(contentRect, bgRect, track); + } + + void DrawName(Rect rect, bool isSelected) + { + var labelRect = rect; + labelRect.xMin += 20; + var actorName = track != null ? track.name : "missing"; + labelRect.width = m_Styles.groupFont.CalcSize(new GUIContent(actorName)).x; + labelRect.width = Math.Max(labelRect.width, 50.0f); + + // if we aren't bound to anything, we show a text field that allows to rename the actor + // otherwise we show a ObjectField to allow binding to a go + if (track != null && track is GroupTrack) + { + var textColor = m_Styles.groupFont.normal.textColor; + + if (isSelected) + textColor = Color.white; + + string newName; + + EditorGUI.BeginChangeCheck(); + using (new StyleNormalColorOverride(m_Styles.groupFont, textColor)) + { + newName = EditorGUI.DelayedTextField(labelRect, GUIContent.none, track.GetInstanceID(), track.name, m_Styles.groupFont); + } + + if (EditorGUI.EndChangeCheck() && !string.IsNullOrEmpty(newName)) + { + track.name = newName; + displayName = track.name; + } + } + } + + protected bool IsSubTrack() + { + if (track == null) + return false; + + var parentTrack = track.parent as TrackAsset; + if (parentTrack == null) + return false; + + return parentTrack.GetType() != typeof(GroupTrack); + } + + protected TrackAsset ParentTrack() + { + if (IsSubTrack()) + return track.parent as TrackAsset; + return null; + } + + // is there currently a recording track + bool IsTrackRecording(WindowState state) + { + if (!state.recording) + return false; + if (track.GetType() != typeof(GroupTrack)) + return false; + + return state.GetArmedTrack(track) != null; + } + + void RecomputeRectUnions() + { + m_MustRecomputeUnions = false; + m_Unions.Clear(); + if (children == null) + return; + + foreach (var c in children.OfType<TimelineTrackGUI>()) + { + c.RebuildGUICacheIfNecessary(); + m_Unions.AddRange(TimelineClipUnion.Build(c.clips)); + } + } + + static bool AllChildrenMuted(TimelineGroupGUI groupGui) + { + if (!groupGui.track.muted) + return false; + if (groupGui.children == null) + return true; + return groupGui.children.OfType<TimelineGroupGUI>().All(AllChildrenMuted); + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineGroupGUI.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineGroupGUI.cs.meta new file mode 100644 index 0000000..470f3aa --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineGroupGUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 00187582b67e7654b914b5a0d37daafb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTrackBaseGUI.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTrackBaseGUI.cs new file mode 100644 index 0000000..b957f33 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTrackBaseGUI.cs @@ -0,0 +1,218 @@ +using System.Linq; +using UnityEditor.IMGUI.Controls; +using UnityEngine; +using UnityEngine.Timeline; + +namespace UnityEditor.Timeline +{ + abstract class TimelineTrackBaseGUI : TreeViewItem, IBounds + { + static class Styles + { + public static readonly GUIContent s_LockedAndMuted = EditorGUIUtility.TrTextContent("Locked / Muted"); + public static readonly GUIContent s_LockedAndPartiallyMuted = EditorGUIUtility.TrTextContent("Locked / Partially Muted"); + public static readonly GUIContent s_Locked = EditorGUIUtility.TrTextContent("Locked"); + public static readonly GUIContent s_Muted = EditorGUIUtility.TrTextContent("Muted"); + public static readonly GUIContent s_PartiallyMuted = EditorGUIUtility.TrTextContent("Partially Muted"); + + public static readonly Texture2D lockBg = DirectorStyles.GetBackgroundImage(DirectorStyles.Instance.lockedBG); + } + + protected bool m_IsRoot = false; + protected const float k_ButtonSize = 16.0f; + + readonly TimelineTreeViewGUI m_TreeViewGUI; + readonly TrackDrawer m_Drawer; + + public Vector2 treeViewToWindowTransformation { get; set; } + public bool isExpanded { get; set; } + public bool isDropTarget { protected get; set; } + public TrackAsset track { get; } + TreeViewController treeView { get; } + + public TimelineWindow TimelineWindow + { + get + { + if (m_TreeViewGUI == null) + return null; + + return m_TreeViewGUI.TimelineWindow; + } + } + + public TrackDrawer drawer + { + get { return m_Drawer; } + } + + public virtual float GetVerticalSpacingBetweenTracks() + { + return 3.0f; + } + + public bool visibleRow { get; set; } // is the header row visible + public bool visibleExpanded { get; set; } // is the expanded area (group) visible + public bool drawInsertionMarkerBefore { get; set; } + public bool drawInsertionMarkerAfter { get; set; } + + public abstract Rect boundingRect { get; } + public abstract bool expandable { get; } + public abstract void Draw(Rect headerRect, Rect contentRect, WindowState state); + public abstract void OnGraphRebuilt(); // callback when the corresponding graph is rebuilt. This can happen, but not have the GUI rebuilt. + + protected TimelineTrackBaseGUI(int id, int depth, TreeViewItem parent, string displayName, TrackAsset trackAsset, TreeViewController tv, TimelineTreeViewGUI tvgui) + : base(id, depth, parent, displayName) + { + m_Drawer = TrackDrawer.CreateInstance(trackAsset); + m_Drawer.sequencerState = tvgui.TimelineWindow.state; + + isExpanded = false; + isDropTarget = false; + track = trackAsset; + treeView = tv; + + m_TreeViewGUI = tvgui; + } + + public static TimelineTrackBaseGUI FindGUITrack(TrackAsset track) + { + var allTracks = TimelineWindow.instance.allTracks; + return allTracks.Find(x => x.track == track); + } + + protected void DrawTrackState(Rect trackRect, Rect expandedRect, TrackAsset track) + { + if (Event.current.type == EventType.Layout) + { + bool needStateBox = false; + + //Mute + if (track.muted && !TimelineUtility.IsParentMuted(track)) + { + Rect bgRect = expandedRect; + TimelineWindow.instance.OverlayDrawData.Add(TimelineWindow.OverlayData.CreateColorOverlay(GUIClip.Unclip(bgRect), DirectorStyles.Instance.customSkin.colorTrackDarken)); + needStateBox = true; + } + + //Lock + if (!needStateBox && track.locked && !TimelineUtility.IsLockedFromGroup(track)) + { + Rect bgRect = expandedRect; + TimelineWindow.instance.OverlayDrawData.Add(TimelineWindow.OverlayData.CreateTextureOverlay(GUIClip.Unclip(bgRect), Styles.lockBg)); + needStateBox = true; + } + + if (needStateBox) + { + DrawTrackStateBox(trackRect, track); + } + } + } + + void DrawTrackStateBox(Rect trackRect, TrackAsset track) + { + const float k_LockTextPadding = 40f; + var styles = DirectorStyles.Instance; + + bool locked = track.locked && !TimelineUtility.IsLockedFromGroup(track); + bool muted = track.muted && !TimelineUtility.IsParentMuted(track); + bool allSubTrackMuted = TimelineUtility.IsAllSubTrackMuted(track); + + GUIContent content = null; + if (locked && muted) + { + content = Styles.s_LockedAndMuted; + if (!allSubTrackMuted) + content = Styles.s_LockedAndPartiallyMuted; + } + else if (locked) content = Styles.s_Locked; + else if (muted) + { + content = Styles.s_Muted; + if (!allSubTrackMuted) + content = Styles.s_PartiallyMuted; + } + + // the track could be locked, but we only show the 'locked portion' on the upper most track + // that is causing the lock + if (content == null) + return; + + var textRect = trackRect; + textRect.width = styles.fontClip.CalcSize(content).x + k_LockTextPadding; + textRect.x += (trackRect.width - textRect.width) / 2f; + textRect.height -= 4f; + textRect.y += 2f; + + + TimelineWindow.instance.OverlayDrawData.Add(TimelineWindow.OverlayData.CreateTextBoxOverlay(GUIClip.Unclip(textRect), content.text, styles.fontClip, Color.white, styles.customSkin.colorLockTextBG, styles.displayBackground)); + } + + protected float DrawMuteButton(Rect rect, WindowState state) + { + if (track.mutedInHierarchy) + { + using (new EditorGUI.DisabledScope(TimelineUtility.IsParentMuted(track))) + { + if (GUI.Button(rect, GUIContent.none, TimelineWindow.styles.mute)) + { + MuteTrack.Mute(state, new[] { track }, false); + } + } + + return WindowConstants.trackHeaderButtonSize; + } + + return 0.0f; + } + + protected float DrawLockButton(Rect rect, WindowState state) + { + if (track.lockedInHierarchy) + { + // if the parent is locked, show it the lock disabled + using (new EditorGUI.DisabledScope(TimelineUtility.IsLockedFromGroup(track))) + { + if (GUI.Button(rect, GUIContent.none, TimelineWindow.styles.locked)) + { + LockTrack.SetLockState(new[] { track }, !track.locked, state); + } + } + + return WindowConstants.trackHeaderButtonSize; + } + + return 0.0f; + } + + public void DrawInsertionMarkers(Rect rowRectWithIndent) + { + const float insertionHeight = WindowConstants.trackInsertionMarkerHeight; + if (Event.current.type == EventType.Repaint && (drawInsertionMarkerAfter || drawInsertionMarkerBefore)) + { + if (drawInsertionMarkerBefore) + { + var rect = new Rect(rowRectWithIndent.x, rowRectWithIndent.y - insertionHeight * 0.5f - 2.0f, rowRectWithIndent.width, insertionHeight); + EditorGUI.DrawRect(rect, Color.white); + } + + if (drawInsertionMarkerAfter) + { + var rect = new Rect(rowRectWithIndent.x, rowRectWithIndent.y + rowRectWithIndent.height - insertionHeight * 0.5f + 1.0f, rowRectWithIndent.width, insertionHeight); + EditorGUI.DrawRect(rect, Color.white); + } + } + } + + public void ClearDrawFlags() + { + if (Event.current.type == EventType.Repaint) + { + isDropTarget = false; + drawInsertionMarkerAfter = false; + drawInsertionMarkerBefore = false; + } + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTrackBaseGUI.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTrackBaseGUI.cs.meta new file mode 100644 index 0000000..50e7b73 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTrackBaseGUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9b1a3034a9e81704abdd08677a2d035f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTrackErrorGUI.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTrackErrorGUI.cs new file mode 100644 index 0000000..7e32e2c --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTrackErrorGUI.cs @@ -0,0 +1,185 @@ +using System; +using UnityEditor.IMGUI.Controls; +using UnityEngine; +using UnityEngine.Playables; + +namespace UnityEditor.Timeline +{ + class TimelineTrackErrorGUI : TimelineTrackBaseGUI + { + static class Styles + { + public static readonly GUIContent ErrorText = EditorGUIUtility.TrTextContent("Track cannot be loaded.", "Please fix any compile errors in the script for this track"); + public static readonly Texture2D IconWarn = EditorGUIUtility.LoadIconRequired("console.warnicon.inactive.sml"); + public static readonly GUIContent RemoveTrack = EditorGUIUtility.TrTextContent("Delete"); + + public static readonly Color WarningBoxBackgroundColor = new Color(115.0f / 255.0f, 115.0f / 255.0f, 115.0f / 255.0f); // approved for both skins + public static readonly Color WarningBoxHighlightColor = new Color(229 / 255.0f, 208 / 255.0f, 54 / 255.0f); // brigher than standard warning color for contrast + } + + Rect m_TrackRect; + ScriptableObject m_ScriptableObject; + PlayableAsset m_Owner; + static GUIContent s_GUIContent = new GUIContent(); + + public TimelineTrackErrorGUI(TreeViewController treeview, TimelineTreeViewGUI treeviewGUI, int id, int depth, TreeViewItem parent, string displayName, ScriptableObject track, PlayableAsset owner) + : base(id, depth, parent, displayName, null, treeview, treeviewGUI) + { + m_ScriptableObject = track; + m_Owner = owner; + } + + public override Rect boundingRect + { + get { return m_TrackRect; } + } + + public override bool expandable + { + get { return false; } + } + + public override void Draw(Rect headerRect, Rect contentRect, WindowState state) + { + m_TrackRect = contentRect; + + DrawMissingTrackHeader(headerRect, state); + DrawMissingTrackBody(contentRect); + } + + void DrawMissingTrackHeader(Rect headerRect, WindowState state) + { + var styles = DirectorStyles.Instance; + + // Draw a header + Color backgroundColor = styles.customSkin.colorTrackHeaderBackground; + var bgRect = headerRect; + bgRect.x += styles.trackSwatchStyle.fixedWidth; + bgRect.width -= styles.trackSwatchStyle.fixedWidth; + EditorGUI.DrawRect(bgRect, backgroundColor); + + // draw the warning icon + var errorIcon = Styles.IconWarn; + Rect iconRect = new Rect(headerRect.xMin + styles.trackSwatchStyle.fixedWidth, headerRect.yMin + 0.5f * (headerRect.height - errorIcon.height), errorIcon.width, errorIcon.height); + if (iconRect.width > 0 && iconRect.height > 0) + { + GUI.DrawTexture(iconRect, errorIcon, ScaleMode.ScaleAndCrop, true, 0, DirectorStyles.kClipErrorColor, 0, 0); + } + + // Draw the name + + // m_ScriptableObject == null will return true because the script can't be loaded. so this checks + // to make sure it is actually not null so we can grab the name + object o = m_ScriptableObject; + if (o != null) + { + s_GUIContent.text = m_ScriptableObject.name; + var textStyle = styles.trackHeaderFont; + textStyle.normal.textColor = styles.customSkin.colorTrackFont; // TODO -- we shouldn't modify the style like this. track header does it though :( + Rect textRect = headerRect; + textRect.xMin = iconRect.xMax + 1; + textRect.xMax = Math.Min(textRect.xMin + styles.trackHeaderFont.CalcSize(s_GUIContent).x, headerRect.xMax - 1); + EditorGUI.LabelField(textRect, s_GUIContent, textStyle); + } + + + // Draw the color swatch to the left of the track, darkened by the mute + var color = Color.Lerp(DirectorStyles.kClipErrorColor, styles.customSkin.colorTrackDarken, styles.customSkin.colorTrackDarken.a); + color.a = 1; + using (new GUIColorOverride(color)) + { + var colorSwatchRect = headerRect; + colorSwatchRect.width = styles.trackSwatchStyle.fixedWidth; + GUI.Label(colorSwatchRect, GUIContent.none, styles.trackSwatchStyle); + } + + // draw darken overlay + EditorGUI.DrawRect(bgRect, styles.customSkin.colorTrackDarken); + + DrawRemoveMenu(headerRect, state); + } + + void DrawRemoveMenu(Rect headerRect, WindowState state) + { + const float pad = 3; + const float buttonSize = 16; + var buttonRect = new Rect(headerRect.xMax - buttonSize - pad, headerRect.y + ((headerRect.height - buttonSize) / 2f) + 2, buttonSize, buttonSize); + + if (GUI.Button(buttonRect, GUIContent.none, DirectorStyles.Instance.trackOptions)) + { + GenericMenu menu = new GenericMenu(); + + var owner = m_Owner; + var scriptableObject = m_ScriptableObject; + + menu.AddItem(Styles.RemoveTrack, false, () => + { + if (TrackExtensions.RemoveBrokenTrack(owner, scriptableObject)) + state.Refresh(); + } + ); + + menu.ShowAsContext(); + } + } + + static void DrawMissingTrackBody(Rect contentRect) + { + if (contentRect.width < 0) + return; + + var styles = DirectorStyles.Instance; + + // draw a track rectangle + EditorGUI.DrawRect(contentRect, styles.customSkin.colorTrackDarken); + // draw the warning box + DrawScriptWarningBox(contentRect, Styles.ErrorText); + } + + static void DrawScriptWarningBox(Rect trackRect, GUIContent content) + { + var styles = DirectorStyles.Instance; + const float kTextPadding = 52f; + + var errorIcon = Styles.IconWarn; + float textWidth = styles.fontClip.CalcSize(content).x; + + var outerRect = trackRect; + outerRect.width = textWidth + kTextPadding + errorIcon.width; + outerRect.x += (trackRect.width - outerRect.width) / 2f; + outerRect.height -= 4f; + outerRect.y += 1f; + + bool drawText = true; + if (outerRect.width > trackRect.width) + { + outerRect.x = trackRect.x; + outerRect.width = trackRect.width; + drawText = false; + } + + var innerRect = new Rect(outerRect.x + 2, outerRect.y + 2, outerRect.width - 4, outerRect.height - 4); + using (new GUIColorOverride(Styles.WarningBoxHighlightColor)) + GUI.Box(outerRect, GUIContent.none, styles.displayBackground); + using (new GUIColorOverride(Styles.WarningBoxBackgroundColor)) + GUI.Box(innerRect, GUIContent.none, styles.displayBackground); + + + if (drawText) + { + var iconRect = new Rect(outerRect.x + kTextPadding / 2.0f - 4.0f, outerRect.y + (outerRect.height - errorIcon.height) / 2.0f, errorIcon.width, errorIcon.height); + var textRect = new Rect(iconRect.xMax + 4.0f, outerRect.y, textWidth, outerRect.height); + + GUI.DrawTexture(iconRect, errorIcon, ScaleMode.ScaleAndCrop, true, 0, Styles.WarningBoxHighlightColor, 0, 0); + Graphics.ShadowLabel(textRect, content, styles.fontClip, Color.white, Color.black); + } + else if (errorIcon.width > innerRect.width) + { + var iconRect = new Rect(outerRect.x + (outerRect.width - errorIcon.width) / 2.0f, outerRect.y + (outerRect.height - errorIcon.height) / 2.0f, errorIcon.width, errorIcon.height); + GUI.DrawTexture(iconRect, errorIcon, ScaleMode.ScaleAndCrop, true, 0, Styles.WarningBoxHighlightColor, 0, 0); + } + } + + public override void OnGraphRebuilt() {} + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTrackErrorGUI.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTrackErrorGUI.cs.meta new file mode 100644 index 0000000..dd754e2 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTrackErrorGUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 200617708f5b36a4da2e2a3f1ceacedd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTrackGUI.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTrackGUI.cs new file mode 100644 index 0000000..c15d96d --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTrackGUI.cs @@ -0,0 +1,812 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor.IMGUI.Controls; +using UnityEditor.StyleSheets; +using UnityEngine; +using UnityEngine.Timeline; +using UnityEngine.Playables; +using Object = UnityEngine.Object; + +namespace UnityEditor.Timeline +{ + class TimelineTrackGUI : TimelineGroupGUI, IClipCurveEditorOwner, IRowGUI + { + struct TrackDrawData + { + public bool m_AllowsRecording; + public bool m_ShowTrackBindings; + public bool m_HasBinding; + public bool m_IsSubTrack; + public PlayableBinding m_Binding; + public UnityEngine.Object m_TrackBinding; + public Texture m_TrackIcon; + } + + static class Styles + { + public static readonly string kArmForRecordDisabled = L10n.Tr("Recording is not permitted when Track Offsets are set to Auto. Track Offset settings can be changed in the track menu of the base track."); + public static Texture2D kProblemIcon = DirectorStyles.GetBackgroundImage(DirectorStyles.Instance.warning); + } + + static GUIContent s_ArmForRecordContentOn; + static GUIContent s_ArmForRecordContentOff; + static GUIContent s_ArmForRecordDisabled; + + bool m_InlineCurvesSkipped; + int m_TrackHash = -1; + int m_BlendHash = -1; + int m_LastDirtyIndex = -1; + + readonly InfiniteTrackDrawer m_InfiniteTrackDrawer; + TrackItemsDrawer m_ItemsDrawer; + TrackDrawData m_TrackDrawData; + TrackDrawOptions m_TrackDrawOptions; + readonly TrackEditor m_TrackEditor; + readonly GUIContent m_DefaultTrackIcon; + + + public override bool expandable + { + get { return hasChildren; } + } + + internal InlineCurveEditor inlineCurveEditor { get; set; } + + public ClipCurveEditor clipCurveEditor { get; private set; } + + public bool inlineCurvesSelected + { + get { return SelectionManager.IsCurveEditorFocused(this); } + set + { + if (!value && SelectionManager.IsCurveEditorFocused(this)) + SelectionManager.SelectInlineCurveEditor(null); + else + SelectionManager.SelectInlineCurveEditor(this); + } + } + + bool IClipCurveEditorOwner.showLoops + { + get { return false; } + } + + TrackAsset IClipCurveEditorOwner.owner + { + get { return track; } + } + + static bool DoesTrackAllowsRecording(TrackAsset track) + { + // if the root animation track is in auto mode, recording is not allowed + var animTrack = TimelineUtility.GetSceneReferenceTrack(track) as AnimationTrack; + if (animTrack != null) + return animTrack.trackOffset != TrackOffset.Auto; + + return false; + } + + bool? m_TrackHasAnimatableParameters; + bool trackHasAnimatableParameters + { + get + { + // cache this value to avoid the recomputation + if (!m_TrackHasAnimatableParameters.HasValue) + m_TrackHasAnimatableParameters = track.HasAnyAnimatableParameters() || + track.clips.Any(c => c.HasAnyAnimatableParameters()); + + return m_TrackHasAnimatableParameters.Value; + } + } + + public bool locked + { + get { return track.lockedInHierarchy; } + } + + public bool showMarkers + { + get { return track.GetShowMarkers(); } + } + + public bool muted + { + get { return track.muted; } + } + + public List<TimelineClipGUI> clips + { + get + { + return m_ItemsDrawer.clips == null ? new List<TimelineClipGUI>(0) : m_ItemsDrawer.clips.ToList(); + } + } + + TrackAsset IRowGUI.asset { get { return track; } } + + bool showTrackRecordingDisabled + { + get + { + // if the root animation track is in auto mode, recording is not allowed + var animTrack = TimelineUtility.GetSceneReferenceTrack(track) as AnimationTrack; + return animTrack != null && animTrack.trackOffset == TrackOffset.Auto; + } + } + + public TimelineTrackGUI(TreeViewController tv, TimelineTreeViewGUI w, int id, int depth, TreeViewItem parent, string displayName, TrackAsset sequenceActor) + : base(tv, w, id, depth, parent, displayName, sequenceActor, false) + { + AnimationTrack animationTrack = sequenceActor as AnimationTrack; + if (animationTrack != null) + m_InfiniteTrackDrawer = new InfiniteTrackDrawer(new AnimationTrackKeyDataSource(animationTrack)); + else if (sequenceActor.HasAnyAnimatableParameters() && !sequenceActor.clips.Any()) + m_InfiniteTrackDrawer = new InfiniteTrackDrawer(new TrackPropertyCurvesDataSource(sequenceActor)); + + UpdateInfiniteClipEditor(w.TimelineWindow); + + var bindings = track.outputs.ToArray(); + m_TrackDrawData.m_HasBinding = bindings.Length > 0; + if (m_TrackDrawData.m_HasBinding) + m_TrackDrawData.m_Binding = bindings[0]; + m_TrackDrawData.m_IsSubTrack = IsSubTrack(); + m_TrackDrawData.m_AllowsRecording = DoesTrackAllowsRecording(sequenceActor); + m_DefaultTrackIcon = TrackResourceCache.GetTrackIcon(track); + + + m_TrackEditor = CustomTimelineEditorCache.GetTrackEditor(sequenceActor); + + try + { + m_TrackDrawOptions = m_TrackEditor.GetTrackOptions(track, null); + } + catch (Exception e) + { + Debug.LogException(e); + m_TrackDrawOptions = CustomTimelineEditorCache.GetDefaultTrackEditor().GetTrackOptions(track, null); + } + + m_TrackDrawOptions.errorText = null; // explicitly setting to null for an uninitialized state + + RebuildGUICacheIfNecessary(); + } + + public override float GetVerticalSpacingBetweenTracks() + { + if (track != null && track.isSubTrack) + return 1.0f; // subtracks have less of a gap than tracks + return base.GetVerticalSpacingBetweenTracks(); + } + + void UpdateInfiniteClipEditor(TimelineWindow window) + { + if (clipCurveEditor != null || track == null || !track.ShouldShowInfiniteClipEditor()) + return; + + var dataSource = CurveDataSource.Create(this); + clipCurveEditor = new ClipCurveEditor(dataSource, window, track); + } + + void DetectTrackChanged() + { + if (Event.current.type == EventType.Layout) + { + // incremented when a track or it's clips changed + if (m_LastDirtyIndex != track.DirtyIndex) + { + try + { + m_TrackEditor.OnTrackChanged(track); + } + catch (Exception e) + { + Debug.LogException(e); + } + m_LastDirtyIndex = track.DirtyIndex; + } + OnTrackChanged(); + } + } + + // Called when the source track data, including it's clips have changed has changed. + void OnTrackChanged() + { + // recompute blends if necessary + int newBlendHash = BlendHash(); + if (m_BlendHash != newBlendHash) + { + UpdateClipOverlaps(); + m_BlendHash = newBlendHash; + } + + RebuildGUICacheIfNecessary(); + } + + void UpdateDrawData(WindowState state) + { + if (Event.current.type == EventType.Layout) + { + m_TrackDrawData.m_ShowTrackBindings = false; + m_TrackDrawData.m_TrackBinding = null; + + + if (state.editSequence.director != null && showSceneReference) + { + m_TrackDrawData.m_ShowTrackBindings = state.GetWindow().currentMode.ShouldShowTrackBindings(state); + m_TrackDrawData.m_TrackBinding = state.editSequence.director.GetGenericBinding(track); + } + + var lastError = m_TrackDrawOptions.errorText; + var lastHeight = m_TrackDrawOptions.minimumHeight; + try + { + m_TrackDrawOptions = m_TrackEditor.GetTrackOptions(track, m_TrackDrawData.m_TrackBinding); + } + catch (Exception e) + { + Debug.LogException(e); + m_TrackDrawOptions = CustomTimelineEditorCache.GetDefaultTrackEditor().GetTrackOptions(track, m_TrackDrawData.m_TrackBinding); + } + + m_TrackDrawData.m_AllowsRecording = DoesTrackAllowsRecording(track); + m_TrackDrawData.m_TrackIcon = m_TrackDrawOptions.icon; + if (m_TrackDrawData.m_TrackIcon == null) + m_TrackDrawData.m_TrackIcon = m_DefaultTrackIcon.image; + + // track height has changed. need to update gui + if (!Mathf.Approximately(lastHeight, m_TrackDrawOptions.minimumHeight)) + state.Refresh(); + } + } + + public override void Draw(Rect headerRect, Rect contentRect, WindowState state) + { + DetectTrackChanged(); + UpdateDrawData(state); + + UpdateInfiniteClipEditor(state.GetWindow()); + + var trackHeaderRect = headerRect; + var trackContentRect = contentRect; + + float inlineCurveHeight = contentRect.height - GetTrackContentHeight(state); + bool hasInlineCurve = inlineCurveHeight > 0.0f; + + if (hasInlineCurve) + { + trackHeaderRect.height -= inlineCurveHeight; + trackContentRect.height -= inlineCurveHeight; + } + + if (Event.current.type == EventType.Repaint) + { + m_TreeViewRect = trackContentRect; + } + + if (s_ArmForRecordContentOn == null) + s_ArmForRecordContentOn = new GUIContent(DirectorStyles.GetBackgroundImage(DirectorStyles.Instance.autoKey, StyleState.active)); + + if (s_ArmForRecordContentOff == null) + s_ArmForRecordContentOff = new GUIContent(DirectorStyles.GetBackgroundImage(DirectorStyles.Instance.autoKey)); + + if (s_ArmForRecordDisabled == null) + s_ArmForRecordDisabled = new GUIContent(DirectorStyles.GetBackgroundImage(DirectorStyles.Instance.autoKey), Styles.kArmForRecordDisabled); + + track.SetCollapsed(!isExpanded); + + RebuildGUICacheIfNecessary(); + + // Prevents from drawing outside of bounds, but does not effect layout or markers + bool isOwnerDrawSucceed = false; + + Vector2 visibleTime = state.timeAreaShownRange; + + if (drawer != null) + isOwnerDrawSucceed = drawer.DrawTrack(trackContentRect, track, visibleTime, state); + + if (!isOwnerDrawSucceed) + { + using (new GUIViewportScope(trackContentRect)) + DrawBackground(trackContentRect, track, visibleTime, state); + + if (m_InfiniteTrackDrawer != null) + m_InfiniteTrackDrawer.DrawTrack(trackContentRect, track, visibleTime, state); + + // draw after user customization so overlay text shows up + using (new GUIViewportScope(trackContentRect)) + m_ItemsDrawer.Draw(trackContentRect, state); + } + + DrawTrackHeader(trackHeaderRect, state); + + if (hasInlineCurve) + { + var curvesHeaderRect = headerRect; + curvesHeaderRect.yMin = trackHeaderRect.yMax; + + var curvesContentRect = contentRect; + curvesContentRect.yMin = trackContentRect.yMax; + + DrawInlineCurves(curvesHeaderRect, curvesContentRect, state); + } + + DrawTrackColorKind(headerRect); + DrawTrackState(contentRect, contentRect, track); + } + + void DrawInlineCurves(Rect curvesHeaderRect, Rect curvesContentRect, WindowState state) + { + if (!track.GetShowInlineCurves()) + return; + + // Inline curves are not within the editor window -- case 952571 + if (!IsInlineCurvesEditorInBounds(ToWindowSpace(curvesHeaderRect), curvesContentRect.height, state)) + { + m_InlineCurvesSkipped = true; + return; + } + + // If inline curves were skipped during the last event; we want to avoid rendering them until + // the next Layout event. Otherwise, we still get the RTE prevented above when the user resizes + // the timeline window very fast. -- case 952571 + if (m_InlineCurvesSkipped && Event.current.type != EventType.Layout) + return; + + m_InlineCurvesSkipped = false; + + if (inlineCurveEditor == null) + inlineCurveEditor = new InlineCurveEditor(this); + + + curvesHeaderRect.x += DirectorStyles.kBaseIndent; + curvesHeaderRect.width -= DirectorStyles.kBaseIndent; + + inlineCurveEditor.Draw(curvesHeaderRect, curvesContentRect, state); + } + + static bool IsInlineCurvesEditorInBounds(Rect windowSpaceTrackRect, float inlineCurveHeight, WindowState state) + { + var legalHeight = state.windowHeight; + var trackTop = windowSpaceTrackRect.y; + var inlineCurveOffset = windowSpaceTrackRect.height - inlineCurveHeight; + return legalHeight - trackTop - inlineCurveOffset > 0; + } + + void DrawErrorIcon(Rect position, WindowState state) + { + Rect bindingLabel = position; + bindingLabel.x = position.xMax + 3; + bindingLabel.width = state.bindingAreaWidth; + EditorGUI.LabelField(position, m_ProblemIcon); + } + + void DrawBackground(Rect trackRect, TrackAsset trackAsset, Vector2 visibleTime, WindowState state) + { + bool canDrawRecordBackground = IsRecording(state); + if (canDrawRecordBackground) + { + DrawRecordingTrackBackground(trackRect, trackAsset, visibleTime, state); + } + else + { + Color trackBackgroundColor; + + if (SelectionManager.Contains(track)) + { + trackBackgroundColor = state.IsEditingASubTimeline() ? + DirectorStyles.Instance.customSkin.colorTrackSubSequenceBackgroundSelected : + DirectorStyles.Instance.customSkin.colorTrackBackgroundSelected; + } + else + { + trackBackgroundColor = state.IsEditingASubTimeline() ? + DirectorStyles.Instance.customSkin.colorTrackSubSequenceBackground : + DirectorStyles.Instance.customSkin.colorTrackBackground; + } + + EditorGUI.DrawRect(trackRect, trackBackgroundColor); + } + } + + float InlineCurveHeight() + { + return track.GetShowInlineCurves() && CanDrawInlineCurve() + ? TimelineWindowViewPrefs.GetInlineCurveHeight(track) + : 0.0f; + } + + public override float GetHeight(WindowState state) + { + var height = GetTrackContentHeight(state); + + if (CanDrawInlineCurve()) + height += InlineCurveHeight(); + + return height; + } + + float GetTrackContentHeight(WindowState state) + { + float height = m_TrackDrawOptions.minimumHeight; + if (height <= 0.0f) + height = TrackEditor.DefaultTrackHeight; + height = Mathf.Clamp(height, TrackEditor.MinimumTrackHeight, TrackEditor.MaximumTrackHeight); + + return height * state.trackScale; + } + + static bool CanDrawIcon(GUIContent icon) + { + return icon != null && icon != GUIContent.none && icon.image != null; + } + + bool showSceneReference + { + get + { + return track != null && + m_TrackDrawData.m_HasBinding && + !m_TrackDrawData.m_IsSubTrack && + m_TrackDrawData.m_Binding.sourceObject != null && + m_TrackDrawData.m_Binding.outputTargetType != null && + typeof(Object).IsAssignableFrom(m_TrackDrawData.m_Binding.outputTargetType); + } + } + + void DrawTrackHeader(Rect trackHeaderRect, WindowState state) + { + using (new GUIViewportScope(trackHeaderRect)) + { + Rect rect = trackHeaderRect; + + DrawHeaderBackground(trackHeaderRect); + rect.x += m_Styles.trackSwatchStyle.fixedWidth; + + const float buttonSize = WindowConstants.trackHeaderButtonSize; + const float padding = WindowConstants.trackHeaderButtonPadding; + var buttonRect = new Rect(trackHeaderRect.xMax - buttonSize - padding, rect.y + ((rect.height - buttonSize) / 2f), buttonSize, buttonSize); + + rect.x += DrawTrackIconKind(rect, state); + DrawTrackBinding(rect, trackHeaderRect); + + if (track is GroupTrack) + return; + + buttonRect.x -= Spaced(DrawTrackDropDownMenu(buttonRect)); + buttonRect.x -= Spaced(DrawLockMarkersButton(buttonRect, state)); + buttonRect.x -= Spaced(DrawInlineCurveButton(buttonRect, state)); + buttonRect.x -= Spaced(DrawMuteButton(buttonRect, state)); + buttonRect.x -= Spaced(DrawLockButton(buttonRect, state)); + buttonRect.x -= Spaced(DrawRecordButton(buttonRect, state)); + buttonRect.x -= Spaced(DrawCustomTrackButton(buttonRect, state)); + } + } + + void DrawHeaderBackground(Rect headerRect) + { + Color backgroundColor = SelectionManager.Contains(track) + ? DirectorStyles.Instance.customSkin.colorSelection + : DirectorStyles.Instance.customSkin.colorTrackHeaderBackground; + + var bgRect = headerRect; + bgRect.x += m_Styles.trackSwatchStyle.fixedWidth; + bgRect.width -= m_Styles.trackSwatchStyle.fixedWidth; + + EditorGUI.DrawRect(bgRect, backgroundColor); + } + + void DrawTrackColorKind(Rect rect) + { + // subtracks don't draw the color, the parent does that. + if (track != null && track.isSubTrack) + return; + + if (rect.width <= 0) return; + + using (new GUIColorOverride(m_TrackDrawOptions.trackColor)) + { + rect.width = m_Styles.trackSwatchStyle.fixedWidth; + GUI.Label(rect, GUIContent.none, m_Styles.trackSwatchStyle); + } + } + + float DrawTrackIconKind(Rect rect, WindowState state) + { + // no icons on subtracks + if (track != null && track.isSubTrack) + return 0.0f; + + rect.yMin += (rect.height - 16f) / 2f; + rect.width = 16.0f; + rect.height = 16.0f; + + if (!string.IsNullOrEmpty(m_TrackDrawOptions.errorText)) + { + m_ProblemIcon.image = Styles.kProblemIcon; + m_ProblemIcon.tooltip = m_TrackDrawOptions.errorText; + + if (CanDrawIcon(m_ProblemIcon)) + DrawErrorIcon(rect, state); + } + else + { + var content = GUIContent.Temp(m_TrackDrawData.m_TrackIcon, m_DefaultTrackIcon.tooltip); + if (CanDrawIcon(content)) + GUI.Box(rect, content, GUIStyle.none); + } + + return rect.width; + } + + void DrawTrackBinding(Rect rect, Rect headerRect) + { + if (m_TrackDrawData.m_ShowTrackBindings) + { + DoTrackBindingGUI(rect, headerRect); + return; + } + + var textStyle = m_Styles.trackHeaderFont; + textStyle.normal.textColor = SelectionManager.Contains(track) ? Color.white : m_Styles.customSkin.colorTrackFont; + + string trackName = track.name; + + EditorGUI.BeginChangeCheck(); + + // by default the size is just the width of the string (for selection purposes) + rect.width = m_Styles.trackHeaderFont.CalcSize(new GUIContent(trackName)).x; + + // if we are editing, supply the entire width of the header + if (GUIUtility.keyboardControl == track.GetInstanceID()) + rect.width = (headerRect.xMax - rect.xMin) - (5 * WindowConstants.trackHeaderButtonSize); + + trackName = EditorGUI.DelayedTextField(rect, GUIContent.none, track.GetInstanceID(), track.name, textStyle); + + if (EditorGUI.EndChangeCheck()) + { + TimelineUndo.PushUndo(track, "Rename Track"); + track.name = trackName; + } + } + + float DrawTrackDropDownMenu(Rect rect) + { + rect.y += WindowConstants.trackOptionButtonVerticalPadding; + + if (GUI.Button(rect, GUIContent.none, m_Styles.trackOptions)) + { + // the drop down will apply to all selected tracks + if (!SelectionManager.Contains(track)) + { + SelectionManager.Clear(); + SelectionManager.Add(track); + } + + SequencerContextMenu.ShowTrackContextMenu(SelectionManager.SelectedTracks().ToArray(), null); + } + + return WindowConstants.trackHeaderButtonSize; + } + + bool CanDrawInlineCurve() + { + // Note: A track with animatable parameters always has inline curves. + return trackHasAnimatableParameters || TimelineUtility.TrackHasAnimationCurves(track); + } + + float DrawInlineCurveButton(Rect rect, WindowState state) + { + if (!CanDrawInlineCurve()) + { + return 0.0f; + } + + // Override enable state to display "Show Inline Curves" button in disabled state. + bool prevEnabledState = GUI.enabled; + GUI.enabled = true; + var newValue = GUI.Toggle(rect, track.GetShowInlineCurves(), GUIContent.none, DirectorStyles.Instance.curves); + GUI.enabled = prevEnabledState; + + if (newValue != track.GetShowInlineCurves()) + { + track.SetShowInlineCurves(newValue); + state.GetWindow().treeView.CalculateRowRects(); + } + + return WindowConstants.trackHeaderButtonSize; + } + + float DrawRecordButton(Rect rect, WindowState state) + { + if (m_TrackDrawData.m_AllowsRecording) + { + bool isPlayerDisabled = state.editSequence.director != null && !state.editSequence.director.isActiveAndEnabled; + + GameObject goBinding = m_TrackDrawData.m_TrackBinding as GameObject; + if (goBinding == null) + { + Component c = m_TrackDrawData.m_TrackBinding as Component; + if (c != null) + goBinding = c.gameObject; + } + + if (goBinding == null && m_TrackDrawData.m_IsSubTrack) + { + goBinding = ParentTrack().GetGameObjectBinding(state.editSequence.director); + } + + bool isTrackBindingValid = goBinding != null; + bool trackErrorDisableButton = !string.IsNullOrEmpty(m_TrackDrawOptions.errorText) && isTrackBindingValid && goBinding.activeInHierarchy; + bool disableButton = track.lockedInHierarchy || isPlayerDisabled || trackErrorDisableButton || !isTrackBindingValid; + using (new EditorGUI.DisabledScope(disableButton)) + { + if (IsRecording(state)) + { + state.editorWindow.Repaint(); + float remainder = Time.realtimeSinceStartup % 1; + + var animatedContent = s_ArmForRecordContentOn; + if (remainder < 0.22f) + { + animatedContent = GUIContent.none; + } + if (GUI.Button(rect, animatedContent, GUIStyle.none) || isPlayerDisabled || !isTrackBindingValid) + { + state.UnarmForRecord(track); + } + } + else + { + if (GUI.Button(rect, s_ArmForRecordContentOff, GUIStyle.none)) + { + state.ArmForRecord(track); + } + } + return WindowConstants.trackHeaderButtonSize; + } + } + + if (showTrackRecordingDisabled) + { + using (new EditorGUI.DisabledScope(true)) + GUI.Button(rect, s_ArmForRecordDisabled, GUIStyle.none); + return k_ButtonSize; + } + + return 0.0f; + } + + float DrawCustomTrackButton(Rect rect, WindowState state) + { + if (drawer.DrawTrackHeaderButton(rect, track, state)) + { + return WindowConstants.trackHeaderButtonSize; + } + return 0.0f; + } + + float DrawLockMarkersButton(Rect rect, WindowState state) + { + if (track.GetMarkerCount() == 0) + return 0.0f; + + var markersShown = showMarkers; + var style = TimelineWindow.styles.collapseMarkers; + if (Event.current.type == EventType.Repaint) + style.Draw(rect, GUIContent.none, false, false, markersShown, false); + + // Override enable state to display "Show Marker" button in disabled state. + bool prevEnabledState = GUI.enabled; + GUI.enabled = true; + if (GUI.Button(rect, DirectorStyles.markerCollapseButton, GUIStyle.none)) + { + state.GetWindow().SetShowTrackMarkers(track, !markersShown); + } + GUI.enabled = prevEnabledState; + return WindowConstants.trackHeaderButtonSize; + } + + static void ObjectBindingField(Rect position, Object obj, PlayableBinding binding) + { + bool allowScene = + typeof(GameObject).IsAssignableFrom(binding.outputTargetType) || + typeof(Component).IsAssignableFrom(binding.outputTargetType); + + using (var check = new EditorGUI.ChangeCheckScope()) + { + // FocusType.Passive so it never gets focused when pressing tab + int controlId = GUIUtility.GetControlID("s_ObjectFieldHash".GetHashCode(), FocusType.Passive, position); + var newObject = UnityEditorInternals.DoObjectField(EditorGUI.IndentedRect(position), obj, binding.outputTargetType, controlId, allowScene); + if (check.changed) + { + BindingUtility.Bind(TimelineEditor.inspectedDirector, binding.sourceObject as TrackAsset, newObject); + } + } + } + + void DoTrackBindingGUI(Rect rect, Rect headerRect) + { + var bindingRect = new Rect( + rect.xMin, + rect.y + (rect.height - WindowConstants.trackHeaderButtonSize) / 2f, + headerRect.xMax - WindowConstants.trackHeaderMaxButtonsWidth - rect.xMin, + WindowConstants.trackHeaderButtonSize); + + if (bindingRect.Contains(Event.current.mousePosition) && TimelineDragging.IsDraggingEvent() && DragAndDrop.objectReferences.Length == 1) + { + TimelineDragging.HandleBindingDragAndDrop(track, BindingUtility.GetRequiredBindingType(m_TrackDrawData.m_Binding)); + Event.current.Use(); + } + else + { + if (m_TrackDrawData.m_Binding.outputTargetType != null && typeof(Object).IsAssignableFrom(m_TrackDrawData.m_Binding.outputTargetType)) + { + ObjectBindingField(bindingRect, m_TrackDrawData.m_TrackBinding, m_TrackDrawData.m_Binding); + } + } + } + + bool IsRecording(WindowState state) + { + return state.recording && state.IsArmedForRecord(track); + } + + // background to draw during recording + void DrawRecordingTrackBackground(Rect trackRect, TrackAsset trackAsset, Vector2 visibleTime, WindowState state) + { + if (drawer != null) + drawer.DrawRecordingBackground(trackRect, trackAsset, visibleTime, state); + } + + void UpdateClipOverlaps() + { + TrackExtensions.ComputeBlendsFromOverlaps(track.clips); + } + + internal void RebuildGUICacheIfNecessary() + { + if (m_TrackHash == track.Hash()) + return; + + m_ItemsDrawer = new TrackItemsDrawer(this); + m_TrackHash = track.Hash(); + } + + int BlendHash() + { + var hash = 0; + foreach (var clip in track.clips) + { + hash = HashUtility.CombineHash(hash, + (clip.duration - clip.start).GetHashCode(), + ((int)clip.blendInCurveMode).GetHashCode(), + ((int)clip.blendOutCurveMode).GetHashCode()); + } + return hash; + } + + // callback when the corresponding graph is rebuilt. This can happen, but not have the GUI rebuilt. + public override void OnGraphRebuilt() + { + RefreshCurveEditor(); + } + + void RefreshCurveEditor() + { + var window = TimelineWindow.instance; + if (track != null && window != null && window.state != null) + { + bool hasEditor = clipCurveEditor != null; + bool shouldHaveEditor = track.ShouldShowInfiniteClipEditor(); + if (hasEditor != shouldHaveEditor) + window.state.AddEndFrameDelegate((x, currentEvent) => + { + x.Refresh(); + return true; + }); + } + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTrackGUI.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTrackGUI.cs.meta new file mode 100644 index 0000000..bff54b9 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTrackGUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 468e1bfc887161e4196f33e942fc3199 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTreeView.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTreeView.cs new file mode 100644 index 0000000..9345d9a --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTreeView.cs @@ -0,0 +1,439 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEditor.IMGUI.Controls; +using UnityEngine; + +namespace UnityEditor.Timeline +{ + class TimelineTreeView : ITreeViewGUI + { + float m_FoldoutWidth; + Rect m_DraggingInsertionMarkerRect; + readonly TreeViewController m_TreeView; + + List<Rect> m_RowRects = new List<Rect>(); + List<Rect> m_ExpandedRowRects = new List<Rect>(); + + float m_MaxWidthOfRows; + readonly WindowState m_State; + + static readonly float kMinTrackHeight = 25.0f; + static readonly float kFoldOutOffset = 14.0f; + + static DirectorStyles m_Styles; + + public bool showInsertionMarker { get; set; } + public virtual float topRowMargin { get; private set; } + public virtual float bottomRowMargin { get; private set; } + + public TimelineTreeView(TimelineWindow sequencerWindow, TreeViewController treeView) + { + m_TreeView = treeView; + m_TreeView.useExpansionAnimation = true; + + m_TreeView.selectionChangedCallback += SelectionChangedCallback; + m_TreeView.contextClickOutsideItemsCallback += ContextClickOutsideItemsCallback; + m_TreeView.itemDoubleClickedCallback += ItemDoubleClickedCallback; + m_TreeView.contextClickItemCallback += ContextClickItemCallback; + + m_TreeView.SetConsumeKeyDownEvents(false); + m_Styles = DirectorStyles.Instance; + m_State = sequencerWindow.state; + + m_FoldoutWidth = DirectorStyles.Instance.foldout.fixedWidth; + } + + void ItemDoubleClickedCallback(int id) + { + var trackGUI = m_TreeView.FindItem(id) as TimelineTrackGUI; + if (trackGUI == null) + return; + + if (trackGUI.track == null || trackGUI.track.lockedInHierarchy) + return; + + var selection = SelectionManager.SelectedItems().ToList(); + var items = ItemsUtils.GetItems(trackGUI.track).ToList(); + var addToSelection = !selection.SequenceEqual(items); + + foreach (var i in items) + { + if (addToSelection) + SelectionManager.Add(i); + else + SelectionManager.Remove(i); + } + } + + void ContextClickOutsideItemsCallback() + { + SequencerContextMenu.ShowNewTracksContextMenu(null, m_State); + Event.current.Use(); + } + + void ContextClickItemCallback(int id) + { + // may not occur if another menu is active + if (!m_TreeView.IsSelected(id)) + SelectionChangedCallback(new[] {id}); + + SequencerContextMenu.ShowTrackContextMenu(SelectionManager.SelectedTracks().ToArray(), Event.current.mousePosition); + + Event.current.Use(); + } + + void SelectionChangedCallback(int[] ids) + { + if (Event.current.button == 1 && PickerUtils.PickedLayerableOfType<ISelectable>() != null) + return; + + if (Event.current.command || Event.current.control || Event.current.shift) + SelectionManager.UnSelectTracks(); + else + SelectionManager.Clear(); + + foreach (var id in ids) + { + var trackGUI = (TimelineTrackBaseGUI)m_TreeView.FindItem(id); + SelectionManager.Add(trackGUI.track); + } + + m_State.GetWindow().Repaint(); + } + + public void OnInitialize() {} + + public Rect GetRectForFraming(int row) + { + return GetRowRect(row, 1); // We ignore width by default when framing (only y scroll is affected) + } + + protected virtual Vector2 GetSizeOfRow(TreeViewItem item) + { + if (item.displayName == "root") + return new Vector2(m_TreeView.GetTotalRect().width, 0.0f); + + var trackGroupGui = item as TimelineGroupGUI; + if (trackGroupGui != null) + { + return new Vector2(m_TreeView.GetTotalRect().width, trackGroupGui.GetHeight(m_State)); + } + + float height = TrackEditor.DefaultTrackHeight; + if (item.hasChildren && m_TreeView.data.IsExpanded(item)) + { + height = Mathf.Min(TrackEditor.DefaultTrackHeight, kMinTrackHeight); + } + + return new Vector2(m_TreeView.GetTotalRect().width, height); + } + + public virtual void BeginRowGUI() + { + if (m_TreeView.GetTotalRect().width != GetRowRect(0).width) + { + CalculateRowRects(); + } + + m_DraggingInsertionMarkerRect.x = -1; + + m_TreeView.SetSelection(SelectionManager.SelectedTrackGUI().Select(t => t.id).ToArray(), false); + } + + public virtual void EndRowGUI() + { + // Draw row marker when dragging + if (m_DraggingInsertionMarkerRect.x >= 0 && Event.current.type == EventType.Repaint) + { + Rect insertionRect = m_DraggingInsertionMarkerRect; + const float insertionHeight = 1.0f; + insertionRect.height = insertionHeight; + + if (m_TreeView.dragging.drawRowMarkerAbove) + insertionRect.y -= insertionHeight * 0.5f + 2.0f; + else + insertionRect.y += m_DraggingInsertionMarkerRect.height - insertionHeight * 0.5f + 1.0f; + + EditorGUI.DrawRect(insertionRect, Color.white); + } + } + + public virtual void OnRowGUI(Rect rowRect, TreeViewItem item, int row, bool selected, bool focused) + { + using (new EditorGUI.DisabledScope(TimelineWindow.instance.currentMode.TrackState(TimelineWindow.instance.state) == TimelineModeGUIState.Disabled)) + { + var sqvi = (TimelineTrackBaseGUI)item; + sqvi.treeViewToWindowTransformation = m_TreeView.GetTotalRect().position - m_TreeView.state.scrollPos; + + // this may be called because an encompassing parent is visible + if (!sqvi.visibleExpanded) + return; + + Rect headerRect = rowRect; + Rect contentRect = rowRect; + + headerRect.width = m_State.sequencerHeaderWidth - 2.0f; + contentRect.xMin += m_State.sequencerHeaderWidth; + contentRect.width = rowRect.width - m_State.sequencerHeaderWidth - 1.0f; + + Rect foldoutRect = rowRect; + + var indent = GetFoldoutIndent(item); + var headerRectWithIndent = headerRect; + headerRectWithIndent.xMin = indent; + var rowRectWithIndent = new Rect(rowRect.x + indent, rowRect.y, rowRect.width - indent, rowRect.height); + sqvi.Draw(headerRectWithIndent, contentRect, m_State); + sqvi.DrawInsertionMarkers(rowRectWithIndent); + + if (Event.current.type == EventType.Repaint) + { + m_State.spacePartitioner.AddBounds(sqvi); + + // Show marker below this Item + if (showInsertionMarker) + { + if (m_TreeView.dragging != null && m_TreeView.dragging.GetRowMarkerControlID() == TreeViewController.GetItemControlID(item)) + m_DraggingInsertionMarkerRect = rowRectWithIndent; + } + } + + // Draw foldout (after text content above to ensure drop down icon is rendered above selection highlight) + DrawFoldout(item, foldoutRect, indent); + + sqvi.ClearDrawFlags(); + } + } + + void DrawFoldout(TreeViewItem item, Rect foldoutRect, float indent) + { + var showFoldout = m_TreeView.data.IsExpandable(item); + if (showFoldout) + { + foldoutRect.x = indent - kFoldOutOffset; + foldoutRect.width = m_FoldoutWidth; + EditorGUI.BeginChangeCheck(); + float foldoutIconHeight = DirectorStyles.Instance.foldout.fixedHeight; + foldoutRect.y += foldoutIconHeight / 2.0f; + foldoutRect.height = foldoutIconHeight; + + if (foldoutRect.xMax > m_State.sequencerHeaderWidth) + return; + + //Override Disable state for TrakGroup toggle button to expand/collapse group. + bool previousEnableState = GUI.enabled; + GUI.enabled = true; + bool newExpandedValue = GUI.Toggle(foldoutRect, m_TreeView.data.IsExpanded(item), GUIContent.none, m_Styles.foldout); + GUI.enabled = previousEnableState; + + if (EditorGUI.EndChangeCheck()) + { + if (Event.current.alt) + m_TreeView.data.SetExpandedWithChildren(item, newExpandedValue); + else + m_TreeView.data.SetExpanded(item, newExpandedValue); + } + } + } + + public Rect GetRenameRect(Rect rowRect, int row, TreeViewItem item) + { + return rowRect; + } + + public void BeginPingItem(TreeViewItem item, float topPixelOfRow, float availableWidth) {} + public void EndPingItem() {} + + public Rect GetRowRect(int row, float rowWidth) + { + return GetRowRect(row); + } + + public Rect GetRowRect(int row) + { + if (m_RowRects.Count == 0) + return new Rect(); + + if (row >= m_RowRects.Count) + return new Rect(); + + return m_RowRects[row]; + } + + static float GetSpacing(TreeViewItem item) + { + var trackBase = item as TimelineTrackBaseGUI; + if (trackBase != null) + return trackBase.GetVerticalSpacingBetweenTracks(); + + return 3.0f; + } + + public void CalculateRowRects() + { + if (m_TreeView.isSearching) + return; + + const float startY = 6.0f; + IList<TreeViewItem> rows = m_TreeView.data.GetRows(); + m_RowRects = new List<Rect>(rows.Count); + m_ExpandedRowRects = new List<Rect>(rows.Count); + + float curY = startY; + m_MaxWidthOfRows = 1f; + + // first pass compute the row rects + for (int i = 0; i < rows.Count; ++i) + { + var item = rows[i]; + + if (i != 0) + curY += GetSpacing(item); + + Vector2 rowSize = GetSizeOfRow(item); + m_RowRects.Add(new Rect(0, curY, rowSize.x, rowSize.y)); + m_ExpandedRowRects.Add(m_RowRects[i]); + + curY += rowSize.y; + + if (rowSize.x > m_MaxWidthOfRows) + m_MaxWidthOfRows = rowSize.x; + + // updated the expanded state + var groupGUI = item as TimelineGroupGUI; + if (groupGUI != null) + groupGUI.SetExpanded(m_TreeView.data.IsExpanded(item)); + } + + float halfHeight = halfDropBetweenHeight; + const float kGroupPad = 1.0f; + const float kSkinPadding = 5.0f * 0.6f; + // work bottom up and compute visible regions for groups + for (int i = rows.Count - 1; i > 0; i--) + { + float height = 0; + TimelineTrackBaseGUI item = (TimelineTrackBaseGUI)rows[i]; + if (item.isExpanded && item.children != null && item.children.Count > 0) + { + for (var j = 0; j < item.children.Count; j++) + { + var child = item.children[j]; + int index = rows.IndexOf(child); + if (index > i) + height += m_ExpandedRowRects[index].height + kSkinPadding; + } + + height += kGroupPad; + } + m_ExpandedRowRects[i] = new Rect(m_RowRects[i].x, m_RowRects[i].y, m_RowRects[i].width, m_RowRects[i].height + height); + + var groupGUI = item as TimelineGroupGUI; + if (groupGUI != null) + { + var spacing = GetSpacing(item) + 1; + groupGUI.expandedRect = m_ExpandedRowRects[i]; + groupGUI.rowRect = m_RowRects[i]; + groupGUI.dropRect = new Rect(m_RowRects[i].x, m_RowRects[i].y - spacing, m_RowRects[i].width, m_RowRects[i].height + Mathf.Max(halfHeight, spacing)); + } + } + } + + public virtual bool BeginRename(TreeViewItem item, float delay) + { + return false; + } + + public virtual void EndRename() {} + + protected virtual float GetFoldoutIndent(TreeViewItem item) + { + // Ignore depth when showing search results + if (item.depth <= 1 || m_TreeView.isSearching) + return DirectorStyles.kBaseIndent; + + int depth = item.depth; + var trackGUI = item as TimelineTrackGUI; + + // first level subtracks are not indented + if (trackGUI != null && trackGUI.track != null && trackGUI.track.isSubTrack) + depth--; + + return depth * DirectorStyles.kBaseIndent; + } + + public virtual float GetContentIndent(TreeViewItem item) + { + return GetFoldoutIndent(item); + } + + public int GetNumRowsOnPageUpDown(TreeViewItem fromItem, bool pageUp, float heightOfTreeView) + { + return (int)Mathf.Floor(heightOfTreeView / 30); // return something + } + + // Should return the row number of the first and last row thats fits in the pixel rect defined by top and height + public void GetFirstAndLastRowVisible(out int firstRowVisible, out int lastRowVisible) + { + int rowCount = m_TreeView.data.rowCount; + if (rowCount == 0) + { + firstRowVisible = lastRowVisible = -1; + return; + } + + if (rowCount != m_ExpandedRowRects.Count) + { + Debug.LogError("Mismatch in state: rows vs cached rects. Did you remember to hook up: dataSource.onVisibleRowsChanged += gui.CalculateRowRects ?"); + CalculateRowRects(); + } + + float topPixel = m_TreeView.state.scrollPos.y; + float heightInPixels = m_TreeView.GetTotalRect().height; + + int firstVisible = -1; + int lastVisible = -1; + + Rect visibleRect = new Rect(0, topPixel, m_ExpandedRowRects[0].width, heightInPixels); + for (int i = 0; i < m_ExpandedRowRects.Count; ++i) + { + bool visible = visibleRect.Overlaps(m_ExpandedRowRects[i]); + if (visible) + { + if (firstVisible == -1) + firstVisible = i; + lastVisible = i; + } + + TimelineTrackBaseGUI gui = m_TreeView.data.GetItem(i) as TimelineTrackBaseGUI; + if (gui != null) + { + gui.visibleExpanded = visible; + gui.visibleRow = visibleRect.Overlaps(m_RowRects[i]); + } + } + + if (firstVisible != -1 && lastVisible != -1) + { + firstRowVisible = firstVisible; + lastRowVisible = lastVisible; + } + else + { + firstRowVisible = 0; + lastRowVisible = rowCount - 1; + } + } + + public Vector2 GetTotalSize() + { + if (m_RowRects.Count == 0) + return new Vector2(0, 0); + + return new Vector2(m_MaxWidthOfRows, m_RowRects[m_RowRects.Count - 1].yMax); + } + + public virtual float halfDropBetweenHeight + { + get { return 8f; } + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTreeView.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTreeView.cs.meta new file mode 100644 index 0000000..a555d92 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTreeView.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 82a8ce43816925d4b971951094cb755c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTreeViewGUI.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTreeViewGUI.cs new file mode 100644 index 0000000..6160a35 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTreeViewGUI.cs @@ -0,0 +1,182 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEditor.IMGUI.Controls; +using UnityEngine; +using UnityEngine.Timeline; + +namespace UnityEditor.Timeline +{ + class TimelineTreeViewGUI + { + readonly TimelineAsset m_Timeline; + readonly TreeViewController m_TreeView; + readonly TimelineTreeView m_TimelineTreeView; + readonly TimelineWindow m_Window; + readonly TimelineDataSource m_DataSource; + + TreeViewItem root + { + get { return m_DataSource.root; } + } + + TimelineTrackBaseGUI[] visibleTrackGuis + { + get + { + int firstRow; + int lastRow; + var visibleRows = new List<TimelineTrackBaseGUI>(); + m_TreeView.gui.GetFirstAndLastRowVisible(out firstRow, out lastRow); + + for (int r = firstRow; r <= lastRow; r++) + { + var track = m_TreeView.data.GetItem(r) as TimelineTrackBaseGUI; + if (track != null && track != root) + { + AddVisibleTrackRecursive(ref visibleRows, track); + } + } + return visibleRows.ToArray(); + } + } + + public TrackAsset[] visibleTracks + { + get { return visibleTrackGuis.Select(x => x.track).ToArray(); } + } + + public List<TimelineClipGUI> allClipGuis + { + get + { + TimelineDataSource dataSource = m_TreeView.data as TimelineDataSource; + if (dataSource != null && dataSource.allTrackGuis != null) + return dataSource.allTrackGuis.OfType<TimelineTrackGUI>().SelectMany(x => x.clips).ToList(); + + return null; + } + } + + public List<TimelineTrackBaseGUI> allTrackGuis + { + get + { + var dataSource = m_TreeView.data as TimelineDataSource; + if (dataSource != null) + return dataSource.allTrackGuis; + return null; + } + } + + public Vector2 contentSize + { + get { return m_TreeView.GetContentSize(); } + } + + public Vector2 scrollPosition + { + get { return m_TreeView.state.scrollPos; } + set + { + Rect r = m_TreeView.GetTotalRect(); + Vector2 visibleContent = m_TreeView.GetContentSize(); + m_TreeView.state.scrollPos = new Vector2(value.x, Mathf.Clamp(value.y, 0, Mathf.Max(0, visibleContent.y - r.height))); + } + } + + public bool showingVerticalScrollBar + { + get { return m_TreeView.showingVerticalScrollBar; } + } + + public void FrameItem(TreeViewItem item) + { + m_TreeView.Frame(item.id, true, false, true); + } + + public TimelineDragging timelineDragging { get {return m_TreeView.dragging as TimelineDragging; }} + + public TimelineTreeViewGUI(TimelineWindow sequencerWindow, TimelineAsset timeline, Rect rect) + { + m_Timeline = timeline; + m_Window = sequencerWindow; + + var treeviewState = new TreeViewState(); + treeviewState.scrollPos = new Vector2(treeviewState.scrollPos.x, TimelineWindowViewPrefs.GetOrCreateViewModel(m_Timeline).verticalScroll); + + m_TreeView = new TreeViewController(sequencerWindow, treeviewState); + m_TreeView.horizontalScrollbarStyle = GUIStyle.none; + m_TreeView.scrollViewStyle = GUI.skin.scrollView; + m_TreeView.keyboardInputCallback = sequencerWindow.TreeViewKeyboardCallback; + + + m_TimelineTreeView = new TimelineTreeView(sequencerWindow, m_TreeView); + var dragging = new TimelineDragging(m_TreeView, m_Window, m_Timeline); + m_DataSource = new TimelineDataSource(this, m_TreeView, sequencerWindow); + + m_DataSource.onVisibleRowsChanged += m_TimelineTreeView.CalculateRowRects; + m_TreeView.Init(rect, m_DataSource, m_TimelineTreeView, dragging); + + m_DataSource.ExpandItems(m_DataSource.root); + } + + public ITreeViewGUI gui + { + get { return m_TimelineTreeView; } + } + public ITreeViewDataSource data + { + get { return m_TreeView == null ? null : m_TreeView.data; } + } + + public TimelineWindow TimelineWindow + { + get { return m_Window; } + } + + public void CalculateRowRects() + { + m_TimelineTreeView.CalculateRowRects(); + } + + public void Reload() + { + AnimationClipCurveCache.Instance.ClearCachedProxyClips(); + + m_TreeView.ReloadData(); + m_DataSource.ExpandItems(m_DataSource.root); + m_TimelineTreeView.CalculateRowRects(); + } + + public void OnGUI(Rect rect) + { + int keyboardControl = GUIUtility.GetControlID(FocusType.Passive, rect); + m_TreeView.OnGUI(rect, keyboardControl); + TimelineWindowViewPrefs.GetOrCreateViewModel(m_Timeline).verticalScroll = m_TreeView.state.scrollPos.y; + } + + public Rect GetRowRect(int row) + { + return m_TimelineTreeView.GetRowRect(row); + } + + static void AddVisibleTrackRecursive(ref List<TimelineTrackBaseGUI> list, TimelineTrackBaseGUI track) + { + if (track == null) + return; + + list.Add(track); + + if (!track.isExpanded) + return; + + if (track.children != null) + { + foreach (var c in track.children) + { + AddVisibleTrackRecursive(ref list, c as TimelineTrackBaseGUI); + } + } + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTreeViewGUI.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTreeViewGUI.cs.meta new file mode 100644 index 0000000..7363134 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TimelineTreeViewGUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f9d8c8b21846a654092a24f25aa41421 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TrackPropertyCurvesDataSource.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TrackPropertyCurvesDataSource.cs new file mode 100644 index 0000000..4b808ea --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TrackPropertyCurvesDataSource.cs @@ -0,0 +1,15 @@ +using UnityEngine; +using UnityEngine.Timeline; + +namespace UnityEditor.Timeline +{ + class TrackPropertyCurvesDataSource : BasePropertyKeyDataSource + { + protected override AnimationClip animationClip { get; } + + public TrackPropertyCurvesDataSource(TrackAsset track) + { + animationClip = track != null ? track.curves : null; + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TrackPropertyCurvesDataSource.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TrackPropertyCurvesDataSource.cs.meta new file mode 100644 index 0000000..fc351b4 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/treeview/TrackPropertyCurvesDataSource.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f8964ef46e1d446dc9eb17a36a3e1f14 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: |
