Maze / Library /PackageCache /com.unity.timeline@1.6.5 /Editor /Manipulators /Move /MoveItemModeRipple.cs
| using System; | |
| using System.Collections.Generic; | |
| using System.Linq; | |
| using UnityEngine; | |
| using Object = UnityEngine.Object; | |
| namespace UnityEditor.Timeline | |
| { | |
| class MoveItemModeRipple : IMoveItemMode, IMoveItemDrawer | |
| { | |
| const float k_SnapToEdgeDistance = 30.0f; | |
| class PrevItemInfo | |
| { | |
| public ITimelineItem item; | |
| public ITimelineItem firstSelectedItem; | |
| public bool blending; | |
| public PrevItemInfo(ITimelineItem item, ITimelineItem firstSelectedItem) | |
| { | |
| this.item = item; | |
| this.firstSelectedItem = firstSelectedItem; | |
| blending = item != null && item.end > firstSelectedItem.start; | |
| } | |
| } | |
| readonly Dictionary<Object, List<ITimelineItem>> m_NextItems = new Dictionary<Object, List<ITimelineItem>>(); | |
| readonly Dictionary<Object, PrevItemInfo> m_PreviousItem = new Dictionary<Object, PrevItemInfo>(); | |
| double m_PreviousEnd; | |
| bool m_TrackLocked; | |
| bool m_Detached; | |
| public void OnTrackDetach(IEnumerable<ItemsPerTrack> itemsGroups) | |
| { | |
| if (m_TrackLocked) | |
| return; | |
| if (m_Detached) | |
| return; | |
| if (itemsGroups.Any(x => x.markers.Any())) | |
| return; | |
| // Ripple can either remove or not clips when detaching them from their track. | |
| // Keep it off for now. TODO: add clutch key to toggle this feature? | |
| //EditModeRippleUtils.Remove(manipulatedClipsList); | |
| StartDetachMode(itemsGroups); | |
| } | |
| public void HandleTrackSwitch(IEnumerable<ItemsPerTrack> itemsGroups) | |
| { | |
| // Nothing | |
| } | |
| public bool AllowTrackSwitch() | |
| { | |
| return !m_TrackLocked; | |
| } | |
| public double AdjustStartTime(WindowState state, ItemsPerTrack itemsGroup, double time) | |
| { | |
| var track = itemsGroup.targetTrack; | |
| if (track == null) | |
| return time; | |
| double start; | |
| double end; | |
| if (EditModeUtils.IsInfiniteTrack(track)) | |
| { | |
| EditModeUtils.GetInfiniteClipBoundaries(track, out start, out end); | |
| } | |
| else | |
| { | |
| var siblings = ItemsUtils.GetItemsExcept(track, itemsGroup.items); | |
| var firstIntersectedItem = EditModeUtils.GetFirstIntersectedItem(siblings, time); | |
| if (firstIntersectedItem == null) | |
| return time; | |
| start = firstIntersectedItem.start; | |
| end = firstIntersectedItem.end; | |
| } | |
| var closestTime = Math.Abs(time - start) < Math.Abs(time - end) ? start : end; | |
| var pixelTime = state.TimeToPixel(time); | |
| var pixelClosestTime = state.TimeToPixel(closestTime); | |
| if (Math.Abs(pixelTime - pixelClosestTime) < k_SnapToEdgeDistance) | |
| return closestTime; | |
| return time; | |
| } | |
| void StartDetachMode(IEnumerable<ItemsPerTrack> itemsGroups) | |
| { | |
| m_Detached = true; | |
| foreach (var itemsGroup in itemsGroups) | |
| EditModeUtils.SetParentTrack(itemsGroup.items, null); | |
| } | |
| public void OnModeClutchEnter(IEnumerable<ItemsPerTrack> itemsGroups) | |
| { | |
| StartDetachMode(itemsGroups); | |
| m_TrackLocked = false; | |
| } | |
| public void OnModeClutchExit(IEnumerable<ItemsPerTrack> itemsGroups) | |
| { | |
| m_Detached = false; | |
| m_TrackLocked = false; | |
| } | |
| public void BeginMove(IEnumerable<ItemsPerTrack> itemsGroups) | |
| { | |
| m_NextItems.Clear(); | |
| m_PreviousItem.Clear(); | |
| var itemTypes = ItemsUtils.GetItemTypes(itemsGroups).ToList(); | |
| foreach (var itemsGroup in itemsGroups) | |
| { | |
| //can only ripple items of the same type as those selected | |
| var sortedSelectedItems = itemsGroup.items.OrderBy(i => i.start).ToList(); | |
| var siblings = itemsGroup.targetTrack.GetItemsExcept(itemsGroup.items); | |
| var sortedSiblingsToRipple = siblings.Where(i => itemTypes.Contains(i.GetType())).OrderBy(i => i.start).ToList(); | |
| var start = sortedSelectedItems.First().start; | |
| m_NextItems.Add(itemsGroup.targetTrack, sortedSiblingsToRipple.Where(i => i.start > start).ToList()); | |
| m_PreviousItem.Add(itemsGroup.targetTrack, CalculatePrevItemInfo(sortedSelectedItems, sortedSiblingsToRipple, itemTypes)); | |
| } | |
| m_PreviousEnd = itemsGroups.Max(m => m.items.Max(c => c.end)); | |
| } | |
| public void UpdateMove(IEnumerable<ItemsPerTrack> itemsGroups) | |
| { | |
| if (m_Detached) | |
| return; | |
| m_TrackLocked = true; | |
| var overlap = 0.0; | |
| foreach (var itemsGroup in itemsGroups) | |
| { | |
| var track = itemsGroup.targetTrack; | |
| if (track == null) continue; | |
| var prevItemInfo = m_PreviousItem[track]; | |
| if (prevItemInfo.item != null) | |
| { | |
| var prevItem = prevItemInfo.item; | |
| var firstItem = prevItemInfo.firstSelectedItem; | |
| if (prevItemInfo.blending) | |
| prevItemInfo.blending = prevItem.end > firstItem.start; | |
| if (prevItemInfo.blending) | |
| { | |
| var b = EditModeUtils.BlendDuration(firstItem, TrimEdge.End); | |
| overlap = Math.Max(overlap, Math.Max(prevItem.start, prevItem.end - firstItem.end + firstItem.start + b) - firstItem.start); | |
| } | |
| else | |
| { | |
| overlap = Math.Max(overlap, prevItem.end - firstItem.start); | |
| } | |
| } | |
| } | |
| if (overlap > 0) | |
| { | |
| foreach (var itemsGroup in itemsGroups) | |
| { | |
| foreach (var item in itemsGroup.items) | |
| item.start += overlap; | |
| } | |
| } | |
| var newEnd = itemsGroups.Max(m => m.items.Max(c => c.end)); | |
| var offset = newEnd - m_PreviousEnd; | |
| m_PreviousEnd = newEnd; | |
| foreach (var itemsGroup in itemsGroups) | |
| { | |
| foreach (var item in m_NextItems[itemsGroup.targetTrack]) | |
| item.start += offset; | |
| } | |
| } | |
| static PrevItemInfo CalculatePrevItemInfo(List<ITimelineItem> orderedSelection, List<ITimelineItem> orderedSiblings, IEnumerable<Type> itemTypes) | |
| { | |
| ITimelineItem previousItem = null; | |
| ITimelineItem firstSelectedItem = null; | |
| var gap = double.PositiveInfinity; | |
| foreach (var type in itemTypes) | |
| { | |
| var firstSelectedItemOfType = orderedSelection.FirstOrDefault(i => i.GetType() == type); | |
| if (firstSelectedItemOfType == null) continue; | |
| var previousItemOfType = orderedSiblings.LastOrDefault(i => i.GetType() == type && i.start < firstSelectedItemOfType.start); | |
| if (previousItemOfType == null) continue; | |
| var currentGap = firstSelectedItemOfType.start - previousItemOfType.end; | |
| if (currentGap < gap) | |
| { | |
| gap = currentGap; | |
| firstSelectedItem = firstSelectedItemOfType; | |
| previousItem = previousItemOfType; | |
| } | |
| } | |
| return new PrevItemInfo(previousItem, firstSelectedItem); | |
| } | |
| public bool ValidateMove(ItemsPerTrack itemsGroup) | |
| { | |
| return true; | |
| } | |
| public void FinishMove(IEnumerable<ItemsPerTrack> itemsGroups) | |
| { | |
| if (m_Detached) | |
| EditModeRippleUtils.Insert(itemsGroups); | |
| m_Detached = false; | |
| m_TrackLocked = false; | |
| } | |
| public void DrawGUI(WindowState state, IEnumerable<MovingItems> movingItems, Color color) | |
| { | |
| if (m_Detached) | |
| { | |
| var xMin = float.MaxValue; | |
| var xMax = float.MinValue; | |
| foreach (var grabbedItems in movingItems) | |
| { | |
| xMin = Math.Min(xMin, grabbedItems.onTrackItemsBounds.Min(b => b.xMin)); // TODO Cache this? | |
| xMax = Math.Max(xMax, grabbedItems.onTrackItemsBounds.Max(b => b.xMax)); | |
| } | |
| foreach (var grabbedItems in movingItems) | |
| { | |
| var bounds = Rect.MinMaxRect(xMin, grabbedItems.onTrackItemsBounds[0].yMin, | |
| xMax, grabbedItems.onTrackItemsBounds[0].yMax); | |
| EditModeGUIUtils.DrawOverlayRect(bounds, new Color(1.0f, 1.0f, 1.0f, 0.5f)); | |
| EditModeGUIUtils.DrawBoundsEdge(bounds, color, TrimEdge.Start); | |
| } | |
| } | |
| else | |
| { | |
| foreach (var grabbedItems in movingItems) | |
| { | |
| var bounds = Rect.MinMaxRect(grabbedItems.onTrackItemsBounds.Min(b => b.xMin), grabbedItems.onTrackItemsBounds[0].yMin, | |
| grabbedItems.onTrackItemsBounds.Max(b => b.xMax), grabbedItems.onTrackItemsBounds[0].yMax); | |
| EditModeGUIUtils.DrawBoundsEdge(bounds, color, TrimEdge.Start); | |
| } | |
| } | |
| TimelineCursors.SetCursor(TimelineCursors.CursorType.Ripple); | |
| } | |
| } | |
| } | |