From c55fba8ab2a1c9d3df65eda4a5a1e957f4aa1f78 Mon Sep 17 00:00:00 2001 From: Andrew Lee Date: Sun, 19 Apr 2020 17:19:32 -0400 Subject: Inital commit --- .../Editor/Actions/ClipsActions.cs | 376 ++++++++ .../Editor/Actions/ClipsActions.cs.meta | 11 + .../Editor/Actions/ItemAction.cs | 124 +++ .../Editor/Actions/ItemAction.cs.meta | 11 + .../Editor/Actions/MarkerActions.cs | 17 + .../Editor/Actions/MarkerActions.cs.meta | 11 + .../Editor/Actions/Menus.meta | 8 + .../Editor/Actions/Menus/MenuItemActionBase.cs | 177 ++++ .../Actions/Menus/MenuItemActionBase.cs.meta | 11 + .../Editor/Actions/Menus/TimelineContextMenu.cs | 448 ++++++++++ .../Actions/Menus/TimelineContextMenu.cs.meta | 11 + .../Editor/Actions/TimelineActions.cs | 946 +++++++++++++++++++++ .../Editor/Actions/TimelineActions.cs.meta | 11 + .../Editor/Actions/TrackActions.cs | 521 ++++++++++++ .../Editor/Actions/TrackActions.cs.meta | 11 + 15 files changed, 2694 insertions(+) create mode 100644 Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/ClipsActions.cs create mode 100644 Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/ClipsActions.cs.meta create mode 100644 Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/ItemAction.cs create mode 100644 Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/ItemAction.cs.meta create mode 100644 Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/MarkerActions.cs create mode 100644 Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/MarkerActions.cs.meta create mode 100644 Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/Menus.meta create mode 100644 Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/Menus/MenuItemActionBase.cs create mode 100644 Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/Menus/MenuItemActionBase.cs.meta create mode 100644 Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/Menus/TimelineContextMenu.cs create mode 100644 Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/Menus/TimelineContextMenu.cs.meta create mode 100644 Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/TimelineActions.cs create mode 100644 Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/TimelineActions.cs.meta create mode 100644 Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/TrackActions.cs create mode 100644 Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/TrackActions.cs.meta (limited to 'Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions') diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/ClipsActions.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/ClipsActions.cs new file mode 100644 index 0000000..e12af71 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/ClipsActions.cs @@ -0,0 +1,376 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using JetBrains.Annotations; +using UnityEngine; +using UnityEngine.Timeline; +using UnityEngine.Playables; +using ClipAction = UnityEditor.Timeline.ItemAction; + +namespace UnityEditor.Timeline +{ + [MenuEntry("Edit in Animation Window", MenuOrder.ClipEditAction.EditInAnimationWindow), UsedImplicitly] + class EditClipInAnimationWindow : ClipAction + { + protected override MenuActionDisplayState GetDisplayState(WindowState state, TimelineClip[] clips) + { + if (clips.Length == 1 && clips[0].animationClip != null) + return MenuActionDisplayState.Visible; + return MenuActionDisplayState.Hidden; + } + + public override bool Execute(WindowState state, TimelineClip[] clips) + { + var clip = clips[0]; + + if (clip.curves != null || clip.animationClip != null) + { + var clipToEdit = clip.animationClip != null ? clip.animationClip : clip.curves; + if (clipToEdit == null) + return false; + + var gameObject = state.GetSceneReference(clip.parentTrack); + var timeController = TimelineAnimationUtilities.CreateTimeController(state, clip); + TimelineAnimationUtilities.EditAnimationClipWithTimeController( + clipToEdit, timeController, clip.animationClip != null ? gameObject : null); + return true; + } + + return false; + } + } + + [MenuEntry("Edit Sub-Timeline", MenuOrder.ClipEditAction.EditSubTimeline), UsedImplicitly] + class EditSubTimeline : ClipAction + { + private static readonly string MultiItemPrefix = "Edit Sub-Timelines/"; + private static readonly string SingleItemPrefix = "Edit "; + + protected override MenuActionDisplayState GetDisplayState(WindowState state, TimelineClip[] clips) + { + return IsValid(state, clips) ? MenuActionDisplayState.Visible : MenuActionDisplayState.Hidden; + } + + bool IsValid(WindowState state, TimelineClip[] clips) + { + if (clips.Length != 1 || state == null || state.editSequence.director == null) return false; + var clip = clips[0]; + + var directors = TimelineUtility.GetSubTimelines(clip, state.editSequence.director); + return directors.Any(x => x != null); + } + + public override bool Execute(WindowState state, TimelineClip[] clips) + { + if (!IsValid(state, clips)) return false; + + var clip = clips[0]; + + var directors = TimelineUtility.GetSubTimelines(clip, state.editSequence.director); + ExecuteInternal(state, directors, 0, clip); + + return true; + } + + static void ExecuteInternal(WindowState state, IList directors, int directorIndex, TimelineClip clip) + { + SelectionManager.Clear(); + state.GetWindow().SetCurrentTimeline(directors[directorIndex], clip); + } + + protected override void AddMenuItem(WindowState state, TimelineClip[] items, List menuItems) + { + if (items == null || items.Length != 1) + return; + + var mode = TimelineWindow.instance.currentMode.mode; + var menuItem = new MenuActionItem() + { + category = category, + entryName = GetDisplayName(items), + shortCut = string.Empty, + isChecked = false, + isActiveInMode = IsActionActiveInMode(this, mode), + priority = priority, + state = GetDisplayState(state, items), + callback = null + }; + + var subDirectors = TimelineUtility.GetSubTimelines(items[0], state.editSequence.director); + if (subDirectors.Count == 1) + { + menuItem.entryName = SingleItemPrefix + DisplayNameHelper.GetDisplayName(subDirectors[0]); + menuItem.callback = () => Execute(state, items); + 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(state, subDirectors, index, items[0]); + menuItems.Add(menuItem); + } + } + } + } + + [MenuEntry("Editing/Trim Start", MenuOrder.ClipAction.TrimStart)] + [Shortcut(Shortcuts.Clip.trimStart), UsedImplicitly] + class TrimStart : ItemAction + { + protected override MenuActionDisplayState GetDisplayState(WindowState state, TimelineClip[] clips) + { + return clips.All(x => state.editSequence.time <= x.start || state.editSequence.time >= x.start + x.duration) ? + MenuActionDisplayState.Disabled : MenuActionDisplayState.Visible; + } + + public override bool Execute(WindowState state, TimelineClip[] clips) + { + return ClipModifier.TrimStart(clips, state.editSequence.time); + } + } + + [MenuEntry("Editing/Trim End", MenuOrder.ClipAction.TrimEnd), UsedImplicitly] + [Shortcut(Shortcuts.Clip.trimEnd)] + class TrimEnd : ItemAction + { + protected override MenuActionDisplayState GetDisplayState(WindowState state, TimelineClip[] clips) + { + return clips.All(x => state.editSequence.time <= x.start || state.editSequence.time >= x.start + x.duration) ? + MenuActionDisplayState.Disabled : MenuActionDisplayState.Visible; + } + + public override bool Execute(WindowState state, TimelineClip[] clips) + { + return ClipModifier.TrimEnd(clips, state.editSequence.time); + } + } + + [Shortcut(Shortcuts.Clip.split), MenuEntry("Editing/Split", MenuOrder.ClipAction.Split), UsedImplicitly] + class Split : ClipAction + { + protected override MenuActionDisplayState GetDisplayState(WindowState state, TimelineClip[] clips) + { + return clips.All(x => state.editSequence.time <= x.start || state.editSequence.time >= x.start + x.duration) ? + MenuActionDisplayState.Disabled : MenuActionDisplayState.Visible; + } + + public override bool Execute(WindowState state, TimelineClip[] clips) + { + bool success = ClipModifier.Split(clips, state.editSequence.time, state.editSequence.director); + if (success) + state.Refresh(); + return success; + } + } + + [MenuEntry("Editing/Complete Last Loop", MenuOrder.ClipAction.CompleteLastLoop), UsedImplicitly] + class CompleteLastLoop : ClipAction + { + protected override MenuActionDisplayState GetDisplayState(WindowState state, TimelineClip[] clips) + { + bool canDisplay = clips.Any(TimelineHelpers.HasUsableAssetDuration); + return canDisplay ? MenuActionDisplayState.Visible : MenuActionDisplayState.Disabled; + } + + public override bool Execute(WindowState state, TimelineClip[] clips) + { + return ClipModifier.CompleteLastLoop(clips); + } + } + + [MenuEntry("Editing/Trim Last Loop", MenuOrder.ClipAction.TrimLastLoop), UsedImplicitly] + class TrimLastLoop : ClipAction + { + protected override MenuActionDisplayState GetDisplayState(WindowState state, TimelineClip[] clips) + { + bool canDisplay = clips.Any(TimelineHelpers.HasUsableAssetDuration); + return canDisplay ? MenuActionDisplayState.Visible : MenuActionDisplayState.Disabled; + } + + public override bool Execute(WindowState state, TimelineClip[] clips) + { + return ClipModifier.TrimLastLoop(clips); + } + } + + [MenuEntry("Editing/Match Duration", MenuOrder.ClipAction.MatchDuration), UsedImplicitly] + class MatchDuration : ClipAction + { + protected override MenuActionDisplayState GetDisplayState(WindowState state, TimelineClip[] clips) + { + return clips.Length > 1 ? MenuActionDisplayState.Visible : MenuActionDisplayState.Disabled; + } + + public override bool Execute(WindowState state, TimelineClip[] clips) + { + return ClipModifier.MatchDuration(clips); + } + } + + [MenuEntry("Editing/Double Speed", MenuOrder.ClipAction.DoubleSpeed), UsedImplicitly] + class DoubleSpeed : ClipAction + { + protected override MenuActionDisplayState GetDisplayState(WindowState state, TimelineClip[] clips) + { + bool canDisplay = clips.All(x => x.SupportsSpeedMultiplier()); + + return canDisplay ? MenuActionDisplayState.Visible : MenuActionDisplayState.Disabled; + } + + public override bool Execute(WindowState state, TimelineClip[] clips) + { + return ClipModifier.DoubleSpeed(clips); + } + } + + [MenuEntry("Editing/Half Speed", MenuOrder.ClipAction.HalfSpeed), UsedImplicitly] + class HalfSpeed : ClipAction + { + protected override MenuActionDisplayState GetDisplayState(WindowState state, TimelineClip[] clips) + { + bool canDisplay = clips.All(x => x.SupportsSpeedMultiplier()); + + return canDisplay ? MenuActionDisplayState.Visible : MenuActionDisplayState.Disabled; + } + + public override bool Execute(WindowState state, TimelineClip[] clips) + { + return ClipModifier.HalfSpeed(clips); + } + } + + [MenuEntry("Editing/Reset Duration", MenuOrder.ClipAction.ResetDuration), UsedImplicitly] + class ResetDuration : ClipAction + { + protected override MenuActionDisplayState GetDisplayState(WindowState state, TimelineClip[] clips) + { + bool canDisplay = clips.Any(TimelineHelpers.HasUsableAssetDuration); + return canDisplay ? MenuActionDisplayState.Visible : MenuActionDisplayState.Disabled; + } + + public override bool Execute(WindowState state, TimelineClip[] clips) + { + return ClipModifier.ResetEditing(clips); + } + } + + [MenuEntry("Editing/Reset Speed", MenuOrder.ClipAction.ResetSpeed), UsedImplicitly] + class ResetSpeed : ClipAction + { + protected override MenuActionDisplayState GetDisplayState(WindowState state, TimelineClip[] clips) + { + bool canDisplay = clips.All(x => x.SupportsSpeedMultiplier()); + + return canDisplay ? MenuActionDisplayState.Visible : MenuActionDisplayState.Disabled; + } + + public override bool Execute(WindowState state, TimelineClip[] clips) + { + return ClipModifier.ResetSpeed(clips); + } + } + + [MenuEntry("Editing/Reset All", MenuOrder.ClipAction.ResetAll), UsedImplicitly] + class ResetAll : ClipAction + { + protected override MenuActionDisplayState GetDisplayState(WindowState state, TimelineClip[] clips) + { + bool canDisplay = clips.Any(TimelineHelpers.HasUsableAssetDuration) || + clips.All(x => x.SupportsSpeedMultiplier()); + + return canDisplay ? MenuActionDisplayState.Visible : MenuActionDisplayState.Disabled; + } + + public override bool Execute(WindowState state, TimelineClip[] clips) + { + var speedResult = ClipModifier.ResetSpeed(clips); + var editResult = ClipModifier.ResetEditing(clips); + return speedResult || editResult; + } + } + + [MenuEntry("Tile", MenuOrder.ClipAction.Tile), UsedImplicitly] + class Tile : ClipAction + { + protected override MenuActionDisplayState GetDisplayState(WindowState state, TimelineClip[] clips) + { + return clips.Length > 1 ? MenuActionDisplayState.Visible : MenuActionDisplayState.Disabled; + } + + public override bool Execute(WindowState state, TimelineClip[] clips) + { + return ClipModifier.Tile(clips); + } + } + + [MenuEntry("Find Source Asset", MenuOrder.ClipAction.FindSourceAsset), UsedImplicitly] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class FindSourceAsset : ClipAction + { + protected override MenuActionDisplayState GetDisplayState(WindowState state, + TimelineClip[] clips) + { + if (clips.Length > 1) + return MenuActionDisplayState.Disabled; + + if (GetUnderlyingAsset(state, clips[0]) == null) + return MenuActionDisplayState.Disabled; + + return MenuActionDisplayState.Visible; + } + + public override bool Execute(WindowState state, TimelineClip[] clips) + { + EditorGUIUtility.PingObject(GetUnderlyingAsset(state, clips[0])); + 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(WindowState state, 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 (state.editSequence.director == null && field.isSceneReference) + continue; + var obj = field.Find(asset, state.editSequence.director); + if (obj != null) + return obj; + } + + return GetExternalPlayableAsset(clip); + } + } + + class CopyClipsToClipboard : ClipAction + { + public override bool Execute(WindowState state, TimelineClip[] clips) + { + TimelineEditor.clipboard.CopyItems(clips.ToItems()); + return true; + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/ClipsActions.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/ClipsActions.cs.meta new file mode 100644 index 0000000..666f56e --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/ClipsActions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4b721099b5d509d4093e516f59ad9ad6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/ItemAction.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/ItemAction.cs new file mode 100644 index 0000000..cd4c3a4 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/ItemAction.cs @@ -0,0 +1,124 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.Timeline; + +namespace UnityEditor.Timeline +{ + [ActiveInMode(TimelineModes.Default)] + abstract class ItemAction : MenuItemActionBase where T : class + { + public abstract bool Execute(WindowState state, T[] items); + + protected virtual MenuActionDisplayState GetDisplayState(WindowState state, T[] items) + { + return items.Length > 0 ? MenuActionDisplayState.Visible : MenuActionDisplayState.Disabled; + } + + protected virtual string GetDisplayName(T[] items) + { + return menuName; + } + + public bool CanExecute(WindowState state, T[] items) + { + return GetDisplayState(state, items) == MenuActionDisplayState.Visible; + } + + protected virtual void AddMenuItem(WindowState state, T[] items, List menuItem) + { + var mode = TimelineWindow.instance.currentMode.mode; + menuItem.Add( + new MenuActionItem() + { + category = category, + entryName = GetDisplayName(items), + shortCut = this.shortCut, + isChecked = false, + isActiveInMode = IsActionActiveInMode(this, mode), + priority = priority, + state = GetDisplayState(state, items), + callback = () => Execute(state, items) + } + ); + } + + public static bool HandleShortcut(WindowState state, Event evt, T item) + { + T[] items = { item }; + + foreach (ItemAction action in actions) + { + var attr = action.GetType().GetCustomAttributes(typeof(ShortcutAttribute), true); + + foreach (ShortcutAttribute shortcut in attr) + { + if (shortcut.MatchesEvent(evt)) + { + if (s_ShowActionTriggeredByShortcut) + Debug.Log(action.GetType().Name); + + if (!IsActionActiveInMode(action, TimelineWindow.instance.currentMode.mode)) + return false; + + var result = action.Execute(state, items); + state.Refresh(); + state.Evaluate(); + return result; + } + } + } + + return false; + } + + static List> s_ActionClasses; + + static List> actions + { + get + { + if (s_ActionClasses == null) + { + s_ActionClasses = GetActionsOfType(typeof(ItemAction)).Select(x => (ItemAction)x.GetConstructors()[0].Invoke(null)).ToList(); + } + + return s_ActionClasses; + } + } + + public static void GetMenuEntries(T[] items, List menuItems) + { + if (items == null || items.Length == 0) + return; + + foreach (var action in actions) + { + if (action.showInMenu) + action.AddMenuItem(TimelineWindow.instance.state, items, menuItems); + } + } + + public static bool Invoke(WindowState state, T[] items) + where TAction : ItemAction + { + var itemsDerived = items.ToArray(); + + if (!itemsDerived.Any()) + return false; + + var action = actions.FirstOrDefault(x => x.GetType() == typeof(TAction)); + + if (action != null) + return action.Execute(state, itemsDerived); + + return false; + } + + public static bool Invoke(WindowState state, T item) + where TAction : ItemAction + { + return Invoke(state, new[] {item}); + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/ItemAction.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/ItemAction.cs.meta new file mode 100644 index 0000000..fef443a --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/ItemAction.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 84b5362754a9d934ba259398b757d0be +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/MarkerActions.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/MarkerActions.cs new file mode 100644 index 0000000..410b5c3 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/MarkerActions.cs @@ -0,0 +1,17 @@ +using JetBrains.Annotations; +using UnityEngine; +using UnityEngine.Timeline; +using MarkerAction = UnityEditor.Timeline.ItemAction; + +namespace UnityEditor.Timeline +{ + [UsedImplicitly] + class CopyMarkersToClipboard : MarkerAction + { + public override bool Execute(WindowState state, IMarker[] markers) + { + TimelineEditor.clipboard.CopyItems(markers.ToItems()); + return true; + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/MarkerActions.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/MarkerActions.cs.meta new file mode 100644 index 0000000..2733046 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/MarkerActions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5da77d4d078922b4c8466e9e35fb3f5e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/Menus.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/Menus.meta new file mode 100644 index 0000000..45d9fa7 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/Menus.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 985eed4bc2fbee941b761b8816d9055d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/Menus/MenuItemActionBase.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/Menus/MenuItemActionBase.cs new file mode 100644 index 0000000..241650f --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/Menus/MenuItemActionBase.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using UnityEngine; + +namespace UnityEditor.Timeline +{ + enum MenuActionDisplayState + { + Visible, + Disabled, + Hidden + } + + struct MenuActionItem + { + public string category; + public string entryName; + public string shortCut; + public int priority; + public bool isChecked; + public bool isActiveInMode; + public MenuActionDisplayState state; + public GenericMenu.MenuFunction callback; + } + + class MenuItemActionBase + { + public Vector2? mousePosition { get; set; } + + protected static bool s_ShowActionTriggeredByShortcut = false; + + private static MenuEntryAttribute NoMenu = new MenuEntryAttribute(null, MenuOrder.DefaultPriority); + private MenuEntryAttribute m_MenuInfo; + private string m_ShortCut = null; + + + public static IEnumerable GetActionsOfType(Type actionType) + { + var query = TypeCache.GetTypesDerivedFrom(actionType).Where(type => !type.IsGenericType && !type.IsNested && !type.IsAbstract); + return query; + } + + public static ShortcutAttribute GetShortcutAttributeForAction(MenuItemActionBase action) + { + var shortcutAttributes = action.GetType() + .GetCustomAttributes(typeof(ShortcutAttribute), true) + .Cast(); + + foreach (var shortcutAttribute in shortcutAttributes) + { + var shortcutOverride = shortcutAttribute as ShortcutPlatformOverrideAttribute; + if (shortcutOverride != null) + { + if (shortcutOverride.MatchesCurrentPlatform()) + return shortcutOverride; + } + else + { + return shortcutAttribute; + } + } + + return null; + } + + public static void BuildMenu(GenericMenu menu, List items) + { + // sorted the outer menu by priority, then sort the innermenu by priority + var sortedItems = + items.GroupBy(x => string.IsNullOrEmpty(x.category) ? x.entryName : x.category). + OrderBy(x => x.Min(y => y.priority)). + SelectMany(x => x.OrderBy(z => z.priority)); + + int lastPriority = Int32.MinValue; + string lastCategory = string.Empty; + + foreach (var s in sortedItems) + { + if (s.state == MenuActionDisplayState.Hidden) + continue; + + var priority = s.priority; + if (lastPriority == Int32.MinValue) + { + lastPriority = priority; + } + else if ((priority / MenuOrder.SeparatorAt) > (lastPriority / MenuOrder.SeparatorAt)) + { + string path = String.Empty; + if (lastCategory == s.category) + path = s.category; + menu.AddSeparator(path); + } + + lastPriority = priority; + lastCategory = s.category; + + string entry = s.category + s.entryName; + if (!string.IsNullOrEmpty(s.shortCut)) + entry += " " + s.shortCut; + + if (s.state == MenuActionDisplayState.Visible && s.isActiveInMode) + menu.AddItem(new GUIContent(entry), s.isChecked, s.callback); + else + menu.AddDisabledItem(new GUIContent(entry)); + } + } + + public static ActiveInModeAttribute GetActiveInModeAttribute(MenuItemActionBase action) + { + var attr = action.GetType().GetCustomAttributes(typeof(ActiveInModeAttribute), true); + + if (attr.Length > 0) + return (attr[0] as ActiveInModeAttribute); + + return null; + } + + public static bool IsActionActiveInMode(MenuItemActionBase action, TimelineModes mode) + { + ActiveInModeAttribute attr = GetActiveInModeAttribute(action); + return attr != null && (attr.modes & mode) != 0; + } + + public int priority + { + get { return menuInfo.priority; } + } + + public string category + { + get { return menuInfo.subMenuPath; } + } + + public string menuName + { + get + { + if (string.IsNullOrEmpty(menuInfo.name)) + return L10n.Tr(GetType().Name); + return menuInfo.name; + } + } + + // shortcut used by the menu + public string shortCut + { + get + { + if (m_ShortCut == null) + { + var shortcutAttribute = GetShortcutAttributeForAction(this); + m_ShortCut = shortcutAttribute == null ? string.Empty : shortcutAttribute.GetMenuShortcut(); + } + return m_ShortCut; + } + } + + public bool showInMenu + { + get { return menuInfo != NoMenu; } + } + + private MenuEntryAttribute menuInfo + { + get + { + if (m_MenuInfo == null) + m_MenuInfo = GetType().GetCustomAttributes(typeof(MenuEntryAttribute), false).OfType().DefaultIfEmpty(NoMenu).First(); + return m_MenuInfo; + } + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/Menus/MenuItemActionBase.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/Menus/MenuItemActionBase.cs.meta new file mode 100644 index 0000000..045ed68 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/Menus/MenuItemActionBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5882d0e4313310143acb11d1a66c597f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/Menus/TimelineContextMenu.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/Menus/TimelineContextMenu.cs new file mode 100644 index 0000000..495a9c3 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/Menus/TimelineContextMenu.cs @@ -0,0 +1,448 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.Playables; +using UnityEngine.Timeline; +using Object = UnityEngine.Object; + +namespace UnityEditor.Timeline +{ + static class SequencerContextMenu + { + static readonly TimelineAction[] MarkerHeaderCommonOperations = + { + new PasteAction() + }; + + public static readonly TimelineAction[] MarkerHeaderMenuItems = + TimelineAction.AllActions.OfType(). + Where(a => a.showInMenu). + Union(MarkerHeaderCommonOperations). + ToArray(); + + + static class Styles + { + public static readonly string addItemFromAssetTemplate = L10n.Tr("Add {0} From {1}"); + public static readonly string addSingleItemFromAssetTemplate = L10n.Tr("Add From {1}"); + public static readonly string addItemTemplate = L10n.Tr("Add {0}"); + public static readonly string typeSelectorTemplate = L10n.Tr("Select {0}"); + public static readonly string trackGroup = L10n.Tr("Track Group"); + public static readonly string trackSubGroup = L10n.Tr("Track Sub-Group"); + public static readonly string addTrackLayer = L10n.Tr("Add Layer"); + public static readonly string layerName = L10n.Tr("Layer {0}"); + } + + public static void ShowMarkerHeaderContextMenu(Vector2? mousePosition, WindowState state) + { + var menu = new GenericMenu(); + List items = new List(100); + BuildMarkerHeaderContextMenu(items, mousePosition, state); + MenuItemActionBase.BuildMenu(menu, items); + menu.ShowAsContext(); + } + + public static void ShowNewTracksContextMenu(ICollection tracks, WindowState state) + { + var menu = new GenericMenu(); + List items = new List(100); + BuildNewTracksContextMenu(items, tracks, state); + MenuItemActionBase.BuildMenu(menu, items); + menu.ShowAsContext(); + } + + public static void ShowNewTracksContextMenu(ICollection tracks, WindowState state, Rect rect) + { + var menu = new GenericMenu(); + List items = new List(100); + BuildNewTracksContextMenu(items, tracks, state); + MenuItemActionBase.BuildMenu(menu, items); + menu.DropDown(rect); + } + + public static void ShowTrackContextMenu(TrackAsset[] tracks, Vector2? mousePosition) + { + if (tracks == null || tracks.Length == 0) + return; + + var items = new List(); + var menu = new GenericMenu(); + BuildTrackContextMenu(items, tracks, mousePosition); + MenuItemActionBase.BuildMenu(menu, items); + menu.ShowAsContext(); + } + + public static void ShowItemContextMenu(Vector2 mousePosition, TimelineClip[] clips, IMarker[] markers) + { + var menu = new GenericMenu(); + var items = new List(); + BuildItemContextMenu(items, mousePosition, clips, markers); + MenuItemActionBase.BuildMenu(menu, items); + menu.ShowAsContext(); + } + + internal static void BuildItemContextMenu(List items, Vector2 mousePosition, TimelineClip[] clips, IMarker[] markers) + { + var state = TimelineWindow.instance.state; + + TimelineAction.GetMenuEntries(TimelineAction.MenuActions, mousePosition, items); + ItemAction.GetMenuEntries(clips, items); + ItemAction.GetMenuEntries(markers, items); + + if (clips.Length > 0) + AddMarkerMenuCommands(items, clips.Select(c => c.parentTrack).Distinct().ToList(), TimelineHelpers.GetCandidateTime(state, mousePosition)); + } + + internal static void BuildNewTracksContextMenu(List menuItems, ICollection parentTracks, WindowState state, string format = null) + { + if (parentTracks == null) + parentTracks = new TrackAsset[0]; + + if (string.IsNullOrEmpty(format)) + format = "{0}"; + + // Add Group or SubGroup + var title = string.Format(format, parentTracks.Any(t => t != null) ? Styles.trackSubGroup : Styles.trackGroup); + var menuState = MenuActionDisplayState.Visible; + if (state.editSequence.isReadOnly) + menuState = MenuActionDisplayState.Disabled; + if (parentTracks.Any() && parentTracks.Any(t => t != null && t.lockedInHierarchy)) + menuState = MenuActionDisplayState.Disabled; + + GenericMenu.MenuFunction command = () => + { + SelectionManager.Clear(); + if (parentTracks.Count == 0) + Selection.Add(TimelineHelpers.CreateTrack(null, title)); + + foreach (var parentTrack in parentTracks) + Selection.Add(TimelineHelpers.CreateTrack(parentTrack, title)); + + TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved); + }; + + menuItems.Add( + new MenuActionItem() + { + category = string.Empty, + entryName = title, + shortCut = string.Empty, + isActiveInMode = true, + isChecked = false, + priority = MenuOrder.AddGroupItemStart, + state = menuState, + callback = command + } + ); + + + var allTypes = TypeUtility.AllTrackTypes().Where(x => x != typeof(GroupTrack) && !TypeUtility.IsHiddenInMenu(x)).ToList(); + + int builtInPriority = MenuOrder.AddTrackItemStart; + int customPriority = MenuOrder.AddCustomTrackItemStart; + foreach (var trackType in allTypes) + { + var trackItemType = trackType; + + command = () => + { + SelectionManager.Clear(); + + if (parentTracks.Count == 0) + SelectionManager.Add(TimelineHelpers.CreateTrack((Type)trackItemType, null)); + + foreach (var parentTrack in parentTracks) + SelectionManager.Add(TimelineHelpers.CreateTrack((Type)trackItemType, parentTrack)); + }; + + menuItems.Add( + new MenuActionItem() + { + category = TimelineHelpers.GetTrackCategoryName(trackType), + entryName = string.Format(format, TimelineHelpers.GetTrackMenuName(trackItemType)), + shortCut = string.Empty, + isActiveInMode = true, + isChecked = false, + priority = TypeUtility.IsBuiltIn(trackType) ? builtInPriority++ : customPriority++, + state = menuState, + callback = command + } + ); + } + } + + internal static void BuildMarkerHeaderContextMenu(List menu, Vector2? mousePosition, WindowState state) + { + TimelineAction.GetMenuEntries(MarkerHeaderMenuItems, null, menu); + + var timeline = state.editSequence.asset; + var time = TimelineHelpers.GetCandidateTime(state, mousePosition); + var enabled = timeline.markerTrack == null || !timeline.markerTrack.lockedInHierarchy; + + var addMarkerCommand = new Action + ( + (type, obj) => AddSingleMarkerCallback(type, time, timeline, state.editSequence.director, obj) + ); + + AddMarkerMenuCommands(menu, new TrackAsset[] {timeline.markerTrack}, addMarkerCommand, enabled); + } + + internal static void BuildTrackContextMenu(List items, TrackAsset[] tracks, Vector2? mousePosition) + { + if (tracks == null || tracks.Length == 0) + return; + + TimelineAction.GetMenuEntries(TimelineAction.MenuActions, mousePosition, items); + TrackAction.GetMenuEntries(TimelineWindow.instance.state, mousePosition, tracks, items); + AddLayeredTrackCommands(items, tracks); + + var first = tracks.First().GetType(); + var allTheSame = tracks.All(t => t.GetType() == first); + if (allTheSame) + { + if (first != typeof(GroupTrack)) + { + var candidateTime = TimelineHelpers.GetCandidateTime(TimelineWindow.instance.state, mousePosition, tracks); + AddClipMenuCommands(items, tracks, candidateTime); + AddMarkerMenuCommands(items, tracks, candidateTime); + } + else + { + BuildNewTracksContextMenu(items, tracks, TimelineWindow.instance.state, Styles.addItemTemplate); + } + } + } + + static void AddLayeredTrackCommands(List menuItems, ICollection tracks) + { + if (tracks.Count == 0) + return; + + var layeredType = tracks.First().GetType(); + // animation tracks have a special menu. + if (layeredType == typeof(AnimationTrack)) + return; + + // must implement ILayerable + if (!typeof(UnityEngine.Timeline.ILayerable).IsAssignableFrom(layeredType)) + return; + + if (tracks.Any(t => t.GetType() != layeredType)) + return; + + // only supported on the master track no nesting. + if (tracks.Any(t => t.isSubTrack)) + return; + + var enabled = tracks.All(t => t != null && !t.lockedInHierarchy) && !TimelineWindow.instance.state.editSequence.isReadOnly; + int priority = MenuOrder.TrackAddMenu.AddLayerTrack; + GenericMenu.MenuFunction menuCallback = () => + { + foreach (var track in tracks) + TimelineHelpers.CreateTrack(layeredType, track, string.Format(Styles.layerName, track.GetChildTracks().Count() + 1)); + }; + + var entryName = Styles.addTrackLayer; + menuItems.Add( + new MenuActionItem() + { + category = string.Empty, + entryName = entryName, + shortCut = string.Empty, + isActiveInMode = true, + isChecked = false, + priority = priority++, + state = enabled ? MenuActionDisplayState.Visible : MenuActionDisplayState.Disabled, + callback = menuCallback + } + ); + } + + static void AddClipMenuCommands(List menuItems, ICollection tracks, double candidateTime) + { + if (!tracks.Any()) + return; + + var trackAsset = tracks.First(); + var trackType = trackAsset.GetType(); + if (tracks.Any(t => t.GetType() != trackType)) + return; + + var enabled = tracks.All(t => t != null && !t.lockedInHierarchy) && !TimelineWindow.instance.state.editSequence.isReadOnly; + var assetTypes = TypeUtility.GetPlayableAssetsHandledByTrack(trackType); + var visibleAssetTypes = TypeUtility.GetVisiblePlayableAssetsHandledByTrack(trackType); + + // skips the name if there is only a single type + var commandNameTemplate = assetTypes.Count() == 1 ? Styles.addSingleItemFromAssetTemplate : Styles.addItemFromAssetTemplate; + int builtInPriority = MenuOrder.AddClipItemStart; + int customPriority = MenuOrder.AddCustomClipItemStart; + foreach (var assetType in assetTypes) + { + var assetItemType = assetType; + var category = TimelineHelpers.GetItemCategoryName(assetType); + Action onObjectChanged = obj => + { + if (obj != null) + { + foreach (var t in tracks) + { + TimelineHelpers.CreateClipOnTrack(assetItemType, obj, t, candidateTime); + } + } + }; + + foreach (var objectReference in TypeUtility.ObjectReferencesForType(assetType)) + { + var isSceneReference = objectReference.isSceneReference; + var dataType = objectReference.type; + GenericMenu.MenuFunction menuCallback = () => + { + ObjectSelector.get.Show(null, dataType, null, isSceneReference, null, (obj) => onObjectChanged(obj), null); + ObjectSelector.get.titleContent = EditorGUIUtility.TrTextContent(string.Format(Styles.typeSelectorTemplate, TypeUtility.GetDisplayName(dataType))); + }; + + menuItems.Add( + new MenuActionItem() + { + category = category, + entryName = string.Format(commandNameTemplate, TypeUtility.GetDisplayName(assetType), TypeUtility.GetDisplayName(objectReference.type)), + shortCut = string.Empty, + isActiveInMode = true, + isChecked = false, + priority = TypeUtility.IsBuiltIn(assetType) ? builtInPriority++ : customPriority++, + state = enabled ? MenuActionDisplayState.Visible : MenuActionDisplayState.Disabled, + callback = menuCallback + } + ); + } + } + + foreach (var assetType in visibleAssetTypes) + { + var assetItemType = assetType; + var category = TimelineHelpers.GetItemCategoryName(assetType); + var commandName = string.Format(Styles.addItemTemplate, TypeUtility.GetDisplayName(assetType)); + GenericMenu.MenuFunction command = () => + { + foreach (var t in tracks) + { + TimelineHelpers.CreateClipOnTrack(assetItemType, t, candidateTime); + } + }; + + menuItems.Add( + new MenuActionItem() + { + category = category, + entryName = commandName, + shortCut = string.Empty, + isActiveInMode = true, + isChecked = false, + priority = TypeUtility.IsBuiltIn(assetItemType) ? builtInPriority++ : customPriority++, + state = enabled ? MenuActionDisplayState.Visible : MenuActionDisplayState.Disabled, + callback = command + } + ); + } + } + + static void AddMarkerMenuCommands(List menu, IEnumerable markerTypes, Action addMarkerCommand, bool enabled) + { + int builtInPriority = MenuOrder.AddMarkerItemStart; + int customPriority = MenuOrder.AddCustomMarkerItemStart; + foreach (var markerType in markerTypes) + { + var markerItemType = markerType; + string category = TimelineHelpers.GetItemCategoryName(markerItemType); + menu.Add( + new MenuActionItem() + { + category = category, + entryName = string.Format(Styles.addItemTemplate, TypeUtility.GetDisplayName(markerType)), + shortCut = string.Empty, + isActiveInMode = true, + isChecked = false, + priority = TypeUtility.IsBuiltIn(markerType) ? builtInPriority++ : customPriority++, + state = enabled ? MenuActionDisplayState.Visible : MenuActionDisplayState.Disabled, + callback = () => addMarkerCommand(markerItemType, null) + } + ); + + foreach (var objectReference in TypeUtility.ObjectReferencesForType(markerType)) + { + var isSceneReference = objectReference.isSceneReference; + GenericMenu.MenuFunction menuCallback = () => + { + var dataType = markerItemType; + ObjectSelector.get.Show(null, dataType, null, isSceneReference, null, (obj) => addMarkerCommand(markerItemType, obj), null); + ObjectSelector.get.titleContent = EditorGUIUtility.TrTextContent(string.Format(Styles.typeSelectorTemplate, TypeUtility.GetDisplayName(dataType))); + }; + + menu.Add( + new MenuActionItem() + { + category = TimelineHelpers.GetItemCategoryName(markerItemType), + entryName = string.Format(Styles.addItemFromAssetTemplate, TypeUtility.GetDisplayName(markerType), TypeUtility.GetDisplayName(objectReference.type)), + shortCut = string.Empty, + isActiveInMode = true, + isChecked = false, + priority = TypeUtility.IsBuiltIn(markerType) ? builtInPriority++ : customPriority++, + state = enabled ? MenuActionDisplayState.Visible : MenuActionDisplayState.Disabled, + callback = menuCallback + } + ); + } + } + } + + static void AddMarkerMenuCommands(List menuItems, ICollection tracks, double candidateTime) + { + if (tracks.Count == 0) + return; + + var enabled = tracks.All(t => !t.lockedInHierarchy) && !TimelineWindow.instance.state.editSequence.isReadOnly; + var addMarkerCommand = new Action((type, obj) => AddMarkersCallback(tracks, type, candidateTime, obj)); + + AddMarkerMenuCommands(menuItems, tracks, addMarkerCommand, enabled); + } + + static void AddMarkerMenuCommands(List menuItems, ICollection tracks, Action command, bool enabled) + { + var markerTypes = TypeUtility.GetBuiltInMarkerTypes().Union(TypeUtility.GetUserMarkerTypes()); + if (tracks != null) + markerTypes = markerTypes.Where(x => tracks.All(track => (track == null) || TypeUtility.DoesTrackSupportMarkerType(track, x))); // null track indicates marker track to be created + + AddMarkerMenuCommands(menuItems, markerTypes, command, enabled); + } + + static void AddMarkersCallback(ICollection targets, Type markerType, double time, Object obj) + { + SelectionManager.Clear(); + foreach (var target in targets) + { + var marker = TimelineHelpers.CreateMarkerOnTrack(markerType, obj, target, time); + SelectionManager.Add(marker); + } + TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved); + } + + static void AddSingleMarkerCallback(Type markerType, double time, TimelineAsset timeline, PlayableDirector director, Object assignableObject) + { + timeline.CreateMarkerTrack(); + var markerTrack = timeline.markerTrack; + + SelectionManager.Clear(); + var marker = TimelineHelpers.CreateMarkerOnTrack(markerType, assignableObject, markerTrack, time); + SelectionManager.Add(marker); + + if (typeof(INotification).IsAssignableFrom(markerType) && director != null) + { + if (director != null && director.GetGenericBinding(markerTrack) == null) + director.SetGenericBinding(markerTrack, director.gameObject); + } + + TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved); + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/Menus/TimelineContextMenu.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/Menus/TimelineContextMenu.cs.meta new file mode 100644 index 0000000..adfa86d --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/Menus/TimelineContextMenu.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: de86b4ed8106fd84a8bc2f5d69798d53 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/TimelineActions.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/TimelineActions.cs new file mode 100644 index 0000000..25e72e8 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/TimelineActions.cs @@ -0,0 +1,946 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using UnityEditor.ShortcutManagement; +using UnityEngine; +using UnityEngine.Timeline; +using MenuEntryPair = System.Collections.Generic.KeyValuePair; + +namespace UnityEditor.Timeline +{ + [ActiveInMode(TimelineModes.Default)] + abstract class TimelineAction : MenuItemActionBase + { + public abstract bool Execute(WindowState state); + + public virtual MenuActionDisplayState GetDisplayState(WindowState state) + { + return MenuActionDisplayState.Visible; + } + + public virtual bool IsChecked(WindowState state) + { + return false; + } + + protected string GetDisplayName(WindowState state) + { + return menuName; + } + + bool CanExecute(WindowState state) + { + return GetDisplayState(state) == MenuActionDisplayState.Visible; + } + + public static void Invoke(WindowState state) where T : TimelineAction + { + var action = AllActions.FirstOrDefault(x => x.GetType() == typeof(T)); + if (action != null && action.CanExecute(state)) + action.Execute(state); + } + + // an instance of all TimelineActions + public static readonly TimelineAction[] AllActions = GetActionsOfType(typeof(TimelineAction)).Select(x => (TimelineAction)x.GetConstructors()[0].Invoke(null)).ToArray(); + + // an instance of all TimelineActions that should appear in a regular contextMenu + public static readonly TimelineAction[] MenuActions = AllActions.Where(a => a.showInMenu && !(a is MarkerHeaderAction)).ToArray(); + + public static void GetMenuEntries(IEnumerable actions, Vector2? mousePos, List items) + { + var state = TimelineWindow.instance.state; + var mode = TimelineWindow.instance.currentMode.mode; + + foreach (var action in actions) + { + var actionItem = action; + action.mousePosition = mousePos; + items.Add( + new MenuActionItem() + { + category = action.category, + entryName = action.GetDisplayName(state), + shortCut = action.shortCut, + isChecked = action.IsChecked(state), + isActiveInMode = IsActionActiveInMode(action, mode), + priority = action.priority, + state = action.GetDisplayState(state), + callback = () => + { + actionItem.mousePosition = mousePos; + actionItem.Execute(state); + actionItem.mousePosition = null; + } + } + ); + action.mousePosition = null; + } + } + + public static bool HandleShortcut(WindowState state, Event evt) + { + if (EditorGUI.IsEditingTextField()) + return false; + + foreach (var action in AllActions) + { + var attr = action.GetType().GetCustomAttributes(typeof(ShortcutAttribute), true); + + foreach (ShortcutAttribute shortcut in attr) + { + if (shortcut.MatchesEvent(evt)) + { + if (s_ShowActionTriggeredByShortcut) + Debug.Log(action.GetType().Name); + + if (!IsActionActiveInMode(action, TimelineWindow.instance.currentMode.mode)) + return false; + + var handled = action.Execute(state); + if (handled) + return true; + } + } + } + + return false; + } + + protected static bool DoInternal(Type t, WindowState state) + { + var action = (TimelineAction)t.GetConstructors()[0].Invoke(null); + + if (action.CanExecute(state)) + return action.Execute(state); + + return false; + } + } + + // indicates the action only applies to the marker header menu + abstract class MarkerHeaderAction : TimelineAction + { + } + + + [MenuEntry("Copy", MenuOrder.TimelineAction.Copy)] + [Shortcut("Main Menu/Edit/Copy", EventCommandNames.Copy)] + class CopyAction : TimelineAction + { + public static bool Do(WindowState state) + { + return DoInternal(typeof(CopyAction), state); + } + + public override MenuActionDisplayState GetDisplayState(WindowState state) + { + return SelectionManager.Count() > 0 ? MenuActionDisplayState.Visible : MenuActionDisplayState.Disabled; + } + + public override bool Execute(WindowState state) + { + TimelineEditor.clipboard.Clear(); + + var clips = SelectionManager.SelectedClips().ToArray(); + if (clips.Length > 0) + { + ItemAction.Invoke(state, clips); + } + var markers = SelectionManager.SelectedMarkers().ToArray(); + if (markers.Length > 0) + { + ItemAction.Invoke(state, markers); + } + var tracks = SelectionManager.SelectedTracks().ToArray(); + if (tracks.Length > 0) + { + CopyTracksToClipboard.Do(state, tracks); + } + + return true; + } + } + + [MenuEntry("Paste", MenuOrder.TimelineAction.Paste)] + [Shortcut("Main Menu/Edit/Paste", EventCommandNames.Paste)] + class PasteAction : TimelineAction + { + public static bool Do(WindowState state) + { + return DoInternal(typeof(PasteAction), state); + } + + public override MenuActionDisplayState GetDisplayState(WindowState state) + { + return CanPaste(state) ? MenuActionDisplayState.Visible : MenuActionDisplayState.Disabled; + } + + public override bool Execute(WindowState state) + { + if (!CanPaste(state)) + return false; + + PasteItems(state, mousePosition); + PasteTracks(state); + + state.Refresh(); + + mousePosition = null; + return true; + } + + bool CanPaste(WindowState state) + { + var copiedItems = TimelineEditor.clipboard.GetCopiedItems().ToList(); + + if (!copiedItems.Any()) + return TimelineEditor.clipboard.GetTracks().Any(); + + return CanPasteItems(copiedItems, state, mousePosition); + } + + static bool CanPasteItems(ICollection itemsGroups, WindowState state, Vector2? mousePosition) + { + var hasItemsCopiedFromMultipleTracks = itemsGroups.Count > 1; + var allItemsCopiedFromCurrentAsset = itemsGroups.All(x => x.targetTrack.timelineAsset == state.editSequence.asset); + var hasUsedShortcut = mousePosition == null; + var anySourceLocked = itemsGroups.Any(x => x.targetTrack != null && x.targetTrack.lockedInHierarchy); + + var targetTrack = GetPickedTrack(); + if (targetTrack == null) + targetTrack = SelectionManager.SelectedTracks().FirstOrDefault(); + + //do not paste if the user copied items from another timeline + //if the copied items comes from > 1 track (since we do not know where to paste the copied items) + //or if a keyboard shortcut was used (since the user will not see the paste result) + if (!allItemsCopiedFromCurrentAsset) + { + var isSelectedTrackInCurrentAsset = targetTrack != null && targetTrack.timelineAsset == state.editSequence.asset; + if (hasItemsCopiedFromMultipleTracks || (hasUsedShortcut && !isSelectedTrackInCurrentAsset)) + return false; + } + + if (hasUsedShortcut) + return !anySourceLocked; // copy/paste to same track + + if (hasItemsCopiedFromMultipleTracks) + { + //do not paste if the track which received the paste action does not contain a copied clip + return !anySourceLocked && itemsGroups.Select(x => x.targetTrack).Contains(targetTrack); + } + + var copiedItems = itemsGroups.SelectMany(i => i.items); + return IsTrackValidForItems(targetTrack, copiedItems); + } + + static void PasteItems(WindowState state, Vector2? mousePosition) + { + var copiedItems = TimelineEditor.clipboard.GetCopiedItems().ToList(); + var numberOfUniqueParentsInClipboard = copiedItems.Count(); + + if (numberOfUniqueParentsInClipboard == 0) return; + List newItems; + + //if the copied items were on a single parent, then use the mouse position to get the parent OR the original parent + if (numberOfUniqueParentsInClipboard == 1) + { + var itemsGroup = copiedItems.First(); + TrackAsset target = null; + if (mousePosition.HasValue) + target = GetPickedTrack(); + if (target == null) + target = FindSuitableParentForSingleTrackPasteWithoutMouse(itemsGroup); + + var candidateTime = TimelineHelpers.GetCandidateTime(state, mousePosition, target); + newItems = TimelineHelpers.DuplicateItemsUsingCurrentEditMode(state, TimelineEditor.clipboard.exposedPropertyTable, TimelineEditor.inspectedDirector, itemsGroup, target, candidateTime, "Paste Items").ToList(); + } + //if copied items were on multiple parents, then the destination parents are the same as the original parents + else + { + var time = TimelineHelpers.GetCandidateTime(state, mousePosition, copiedItems.Select(c => c.targetTrack).ToArray()); + newItems = TimelineHelpers.DuplicateItemsUsingCurrentEditMode(state, TimelineEditor.clipboard.exposedPropertyTable, TimelineEditor.inspectedDirector, copiedItems, time, "Paste Items").ToList(); + } + + TimelineHelpers.FrameItems(state, newItems); + SelectionManager.RemoveTimelineSelection(); + foreach (var item in newItems) + { + SelectionManager.Add(item); + } + } + + static TrackAsset FindSuitableParentForSingleTrackPasteWithoutMouse(ItemsPerTrack itemsGroup) + { + var groupParent = itemsGroup.targetTrack; //set a main parent in the clipboard + var selectedTracks = SelectionManager.SelectedTracks(); + + if (selectedTracks.Contains(groupParent)) + { + return groupParent; + } + + //find a selected track suitable for all items + var itemsToPaste = itemsGroup.items; + var compatibleTrack = selectedTracks.FirstOrDefault(t => IsTrackValidForItems(t, itemsToPaste)); + return compatibleTrack != null ? compatibleTrack : groupParent; + } + + static bool IsTrackValidForItems(TrackAsset track, IEnumerable items) + { + if (track == null || track.lockedInHierarchy) return false; + return items.All(i => i.IsCompatibleWithTrack(track)); + } + + static TrackAsset GetPickedTrack() + { + var rowGUI = PickerUtils.pickedElements.OfType().FirstOrDefault(); + if (rowGUI != null) + return rowGUI.asset; + + return null; + } + + static void PasteTracks(WindowState state) + { + var trackData = TimelineEditor.clipboard.GetTracks().ToList(); + if (trackData.Any()) + { + SelectionManager.RemoveTimelineSelection(); + } + + foreach (var track in trackData) + { + var newTrack = track.item.Duplicate(TimelineEditor.clipboard.exposedPropertyTable, TimelineEditor.inspectedDirector, TimelineEditor.inspectedAsset); + SelectionManager.Add(newTrack); + foreach (var childTrack in newTrack.GetFlattenedChildTracks()) + { + SelectionManager.Add(childTrack); + } + + if (track.parent != null && track.parent.timelineAsset == state.editSequence.asset) + { + TrackExtensions.ReparentTracks(new List { newTrack }, track.parent, track.item); + } + } + } + } + + [MenuEntry("Duplicate", MenuOrder.TimelineAction.Duplicate)] + [Shortcut("Main Menu/Edit/Duplicate", EventCommandNames.Duplicate)] + class DuplicateAction : TimelineAction + { + public override bool Execute(WindowState state) + { + return Execute(state, (item1, item2) => ItemsUtils.TimeGapBetweenItems(item1, item2, state)); + } + + internal bool Execute(WindowState state, Func gapBetweenItems) + { + var selectedItems = SelectionManager.SelectedItems().ToItemsPerTrack().ToList(); + if (selectedItems.Any()) + { + var requestedTime = CalculateDuplicateTime(selectedItems, gapBetweenItems); + var duplicatedItems = TimelineHelpers.DuplicateItemsUsingCurrentEditMode(state, TimelineEditor.inspectedDirector, TimelineEditor.inspectedDirector, selectedItems, requestedTime, "Duplicate Items"); + + TimelineHelpers.FrameItems(state, duplicatedItems); + SelectionManager.RemoveTimelineSelection(); + foreach (var item in duplicatedItems) + SelectionManager.Add(item); + } + + var tracks = SelectionManager.SelectedTracks().ToArray(); + if (tracks.Length > 0) + TrackAction.Invoke(state, tracks); + + state.Refresh(); + return true; + } + + static double CalculateDuplicateTime(IEnumerable duplicatedItems, Func gapBetweenItems) + { + //Find the end time of the rightmost item + var itemsOnTracks = duplicatedItems.SelectMany(i => i.targetTrack.GetItems()).ToList(); + var time = itemsOnTracks.Max(i => i.end); + + //From all the duplicated items, select the leftmost items + var firstDuplicatedItems = duplicatedItems.Select(i => i.leftMostItem); + var leftMostDuplicatedItems = firstDuplicatedItems.OrderBy(i => i.start).GroupBy(i => i.start).FirstOrDefault(); + if (leftMostDuplicatedItems == null) return 0.0; + + foreach (var leftMostItem in leftMostDuplicatedItems) + { + var siblings = leftMostItem.parentTrack.GetItems(); + var rightMostSiblings = siblings.OrderByDescending(i => i.end).GroupBy(i => i.end).FirstOrDefault(); + if (rightMostSiblings == null) continue; + + foreach (var sibling in rightMostSiblings) + time = Math.Max(time, sibling.end + gapBetweenItems(leftMostItem, sibling)); + } + + return time; + } + } + + [MenuEntry("Delete", MenuOrder.TimelineAction.Delete)] + [Shortcut("Main Menu/Edit/Delete", EventCommandNames.Delete)] + [ShortcutPlatformOverride(RuntimePlatform.OSXEditor, KeyCode.Backspace, ShortcutModifiers.Action)] + [ActiveInMode(TimelineModes.Default)] + class DeleteAction : TimelineAction + { + public override MenuActionDisplayState GetDisplayState(WindowState state) + { + return CanDelete(state) ? MenuActionDisplayState.Visible : MenuActionDisplayState.Disabled; + } + + static bool CanDelete(WindowState state) + { + if (state.editSequence.isReadOnly) + return false; + // All() returns true when empty + return SelectionManager.SelectedTracks().All(x => !x.lockedInHierarchy) && + SelectionManager.SelectedItems().All(x => x.parentTrack == null || !x.parentTrack.lockedInHierarchy); + } + + public override bool Execute(WindowState state) + { + if (SelectionManager.GetCurrentInlineEditorCurve() != null) + return false; + + if (!CanDelete(state)) + return false; + + var selectedItems = SelectionManager.SelectedItems(); + DeleteItems(selectedItems); + + var tracks = SelectionManager.SelectedTracks().ToArray(); + if (tracks.Any()) + TrackAction.Invoke(state, tracks); + + state.Refresh(); + return selectedItems.Any() || tracks.Length > 0; + } + + internal static void DeleteItems(IEnumerable items) + { + var tracks = items.GroupBy(c => c.parentTrack); + + foreach (var track in tracks) + TimelineUndo.PushUndo(track.Key, "Delete Items"); + + TimelineAnimationUtilities.UnlinkAnimationWindowFromClips(items.OfType().Select(i => i.clip)); + + EditMode.PrepareItemsDelete(ItemsUtils.ToItemsPerTrack(items)); + EditModeUtils.Delete(items); + + SelectionManager.RemoveAllClips(); + } + } + + [MenuEntry("Match Content", MenuOrder.TimelineAction.MatchContent)] + [Shortcut(Shortcuts.Timeline.matchContent)] + class MatchContent : TimelineAction + { + public override MenuActionDisplayState GetDisplayState(WindowState state) + { + var clips = SelectionManager.SelectedClips().ToArray(); + + if (!clips.Any() || SelectionManager.GetCurrentInlineEditorCurve() != null) + return MenuActionDisplayState.Hidden; + + return clips.Any(TimelineHelpers.HasUsableAssetDuration) + ? MenuActionDisplayState.Visible + : MenuActionDisplayState.Disabled; + } + + public override bool Execute(WindowState state) + { + if (SelectionManager.GetCurrentInlineEditorCurve() != null) + return false; + + var clips = SelectionManager.SelectedClips().ToArray(); + return clips.Length > 0 && ClipModifier.MatchContent(clips); + } + } + + [Shortcut(Shortcuts.Timeline.play)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class PlayTimelineAction : TimelineAction + { + public override bool Execute(WindowState state) + { + var currentState = state.playing; + state.SetPlaying(!currentState); + return true; + } + } + + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class SelectAllAction : TimelineAction + { + public override bool Execute(WindowState state) + { + // otherwise select all tracks. + SelectionManager.Clear(); + state.GetWindow().allTracks.ForEach(x => SelectionManager.Add(x.track)); + + return true; + } + } + + [Shortcut(Shortcuts.Timeline.previousFrame)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class PreviousFrameAction : TimelineAction + { + public override bool Execute(WindowState state) + { + state.editSequence.frame--; + return true; + } + } + + [Shortcut(Shortcuts.Timeline.nextFrame)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class NextFrameAction : TimelineAction + { + public override bool Execute(WindowState state) + { + state.editSequence.frame++; + return true; + } + } + + [Shortcut(Shortcuts.Timeline.frameAll)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class FrameAllAction : TimelineAction + { + public override bool Execute(WindowState state) + { + var inlineCurveEditor = SelectionManager.GetCurrentInlineEditorCurve(); + if (inlineCurveEditor != null && inlineCurveEditor.inlineCurvesSelected) + { + FrameSelectedAction.FrameInlineCurves(inlineCurveEditor, state, false); + return true; + } + + if (state.IsEditingASubItem()) + return false; + + var w = state.GetWindow(); + if (w == null || w.treeView == null) + return false; + + var visibleTracks = w.treeView.visibleTracks.ToList(); + if (state.editSequence.asset != null && state.editSequence.asset.markerTrack != null) + visibleTracks.Add(state.editSequence.asset.markerTrack); + + if (visibleTracks.Count == 0) + return false; + + var startTime = float.MaxValue; + var endTime = float.MinValue; + + foreach (var t in visibleTracks) + { + if (t == null) + continue; + + double trackStart, trackEnd; + t.GetItemRange(out trackStart, out trackEnd); + startTime = Mathf.Min(startTime, (float)trackStart); + endTime = Mathf.Max(endTime, (float)(trackEnd)); + } + + if (startTime != float.MinValue) + { + FrameSelectedAction.FrameRange(startTime, endTime, state); + return true; + } + + return false; + } + } + + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class FrameSelectedAction : TimelineAction + { + public static void FrameRange(float startTime, float endTime, WindowState state) + { + if (startTime > endTime) + { + return; + } + + var halfDuration = endTime - Math.Max(0.0f, startTime); + + if (halfDuration > 0.0f) + { + state.SetTimeAreaShownRange(Mathf.Max(-10.0f, startTime - (halfDuration * 0.1f)), + endTime + (halfDuration * 0.1f)); + } + else + { + // start == end + // keep the zoom level constant, only pan the time area to center the item + var currentRange = state.timeAreaShownRange.y - state.timeAreaShownRange.x; + state.SetTimeAreaShownRange(startTime - currentRange / 2, startTime + currentRange / 2); + } + + TimelineZoomManipulator.InvalidateWheelZoom(); + state.Evaluate(); + } + + public override bool Execute(WindowState state) + { + var inlineCurveEditor = SelectionManager.GetCurrentInlineEditorCurve(); + if (inlineCurveEditor != null && inlineCurveEditor.inlineCurvesSelected) + { + FrameInlineCurves(inlineCurveEditor, state, true); + return true; + } + + if (state.IsEditingASubItem()) + return false; + + if (SelectionManager.Count() == 0) + return false; + + var startTime = float.MaxValue; + var endTime = float.MinValue; + + var clips = SelectionManager.SelectedClipGUI(); + var markers = SelectionManager.SelectedMarkers(); + if (!clips.Any() && !markers.Any()) + return false; + + foreach (var c in clips) + { + startTime = Mathf.Min(startTime, (float)c.clip.start); + endTime = Mathf.Max(endTime, (float)c.clip.end); + if (c.clipCurveEditor != null) + { + c.clipCurveEditor.FrameClip(); + } + } + + foreach (var marker in markers) + { + startTime = Mathf.Min(startTime, (float)marker.time); + endTime = Mathf.Max(endTime, (float)marker.time); + } + + FrameRange(startTime, endTime, state); + + return true; + } + + public static void FrameInlineCurves(IClipCurveEditorOwner curveEditorOwner, WindowState state, bool selectionOnly) + { + var curveEditor = curveEditorOwner.clipCurveEditor.curveEditor; + var frameBounds = selectionOnly ? curveEditor.GetSelectionBounds() : curveEditor.GetClipBounds(); + + var clipGUI = curveEditorOwner as TimelineClipGUI; + var areaOffset = 0.0f; + + if (clipGUI != null) + { + areaOffset = (float)Math.Max(0.0, clipGUI.clip.FromLocalTimeUnbound(0.0)); + + var timeScale = (float)clipGUI.clip.timeScale; // Note: The getter for clip.timeScale is guaranteed to never be zero. + + // Apply scaling + var newMin = frameBounds.min.x / timeScale; + var newMax = (frameBounds.max.x - frameBounds.min.x) / timeScale + newMin; + + frameBounds.SetMinMax( + new Vector3(newMin, frameBounds.min.y, frameBounds.min.z), + new Vector3(newMax, frameBounds.max.y, frameBounds.max.z)); + } + + curveEditor.Frame(frameBounds, true, true); + + var area = curveEditor.shownAreaInsideMargins; + area.x += areaOffset; + + var curveStart = curveEditorOwner.clipCurveEditor.dataSource.start; + FrameRange(curveStart + frameBounds.min.x, curveStart + frameBounds.max.x, state); + } + } + + [Shortcut(Shortcuts.Timeline.previousKey)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class PrevKeyAction : TimelineAction + { + public override bool Execute(WindowState state) + { + var keyTraverser = new Utilities.KeyTraverser(state.editSequence.asset, 0.01f / state.referenceSequence.frameRate); + var time = keyTraverser.GetPrevKey((float)state.editSequence.time, state.dirtyStamp); + if (time != state.editSequence.time) + { + state.editSequence.time = time; + } + + return true; + } + } + + [Shortcut(Shortcuts.Timeline.nextKey)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class NextKeyAction : TimelineAction + { + public override bool Execute(WindowState state) + { + var keyTraverser = new Utilities.KeyTraverser(state.editSequence.asset, 0.01f / state.referenceSequence.frameRate); + var time = keyTraverser.GetNextKey((float)state.editSequence.time, state.dirtyStamp); + if (time != state.editSequence.time) + { + state.editSequence.time = time; + } + + return true; + } + } + + [Shortcut(Shortcuts.Timeline.goToStart)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class GotoStartAction : TimelineAction + { + public override bool Execute(WindowState state) + { + state.editSequence.time = 0.0f; + state.EnsurePlayHeadIsVisible(); + + return true; + } + } + + [Shortcut(Shortcuts.Timeline.goToEnd)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class GotoEndAction : TimelineAction + { + public override bool Execute(WindowState state) + { + state.editSequence.time = state.editSequence.duration; + state.EnsurePlayHeadIsVisible(); + + return true; + } + } + + [Shortcut(Shortcuts.Timeline.zoomIn)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class ZoomIn : TimelineAction + { + public override bool Execute(WindowState state) + { + TimelineZoomManipulator.Instance.DoZoom(1.15f, state); + return true; + } + } + + [Shortcut(Shortcuts.Timeline.zoomOut)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class ZoomOut : TimelineAction + { + public override bool Execute(WindowState state) + { + TimelineZoomManipulator.Instance.DoZoom(0.85f, state); + return true; + } + } + + [Shortcut(Shortcuts.Timeline.collapseGroup)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class CollapseGroup : TimelineAction + { + public override bool Execute(WindowState state) + { + return KeyboardNavigation.CollapseGroup(state); + } + } + + [Shortcut(Shortcuts.Timeline.unCollapseGroup)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class UnCollapseGroup : TimelineAction + { + public override bool Execute(WindowState state) + { + return KeyboardNavigation.UnCollapseGroup(state); + } + } + + [Shortcut(Shortcuts.Timeline.selectLeftItem)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class SelectLeftClip : TimelineAction + { + public override bool Execute(WindowState state) + { + // Switches to track header if no left track exists + return KeyboardNavigation.SelectLeftItem(state); + } + } + + [Shortcut(Shortcuts.Timeline.selectRightItem)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class SelectRightClip : TimelineAction + { + public override bool Execute(WindowState state) + { + return KeyboardNavigation.SelectRightItem(state); + } + } + + [Shortcut(Shortcuts.Timeline.selectUpItem)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class SelectUpClip : TimelineAction + { + public override bool Execute(WindowState state) + { + return KeyboardNavigation.SelectUpItem(state); + } + } + + [Shortcut(Shortcuts.Timeline.selectUpTrack)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class SelectUpTrack : TimelineAction + { + public override bool Execute(WindowState state) + { + return KeyboardNavigation.SelectUpTrack(); + } + } + + [Shortcut(Shortcuts.Timeline.selectDownItem)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class SelectDownClip : TimelineAction + { + public override bool Execute(WindowState state) + { + return KeyboardNavigation.SelectDownItem(state); + } + } + + [Shortcut(Shortcuts.Timeline.selectDownTrack)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class SelectDownTrack : TimelineAction + { + public override bool Execute(WindowState state) + { + if (!KeyboardNavigation.ClipAreaActive() && !KeyboardNavigation.TrackHeadActive()) + return KeyboardNavigation.FocusFirstVisibleItem(state); + else + return KeyboardNavigation.SelectDownTrack(); + } + } + + [Shortcut(Shortcuts.Timeline.multiSelectLeft)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class MultiselectLeftClip : TimelineAction + { + public override bool Execute(WindowState state) + { + return KeyboardNavigation.SelectLeftItem(state, true); + } + } + + [Shortcut(Shortcuts.Timeline.multiSelectRight)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class MultiselectRightClip : TimelineAction + { + public override bool Execute(WindowState state) + { + return KeyboardNavigation.SelectRightItem(state, true); + } + } + + [Shortcut(Shortcuts.Timeline.multiSelectUp)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class MultiselectUpTrack : TimelineAction + { + public override bool Execute(WindowState state) + { + return KeyboardNavigation.SelectUpTrack(true); + } + } + + [Shortcut(Shortcuts.Timeline.multiSelectDown)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class MultiselectDownTrack : TimelineAction + { + public override bool Execute(WindowState state) + { + return KeyboardNavigation.SelectDownTrack(true); + } + } + + [Shortcut(Shortcuts.Timeline.toggleClipTrackArea)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class ToggleClipTrackArea : TimelineAction + { + public override bool Execute(WindowState state) + { + if (KeyboardNavigation.TrackHeadActive()) + return KeyboardNavigation.FocusFirstVisibleItem(state, SelectionManager.SelectedTracks()); + + if (!KeyboardNavigation.ClipAreaActive()) + return KeyboardNavigation.FocusFirstVisibleItem(state); + + var item = KeyboardNavigation.GetVisibleSelectedItems().LastOrDefault(); + if (item != null) + SelectionManager.SelectOnly(item.parentTrack); + return true; + } + } + + [MenuEntry("Mute", MenuOrder.TrackAction.MuteTrack)] + class ToggleMuteMarkersOnTimeline : MarkerHeaderAction + { + public override bool IsChecked(WindowState state) + { + return IsMarkerTrackValid(state) && state.editSequence.asset.markerTrack.muted; + } + + public override bool Execute(WindowState state) + { + if (state.showMarkerHeader) + ToggleMute(state); + return true; + } + + static void ToggleMute(WindowState state) + { + var timeline = state.editSequence.asset; + timeline.CreateMarkerTrack(); + + TimelineUndo.PushUndo(timeline.markerTrack, "Toggle Mute"); + timeline.markerTrack.muted = !timeline.markerTrack.muted; + } + + static bool IsMarkerTrackValid(WindowState state) + { + var timeline = state.editSequence.asset; + return timeline != null && timeline.markerTrack != null; + } + } + + [MenuEntry("Show Markers", MenuOrder.TrackAction.ShowHideMarkers)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class ToggleShowMarkersOnTimeline : MarkerHeaderAction + { + public override bool IsChecked(WindowState state) + { + return state.showMarkerHeader; + } + + public override bool Execute(WindowState state) + { + ToggleShow(state); + return true; + } + + static void ToggleShow(WindowState state) + { + state.GetWindow().SetShowMarkerHeader(!state.showMarkerHeader); + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/TimelineActions.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/TimelineActions.cs.meta new file mode 100644 index 0000000..39295c4 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/TimelineActions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b1c789407b55e3a4c9cc86135a714e33 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/TrackActions.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/TrackActions.cs new file mode 100644 index 0000000..d379bd0 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/TrackActions.cs @@ -0,0 +1,521 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using JetBrains.Annotations; +using UnityEngine; +using UnityEngine.Timeline; + +namespace UnityEditor.Timeline +{ + [ActiveInMode(TimelineModes.Default)] + abstract class TrackAction : MenuItemActionBase + { + public abstract bool Execute(WindowState state, TrackAsset[] tracks); + + protected virtual MenuActionDisplayState GetDisplayState(WindowState state, TrackAsset[] tracks) + { + return tracks.Length > 0 ? MenuActionDisplayState.Visible : MenuActionDisplayState.Disabled; + } + + protected virtual bool IsChecked(WindowState state, TrackAsset[] tracks) + { + return false; + } + + protected virtual string GetDisplayName(TrackAsset[] tracks) + { + return menuName; + } + + public static void Invoke(WindowState state, TrackAsset[] tracks) where T : TrackAction + { + actions.First(x => x.GetType() == typeof(T)).Execute(state, tracks); + } + + static List s_ActionClasses; + + static List actions + { + get + { + if (s_ActionClasses == null) + s_ActionClasses = + GetActionsOfType(typeof(TrackAction)) + .Select(x => (TrackAction)x.GetConstructors()[0].Invoke(null)) + .OrderBy(x => x.priority).ThenBy(x => x.category) + .ToList(); + + return s_ActionClasses; + } + } + + public static void GetMenuEntries(WindowState state, Vector2? mousePos, TrackAsset[] tracks, List items) + { + var mode = TimelineWindow.instance.currentMode.mode; + foreach (var action in actions) + { + if (!action.showInMenu) + continue; + + var actionItem = action; + items.Add( + new MenuActionItem() + { + category = action.category, + entryName = action.GetDisplayName(tracks), + shortCut = action.shortCut, + isChecked = action.IsChecked(state, tracks), + isActiveInMode = IsActionActiveInMode(action, mode), + priority = action.priority, + state = action.GetDisplayState(state, tracks), + callback = () => + { + actionItem.mousePosition = mousePos; + actionItem.Execute(state, tracks); + actionItem.mousePosition = null; + } + } + ); + } + } + + public static bool HandleShortcut(WindowState state, Event evt, TrackAsset[] tracks) + { + foreach (var action in actions) + { + var attr = action.GetType().GetCustomAttributes(typeof(ShortcutAttribute), true); + + foreach (ShortcutAttribute shortcut in attr) + { + if (shortcut.MatchesEvent(evt)) + { + if (s_ShowActionTriggeredByShortcut) + Debug.Log(action.GetType().Name); + + if (!IsActionActiveInMode(action, TimelineWindow.instance.currentMode.mode)) + return false; + + return action.Execute(state, tracks); + } + } + } + + return false; + } + + // For testing + internal MenuActionDisplayState InternalGetDisplayState(WindowState state, TrackAsset[] tracks) + { + return GetDisplayState(state, tracks); + } + } + + [MenuEntry("Edit in Animation Window", MenuOrder.TrackAction.EditInAnimationWindow)] + class EditTrackInAnimationWindow : TrackAction + { + public static bool Do(WindowState state, TrackAsset track) + { + AnimationClip clipToEdit = null; + + AnimationTrack animationTrack = track as AnimationTrack; + if (animationTrack != null) + { + if (!animationTrack.CanConvertToClipMode()) + return false; + + clipToEdit = animationTrack.infiniteClip; + } + else if (track.hasCurves) + { + clipToEdit = track.curves; + } + + if (clipToEdit == null) + return false; + + var gameObject = state.GetSceneReference(track); + var timeController = TimelineAnimationUtilities.CreateTimeController(state, CreateTimeControlClipData(track)); + TimelineAnimationUtilities.EditAnimationClipWithTimeController(clipToEdit, timeController, gameObject); + + return true; + } + + protected override MenuActionDisplayState GetDisplayState(WindowState state, TrackAsset[] tracks) + { + if (tracks.Length == 0) + return MenuActionDisplayState.Hidden; + + if (tracks[0] is AnimationTrack) + { + var animTrack = tracks[0] as AnimationTrack; + if (animTrack.CanConvertToClipMode()) + return MenuActionDisplayState.Visible; + } + else if (tracks[0].hasCurves) + { + return MenuActionDisplayState.Visible; + } + + return MenuActionDisplayState.Hidden; + } + + public override bool Execute(WindowState state, TrackAsset[] tracks) + { + return Do(state, tracks[0]); + } + + static TimelineWindowTimeControl.ClipData CreateTimeControlClipData(TrackAsset track) + { + var data = new TimelineWindowTimeControl.ClipData(); + data.track = track; + data.start = track.start; + data.duration = track.duration; + return data; + } + } + + [MenuEntry("Lock selected track only", MenuOrder.TrackAction.LockSelected)] + class LockSelectedTrack : TrackAction + { + public static readonly string LockSelectedTrackOnlyText = L10n.Tr("Lock selected track only"); + public static readonly string UnlockSelectedTrackOnlyText = L10n.Tr("Unlock selected track only"); + + protected override MenuActionDisplayState GetDisplayState(WindowState state, TrackAsset[] tracks) + { + if (tracks.Any(track => TimelineUtility.IsLockedFromGroup(track) || track is GroupTrack || + !track.subTracksObjects.Any())) + return MenuActionDisplayState.Hidden; + return MenuActionDisplayState.Visible; + } + + public override bool Execute(WindowState state, TrackAsset[] tracks) + { + if (!tracks.Any()) return false; + + var hasUnlockedTracks = tracks.Any(x => !x.locked); + Lock(state, tracks.Where(p => !(p is GroupTrack)).ToArray(), hasUnlockedTracks); + return true; + } + + protected override string GetDisplayName(TrackAsset[] tracks) + { + return tracks.All(t => t.locked) ? UnlockSelectedTrackOnlyText : LockSelectedTrackOnlyText; + } + + public static void Lock(WindowState state, TrackAsset[] tracks, bool shouldlock) + { + if (tracks.Length == 0) + return; + + foreach (var track in tracks.Where(t => !TimelineUtility.IsLockedFromGroup(t))) + { + TimelineUndo.PushUndo(track, "Lock Tracks"); + track.locked = shouldlock; + } + TimelineEditor.Refresh(RefreshReason.WindowNeedsRedraw); + } + } + + [MenuEntry("Lock", MenuOrder.TrackAction.LockTrack)] + [Shortcut(Shortcuts.Timeline.toggleLock)] + class LockTrack : TrackAction + { + public static readonly string UnlockText = L10n.Tr("Unlock"); + + protected override MenuActionDisplayState GetDisplayState(WindowState state, TrackAsset[] tracks) + { + bool hasUnlockableTracks = tracks.Any(x => TimelineUtility.IsLockedFromGroup(x)); + if (hasUnlockableTracks) + return MenuActionDisplayState.Disabled; + return MenuActionDisplayState.Visible; + } + + protected override string GetDisplayName(TrackAsset[] tracks) + { + return tracks.Any(x => !x.locked) ? base.GetDisplayName(tracks) : UnlockText; + } + + public override bool Execute(WindowState state, TrackAsset[] tracks) + { + if (!tracks.Any()) return false; + + var hasUnlockedTracks = tracks.Any(x => !x.locked); + SetLockState(tracks, hasUnlockedTracks, state); + return true; + } + + public static void SetLockState(TrackAsset[] tracks, bool shouldLock, WindowState state = null) + { + if (tracks.Length == 0) + return; + + foreach (var track in tracks) + { + if (TimelineUtility.IsLockedFromGroup(track)) + continue; + + if (track as GroupTrack == null) + SetLockState(track.GetChildTracks().ToArray(), shouldLock, state); + + TimelineUndo.PushUndo(track, "Lock Tracks"); + track.locked = shouldLock; + } + + if (state != null) + { + // find the tracks we've locked. unselect anything locked and remove recording. + foreach (var track in tracks) + { + if (TimelineUtility.IsLockedFromGroup(track) || !track.locked) + continue; + + var flattenedChildTracks = track.GetFlattenedChildTracks(); + foreach (var i in track.clips) + SelectionManager.Remove(i); + state.UnarmForRecord(track); + foreach (var child in flattenedChildTracks) + { + SelectionManager.Remove(child); + state.UnarmForRecord(child); + foreach (var clip in child.GetClips()) + SelectionManager.Remove(clip); + } + } + + // no need to rebuild, just repaint (including inspectors) + InspectorWindow.RepaintAllInspectors(); + state.editorWindow.Repaint(); + } + } + } + + [UsedImplicitly] + [MenuEntry("Show Markers", MenuOrder.TrackAction.ShowHideMarkers)] + [ActiveInMode(TimelineModes.Default | TimelineModes.ReadOnly)] + class ShowHideMarkers : TrackAction + { + protected override bool IsChecked(WindowState state, TrackAsset[] tracks) + { + return tracks.All(x => x.GetShowMarkers()); + } + + protected override MenuActionDisplayState GetDisplayState(WindowState state, TrackAsset[] tracks) + { + if (tracks.Any(x => x is GroupTrack) || tracks.Any(t => t.GetMarkerCount() == 0)) + return MenuActionDisplayState.Hidden; + + if (tracks.Any(t => t.lockedInHierarchy)) + return MenuActionDisplayState.Disabled; + + return MenuActionDisplayState.Visible; + } + + public override bool Execute(WindowState state, TrackAsset[] tracks) + { + if (!tracks.Any()) return false; + + var hasUnlockedTracks = tracks.Any(x => !x.GetShowMarkers()); + ShowHide(state, tracks, hasUnlockedTracks); + return true; + } + + static void ShowHide(WindowState state, TrackAsset[] tracks, bool shouldLock) + { + if (tracks.Length == 0) + return; + + var window = state.GetWindow(); + foreach (var track in tracks) + { + window.SetShowTrackMarkers(track, shouldLock); + } + + TimelineEditor.Refresh(RefreshReason.WindowNeedsRedraw); + } + } + + [MenuEntry("Mute selected track only", MenuOrder.TrackAction.MuteSelected), UsedImplicitly] + class MuteSelectedTrack : TrackAction + { + public static readonly string UnmuteSelectedText = L10n.Tr("Unmute selected track only"); + protected override MenuActionDisplayState GetDisplayState(WindowState state, TrackAsset[] tracks) + { + if (tracks.Any(track => TimelineUtility.IsParentMuted(track) || track is GroupTrack || + !track.subTracksObjects.Any())) + return MenuActionDisplayState.Hidden; + return MenuActionDisplayState.Visible; + } + + public override bool Execute(WindowState state, TrackAsset[] tracks) + { + if (!tracks.Any()) + return false; + + var hasUnmutedTracks = tracks.Any(x => !x.muted); + Mute(state, tracks.Where(p => !(p is GroupTrack)).ToArray(), hasUnmutedTracks); + return true; + } + + protected override string GetDisplayName(TrackAsset[] tracks) + { + return tracks.All(t => t.muted) ? UnmuteSelectedText : base.GetDisplayName(tracks); + } + + public static void Mute(WindowState state, TrackAsset[] tracks, bool shouldMute) + { + if (tracks.Length == 0) + return; + + foreach (var track in tracks.Where(t => !TimelineUtility.IsParentMuted(t))) + { + TimelineUndo.PushUndo(track, "Mute Tracks"); + track.muted = shouldMute; + } + + state.Refresh(); + } + } + + [MenuEntry("Mute", MenuOrder.TrackAction.MuteTrack)] + [Shortcut(Shortcuts.Timeline.toggleMute)] + class MuteTrack : TrackAction + { + public static readonly string UnMuteText = L10n.Tr("Unmute"); + + protected override MenuActionDisplayState GetDisplayState(WindowState state, TrackAsset[] tracks) + { + if (tracks.Any(track => TimelineUtility.IsParentMuted(track))) + return MenuActionDisplayState.Disabled; + return MenuActionDisplayState.Visible; + } + + protected override string GetDisplayName(TrackAsset[] tracks) + { + return tracks.Any(x => !x.muted) ? base.GetDisplayName(tracks) : UnMuteText; + } + + public override bool Execute(WindowState state, TrackAsset[] tracks) + { + if (!tracks.Any() || tracks.Any(track => TimelineUtility.IsParentMuted(track))) + return false; + + var hasUnmutedTracks = tracks.Any(x => !x.muted); + Mute(state, tracks, hasUnmutedTracks); + return true; + } + + public static void Mute(WindowState state, TrackAsset[] tracks, bool shouldMute) + { + if (tracks.Length == 0) + return; + + foreach (var track in tracks) + { + if (track as GroupTrack == null) + Mute(state, track.GetChildTracks().ToArray(), shouldMute); + TimelineUndo.PushUndo(track, "Mute Tracks"); + track.muted = shouldMute; + } + + state.Refresh(); + } + } + + class DeleteTracks : TrackAction + { + public static void Do(TimelineAsset timeline, TrackAsset track) + { + SelectionManager.Remove(track); + TrackModifier.DeleteTrack(timeline, track); + } + + public override bool Execute(WindowState state, TrackAsset[] tracks) + { + // disable preview mode so deleted tracks revert to default state + // Case 956129: Disable preview mode _before_ deleting the tracks, since clip data is still needed + state.previewMode = false; + + TimelineAnimationUtilities.UnlinkAnimationWindowFromTracks(tracks); + + foreach (var track in tracks) + Do(state.editSequence.asset, track); + + state.Refresh(); + + return true; + } + } + + class CopyTracksToClipboard : TrackAction + { + public static bool Do(WindowState state, TrackAsset[] tracks) + { + var action = new CopyTracksToClipboard(); + + return action.Execute(state, tracks); + } + + public override bool Execute(WindowState state, TrackAsset[] tracks) + { + TimelineEditor.clipboard.CopyTracks(tracks); + + return true; + } + } + + class DuplicateTracks : TrackAction + { + public override bool Execute(WindowState state, TrackAsset[] tracks) + { + if (tracks.Any()) + { + SelectionManager.RemoveTimelineSelection(); + } + + foreach (var track in TrackExtensions.FilterTracks(tracks)) + { + var newTrack = track.Duplicate(TimelineEditor.inspectedDirector, TimelineEditor.inspectedDirector); + SelectionManager.Add(newTrack); + foreach (var childTrack in newTrack.GetFlattenedChildTracks()) + { + SelectionManager.Add(childTrack); + } + } + + state.Refresh(); + + return true; + } + } + + [MenuEntry("Remove Invalid Markers", MenuOrder.TrackAction.RemoveInvalidMarkers), UsedImplicitly] + class RemoveInvalidMarkersAction : TrackAction + { + protected override MenuActionDisplayState GetDisplayState(WindowState state, TrackAsset[] tracks) + { + if (tracks.Any(target => target != null && target.GetMarkerCount() != target.GetMarkersRaw().Count())) + return MenuActionDisplayState.Visible; + + return MenuActionDisplayState.Hidden; + } + + public override bool Execute(WindowState state, TrackAsset[] tracks) + { + bool anyRemoved = false; + foreach (var target in tracks) + { + var invalids = target.GetMarkersRaw().Where(x => !(x is IMarker)).ToList(); + foreach (var m in invalids) + { + anyRemoved = true; + target.DeleteMarkerRaw(m); + } + } + + if (anyRemoved) + TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved); + + return anyRemoved; + } + } +} diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/TrackActions.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/TrackActions.cs.meta new file mode 100644 index 0000000..483f9eb --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Actions/TrackActions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fda82b5ca7a4c5f40b497c4f5f4bd950 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: -- cgit v1.2.3