Initial Commit
This commit is contained in:
parent
53eb92e9af
commit
270ab7d11f
15341 changed files with 700234 additions and 0 deletions
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: db34f310723c62440a05d3e69f262a70
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class AddDeleteItemModeMix : IAddDeleteItemMode
|
||||
{
|
||||
public void InsertItemsAtTime(IEnumerable<ItemsPerTrack> itemsGroups, double requestedTime)
|
||||
{
|
||||
ItemsUtils.SetItemsStartTime(itemsGroups, requestedTime);
|
||||
EditModeMixUtils.PrepareItemsForInsertion(itemsGroups);
|
||||
|
||||
if (!EditModeMixUtils.CanInsert(itemsGroups))
|
||||
{
|
||||
var validTime = itemsGroups.Select(c => c.targetTrack).Max(parent => parent.end);
|
||||
ItemsUtils.SetItemsStartTime(itemsGroups, validTime);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveItems(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
// Nothing
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 41e14f40b915ca743a3dffd18ffc65ab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,18 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class AddDeleteItemModeReplace : IAddDeleteItemMode
|
||||
{
|
||||
public void InsertItemsAtTime(IEnumerable<ItemsPerTrack> itemsGroups, double requestedTime)
|
||||
{
|
||||
ItemsUtils.SetItemsStartTime(itemsGroups, requestedTime);
|
||||
EditModeReplaceUtils.Insert(itemsGroups);
|
||||
}
|
||||
|
||||
public void RemoveItems(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
// Nothing
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ea28dc637ae40484da709200d3328587
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,18 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class AddDeleteItemModeRipple : IAddDeleteItemMode
|
||||
{
|
||||
public void InsertItemsAtTime(IEnumerable<ItemsPerTrack> itemsGroups, double requestedTime)
|
||||
{
|
||||
ItemsUtils.SetItemsStartTime(itemsGroups, requestedTime);
|
||||
EditModeRippleUtils.Insert(itemsGroups);
|
||||
}
|
||||
|
||||
public void RemoveItems(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
EditModeRippleUtils.Remove(itemsGroups);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 88338eb35defad644a48718188e8f219
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
interface IAddDeleteItemMode
|
||||
{
|
||||
void InsertItemsAtTime(IEnumerable<ItemsPerTrack> itemsGroups, double requestedTime);
|
||||
void RemoveItems(IEnumerable<ItemsPerTrack> itemsGroups);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4db13e1060deaae48b30246ed63b7c9b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4ad09461bf994e54da846f726a23118e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,95 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class TimelineCursors
|
||||
{
|
||||
public enum CursorType
|
||||
{
|
||||
MixBoth,
|
||||
MixLeft,
|
||||
MixRight,
|
||||
Replace,
|
||||
Ripple,
|
||||
|
||||
Pan
|
||||
}
|
||||
|
||||
class CursorInfo
|
||||
{
|
||||
public readonly string assetPath;
|
||||
public readonly Vector2 hotSpot;
|
||||
public readonly MouseCursor mouseCursorType;
|
||||
|
||||
public CursorInfo(string assetPath, Vector2 hotSpot, MouseCursor mouseCursorType)
|
||||
{
|
||||
this.assetPath = assetPath;
|
||||
this.hotSpot = hotSpot;
|
||||
this.mouseCursorType = mouseCursorType;
|
||||
}
|
||||
}
|
||||
|
||||
const string k_CursorAssetRoot = "Cursors/";
|
||||
const string k_CursorAssetsNamespace = "Timeline.";
|
||||
const string k_CursorAssetExtension = ".png";
|
||||
|
||||
const string k_MixBothCursorAssetName = k_CursorAssetsNamespace + "MixBoth" + k_CursorAssetExtension;
|
||||
const string k_MixLeftCursorAssetName = k_CursorAssetsNamespace + "MixLeft" + k_CursorAssetExtension;
|
||||
const string k_MixRightCursorAssetName = k_CursorAssetsNamespace + "MixRight" + k_CursorAssetExtension;
|
||||
const string k_ReplaceCursorAssetName = k_CursorAssetsNamespace + "Replace" + k_CursorAssetExtension;
|
||||
const string k_RippleCursorAssetName = k_CursorAssetsNamespace + "Ripple" + k_CursorAssetExtension;
|
||||
|
||||
static readonly string s_PlatformPath = (Application.platform == RuntimePlatform.WindowsEditor) ? "Windows/" : "macOS/";
|
||||
static readonly string s_CursorAssetDirectory = k_CursorAssetRoot + s_PlatformPath;
|
||||
|
||||
static readonly Dictionary<CursorType, CursorInfo> s_CursorInfoLookup = new Dictionary<CursorType, CursorInfo>
|
||||
{
|
||||
{CursorType.MixBoth, new CursorInfo(s_CursorAssetDirectory + k_MixBothCursorAssetName, new Vector2(16, 18), MouseCursor.CustomCursor)},
|
||||
{CursorType.MixLeft, new CursorInfo(s_CursorAssetDirectory + k_MixLeftCursorAssetName, new Vector2(7, 18), MouseCursor.CustomCursor)},
|
||||
{CursorType.MixRight, new CursorInfo(s_CursorAssetDirectory + k_MixRightCursorAssetName, new Vector2(25, 18), MouseCursor.CustomCursor)},
|
||||
{CursorType.Replace, new CursorInfo(s_CursorAssetDirectory + k_ReplaceCursorAssetName, new Vector2(16, 28), MouseCursor.CustomCursor)},
|
||||
{CursorType.Ripple, new CursorInfo(s_CursorAssetDirectory + k_RippleCursorAssetName, new Vector2(26, 19), MouseCursor.CustomCursor)},
|
||||
{CursorType.Pan, new CursorInfo(null, Vector2.zero, MouseCursor.Pan)}
|
||||
};
|
||||
|
||||
static readonly Dictionary<string, Texture2D> s_CursorAssetCache = new Dictionary<string, Texture2D>();
|
||||
|
||||
static CursorType? s_CurrentCursor;
|
||||
|
||||
public static void SetCursor(CursorType cursorType)
|
||||
{
|
||||
if (s_CurrentCursor.HasValue && s_CurrentCursor.Value == cursorType) return;
|
||||
|
||||
s_CurrentCursor = cursorType;
|
||||
var cursorInfo = s_CursorInfoLookup[cursorType];
|
||||
|
||||
Texture2D cursorAsset = null;
|
||||
|
||||
if (cursorInfo.mouseCursorType == MouseCursor.CustomCursor)
|
||||
{
|
||||
cursorAsset = LoadCursorAsset(cursorInfo.assetPath);
|
||||
}
|
||||
|
||||
EditorGUIUtility.SetCurrentViewCursor(cursorAsset, cursorInfo.hotSpot, cursorInfo.mouseCursorType);
|
||||
}
|
||||
|
||||
public static void ClearCursor()
|
||||
{
|
||||
if (!s_CurrentCursor.HasValue) return;
|
||||
|
||||
EditorGUIUtility.ClearCurrentViewCursor();
|
||||
s_CurrentCursor = null;
|
||||
}
|
||||
|
||||
static Texture2D LoadCursorAsset(string assetPath)
|
||||
{
|
||||
if (!s_CursorAssetCache.ContainsKey(assetPath))
|
||||
{
|
||||
s_CursorAssetCache.Add(assetPath, (Texture2D)EditorGUIUtility.Load(assetPath));
|
||||
}
|
||||
|
||||
return s_CursorAssetCache[assetPath];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f16e09785c984c445a0467e30f845636
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,344 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
static class EditMode
|
||||
{
|
||||
public enum EditType
|
||||
{
|
||||
None = -1,
|
||||
Mix = 0,
|
||||
Ripple = 1,
|
||||
Replace = 2
|
||||
}
|
||||
|
||||
interface ISubEditMode
|
||||
{
|
||||
IMoveItemMode moveItemMode { get; }
|
||||
IMoveItemDrawer moveItemDrawer { get; }
|
||||
ITrimItemMode trimItemMode { get; }
|
||||
ITrimItemDrawer trimItemDrawer { get; }
|
||||
IAddDeleteItemMode addDeleteItemMode { get; }
|
||||
|
||||
Color color { get; }
|
||||
KeyCode clutchKey { get; }
|
||||
|
||||
void Reset();
|
||||
}
|
||||
|
||||
class SubEditMode<TMoveMode, TTrimMode, TAddDeleteMode>: ISubEditMode
|
||||
where TMoveMode : class, IMoveItemMode, IMoveItemDrawer, new()
|
||||
where TTrimMode : class, ITrimItemMode, ITrimItemDrawer, new()
|
||||
where TAddDeleteMode : class, IAddDeleteItemMode, new()
|
||||
{
|
||||
public SubEditMode(Color guiColor, KeyCode key)
|
||||
{
|
||||
color = guiColor;
|
||||
clutchKey = key;
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
m_MoveItemMode = new TMoveMode();
|
||||
m_TrimItemMode = new TTrimMode();
|
||||
m_AddDeleteItemMode = new TAddDeleteMode();
|
||||
}
|
||||
|
||||
TMoveMode m_MoveItemMode;
|
||||
TTrimMode m_TrimItemMode;
|
||||
TAddDeleteMode m_AddDeleteItemMode;
|
||||
|
||||
public IMoveItemMode moveItemMode { get { return m_MoveItemMode; } }
|
||||
public IMoveItemDrawer moveItemDrawer { get { return m_MoveItemMode; } }
|
||||
public ITrimItemMode trimItemMode { get { return m_TrimItemMode; } }
|
||||
public ITrimItemDrawer trimItemDrawer { get { return m_TrimItemMode; } }
|
||||
public IAddDeleteItemMode addDeleteItemMode { get { return m_AddDeleteItemMode; } }
|
||||
public Color color { get; }
|
||||
public KeyCode clutchKey { get; }
|
||||
}
|
||||
|
||||
static readonly Dictionary<EditType, ISubEditMode> k_EditModes = new Dictionary<EditType, ISubEditMode>
|
||||
{
|
||||
{ EditType.Mix, new SubEditMode<MoveItemModeMix, TrimItemModeMix, AddDeleteItemModeMix>(DirectorStyles.kMixToolColor, KeyCode.Alpha1) },
|
||||
{ EditType.Ripple, new SubEditMode<MoveItemModeRipple, TrimItemModeRipple, AddDeleteItemModeRipple>(DirectorStyles.kRippleToolColor, KeyCode.Alpha2) },
|
||||
{ EditType.Replace, new SubEditMode<MoveItemModeReplace, TrimItemModeReplace, AddDeleteItemModeReplace>(DirectorStyles.kReplaceToolColor, KeyCode.Alpha3) }
|
||||
};
|
||||
|
||||
static EditType s_CurrentEditType = EditType.Mix;
|
||||
static EditType s_OverrideEditType = EditType.None;
|
||||
|
||||
static ITrimmable s_CurrentTrimItem;
|
||||
static TrimEdge s_CurrentTrimDirection;
|
||||
static MoveItemHandler s_CurrentMoveItemHandler;
|
||||
static EditModeInputHandler s_InputHandler = new EditModeInputHandler();
|
||||
|
||||
static ITrimItemMode trimMode
|
||||
{
|
||||
get { return GetSubEditMode(editType).trimItemMode; }
|
||||
}
|
||||
|
||||
static ITrimItemDrawer trimDrawer
|
||||
{
|
||||
get { return GetSubEditMode(editType).trimItemDrawer; }
|
||||
}
|
||||
|
||||
static IMoveItemMode moveMode
|
||||
{
|
||||
get { return GetSubEditMode(editType).moveItemMode; }
|
||||
}
|
||||
|
||||
static IMoveItemDrawer moveDrawer
|
||||
{
|
||||
get { return GetSubEditMode(editType).moveItemDrawer; }
|
||||
}
|
||||
|
||||
static IAddDeleteItemMode addDeleteMode
|
||||
{
|
||||
get { return GetSubEditMode(editType).addDeleteItemMode; }
|
||||
}
|
||||
|
||||
public static EditModeInputHandler inputHandler
|
||||
{
|
||||
get { return s_InputHandler; }
|
||||
}
|
||||
|
||||
static Color modeColor
|
||||
{
|
||||
get { return GetSubEditMode(editType).color; }
|
||||
}
|
||||
|
||||
public static EditType editType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_OverrideEditType != EditType.None)
|
||||
return s_OverrideEditType;
|
||||
|
||||
var window = TimelineWindow.instance;
|
||||
if (window != null)
|
||||
s_CurrentEditType = window.state.editType;
|
||||
|
||||
return s_CurrentEditType;
|
||||
}
|
||||
set
|
||||
{
|
||||
s_CurrentEditType = value;
|
||||
|
||||
var window = TimelineWindow.instance;
|
||||
if (window != null)
|
||||
window.state.editType = value;
|
||||
|
||||
s_OverrideEditType = EditType.None;
|
||||
}
|
||||
}
|
||||
|
||||
static ISubEditMode GetSubEditMode(EditType type)
|
||||
{
|
||||
var subEditMode = k_EditModes[type];
|
||||
if (subEditMode != null)
|
||||
return subEditMode;
|
||||
|
||||
Debug.LogError("Unsupported editmode type");
|
||||
return null;
|
||||
}
|
||||
|
||||
static EditType GetSubEditType(KeyCode key)
|
||||
{
|
||||
foreach (var subEditMode in k_EditModes)
|
||||
{
|
||||
if (subEditMode.Value.clutchKey == key)
|
||||
return subEditMode.Key;
|
||||
}
|
||||
return EditType.None;
|
||||
}
|
||||
|
||||
public static void ClearEditMode()
|
||||
{
|
||||
k_EditModes[editType].Reset();
|
||||
}
|
||||
|
||||
public static void BeginTrim(ITimelineItem item, TrimEdge trimDirection)
|
||||
{
|
||||
var itemToTrim = item as ITrimmable;
|
||||
if (itemToTrim == null) return;
|
||||
|
||||
s_CurrentTrimItem = itemToTrim;
|
||||
s_CurrentTrimDirection = trimDirection;
|
||||
trimMode.OnBeforeTrim(itemToTrim, trimDirection);
|
||||
UndoExtensions.RegisterTrack(itemToTrim.parentTrack, L10n.Tr("Trim Clip"));
|
||||
}
|
||||
|
||||
public static void TrimStart(ITimelineItem item, double time, bool affectTimeScale)
|
||||
{
|
||||
var itemToTrim = item as ITrimmable;
|
||||
if (itemToTrim == null) return;
|
||||
|
||||
trimMode.TrimStart(itemToTrim, time, affectTimeScale);
|
||||
}
|
||||
|
||||
public static void TrimEnd(ITimelineItem item, double time, bool affectTimeScale)
|
||||
{
|
||||
var itemToTrim = item as ITrimmable;
|
||||
if (itemToTrim == null) return;
|
||||
|
||||
trimMode.TrimEnd(itemToTrim, time, affectTimeScale);
|
||||
}
|
||||
|
||||
public static void DrawTrimGUI(WindowState state, TimelineItemGUI item, TrimEdge edge)
|
||||
{
|
||||
trimDrawer.DrawGUI(state, item.rect, modeColor, edge);
|
||||
}
|
||||
|
||||
public static void FinishTrim()
|
||||
{
|
||||
s_CurrentTrimItem = null;
|
||||
|
||||
TimelineCursors.ClearCursor();
|
||||
ClearEditMode();
|
||||
|
||||
TimelineEditor.Refresh(RefreshReason.ContentsModified);
|
||||
}
|
||||
|
||||
public static void BeginMove(MoveItemHandler moveItemHandler)
|
||||
{
|
||||
s_CurrentMoveItemHandler = moveItemHandler;
|
||||
moveMode.BeginMove(s_CurrentMoveItemHandler.movingItems);
|
||||
}
|
||||
|
||||
public static void UpdateMove()
|
||||
{
|
||||
moveMode.UpdateMove(s_CurrentMoveItemHandler.movingItems);
|
||||
}
|
||||
|
||||
public static void OnTrackDetach(IEnumerable<ItemsPerTrack> grabbedTrackItems)
|
||||
{
|
||||
moveMode.OnTrackDetach(grabbedTrackItems);
|
||||
}
|
||||
|
||||
public static void HandleTrackSwitch(IEnumerable<ItemsPerTrack> grabbedTrackItems)
|
||||
{
|
||||
moveMode.HandleTrackSwitch(grabbedTrackItems);
|
||||
}
|
||||
|
||||
public static bool AllowTrackSwitch()
|
||||
{
|
||||
return moveMode.AllowTrackSwitch();
|
||||
}
|
||||
|
||||
public static double AdjustStartTime(WindowState state, ItemsPerTrack itemsGroup, double time)
|
||||
{
|
||||
return moveMode.AdjustStartTime(state, itemsGroup, time);
|
||||
}
|
||||
|
||||
public static bool ValidateDrag(ItemsPerTrack itemsGroup)
|
||||
{
|
||||
return moveMode.ValidateMove(itemsGroup);
|
||||
}
|
||||
|
||||
public static void DrawMoveGUI(WindowState state, IEnumerable<MovingItems> movingItems)
|
||||
{
|
||||
moveDrawer.DrawGUI(state, movingItems, modeColor);
|
||||
}
|
||||
|
||||
public static void FinishMove()
|
||||
{
|
||||
var manipulatedItemsList = s_CurrentMoveItemHandler.movingItems;
|
||||
moveMode.FinishMove(manipulatedItemsList);
|
||||
|
||||
foreach (var itemsGroup in manipulatedItemsList)
|
||||
foreach (var item in itemsGroup.items)
|
||||
item.parentTrack = itemsGroup.targetTrack;
|
||||
|
||||
s_CurrentMoveItemHandler = null;
|
||||
|
||||
TimelineCursors.ClearCursor();
|
||||
ClearEditMode();
|
||||
|
||||
TimelineEditor.Refresh(RefreshReason.ContentsModified);
|
||||
}
|
||||
|
||||
public static void FinalizeInsertItemsAtTime(IEnumerable<ItemsPerTrack> newItems, double requestedTime)
|
||||
{
|
||||
addDeleteMode.InsertItemsAtTime(newItems, requestedTime);
|
||||
}
|
||||
|
||||
public static void PrepareItemsDelete(IEnumerable<ItemsPerTrack> newItems)
|
||||
{
|
||||
addDeleteMode.RemoveItems(newItems);
|
||||
}
|
||||
|
||||
public static void HandleModeClutch()
|
||||
{
|
||||
if (Event.current.type == EventType.KeyDown && EditorGUI.IsEditingTextField())
|
||||
return;
|
||||
|
||||
var prevType = editType;
|
||||
|
||||
if (Event.current.type == EventType.KeyDown)
|
||||
{
|
||||
var clutchEditType = GetSubEditType(Event.current.keyCode);
|
||||
if (clutchEditType != EditType.None)
|
||||
{
|
||||
s_OverrideEditType = clutchEditType;
|
||||
Event.current.Use();
|
||||
}
|
||||
}
|
||||
else if (Event.current.type == EventType.KeyUp)
|
||||
{
|
||||
var clutchEditType = GetSubEditType(Event.current.keyCode);
|
||||
if (clutchEditType == s_OverrideEditType)
|
||||
{
|
||||
s_OverrideEditType = EditType.None;
|
||||
Event.current.Use();
|
||||
}
|
||||
}
|
||||
|
||||
if (prevType != editType)
|
||||
{
|
||||
if (s_CurrentTrimItem != null)
|
||||
{
|
||||
trimMode.OnBeforeTrim(s_CurrentTrimItem, s_CurrentTrimDirection);
|
||||
}
|
||||
else if (s_CurrentMoveItemHandler != null)
|
||||
{
|
||||
if (s_CurrentMoveItemHandler.movingItems == null)
|
||||
{
|
||||
s_CurrentMoveItemHandler = null;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var movingItems in s_CurrentMoveItemHandler.movingItems)
|
||||
{
|
||||
if (movingItems != null && movingItems.HasAnyDetachedParents())
|
||||
{
|
||||
foreach (var items in movingItems.items)
|
||||
{
|
||||
items.parentTrack = movingItems.originalTrack;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var movingSelection = s_CurrentMoveItemHandler.movingItems;
|
||||
|
||||
// Handle clutch key transition if needed
|
||||
GetSubEditMode(prevType).moveItemMode.OnModeClutchExit(movingSelection);
|
||||
moveMode.OnModeClutchEnter(movingSelection);
|
||||
|
||||
moveMode.BeginMove(movingSelection);
|
||||
moveMode.HandleTrackSwitch(movingSelection);
|
||||
|
||||
UpdateMove();
|
||||
s_CurrentMoveItemHandler.RefreshPreviewItems();
|
||||
|
||||
TimelineWindow.instance.state.rebuildGraph = true; // TODO Rebuild only if parent changed
|
||||
}
|
||||
|
||||
TimelineWindow.instance.Repaint(); // TODO Refresh the toolbar without doing a full repaint?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dfd084fea478f3148b7de3d83bab1d8c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,199 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class EditModeInputHandler
|
||||
{
|
||||
readonly MoveInputHandler m_MoveHandler;
|
||||
readonly TrimInputHandler m_TrimHandler;
|
||||
|
||||
public EditModeInputHandler()
|
||||
{
|
||||
m_MoveHandler = new MoveInputHandler();
|
||||
m_TrimHandler = new TrimInputHandler();
|
||||
}
|
||||
|
||||
public void ProcessMove(InputEvent action, double value)
|
||||
{
|
||||
if (TimelineWindow.instance != null && TimelineWindow.instance.state != null)
|
||||
ProcessInputAction(m_MoveHandler, action, value, TimelineWindow.instance.state);
|
||||
}
|
||||
|
||||
public void ProcessTrim(InputEvent action, double value, bool stretch)
|
||||
{
|
||||
if (TimelineWindow.instance != null && TimelineWindow.instance.state != null)
|
||||
{
|
||||
m_TrimHandler.stretch = stretch;
|
||||
ProcessInputAction(m_TrimHandler, action, value, TimelineWindow.instance.state);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetValueForEdge(IEnumerable<ITimelineItem> items, AttractedEdge edge, double value)
|
||||
{
|
||||
if (TimelineWindow.instance != null && TimelineWindow.instance.state != null)
|
||||
MoveInputHandler.SetValueForEdge(items, edge, value, TimelineWindow.instance.state);
|
||||
}
|
||||
|
||||
public void OnGUI(WindowState state, Event evt)
|
||||
{
|
||||
if (TimelineWindow.instance != null && TimelineWindow.instance.state != null)
|
||||
{
|
||||
m_MoveHandler.OnGUI(evt);
|
||||
m_TrimHandler.OnGUI(state);
|
||||
}
|
||||
}
|
||||
|
||||
static void ProcessInputAction(IInputHandler handler, InputEvent action, double value, WindowState state)
|
||||
{
|
||||
var items = SelectionManager.SelectedItems();
|
||||
switch (action)
|
||||
{
|
||||
case InputEvent.None:
|
||||
return;
|
||||
case InputEvent.DragEnter:
|
||||
handler.OnEnterDrag(items, state);
|
||||
break;
|
||||
case InputEvent.Drag:
|
||||
handler.OnDrag(value, state);
|
||||
break;
|
||||
case InputEvent.DragExit:
|
||||
handler.OnExitDrag();
|
||||
break;
|
||||
case InputEvent.KeyboardInput:
|
||||
handler.OnSetValue(items, value, state);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
interface IInputHandler
|
||||
{
|
||||
void OnEnterDrag(IEnumerable<ITimelineItem> items, WindowState state);
|
||||
void OnDrag(double value, WindowState state);
|
||||
void OnExitDrag();
|
||||
void OnSetValue(IEnumerable<ITimelineItem> items, double value, WindowState state);
|
||||
}
|
||||
|
||||
class TrimInputHandler : IInputHandler
|
||||
{
|
||||
bool isDragging { get; set; }
|
||||
public bool stretch { get; set; }
|
||||
|
||||
IEnumerable<ITimelineItem> grabbedItems { get; set; }
|
||||
|
||||
public void OnEnterDrag(IEnumerable<ITimelineItem> items, WindowState state)
|
||||
{
|
||||
grabbedItems = items.OfType<ITrimmable>().ToArray();
|
||||
foreach (var item in grabbedItems)
|
||||
{
|
||||
EditMode.BeginTrim(item, TrimEdge.End);
|
||||
}
|
||||
|
||||
isDragging = true;
|
||||
}
|
||||
|
||||
public void OnDrag(double endValue, WindowState state)
|
||||
{
|
||||
var trimValue = endValue;
|
||||
trimValue = TimeReferenceUtility.SnapToFrameIfRequired(trimValue);
|
||||
|
||||
foreach (var item in grabbedItems)
|
||||
{
|
||||
EditMode.TrimEnd(item, trimValue, stretch);
|
||||
}
|
||||
state.UpdateRootPlayableDuration(state.editSequence.duration);
|
||||
}
|
||||
|
||||
public void OnExitDrag()
|
||||
{
|
||||
isDragging = false;
|
||||
EditMode.FinishTrim();
|
||||
TimelineWindow.instance.Repaint();
|
||||
}
|
||||
|
||||
public void OnSetValue(IEnumerable<ITimelineItem> items, double endValue, WindowState state)
|
||||
{
|
||||
foreach (var item in items.OfType<ITrimmable>())
|
||||
{
|
||||
EditMode.BeginTrim(item, TrimEdge.End);
|
||||
EditMode.TrimEnd(item, endValue, stretch);
|
||||
EditMode.FinishTrim();
|
||||
}
|
||||
state.UpdateRootPlayableDuration(state.editSequence.duration);
|
||||
}
|
||||
|
||||
public void OnGUI(WindowState state)
|
||||
{
|
||||
if (!isDragging) return;
|
||||
|
||||
foreach (var item in grabbedItems)
|
||||
{
|
||||
EditMode.DrawTrimGUI(state, item.gui, TrimEdge.End);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MoveInputHandler : IInputHandler
|
||||
{
|
||||
MoveItemHandler m_MoveItemHandler;
|
||||
|
||||
public void OnEnterDrag(IEnumerable<ITimelineItem> items, WindowState state)
|
||||
{
|
||||
if (items.Any())
|
||||
{
|
||||
m_MoveItemHandler = new MoveItemHandler(state);
|
||||
m_MoveItemHandler.Grab(items, items.First().parentTrack);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnDrag(double value, WindowState state)
|
||||
{
|
||||
if (m_MoveItemHandler == null) return;
|
||||
|
||||
var startValue = value;
|
||||
startValue = TimeReferenceUtility.SnapToFrameIfRequired(startValue);
|
||||
m_MoveItemHandler.OnAttractedEdge(null, ManipulateEdges.Both, AttractedEdge.None, startValue);
|
||||
}
|
||||
|
||||
public void OnExitDrag()
|
||||
{
|
||||
if (m_MoveItemHandler == null) return;
|
||||
|
||||
m_MoveItemHandler.Drop();
|
||||
m_MoveItemHandler = null;
|
||||
GUIUtility.ExitGUI();
|
||||
}
|
||||
|
||||
public void OnSetValue(IEnumerable<ITimelineItem> items, double value, WindowState state)
|
||||
{
|
||||
if (!items.Any()) return;
|
||||
|
||||
m_MoveItemHandler = new MoveItemHandler(state);
|
||||
m_MoveItemHandler.Grab(items, items.First().parentTrack);
|
||||
m_MoveItemHandler.OnAttractedEdge(null, ManipulateEdges.Both, AttractedEdge.None, value);
|
||||
m_MoveItemHandler.Drop();
|
||||
m_MoveItemHandler = null;
|
||||
}
|
||||
|
||||
public void OnGUI(Event evt)
|
||||
{
|
||||
if (m_MoveItemHandler != null)
|
||||
m_MoveItemHandler.OnGUI(evt);
|
||||
}
|
||||
|
||||
public static void SetValueForEdge(IEnumerable<ITimelineItem> items, AttractedEdge edge, double value, WindowState state)
|
||||
{
|
||||
var handler = new MoveItemHandler(state);
|
||||
foreach (var item in items)
|
||||
{
|
||||
handler.Grab(new[] {item}, item.parentTrack);
|
||||
handler.OnAttractedEdge(null, ManipulateEdges.Both, edge, value);
|
||||
handler.Drop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 459f6a07ee4a58b42ba2568b097c3ec4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class HeaderSplitterManipulator : Manipulator
|
||||
{
|
||||
bool m_Captured;
|
||||
|
||||
protected override bool MouseDown(Event evt, WindowState state)
|
||||
{
|
||||
Rect headerSplitterRect = state.GetWindow().headerSplitterRect;
|
||||
if (headerSplitterRect.Contains(evt.mousePosition))
|
||||
{
|
||||
m_Captured = true;
|
||||
state.AddCaptured(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool MouseDrag(Event evt, WindowState state)
|
||||
{
|
||||
if (!m_Captured)
|
||||
return false;
|
||||
|
||||
state.sequencerHeaderWidth = evt.mousePosition.x;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool MouseUp(Event evt, WindowState state)
|
||||
{
|
||||
if (!m_Captured)
|
||||
return false;
|
||||
|
||||
state.RemoveCaptured(this);
|
||||
m_Captured = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Overlay(Event evt, WindowState state)
|
||||
{
|
||||
Rect rect = state.GetWindow().sequenceRect;
|
||||
EditorGUIUtility.AddCursorRect(rect, MouseCursor.SplitResizeLeftRight);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7db6186effeb4d159963cad6defd26e5
|
||||
timeCreated: 1604439091
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bd3ffb9a97575a44a82f9ca086813154
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dec586c160776104da4d9a4e472662bc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,140 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class EaseClip : Manipulator
|
||||
{
|
||||
bool m_IsCaptured;
|
||||
bool m_UndoSaved;
|
||||
TimelineClipHandle m_EaseClipHandler;
|
||||
ManipulateEdges m_Edges;
|
||||
TimelineClip m_Clip;
|
||||
StringBuilder m_OverlayText = new StringBuilder("");
|
||||
double m_OriginalValue;
|
||||
|
||||
public static readonly string EaseInClipText = L10n.Tr("Ease In Clip");
|
||||
public static readonly string EaseOutClipText = L10n.Tr("Ease Out Clip");
|
||||
public static readonly string EaseInText = L10n.Tr("Ease In");
|
||||
public static readonly string EaseOutText = L10n.Tr("Ease Out");
|
||||
public static readonly string DurationText = L10n.Tr("Duration: ");
|
||||
|
||||
protected override bool MouseDown(Event evt, WindowState state)
|
||||
{
|
||||
if (evt.modifiers != ManipulatorsUtils.actionModifier)
|
||||
return false;
|
||||
return MouseDownInternal(evt, state, PickerUtils.TopmostPickedItem() as TimelineClipHandle);
|
||||
}
|
||||
|
||||
protected bool MouseDownInternal(Event evt, WindowState state, TimelineClipHandle handle)
|
||||
{
|
||||
if (handle == null)
|
||||
return false;
|
||||
|
||||
if (handle.clipGUI.clip != null && !handle.clipGUI.clip.clipCaps.HasAny(ClipCaps.Blending))
|
||||
return false;
|
||||
|
||||
m_Edges = ManipulateEdges.Right;
|
||||
if (handle.trimDirection == TrimEdge.Start)
|
||||
m_Edges = ManipulateEdges.Left;
|
||||
|
||||
if (m_Edges == ManipulateEdges.Left && handle.clipGUI.clip.hasBlendIn || m_Edges == ManipulateEdges.Right && handle.clipGUI.clip.hasBlendOut)
|
||||
return false;
|
||||
|
||||
m_IsCaptured = true;
|
||||
m_UndoSaved = false;
|
||||
|
||||
m_EaseClipHandler = handle;
|
||||
m_Clip = handle.clipGUI.clip;
|
||||
m_OriginalValue = m_Edges == ManipulateEdges.Left ? m_Clip.easeInDuration : m_Clip.easeOutDuration;
|
||||
|
||||
|
||||
// Change cursor only when OnGUI Process (not in test)
|
||||
if (GUIUtility.guiDepth > 0)
|
||||
TimelineCursors.SetCursor(m_Edges == ManipulateEdges.Left ? TimelineCursors.CursorType.MixRight : TimelineCursors.CursorType.MixLeft);
|
||||
|
||||
state.AddCaptured(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool MouseUp(Event evt, WindowState state)
|
||||
{
|
||||
if (!m_IsCaptured)
|
||||
return false;
|
||||
m_IsCaptured = false;
|
||||
m_UndoSaved = false;
|
||||
state.captured.Clear();
|
||||
|
||||
// Clear cursor only when OnGUI Process (not in test)
|
||||
if (GUIUtility.guiDepth > 0)
|
||||
TimelineCursors.ClearCursor();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool MouseDrag(Event evt, WindowState state)
|
||||
{
|
||||
if (!m_IsCaptured)
|
||||
return false;
|
||||
if (!m_UndoSaved)
|
||||
{
|
||||
var uiClip = m_EaseClipHandler.clipGUI;
|
||||
string undoName = m_Edges == ManipulateEdges.Left ? EaseInClipText : EaseOutClipText;
|
||||
UndoExtensions.RegisterClip(uiClip.clip, undoName);
|
||||
m_UndoSaved = true;
|
||||
}
|
||||
|
||||
double d = state.PixelDeltaToDeltaTime(evt.delta.x);
|
||||
|
||||
var duration = m_Clip.duration;
|
||||
var easeInDurationLimit = duration - m_Clip.easeOutDuration;
|
||||
var easeOutDurationLimit = duration - m_Clip.easeInDuration;
|
||||
|
||||
if (m_Edges == ManipulateEdges.Left)
|
||||
{
|
||||
m_Clip.easeInDuration = Math.Min(easeInDurationLimit, Math.Max(0, m_Clip.easeInDuration + d));
|
||||
}
|
||||
else if (m_Edges == ManipulateEdges.Right)
|
||||
{
|
||||
m_Clip.easeOutDuration = Math.Min(easeOutDurationLimit, Math.Max(0, m_Clip.easeOutDuration - d));
|
||||
}
|
||||
RefreshOverlayStrings(m_EaseClipHandler, state);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Overlay(Event evt, WindowState state)
|
||||
{
|
||||
if (!m_IsCaptured)
|
||||
return;
|
||||
if (m_OverlayText.Length > 0)
|
||||
{
|
||||
int stringLength = m_OverlayText.Length;
|
||||
var r = new Rect(evt.mousePosition.x - (stringLength / 2.0f),
|
||||
m_EaseClipHandler.clipGUI.rect.yMax,
|
||||
stringLength, 20);
|
||||
GUI.Label(r, m_OverlayText.ToString(), TimelineWindow.styles.tinyFont);
|
||||
}
|
||||
}
|
||||
|
||||
void RefreshOverlayStrings(TimelineClipHandle handle, WindowState state)
|
||||
{
|
||||
m_OverlayText.Length = 0;
|
||||
m_OverlayText.Append(m_Edges == ManipulateEdges.Left ? EaseInText : EaseOutText);
|
||||
|
||||
var easeDuration = m_Edges == ManipulateEdges.Left ? m_Clip.easeInDuration : m_Clip.easeOutDuration;
|
||||
var deltaDuration = easeDuration - m_OriginalValue;
|
||||
|
||||
// round to frame so we don't show partial time codes due to no frame snapping
|
||||
if (state.timeFormat == TimeFormat.Timecode)
|
||||
{
|
||||
easeDuration = TimeUtility.RoundToFrame(easeDuration, state.referenceSequence.frameRate);
|
||||
deltaDuration = TimeUtility.RoundToFrame(deltaDuration, state.referenceSequence.frameRate);
|
||||
}
|
||||
|
||||
m_OverlayText.Append(DurationText);
|
||||
m_OverlayText.Append(state.timeFormat.ToTimeStringWithDelta(easeDuration, state.referenceSequence.frameRate, deltaDuration));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b7cabea05434bb9479aee1e121b0d103
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,61 @@
|
|||
using UnityEditor.ShortcutManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class Jog : Manipulator
|
||||
{
|
||||
Vector2 m_MouseDownOrigin = Vector2.zero;
|
||||
|
||||
[ClutchShortcut("Timeline/Jog", typeof(TimelineWindow), KeyCode.J)]
|
||||
static void JogShortcut(ShortcutArguments args)
|
||||
{
|
||||
if (args.stage == ShortcutStage.Begin)
|
||||
{
|
||||
(args.context as TimelineWindow).state.isJogging = true;
|
||||
}
|
||||
else if (args.stage == ShortcutStage.End)
|
||||
{
|
||||
(args.context as TimelineWindow).state.isJogging = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool MouseDown(Event evt, WindowState state)
|
||||
{
|
||||
if (!state.isJogging)
|
||||
return false;
|
||||
|
||||
m_MouseDownOrigin = evt.mousePosition;
|
||||
state.playbackSpeed = 0.0f;
|
||||
state.Play();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool MouseUp(Event evt, WindowState state)
|
||||
{
|
||||
if (!state.isJogging)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_MouseDownOrigin = evt.mousePosition;
|
||||
state.playbackSpeed = 0.0f;
|
||||
state.Play();
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool MouseDrag(Event evt, WindowState state)
|
||||
{
|
||||
if (!state.isJogging)
|
||||
return false;
|
||||
|
||||
var distance = evt.mousePosition - m_MouseDownOrigin;
|
||||
|
||||
state.playbackSpeed = distance.x * 0.002f;
|
||||
state.Play();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 949b7e126b3f27940885a6808a15458e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class MarkerHeaderTrackManipulator : Manipulator
|
||||
{
|
||||
protected override bool ContextClick(Event evt, WindowState state)
|
||||
{
|
||||
if (!IsMouseOverMarkerHeader(evt.mousePosition, state))
|
||||
return false;
|
||||
|
||||
SelectionManager.SelectOnly(state.editSequence.asset.markerTrack);
|
||||
SequencerContextMenu.ShowTrackContextMenu(evt.mousePosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool MouseDown(Event evt, WindowState state)
|
||||
{
|
||||
if (evt.button != 0 || !IsMouseOverMarkerHeader(evt.mousePosition, state))
|
||||
return false;
|
||||
|
||||
SelectionManager.SelectOnly(state.editSequence.asset.markerTrack);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool IsMouseOverMarkerHeader(Vector2 mousePosition, WindowState state)
|
||||
{
|
||||
if (!state.showMarkerHeader)
|
||||
return false;
|
||||
|
||||
return state.GetWindow().markerHeaderRect.Contains(mousePosition)
|
||||
|| state.GetWindow().markerContentRect.Contains(mousePosition);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e74ddf4132f3401409c824bed60280ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,36 @@
|
|||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class RectangleSelect : RectangleTool
|
||||
{
|
||||
protected override bool enableAutoPan { get { return false; } }
|
||||
|
||||
protected override bool CanStartRectangle(Event evt)
|
||||
{
|
||||
if (evt.button != 0 || evt.alt)
|
||||
return false;
|
||||
|
||||
return PickerUtils.pickedElements.All(e => e is IRowGUI);
|
||||
}
|
||||
|
||||
protected override bool OnFinish(Event evt, WindowState state, Rect rect)
|
||||
{
|
||||
var selectables = state.spacePartitioner.GetItemsInArea<ISelectable>(rect).ToList();
|
||||
|
||||
if (!selectables.Any())
|
||||
return false;
|
||||
|
||||
if (ItemSelection.CanClearSelection(evt))
|
||||
SelectionManager.Clear();
|
||||
|
||||
foreach (var selectable in selectables)
|
||||
{
|
||||
ItemSelection.HandleItemSelection(evt, selectable);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: edd4f4b395430604d935bcf0b14c7d42
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,175 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
abstract class RectangleTool
|
||||
{
|
||||
readonly struct TimelinePoint
|
||||
{
|
||||
readonly double m_Time;
|
||||
readonly float m_YPos;
|
||||
readonly float m_YScrollPos;
|
||||
|
||||
readonly WindowState m_State;
|
||||
readonly TimelineTreeViewGUI m_TreeViewGUI;
|
||||
|
||||
public TimelinePoint(WindowState state, Vector2 mousePosition)
|
||||
{
|
||||
m_State = state;
|
||||
m_TreeViewGUI = state.GetWindow().treeView;
|
||||
|
||||
m_Time = m_State.PixelToTime(mousePosition.x);
|
||||
m_YPos = mousePosition.y;
|
||||
m_YScrollPos = m_TreeViewGUI.scrollPosition.y;
|
||||
}
|
||||
|
||||
public Vector2 ToPixel()
|
||||
{
|
||||
return new Vector2(m_State.TimeToPixel(m_Time), m_YPos - (m_TreeViewGUI.scrollPosition.y - m_YScrollPos));
|
||||
}
|
||||
}
|
||||
|
||||
const float k_HeaderSplitterOverlap = WindowConstants.headerSplitterWidth / 2;
|
||||
|
||||
TimeAreaAutoPanner m_TimeAreaAutoPanner;
|
||||
|
||||
TimelinePoint m_StartPoint;
|
||||
Vector2 m_EndPixel = Vector2.zero;
|
||||
|
||||
Rect m_ActiveRect;
|
||||
|
||||
protected abstract bool enableAutoPan { get; }
|
||||
protected abstract bool CanStartRectangle(Event evt);
|
||||
protected abstract bool OnFinish(Event evt, WindowState state, Rect rect);
|
||||
|
||||
int m_Id;
|
||||
|
||||
public void OnGUI(WindowState state, EventType rawType, Vector2 mousePosition)
|
||||
{
|
||||
if (m_Id == 0)
|
||||
m_Id = GUIUtility.GetPermanentControlID();
|
||||
|
||||
if (state == null || state.GetWindow().treeView == null)
|
||||
return;
|
||||
|
||||
var evt = Event.current;
|
||||
|
||||
if (rawType == EventType.MouseDown || evt.type == EventType.MouseDown)
|
||||
{
|
||||
if (state.IsCurrentEditingASequencerTextField())
|
||||
return;
|
||||
|
||||
m_ActiveRect = TimelineWindow.instance.sequenceContentRect;
|
||||
|
||||
//remove the track header splitter overlap
|
||||
m_ActiveRect.x += k_HeaderSplitterOverlap;
|
||||
m_ActiveRect.width -= k_HeaderSplitterOverlap;
|
||||
|
||||
if (!m_ActiveRect.Contains(mousePosition))
|
||||
return;
|
||||
|
||||
if (!CanStartRectangle(evt))
|
||||
return;
|
||||
|
||||
if (enableAutoPan)
|
||||
m_TimeAreaAutoPanner = new TimeAreaAutoPanner(state);
|
||||
|
||||
m_StartPoint = new TimelinePoint(state, mousePosition);
|
||||
m_EndPixel = mousePosition;
|
||||
|
||||
GUIUtility.hotControl = m_Id; //HACK: Because the treeView eats all the events, steal the hotControl if necessary...
|
||||
evt.Use();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch (evt.GetTypeForControl(m_Id))
|
||||
{
|
||||
case EventType.KeyDown:
|
||||
{
|
||||
if (GUIUtility.hotControl == m_Id)
|
||||
{
|
||||
if (evt.keyCode == KeyCode.Escape)
|
||||
{
|
||||
m_TimeAreaAutoPanner = null;
|
||||
|
||||
GUIUtility.hotControl = 0;
|
||||
evt.Use();
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
case EventType.MouseDrag:
|
||||
{
|
||||
if (GUIUtility.hotControl != m_Id)
|
||||
return;
|
||||
|
||||
m_EndPixel = mousePosition;
|
||||
evt.Use();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
case EventType.MouseUp:
|
||||
{
|
||||
if (GUIUtility.hotControl != m_Id)
|
||||
return;
|
||||
|
||||
m_TimeAreaAutoPanner = null;
|
||||
|
||||
var rect = CurrentRectangle();
|
||||
|
||||
if (IsValidRect(rect))
|
||||
OnFinish(evt, state, rect);
|
||||
|
||||
GUIUtility.hotControl = 0;
|
||||
evt.Use();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (GUIUtility.hotControl == m_Id)
|
||||
{
|
||||
if (evt.type == EventType.Repaint)
|
||||
{
|
||||
var r = CurrentRectangle();
|
||||
|
||||
if (IsValidRect(r))
|
||||
{
|
||||
using (new GUIViewportScope(m_ActiveRect))
|
||||
{
|
||||
DrawRectangle(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_TimeAreaAutoPanner != null)
|
||||
m_TimeAreaAutoPanner.OnGUI(evt);
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawRectangle(Rect rect)
|
||||
{
|
||||
EditorStyles.selectionRect.Draw(rect, GUIContent.none, false, false, false, false);
|
||||
}
|
||||
|
||||
static bool IsValidRect(Rect rect)
|
||||
{
|
||||
return rect.width >= 1.0f && rect.height >= 1.0f;
|
||||
}
|
||||
|
||||
Rect CurrentRectangle()
|
||||
{
|
||||
var startPixel = m_StartPoint.ToPixel();
|
||||
return Rect.MinMaxRect(
|
||||
Math.Min(startPixel.x, m_EndPixel.x),
|
||||
Math.Min(startPixel.y, m_EndPixel.y),
|
||||
Math.Max(startPixel.x, m_EndPixel.x),
|
||||
Math.Max(startPixel.y, m_EndPixel.y));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 24a7ce8b48db53747a4e8abbda77eac4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,23 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class RectangleZoom : RectangleTool
|
||||
{
|
||||
protected override bool enableAutoPan { get { return true; } }
|
||||
|
||||
protected override bool CanStartRectangle(Event evt)
|
||||
{
|
||||
return evt.button == 1 && evt.modifiers == (EventModifiers.Alt | EventModifiers.Shift);
|
||||
}
|
||||
|
||||
protected override bool OnFinish(Event evt, WindowState state, Rect rect)
|
||||
{
|
||||
var x = state.PixelToTime(rect.xMin);
|
||||
var y = state.PixelToTime(rect.xMax);
|
||||
state.SetTimeAreaShownRange(x, y);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5aa8f57287fc17149bcd798be813180b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,297 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class ClearSelection : Manipulator
|
||||
{
|
||||
protected override bool MouseDown(Event evt, WindowState state)
|
||||
{
|
||||
// If we hit this point this means no one used the mouse down events. We can safely clear the selection if needed
|
||||
if (evt.button != 0)
|
||||
return false;
|
||||
|
||||
var window = state.GetWindow();
|
||||
|
||||
if (!window.sequenceRect.Contains(evt.mousePosition))
|
||||
return false;
|
||||
|
||||
if (ItemSelection.CanClearSelection(evt))
|
||||
{
|
||||
SelectionManager.Clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static class ItemSelection
|
||||
{
|
||||
public static bool CanClearSelection(Event evt)
|
||||
{
|
||||
return !evt.control && !evt.command && !evt.shift;
|
||||
}
|
||||
|
||||
public static void RangeSelectItems(ITimelineItem lastItemToSelect)
|
||||
{
|
||||
var selectSorted = SelectionManager.SelectedItems().ToList();
|
||||
var firstSelect = selectSorted.FirstOrDefault();
|
||||
if (firstSelect == null)
|
||||
{
|
||||
SelectionManager.Add(lastItemToSelect);
|
||||
return;
|
||||
}
|
||||
|
||||
var allTracks = TimelineEditor.inspectedAsset.flattenedTracks;
|
||||
var allItems = allTracks.SelectMany(ItemsUtils.GetItems).ToList();
|
||||
TimelineHelpers.RangeSelect(allItems, selectSorted, lastItemToSelect, SelectionManager.Add, SelectionManager.Remove);
|
||||
}
|
||||
|
||||
public static ISelectable HandleSingleSelection(Event evt)
|
||||
{
|
||||
var item = PickerUtils.TopmostPickedItemOfType<ISelectable>(i => i.CanSelect(evt));
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
var selected = item.IsSelected();
|
||||
if (!selected && CanClearSelection(evt))
|
||||
SelectionManager.Clear();
|
||||
|
||||
if (evt.modifiers == EventModifiers.Shift)
|
||||
{
|
||||
if (!selected)
|
||||
RangeSelectItems((item as TimelineItemGUI)?.item);
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleItemSelection(evt, item);
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static void HandleItemSelection(Event evt, ISelectable item)
|
||||
{
|
||||
if (evt.modifiers == ManipulatorsUtils.actionModifier)
|
||||
{
|
||||
if (item.IsSelected())
|
||||
item.Deselect();
|
||||
else
|
||||
item.Select();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!item.IsSelected())
|
||||
item.Select();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SelectAndMoveItem : Manipulator
|
||||
{
|
||||
bool m_Dragged;
|
||||
SnapEngine m_SnapEngine;
|
||||
TimeAreaAutoPanner m_TimeAreaAutoPanner;
|
||||
Vector2 m_MouseDownPosition;
|
||||
|
||||
bool m_HorizontalMovementDone;
|
||||
bool m_VerticalMovementDone;
|
||||
|
||||
MoveItemHandler m_MoveItemHandler;
|
||||
bool m_CycleMarkersPending;
|
||||
|
||||
protected override bool MouseDown(Event evt, WindowState state)
|
||||
{
|
||||
if (evt.alt || evt.button != 0)
|
||||
return false;
|
||||
|
||||
m_Dragged = false;
|
||||
|
||||
// Cycling markers and selection are mutually exclusive operations
|
||||
if (!HandleMarkerCycle() && !HandleSingleSelection(evt))
|
||||
return false;
|
||||
|
||||
m_MouseDownPosition = evt.mousePosition;
|
||||
m_VerticalMovementDone = false;
|
||||
m_HorizontalMovementDone = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool MouseUp(Event evt, WindowState state)
|
||||
{
|
||||
if (!m_Dragged)
|
||||
{
|
||||
var item = PickerUtils.TopmostPickedItem() as ISelectable;
|
||||
|
||||
if (item == null)
|
||||
return false;
|
||||
|
||||
if (!item.IsSelected())
|
||||
return false;
|
||||
|
||||
// Re-selecting an item part of a multi-selection should only keep this item selected.
|
||||
if (SelectionManager.Count() > 1 && ItemSelection.CanClearSelection(evt))
|
||||
{
|
||||
SelectionManager.Clear();
|
||||
item.Select();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_CycleMarkersPending)
|
||||
{
|
||||
m_CycleMarkersPending = false;
|
||||
TimelineMarkerClusterGUI.CycleMarkers();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_TimeAreaAutoPanner = null;
|
||||
|
||||
DropItems();
|
||||
|
||||
m_SnapEngine = null;
|
||||
m_MoveItemHandler = null;
|
||||
|
||||
state.Evaluate();
|
||||
state.RemoveCaptured(this);
|
||||
m_Dragged = false;
|
||||
TimelineCursors.ClearCursor();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool DoubleClick(Event evt, WindowState state)
|
||||
{
|
||||
return MouseDown(evt, state) && MouseUp(evt, state);
|
||||
}
|
||||
|
||||
protected override bool MouseDrag(Event evt, WindowState state)
|
||||
{
|
||||
if (state.editSequence.isReadOnly)
|
||||
return false;
|
||||
|
||||
// case 1099285 - ctrl-click can cause no clips to be selected
|
||||
var selectedItemsGUI = SelectionManager.SelectedItems();
|
||||
if (!selectedItemsGUI.Any())
|
||||
{
|
||||
m_Dragged = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
const float hDeadZone = 5.0f;
|
||||
const float vDeadZone = 5.0f;
|
||||
|
||||
bool vDone = m_VerticalMovementDone || Math.Abs(evt.mousePosition.y - m_MouseDownPosition.y) > vDeadZone;
|
||||
bool hDone = m_HorizontalMovementDone || Math.Abs(evt.mousePosition.x - m_MouseDownPosition.x) > hDeadZone;
|
||||
|
||||
m_CycleMarkersPending = false;
|
||||
|
||||
if (!m_Dragged)
|
||||
{
|
||||
var canStartMove = vDone || hDone;
|
||||
|
||||
if (canStartMove)
|
||||
{
|
||||
state.AddCaptured(this);
|
||||
m_Dragged = true;
|
||||
|
||||
var referenceTrack = GetTrackDropTargetAt(state, m_MouseDownPosition);
|
||||
|
||||
foreach (var item in selectedItemsGUI)
|
||||
item.gui.StartDrag();
|
||||
|
||||
m_MoveItemHandler = new MoveItemHandler(state);
|
||||
|
||||
m_MoveItemHandler.Grab(selectedItemsGUI, referenceTrack, m_MouseDownPosition);
|
||||
|
||||
m_SnapEngine = new SnapEngine(m_MoveItemHandler, m_MoveItemHandler, ManipulateEdges.Both,
|
||||
state, m_MouseDownPosition);
|
||||
|
||||
m_TimeAreaAutoPanner = new TimeAreaAutoPanner(state);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_VerticalMovementDone)
|
||||
{
|
||||
m_VerticalMovementDone = vDone;
|
||||
|
||||
if (m_VerticalMovementDone)
|
||||
m_MoveItemHandler.OnTrackDetach();
|
||||
}
|
||||
|
||||
if (!m_HorizontalMovementDone)
|
||||
{
|
||||
m_HorizontalMovementDone = hDone;
|
||||
}
|
||||
|
||||
if (m_Dragged)
|
||||
{
|
||||
if (m_HorizontalMovementDone)
|
||||
m_SnapEngine.Snap(evt.mousePosition, evt.modifiers);
|
||||
|
||||
if (m_VerticalMovementDone)
|
||||
{
|
||||
var track = GetTrackDropTargetAt(state, evt.mousePosition);
|
||||
m_MoveItemHandler.UpdateTrackTarget(track);
|
||||
}
|
||||
|
||||
state.Evaluate();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Overlay(Event evt, WindowState state)
|
||||
{
|
||||
if (!m_Dragged)
|
||||
return;
|
||||
|
||||
if (m_TimeAreaAutoPanner != null)
|
||||
m_TimeAreaAutoPanner.OnGUI(evt);
|
||||
|
||||
m_MoveItemHandler.OnGUI(evt);
|
||||
|
||||
if (!m_MoveItemHandler.allowTrackSwitch || m_MoveItemHandler.targetTrack != null)
|
||||
{
|
||||
TimeIndicator.Draw(state, m_MoveItemHandler.start, m_MoveItemHandler.end);
|
||||
m_SnapEngine.OnGUI();
|
||||
}
|
||||
}
|
||||
|
||||
bool HandleMarkerCycle()
|
||||
{
|
||||
m_CycleMarkersPending = TimelineMarkerClusterGUI.CanCycleMarkers();
|
||||
return m_CycleMarkersPending;
|
||||
}
|
||||
|
||||
static bool HandleSingleSelection(Event evt)
|
||||
{
|
||||
return ItemSelection.HandleSingleSelection(evt) != null;
|
||||
}
|
||||
|
||||
void DropItems()
|
||||
{
|
||||
// Order matters here: m_MoveItemHandler.movingItems is destroyed during call to Drop()
|
||||
foreach (var movingItem in m_MoveItemHandler.movingItems)
|
||||
{
|
||||
foreach (var item in movingItem.items)
|
||||
item.gui.StopDrag();
|
||||
}
|
||||
|
||||
m_MoveItemHandler.Drop();
|
||||
}
|
||||
|
||||
static TrackAsset GetTrackDropTargetAt(WindowState state, Vector2 point)
|
||||
{
|
||||
var track = state.spacePartitioner.GetItemsAtPosition<IRowGUI>(point).FirstOrDefault();
|
||||
return track != null ? track.asset : null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f4f988528bbbb0846a4cb50efb4587a5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,20 @@
|
|||
using UnityEngine;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class TrackZoom : Manipulator
|
||||
{
|
||||
// only handles 'vertical' zoom. horizontal is handled in timelineGUI
|
||||
protected override bool MouseWheel(Event evt, WindowState state)
|
||||
{
|
||||
if (EditorGUI.actionKey)
|
||||
{
|
||||
state.trackScale = Mathf.Min(Mathf.Max(state.trackScale + (evt.delta.y * 0.1f), 1.0f), 100.0f);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6e7c80eefe2def5459e0b486b3ab96e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,205 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class TrimClip : Manipulator
|
||||
{
|
||||
private readonly string kDurationText = L10n.Tr("Duration:");
|
||||
private readonly string kSpeedText = L10n.Tr("Speed:");
|
||||
|
||||
class TrimClipAttractionHandler : IAttractionHandler
|
||||
{
|
||||
public void OnAttractedEdge(IAttractable attractable, ManipulateEdges manipulateEdges, AttractedEdge edge, double time)
|
||||
{
|
||||
var clipGUI = attractable as TimelineClipGUI;
|
||||
if (clipGUI == null)
|
||||
return;
|
||||
|
||||
var clipItem = ItemsUtils.ToItem(clipGUI.clip);
|
||||
if (manipulateEdges == ManipulateEdges.Right)
|
||||
{
|
||||
bool affectTimeScale = IsAffectingTimeScale(clipGUI.clip);
|
||||
EditMode.TrimEnd(clipItem, time, affectTimeScale);
|
||||
}
|
||||
else if (manipulateEdges == ManipulateEdges.Left)
|
||||
{
|
||||
bool affectTimeScale = IsAffectingTimeScale(clipGUI.clip);
|
||||
EditMode.TrimStart(clipItem, time, affectTimeScale);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsAffectingTimeScale(TimelineClip clip)
|
||||
{
|
||||
bool autoScale = (clip.clipCaps & ClipCaps.AutoScale) == ClipCaps.AutoScale;
|
||||
|
||||
// TODO Do not use Event.current from here.
|
||||
bool affectTimeScale = (autoScale && (Event.current.modifiers != EventModifiers.Shift))
|
||||
|| (!autoScale && (Event.current.modifiers == EventModifiers.Shift));
|
||||
return affectTimeScale;
|
||||
}
|
||||
}
|
||||
|
||||
bool m_IsCaptured;
|
||||
TimelineClipHandle m_TrimClipHandler;
|
||||
|
||||
double m_OriginalDuration;
|
||||
double m_OriginalTimeScale;
|
||||
double m_OriginalEaseInDuration;
|
||||
double m_OriginalEaseOutDuration;
|
||||
|
||||
bool m_UndoSaved;
|
||||
SnapEngine m_SnapEngine;
|
||||
|
||||
readonly List<string> m_OverlayStrings = new List<string>();
|
||||
|
||||
static readonly double kEpsilon = 0.0000001;
|
||||
|
||||
protected override bool MouseDown(Event evt, WindowState state)
|
||||
{
|
||||
var handle = PickerUtils.TopmostPickedItem() as TimelineClipHandle;
|
||||
if (handle == null)
|
||||
return false;
|
||||
|
||||
if (handle.clipGUI.clip.GetParentTrack() != null && handle.clipGUI.clip.GetParentTrack().lockedInHierarchy)
|
||||
return false;
|
||||
|
||||
m_TrimClipHandler = handle;
|
||||
|
||||
m_IsCaptured = true;
|
||||
state.AddCaptured(this);
|
||||
|
||||
m_UndoSaved = false;
|
||||
|
||||
var clip = m_TrimClipHandler.clipGUI.clip;
|
||||
|
||||
m_OriginalDuration = clip.duration;
|
||||
m_OriginalTimeScale = clip.timeScale;
|
||||
m_OriginalEaseInDuration = clip.easeInDuration;
|
||||
m_OriginalEaseOutDuration = clip.easeOutDuration;
|
||||
|
||||
RefreshOverlayStrings(m_TrimClipHandler, state);
|
||||
|
||||
// in ripple trim, the right edge moves and needs to snap
|
||||
var edges = ManipulateEdges.Right;
|
||||
if (EditMode.editType != EditMode.EditType.Ripple && m_TrimClipHandler.trimDirection == TrimEdge.Start)
|
||||
edges = ManipulateEdges.Left;
|
||||
m_SnapEngine = new SnapEngine(m_TrimClipHandler.clipGUI, new TrimClipAttractionHandler(), edges, state,
|
||||
evt.mousePosition);
|
||||
|
||||
EditMode.BeginTrim(ItemsUtils.ToItem(clip), m_TrimClipHandler.trimDirection);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool MouseUp(Event evt, WindowState state)
|
||||
{
|
||||
if (!m_IsCaptured)
|
||||
return false;
|
||||
|
||||
m_IsCaptured = false;
|
||||
m_TrimClipHandler = null;
|
||||
m_UndoSaved = false;
|
||||
m_SnapEngine = null;
|
||||
EditMode.FinishTrim();
|
||||
|
||||
state.captured.Clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool MouseDrag(Event evt, WindowState state)
|
||||
{
|
||||
if (state.editSequence.isReadOnly)
|
||||
return false;
|
||||
|
||||
if (!m_IsCaptured)
|
||||
return false;
|
||||
|
||||
var uiClip = m_TrimClipHandler.clipGUI;
|
||||
if (!m_UndoSaved)
|
||||
{
|
||||
UndoExtensions.RegisterClip(uiClip.clip, L10n.Tr("Trim Clip"));
|
||||
if (TimelineUtility.IsRecordableAnimationClip(uiClip.clip))
|
||||
{
|
||||
TimelineUndo.PushUndo(uiClip.clip.animationClip, L10n.Tr("Trim Clip"));
|
||||
}
|
||||
|
||||
m_UndoSaved = true;
|
||||
}
|
||||
|
||||
//Reset to original ease values. The trim operation will calculate the proper blend values.
|
||||
uiClip.clip.easeInDuration = m_OriginalEaseInDuration;
|
||||
uiClip.clip.easeOutDuration = m_OriginalEaseOutDuration;
|
||||
|
||||
if (m_SnapEngine != null)
|
||||
m_SnapEngine.Snap(evt.mousePosition, evt.modifiers);
|
||||
|
||||
RefreshOverlayStrings(m_TrimClipHandler, state);
|
||||
|
||||
if (Selection.activeObject != null)
|
||||
EditorUtility.SetDirty(Selection.activeObject);
|
||||
|
||||
// updates the duration of the graph without rebuilding
|
||||
state.UpdateRootPlayableDuration(state.editSequence.duration);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Overlay(Event evt, WindowState state)
|
||||
{
|
||||
if (!m_IsCaptured)
|
||||
return;
|
||||
|
||||
EditMode.DrawTrimGUI(state, m_TrimClipHandler.clipGUI, m_TrimClipHandler.trimDirection);
|
||||
|
||||
bool trimStart = m_TrimClipHandler.trimDirection == TrimEdge.Start;
|
||||
|
||||
TimeIndicator.Draw(state, trimStart ? m_TrimClipHandler.clipGUI.start : m_TrimClipHandler.clipGUI.end);
|
||||
|
||||
if (m_SnapEngine != null)
|
||||
m_SnapEngine.OnGUI(trimStart, !trimStart);
|
||||
|
||||
if (m_OverlayStrings.Count > 0)
|
||||
{
|
||||
const float padding = 4.0f;
|
||||
var labelStyle = TimelineWindow.styles.tinyFont;
|
||||
var longestLine = labelStyle.CalcSize(
|
||||
new GUIContent(m_OverlayStrings.Aggregate("", (max, cur) => max.Length > cur.Length ? max : cur)));
|
||||
var stringLength = longestLine.x + padding;
|
||||
var lineHeight = longestLine.y + padding;
|
||||
|
||||
var r = new Rect(evt.mousePosition.x - (stringLength / 2.0f),
|
||||
m_TrimClipHandler.clipGUI.rect.yMax,
|
||||
stringLength, lineHeight);
|
||||
|
||||
foreach (var s in m_OverlayStrings)
|
||||
{
|
||||
GUI.Label(r, s, labelStyle);
|
||||
r.y += lineHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RefreshOverlayStrings(TimelineClipHandle handle, WindowState state)
|
||||
{
|
||||
m_OverlayStrings.Clear();
|
||||
|
||||
var differenceDuration = handle.clipGUI.clip.duration - m_OriginalDuration;
|
||||
m_OverlayStrings.Add($"{kDurationText} {state.timeFormat.ToTimeStringWithDelta(handle.clipGUI.clip.duration, state.referenceSequence.frameRate, differenceDuration)}");
|
||||
|
||||
var differenceSpeed = m_OriginalTimeScale - handle.clipGUI.clip.timeScale;
|
||||
if (Math.Abs(differenceSpeed) > kEpsilon)
|
||||
{
|
||||
var sign = differenceSpeed > 0 ? "+" : "";
|
||||
var timeScale = handle.clipGUI.clip.timeScale.ToString("f2");
|
||||
var deltaSpeed = differenceSpeed.ToString("p2");
|
||||
m_OverlayStrings.Add($"{kSpeedText} {timeScale} ({sign}{deltaSpeed}) ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 511aa760b8728a940a41c29837945292
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,72 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class TimeAreaAutoPanner
|
||||
{
|
||||
readonly WindowState m_State;
|
||||
readonly TimelineWindow m_Window;
|
||||
readonly Rect m_ViewRect;
|
||||
|
||||
const float k_PixelDistanceToMaxSpeed = 100.0f;
|
||||
const float k_MaxPanSpeed = 30.0f;
|
||||
|
||||
public TimeAreaAutoPanner(WindowState state)
|
||||
{
|
||||
m_State = state;
|
||||
m_Window = m_State.GetWindow();
|
||||
|
||||
var shownRange = m_State.timeAreaShownRange;
|
||||
var trackViewBounds = m_Window.sequenceRect;
|
||||
m_ViewRect = Rect.MinMaxRect(m_State.TimeToPixel(shownRange.x), trackViewBounds.yMin,
|
||||
m_State.TimeToPixel(shownRange.y), trackViewBounds.yMax);
|
||||
}
|
||||
|
||||
public void OnGUI(Event evt)
|
||||
{
|
||||
if (evt.type != EventType.Layout)
|
||||
return;
|
||||
|
||||
var hFactor = 0.0f;
|
||||
var vFactor = 0.0f;
|
||||
|
||||
bool horizontalPan = GetPanFactor(evt.mousePosition.x, m_ViewRect.xMin, m_ViewRect.xMax, out hFactor);
|
||||
bool verticalPan = GetPanFactor(evt.mousePosition.y, m_ViewRect.yMin, m_ViewRect.yMax, out vFactor);
|
||||
|
||||
if (horizontalPan)
|
||||
{
|
||||
var translation = m_State.timeAreaTranslation;
|
||||
translation.x += hFactor * k_MaxPanSpeed;
|
||||
|
||||
m_State.SetTimeAreaTransform(translation, m_State.timeAreaScale);
|
||||
}
|
||||
|
||||
if (verticalPan)
|
||||
{
|
||||
var translation = m_Window.treeView.scrollPosition;
|
||||
translation.y -= vFactor * k_MaxPanSpeed;
|
||||
|
||||
m_Window.treeView.scrollPosition = translation;
|
||||
}
|
||||
}
|
||||
|
||||
static bool GetPanFactor(float v, float min, float max, out float factor)
|
||||
{
|
||||
factor = 0.0f;
|
||||
|
||||
if (v < min)
|
||||
{
|
||||
factor = Mathf.Clamp01((min - v) / k_PixelDistanceToMaxSpeed);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (v > max)
|
||||
{
|
||||
factor = -Mathf.Clamp01((v - max) / k_PixelDistanceToMaxSpeed);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 02e1fe0a338b35545a5fed1345848332
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,47 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
static class TimeIndicator
|
||||
{
|
||||
static readonly Tooltip s_Tooltip = new Tooltip(DirectorStyles.Instance.displayBackground, DirectorStyles.Instance.tinyFont);
|
||||
|
||||
public static void Draw(WindowState state, double time)
|
||||
{
|
||||
var bounds = state.timeAreaRect;
|
||||
bounds.xMin = Mathf.Max(bounds.xMin, state.TimeToTimeAreaPixel(time));
|
||||
|
||||
using (new GUIViewportScope(state.timeAreaRect))
|
||||
{
|
||||
s_Tooltip.text = TimeReferenceUtility.ToTimeString(time);
|
||||
|
||||
var tooltipBounds = s_Tooltip.bounds;
|
||||
tooltipBounds.xMin = bounds.xMin - (tooltipBounds.width / 2.0f);
|
||||
tooltipBounds.y = bounds.y;
|
||||
s_Tooltip.bounds = tooltipBounds;
|
||||
|
||||
if (time >= 0)
|
||||
s_Tooltip.Draw();
|
||||
}
|
||||
|
||||
if (time >= 0)
|
||||
{
|
||||
Graphics.DrawLineAtTime(state, time, Color.black, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Draw(WindowState state, double start, double end)
|
||||
{
|
||||
var bounds = state.timeAreaRect;
|
||||
bounds.xMin = Mathf.Max(bounds.xMin, state.TimeToTimeAreaPixel(start));
|
||||
bounds.xMax = Mathf.Min(bounds.xMax, state.TimeToTimeAreaPixel(end));
|
||||
|
||||
var color = DirectorStyles.Instance.selectedStyle.focused.textColor;
|
||||
color.a = 0.12f;
|
||||
EditorGUI.DrawRect(bounds, color);
|
||||
|
||||
Draw(state, start);
|
||||
Draw(state, end);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1d446e57147ca9b4183edfbbfa9bf206
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,54 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class TimelineClipGroup
|
||||
{
|
||||
readonly TimelineClip[] m_Clips;
|
||||
readonly TimelineClip m_LeftMostClip;
|
||||
readonly TimelineClip m_RightMostClip;
|
||||
|
||||
public TimelineClip[] clips
|
||||
{
|
||||
get { return m_Clips; }
|
||||
}
|
||||
|
||||
public double start
|
||||
{
|
||||
get { return m_LeftMostClip.start; }
|
||||
set
|
||||
{
|
||||
var offset = value - m_LeftMostClip.start;
|
||||
|
||||
foreach (var clip in m_Clips)
|
||||
clip.start += offset;
|
||||
}
|
||||
}
|
||||
|
||||
public double end
|
||||
{
|
||||
get { return m_RightMostClip.end; }
|
||||
}
|
||||
|
||||
public TimelineClipGroup(IEnumerable<TimelineClip> clips)
|
||||
{
|
||||
Debug.Assert(clips != null && clips.Any());
|
||||
|
||||
m_Clips = clips.ToArray();
|
||||
m_LeftMostClip = null;
|
||||
m_RightMostClip = null;
|
||||
|
||||
foreach (var clip in m_Clips)
|
||||
{
|
||||
if (m_LeftMostClip == null || clip.start < m_LeftMostClip.start)
|
||||
m_LeftMostClip = clip;
|
||||
|
||||
if (m_RightMostClip == null || clip.end > m_RightMostClip.end)
|
||||
m_RightMostClip = clip;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b8c3bda3a988b5f4c910a5c3f722d0be
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f6d64d8648793944dbadfd71f0f4b0a1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,23 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
enum TrimEdge
|
||||
{
|
||||
Start,
|
||||
End
|
||||
}
|
||||
|
||||
interface ITrimItemMode
|
||||
{
|
||||
void OnBeforeTrim(ITrimmable item, TrimEdge trimDirection);
|
||||
|
||||
void TrimStart(ITrimmable item, double time, bool affectTimeScale);
|
||||
void TrimEnd(ITrimmable item, double time, bool affectTimeScale);
|
||||
}
|
||||
|
||||
interface ITrimItemDrawer
|
||||
{
|
||||
void DrawGUI(WindowState state, Rect bounds, Color color, TrimEdge edge);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b4a5ce78107bc38409a3bb5e8b3289ac
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,102 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class TrimItemModeMix : ITrimItemMode, ITrimItemDrawer
|
||||
{
|
||||
ITrimmable m_Item;
|
||||
|
||||
double m_Min;
|
||||
double m_Max;
|
||||
|
||||
public void OnBeforeTrim(ITrimmable item, TrimEdge trimDirection)
|
||||
{
|
||||
m_Item = item;
|
||||
|
||||
var sortedItems = ItemsUtils.GetItemsExcept(item.parentTrack, new[] {item})
|
||||
.OfType<ITrimmable>()
|
||||
.OrderBy(c => c.start);
|
||||
|
||||
var itemStart = (DiscreteTime)item.start;
|
||||
var itemEnd = (DiscreteTime)item.end;
|
||||
|
||||
var overlapped = sortedItems.LastOrDefault(c => (DiscreteTime)c.start == itemStart && (DiscreteTime)c.end == itemEnd);
|
||||
|
||||
ITrimmable nextItem;
|
||||
ITrimmable prevItem;
|
||||
|
||||
m_Min = 0.0;
|
||||
m_Max = double.PositiveInfinity;
|
||||
|
||||
var blendableItem = item as IBlendable;
|
||||
if (blendableItem != null && blendableItem.supportsBlending)
|
||||
{
|
||||
if (trimDirection == TrimEdge.Start)
|
||||
{
|
||||
nextItem = sortedItems.FirstOrDefault(c => (DiscreteTime)c.start >= itemStart && (DiscreteTime)c.end > itemEnd);
|
||||
prevItem = sortedItems.LastOrDefault(c => (DiscreteTime)c.start <= itemStart && (DiscreteTime)c.end < itemEnd);
|
||||
|
||||
if (prevItem != null)
|
||||
m_Min = prevItem.start + EditModeUtils.BlendDuration(prevItem, TrimEdge.Start);
|
||||
|
||||
if (nextItem != null)
|
||||
m_Max = nextItem.start;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextItem = sortedItems.FirstOrDefault(c => c != overlapped && (DiscreteTime)c.start >= itemStart && (DiscreteTime)c.end >= itemEnd);
|
||||
prevItem = sortedItems.LastOrDefault(c => c != overlapped && (DiscreteTime)c.start <= itemStart && (DiscreteTime)c.end <= itemEnd);
|
||||
|
||||
if (prevItem != null)
|
||||
m_Min = prevItem.end;
|
||||
|
||||
if (nextItem != null)
|
||||
m_Max = nextItem.end - EditModeUtils.BlendDuration(nextItem, TrimEdge.End);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nextItem = sortedItems.FirstOrDefault(c => (DiscreteTime)c.start > itemStart);
|
||||
prevItem = sortedItems.LastOrDefault(c => (DiscreteTime)c.start < itemStart);
|
||||
|
||||
if (prevItem != null)
|
||||
m_Min = prevItem.end;
|
||||
|
||||
if (nextItem != null)
|
||||
m_Max = nextItem.start;
|
||||
}
|
||||
}
|
||||
|
||||
public void TrimStart(ITrimmable item, double time, bool affectTimeScale)
|
||||
{
|
||||
time = Math.Min(Math.Max(time, m_Min), m_Max);
|
||||
item.SetStart(time, affectTimeScale);
|
||||
}
|
||||
|
||||
public void TrimEnd(ITrimmable item, double time, bool affectTimeScale)
|
||||
{
|
||||
time = Math.Min(Math.Max(time, m_Min), m_Max);
|
||||
item.SetEnd(time, affectTimeScale);
|
||||
}
|
||||
|
||||
public void DrawGUI(WindowState state, Rect bounds, Color color, TrimEdge edge)
|
||||
{
|
||||
if (EditModeUtils.HasBlends(m_Item, edge))
|
||||
{
|
||||
EditModeGUIUtils.DrawBoundsEdge(bounds, color, edge);
|
||||
var cursorType = (edge == TrimEdge.End)
|
||||
? TimelineCursors.CursorType.MixRight
|
||||
: TimelineCursors.CursorType.MixLeft;
|
||||
|
||||
TimelineCursors.SetCursor(cursorType);
|
||||
}
|
||||
else
|
||||
{
|
||||
TimelineCursors.ClearCursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5ddd9f7d3cce6724696a33752ab2f5a4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,139 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class TrimItemModeReplace : ITrimItemMode, ITrimItemDrawer
|
||||
{
|
||||
ITrimmable m_Item;
|
||||
|
||||
ITrimmable m_ItemToBeReplaced;
|
||||
double m_ClipOriginalEdgeValue;
|
||||
bool m_TrimReplace;
|
||||
|
||||
double m_Min;
|
||||
double m_Max;
|
||||
|
||||
public void OnBeforeTrim(ITrimmable item, TrimEdge trimDirection)
|
||||
{
|
||||
m_Item = item;
|
||||
|
||||
var sortedClips = ItemsUtils.GetItemsExcept(item.parentTrack, new[] { item })
|
||||
.OfType<ITrimmable>()
|
||||
.OrderBy(c => c.start);
|
||||
|
||||
var clipStart = (DiscreteTime)item.start;
|
||||
var clipEnd = (DiscreteTime)item.end;
|
||||
|
||||
var overlapped = sortedClips.LastOrDefault(c => (DiscreteTime)c.start == clipStart && (DiscreteTime)c.end == clipEnd);
|
||||
|
||||
ITrimmable nextItem;
|
||||
ITrimmable prevItem;
|
||||
|
||||
m_Min = 0.0;
|
||||
m_Max = double.PositiveInfinity;
|
||||
|
||||
if (trimDirection == TrimEdge.Start)
|
||||
{
|
||||
nextItem = sortedClips.FirstOrDefault(c => (DiscreteTime)c.start >= clipStart && (DiscreteTime)c.end > clipEnd);
|
||||
prevItem = sortedClips.LastOrDefault(c => (DiscreteTime)c.start <= clipStart && (DiscreteTime)c.end < clipEnd);
|
||||
|
||||
if (prevItem != null)
|
||||
m_Min = prevItem.start + EditModeUtils.BlendDuration(prevItem, TrimEdge.Start) + TimelineClip.kMinDuration;
|
||||
|
||||
if (nextItem != null)
|
||||
m_Max = nextItem.start;
|
||||
|
||||
m_ItemToBeReplaced = prevItem;
|
||||
|
||||
if (m_ItemToBeReplaced != null)
|
||||
m_ClipOriginalEdgeValue = m_ItemToBeReplaced.end;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextItem = sortedClips.FirstOrDefault(c => c != overlapped && (DiscreteTime)c.start >= clipStart && (DiscreteTime)c.end >= clipEnd);
|
||||
prevItem = sortedClips.LastOrDefault(c => c != overlapped && (DiscreteTime)c.start <= clipStart && (DiscreteTime)c.end <= clipEnd);
|
||||
|
||||
if (prevItem != null)
|
||||
m_Min = prevItem.end;
|
||||
|
||||
if (nextItem != null)
|
||||
m_Max = nextItem.end - EditModeUtils.BlendDuration(nextItem, TrimEdge.End) - TimelineClip.kMinDuration;
|
||||
|
||||
m_ItemToBeReplaced = nextItem;
|
||||
|
||||
if (m_ItemToBeReplaced != null)
|
||||
m_ClipOriginalEdgeValue = m_ItemToBeReplaced.start;
|
||||
}
|
||||
|
||||
m_TrimReplace = false;
|
||||
}
|
||||
|
||||
public void TrimStart(ITrimmable item, double time, bool affectTimeScale)
|
||||
{
|
||||
time = Math.Min(Math.Max(time, m_Min), m_Max);
|
||||
|
||||
if (m_ItemToBeReplaced != null)
|
||||
{
|
||||
if (!m_TrimReplace)
|
||||
m_TrimReplace = item.start >= m_ItemToBeReplaced.end;
|
||||
}
|
||||
|
||||
time = Math.Max(time, 0.0);
|
||||
|
||||
item.SetStart(time, affectTimeScale);
|
||||
|
||||
if (m_ItemToBeReplaced != null && m_TrimReplace)
|
||||
{
|
||||
var prevEnd = Math.Min(item.start, m_ClipOriginalEdgeValue);
|
||||
m_ItemToBeReplaced.SetEnd(prevEnd, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void TrimEnd(ITrimmable item, double time, bool affectTimeScale)
|
||||
{
|
||||
time = Math.Min(Math.Max(time, m_Min), m_Max);
|
||||
|
||||
if (m_ItemToBeReplaced != null)
|
||||
{
|
||||
if (!m_TrimReplace)
|
||||
m_TrimReplace = item.end <= m_ItemToBeReplaced.start;
|
||||
}
|
||||
|
||||
item.SetEnd(time, affectTimeScale);
|
||||
|
||||
if (m_ItemToBeReplaced != null && m_TrimReplace)
|
||||
{
|
||||
var nextStart = Math.Max(item.end, m_ClipOriginalEdgeValue);
|
||||
m_ItemToBeReplaced.SetStart(nextStart, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawGUI(WindowState state, Rect bounds, Color color, TrimEdge edge)
|
||||
{
|
||||
bool shouldDraw = m_ItemToBeReplaced != null && (edge == TrimEdge.End && m_Item.end > m_ClipOriginalEdgeValue) ||
|
||||
(edge == TrimEdge.Start && m_Item.start < m_ClipOriginalEdgeValue);
|
||||
|
||||
if (shouldDraw)
|
||||
{
|
||||
var cursorType = TimelineCursors.CursorType.Replace;
|
||||
if (EditModeUtils.HasBlends(m_Item, edge))
|
||||
{
|
||||
color = DirectorStyles.kMixToolColor;
|
||||
cursorType = (edge == TrimEdge.End)
|
||||
? TimelineCursors.CursorType.MixRight
|
||||
: TimelineCursors.CursorType.MixLeft;
|
||||
}
|
||||
|
||||
EditModeGUIUtils.DrawBoundsEdge(bounds, color, edge);
|
||||
TimelineCursors.SetCursor(cursorType);
|
||||
}
|
||||
else
|
||||
{
|
||||
TimelineCursors.ClearCursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 769f6f5dd7c8f2d4c9ab1caba0bd2628
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,97 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class TrimItemModeRipple : ITrimItemMode, ITrimItemDrawer
|
||||
{
|
||||
double m_OriginalClipStart;
|
||||
double m_OriginalClipEnd;
|
||||
|
||||
ITrimmable[] m_NextItems;
|
||||
|
||||
double m_BlendDuration;
|
||||
|
||||
double m_TrimStartShift;
|
||||
|
||||
public void OnBeforeTrim(ITrimmable item, TrimEdge trimDirection)
|
||||
{
|
||||
m_OriginalClipStart = item.start;
|
||||
m_OriginalClipEnd = item.end;
|
||||
m_TrimStartShift = 0.0;
|
||||
|
||||
var sortedClips = ItemsUtils.GetItemsExcept(item.parentTrack, new[] { item })
|
||||
.OfType<ITrimmable>()
|
||||
.OrderBy(c => c.start);
|
||||
|
||||
var clipStart = (DiscreteTime)item.start;
|
||||
var clipEnd = (DiscreteTime)item.end;
|
||||
|
||||
m_NextItems = sortedClips.Where(c => (DiscreteTime)c.start >= clipStart && (DiscreteTime)c.end >= clipEnd).ToArray();
|
||||
|
||||
var overlapped = sortedClips.LastOrDefault(c => (DiscreteTime)c.start == clipStart && (DiscreteTime)c.end == clipEnd);
|
||||
|
||||
if (overlapped != null)
|
||||
{
|
||||
m_BlendDuration = overlapped.end - overlapped.start;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_BlendDuration = 0.0;
|
||||
|
||||
var prevClip = sortedClips.LastOrDefault(c => (DiscreteTime)c.start <= clipStart && (DiscreteTime)c.end <= clipEnd);
|
||||
if (prevClip != null)
|
||||
m_BlendDuration += Math.Max(prevClip.end - item.start, 0.0);
|
||||
|
||||
var nextClip = sortedClips.FirstOrDefault(c => (DiscreteTime)c.start >= clipStart && (DiscreteTime)c.end >= clipEnd);
|
||||
if (nextClip != null)
|
||||
m_BlendDuration += Math.Max(item.end - nextClip.start, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
public void TrimStart(ITrimmable item, double time, bool affectTimeScale)
|
||||
{
|
||||
var prevEnd = item.end;
|
||||
|
||||
// HACK If time is negative, make sure we shift the SetStart operation to a positive space.
|
||||
if (time < 0.0)
|
||||
m_TrimStartShift = Math.Max(-time, m_TrimStartShift);
|
||||
|
||||
item.start = m_OriginalClipEnd - item.duration + m_TrimStartShift;
|
||||
time += m_TrimStartShift;
|
||||
|
||||
if (m_BlendDuration > 0.0)
|
||||
time = Math.Min(time, item.end - m_BlendDuration);
|
||||
|
||||
item.SetStart(time, affectTimeScale);
|
||||
|
||||
item.start = m_OriginalClipStart;
|
||||
|
||||
var offset = item.end - prevEnd;
|
||||
foreach (var timelineClip in m_NextItems)
|
||||
timelineClip.start += offset;
|
||||
}
|
||||
|
||||
public void TrimEnd(ITrimmable item, double time, bool affectTimeScale)
|
||||
{
|
||||
var prevEnd = item.end;
|
||||
|
||||
if (m_BlendDuration > 0.0)
|
||||
time = Math.Max(time, item.start + m_BlendDuration);
|
||||
|
||||
item.SetEnd(time, affectTimeScale);
|
||||
|
||||
var offset = item.end - prevEnd;
|
||||
foreach (var timelineClip in m_NextItems)
|
||||
timelineClip.start += offset;
|
||||
}
|
||||
|
||||
public void DrawGUI(WindowState state, Rect bounds, Color color, TrimEdge edge)
|
||||
{
|
||||
EditModeGUIUtils.DrawBoundsEdge(bounds, color, edge);
|
||||
TimelineCursors.SetCursor(TimelineCursors.CursorType.Ripple);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 20c8bb6b47a526c4c96ca73314fe2856
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 67e9c6cf60c57a54f9f4db1bc33fd2e3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,27 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
static class EditModeGUIUtils
|
||||
{
|
||||
public static void DrawBoundsEdge(Rect bounds, Color color, TrimEdge edge, float width = 4.0f)
|
||||
{
|
||||
var r = bounds;
|
||||
r.yMin += 2.0f;
|
||||
r.yMax -= 2.0f;
|
||||
r.width = width;
|
||||
|
||||
r.x = edge == TrimEdge.End ? bounds.xMax : bounds.xMin - width;
|
||||
|
||||
EditorGUI.DrawRect(r, color);
|
||||
}
|
||||
|
||||
public static void DrawOverlayRect(Rect bounds, Color overlayColor)
|
||||
{
|
||||
var c = overlayColor;
|
||||
c.a = 0.2f;
|
||||
EditorGUI.DrawRect(bounds, c);
|
||||
EditorGUI.DrawOutline(bounds, 1.0f, new Color(1.0f, 1.0f, 1.0f, 0.5f));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8938e753b3f47374889d5cf3265b563c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,138 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
static class EditModeMixUtils
|
||||
{
|
||||
static readonly List<PlacementValidity> k_UnrecoverablePlacements = new List<PlacementValidity>
|
||||
{
|
||||
PlacementValidity.InvalidIsWithin,
|
||||
PlacementValidity.InvalidStartsInBlend,
|
||||
PlacementValidity.InvalidContainsBlend,
|
||||
PlacementValidity.InvalidOverlapWithNonBlendableClip
|
||||
};
|
||||
|
||||
public static bool CanInsert(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
foreach (var itemsGroup in itemsGroups)
|
||||
{
|
||||
List<ITimelineItem> siblings = ItemsUtils.GetItemsExcept(itemsGroup.targetTrack, itemsGroup.items).ToList();
|
||||
foreach (var item in itemsGroup.items)
|
||||
{
|
||||
var placementValidity = GetPlacementValidity(item, siblings);
|
||||
|
||||
if (k_UnrecoverablePlacements.Contains(placementValidity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//Corrects clips durations to fit at insertion point, if needed
|
||||
public static void PrepareItemsForInsertion(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
foreach (var itemsGroup in itemsGroups)
|
||||
{
|
||||
var siblings = ItemsUtils.GetItemsExcept(itemsGroup.targetTrack, itemsGroup.items);
|
||||
foreach (var item in itemsGroup.items.OfType<ITrimmable>())
|
||||
{
|
||||
var eatenItems = siblings.Where(c => EditModeUtils.IsItemWithinRange(c, item.start, item.end)).ToList();
|
||||
|
||||
var intersectedItem = EditModeUtils.GetFirstIntersectedItem(siblings, item.end);
|
||||
if (intersectedItem != null)
|
||||
eatenItems.Add(intersectedItem);
|
||||
|
||||
var blendableItems = eatenItems.OfType<IBlendable>();
|
||||
if (blendableItems.Any())
|
||||
{
|
||||
var minTime = blendableItems.Min(c => c.end - c.rightBlendDuration);
|
||||
|
||||
if (item.end > minTime)
|
||||
item.SetEnd(minTime, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static PlacementValidity GetPlacementValidity(ITimelineItem item, IEnumerable<ITimelineItem> otherItems)
|
||||
{
|
||||
if (item.duration <= 0.0)
|
||||
return PlacementValidity.Valid; //items without any duration can always be placed
|
||||
|
||||
var sortedItems = otherItems.Where(i => i.duration > 0.0).OrderBy(c => c.start);
|
||||
var candidates = new List<ITimelineItem>();
|
||||
foreach (var sortedItem in sortedItems)
|
||||
{
|
||||
if ((DiscreteTime)sortedItem.start >= (DiscreteTime)item.end)
|
||||
{
|
||||
// No need to process further
|
||||
break;
|
||||
}
|
||||
|
||||
if ((DiscreteTime)sortedItem.end <= (DiscreteTime)item.start)
|
||||
{
|
||||
// Skip
|
||||
continue;
|
||||
}
|
||||
|
||||
candidates.Add(sortedItem);
|
||||
}
|
||||
|
||||
var discreteStart = (DiscreteTime)item.start;
|
||||
var discreteEnd = (DiscreteTime)item.end;
|
||||
|
||||
// Note: Order of tests matters
|
||||
for (int i = 0, n = candidates.Count; i < n; i++)
|
||||
{
|
||||
var candidate = candidates[i];
|
||||
|
||||
var blendItem = item as IBlendable;
|
||||
if (blendItem != null && blendItem.supportsBlending)
|
||||
{
|
||||
if (EditModeUtils.Contains(candidate.start, candidate.end, item))
|
||||
return PlacementValidity.InvalidIsWithin;
|
||||
|
||||
if (i < n - 1)
|
||||
{
|
||||
var nextCandidate = candidates[i + 1];
|
||||
|
||||
var discreteNextCandidateStart = (DiscreteTime)nextCandidate.start;
|
||||
var discreteCandidateEnd = (DiscreteTime)candidate.end;
|
||||
|
||||
if (discreteCandidateEnd > discreteNextCandidateStart)
|
||||
{
|
||||
if (discreteStart >= discreteNextCandidateStart)
|
||||
{
|
||||
// Note: In case the placement is fully within a blend,
|
||||
// InvalidStartsInBlend MUST have priority
|
||||
return PlacementValidity.InvalidStartsInBlend;
|
||||
}
|
||||
|
||||
if (discreteEnd > discreteNextCandidateStart && discreteEnd <= discreteCandidateEnd)
|
||||
return PlacementValidity.InvalidEndsInBlend;
|
||||
|
||||
if (discreteStart < discreteNextCandidateStart && discreteEnd > discreteCandidateEnd)
|
||||
return PlacementValidity.InvalidContainsBlend;
|
||||
}
|
||||
}
|
||||
|
||||
if (EditModeUtils.Contains(item.start, item.end, candidate))
|
||||
return PlacementValidity.InvalidContains;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (EditModeUtils.Overlaps(item, candidate.start, candidate.end)
|
||||
|| EditModeUtils.Overlaps(candidate, item.start, item.end))
|
||||
return PlacementValidity.InvalidOverlapWithNonBlendableClip;
|
||||
}
|
||||
}
|
||||
|
||||
return PlacementValidity.Valid;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 505965fb9ab352b4d88882d7c8d822bf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,57 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
static class EditModeReplaceUtils
|
||||
{
|
||||
public static void Insert(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
foreach (var itemsGroup in itemsGroups)
|
||||
{
|
||||
Insert(itemsGroup.targetTrack, itemsGroup.items);
|
||||
}
|
||||
}
|
||||
|
||||
static void Insert(TrackAsset track, IEnumerable<ITimelineItem> items)
|
||||
{
|
||||
if (track == null) return;
|
||||
var orderedItems = ItemsUtils.GetItemsExcept(track, items)
|
||||
.OfType<ITrimmable>()
|
||||
.OrderBy(i => i.start).ToArray();
|
||||
|
||||
foreach (var item in items.OfType<ITrimmable>())
|
||||
{
|
||||
var from = item.start;
|
||||
var to = item.end;
|
||||
|
||||
var overlappedItems = orderedItems.Where(i => EditModeUtils.Overlaps(i, from, to));
|
||||
|
||||
foreach (var overlappedItem in overlappedItems)
|
||||
{
|
||||
if (EditModeUtils.IsItemWithinRange(overlappedItem, from, to))
|
||||
{
|
||||
overlappedItem.Delete();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (overlappedItem.start >= from)
|
||||
overlappedItem.TrimStart(to);
|
||||
else
|
||||
overlappedItem.TrimEnd(from);
|
||||
}
|
||||
}
|
||||
|
||||
var includingItems = orderedItems.Where(c => c.start<from && c.end> to);
|
||||
foreach (var includingItem in includingItems)
|
||||
{
|
||||
var newItem = includingItem.CloneTo(track, includingItem.start) as ITrimmable;
|
||||
includingItem.TrimStart(to);
|
||||
if (newItem != null)
|
||||
newItem.SetEnd(from, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6ca745fb561cbf640b6e603f95662fa0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,108 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
static class EditModeRippleUtils
|
||||
{
|
||||
public static void Insert(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
var start = double.MaxValue;
|
||||
var end = double.MinValue;
|
||||
|
||||
foreach (var itemsGroup in itemsGroups)
|
||||
{
|
||||
start = Math.Min(start, itemsGroup.items.Min(c => c.start));
|
||||
end = Math.Max(end, itemsGroup.items.Max(c => c.end));
|
||||
}
|
||||
|
||||
var offset = 0.0;
|
||||
var discreteStart = (DiscreteTime)start;
|
||||
var discreteEnd = (DiscreteTime)end;
|
||||
var itemTypes = ItemsUtils.GetItemTypes(itemsGroups);
|
||||
var siblingsToRipple = new List<ITimelineItem>();
|
||||
|
||||
foreach (var itemsGroup in itemsGroups)
|
||||
{
|
||||
//can only ripple items of the same type as those selected
|
||||
siblingsToRipple.AddRange(ItemsUtils.GetItemsExcept(itemsGroup.targetTrack, itemsGroup.items).Where(i => itemTypes.Contains(i.GetType())));
|
||||
foreach (var item in siblingsToRipple)
|
||||
{
|
||||
var discreteItemStart = (DiscreteTime)item.start;
|
||||
var discreteItemEnd = (DiscreteTime)item.end;
|
||||
|
||||
if ((discreteItemStart < discreteStart && discreteItemEnd > discreteStart) || (discreteItemStart >= discreteStart && discreteItemStart < discreteEnd))
|
||||
offset = Math.Max(offset, end - item.start);
|
||||
}
|
||||
}
|
||||
|
||||
if (offset > 0.0)
|
||||
{
|
||||
foreach (var sibling in siblingsToRipple)
|
||||
{
|
||||
if ((DiscreteTime)sibling.end > (DiscreteTime)start)
|
||||
sibling.start += offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Remove(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
foreach (var itemsGroup in itemsGroups)
|
||||
Remove(itemsGroup.targetTrack, itemsGroup.items);
|
||||
}
|
||||
|
||||
static void Remove(TrackAsset track, IEnumerable<ITimelineItem> items)
|
||||
{
|
||||
if (track == null) return;
|
||||
|
||||
//can only ripple items of the same type as those selected
|
||||
var itemTypes = ItemsUtils.GetItemTypes(items);
|
||||
var siblingsToRipple = ItemsUtils.GetItemsExcept(track, items)
|
||||
.Where(i => itemTypes.Contains(i.GetType()))
|
||||
.OrderBy(c => c.start)
|
||||
.ToArray();
|
||||
|
||||
var orderedItems = items
|
||||
.OrderBy(c => c.start)
|
||||
.ToArray();
|
||||
|
||||
var cumulativeOffset = 0.0;
|
||||
|
||||
foreach (var item in orderedItems)
|
||||
{
|
||||
var offset = item.end - item.start;
|
||||
var start = item.start - cumulativeOffset;
|
||||
var end = item.end - cumulativeOffset;
|
||||
|
||||
var nextItem = siblingsToRipple.FirstOrDefault(c => (DiscreteTime)c.start > (DiscreteTime)start && (DiscreteTime)c.start < (DiscreteTime)end);
|
||||
if (nextItem != null)
|
||||
{
|
||||
offset -= end - nextItem.start;
|
||||
}
|
||||
|
||||
var prevItem = siblingsToRipple.FirstOrDefault(c => (DiscreteTime)c.end > (DiscreteTime)start && (DiscreteTime)c.end < (DiscreteTime)end);
|
||||
if (prevItem != null)
|
||||
{
|
||||
offset -= prevItem.end - start;
|
||||
}
|
||||
|
||||
if (offset <= 0.0)
|
||||
continue;
|
||||
|
||||
cumulativeOffset += offset;
|
||||
|
||||
for (int i = siblingsToRipple.Length - 1; i >= 0; --i)
|
||||
{
|
||||
var c = siblingsToRipple[i];
|
||||
if ((DiscreteTime)c.start < (DiscreteTime)start)
|
||||
break;
|
||||
|
||||
c.start = c.start - offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 495e2738ac7d88a41a158cd2e237d70b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,123 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
static class EditModeUtils
|
||||
{
|
||||
public static void Delete(IEnumerable<ITimelineItem> items)
|
||||
{
|
||||
if (items == null)
|
||||
return;
|
||||
|
||||
foreach (var item in items)
|
||||
item.Delete();
|
||||
}
|
||||
|
||||
public static void SetStart(IEnumerable<ITimelineItem> items, double time)
|
||||
{
|
||||
var offset = time - items.Min(c => c.start);
|
||||
|
||||
foreach (var item in items)
|
||||
item.start += offset;
|
||||
}
|
||||
|
||||
public static void SetParentTrack(IEnumerable<ITimelineItem> items, TrackAsset parentTrack)
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item.parentTrack == parentTrack)
|
||||
continue;
|
||||
|
||||
item.parentTrack = parentTrack;
|
||||
|
||||
var clipGUI = item.gui as TimelineClipGUI;
|
||||
if (clipGUI != null)
|
||||
{
|
||||
clipGUI.clipCurveEditor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ITimelineItem GetFirstIntersectedItem(IEnumerable<ITimelineItem> items, double time)
|
||||
{
|
||||
return items.FirstOrDefault(c => Intersects(time, c.start, c.end));
|
||||
}
|
||||
|
||||
static bool Intersects(double time, double start, double end)
|
||||
{
|
||||
var discreteTime = (DiscreteTime)time;
|
||||
return discreteTime > (DiscreteTime)start && discreteTime < (DiscreteTime)end;
|
||||
}
|
||||
|
||||
public static bool Overlaps(ITimelineItem item, double from, double to)
|
||||
{
|
||||
var discreteFrom = (DiscreteTime)from;
|
||||
var discreteTo = (DiscreteTime)to;
|
||||
var discreteStart = (DiscreteTime)item.start;
|
||||
|
||||
if (discreteStart >= discreteFrom && discreteStart < discreteTo)
|
||||
return true;
|
||||
|
||||
var discreteEnd = (DiscreteTime)item.end;
|
||||
|
||||
if (discreteEnd > discreteFrom && discreteEnd <= discreteTo)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsItemWithinRange(ITimelineItem item, double from, double to)
|
||||
{
|
||||
return (DiscreteTime)item.start >= (DiscreteTime)from && (DiscreteTime)item.end <= (DiscreteTime)to;
|
||||
}
|
||||
|
||||
public static bool IsRangeWithinItem(double from, double to, ITimelineItem item)
|
||||
{
|
||||
return (DiscreteTime)from >= (DiscreteTime)item.start && (DiscreteTime)to <= (DiscreteTime)item.end;
|
||||
}
|
||||
|
||||
public static bool Contains(double from, double to, ITimelineItem item)
|
||||
{
|
||||
return (DiscreteTime)from < (DiscreteTime)item.start && (DiscreteTime)to > (DiscreteTime)item.end;
|
||||
}
|
||||
|
||||
public static bool HasBlends(ITimelineItem item, TrimEdge edge)
|
||||
{
|
||||
var blendable = item as IBlendable;
|
||||
if (blendable == null) return false;
|
||||
|
||||
return edge == TrimEdge.Start && blendable.hasLeftBlend || edge == TrimEdge.End && blendable.hasRightBlend;
|
||||
}
|
||||
|
||||
public static double BlendDuration(ITimelineItem item, TrimEdge edge)
|
||||
{
|
||||
var blendable = item as IBlendable;
|
||||
if (blendable == null) return 0.0;
|
||||
|
||||
return edge == TrimEdge.Start ? blendable.leftBlendDuration : blendable.rightBlendDuration;
|
||||
}
|
||||
|
||||
public static bool IsInfiniteTrack(TrackAsset track)
|
||||
{
|
||||
var aTrack = track as AnimationTrack;
|
||||
return aTrack != null && aTrack.CanConvertToClipMode();
|
||||
}
|
||||
|
||||
public static void GetInfiniteClipBoundaries(TrackAsset track, out double start, out double end)
|
||||
{
|
||||
var info = AnimationClipCurveCache.Instance.GetCurveInfo(((AnimationTrack)track).infiniteClip);
|
||||
if (info.keyTimes.Length > 0)
|
||||
{
|
||||
start = info.keyTimes.Min();
|
||||
end = info.keyTimes.Max();
|
||||
}
|
||||
else
|
||||
{
|
||||
start = end = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 70eae1897c9d308448eb3bb0b5be9f58
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,19 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
static class ManipulatorsUtils
|
||||
{
|
||||
public static EventModifiers actionModifier
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Application.platform == RuntimePlatform.OSXEditor ||
|
||||
Application.platform == RuntimePlatform.OSXPlayer)
|
||||
return EventModifiers.Command;
|
||||
|
||||
return EventModifiers.Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e177382a693dea644acd34e3e7a3feb3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,13 @@
|
|||
namespace UnityEditor.Timeline
|
||||
{
|
||||
enum PlacementValidity
|
||||
{
|
||||
Valid,
|
||||
InvalidContains,
|
||||
InvalidIsWithin,
|
||||
InvalidStartsInBlend,
|
||||
InvalidEndsInBlend,
|
||||
InvalidContainsBlend,
|
||||
InvalidOverlapWithNonBlendableClip
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 335020228a0fe124897f51f25f6350ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue