651 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			651 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | using System; | ||
|  | using System.Collections.Generic; | ||
|  | using System.Linq; | ||
|  | using UnityEditor.Timeline; | ||
|  | using UnityEngine; | ||
|  | using UnityEngine.Timeline; | ||
|  | using UnityEngine.Playables; | ||
|  | using UnityObject = UnityEngine.Object; | ||
|  | #if UNITY_6000_2_OR_NEWER | ||
|  | using TreeViewDragging = UnityEditor.IMGUI.Controls.TreeViewDragging<int>; | ||
|  | using TreeViewController = UnityEditor.IMGUI.Controls.TreeViewController<int>; | ||
|  | using TreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem<int>; | ||
|  | #else | ||
|  | using UnityEditor.IMGUI.Controls; | ||
|  | #endif | ||
|  | 
 | ||
|  | namespace UnityEditor | ||
|  | { | ||
|  |     class TimelineDragging : TreeViewDragging | ||
|  |     { | ||
|  |         public delegate bool TypeResolver(IEnumerable<Type> types, Action<Type> onComplete, string format); | ||
|  | 
 | ||
|  |         private static readonly string k_SelectTrackWithBinding = L10n.Tr("Add {0}"); | ||
|  |         private static readonly string k_SelectTrackWithClip = L10n.Tr("Add Clip With {0}"); | ||
|  |         private static readonly string k_SelectClip = L10n.Tr("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 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(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.Rejected; | ||
|  | 
 | ||
|  |             if (targetTrack != null && targetTrack.lockedInHierarchy) | ||
|  |                 return DragAndDropVisualMode.Rejected; | ||
|  | 
 | ||
|  |             var tracksWithBinding = objectsBeingDropped.SelectMany(TypeUtility.GetTracksCreatableFromObject).Distinct(); | ||
|  |             if (!tracksWithBinding.Any()) | ||
|  |                 return DragAndDropVisualMode.None; | ||
|  | 
 | ||
|  |             if (perform) | ||
|  |             { | ||
|  |                 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); | ||
|  |                             } | ||
|  |                             BindingUtility.BindWithEditorValidation(director, newTrack, obj); | ||
|  |                         } | ||
|  |                     } | ||
|  |                     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(); | ||
|  | 
 | ||
|  |             // No tracks or markers support this object | ||
|  |             if (!(markerTypes.Any() || validTrackTypes.Any())) | ||
|  |             { | ||
|  |                 return DragAndDropVisualMode.Rejected; | ||
|  |             } | ||
|  |             // track is not compatible with marker | ||
|  |             if (targetTrack != null && markerTypes.Any(o => !TypeUtility.DoesTrackSupportMarkerType(targetTrack, o))) | ||
|  |             { | ||
|  |                 // track is not compatible with object | ||
|  |                 if (!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 OnTrackBindingDragUpdate(TrackAsset dropTarget) | ||
|  |         { | ||
|  |             if (DragAndDrop.objectReferences.Length == 0) | ||
|  |             { | ||
|  |                 OnRejectTrackBindingDragUpdate(); | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  |             var trackEditor = CustomTimelineEditorCache.GetTrackEditor(dropTarget); | ||
|  |             var isDragValid = trackEditor.IsBindingAssignableFrom_Safe(DragAndDrop.objectReferences[0], dropTarget); | ||
|  |             if (isDragValid) | ||
|  |                 OnAcceptTrackBindingDragUpdate(); | ||
|  |             else | ||
|  |                 OnRejectTrackBindingDragUpdate(); | ||
|  |         } | ||
|  | 
 | ||
|  |         static void OnAcceptTrackBindingDragUpdate() | ||
|  |         { | ||
|  |             DragAndDrop.visualMode = DragAndDropVisualMode.Link; | ||
|  |         } | ||
|  | 
 | ||
|  |         static void OnRejectTrackBindingDragUpdate() | ||
|  |         { | ||
|  |             DragAndDrop.visualMode = DragAndDropVisualMode.Rejected; | ||
|  |             DragAndDrop.activeControlID = 0; | ||
|  |         } | ||
|  | 
 | ||
|  |         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; | ||
|  |         } | ||
|  |     } | ||
|  | } |