393 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			393 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System.Collections.Generic;
 | |
| using System.Linq;
 | |
| using JetBrains.Annotations;
 | |
| using UnityEditor.Timeline.Actions;
 | |
| using UnityEngine;
 | |
| using UnityEngine.Timeline;
 | |
| using UnityEngine.Playables;
 | |
| 
 | |
| namespace UnityEditor.Timeline
 | |
| {
 | |
|     [MenuEntry("Edit in Animation Window", MenuPriority.ClipEditActionSection.editInAnimationWindow), UsedImplicitly]
 | |
|     class EditClipInAnimationWindow : ClipAction
 | |
|     {
 | |
|         public override ActionValidity Validate(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             if (!GetEditableClip(clips, out _, out _))
 | |
|                 return ActionValidity.NotApplicable;
 | |
|             return ActionValidity.Valid;
 | |
|         }
 | |
| 
 | |
|         public override bool Execute(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             TimelineClip clip;
 | |
|             AnimationClip clipToEdit;
 | |
|             if (!GetEditableClip(clips, out clip, out clipToEdit))
 | |
|                 return false;
 | |
| 
 | |
|             GameObject gameObject = null;
 | |
|             if (TimelineEditor.inspectedDirector != null)
 | |
|                 gameObject = TimelineUtility.GetSceneGameObject(TimelineEditor.inspectedDirector, clip.GetParentTrack());
 | |
| 
 | |
|             var timeController = TimelineAnimationUtilities.CreateTimeController(clip);
 | |
|             TimelineAnimationUtilities.EditAnimationClipWithTimeController(
 | |
|                 clipToEdit, timeController, clip.animationClip != null ? gameObject : null);
 | |
| 
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         private static bool GetEditableClip(IEnumerable<TimelineClip> clips, out TimelineClip clip, out AnimationClip animClip)
 | |
|         {
 | |
|             clip = null;
 | |
|             animClip = null;
 | |
| 
 | |
|             if (clips.Count() != 1)
 | |
|                 return false;
 | |
| 
 | |
|             clip = clips.FirstOrDefault();
 | |
|             if (clip == null)
 | |
|                 return false;
 | |
| 
 | |
|             if (clip.animationClip != null)
 | |
|                 animClip = clip.animationClip;
 | |
|             else if (clip.curves != null && !clip.curves.empty)
 | |
|                 animClip = clip.curves;
 | |
| 
 | |
|             return animClip != null;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     [MenuEntry("Edit Sub-Timeline", MenuPriority.ClipEditActionSection.editSubTimeline), UsedImplicitly]
 | |
|     class EditSubTimeline : ClipAction
 | |
|     {
 | |
|         private static readonly string MultiItemPrefix = "Edit Sub-Timelines/";
 | |
|         private static readonly string SingleItemPrefix = "Edit ";
 | |
| 
 | |
|         public override ActionValidity Validate(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             if (clips == null || clips.Count() != 1 || TimelineEditor.inspectedDirector == null)
 | |
|                 return ActionValidity.NotApplicable;
 | |
| 
 | |
|             var clip = clips.First();
 | |
|             var directors = TimelineUtility.GetSubTimelines(clip, TimelineEditor.inspectedDirector);
 | |
|             return directors.Any(x => x != null) ? ActionValidity.Valid : ActionValidity.NotApplicable;
 | |
|         }
 | |
| 
 | |
|         public override bool Execute(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             if (Validate(clips) != ActionValidity.Valid) return false;
 | |
| 
 | |
|             var clip = clips.First();
 | |
| 
 | |
|             var directors = TimelineUtility.GetSubTimelines(clip, TimelineEditor.inspectedDirector);
 | |
|             ExecuteInternal(directors, 0, clip);
 | |
| 
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         static void ExecuteInternal(IList<PlayableDirector> directors, int directorIndex, TimelineClip clip)
 | |
|         {
 | |
|             SelectionManager.Clear();
 | |
|             TimelineWindow.instance.SetCurrentTimeline(directors[directorIndex], clip);
 | |
|         }
 | |
| 
 | |
|         internal void AddMenuItem(List<MenuActionItem> menuItems)
 | |
|         {
 | |
|             var clips = TimelineEditor.selectedClips;
 | |
|             if (clips == null || clips.Length != 1)
 | |
|                 return;
 | |
| 
 | |
|             var mode = TimelineWindow.instance.currentMode.mode;
 | |
|             MenuEntryAttribute menuAttribute = GetType().GetCustomAttributes(typeof(MenuEntryAttribute), false).OfType<MenuEntryAttribute>().FirstOrDefault();
 | |
|             var menuItem = new MenuActionItem()
 | |
|             {
 | |
|                 category = menuAttribute.subMenuPath ?? string.Empty,
 | |
|                 entryName = menuAttribute.name,
 | |
|                 isActiveInMode = this.IsActionActiveInMode(mode),
 | |
|                 priority = menuAttribute.priority,
 | |
|                 state = Validate(clips),
 | |
|                 callback = null
 | |
|             };
 | |
| 
 | |
|             var subDirectors = TimelineUtility.GetSubTimelines(clips[0], TimelineEditor.inspectedDirector);
 | |
|             if (subDirectors.Count == 1)
 | |
|             {
 | |
|                 menuItem.entryName = SingleItemPrefix + DisplayNameHelper.GetDisplayName(subDirectors[0]);
 | |
|                 menuItem.callback = () =>
 | |
|                 {
 | |
|                     Execute(clips);
 | |
|                 };
 | |
|                 menuItems.Add(menuItem);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 for (int i = 0; i < subDirectors.Count; i++)
 | |
|                 {
 | |
|                     var index = i;
 | |
|                     menuItem.category = MultiItemPrefix;
 | |
|                     menuItem.entryName = DisplayNameHelper.GetDisplayName(subDirectors[i]);
 | |
|                     menuItem.callback = () =>
 | |
|                     {
 | |
|                         ExecuteInternal(subDirectors, index, clips[0]);
 | |
|                     };
 | |
|                     menuItems.Add(menuItem);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     [MenuEntry("Editing/Trim Start", MenuPriority.ClipActionSection.trimStart)]
 | |
|     [Shortcut(Shortcuts.Clip.trimStart), UsedImplicitly]
 | |
|     class TrimStart : ClipAction
 | |
|     {
 | |
|         public override ActionValidity Validate(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             return clips.All(x => TimelineEditor.inspectedSequenceTime <= x.start || TimelineEditor.inspectedSequenceTime >= x.start + x.duration) ? ActionValidity.Invalid : ActionValidity.Valid;
 | |
|         }
 | |
| 
 | |
|         public override bool Execute(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             return ClipModifier.TrimStart(clips, TimelineEditor.inspectedSequenceTime);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     [MenuEntry("Editing/Trim End", MenuPriority.ClipActionSection.trimEnd), UsedImplicitly]
 | |
|     [Shortcut(Shortcuts.Clip.trimEnd)]
 | |
|     class TrimEnd : ClipAction
 | |
|     {
 | |
|         public override ActionValidity Validate(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             return clips.All(x => TimelineEditor.inspectedSequenceTime <= x.start || TimelineEditor.inspectedSequenceTime >= x.start + x.duration) ? ActionValidity.Invalid : ActionValidity.Valid;
 | |
|         }
 | |
| 
 | |
|         public override bool Execute(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             return ClipModifier.TrimEnd(clips, TimelineEditor.inspectedSequenceTime);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     [Shortcut(Shortcuts.Clip.split)]
 | |
|     [MenuEntry("Editing/Split", MenuPriority.ClipActionSection.split), UsedImplicitly]
 | |
|     class Split : ClipAction
 | |
|     {
 | |
|         public override ActionValidity Validate(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             return clips.All(x => TimelineEditor.inspectedSequenceTime <= x.start || TimelineEditor.inspectedSequenceTime >= x.start + x.duration) ? ActionValidity.Invalid : ActionValidity.Valid;
 | |
|         }
 | |
| 
 | |
|         public override bool Execute(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             bool success = ClipModifier.Split(clips, TimelineEditor.inspectedSequenceTime, TimelineEditor.inspectedDirector);
 | |
|             if (success)
 | |
|                 TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved);
 | |
|             return success;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     [MenuEntry("Editing/Complete Last Loop", MenuPriority.ClipActionSection.completeLastLoop), UsedImplicitly]
 | |
|     class CompleteLastLoop : ClipAction
 | |
|     {
 | |
|         public override ActionValidity Validate(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             bool canDisplay = clips.Any(TimelineHelpers.HasUsableAssetDuration);
 | |
|             return canDisplay ? ActionValidity.Valid : ActionValidity.Invalid;
 | |
|         }
 | |
| 
 | |
|         public override bool Execute(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             return ClipModifier.CompleteLastLoop(clips);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     [MenuEntry("Editing/Trim Last Loop", MenuPriority.ClipActionSection.trimLastLoop), UsedImplicitly]
 | |
|     class TrimLastLoop : ClipAction
 | |
|     {
 | |
|         public override ActionValidity Validate(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             bool canDisplay = clips.Any(TimelineHelpers.HasUsableAssetDuration);
 | |
|             return canDisplay ? ActionValidity.Valid : ActionValidity.Invalid;
 | |
|         }
 | |
| 
 | |
|         public override bool Execute(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             return ClipModifier.TrimLastLoop(clips);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     [MenuEntry("Editing/Match Duration", MenuPriority.ClipActionSection.matchDuration), UsedImplicitly]
 | |
|     class MatchDuration : ClipAction
 | |
|     {
 | |
|         public override ActionValidity Validate(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             return clips.Count() > 1 ? ActionValidity.Valid : ActionValidity.Invalid;
 | |
|         }
 | |
| 
 | |
|         public override bool Execute(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             return ClipModifier.MatchDuration(clips);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     [MenuEntry("Editing/Double Speed", MenuPriority.ClipActionSection.doubleSpeed), UsedImplicitly]
 | |
|     class DoubleSpeed : ClipAction
 | |
|     {
 | |
|         public override ActionValidity Validate(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             bool canDisplay = clips.All(x => x.SupportsSpeedMultiplier());
 | |
| 
 | |
|             return canDisplay ? ActionValidity.Valid : ActionValidity.Invalid;
 | |
|         }
 | |
| 
 | |
|         public override bool Execute(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             return ClipModifier.DoubleSpeed(clips);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     [MenuEntry("Editing/Half Speed", MenuPriority.ClipActionSection.halfSpeed), UsedImplicitly]
 | |
|     class HalfSpeed : ClipAction
 | |
|     {
 | |
|         public override ActionValidity Validate(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             bool canDisplay = clips.All(x => x.SupportsSpeedMultiplier());
 | |
| 
 | |
|             return canDisplay ? ActionValidity.Valid : ActionValidity.Invalid;
 | |
|         }
 | |
| 
 | |
|         public override bool Execute(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             return ClipModifier.HalfSpeed(clips);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     [MenuEntry("Editing/Reset Duration", MenuPriority.ClipActionSection.resetDuration), UsedImplicitly]
 | |
|     class ResetDuration : ClipAction
 | |
|     {
 | |
|         public override ActionValidity Validate(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             bool canDisplay = clips.Any(TimelineHelpers.HasUsableAssetDuration);
 | |
|             return canDisplay ? ActionValidity.Valid : ActionValidity.Invalid;
 | |
|         }
 | |
| 
 | |
|         public override bool Execute(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             return ClipModifier.ResetEditing(clips);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     [MenuEntry("Editing/Reset Speed", MenuPriority.ClipActionSection.resetSpeed), UsedImplicitly]
 | |
|     class ResetSpeed : ClipAction
 | |
|     {
 | |
|         public override ActionValidity Validate(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             bool canDisplay = clips.All(x => x.SupportsSpeedMultiplier());
 | |
| 
 | |
|             return canDisplay ? ActionValidity.Valid : ActionValidity.Invalid;
 | |
|         }
 | |
| 
 | |
|         public override bool Execute(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             return ClipModifier.ResetSpeed(clips);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     [MenuEntry("Editing/Reset All", MenuPriority.ClipActionSection.resetAll), UsedImplicitly]
 | |
|     class ResetAll : ClipAction
 | |
|     {
 | |
|         public override ActionValidity Validate(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             bool canDisplay = clips.Any(TimelineHelpers.HasUsableAssetDuration) || clips.All(x => x.SupportsSpeedMultiplier());
 | |
| 
 | |
|             return canDisplay ? ActionValidity.Valid : ActionValidity.Invalid;
 | |
|         }
 | |
| 
 | |
|         public override bool Execute(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             var speedResult = ClipModifier.ResetSpeed(clips);
 | |
|             var editResult = ClipModifier.ResetEditing(clips);
 | |
|             return speedResult || editResult;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     [MenuEntry("Tile", MenuPriority.ClipActionSection.tile), UsedImplicitly]
 | |
|     class Tile : ClipAction
 | |
|     {
 | |
|         public override ActionValidity Validate(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             return clips.Count() > 1 ? ActionValidity.Valid : ActionValidity.Invalid;
 | |
|         }
 | |
| 
 | |
|         public override bool Execute(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             return ClipModifier.Tile(clips);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     [MenuEntry("Find Source Asset", MenuPriority.ClipActionSection.findSourceAsset), UsedImplicitly]
 | |
|     [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)]
 | |
|     class FindSourceAsset : ClipAction
 | |
|     {
 | |
|         public override ActionValidity Validate(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             if (clips.Count() > 1)
 | |
|                 return ActionValidity.Invalid;
 | |
| 
 | |
|             if (GetUnderlyingAsset(clips.First()) == null)
 | |
|                 return ActionValidity.Invalid;
 | |
| 
 | |
|             return ActionValidity.Valid;
 | |
|         }
 | |
| 
 | |
|         public override bool Execute(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             EditorGUIUtility.PingObject(GetUnderlyingAsset(clips.First()));
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         private static UnityEngine.Object GetExternalPlayableAsset(TimelineClip clip)
 | |
|         {
 | |
|             if (clip.asset == null)
 | |
|                 return null;
 | |
| 
 | |
|             if ((clip.asset.hideFlags & HideFlags.HideInHierarchy) != 0)
 | |
|                 return null;
 | |
| 
 | |
|             return clip.asset;
 | |
|         }
 | |
| 
 | |
|         private static UnityEngine.Object GetUnderlyingAsset(TimelineClip clip)
 | |
|         {
 | |
|             var asset = clip.asset as ScriptableObject;
 | |
|             if (asset == null)
 | |
|                 return null;
 | |
| 
 | |
|             var fields = ObjectReferenceField.FindObjectReferences(asset.GetType());
 | |
|             if (fields.Length == 0)
 | |
|                 return GetExternalPlayableAsset(clip);
 | |
| 
 | |
|             // Find the first non-null field
 | |
|             foreach (var field in fields)
 | |
|             {
 | |
|                 // skip scene refs in asset mode
 | |
|                 if (TimelineEditor.inspectedDirector == null && field.isSceneReference)
 | |
|                     continue;
 | |
|                 var obj = field.Find(asset, TimelineEditor.inspectedDirector);
 | |
|                 if (obj != null)
 | |
|                     return obj;
 | |
|             }
 | |
| 
 | |
|             return GetExternalPlayableAsset(clip);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     class CopyClipsToClipboard : ClipAction
 | |
|     {
 | |
|         public override ActionValidity Validate(IEnumerable<TimelineClip> clips) => ActionValidity.Valid;
 | |
|         public override bool Execute(IEnumerable<TimelineClip> clips)
 | |
|         {
 | |
|             TimelineEditor.clipboard.CopyItems(clips.ToItems());
 | |
|             return true;
 | |
|         }
 | |
|     }
 | |
| }
 |