Initial Commit
This commit is contained in:
parent
53eb92e9af
commit
270ab7d11f
15341 changed files with 700234 additions and 0 deletions
|
@ -0,0 +1,28 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
interface IMoveItemMode
|
||||
{
|
||||
void OnTrackDetach(IEnumerable<ItemsPerTrack> itemsGroups);
|
||||
void HandleTrackSwitch(IEnumerable<ItemsPerTrack> itemsGroups);
|
||||
bool AllowTrackSwitch();
|
||||
|
||||
double AdjustStartTime(WindowState state, ItemsPerTrack itemsGroup, double time);
|
||||
|
||||
void OnModeClutchEnter(IEnumerable<ItemsPerTrack> itemsGroups);
|
||||
void OnModeClutchExit(IEnumerable<ItemsPerTrack> itemsGroups);
|
||||
|
||||
void BeginMove(IEnumerable<ItemsPerTrack> itemsGroups);
|
||||
void UpdateMove(IEnumerable<ItemsPerTrack> itemsGroups);
|
||||
void FinishMove(IEnumerable<ItemsPerTrack> itemsGroups);
|
||||
|
||||
bool ValidateMove(ItemsPerTrack itemsGroup);
|
||||
}
|
||||
|
||||
interface IMoveItemDrawer
|
||||
{
|
||||
void DrawGUI(WindowState state, IEnumerable<MovingItems> movingItems, Color color);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3ff3d24ea34f9f74cb138e435f5f491e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,311 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class MoveItemHandler : IAttractable, IAttractionHandler
|
||||
{
|
||||
bool m_Grabbing;
|
||||
|
||||
MovingItems m_LeftMostMovingItems;
|
||||
MovingItems m_RightMostMovingItems;
|
||||
|
||||
HashSet<TimelineItemGUI> m_ItemGUIs;
|
||||
ItemsGroup m_ItemsGroup;
|
||||
|
||||
public TrackAsset targetTrack { get; private set; }
|
||||
|
||||
public bool allowTrackSwitch { get; private set; }
|
||||
|
||||
int m_GrabbedModalUndoGroup = -1;
|
||||
|
||||
readonly WindowState m_State;
|
||||
|
||||
public MovingItems[] movingItems { get; private set; }
|
||||
|
||||
public MoveItemHandler(WindowState state)
|
||||
{
|
||||
m_State = state;
|
||||
}
|
||||
|
||||
public void Grab(IEnumerable<ITimelineItem> items, TrackAsset referenceTrack)
|
||||
{
|
||||
Grab(items, referenceTrack, Vector2.zero);
|
||||
}
|
||||
|
||||
public void Grab(IEnumerable<ITimelineItem> items, TrackAsset referenceTrack, Vector2 mousePosition)
|
||||
{
|
||||
if (items == null) return;
|
||||
|
||||
items = items.ToArray(); // Cache enumeration result
|
||||
|
||||
if (!items.Any()) return;
|
||||
|
||||
m_GrabbedModalUndoGroup = Undo.GetCurrentGroup();
|
||||
|
||||
var trackItems = items.GroupBy(c => c.parentTrack).ToArray();
|
||||
var trackItemsCount = trackItems.Length;
|
||||
var tracks = items.Select(c => c.parentTrack).Where(x => x != null).Distinct();
|
||||
|
||||
movingItems = new MovingItems[trackItemsCount];
|
||||
|
||||
allowTrackSwitch = trackItemsCount == 1 && !trackItems.SelectMany(x => x).Any(x => x is MarkerItem); // For now, track switch is only supported when all items are on the same track and there are no items
|
||||
|
||||
// one push per track handles all the clips on the track
|
||||
UndoExtensions.RegisterTracks(tracks, L10n.Tr("Move Items"));
|
||||
foreach (var sourceTrack in tracks)
|
||||
{
|
||||
// push all markers on the track because of ripple
|
||||
UndoExtensions.RegisterMarkers(sourceTrack.GetMarkers(), L10n.Tr("Move Items"));
|
||||
}
|
||||
|
||||
for (var i = 0; i < trackItemsCount; ++i)
|
||||
{
|
||||
var track = trackItems[i].Key;
|
||||
var grabbedItems = new MovingItems(m_State, track, trackItems[i].ToArray(), referenceTrack, mousePosition, allowTrackSwitch);
|
||||
movingItems[i] = grabbedItems;
|
||||
}
|
||||
|
||||
m_LeftMostMovingItems = null;
|
||||
m_RightMostMovingItems = null;
|
||||
|
||||
foreach (var grabbedTrackItems in movingItems)
|
||||
{
|
||||
if (m_LeftMostMovingItems == null || m_LeftMostMovingItems.start > grabbedTrackItems.start)
|
||||
m_LeftMostMovingItems = grabbedTrackItems;
|
||||
|
||||
if (m_RightMostMovingItems == null || m_RightMostMovingItems.end < grabbedTrackItems.end)
|
||||
m_RightMostMovingItems = grabbedTrackItems;
|
||||
}
|
||||
|
||||
m_ItemGUIs = new HashSet<TimelineItemGUI>();
|
||||
m_ItemsGroup = new ItemsGroup(items);
|
||||
|
||||
foreach (var item in items)
|
||||
m_ItemGUIs.Add(item.gui);
|
||||
|
||||
targetTrack = referenceTrack;
|
||||
|
||||
EditMode.BeginMove(this);
|
||||
m_Grabbing = true;
|
||||
}
|
||||
|
||||
public void Drop()
|
||||
{
|
||||
if (IsValidDrop())
|
||||
{
|
||||
foreach (var grabbedItems in movingItems)
|
||||
{
|
||||
var track = grabbedItems.targetTrack;
|
||||
UndoExtensions.RegisterTrack(track, L10n.Tr("Move Items"));
|
||||
|
||||
if (EditModeUtils.IsInfiniteTrack(track) && grabbedItems.clips.Any())
|
||||
((AnimationTrack)track).ConvertToClipMode();
|
||||
}
|
||||
|
||||
EditMode.FinishMove();
|
||||
|
||||
Done();
|
||||
}
|
||||
else
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
|
||||
EditMode.ClearEditMode();
|
||||
}
|
||||
|
||||
bool IsValidDrop()
|
||||
{
|
||||
return movingItems.All(g => g.canDrop);
|
||||
}
|
||||
|
||||
void Cancel()
|
||||
{
|
||||
if (!m_Grabbing)
|
||||
return;
|
||||
|
||||
// TODO fix undo reselection persistency
|
||||
// identify the clips by their playable asset, since that reference will survive the undo
|
||||
// This is a workaround, until a more persistent fix for selection of clips across Undo can be found
|
||||
var assets = movingItems.SelectMany(x => x.clips).Select(x => x.asset);
|
||||
|
||||
Undo.RevertAllDownToGroup(m_GrabbedModalUndoGroup);
|
||||
|
||||
// reselect the clips from the original clip
|
||||
var clipsToSelect = movingItems.Select(x => x.originalTrack).SelectMany(x => x.GetClips()).Where(x => assets.Contains(x.asset)).ToArray();
|
||||
SelectionManager.RemoveTimelineSelection();
|
||||
|
||||
foreach (var c in clipsToSelect)
|
||||
SelectionManager.Add(c);
|
||||
|
||||
Done();
|
||||
}
|
||||
|
||||
void Done()
|
||||
{
|
||||
foreach (var movingItem in movingItems)
|
||||
{
|
||||
foreach (var item in movingItem.items)
|
||||
{
|
||||
if (item.gui != null)
|
||||
item.gui.isInvalid = false;
|
||||
}
|
||||
}
|
||||
|
||||
movingItems = null;
|
||||
m_LeftMostMovingItems = null;
|
||||
m_RightMostMovingItems = null;
|
||||
m_Grabbing = false;
|
||||
|
||||
m_State.Refresh();
|
||||
}
|
||||
|
||||
public double start { get { return m_ItemsGroup.start; } }
|
||||
|
||||
public double end { get { return m_ItemsGroup.end; } }
|
||||
|
||||
public bool ShouldSnapTo(ISnappable snappable)
|
||||
{
|
||||
var itemGUI = snappable as TimelineItemGUI;
|
||||
return itemGUI != null && !m_ItemGUIs.Contains(itemGUI);
|
||||
}
|
||||
|
||||
public void UpdateTrackTarget(TrackAsset track)
|
||||
{
|
||||
if (!EditMode.AllowTrackSwitch())
|
||||
return;
|
||||
|
||||
targetTrack = track;
|
||||
|
||||
var targetTracksChanged = false;
|
||||
|
||||
foreach (var grabbedItem in movingItems)
|
||||
{
|
||||
var prevTrackGUI = grabbedItem.targetTrack;
|
||||
|
||||
grabbedItem.SetReferenceTrack(track);
|
||||
|
||||
targetTracksChanged = grabbedItem.targetTrack != prevTrackGUI;
|
||||
}
|
||||
|
||||
if (targetTracksChanged)
|
||||
EditMode.HandleTrackSwitch(movingItems);
|
||||
|
||||
RefreshPreviewItems();
|
||||
|
||||
m_State.rebuildGraph |= targetTracksChanged;
|
||||
}
|
||||
|
||||
public void OnGUI(Event evt)
|
||||
{
|
||||
if (!m_Grabbing)
|
||||
return;
|
||||
|
||||
if (evt.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
var isValid = IsValidDrop();
|
||||
|
||||
using (new GUIViewportScope(m_State.GetWindow().sequenceContentRect))
|
||||
{
|
||||
foreach (var grabbedClip in movingItems)
|
||||
{
|
||||
grabbedClip.RefreshBounds(m_State, evt.mousePosition);
|
||||
|
||||
if (!grabbedClip.HasAnyDetachedParents())
|
||||
continue;
|
||||
|
||||
grabbedClip.Draw(isValid);
|
||||
}
|
||||
|
||||
if (isValid)
|
||||
{
|
||||
EditMode.DrawMoveGUI(m_State, movingItems);
|
||||
}
|
||||
else
|
||||
{
|
||||
TimelineCursors.ClearCursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnAttractedEdge(IAttractable attractable, ManipulateEdges manipulateEdges, AttractedEdge edge, double time)
|
||||
{
|
||||
double offset;
|
||||
|
||||
if (edge == AttractedEdge.Right)
|
||||
{
|
||||
var duration = end - start;
|
||||
var startTime = time - duration;
|
||||
startTime = EditMode.AdjustStartTime(m_State, m_RightMostMovingItems, startTime);
|
||||
|
||||
offset = startTime + duration - end;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (edge == AttractedEdge.Left)
|
||||
time = EditMode.AdjustStartTime(m_State, m_LeftMostMovingItems, time);
|
||||
|
||||
offset = time - start;
|
||||
}
|
||||
|
||||
if (start + offset < 0.0)
|
||||
offset = -start;
|
||||
|
||||
if (!offset.Equals(0.0))
|
||||
{
|
||||
foreach (var grabbedClips in movingItems)
|
||||
grabbedClips.start += offset;
|
||||
|
||||
EditMode.UpdateMove();
|
||||
|
||||
RefreshPreviewItems();
|
||||
}
|
||||
}
|
||||
|
||||
public void RefreshPreviewItems()
|
||||
{
|
||||
foreach (var movingItemsGroup in movingItems)
|
||||
{
|
||||
// Check validity
|
||||
var valid = ValidateItemDrag(movingItemsGroup);
|
||||
|
||||
foreach (var item in movingItemsGroup.items)
|
||||
{
|
||||
if (item.gui != null)
|
||||
item.gui.isInvalid = !valid;
|
||||
}
|
||||
|
||||
movingItemsGroup.canDrop = valid;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ValidateItemDrag(ItemsPerTrack itemsGroup)
|
||||
{
|
||||
//TODO-marker: this is to prevent the drag operation from being canceled when moving only markers
|
||||
if (itemsGroup.clips.Any())
|
||||
{
|
||||
if (itemsGroup.targetTrack == null)
|
||||
return false;
|
||||
|
||||
if (itemsGroup.targetTrack.lockedInHierarchy)
|
||||
return false;
|
||||
|
||||
if (itemsGroup.items.Any(i => !i.IsCompatibleWithTrack(itemsGroup.targetTrack)))
|
||||
return false;
|
||||
|
||||
return EditMode.ValidateDrag(itemsGroup);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnTrackDetach()
|
||||
{
|
||||
EditMode.OnTrackDetach(movingItems);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f6bd368ab00d75c459e2582e017191e6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,161 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class MoveItemModeMix : IMoveItemMode, IMoveItemDrawer
|
||||
{
|
||||
TimelineClip[] m_ClipsMoved;
|
||||
Dictionary<TimelineClip, double> m_OriginalEaseInDuration = new Dictionary<TimelineClip, double>();
|
||||
Dictionary<TimelineClip, double> m_OriginalEaseOutDuration = new Dictionary<TimelineClip, double>();
|
||||
|
||||
public void OnTrackDetach(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
// Nothing
|
||||
}
|
||||
|
||||
public void HandleTrackSwitch(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
foreach (var itemsGroup in itemsGroups)
|
||||
{
|
||||
var targetTrack = itemsGroup.targetTrack;
|
||||
if (targetTrack != null && itemsGroup.items.Any())
|
||||
{
|
||||
var compatible = itemsGroup.items.First().IsCompatibleWithTrack(targetTrack) &&
|
||||
!EditModeUtils.IsInfiniteTrack(targetTrack);
|
||||
var track = compatible ? targetTrack : null;
|
||||
|
||||
UndoExtensions.RegisterTrack(track, L10n.Tr("Move Items"));
|
||||
EditModeUtils.SetParentTrack(itemsGroup.items, track);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditModeUtils.SetParentTrack(itemsGroup.items, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool AllowTrackSwitch()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public double AdjustStartTime(WindowState state, ItemsPerTrack itemsGroup, double time)
|
||||
{
|
||||
return time;
|
||||
}
|
||||
|
||||
public void OnModeClutchEnter(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
// Nothing
|
||||
}
|
||||
|
||||
public void OnModeClutchExit(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
// Nothing
|
||||
}
|
||||
|
||||
public void BeginMove(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
m_ClipsMoved = itemsGroups.SelectMany(i => i.clips).ToArray();
|
||||
foreach (var clip in m_ClipsMoved)
|
||||
{
|
||||
m_OriginalEaseInDuration[clip] = clip.easeInDuration;
|
||||
m_OriginalEaseOutDuration[clip] = clip.easeOutDuration;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateMove(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
//Compute Blends before updating ease values.
|
||||
foreach(var t in itemsGroups.Select(i=>i.targetTrack).Where(t => t != null))
|
||||
t.ComputeBlendsFromOverlaps();
|
||||
//Reset to original ease values. The trim operation will calculate the proper blend values.
|
||||
foreach(var clip in m_ClipsMoved)
|
||||
{
|
||||
clip.easeInDuration = m_OriginalEaseInDuration[clip];
|
||||
clip.easeOutDuration = m_OriginalEaseOutDuration[clip];
|
||||
EditorUtility.SetDirty(clip.asset);
|
||||
}
|
||||
}
|
||||
|
||||
public void FinishMove(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
var allClips = itemsGroups.Select(i=>i.targetTrack)
|
||||
.Where(t=>t != null).SelectMany(t => t.clips);
|
||||
// update easeIn easeOut durations to apply any modifications caused by blends created or modified by clip move.
|
||||
foreach (var clip in allClips)
|
||||
{
|
||||
clip.easeInDuration = clip.easeInDuration;
|
||||
clip.easeOutDuration = clip.easeOutDuration;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ValidateMove(ItemsPerTrack itemsGroup)
|
||||
{
|
||||
var track = itemsGroup.targetTrack;
|
||||
var items = itemsGroup.items;
|
||||
|
||||
if (EditModeUtils.IsInfiniteTrack(track))
|
||||
{
|
||||
double startTime;
|
||||
double stopTime;
|
||||
EditModeUtils.GetInfiniteClipBoundaries(track, out startTime, out stopTime);
|
||||
|
||||
return items.All(item =>
|
||||
!EditModeUtils.IsItemWithinRange(item, startTime, stopTime) &&
|
||||
!EditModeUtils.IsRangeWithinItem(startTime, stopTime, item));
|
||||
}
|
||||
|
||||
var siblings = ItemsUtils.GetItemsExcept(itemsGroup.targetTrack, items);
|
||||
return items.All(item => EditModeMixUtils.GetPlacementValidity(item, siblings) == PlacementValidity.Valid);
|
||||
}
|
||||
|
||||
public void DrawGUI(WindowState state, IEnumerable<MovingItems> movingItems, Color color)
|
||||
{
|
||||
var selectionHasAnyBlendIn = false;
|
||||
var selectionHasAnyBlendOut = false;
|
||||
|
||||
foreach (var grabbedItems in movingItems)
|
||||
{
|
||||
var bounds = grabbedItems.onTrackItemsBounds;
|
||||
|
||||
var counter = 0;
|
||||
foreach (var item in grabbedItems.items.OfType<IBlendable>())
|
||||
{
|
||||
if (item.hasLeftBlend)
|
||||
{
|
||||
EditModeGUIUtils.DrawBoundsEdge(bounds[counter], color, TrimEdge.Start);
|
||||
selectionHasAnyBlendIn = true;
|
||||
}
|
||||
|
||||
if (item.hasRightBlend)
|
||||
{
|
||||
EditModeGUIUtils.DrawBoundsEdge(bounds[counter], color, TrimEdge.End);
|
||||
selectionHasAnyBlendOut = true;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectionHasAnyBlendIn && selectionHasAnyBlendOut)
|
||||
{
|
||||
TimelineCursors.SetCursor(TimelineCursors.CursorType.MixBoth);
|
||||
}
|
||||
else if (selectionHasAnyBlendIn)
|
||||
{
|
||||
TimelineCursors.SetCursor(TimelineCursors.CursorType.MixLeft);
|
||||
}
|
||||
else if (selectionHasAnyBlendOut)
|
||||
{
|
||||
TimelineCursors.SetCursor(TimelineCursors.CursorType.MixRight);
|
||||
}
|
||||
else
|
||||
{
|
||||
TimelineCursors.ClearCursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a2a8aecb05814e644abbb070fbd91156
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,99 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class MoveItemModeReplace : IMoveItemMode, IMoveItemDrawer
|
||||
{
|
||||
public void OnTrackDetach(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
// Nothing
|
||||
}
|
||||
|
||||
public void HandleTrackSwitch(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
// Nothing
|
||||
}
|
||||
|
||||
public bool AllowTrackSwitch()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public double AdjustStartTime(WindowState state, ItemsPerTrack itemsGroup, double time)
|
||||
{
|
||||
return time;
|
||||
}
|
||||
|
||||
public void OnModeClutchEnter(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
public void OnModeClutchExit(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
public void BeginMove(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
foreach (var itemsGroup in itemsGroups)
|
||||
{
|
||||
EditModeUtils.SetParentTrack(itemsGroup.items, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateMove(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
// Nothing
|
||||
}
|
||||
|
||||
public void FinishMove(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
EditModeReplaceUtils.Insert(itemsGroups);
|
||||
}
|
||||
|
||||
public bool ValidateMove(ItemsPerTrack itemsGroup)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void DrawGUI(WindowState state, IEnumerable<MovingItems> movingItems, Color color)
|
||||
{
|
||||
var operationWillReplace = false;
|
||||
|
||||
foreach (var itemsPerTrack in movingItems)
|
||||
{
|
||||
var bounds = itemsPerTrack.onTrackItemsBounds;
|
||||
|
||||
var counter = 0;
|
||||
foreach (var item in itemsPerTrack.items)
|
||||
{
|
||||
if (EditModeUtils.GetFirstIntersectedItem(itemsPerTrack.items, item.start) != null)
|
||||
{
|
||||
EditModeGUIUtils.DrawBoundsEdge(bounds[counter], color, TrimEdge.Start);
|
||||
operationWillReplace = true;
|
||||
}
|
||||
|
||||
if (EditModeUtils.GetFirstIntersectedItem(itemsPerTrack.items, item.end) != null)
|
||||
{
|
||||
EditModeGUIUtils.DrawBoundsEdge(bounds[counter], color, TrimEdge.End);
|
||||
operationWillReplace = true;
|
||||
}
|
||||
|
||||
counter++;
|
||||
// TODO Display swallowed clips?
|
||||
}
|
||||
}
|
||||
|
||||
if (operationWillReplace)
|
||||
{
|
||||
TimelineCursors.SetCursor(TimelineCursors.CursorType.Replace);
|
||||
}
|
||||
else
|
||||
{
|
||||
TimelineCursors.ClearCursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ea5e2240e8a7d9046a651557deec40b2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,271 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: eebde5009793ce948bf5d4c4435b89b9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,137 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class MovingItems : ItemsPerTrack
|
||||
{
|
||||
TrackAsset m_ReferenceTrack;
|
||||
readonly bool m_AllowTrackSwitch;
|
||||
|
||||
readonly Rect[] m_ItemsBoundsOnTrack;
|
||||
readonly Vector2[] m_ItemsMouseOffsets;
|
||||
|
||||
static readonly Rect s_InvisibleBounds = new Rect(float.MaxValue, float.MaxValue, 0.0f, 0.0f);
|
||||
|
||||
public TrackAsset originalTrack { get; }
|
||||
|
||||
public override TrackAsset targetTrack
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_AllowTrackSwitch)
|
||||
return m_ReferenceTrack;
|
||||
|
||||
return originalTrack;
|
||||
}
|
||||
}
|
||||
|
||||
public bool canDrop;
|
||||
|
||||
public double start
|
||||
{
|
||||
get { return m_ItemsGroup.start; }
|
||||
set { m_ItemsGroup.start = value; }
|
||||
}
|
||||
|
||||
public double end
|
||||
{
|
||||
get { return m_ItemsGroup.end; }
|
||||
}
|
||||
|
||||
public Rect[] onTrackItemsBounds
|
||||
{
|
||||
get { return m_ItemsBoundsOnTrack; }
|
||||
}
|
||||
|
||||
public MovingItems(WindowState state, TrackAsset parentTrack, ITimelineItem[] items, TrackAsset referenceTrack, Vector2 mousePosition, bool allowTrackSwitch)
|
||||
: base(parentTrack, items)
|
||||
{
|
||||
originalTrack = parentTrack;
|
||||
m_ReferenceTrack = referenceTrack;
|
||||
m_AllowTrackSwitch = allowTrackSwitch;
|
||||
|
||||
m_ItemsBoundsOnTrack = new Rect[items.Length];
|
||||
m_ItemsMouseOffsets = new Vector2[items.Length];
|
||||
|
||||
for (int i = 0; i < items.Length; ++i)
|
||||
{
|
||||
var itemGUi = items[i].gui;
|
||||
|
||||
if (itemGUi != null)
|
||||
{
|
||||
m_ItemsBoundsOnTrack[i] = itemGUi.rect;
|
||||
m_ItemsMouseOffsets[i] = mousePosition - m_ItemsBoundsOnTrack[i].position;
|
||||
}
|
||||
}
|
||||
|
||||
canDrop = true;
|
||||
}
|
||||
|
||||
public void SetReferenceTrack(TrackAsset track)
|
||||
{
|
||||
m_ReferenceTrack = track;
|
||||
}
|
||||
|
||||
public bool HasAnyDetachedParents()
|
||||
{
|
||||
return m_ItemsGroup.items.Any(x => x.parentTrack == null);
|
||||
}
|
||||
|
||||
public void RefreshBounds(WindowState state, Vector2 mousePosition)
|
||||
{
|
||||
for (int i = 0; i < m_ItemsGroup.items.Length; ++i)
|
||||
{
|
||||
var item = m_ItemsGroup.items[i];
|
||||
var itemGUI = item.gui;
|
||||
|
||||
if (item.parentTrack != null)
|
||||
{
|
||||
m_ItemsBoundsOnTrack[i] = itemGUI.visible ? itemGUI.rect : s_InvisibleBounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (targetTrack != null)
|
||||
{
|
||||
var trackGUI = (TimelineTrackGUI)TimelineWindow.instance.allTracks.FirstOrDefault(t => t.track == targetTrack);
|
||||
if (trackGUI == null) return;
|
||||
var trackRect = trackGUI.boundingRect;
|
||||
m_ItemsBoundsOnTrack[i] = itemGUI.RectToTimeline(trackRect, state);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ItemsBoundsOnTrack[i].position = mousePosition - m_ItemsMouseOffsets[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(bool isValid)
|
||||
{
|
||||
for (int i = 0; i < m_ItemsBoundsOnTrack.Length; ++i)
|
||||
{
|
||||
var rect = m_ItemsBoundsOnTrack[i];
|
||||
DrawItemInternal(m_ItemsGroup.items[i], rect, isValid);
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawItemInternal(ITimelineItem item, Rect rect, bool isValid)
|
||||
{
|
||||
var clipGUI = item.gui as TimelineClipGUI;
|
||||
|
||||
if (clipGUI != null)
|
||||
{
|
||||
if (isValid)
|
||||
{
|
||||
clipGUI.DrawGhostClip(rect);
|
||||
}
|
||||
else
|
||||
{
|
||||
clipGUI.DrawInvalidClip(rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 81a142c61a4e14d46bb21b02548ad24d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue