| using System; |
| using System.Collections.Generic; |
| using System.Linq; |
| using UnityEditor.IMGUI.Controls; |
| using UnityEditor.Timeline; |
| using UnityEngine; |
| using UnityEngine.Timeline; |
| using UnityEngine.Playables; |
| using UnityObject = UnityEngine.Object; |
|
|
| namespace UnityEditor |
| { |
| class TimelineDragging : TreeViewDragging |
| { |
| public delegate bool TypeResolver(IEnumerable<Type> types, Action<Type> onComplete, string format); |
|
|
| private static readonly string k_SelectTrackWithBinding = 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; |
|
|
| |
| 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; |
|
|
| |
| |
| 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[] { }; |
|
|
| string title = draggedItemIDs.Count + (draggedItemIDs.Count > 1 ? "s" : ""); |
|
|
| 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; |
| |
| |
| 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; |
| } |
|
|
| |
| 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()) |
| { |
| |
| 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); |
|
|
| |
| if (retMode == DragAndDropVisualMode.None) |
| { |
| retMode = HandleClipPaneObjectDragAndDrop(objectsBeingDropped, track, perform, m_Timeline, parent, director, m_Window.state.timeAreaShownRange.x, ResolveType, insertBefore); |
| } |
|
|
| |
| 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 (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; |
|
|
| |
| if (targetTrack != null && targetTrack.lockedInHierarchy) |
| return DragAndDropVisualMode.Rejected; |
|
|
| |
| if (targetTrack is GroupTrack) |
| { |
| parent = targetTrack; |
| targetTrack = null; |
| } |
|
|
| |
| if (objectsBeingDropped.Any(o => o is MonoScript)) |
| return HandleClipPaneMonoScriptDragAndDrop(objectsBeingDropped.OfType<MonoScript>(), targetTrack, perform, timeline, parent, director, candidateTime); |
|
|
| |
| if (!objectsBeingDropped.Any() || objectsBeingDropped.Any(o => !ValidateObjectDrop(o))) |
| return DragAndDropVisualMode.Rejected; |
|
|
| |
| if (director == null && objectsBeingDropped.Any(o => o.IsSceneObject())) |
| return DragAndDropVisualMode.Rejected; |
|
|
| var validTrackTypes = objectsBeingDropped.SelectMany(o => TypeUtility.GetTrackTypesForObject(o)).Distinct().ToList(); |
| |
| 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(); |
|
|
| |
| if (!(markerTypes.Any() || validTrackTypes.Any())) |
| { |
| return DragAndDropVisualMode.Rejected; |
| } |
| |
| if (targetTrack != null && markerTypes.Any(o => !TypeUtility.DoesTrackSupportMarkerType(targetTrack, o))) |
| { |
| |
| if (!validTrackTypes.Contains(targetTrack.GetType())) |
| return DragAndDropVisualMode.Rejected; |
| } |
|
|
| |
| if (perform) |
| { |
| |
| if (targetTrack == null) |
| { |
| var createdTrack = HandleTrackAndItemCreation(objectsBeingDropped, candidateTime, typeResolver, timeline, parent, validTrackTypes, insertBefore); |
| if (!createdTrack) |
| { |
| timeline.CreateMarkerTrack(); |
| HandleItemCreation(objectsBeingDropped, timeline.markerTrack, candidateTime, typeResolver, true); |
| } |
| } |
| |
| else |
| { |
| HandleItemCreation(objectsBeingDropped, targetTrack, candidateTime, typeResolver, true); |
| } |
| } |
|
|
| 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); |
| }; |
| return typeResolver(validTrackTypes, t => onResolved(t), k_SelectTrackWithClip); |
| } |
|
|
| 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 (assetTypes.All(x => x.Count() <= 1)) |
| { |
| foreach (var type in flatAssetTypes) |
| { |
| onCreateItem(type); |
| } |
| } |
| else |
| { |
| if (!allowMenu) |
| { |
| flatAssetTypes = new[] { flatAssetTypes.First() }; |
| } |
|
|
| typeResolver(flatAssetTypes, onCreateItem, k_SelectClip); |
| } |
| } |
|
|
| |
| 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) |
| { |
| |
| 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) |
| { |
| |
| 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; |
|
|
| |
| 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; |
| } |
|
|
| |
| return true; |
| } |
| } |
| } |
|
|