TimelineDragging.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using UnityEditor.IMGUI.Controls;
  5. using UnityEditor.Timeline;
  6. using UnityEngine;
  7. using UnityEngine.Timeline;
  8. using UnityEngine.Playables;
  9. using UnityObject = UnityEngine.Object;
  10. namespace UnityEditor
  11. {
  12. class TimelineDragging : TreeViewDragging
  13. {
  14. public delegate bool TypeResolver(IEnumerable<Type> types, Action<Type> onComplete, string format);
  15. private static readonly string k_SelectTrackWithBinding = L10n.Tr("Add {0}");
  16. private static readonly string k_SelectTrackWithClip = L10n.Tr("Add Clip With {0}");
  17. private static readonly string k_SelectClip = L10n.Tr("Add {0}");
  18. const string k_GenericDragId = "TimelineDragging";
  19. readonly int kDragSensitivity = 2;
  20. readonly TimelineAsset m_Timeline;
  21. readonly TimelineWindow m_Window;
  22. class TimelineDragData
  23. {
  24. public TimelineDragData(List<TreeViewItem> draggedItems)
  25. {
  26. this.draggedItems = draggedItems;
  27. }
  28. public readonly List<TreeViewItem> draggedItems;
  29. }
  30. public TimelineDragging(TreeViewController treeView, TimelineWindow window, TimelineAsset data)
  31. : base(treeView)
  32. {
  33. m_Timeline = data;
  34. m_Window = window;
  35. }
  36. public override bool CanStartDrag(TreeViewItem targetItem, List<int> draggedItemIDs, Vector2 mouseDownPosition)
  37. {
  38. if (Event.current.modifiers != EventModifiers.None)
  39. return false;
  40. // Can only drag when starting in the track header area
  41. if (mouseDownPosition.x > m_Window.sequenceHeaderRect.xMax)
  42. return false;
  43. var trackBaseGUI = targetItem as TimelineTrackBaseGUI;
  44. if (trackBaseGUI == null || trackBaseGUI.track == null)
  45. return false;
  46. if (trackBaseGUI.track.lockedInHierarchy)
  47. return false;
  48. if (Event.current.type == EventType.MouseDrag && Mathf.Abs(Event.current.delta.y) < kDragSensitivity)
  49. return false;
  50. // Make sure dragged items are selected
  51. // TODO Use similar system than the SceneHierarchyWindow in order to handle selection between treeView and tracks.
  52. SelectionManager.Clear();
  53. var draggedTrackGUIs = m_Window.allTracks.Where(t => draggedItemIDs.Contains(t.id));
  54. foreach (var trackGUI in draggedTrackGUIs)
  55. SelectionManager.Add(trackGUI.track);
  56. return true;
  57. }
  58. public override void StartDrag(TreeViewItem draggedNode, List<int> draggedItemIDs)
  59. {
  60. DragAndDrop.PrepareStartDrag();
  61. var tvItems = SelectionManager.SelectedTrackGUI().Cast<TreeViewItem>().ToList();
  62. DragAndDrop.SetGenericData(k_GenericDragId, new TimelineDragData(tvItems));
  63. DragAndDrop.objectReferences = new UnityObject[] {}; // this IS required for dragging to work
  64. string title = draggedItemIDs.Count + (draggedItemIDs.Count > 1 ? "s" : ""); // title is only shown on OSX (at the cursor)
  65. TimelineGroupGUI groupGui = draggedNode as TimelineGroupGUI;
  66. if (groupGui != null)
  67. {
  68. title = groupGui.displayName;
  69. }
  70. DragAndDrop.StartDrag(title);
  71. }
  72. public static bool IsDraggingEvent()
  73. {
  74. return Event.current.type == EventType.DragUpdated ||
  75. Event.current.type == EventType.DragExited ||
  76. Event.current.type == EventType.DragPerform;
  77. }
  78. public static bool ResolveType(IEnumerable<System.Type> types, Action<Type> onComplete, string formatString)
  79. {
  80. if (!types.Any() || onComplete == null)
  81. return false;
  82. if (types.Count() == 1)
  83. {
  84. onComplete(types.First());
  85. return true;
  86. }
  87. var menu = new GenericMenu();
  88. var builtInTypes = types.Where(TypeUtility.IsBuiltIn).OrderBy(TypeUtility.GetDisplayName).ToArray();
  89. var customTypes = types.Where(x => !TypeUtility.IsBuiltIn(x)).OrderBy(TypeUtility.GetDisplayName).ToArray();
  90. foreach (var t in builtInTypes)
  91. {
  92. menu.AddItem(new GUIContent(string.Format(formatString, TypeUtility.GetDisplayName(t))), false, s => onComplete((System.Type)s), t);
  93. }
  94. if (builtInTypes.Length != 0 && customTypes.Length != 0)
  95. menu.AddSeparator(string.Empty);
  96. foreach (var t in customTypes)
  97. {
  98. menu.AddItem(new GUIContent(string.Format(formatString, TypeUtility.GetDisplayName(t))), false, s => onComplete((System.Type)s), t);
  99. }
  100. menu.ShowAsContext();
  101. return true;
  102. }
  103. public override bool DragElement(TreeViewItem targetItem, Rect targetItemRect, int row)
  104. {
  105. if (TimelineWindow.instance.state.editSequence.isReadOnly)
  106. return false;
  107. // the drop rect contains the row rect plus additional spacing. The base drag element overlaps 1/2 the height of the next track
  108. // which interferes with track bindings
  109. var targetTrack = targetItem as TimelineGroupGUI;
  110. if (row > 0 && targetTrack != null && !targetTrack.dropRect.Contains(Event.current.mousePosition))
  111. return false;
  112. return base.DragElement(targetItem, targetItemRect, row);
  113. }
  114. TreeViewItem GetNextItem(TreeViewItem item)
  115. {
  116. if (item == null)
  117. return null;
  118. if (item.parent == null)
  119. {
  120. int row = m_Window.treeView.data.GetRow(item.id);
  121. var items = m_Window.treeView.data.GetRows();
  122. if (items.Count > row + 1)
  123. return items[row + 1];
  124. return null;
  125. }
  126. var children = item.parent.children;
  127. if (children == null)
  128. return null;
  129. for (int i = 0; i < children.Count - 1; i++)
  130. {
  131. if (children[i] == item)
  132. return children[i + 1];
  133. }
  134. return null;
  135. }
  136. private static TrackAsset GetTrack(TreeViewItem item)
  137. {
  138. TimelineTrackBaseGUI baseGui = item as TimelineTrackBaseGUI;
  139. if (baseGui == null)
  140. return null;
  141. return baseGui.track;
  142. }
  143. // The drag and drop may be over an expanded group but might be between tracks
  144. private void HandleNestedItemGUI(ref TreeViewItem parentItem, ref TreeViewItem targetItem, ref TreeViewItem insertBefore)
  145. {
  146. const float kTopPad = 5;
  147. const float kBottomPad = 5;
  148. insertBefore = null;
  149. if (!ShouldUseHierarchyDragAndDrop())
  150. return;
  151. var targetTrack = targetItem as TimelineGroupGUI;
  152. if (targetTrack == null)
  153. return;
  154. var mousePosition = Event.current.mousePosition;
  155. var dropBefore = targetTrack.rowRect.yMin + kTopPad > mousePosition.y;
  156. var dropAfter = !(targetTrack.track is GroupTrack) && (targetTrack.rowRect.yMax - kBottomPad < mousePosition.y);
  157. targetTrack.drawInsertionMarkerBefore = dropBefore;
  158. targetTrack.drawInsertionMarkerAfter = dropAfter;
  159. if (dropBefore)
  160. {
  161. targetItem = parentItem;
  162. parentItem = targetItem != null ? targetItem.parent : null;
  163. insertBefore = targetTrack;
  164. }
  165. else if (dropAfter)
  166. {
  167. targetItem = parentItem;
  168. parentItem = targetItem != null ? targetItem.parent : null;
  169. insertBefore = GetNextItem(targetTrack);
  170. }
  171. else if (targetTrack.track is GroupTrack)
  172. {
  173. targetTrack.isDropTarget = true;
  174. }
  175. }
  176. public override DragAndDropVisualMode DoDrag(TreeViewItem parentItem, TreeViewItem targetItem, bool perform, DropPosition dropPos)
  177. {
  178. m_Window.isDragging = false;
  179. var retMode = DragAndDropVisualMode.None;
  180. var trackDragData = DragAndDrop.GetGenericData(k_GenericDragId) as TimelineDragData;
  181. if (trackDragData != null)
  182. {
  183. retMode = HandleTrackDrop(parentItem, targetItem, perform, dropPos);
  184. if (retMode == DragAndDropVisualMode.Copy && targetItem != null && Event.current.type == EventType.DragUpdated)
  185. {
  186. var targetActor = targetItem as TimelineGroupGUI;
  187. if (targetActor != null)
  188. targetActor.isDropTarget = true;
  189. }
  190. }
  191. else if (DragAndDrop.objectReferences.Any())
  192. {
  193. var objectsBeingDropped = DragAndDrop.objectReferences.OfType<UnityObject>();
  194. var director = m_Window.state.editSequence.director;
  195. if (ShouldUseHierarchyDragAndDrop())
  196. {
  197. // for object drawing
  198. var originalTarget = targetItem;
  199. TreeViewItem insertBeforeItem = null;
  200. HandleNestedItemGUI(ref parentItem, ref targetItem, ref insertBeforeItem);
  201. var track = GetTrack(targetItem);
  202. var parent = GetTrack(parentItem);
  203. var insertBefore = GetTrack(insertBeforeItem);
  204. retMode = HandleHierarchyPaneDragAndDrop(objectsBeingDropped, track, perform, m_Timeline, director, ResolveType, insertBefore);
  205. // fallback to old clip behaviour
  206. if (retMode == DragAndDropVisualMode.None)
  207. {
  208. retMode = HandleClipPaneObjectDragAndDrop(objectsBeingDropped, track, perform, m_Timeline, parent, director, m_Window.state.timeAreaShownRange.x, ResolveType, insertBefore);
  209. }
  210. // if we are rejected, clear any drop markers
  211. if (retMode == DragAndDropVisualMode.Rejected && targetItem != null)
  212. {
  213. ClearInsertionMarkers(originalTarget);
  214. ClearInsertionMarkers(targetItem);
  215. ClearInsertionMarkers(parentItem);
  216. ClearInsertionMarkers(insertBeforeItem);
  217. }
  218. }
  219. else
  220. {
  221. var candidateTime = TimelineHelpers.GetCandidateTime(Event.current.mousePosition);
  222. retMode = HandleClipPaneObjectDragAndDrop(objectsBeingDropped, GetTrack(targetItem), perform, m_Timeline, GetTrack(parentItem), director, candidateTime, ResolveType);
  223. }
  224. }
  225. m_Window.isDragging = false;
  226. return retMode;
  227. }
  228. void ClearInsertionMarkers(TreeViewItem item)
  229. {
  230. var trackGUI = item as TimelineTrackBaseGUI;
  231. if (trackGUI != null)
  232. {
  233. trackGUI.drawInsertionMarkerAfter = false;
  234. trackGUI.drawInsertionMarkerBefore = false;
  235. trackGUI.isDropTarget = false;
  236. }
  237. }
  238. bool ShouldUseHierarchyDragAndDrop()
  239. {
  240. return m_Window.state.IsEditingAnEmptyTimeline() || m_Window.state.sequencerHeaderWidth > Event.current.mousePosition.x;
  241. }
  242. public static DragAndDropVisualMode HandleHierarchyPaneDragAndDrop(IEnumerable<UnityObject> objectsBeingDropped, TrackAsset targetTrack, bool perform, TimelineAsset timeline, PlayableDirector director, TypeResolver typeResolver, TrackAsset insertBefore = null)
  243. {
  244. if (timeline == null)
  245. return DragAndDropVisualMode.Rejected;
  246. // if we are over a target track, defer to track binding system (implemented in TrackGUIs), unless we are a groupTrack
  247. if (targetTrack != null && (targetTrack as GroupTrack) == null)
  248. return DragAndDropVisualMode.None;
  249. if (targetTrack != null && targetTrack.lockedInHierarchy)
  250. return DragAndDropVisualMode.Rejected;
  251. var tracksWithBinding = objectsBeingDropped.SelectMany(TypeUtility.GetTracksCreatableFromObject).Distinct();
  252. if (!tracksWithBinding.Any())
  253. return DragAndDropVisualMode.None;
  254. if (perform)
  255. {
  256. System.Action<Type> onResolve = trackType =>
  257. {
  258. foreach (var obj in objectsBeingDropped)
  259. {
  260. if (!obj.IsPrefab() && TypeUtility.IsTrackCreatableFromObject(obj, trackType))
  261. {
  262. var newTrack = TimelineHelpers.CreateTrack(timeline, trackType, targetTrack, string.Empty);
  263. if (insertBefore != null)
  264. {
  265. if (targetTrack != null)
  266. targetTrack.MoveLastTrackBefore(insertBefore);
  267. else
  268. timeline.MoveLastTrackBefore(insertBefore);
  269. }
  270. TimelineHelpers.Bind(newTrack, obj, director);
  271. }
  272. }
  273. TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved);
  274. };
  275. typeResolver(tracksWithBinding, onResolve, k_SelectTrackWithBinding);
  276. }
  277. return DragAndDropVisualMode.Copy;
  278. }
  279. public static DragAndDropVisualMode HandleClipPaneObjectDragAndDrop(IEnumerable<UnityObject> objectsBeingDropped, TrackAsset targetTrack, bool perform, TimelineAsset timeline, TrackAsset parent, PlayableDirector director, double candidateTime, TypeResolver typeResolver, TrackAsset insertBefore = null)
  280. {
  281. if (timeline == null)
  282. return DragAndDropVisualMode.Rejected;
  283. // locked tracks always reject
  284. if (targetTrack != null && targetTrack.lockedInHierarchy)
  285. return DragAndDropVisualMode.Rejected;
  286. // treat group tracks as having no track
  287. if (targetTrack is GroupTrack)
  288. {
  289. parent = targetTrack;
  290. targetTrack = null;
  291. }
  292. // Special case for monoscripts, since they describe the type
  293. if (objectsBeingDropped.Any(o => o is MonoScript))
  294. return HandleClipPaneMonoScriptDragAndDrop(objectsBeingDropped.OfType<MonoScript>(), targetTrack, perform, timeline, parent, director, candidateTime);
  295. // no unity objects, or explicit exceptions
  296. if (!objectsBeingDropped.Any() || objectsBeingDropped.Any(o => !ValidateObjectDrop(o)))
  297. return DragAndDropVisualMode.Rejected;
  298. // reject scene references if we have no context
  299. if (director == null && objectsBeingDropped.Any(o => o.IsSceneObject()))
  300. return DragAndDropVisualMode.Rejected;
  301. var validTrackTypes = objectsBeingDropped.SelectMany(o => TypeUtility.GetTrackTypesForObject(o)).Distinct().ToList();
  302. // special case for playable assets
  303. if (objectsBeingDropped.Any(o => TypeUtility.IsConcretePlayableAsset(o.GetType())))
  304. {
  305. var playableAssets = objectsBeingDropped.OfType<IPlayableAsset>().Where(o => TypeUtility.IsConcretePlayableAsset(o.GetType()));
  306. return HandleClipPanePlayableAssetDragAndDrop(playableAssets, targetTrack, perform, timeline, parent, director, candidateTime, typeResolver);
  307. }
  308. var markerTypes = objectsBeingDropped.SelectMany(o => TypeUtility.MarkerTypesWithFieldForObject(o)).Distinct();
  309. // No tracks or markers support this object
  310. if (!(markerTypes.Any() || validTrackTypes.Any()))
  311. {
  312. return DragAndDropVisualMode.Rejected;
  313. }
  314. // track is not compatible with marker
  315. if (targetTrack != null && markerTypes.Any(o => !TypeUtility.DoesTrackSupportMarkerType(targetTrack, o)))
  316. {
  317. // track is not compatible with object
  318. if (!validTrackTypes.Contains(targetTrack.GetType()))
  319. return DragAndDropVisualMode.Rejected;
  320. }
  321. // there is no target track, dropping to empty space, or onto a group
  322. if (perform)
  323. {
  324. // choose track and then clip
  325. if (targetTrack == null)
  326. {
  327. var createdTrack = HandleTrackAndItemCreation(objectsBeingDropped, candidateTime, typeResolver, timeline, parent, validTrackTypes, insertBefore);
  328. if (!createdTrack)
  329. {
  330. timeline.CreateMarkerTrack();
  331. HandleItemCreation(objectsBeingDropped, timeline.markerTrack, candidateTime, typeResolver, true); // menu is always popped if ambiguous choice
  332. }
  333. }
  334. // just choose clip/marker
  335. else
  336. {
  337. HandleItemCreation(objectsBeingDropped, targetTrack, candidateTime, typeResolver, true); // menu is always popped if ambiguous choice
  338. }
  339. }
  340. return DragAndDropVisualMode.Copy;
  341. }
  342. static bool HandleTrackAndItemCreation(IEnumerable<UnityEngine.Object> objectsBeingDropped, double candidateTime, TypeResolver typeResolver, TimelineAsset timeline, TrackAsset parent, IEnumerable<Type> validTrackTypes, TrackAsset insertBefore = null)
  343. {
  344. Action<Type> onResolved = t =>
  345. {
  346. var newTrack = TimelineHelpers.CreateTrack(timeline, t, parent, string.Empty);
  347. if (insertBefore != null)
  348. {
  349. if (parent != null)
  350. parent.MoveLastTrackBefore(insertBefore);
  351. else
  352. timeline.MoveLastTrackBefore(insertBefore);
  353. }
  354. HandleItemCreation(objectsBeingDropped, newTrack, candidateTime, typeResolver, validTrackTypes.Count() == 1); // menu is popped if ambiguous clip choice and unambiguous track choice
  355. };
  356. return typeResolver(validTrackTypes, t => onResolved(t), k_SelectTrackWithClip); // Did it create a track
  357. }
  358. static void HandleItemCreation(IEnumerable<UnityEngine.Object> objectsBeingDropped, TrackAsset targetTrack, double candidateTime, TypeResolver typeResolver, bool allowMenu)
  359. {
  360. var assetTypes = objectsBeingDropped.Select(o =>
  361. TypeUtility.GetAssetTypesForObject(targetTrack.GetType(), o)
  362. .Union(TypeUtility.MarkerTypesWithFieldForObject(o))).ToList();
  363. Action<Type> onCreateItem = assetType =>
  364. {
  365. if (typeof(PlayableAsset).IsAssignableFrom(assetType))
  366. {
  367. TimelineHelpers.CreateClipsFromObjects(assetType, targetTrack, candidateTime,
  368. objectsBeingDropped);
  369. }
  370. else
  371. {
  372. TimelineHelpers.CreateMarkersFromObjects(assetType, targetTrack, candidateTime, objectsBeingDropped);
  373. }
  374. };
  375. var flatAssetTypes = assetTypes.SelectMany(x => x).Distinct();
  376. // If there is a one to one mapping between assets and timeline types, no need to go through the type resolution, not ambiguous.
  377. if (assetTypes.All(x => x.Count() <= 1))
  378. {
  379. foreach (var type in flatAssetTypes)
  380. {
  381. onCreateItem(type);
  382. }
  383. }
  384. else
  385. {
  386. if (!allowMenu) // If we already popped a menu, and are presented with an ambiguous choice, take the first entry
  387. {
  388. flatAssetTypes = new[] {flatAssetTypes.First()};
  389. }
  390. typeResolver(flatAssetTypes, onCreateItem, k_SelectClip);
  391. }
  392. }
  393. /// Handles drag and drop of a mono script.
  394. public static DragAndDropVisualMode HandleClipPaneMonoScriptDragAndDrop(IEnumerable<MonoScript> scriptsBeingDropped, TrackAsset targetTrack, bool perform, TimelineAsset timeline, TrackAsset parent, PlayableDirector director, double candidateTime)
  395. {
  396. var playableAssetTypes = scriptsBeingDropped.Select(s => s.GetClass()).Where(TypeUtility.IsConcretePlayableAsset).Distinct();
  397. if (!playableAssetTypes.Any())
  398. return DragAndDropVisualMode.Rejected;
  399. var targetTrackType = typeof(PlayableTrack);
  400. if (targetTrack != null)
  401. targetTrackType = targetTrack.GetType();
  402. var trackAssetsTypes = TypeUtility.GetPlayableAssetsHandledByTrack(targetTrackType);
  403. var supportedTypes = trackAssetsTypes.Intersect(playableAssetTypes);
  404. if (!supportedTypes.Any())
  405. return DragAndDropVisualMode.Rejected;
  406. if (perform)
  407. {
  408. if (targetTrack == null)
  409. targetTrack = TimelineHelpers.CreateTrack(timeline, targetTrackType, parent, string.Empty);
  410. TimelineHelpers.CreateClipsFromTypes(supportedTypes, targetTrack, candidateTime);
  411. }
  412. return DragAndDropVisualMode.Copy;
  413. }
  414. public static DragAndDropVisualMode HandleClipPanePlayableAssetDragAndDrop(IEnumerable<IPlayableAsset> assetsBeingDropped, TrackAsset targetTrack, bool perform, TimelineAsset timeline, TrackAsset parent, PlayableDirector director, double candidateTime, TypeResolver typeResolver)
  415. {
  416. // get the list of supported track types
  417. var assetTypes = assetsBeingDropped.Select(x => x.GetType()).Distinct();
  418. IEnumerable<Type> supportedTypes = null;
  419. if (targetTrack == null)
  420. {
  421. supportedTypes = TypeUtility.AllTrackTypes().Where(t => TypeUtility.GetPlayableAssetsHandledByTrack(t).Intersect(assetTypes).Any()).ToList();
  422. }
  423. else
  424. {
  425. supportedTypes = Enumerable.Empty<Type>();
  426. var trackAssetTypes = TypeUtility.GetPlayableAssetsHandledByTrack(targetTrack.GetType());
  427. if (trackAssetTypes.Intersect(assetTypes).Any())
  428. supportedTypes = new[] {targetTrack.GetType()};
  429. }
  430. if (!supportedTypes.Any())
  431. return DragAndDropVisualMode.Rejected;
  432. if (perform)
  433. {
  434. Action<Type> onResolved = (t) =>
  435. {
  436. if (targetTrack == null)
  437. targetTrack = TimelineHelpers.CreateTrack(timeline, t, parent, string.Empty);
  438. var clipTypes = TypeUtility.GetPlayableAssetsHandledByTrack(targetTrack.GetType());
  439. foreach (var asset in assetsBeingDropped)
  440. {
  441. if (clipTypes.Contains(asset.GetType()))
  442. TimelineHelpers.CreateClipOnTrackFromPlayableAsset(asset, targetTrack, candidateTime);
  443. }
  444. };
  445. typeResolver(supportedTypes, onResolved, k_SelectTrackWithClip);
  446. }
  447. return DragAndDropVisualMode.Copy;
  448. }
  449. static bool ValidateObjectDrop(UnityObject obj)
  450. {
  451. // legacy animation clips are not supported at all
  452. AnimationClip clip = obj as AnimationClip;
  453. if (clip != null && clip.legacy)
  454. return false;
  455. return !(obj is TimelineAsset);
  456. }
  457. public DragAndDropVisualMode HandleTrackDrop(TreeViewItem parentItem, TreeViewItem targetItem, bool perform, DropPosition dropPos)
  458. {
  459. ((TimelineTreeView)m_Window.treeView.gui).showInsertionMarker = false;
  460. var trackDragData = (TimelineDragData)DragAndDrop.GetGenericData(k_GenericDragId);
  461. bool validDrag = ValidDrag(targetItem, trackDragData.draggedItems);
  462. if (!validDrag)
  463. return DragAndDropVisualMode.None;
  464. var draggedTracks = trackDragData.draggedItems.OfType<TimelineGroupGUI>().Select(x => x.track).ToList();
  465. if (draggedTracks.Count == 0)
  466. return DragAndDropVisualMode.None;
  467. if (parentItem != null)
  468. {
  469. var parentActor = parentItem as TimelineGroupGUI;
  470. if (parentActor != null && parentActor.track != null)
  471. {
  472. if (parentActor.track.lockedInHierarchy)
  473. return DragAndDropVisualMode.Rejected;
  474. if (draggedTracks.Any(x => !TimelineCreateUtilities.ValidateParentTrack(parentActor.track, x.GetType())))
  475. return DragAndDropVisualMode.Rejected;
  476. }
  477. }
  478. var insertAfterItem = targetItem as TimelineGroupGUI;
  479. if (insertAfterItem != null && insertAfterItem.track != null)
  480. {
  481. ((TimelineTreeView)m_Window.treeView.gui).showInsertionMarker = true;
  482. }
  483. if (dropPos == DropPosition.Upon)
  484. {
  485. var groupGUI = targetItem as TimelineGroupGUI;
  486. if (groupGUI != null)
  487. groupGUI.isDropTarget = true;
  488. }
  489. if (perform)
  490. {
  491. PlayableAsset targetParent = m_Timeline;
  492. var parentActor = parentItem as TimelineGroupGUI;
  493. if (parentActor != null && parentActor.track != null)
  494. targetParent = parentActor.track;
  495. TrackAsset siblingTrack = insertAfterItem != null ? insertAfterItem.track : null;
  496. // where the user drops after the last track, make sure to place it after all the tracks
  497. if (targetParent == m_Timeline && dropPos == DropPosition.Below && siblingTrack == null)
  498. {
  499. siblingTrack = m_Timeline.GetRootTracks().LastOrDefault(x => !draggedTracks.Contains(x));
  500. }
  501. if (TrackExtensions.ReparentTracks(TrackExtensions.FilterTracks(draggedTracks).ToList(), targetParent, siblingTrack, dropPos == DropPosition.Above))
  502. {
  503. m_Window.state.Refresh();
  504. }
  505. }
  506. return DragAndDropVisualMode.Move;
  507. }
  508. public static void HandleBindingDragAndDrop(TrackAsset dropTarget, Type requiredBindingType)
  509. {
  510. var objectBeingDragged = DragAndDrop.objectReferences[0];
  511. var action = BindingUtility.GetBindingAction(requiredBindingType, objectBeingDragged);
  512. DragAndDrop.visualMode = action == BindingAction.DoNotBind
  513. ? DragAndDropVisualMode.Rejected
  514. : DragAndDropVisualMode.Link;
  515. if (action == BindingAction.DoNotBind || Event.current.type != EventType.DragPerform)
  516. return;
  517. var director = TimelineEditor.inspectedDirector;
  518. switch (action)
  519. {
  520. case BindingAction.BindDirectly:
  521. {
  522. BindingUtility.Bind(director, dropTarget, objectBeingDragged);
  523. break;
  524. }
  525. case BindingAction.BindToExistingComponent:
  526. {
  527. var gameObjectBeingDragged = objectBeingDragged as GameObject;
  528. Debug.Assert(gameObjectBeingDragged != null, "The object being dragged was detected as being a GameObject");
  529. BindingUtility.Bind(director, dropTarget, gameObjectBeingDragged.GetComponent(requiredBindingType));
  530. break;
  531. }
  532. case BindingAction.BindToMissingComponent:
  533. {
  534. var gameObjectBeingDragged = objectBeingDragged as GameObject;
  535. Debug.Assert(gameObjectBeingDragged != null, "The object being dragged was detected as being a GameObject");
  536. var typeNameOfComponent = requiredBindingType.ToString().Split(".".ToCharArray()).Last();
  537. var bindMenu = new GenericMenu();
  538. bindMenu.AddItem(
  539. EditorGUIUtility.TextContent("Create " + typeNameOfComponent + " on " + gameObjectBeingDragged.name),
  540. false,
  541. nullParam => BindingUtility.Bind(director, dropTarget, Undo.AddComponent(gameObjectBeingDragged, requiredBindingType)),
  542. null);
  543. bindMenu.AddSeparator("");
  544. bindMenu.AddItem(EditorGUIUtility.TrTextContent("Cancel"), false, userData => {}, null);
  545. bindMenu.ShowAsContext();
  546. break;
  547. }
  548. default:
  549. {
  550. //no-op
  551. return;
  552. }
  553. }
  554. DragAndDrop.AcceptDrag();
  555. }
  556. static bool ValidDrag(TreeViewItem target, List<TreeViewItem> draggedItems)
  557. {
  558. TreeViewItem currentParent = target;
  559. while (currentParent != null)
  560. {
  561. if (draggedItems.Contains(currentParent))
  562. return false;
  563. currentParent = currentParent.parent;
  564. }
  565. // dragging into the sequence itself
  566. return true;
  567. }
  568. }
  569. }