2022-01-12 10:06:03 +03:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using UnityEditor.IMGUI.Controls ;
using UnityEditor.Timeline ;
using UnityEngine ;
using UnityEngine.Timeline ;
using UnityEngine.Playables ;
using UnityObject = UnityEngine . Object ;
namespace UnityEditor
{
class TimelineDragging : TreeViewDragging
{
public delegate bool TypeResolver ( IEnumerable < Type > types , Action < Type > onComplete , string format ) ;
2022-01-12 10:39:15 +03:00
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}" ) ;
2022-01-12 10:06:03 +03:00
const string k_GenericDragId = "TimelineDragging" ;
readonly int kDragSensitivity = 2 ;
readonly TimelineAsset m_Timeline ;
readonly TimelineWindow m_Window ;
class TimelineDragData
{
public TimelineDragData ( List < TreeViewItem > draggedItems )
{
this . draggedItems = draggedItems ;
}
public readonly List < TreeViewItem > draggedItems ;
}
public TimelineDragging ( TreeViewController treeView , TimelineWindow window , TimelineAsset data )
: base ( treeView )
{
m_Timeline = data ;
m_Window = window ;
}
public override bool CanStartDrag ( TreeViewItem targetItem , List < int > draggedItemIDs , Vector2 mouseDownPosition )
{
if ( Event . current . modifiers ! = EventModifiers . None )
return false ;
// Can only drag when starting in the track header area
if ( mouseDownPosition . x > m_Window . sequenceHeaderRect . xMax )
return false ;
var trackBaseGUI = targetItem as TimelineTrackBaseGUI ;
if ( trackBaseGUI = = null | | trackBaseGUI . track = = null )
return false ;
if ( trackBaseGUI . track . lockedInHierarchy )
return false ;
if ( Event . current . type = = EventType . MouseDrag & & Mathf . Abs ( Event . current . delta . y ) < kDragSensitivity )
return false ;
// Make sure dragged items are selected
// TODO Use similar system than the SceneHierarchyWindow in order to handle selection between treeView and tracks.
SelectionManager . Clear ( ) ;
var draggedTrackGUIs = m_Window . allTracks . Where ( t = > draggedItemIDs . Contains ( t . id ) ) ;
foreach ( var trackGUI in draggedTrackGUIs )
SelectionManager . Add ( trackGUI . track ) ;
return true ;
}
public override void StartDrag ( TreeViewItem draggedNode , List < int > draggedItemIDs )
{
DragAndDrop . PrepareStartDrag ( ) ;
var tvItems = SelectionManager . SelectedTrackGUI ( ) . Cast < TreeViewItem > ( ) . ToList ( ) ;
DragAndDrop . SetGenericData ( k_GenericDragId , new TimelineDragData ( tvItems ) ) ;
DragAndDrop . objectReferences = new UnityObject [ ] { } ; // this IS required for dragging to work
string title = draggedItemIDs . Count + ( draggedItemIDs . Count > 1 ? "s" : "" ) ; // title is only shown on OSX (at the cursor)
TimelineGroupGUI groupGui = draggedNode as TimelineGroupGUI ;
if ( groupGui ! = null )
{
title = groupGui . displayName ;
}
DragAndDrop . StartDrag ( title ) ;
}
public static bool IsDraggingEvent ( )
{
return Event . current . type = = EventType . DragUpdated | |
Event . current . type = = EventType . DragExited | |
Event . current . type = = EventType . DragPerform ;
}
public static bool ResolveType ( IEnumerable < System . Type > types , Action < Type > onComplete , string formatString )
{
if ( ! types . Any ( ) | | onComplete = = null )
return false ;
if ( types . Count ( ) = = 1 )
{
onComplete ( types . First ( ) ) ;
return true ;
}
var menu = new GenericMenu ( ) ;
var builtInTypes = types . Where ( TypeUtility . IsBuiltIn ) . OrderBy ( TypeUtility . GetDisplayName ) . ToArray ( ) ;
var customTypes = types . Where ( x = > ! TypeUtility . IsBuiltIn ( x ) ) . OrderBy ( TypeUtility . GetDisplayName ) . ToArray ( ) ;
foreach ( var t in builtInTypes )
{
menu . AddItem ( new GUIContent ( string . Format ( formatString , TypeUtility . GetDisplayName ( t ) ) ) , false , s = > onComplete ( ( System . Type ) s ) , t ) ;
}
if ( builtInTypes . Length ! = 0 & & customTypes . Length ! = 0 )
menu . AddSeparator ( string . Empty ) ;
foreach ( var t in customTypes )
{
menu . AddItem ( new GUIContent ( string . Format ( formatString , TypeUtility . GetDisplayName ( t ) ) ) , false , s = > onComplete ( ( System . Type ) s ) , t ) ;
}
menu . ShowAsContext ( ) ;
return true ;
}
public override bool DragElement ( TreeViewItem targetItem , Rect targetItemRect , int row )
{
if ( TimelineWindow . instance . state . editSequence . isReadOnly )
return false ;
// the drop rect contains the row rect plus additional spacing. The base drag element overlaps 1/2 the height of the next track
// which interferes with track bindings
var targetTrack = targetItem as TimelineGroupGUI ;
if ( row > 0 & & targetTrack ! = null & & ! targetTrack . dropRect . Contains ( Event . current . mousePosition ) )
return false ;
return base . DragElement ( targetItem , targetItemRect , row ) ;
}
TreeViewItem GetNextItem ( TreeViewItem item )
{
if ( item = = null )
return null ;
if ( item . parent = = null )
{
int row = m_Window . treeView . data . GetRow ( item . id ) ;
var items = m_Window . treeView . data . GetRows ( ) ;
if ( items . Count > row + 1 )
return items [ row + 1 ] ;
return null ;
}
var children = item . parent . children ;
if ( children = = null )
return null ;
for ( int i = 0 ; i < children . Count - 1 ; i + + )
{
if ( children [ i ] = = item )
return children [ i + 1 ] ;
}
return null ;
}
private static TrackAsset GetTrack ( TreeViewItem item )
{
TimelineTrackBaseGUI baseGui = item as TimelineTrackBaseGUI ;
if ( baseGui = = null )
return null ;
return baseGui . track ;
}
// The drag and drop may be over an expanded group but might be between tracks
private void HandleNestedItemGUI ( ref TreeViewItem parentItem , ref TreeViewItem targetItem , ref TreeViewItem insertBefore )
{
const float kTopPad = 5 ;
const float kBottomPad = 5 ;
insertBefore = null ;
if ( ! ShouldUseHierarchyDragAndDrop ( ) )
return ;
var targetTrack = targetItem as TimelineGroupGUI ;
if ( targetTrack = = null )
return ;
var mousePosition = Event . current . mousePosition ;
var dropBefore = targetTrack . rowRect . yMin + kTopPad > mousePosition . y ;
var dropAfter = ! ( targetTrack . track is GroupTrack ) & & ( targetTrack . rowRect . yMax - kBottomPad < mousePosition . y ) ;
targetTrack . drawInsertionMarkerBefore = dropBefore ;
targetTrack . drawInsertionMarkerAfter = dropAfter ;
if ( dropBefore )
{
targetItem = parentItem ;
parentItem = targetItem ! = null ? targetItem . parent : null ;
insertBefore = targetTrack ;
}
else if ( dropAfter )
{
targetItem = parentItem ;
parentItem = targetItem ! = null ? targetItem . parent : null ;
insertBefore = GetNextItem ( targetTrack ) ;
}
else if ( targetTrack . track is GroupTrack )
{
targetTrack . isDropTarget = true ;
}
}
public override DragAndDropVisualMode DoDrag ( TreeViewItem parentItem , TreeViewItem targetItem , bool perform , DropPosition dropPos )
{
m_Window . isDragging = false ;
var retMode = DragAndDropVisualMode . None ;
var trackDragData = DragAndDrop . GetGenericData ( k_GenericDragId ) as TimelineDragData ;
if ( trackDragData ! = null )
{
retMode = HandleTrackDrop ( parentItem , targetItem , perform , dropPos ) ;
if ( retMode = = DragAndDropVisualMode . Copy & & targetItem ! = null & & Event . current . type = = EventType . DragUpdated )
{
var targetActor = targetItem as TimelineGroupGUI ;
if ( targetActor ! = null )
targetActor . isDropTarget = true ;
}
}
else if ( DragAndDrop . objectReferences . Any ( ) )
{
var objectsBeingDropped = DragAndDrop . objectReferences . OfType < UnityObject > ( ) ;
var director = m_Window . state . editSequence . director ;
if ( ShouldUseHierarchyDragAndDrop ( ) )
{
// for object drawing
var originalTarget = targetItem ;
TreeViewItem insertBeforeItem = null ;
HandleNestedItemGUI ( ref parentItem , ref targetItem , ref insertBeforeItem ) ;
var track = GetTrack ( targetItem ) ;
var parent = GetTrack ( parentItem ) ;
var insertBefore = GetTrack ( insertBeforeItem ) ;
retMode = HandleHierarchyPaneDragAndDrop ( objectsBeingDropped , track , perform , m_Timeline , director , ResolveType , insertBefore ) ;
// fallback to old clip behaviour
if ( retMode = = DragAndDropVisualMode . None )
{
retMode = HandleClipPaneObjectDragAndDrop ( objectsBeingDropped , track , perform , m_Timeline , parent , director , m_Window . state . timeAreaShownRange . x , ResolveType , insertBefore ) ;
}
// if we are rejected, clear any drop markers
if ( retMode = = DragAndDropVisualMode . Rejected & & targetItem ! = null )
{
ClearInsertionMarkers ( originalTarget ) ;
ClearInsertionMarkers ( targetItem ) ;
ClearInsertionMarkers ( parentItem ) ;
ClearInsertionMarkers ( insertBeforeItem ) ;
}
}
else
{
2022-01-12 10:39:15 +03:00
var candidateTime = TimelineHelpers . GetCandidateTime ( Event . current . mousePosition ) ;
2022-01-12 10:06:03 +03:00
retMode = HandleClipPaneObjectDragAndDrop ( objectsBeingDropped , GetTrack ( targetItem ) , perform , m_Timeline , GetTrack ( parentItem ) , director , candidateTime , ResolveType ) ;
}
}
m_Window . isDragging = false ;
return retMode ;
}
void ClearInsertionMarkers ( TreeViewItem item )
{
var trackGUI = item as TimelineTrackBaseGUI ;
if ( trackGUI ! = null )
{
trackGUI . drawInsertionMarkerAfter = false ;
trackGUI . drawInsertionMarkerBefore = false ;
trackGUI . isDropTarget = false ;
}
}
bool ShouldUseHierarchyDragAndDrop ( )
{
return m_Window . state . IsEditingAnEmptyTimeline ( ) | | m_Window . state . sequencerHeaderWidth > Event . current . mousePosition . x ;
}
public static DragAndDropVisualMode HandleHierarchyPaneDragAndDrop ( IEnumerable < UnityObject > objectsBeingDropped , TrackAsset targetTrack , bool perform , TimelineAsset timeline , PlayableDirector director , TypeResolver typeResolver , TrackAsset insertBefore = null )
{
if ( timeline = = null )
return DragAndDropVisualMode . Rejected ;
// if we are over a target track, defer to track binding system (implemented in TrackGUIs), unless we are a groupTrack
if ( targetTrack ! = null & & ( targetTrack as GroupTrack ) = = null )
return DragAndDropVisualMode . None ;
if ( targetTrack ! = null & & targetTrack . lockedInHierarchy )
return DragAndDropVisualMode . Rejected ;
var tracksWithBinding = objectsBeingDropped . SelectMany ( TypeUtility . GetTracksCreatableFromObject ) . Distinct ( ) ;
if ( ! tracksWithBinding . Any ( ) )
return DragAndDropVisualMode . None ;
if ( perform )
{
System . Action < Type > onResolve = trackType = >
{
foreach ( var obj in objectsBeingDropped )
{
if ( ! obj . IsPrefab ( ) & & TypeUtility . IsTrackCreatableFromObject ( obj , trackType ) )
{
var newTrack = TimelineHelpers . CreateTrack ( timeline , trackType , targetTrack , string . Empty ) ;
if ( insertBefore ! = null )
{
if ( targetTrack ! = null )
targetTrack . MoveLastTrackBefore ( insertBefore ) ;
else
timeline . MoveLastTrackBefore ( insertBefore ) ;
}
TimelineHelpers . Bind ( newTrack , obj , director ) ;
}
}
TimelineEditor . Refresh ( RefreshReason . ContentsAddedOrRemoved ) ;
} ;
typeResolver ( tracksWithBinding , onResolve , k_SelectTrackWithBinding ) ;
}
return DragAndDropVisualMode . Copy ;
}
public static DragAndDropVisualMode HandleClipPaneObjectDragAndDrop ( IEnumerable < UnityObject > objectsBeingDropped , TrackAsset targetTrack , bool perform , TimelineAsset timeline , TrackAsset parent , PlayableDirector director , double candidateTime , TypeResolver typeResolver , TrackAsset insertBefore = null )
{
if ( timeline = = null )
return DragAndDropVisualMode . Rejected ;
// locked tracks always reject
if ( targetTrack ! = null & & targetTrack . lockedInHierarchy )
return DragAndDropVisualMode . Rejected ;
// treat group tracks as having no track
if ( targetTrack is GroupTrack )
{
parent = targetTrack ;
targetTrack = null ;
}
// Special case for monoscripts, since they describe the type
if ( objectsBeingDropped . Any ( o = > o is MonoScript ) )
return HandleClipPaneMonoScriptDragAndDrop ( objectsBeingDropped . OfType < MonoScript > ( ) , targetTrack , perform , timeline , parent , director , candidateTime ) ;
// no unity objects, or explicit exceptions
if ( ! objectsBeingDropped . Any ( ) | | objectsBeingDropped . Any ( o = > ! ValidateObjectDrop ( o ) ) )
return DragAndDropVisualMode . Rejected ;
// reject scene references if we have no context
if ( director = = null & & objectsBeingDropped . Any ( o = > o . IsSceneObject ( ) ) )
return DragAndDropVisualMode . Rejected ;
var validTrackTypes = objectsBeingDropped . SelectMany ( o = > TypeUtility . GetTrackTypesForObject ( o ) ) . Distinct ( ) . ToList ( ) ;
// special case for playable assets
if ( objectsBeingDropped . Any ( o = > TypeUtility . IsConcretePlayableAsset ( o . GetType ( ) ) ) )
{
var playableAssets = objectsBeingDropped . OfType < IPlayableAsset > ( ) . Where ( o = > TypeUtility . IsConcretePlayableAsset ( o . GetType ( ) ) ) ;
return HandleClipPanePlayableAssetDragAndDrop ( playableAssets , targetTrack , perform , timeline , parent , director , candidateTime , typeResolver ) ;
}
var markerTypes = objectsBeingDropped . SelectMany ( o = > TypeUtility . MarkerTypesWithFieldForObject ( o ) ) . Distinct ( ) ;
2022-01-12 10:39:15 +03:00
// No tracks or markers support this object
if ( ! ( markerTypes . Any ( ) | | validTrackTypes . Any ( ) ) )
2022-01-12 10:06:03 +03:00
{
2022-01-12 10:39:15 +03:00
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 ( ) ) )
2022-01-12 10:06:03 +03:00
return DragAndDropVisualMode . Rejected ;
}
// there is no target track, dropping to empty space, or onto a group
if ( perform )
{
// choose track and then clip
if ( targetTrack = = null )
{
var createdTrack = HandleTrackAndItemCreation ( objectsBeingDropped , candidateTime , typeResolver , timeline , parent , validTrackTypes , insertBefore ) ;
if ( ! createdTrack )
{
timeline . CreateMarkerTrack ( ) ;
HandleItemCreation ( objectsBeingDropped , timeline . markerTrack , candidateTime , typeResolver , true ) ; // menu is always popped if ambiguous choice
}
}
// just choose clip/marker
else
{
HandleItemCreation ( objectsBeingDropped , targetTrack , candidateTime , typeResolver , true ) ; // menu is always popped if ambiguous choice
}
}
return DragAndDropVisualMode . Copy ;
}
static bool HandleTrackAndItemCreation ( IEnumerable < UnityEngine . Object > objectsBeingDropped , double candidateTime , TypeResolver typeResolver , TimelineAsset timeline , TrackAsset parent , IEnumerable < Type > validTrackTypes , TrackAsset insertBefore = null )
{
Action < Type > onResolved = t = >
{
var newTrack = TimelineHelpers . CreateTrack ( timeline , t , parent , string . Empty ) ;
if ( insertBefore ! = null )
{
if ( parent ! = null )
parent . MoveLastTrackBefore ( insertBefore ) ;
else
timeline . MoveLastTrackBefore ( insertBefore ) ;
}
HandleItemCreation ( objectsBeingDropped , newTrack , candidateTime , typeResolver , validTrackTypes . Count ( ) = = 1 ) ; // menu is popped if ambiguous clip choice and unambiguous track choice
} ;
return typeResolver ( validTrackTypes , t = > onResolved ( t ) , k_SelectTrackWithClip ) ; // Did it create a track
}
static void HandleItemCreation ( IEnumerable < UnityEngine . Object > objectsBeingDropped , TrackAsset targetTrack , double candidateTime , TypeResolver typeResolver , bool allowMenu )
{
var assetTypes = objectsBeingDropped . Select ( o = >
TypeUtility . GetAssetTypesForObject ( targetTrack . GetType ( ) , o )
. Union ( TypeUtility . MarkerTypesWithFieldForObject ( o ) ) ) . ToList ( ) ;
Action < Type > onCreateItem = assetType = >
{
if ( typeof ( PlayableAsset ) . IsAssignableFrom ( assetType ) )
{
TimelineHelpers . CreateClipsFromObjects ( assetType , targetTrack , candidateTime ,
objectsBeingDropped ) ;
}
else
{
TimelineHelpers . CreateMarkersFromObjects ( assetType , targetTrack , candidateTime , objectsBeingDropped ) ;
}
} ;
var flatAssetTypes = assetTypes . SelectMany ( x = > x ) . Distinct ( ) ;
// If there is a one to one mapping between assets and timeline types, no need to go through the type resolution, not ambiguous.
if ( assetTypes . All ( x = > x . Count ( ) < = 1 ) )
{
foreach ( var type in flatAssetTypes )
{
onCreateItem ( type ) ;
}
}
else
{
if ( ! allowMenu ) // If we already popped a menu, and are presented with an ambiguous choice, take the first entry
{
flatAssetTypes = new [ ] { flatAssetTypes . First ( ) } ;
}
typeResolver ( flatAssetTypes , onCreateItem , k_SelectClip ) ;
}
}
/// Handles drag and drop of a mono script.
public static DragAndDropVisualMode HandleClipPaneMonoScriptDragAndDrop ( IEnumerable < MonoScript > scriptsBeingDropped , TrackAsset targetTrack , bool perform , TimelineAsset timeline , TrackAsset parent , PlayableDirector director , double candidateTime )
{
var playableAssetTypes = scriptsBeingDropped . Select ( s = > s . GetClass ( ) ) . Where ( TypeUtility . IsConcretePlayableAsset ) . Distinct ( ) ;
if ( ! playableAssetTypes . Any ( ) )
return DragAndDropVisualMode . Rejected ;
var targetTrackType = typeof ( PlayableTrack ) ;
if ( targetTrack ! = null )
targetTrackType = targetTrack . GetType ( ) ;
var trackAssetsTypes = TypeUtility . GetPlayableAssetsHandledByTrack ( targetTrackType ) ;
var supportedTypes = trackAssetsTypes . Intersect ( playableAssetTypes ) ;
if ( ! supportedTypes . Any ( ) )
return DragAndDropVisualMode . Rejected ;
if ( perform )
{
if ( targetTrack = = null )
targetTrack = TimelineHelpers . CreateTrack ( timeline , targetTrackType , parent , string . Empty ) ;
TimelineHelpers . CreateClipsFromTypes ( supportedTypes , targetTrack , candidateTime ) ;
}
return DragAndDropVisualMode . Copy ;
}
public static DragAndDropVisualMode HandleClipPanePlayableAssetDragAndDrop ( IEnumerable < IPlayableAsset > assetsBeingDropped , TrackAsset targetTrack , bool perform , TimelineAsset timeline , TrackAsset parent , PlayableDirector director , double candidateTime , TypeResolver typeResolver )
{
// get the list of supported track types
var assetTypes = assetsBeingDropped . Select ( x = > x . GetType ( ) ) . Distinct ( ) ;
IEnumerable < Type > supportedTypes = null ;
if ( targetTrack = = null )
{
supportedTypes = TypeUtility . AllTrackTypes ( ) . Where ( t = > TypeUtility . GetPlayableAssetsHandledByTrack ( t ) . Intersect ( assetTypes ) . Any ( ) ) . ToList ( ) ;
}
else
{
supportedTypes = Enumerable . Empty < Type > ( ) ;
var trackAssetTypes = TypeUtility . GetPlayableAssetsHandledByTrack ( targetTrack . GetType ( ) ) ;
if ( trackAssetTypes . Intersect ( assetTypes ) . Any ( ) )
supportedTypes = new [ ] { targetTrack . GetType ( ) } ;
}
if ( ! supportedTypes . Any ( ) )
return DragAndDropVisualMode . Rejected ;
if ( perform )
{
Action < Type > onResolved = ( t ) = >
{
if ( targetTrack = = null )
targetTrack = TimelineHelpers . CreateTrack ( timeline , t , parent , string . Empty ) ;
var clipTypes = TypeUtility . GetPlayableAssetsHandledByTrack ( targetTrack . GetType ( ) ) ;
foreach ( var asset in assetsBeingDropped )
{
if ( clipTypes . Contains ( asset . GetType ( ) ) )
TimelineHelpers . CreateClipOnTrackFromPlayableAsset ( asset , targetTrack , candidateTime ) ;
}
} ;
typeResolver ( supportedTypes , onResolved , k_SelectTrackWithClip ) ;
}
return DragAndDropVisualMode . Copy ;
}
static bool ValidateObjectDrop ( UnityObject obj )
{
// legacy animation clips are not supported at all
AnimationClip clip = obj as AnimationClip ;
if ( clip ! = null & & clip . legacy )
return false ;
return ! ( obj is TimelineAsset ) ;
}
public DragAndDropVisualMode HandleTrackDrop ( TreeViewItem parentItem , TreeViewItem targetItem , bool perform , DropPosition dropPos )
{
( ( TimelineTreeView ) m_Window . treeView . gui ) . showInsertionMarker = false ;
var trackDragData = ( TimelineDragData ) DragAndDrop . GetGenericData ( k_GenericDragId ) ;
bool validDrag = ValidDrag ( targetItem , trackDragData . draggedItems ) ;
if ( ! validDrag )
return DragAndDropVisualMode . None ;
var draggedTracks = trackDragData . draggedItems . OfType < TimelineGroupGUI > ( ) . Select ( x = > x . track ) . ToList ( ) ;
if ( draggedTracks . Count = = 0 )
return DragAndDropVisualMode . None ;
if ( parentItem ! = null )
{
var parentActor = parentItem as TimelineGroupGUI ;
if ( parentActor ! = null & & parentActor . track ! = null )
{
if ( parentActor . track . lockedInHierarchy )
return DragAndDropVisualMode . Rejected ;
if ( draggedTracks . Any ( x = > ! TimelineCreateUtilities . ValidateParentTrack ( parentActor . track , x . GetType ( ) ) ) )
return DragAndDropVisualMode . Rejected ;
}
}
var insertAfterItem = targetItem as TimelineGroupGUI ;
if ( insertAfterItem ! = null & & insertAfterItem . track ! = null )
{
( ( TimelineTreeView ) m_Window . treeView . gui ) . showInsertionMarker = true ;
}
if ( dropPos = = DropPosition . Upon )
{
var groupGUI = targetItem as TimelineGroupGUI ;
if ( groupGUI ! = null )
groupGUI . isDropTarget = true ;
}
if ( perform )
{
PlayableAsset targetParent = m_Timeline ;
var parentActor = parentItem as TimelineGroupGUI ;
if ( parentActor ! = null & & parentActor . track ! = null )
targetParent = parentActor . track ;
TrackAsset siblingTrack = insertAfterItem ! = null ? insertAfterItem . track : null ;
// where the user drops after the last track, make sure to place it after all the tracks
if ( targetParent = = m_Timeline & & dropPos = = DropPosition . Below & & siblingTrack = = null )
{
siblingTrack = m_Timeline . GetRootTracks ( ) . LastOrDefault ( x = > ! draggedTracks . Contains ( x ) ) ;
}
if ( TrackExtensions . ReparentTracks ( TrackExtensions . FilterTracks ( draggedTracks ) . ToList ( ) , targetParent , siblingTrack , dropPos = = DropPosition . Above ) )
{
m_Window . state . Refresh ( ) ;
}
}
return DragAndDropVisualMode . Move ;
}
public static void HandleBindingDragAndDrop ( TrackAsset dropTarget , Type requiredBindingType )
{
var objectBeingDragged = DragAndDrop . objectReferences [ 0 ] ;
var action = BindingUtility . GetBindingAction ( requiredBindingType , objectBeingDragged ) ;
DragAndDrop . visualMode = action = = BindingAction . DoNotBind
? DragAndDropVisualMode . Rejected
: DragAndDropVisualMode . Link ;
if ( action = = BindingAction . DoNotBind | | Event . current . type ! = EventType . DragPerform )
return ;
var director = TimelineEditor . inspectedDirector ;
switch ( action )
{
case BindingAction . BindDirectly :
{
BindingUtility . Bind ( director , dropTarget , objectBeingDragged ) ;
break ;
}
case BindingAction . BindToExistingComponent :
{
var gameObjectBeingDragged = objectBeingDragged as GameObject ;
Debug . Assert ( gameObjectBeingDragged ! = null , "The object being dragged was detected as being a GameObject" ) ;
BindingUtility . Bind ( director , dropTarget , gameObjectBeingDragged . GetComponent ( requiredBindingType ) ) ;
break ;
}
case BindingAction . BindToMissingComponent :
{
var gameObjectBeingDragged = objectBeingDragged as GameObject ;
Debug . Assert ( gameObjectBeingDragged ! = null , "The object being dragged was detected as being a GameObject" ) ;
var typeNameOfComponent = requiredBindingType . ToString ( ) . Split ( "." . ToCharArray ( ) ) . Last ( ) ;
var bindMenu = new GenericMenu ( ) ;
bindMenu . AddItem (
EditorGUIUtility . TextContent ( "Create " + typeNameOfComponent + " on " + gameObjectBeingDragged . name ) ,
false ,
nullParam = > BindingUtility . Bind ( director , dropTarget , Undo . AddComponent ( gameObjectBeingDragged , requiredBindingType ) ) ,
null ) ;
bindMenu . AddSeparator ( "" ) ;
bindMenu . AddItem ( EditorGUIUtility . TrTextContent ( "Cancel" ) , false , userData = > { } , null ) ;
bindMenu . ShowAsContext ( ) ;
break ;
}
default :
{
//no-op
return ;
}
}
DragAndDrop . AcceptDrag ( ) ;
}
static bool ValidDrag ( TreeViewItem target , List < TreeViewItem > draggedItems )
{
TreeViewItem currentParent = target ;
while ( currentParent ! = null )
{
if ( draggedItems . Contains ( currentParent ) )
return false ;
currentParent = currentParent . parent ;
}
// dragging into the sequence itself
return true ;
}
}
}