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; | ||
|  |         } | ||
|  |     } | ||
|  | } |