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;
 | |
|         }
 | |
|     }
 | |
| }
 |